diff options
Diffstat (limited to 'locale/programs/locarchive.c')
-rw-r--r-- | locale/programs/locarchive.c | 1757 |
1 files changed, 0 insertions, 1757 deletions
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c deleted file mode 100644 index f67b7b8d99..0000000000 --- a/locale/programs/locarchive.c +++ /dev/null @@ -1,1757 +0,0 @@ -/* Copyright (C) 2002-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <assert.h> -#include <dirent.h> -#include <errno.h> -#include <error.h> -#include <fcntl.h> -#include <inttypes.h> -#include <libintl.h> -#include <locale.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <stdint.h> -#include <sys/mman.h> -#include <sys/param.h> -#include <sys/shm.h> -#include <sys/stat.h> - -#include <libc-mmap.h> -#include <libc-pointer-arith.h> -#include "../../crypt/md5.h" -#include "../localeinfo.h" -#include "../locarchive.h" -#include "localedef.h" -#include "locfile.h" - -/* Define the hash function. We define the function as static inline. - We must change the name so as not to conflict with simple-hash.h. */ -#define compute_hashval static archive_hashval -#define hashval_t uint32_t -#include "hashval.h" -#undef compute_hashval - -extern const char *output_prefix; - -#define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive" - -static const char *locnames[] = - { -#define DEFINE_CATEGORY(category, category_name, items, a) \ - [category] = category_name, -#include "categories.def" -#undef DEFINE_CATEGORY - }; - - -/* Size of the initial archive header. */ -#define INITIAL_NUM_NAMES 900 -#define INITIAL_SIZE_STRINGS 7500 -#define INITIAL_NUM_LOCREC 420 -#define INITIAL_NUM_SUMS 2000 - - -/* Get and set values (possibly endian-swapped) in structures mapped - from or written directly to locale archives. */ -#define GET(FIELD) maybe_swap_uint32 (FIELD) -#define SET(FIELD, VALUE) ((FIELD) = maybe_swap_uint32 (VALUE)) -#define INC(FIELD, INCREMENT) SET (FIELD, GET (FIELD) + (INCREMENT)) - - -/* Size of the reserved address space area. */ -#define RESERVE_MMAP_SIZE 512 * 1024 * 1024 - -/* To prepare for enlargements of the mmaped area reserve some address - space. On some machines, being a file mapping rather than an anonymous - mapping affects the address selection. So do this mapping from the - actual file, even though it's only a dummy to reserve address space. */ -static void * -prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags, - void **mmap_base, size_t *mmap_len) -{ - if (total < RESERVE_MMAP_SIZE) - { - void *p = mmap64 (NULL, RESERVE_MMAP_SIZE, PROT_NONE, MAP_SHARED, fd, 0); - if (p != MAP_FAILED) - { - void *aligned_p = PTR_ALIGN_UP (p, MAP_FIXED_ALIGNMENT); - size_t align_adjust = aligned_p - p; - *mmap_base = p; - *mmap_len = RESERVE_MMAP_SIZE; - assert (align_adjust < RESERVE_MMAP_SIZE); - *reserved = RESERVE_MMAP_SIZE - align_adjust; - *xflags = MAP_FIXED; - return aligned_p; - } - } - - *reserved = total; - *xflags = 0; - *mmap_base = NULL; - *mmap_len = 0; - return NULL; -} - - -static void -create_archive (const char *archivefname, struct locarhandle *ah) -{ - int fd; - char fname[strlen (archivefname) + sizeof (".XXXXXX")]; - struct locarhead head; - size_t total; - - strcpy (stpcpy (fname, archivefname), ".XXXXXX"); - - /* Create a temporary file in the correct directory. */ - fd = mkstemp (fname); - if (fd == -1) - error (EXIT_FAILURE, errno, _("cannot create temporary file: %s"), fname); - - /* Create the initial content of the archive. */ - SET (head.magic, AR_MAGIC); - SET (head.serial, 0); - SET (head.namehash_offset, sizeof (struct locarhead)); - SET (head.namehash_used, 0); - SET (head.namehash_size, next_prime (INITIAL_NUM_NAMES)); - - SET (head.string_offset, - (GET (head.namehash_offset) - + GET (head.namehash_size) * sizeof (struct namehashent))); - SET (head.string_used, 0); - SET (head.string_size, INITIAL_SIZE_STRINGS); - - SET (head.locrectab_offset, - GET (head.string_offset) + GET (head.string_size)); - SET (head.locrectab_used, 0); - SET (head.locrectab_size, INITIAL_NUM_LOCREC); - - SET (head.sumhash_offset, - (GET (head.locrectab_offset) - + GET (head.locrectab_size) * sizeof (struct locrecent))); - SET (head.sumhash_used, 0); - SET (head.sumhash_size, next_prime (INITIAL_NUM_SUMS)); - - total = (GET (head.sumhash_offset) - + GET (head.sumhash_size) * sizeof (struct sumhashent)); - - /* Write out the header and create room for the other data structures. */ - if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head)) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot initialize archive file")); - } - - if (ftruncate64 (fd, total) != 0) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot resize archive file")); - } - - size_t reserved, mmap_len; - int xflags; - void *mmap_base; - void *p = prepare_address_space (fd, total, &reserved, &xflags, &mmap_base, - &mmap_len); - - /* Map the header and all the administration data structures. */ - p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0); - if (p == MAP_FAILED) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot map archive header")); - } - - /* Now try to rename it. We don't use the rename function since - this would overwrite a file which has been created in - parallel. */ - if (link (fname, archivefname) == -1) - { - int errval = errno; - - /* We cannot use the just created file. */ - close (fd); - unlink (fname); - - if (errval == EEXIST) - { - /* There is already an archive. Must have been a localedef run - which happened in parallel. Simply open this file then. */ - open_archive (ah, false); - return; - } - - error (EXIT_FAILURE, errval, _("failed to create new locale archive")); - } - - /* Remove the temporary name. */ - unlink (fname); - - /* Make the file globally readable. */ - if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1) - { - int errval = errno; - unlink (archivefname); - error (EXIT_FAILURE, errval, - _("cannot change mode of new locale archive")); - } - - ah->fname = NULL; - ah->fd = fd; - ah->mmap_base = mmap_base; - ah->mmap_len = mmap_len; - ah->addr = p; - ah->mmaped = total; - ah->reserved = reserved; -} - - -/* This structure and qsort comparator function are used below to sort an - old archive's locrec table in order of data position in the file. */ -struct oldlocrecent -{ - unsigned int cnt; - struct locrecent *locrec; -}; - -static int -oldlocrecentcmp (const void *a, const void *b) -{ - struct locrecent *la = ((const struct oldlocrecent *) a)->locrec; - struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec; - uint32_t start_a = -1, end_a = 0; - uint32_t start_b = -1, end_b = 0; - int cnt; - - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - { - if (GET (la->record[cnt].offset) < start_a) - start_a = GET (la->record[cnt].offset); - if (GET (la->record[cnt].offset) + GET (la->record[cnt].len) > end_a) - end_a = GET (la->record[cnt].offset) + GET (la->record[cnt].len); - } - assert (start_a != (uint32_t)-1); - assert (end_a != 0); - - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - { - if (GET (lb->record[cnt].offset) < start_b) - start_b = GET (lb->record[cnt].offset); - if (GET (lb->record[cnt].offset) + GET (lb->record[cnt].len) > end_b) - end_b = GET (lb->record[cnt].offset) + GET (lb->record[cnt].len); - } - assert (start_b != (uint32_t)-1); - assert (end_b != 0); - - if (start_a != start_b) - return (int)start_a - (int)start_b; - return (int)end_a - (int)end_b; -} - - -/* forward decls for below */ -static uint32_t add_locale (struct locarhandle *ah, const char *name, - locale_data_t data, bool replace); -static void add_alias (struct locarhandle *ah, const char *alias, - bool replace, const char *oldname, - uint32_t *locrec_offset_p); - - -static bool -file_data_available_p (struct locarhandle *ah, uint32_t offset, uint32_t size) -{ - if (offset < ah->mmaped && offset + size <= ah->mmaped) - return true; - - struct stat64 st; - if (fstat64 (ah->fd, &st) != 0) - return false; - - if (st.st_size > ah->reserved) - return false; - - size_t start = ALIGN_DOWN (ah->mmaped, MAP_FIXED_ALIGNMENT); - void *p = mmap64 (ah->addr + start, st.st_size - start, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, - ah->fd, start); - if (p == MAP_FAILED) - { - ah->mmaped = start; - return false; - } - - ah->mmaped = st.st_size; - return true; -} - - -static int -compare_from_file (struct locarhandle *ah, void *p1, uint32_t offset2, - uint32_t size) -{ - void *p2 = xmalloc (size); - if (pread (ah->fd, p2, size, offset2) != size) - WITH_CUR_LOCALE (error (4, errno, - _("cannot read data from locale archive"))); - - int res = memcmp (p1, p2, size); - free (p2); - return res; -} - - -static void -enlarge_archive (struct locarhandle *ah, const struct locarhead *head) -{ - struct stat64 st; - int fd; - struct locarhead newhead; - size_t total; - unsigned int cnt, loccnt; - struct namehashent *oldnamehashtab; - struct locarhandle new_ah; - size_t prefix_len = output_prefix ? strlen (output_prefix) : 0; - char archivefname[prefix_len + sizeof (ARCHIVE_NAME)]; - char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1]; - - if (output_prefix) - memcpy (archivefname, output_prefix, prefix_len); - strcpy (archivefname + prefix_len, ARCHIVE_NAME); - strcpy (stpcpy (fname, archivefname), ".XXXXXX"); - - /* Not all of the old file has to be mapped. Change this now this - we will have to access the whole content. */ - if (fstat64 (ah->fd, &st) != 0) - enomap: - error (EXIT_FAILURE, errno, _("cannot map locale archive file")); - - if (st.st_size < ah->reserved) - ah->addr = mmap64 (ah->addr, st.st_size, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_FIXED, ah->fd, 0); - else - { - if (ah->mmap_base) - munmap (ah->mmap_base, ah->mmap_len); - else - munmap (ah->addr, ah->reserved); - ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE, - MAP_SHARED, ah->fd, 0); - ah->reserved = st.st_size; - ah->mmap_base = NULL; - ah->mmap_len = 0; - head = ah->addr; - } - if (ah->addr == MAP_FAILED) - goto enomap; - ah->mmaped = st.st_size; - - /* Create a temporary file in the correct directory. */ - fd = mkstemp (fname); - if (fd == -1) - error (EXIT_FAILURE, errno, _("cannot create temporary file: %s"), fname); - - /* Copy the existing head information. */ - newhead = *head; - - /* Create the new archive header. The sizes of the various tables - should be double from what is currently used. */ - SET (newhead.namehash_size, - MAX (next_prime (2 * GET (newhead.namehash_used)), - GET (newhead.namehash_size))); - if (verbose) - printf ("name: size: %u, used: %d, new: size: %u\n", - GET (head->namehash_size), - GET (head->namehash_used), GET (newhead.namehash_size)); - - SET (newhead.string_offset, (GET (newhead.namehash_offset) - + (GET (newhead.namehash_size) - * sizeof (struct namehashent)))); - /* Keep the string table size aligned to 4 bytes, so that - all the struct { uint32_t } types following are happy. */ - SET (newhead.string_size, MAX ((2 * GET (newhead.string_used) + 3) & -4, - GET (newhead.string_size))); - - SET (newhead.locrectab_offset, - GET (newhead.string_offset) + GET (newhead.string_size)); - SET (newhead.locrectab_size, MAX (2 * GET (newhead.locrectab_used), - GET (newhead.locrectab_size))); - - SET (newhead.sumhash_offset, (GET (newhead.locrectab_offset) - + (GET (newhead.locrectab_size) - * sizeof (struct locrecent)))); - SET (newhead.sumhash_size, - MAX (next_prime (2 * GET (newhead.sumhash_used)), - GET (newhead.sumhash_size))); - - total = (GET (newhead.sumhash_offset) - + GET (newhead.sumhash_size) * sizeof (struct sumhashent)); - - /* The new file is empty now. */ - SET (newhead.namehash_used, 0); - SET (newhead.string_used, 0); - SET (newhead.locrectab_used, 0); - SET (newhead.sumhash_used, 0); - - /* Write out the header and create room for the other data structures. */ - if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead))) - != sizeof (newhead)) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot initialize archive file")); - } - - if (ftruncate64 (fd, total) != 0) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot resize archive file")); - } - - size_t reserved, mmap_len; - int xflags; - void *mmap_base; - void *p = prepare_address_space (fd, total, &reserved, &xflags, &mmap_base, - &mmap_len); - - /* Map the header and all the administration data structures. */ - p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0); - if (p == MAP_FAILED) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot map archive header")); - } - - /* Lock the new file. */ - if (lockf64 (fd, F_LOCK, total) != 0) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot lock new archive")); - } - - new_ah.mmaped = total; - new_ah.mmap_base = mmap_base; - new_ah.mmap_len = mmap_len; - new_ah.addr = p; - new_ah.fd = fd; - new_ah.reserved = reserved; - - /* Walk through the hash name hash table to find out what data is - still referenced and transfer it into the new file. */ - oldnamehashtab = (struct namehashent *) ((char *) ah->addr - + GET (head->namehash_offset)); - - /* Sort the old locrec table in order of data position. */ - struct oldlocrecent oldlocrecarray[GET (head->namehash_size)]; - for (cnt = 0, loccnt = 0; cnt < GET (head->namehash_size); ++cnt) - if (GET (oldnamehashtab[cnt].locrec_offset) != 0) - { - oldlocrecarray[loccnt].cnt = cnt; - oldlocrecarray[loccnt++].locrec - = (struct locrecent *) ((char *) ah->addr - + GET (oldnamehashtab[cnt].locrec_offset)); - } - qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent), - oldlocrecentcmp); - - uint32_t last_locrec_offset = 0; - for (cnt = 0; cnt < loccnt; ++cnt) - { - /* Insert this entry in the new hash table. */ - locale_data_t old_data; - unsigned int idx; - struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec; - - for (idx = 0; idx < __LC_LAST; ++idx) - if (idx != LC_ALL) - { - old_data[idx].size = GET (oldlocrec->record[idx].len); - old_data[idx].addr - = ((char *) ah->addr + GET (oldlocrec->record[idx].offset)); - - __md5_buffer (old_data[idx].addr, old_data[idx].size, - old_data[idx].sum); - } - - if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec) - { - const char *oldname - = ((char *) ah->addr - + GET (oldnamehashtab[oldlocrecarray[cnt - - 1].cnt].name_offset)); - - add_alias - (&new_ah, - ((char *) ah->addr - + GET (oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset)), - 0, oldname, &last_locrec_offset); - continue; - } - - last_locrec_offset = - add_locale - (&new_ah, - ((char *) ah->addr - + GET (oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset)), - old_data, 0); - if (last_locrec_offset == 0) - error (EXIT_FAILURE, 0, _("cannot extend locale archive file")); - } - - /* Make the file globally readable. */ - if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, - _("cannot change mode of resized locale archive")); - } - - /* Rename the new file. */ - if (rename (fname, archivefname) != 0) - { - int errval = errno; - unlink (fname); - error (EXIT_FAILURE, errval, _("cannot rename new archive")); - } - - /* Close the old file. */ - close_archive (ah); - - /* Add the information for the new one. */ - *ah = new_ah; -} - - -void -open_archive (struct locarhandle *ah, bool readonly) -{ - struct stat64 st; - struct stat64 st2; - int fd; - struct locarhead head; - int retry = 0; - size_t prefix_len = output_prefix ? strlen (output_prefix) : 0; - char default_fname[prefix_len + sizeof (ARCHIVE_NAME)]; - const char *archivefname = ah->fname; - - /* If ah has a non-NULL fname open that otherwise open the default. */ - if (archivefname == NULL) - { - archivefname = default_fname; - if (output_prefix) - memcpy (default_fname, output_prefix, prefix_len); - strcpy (default_fname + prefix_len, ARCHIVE_NAME); - } - - while (1) - { - /* Open the archive. We must have exclusive write access. */ - fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR); - if (fd == -1) - { - /* Maybe the file does not yet exist? If we are opening - the default locale archive we ignore the failure and - list an empty archive, otherwise we print an error - and exit. */ - if (errno == ENOENT && archivefname == default_fname) - { - if (readonly) - { - static const struct locarhead nullhead = - { - .namehash_used = 0, - .namehash_offset = 0, - .namehash_size = 0 - }; - - ah->addr = (void *) &nullhead; - ah->fd = -1; - } - else - create_archive (archivefname, ah); - - return; - } - else - error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""), - archivefname); - } - - if (fstat64 (fd, &st) < 0) - error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""), - archivefname); - - if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1) - { - close (fd); - - if (retry++ < max_locarchive_open_retry) - { - struct timespec req; - - /* Wait for a bit. */ - req.tv_sec = 0; - req.tv_nsec = 1000000 * (random () % 500 + 1); - (void) nanosleep (&req, NULL); - - continue; - } - - error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""), - archivefname); - } - - /* One more check. Maybe another process replaced the archive file - with a new, larger one since we opened the file. */ - if (stat64 (archivefname, &st2) == -1 - || st.st_dev != st2.st_dev - || st.st_ino != st2.st_ino) - { - (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead)); - close (fd); - continue; - } - - /* Leave the loop. */ - break; - } - - /* Read the header. */ - if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head)) - { - (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead)); - error (EXIT_FAILURE, errno, _("cannot read archive header")); - } - - ah->fd = fd; - ah->mmaped = st.st_size; - - size_t reserved, mmap_len; - int xflags; - void *mmap_base; - void *p = prepare_address_space (fd, st.st_size, &reserved, &xflags, - &mmap_base, &mmap_len); - - /* Map the entire file. We might need to compare the category data - in the file with the newly added data. */ - ah->addr = mmap64 (p, st.st_size, PROT_READ | (readonly ? 0 : PROT_WRITE), - MAP_SHARED | xflags, fd, 0); - if (ah->addr == MAP_FAILED) - { - (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead)); - error (EXIT_FAILURE, errno, _("cannot map archive header")); - } - ah->reserved = reserved; - ah->mmap_base = mmap_base; - ah->mmap_len = mmap_len; -} - - -void -close_archive (struct locarhandle *ah) -{ - if (ah->fd != -1) - { - if (ah->mmap_base) - munmap (ah->mmap_base, ah->mmap_len); - else - munmap (ah->addr, ah->reserved); - close (ah->fd); - } -} - -#include "../../intl/explodename.c" -#include "../../intl/l10nflist.c" - -static struct namehashent * -insert_name (struct locarhandle *ah, - const char *name, size_t name_len, bool replace) -{ - const struct locarhead *const head = ah->addr; - struct namehashent *namehashtab - = (struct namehashent *) ((char *) ah->addr - + GET (head->namehash_offset)); - unsigned int insert_idx, idx, incr; - - /* Hash value of the locale name. */ - uint32_t hval = archive_hashval (name, name_len); - - insert_idx = -1; - idx = hval % GET (head->namehash_size); - incr = 1 + hval % (GET (head->namehash_size) - 2); - - /* If the name_offset field is zero this means this is a - deleted entry and therefore no entry can be found. */ - while (GET (namehashtab[idx].name_offset) != 0) - { - if (GET (namehashtab[idx].hashval) == hval - && (strcmp (name, - (char *) ah->addr + GET (namehashtab[idx].name_offset)) - == 0)) - { - /* Found the entry. */ - if (GET (namehashtab[idx].locrec_offset) != 0 && ! replace) - { - if (! be_quiet) - error (0, 0, _("locale '%s' already exists"), name); - return NULL; - } - - break; - } - - if (GET (namehashtab[idx].hashval) == hval && ! be_quiet) - { - error (0, 0, "hash collision (%u) %s, %s", - hval, name, - (char *) ah->addr + GET (namehashtab[idx].name_offset)); - } - - /* Remember the first place we can insert the new entry. */ - if (GET (namehashtab[idx].locrec_offset) == 0 && insert_idx == -1) - insert_idx = idx; - - idx += incr; - if (idx >= GET (head->namehash_size)) - idx -= GET (head->namehash_size); - } - - /* Add as early as possible. */ - if (insert_idx != -1) - idx = insert_idx; - - SET (namehashtab[idx].hashval, hval); /* no-op if replacing an old entry. */ - return &namehashtab[idx]; -} - -static void -add_alias (struct locarhandle *ah, const char *alias, bool replace, - const char *oldname, uint32_t *locrec_offset_p) -{ - uint32_t locrec_offset = *locrec_offset_p; - struct locarhead *head = ah->addr; - const size_t name_len = strlen (alias); - struct namehashent *namehashent = insert_name (ah, alias, strlen (alias), - replace); - if (namehashent == NULL && ! replace) - return; - - if (GET (namehashent->name_offset) == 0) - { - /* We are adding a new hash entry for this alias. - Determine whether we have to resize the file. */ - if (GET (head->string_used) + name_len + 1 > GET (head->string_size) - || (100 * GET (head->namehash_used) - > 75 * GET (head->namehash_size))) - { - /* The current archive is not large enough. */ - enlarge_archive (ah, head); - - /* The locrecent might have moved, so we have to look up - the old name afresh. */ - namehashent = insert_name (ah, oldname, strlen (oldname), true); - assert (GET (namehashent->name_offset) != 0); - assert (GET (namehashent->locrec_offset) != 0); - *locrec_offset_p = GET (namehashent->locrec_offset); - - /* Tail call to try the whole thing again. */ - add_alias (ah, alias, replace, oldname, locrec_offset_p); - return; - } - - /* Add the name string. */ - memcpy (ah->addr + GET (head->string_offset) + GET (head->string_used), - alias, name_len + 1); - SET (namehashent->name_offset, - GET (head->string_offset) + GET (head->string_used)); - INC (head->string_used, name_len + 1); - - INC (head->namehash_used, 1); - } - - if (GET (namehashent->locrec_offset) != 0) - { - /* Replacing an existing entry. - Mark that we are no longer using the old locrecent. */ - struct locrecent *locrecent - = (struct locrecent *) ((char *) ah->addr - + GET (namehashent->locrec_offset)); - INC (locrecent->refs, -1); - } - - /* Point this entry at the locrecent installed for the main name. */ - SET (namehashent->locrec_offset, locrec_offset); -} - -static int /* qsort comparator used below */ -cmpcategorysize (const void *a, const void *b) -{ - if (*(const void **) a == NULL) - return 1; - if (*(const void **) b == NULL) - return -1; - return ((*(const struct locale_category_data **) a)->size - - (*(const struct locale_category_data **) b)->size); -} - -/* Check the content of the archive for duplicates. Add the content - of the files if necessary. Returns the locrec_offset. */ -static uint32_t -add_locale (struct locarhandle *ah, - const char *name, locale_data_t data, bool replace) -{ - /* First look for the name. If it already exists and we are not - supposed to replace it don't do anything. If it does not exist - we have to allocate a new locale record. */ - size_t name_len = strlen (name); - uint32_t file_offsets[__LC_LAST]; - unsigned int num_new_offsets = 0; - struct sumhashent *sumhashtab; - uint32_t hval; - unsigned int cnt, idx; - struct locarhead *head; - struct namehashent *namehashent; - unsigned int incr; - struct locrecent *locrecent; - off64_t lastoffset; - char *ptr; - struct locale_category_data *size_order[__LC_LAST]; - /* Page size alignment is a minor optimization for locality; use a - common value here rather than making the localedef output depend - on the page size of the system on which localedef is run. See - <https://sourceware.org/glibc/wiki/Development_Todo/Master#Locale_archive_alignment> - for more discussion. */ - const size_t pagesz = 4096; - int small_mask; - - head = ah->addr; - sumhashtab = (struct sumhashent *) ((char *) ah->addr - + GET (head->sumhash_offset)); - - memset (file_offsets, 0, sizeof (file_offsets)); - - size_order[LC_ALL] = NULL; - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - size_order[cnt] = &data[cnt]; - - /* Sort the array in ascending order of data size. */ - qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize); - - small_mask = 0; - data[LC_ALL].size = 0; - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (size_order[cnt] != NULL) - { - const size_t rounded_size = (size_order[cnt]->size + 15) & -16; - if (data[LC_ALL].size + rounded_size > 2 * pagesz) - { - /* This category makes the small-categories block - stop being small, so this is the end of the road. */ - do - size_order[cnt++] = NULL; - while (cnt < __LC_LAST); - break; - } - data[LC_ALL].size += rounded_size; - small_mask |= 1 << (size_order[cnt] - data); - } - - /* Copy the data for all the small categories into the LC_ALL - pseudo-category. */ - - data[LC_ALL].addr = alloca (data[LC_ALL].size); - memset (data[LC_ALL].addr, 0, data[LC_ALL].size); - - ptr = data[LC_ALL].addr; - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (small_mask & (1 << cnt)) - { - memcpy (ptr, data[cnt].addr, data[cnt].size); - ptr += (data[cnt].size + 15) & -16; - } - __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum); - - /* For each locale category data set determine whether the same data - is already somewhere in the archive. */ - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt))) - { - ++num_new_offsets; - - /* Compute the hash value of the checksum to determine a - starting point for the search in the MD5 hash value - table. */ - hval = archive_hashval (data[cnt].sum, 16); - - idx = hval % GET (head->sumhash_size); - incr = 1 + hval % (GET (head->sumhash_size) - 2); - - while (GET (sumhashtab[idx].file_offset) != 0) - { - if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0) - { - /* Check the content, there could be a collision of - the hash sum. - - Unfortunately the sumhashent record does not include - the size of the stored data. So we have to search for - it. */ - locrecent - = (struct locrecent *) ((char *) ah->addr - + GET (head->locrectab_offset)); - size_t iloc; - for (iloc = 0; iloc < GET (head->locrectab_used); ++iloc) - if (GET (locrecent[iloc].refs) != 0 - && (GET (locrecent[iloc].record[cnt].offset) - == GET (sumhashtab[idx].file_offset))) - break; - - if (iloc != GET (head->locrectab_used) - && data[cnt].size == GET (locrecent[iloc].record[cnt].len) - /* We have to compare the content. Either we can - have the data mmaped or we have to read from - the file. */ - && (file_data_available_p - (ah, GET (sumhashtab[idx].file_offset), - data[cnt].size) - ? memcmp (data[cnt].addr, - (char *) ah->addr - + GET (sumhashtab[idx].file_offset), - data[cnt].size) == 0 - : compare_from_file (ah, data[cnt].addr, - GET (sumhashtab[idx].file_offset), - data[cnt].size) == 0)) - { - /* Found it. */ - file_offsets[cnt] = GET (sumhashtab[idx].file_offset); - --num_new_offsets; - break; - } - } - - idx += incr; - if (idx >= GET (head->sumhash_size)) - idx -= GET (head->sumhash_size); - } - } - - /* Find a slot for the locale name in the hash table. */ - namehashent = insert_name (ah, name, name_len, replace); - if (namehashent == NULL) /* Already exists and !REPLACE. */ - return 0; - - /* Determine whether we have to resize the file. */ - if ((100 * (GET (head->sumhash_used) + num_new_offsets) - > 75 * GET (head->sumhash_size)) - || (GET (namehashent->locrec_offset) == 0 - && (GET (head->locrectab_used) == GET (head->locrectab_size) - || (GET (head->string_used) + name_len + 1 - > GET (head->string_size)) - || (100 * GET (head->namehash_used) - > 75 * GET (head->namehash_size))))) - { - /* The current archive is not large enough. */ - enlarge_archive (ah, head); - return add_locale (ah, name, data, replace); - } - - /* Add the locale data which is not yet in the archive. */ - for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt) - if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt))) - && file_offsets[cnt] == 0) - { - /* The data for this section is not yet available in the - archive. Append it. */ - off64_t lastpos; - uint32_t md5hval; - - lastpos = lseek64 (ah->fd, 0, SEEK_END); - if (lastpos == (off64_t) -1) - error (EXIT_FAILURE, errno, _("cannot add to locale archive")); - - /* If block of small categories would cross page boundary, - align it unless it immediately follows a large category. */ - if (cnt == LC_ALL && lastoffset != lastpos - && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1) - & -pagesz) - > ((data[cnt].size + pagesz - 1) & -pagesz))) - { - size_t sz = pagesz - (lastpos & (pagesz - 1)); - char *zeros = alloca (sz); - - memset (zeros, 0, sz); - if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz)) - error (EXIT_FAILURE, errno, - _("cannot add to locale archive")); - - lastpos += sz; - } - - /* Align all data to a 16 byte boundary. */ - if ((lastpos & 15) != 0) - { - static const char zeros[15] = { 0, }; - - if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15))) - != 16 - (lastpos & 15)) - error (EXIT_FAILURE, errno, _("cannot add to locale archive")); - - lastpos += 16 - (lastpos & 15); - } - - /* Remember the position. */ - file_offsets[cnt] = lastpos; - lastoffset = lastpos + data[cnt].size; - - /* Write the data. */ - if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size)) - != data[cnt].size) - error (EXIT_FAILURE, errno, _("cannot add to locale archive")); - - /* Add the hash value to the hash table. */ - md5hval = archive_hashval (data[cnt].sum, 16); - - idx = md5hval % GET (head->sumhash_size); - incr = 1 + md5hval % (GET (head->sumhash_size) - 2); - - while (GET (sumhashtab[idx].file_offset) != 0) - { - idx += incr; - if (idx >= GET (head->sumhash_size)) - idx -= GET (head->sumhash_size); - } - - memcpy (sumhashtab[idx].sum, data[cnt].sum, 16); - SET (sumhashtab[idx].file_offset, file_offsets[cnt]); - - INC (head->sumhash_used, 1); - } - - lastoffset = file_offsets[LC_ALL]; - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (small_mask & (1 << cnt)) - { - file_offsets[cnt] = lastoffset; - lastoffset += (data[cnt].size + 15) & -16; - } - - if (GET (namehashent->name_offset) == 0) - { - /* Add the name string. */ - memcpy ((char *) ah->addr + GET (head->string_offset) - + GET (head->string_used), - name, name_len + 1); - SET (namehashent->name_offset, - GET (head->string_offset) + GET (head->string_used)); - INC (head->string_used, name_len + 1); - INC (head->namehash_used, 1); - } - - if (GET (namehashent->locrec_offset == 0)) - { - /* Allocate a name location record. */ - SET (namehashent->locrec_offset, (GET (head->locrectab_offset) - + (GET (head->locrectab_used) - * sizeof (struct locrecent)))); - INC (head->locrectab_used, 1); - locrecent = (struct locrecent *) ((char *) ah->addr - + GET (namehashent->locrec_offset)); - SET (locrecent->refs, 1); - } - else - { - /* If there are other aliases pointing to this locrecent, - we still need a new one. If not, reuse the old one. */ - - locrecent = (struct locrecent *) ((char *) ah->addr - + GET (namehashent->locrec_offset)); - if (GET (locrecent->refs) > 1) - { - INC (locrecent->refs, -1); - SET (namehashent->locrec_offset, (GET (head->locrectab_offset) - + (GET (head->locrectab_used) - * sizeof (struct locrecent)))); - INC (head->locrectab_used, 1); - locrecent - = (struct locrecent *) ((char *) ah->addr - + GET (namehashent->locrec_offset)); - SET (locrecent->refs, 1); - } - } - - /* Fill in the table with the locations of the locale data. */ - for (cnt = 0; cnt < __LC_LAST; ++cnt) - { - SET (locrecent->record[cnt].offset, file_offsets[cnt]); - SET (locrecent->record[cnt].len, data[cnt].size); - } - - return GET (namehashent->locrec_offset); -} - - -/* Check the content of the archive for duplicates. Add the content - of the files if necessary. Add all the names, possibly overwriting - old files. */ -int -add_locale_to_archive (struct locarhandle *ah, const char *name, - locale_data_t data, bool replace) -{ - char *normalized_name = NULL; - uint32_t locrec_offset; - - /* First analyze the name to decide how to archive it. */ - const char *language; - const char *modifier; - const char *territory; - const char *codeset; - const char *normalized_codeset; - int mask = _nl_explode_name (strdupa (name), - &language, &modifier, &territory, - &codeset, &normalized_codeset); - if (mask == -1) - return -1; - - if (mask & XPG_NORM_CODESET) - /* This name contains a codeset in unnormalized form. - We will store it in the archive with a normalized name. */ - asprintf (&normalized_name, "%s%s%s.%s%s%s", - language, territory == NULL ? "" : "_", territory ?: "", - (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset, - modifier == NULL ? "" : "@", modifier ?: ""); - - /* This call does the main work. */ - locrec_offset = add_locale (ah, normalized_name ?: name, data, replace); - if (locrec_offset == 0) - { - free (normalized_name); - if (mask & XPG_NORM_CODESET) - free ((char *) normalized_codeset); - return -1; - } - - if ((mask & XPG_CODESET) == 0) - { - /* This name lacks a codeset, so determine the locale's codeset and - add an alias for its name with normalized codeset appended. */ - - const struct - { - unsigned int magic; - unsigned int nstrings; - unsigned int strindex[0]; - } *filedata = data[LC_CTYPE].addr; - codeset = (char *) filedata - + maybe_swap_uint32 (filedata->strindex[_NL_ITEM_INDEX - (_NL_CTYPE_CODESET_NAME)]); - char *normalized_codeset_name = NULL; - - normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset)); - mask |= XPG_NORM_CODESET; - - asprintf (&normalized_codeset_name, "%s%s%s.%s%s%s", - language, territory == NULL ? "" : "_", territory ?: "", - normalized_codeset, - modifier == NULL ? "" : "@", modifier ?: ""); - - add_alias (ah, normalized_codeset_name, replace, - normalized_name ?: name, &locrec_offset); - free (normalized_codeset_name); - } - - /* Now read the locale.alias files looking for lines whose - right hand side matches our name after normalization. */ - int result = 0; - if (alias_file != NULL) - { - FILE *fp; - fp = fopen (alias_file, "rm"); - if (fp == NULL) - error (1, errno, _("locale alias file `%s' not found"), - alias_file); - - /* No threads present. */ - __fsetlocking (fp, FSETLOCKING_BYCALLER); - - while (! feof_unlocked (fp)) - { - /* It is a reasonable approach to use a fix buffer here - because - a) we are only interested in the first two fields - b) these fields must be usable as file names and so must - not be that long */ - char buf[BUFSIZ]; - char *alias; - char *value; - char *cp; - - if (fgets_unlocked (buf, BUFSIZ, fp) == NULL) - /* EOF reached. */ - break; - - cp = buf; - /* Ignore leading white space. */ - while (isspace (cp[0]) && cp[0] != '\n') - ++cp; - - /* A leading '#' signals a comment line. */ - if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n') - { - alias = cp++; - while (cp[0] != '\0' && !isspace (cp[0])) - ++cp; - /* Terminate alias name. */ - if (cp[0] != '\0') - *cp++ = '\0'; - - /* Now look for the beginning of the value. */ - while (isspace (cp[0])) - ++cp; - - if (cp[0] != '\0') - { - value = cp++; - while (cp[0] != '\0' && !isspace (cp[0])) - ++cp; - /* Terminate value. */ - if (cp[0] == '\n') - { - /* This has to be done to make the following - test for the end of line possible. We are - looking for the terminating '\n' which do not - overwrite here. */ - *cp++ = '\0'; - *cp = '\n'; - } - else if (cp[0] != '\0') - *cp++ = '\0'; - - /* Does this alias refer to our locale? We will - normalize the right hand side and compare the - elements of the normalized form. */ - { - const char *rhs_language; - const char *rhs_modifier; - const char *rhs_territory; - const char *rhs_codeset; - const char *rhs_normalized_codeset; - int rhs_mask = _nl_explode_name (value, - &rhs_language, - &rhs_modifier, - &rhs_territory, - &rhs_codeset, - &rhs_normalized_codeset); - if (rhs_mask == -1) - { - result = -1; - goto out; - } - if (!strcmp (language, rhs_language) - && ((rhs_mask & XPG_CODESET) - /* He has a codeset, it must match normalized. */ - ? !strcmp ((mask & XPG_NORM_CODESET) - ? normalized_codeset : codeset, - (rhs_mask & XPG_NORM_CODESET) - ? rhs_normalized_codeset : rhs_codeset) - /* He has no codeset, we must also have none. */ - : (mask & XPG_CODESET) == 0) - /* Codeset (or lack thereof) matches. */ - && !strcmp (territory ?: "", rhs_territory ?: "") - && !strcmp (modifier ?: "", rhs_modifier ?: "")) - /* We have a winner. */ - add_alias (ah, alias, replace, - normalized_name ?: name, &locrec_offset); - if (rhs_mask & XPG_NORM_CODESET) - free ((char *) rhs_normalized_codeset); - } - } - } - - /* Possibly not the whole line fits into the buffer. - Ignore the rest of the line. */ - while (strchr (cp, '\n') == NULL) - { - cp = buf; - if (fgets_unlocked (buf, BUFSIZ, fp) == NULL) - /* Make sure the inner loop will be left. The outer - loop will exit at the `feof' test. */ - *cp = '\n'; - } - } - - out: - fclose (fp); - } - - free (normalized_name); - - if (mask & XPG_NORM_CODESET) - free ((char *) normalized_codeset); - - return result; -} - - -int -add_locales_to_archive (size_t nlist, char *list[], bool replace) -{ - struct locarhandle ah; - int result = 0; - - /* Open the archive. This call never returns if we cannot - successfully open the archive. */ - ah.fname = NULL; - open_archive (&ah, false); - - while (nlist-- > 0) - { - const char *fname = *list++; - size_t fnamelen = strlen (fname); - struct stat64 st; - DIR *dirp; - struct dirent64 *d; - int seen; - locale_data_t data; - int cnt; - - if (! be_quiet) - printf (_("Adding %s\n"), fname); - - /* First see whether this really is a directory and whether it - contains all the require locale category files. */ - if (stat64 (fname, &st) < 0) - { - error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname, - strerror (errno)); - continue; - } - if (!S_ISDIR (st.st_mode)) - { - error (0, 0, _("\"%s\" is no directory; ignored"), fname); - continue; - } - - dirp = opendir (fname); - if (dirp == NULL) - { - error (0, 0, _("cannot open directory \"%s\": %s: ignored"), - fname, strerror (errno)); - continue; - } - - seen = 0; - while ((d = readdir64 (dirp)) != NULL) - { - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - if (strcmp (d->d_name, locnames[cnt]) == 0) - { - unsigned char d_type; - - /* We have an object of the required name. If it's - a directory we have to look at a file with the - prefix "SYS_". Otherwise we have found what we - are looking for. */ -#ifdef _DIRENT_HAVE_D_TYPE - d_type = d->d_type; - - if (d_type != DT_REG) -#endif - { - char fullname[fnamelen + 2 * strlen (d->d_name) + 7]; - -#ifdef _DIRENT_HAVE_D_TYPE - if (d_type == DT_UNKNOWN) -#endif - { - strcpy (stpcpy (stpcpy (fullname, fname), "/"), - d->d_name); - - if (stat64 (fullname, &st) == -1) - /* We cannot stat the file, ignore it. */ - break; - - d_type = IFTODT (st.st_mode); - } - - if (d_type == DT_DIR) - { - /* We have to do more tests. The file is a - directory and it therefore must contain a - regular file with the same name except a - "SYS_" prefix. */ - char *t = stpcpy (stpcpy (fullname, fname), "/"); - strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"), - d->d_name); - - if (stat64 (fullname, &st) == -1) - /* There is no SYS_* file or we cannot - access it. */ - break; - - d_type = IFTODT (st.st_mode); - } - } - - /* If we found a regular file (eventually after - following a symlink) we are successful. */ - if (d_type == DT_REG) - ++seen; - break; - } - } - - closedir (dirp); - - if (seen != __LC_LAST - 1) - { - /* We don't have all locale category files. Ignore the name. */ - error (0, 0, _("incomplete set of locale files in \"%s\""), - fname); - continue; - } - - /* Add the files to the archive. To do this we first compute - sizes and the MD5 sums of all the files. */ - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - { - char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7]; - int fd; - - strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]); - fd = open64 (fullname, O_RDONLY); - if (fd == -1 || fstat64 (fd, &st) == -1) - { - /* Cannot read the file. */ - if (fd != -1) - close (fd); - break; - } - - if (S_ISDIR (st.st_mode)) - { - char *t; - close (fd); - t = stpcpy (stpcpy (fullname, fname), "/"); - strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"), - locnames[cnt]); - - fd = open64 (fullname, O_RDONLY); - if (fd == -1 || fstat64 (fd, &st) == -1 - || !S_ISREG (st.st_mode)) - { - if (fd != -1) - close (fd); - break; - } - } - - /* Map the file. */ - data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED, - fd, 0); - if (data[cnt].addr == MAP_FAILED) - { - /* Cannot map it. */ - close (fd); - break; - } - - data[cnt].size = st.st_size; - __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum); - - /* We don't need the file descriptor anymore. */ - close (fd); - } - - if (cnt != __LC_LAST) - { - while (cnt-- > 0) - if (cnt != LC_ALL) - munmap (data[cnt].addr, data[cnt].size); - - error (0, 0, _("cannot read all files in \"%s\": ignored"), fname); - - continue; - } - - result |= add_locale_to_archive (&ah, basename (fname), data, replace); - - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - munmap (data[cnt].addr, data[cnt].size); - } - - /* We are done. */ - close_archive (&ah); - - return result; -} - - -int -delete_locales_from_archive (size_t nlist, char *list[]) -{ - struct locarhandle ah; - struct locarhead *head; - struct namehashent *namehashtab; - - /* Open the archive. This call never returns if we cannot - successfully open the archive. */ - ah.fname = NULL; - open_archive (&ah, false); - - head = ah.addr; - namehashtab = (struct namehashent *) ((char *) ah.addr - + GET (head->namehash_offset)); - - while (nlist-- > 0) - { - const char *locname = *list++; - uint32_t hval; - unsigned int idx; - unsigned int incr; - - /* Search for this locale in the archive. */ - hval = archive_hashval (locname, strlen (locname)); - - idx = hval % GET (head->namehash_size); - incr = 1 + hval % (GET (head->namehash_size) - 2); - - /* If the name_offset field is zero this means this is no - deleted entry and therefore no entry can be found. */ - while (GET (namehashtab[idx].name_offset) != 0) - { - if (GET (namehashtab[idx].hashval) == hval - && (strcmp (locname, - ((char *) ah.addr - + GET (namehashtab[idx].name_offset))) - == 0)) - { - /* Found the entry. Now mark it as removed by zero-ing - the reference to the locale record. */ - SET (namehashtab[idx].locrec_offset, 0); - break; - } - - idx += incr; - if (idx >= GET (head->namehash_size)) - idx -= GET (head->namehash_size); - } - - if (GET (namehashtab[idx].name_offset) == 0 && ! be_quiet) - error (0, 0, _("locale \"%s\" not in archive"), locname); - } - - close_archive (&ah); - - return 0; -} - - -struct nameent -{ - char *name; - uint32_t locrec_offset; -}; - - -struct dataent -{ - const unsigned char *sum; - uint32_t file_offset; - uint32_t nlink; -}; - - -static int -nameentcmp (const void *a, const void *b) -{ - return strcmp (((const struct nameent *) a)->name, - ((const struct nameent *) b)->name); -} - - -static int -dataentcmp (const void *a, const void *b) -{ - if (((const struct dataent *) a)->file_offset - < ((const struct dataent *) b)->file_offset) - return -1; - - if (((const struct dataent *) a)->file_offset - > ((const struct dataent *) b)->file_offset) - return 1; - - return 0; -} - - -void -show_archive_content (const char *fname, int verbose) -{ - struct locarhandle ah; - struct locarhead *head; - struct namehashent *namehashtab; - struct nameent *names; - size_t cnt, used; - - /* Open the archive. This call never returns if we cannot - successfully open the archive. */ - ah.fname = fname; - open_archive (&ah, true); - - head = ah.addr; - - names = (struct nameent *) xmalloc (GET (head->namehash_used) - * sizeof (struct nameent)); - - namehashtab = (struct namehashent *) ((char *) ah.addr - + GET (head->namehash_offset)); - for (cnt = used = 0; cnt < GET (head->namehash_size); ++cnt) - if (GET (namehashtab[cnt].locrec_offset) != 0) - { - assert (used < GET (head->namehash_used)); - names[used].name = ah.addr + GET (namehashtab[cnt].name_offset); - names[used++].locrec_offset = GET (namehashtab[cnt].locrec_offset); - } - - /* Sort the names. */ - qsort (names, used, sizeof (struct nameent), nameentcmp); - - if (verbose) - { - struct dataent *files; - struct sumhashent *sumhashtab; - int sumused; - - files = (struct dataent *) xmalloc (GET (head->sumhash_used) - * sizeof (struct dataent)); - - sumhashtab = (struct sumhashent *) ((char *) ah.addr - + GET (head->sumhash_offset)); - for (cnt = sumused = 0; cnt < GET (head->sumhash_size); ++cnt) - if (GET (sumhashtab[cnt].file_offset) != 0) - { - assert (sumused < GET (head->sumhash_used)); - files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum; - files[sumused].file_offset = GET (sumhashtab[cnt].file_offset); - files[sumused++].nlink = 0; - } - - /* Sort by file locations. */ - qsort (files, sumused, sizeof (struct dataent), dataentcmp); - - /* Compute nlink fields. */ - for (cnt = 0; cnt < used; ++cnt) - { - struct locrecent *locrec; - int idx; - - locrec = (struct locrecent *) ((char *) ah.addr - + names[cnt].locrec_offset); - for (idx = 0; idx < __LC_LAST; ++idx) - if (GET (locrec->record[LC_ALL].offset) != 0 - ? (idx == LC_ALL - || (GET (locrec->record[idx].offset) - < GET (locrec->record[LC_ALL].offset)) - || ((GET (locrec->record[idx].offset) - + GET (locrec->record[idx].len)) - > (GET (locrec->record[LC_ALL].offset) - + GET (locrec->record[LC_ALL].len)))) - : idx != LC_ALL) - { - struct dataent *data, dataent; - - dataent.file_offset = GET (locrec->record[idx].offset); - data = (struct dataent *) bsearch (&dataent, files, sumused, - sizeof (struct dataent), - dataentcmp); - assert (data != NULL); - ++data->nlink; - } - } - - /* Print it. */ - for (cnt = 0; cnt < used; ++cnt) - { - struct locrecent *locrec; - int idx, i; - - locrec = (struct locrecent *) ((char *) ah.addr - + names[cnt].locrec_offset); - for (idx = 0; idx < __LC_LAST; ++idx) - if (idx != LC_ALL) - { - struct dataent *data, dataent; - - dataent.file_offset = GET (locrec->record[idx].offset); - if (GET (locrec->record[LC_ALL].offset) != 0 - && (dataent.file_offset - >= GET (locrec->record[LC_ALL].offset)) - && (dataent.file_offset + GET (locrec->record[idx].len) - <= (GET (locrec->record[LC_ALL].offset) - + GET (locrec->record[LC_ALL].len)))) - dataent.file_offset = GET (locrec->record[LC_ALL].offset); - - data = (struct dataent *) bsearch (&dataent, files, sumused, - sizeof (struct dataent), - dataentcmp); - printf ("%6d %7x %3d%c ", - GET (locrec->record[idx].len), - GET (locrec->record[idx].offset), - data->nlink, - (dataent.file_offset - == GET (locrec->record[LC_ALL].offset)) - ? '+' : ' '); - for (i = 0; i < 16; i += 4) - printf ("%02x%02x%02x%02x", - data->sum[i], data->sum[i + 1], - data->sum[i + 2], data->sum[i + 3]); - printf (" %s/%s\n", names[cnt].name, - idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES" - : locnames[idx]); - } - } - } - else - for (cnt = 0; cnt < used; ++cnt) - puts (names[cnt].name); - - close_archive (&ah); - - exit (EXIT_SUCCESS); -} |