Updated to fedora-glibc-20090427T1419
[glibc/history.git] / locale / programs / locarchive.c
bloba58505852d86b0e9117f3e9a197dd8ce7997ee31
1 /* Copyright (C) 2002, 2003, 2005, 2007, 2009 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #include <assert.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <error.h>
27 #include <fcntl.h>
28 #include <inttypes.h>
29 #include <libintl.h>
30 #include <locale.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdio_ext.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
42 #include "../../crypt/md5.h"
43 #include "../localeinfo.h"
44 #include "../locarchive.h"
45 #include "localedef.h"
47 /* Define the hash function. We define the function as static inline.
48 We must change the name so as not to conflict with simple-hash.h. */
49 #define compute_hashval static inline archive_hashval
50 #define hashval_t uint32_t
51 #include "hashval.h"
52 #undef compute_hashval
54 extern const char *output_prefix;
56 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
58 static const char *locnames[] =
60 #define DEFINE_CATEGORY(category, category_name, items, a) \
61 [category] = category_name,
62 #include "categories.def"
63 #undef DEFINE_CATEGORY
67 /* Size of the initial archive header. */
68 #define INITIAL_NUM_NAMES 900
69 #define INITIAL_SIZE_STRINGS 7500
70 #define INITIAL_NUM_LOCREC 420
71 #define INITIAL_NUM_SUMS 2000
74 /* Size of the reserved address space area. */
75 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
78 static void
79 create_archive (const char *archivefname, struct locarhandle *ah)
81 int fd;
82 char fname[strlen (archivefname) + sizeof (".XXXXXX")];
83 struct locarhead head;
84 void *p;
85 size_t total;
87 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
89 /* Create a temporary file in the correct directory. */
90 fd = mkstemp (fname);
91 if (fd == -1)
92 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
94 /* Create the initial content of the archive. */
95 head.magic = AR_MAGIC;
96 head.serial = 0;
97 head.namehash_offset = sizeof (struct locarhead);
98 head.namehash_used = 0;
99 head.namehash_size = next_prime (INITIAL_NUM_NAMES);
101 head.string_offset = (head.namehash_offset
102 + head.namehash_size * sizeof (struct namehashent));
103 head.string_used = 0;
104 head.string_size = INITIAL_SIZE_STRINGS;
106 head.locrectab_offset = head.string_offset + head.string_size;
107 head.locrectab_used = 0;
108 head.locrectab_size = INITIAL_NUM_LOCREC;
110 head.sumhash_offset = (head.locrectab_offset
111 + head.locrectab_size * sizeof (struct locrecent));
112 head.sumhash_used = 0;
113 head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
115 total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
117 /* Write out the header and create room for the other data structures. */
118 if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
120 int errval = errno;
121 unlink (fname);
122 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
125 if (ftruncate64 (fd, total) != 0)
127 int errval = errno;
128 unlink (fname);
129 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
132 /* To prepare for enlargements of the mmaped area reserve some
133 address space. */
134 size_t reserved = RESERVE_MMAP_SIZE;
135 int xflags = 0;
136 if (total < reserved
137 && ((p = mmap64 (NULL, reserved, PROT_NONE, MAP_PRIVATE | MAP_ANON,
138 -1, 0)) != MAP_FAILED))
139 xflags = MAP_FIXED;
140 else
142 p = NULL;
143 reserved = total;
146 /* Map the header and all the administration data structures. */
147 p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0);
148 if (p == MAP_FAILED)
150 int errval = errno;
151 unlink (fname);
152 error (EXIT_FAILURE, errval, _("cannot map archive header"));
155 /* Now try to rename it. We don't use the rename function since
156 this would overwrite a file which has been created in
157 parallel. */
158 if (link (fname, archivefname) == -1)
160 int errval = errno;
162 /* We cannot use the just created file. */
163 close (fd);
164 unlink (fname);
166 if (errval == EEXIST)
168 /* There is already an archive. Must have been a localedef run
169 which happened in parallel. Simply open this file then. */
170 open_archive (ah, false);
171 return;
174 error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
177 /* Remove the temporary name. */
178 unlink (fname);
180 /* Make the file globally readable. */
181 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
183 int errval = errno;
184 unlink (archivefname);
185 error (EXIT_FAILURE, errval,
186 _("cannot change mode of new locale archive"));
189 ah->fd = fd;
190 ah->addr = p;
191 ah->mmaped = total;
192 ah->reserved = reserved;
196 /* This structure and qsort comparator function are used below to sort an
197 old archive's locrec table in order of data position in the file. */
198 struct oldlocrecent
200 unsigned int cnt;
201 struct locrecent *locrec;
204 static int
205 oldlocrecentcmp (const void *a, const void *b)
207 struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
208 struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
209 uint32_t start_a = -1, end_a = 0;
210 uint32_t start_b = -1, end_b = 0;
211 int cnt;
213 for (cnt = 0; cnt < __LC_LAST; ++cnt)
214 if (cnt != LC_ALL)
216 if (la->record[cnt].offset < start_a)
217 start_a = la->record[cnt].offset;
218 if (la->record[cnt].offset + la->record[cnt].len > end_a)
219 end_a = la->record[cnt].offset + la->record[cnt].len;
221 assert (start_a != (uint32_t)-1);
222 assert (end_a != 0);
224 for (cnt = 0; cnt < __LC_LAST; ++cnt)
225 if (cnt != LC_ALL)
227 if (lb->record[cnt].offset < start_b)
228 start_b = lb->record[cnt].offset;
229 if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
230 end_b = lb->record[cnt].offset + lb->record[cnt].len;
232 assert (start_b != (uint32_t)-1);
233 assert (end_b != 0);
235 if (start_a != start_b)
236 return (int)start_a - (int)start_b;
237 return (int)end_a - (int)end_b;
241 /* forward decls for below */
242 static uint32_t add_locale (struct locarhandle *ah, const char *name,
243 locale_data_t data, bool replace);
244 void add_alias (struct locarhandle *ah, const char *alias,
245 bool replace, const char *oldname,
246 uint32_t *locrec_offset_p);
249 static bool
250 file_data_available_p (struct locarhandle *ah, uint32_t offset, uint32_t size)
252 if (offset < ah->mmaped && offset + size <= ah->mmaped)
253 return true;
255 struct stat64 st;
256 if (fstat64 (ah->fd, &st) != 0)
257 return false;
259 if (st.st_size > ah->reserved)
260 return false;
262 const size_t pagesz = getpagesize ();
263 size_t start = ah->mmaped & ~(pagesz - 1);
264 void *p = mmap64 (ah->addr + start, st.st_size - start,
265 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
266 ah->fd, start);
267 if (p == MAP_FAILED)
269 ah->mmaped = start;
270 return false;
273 ah->mmaped = st.st_size;
274 return true;
278 static int
279 compare_from_file (struct locarhandle *ah, void *p1, uint32_t offset2,
280 uint32_t size)
282 void *p2 = xmalloc (size);
283 if (pread (ah->fd, p2, size, offset2) != size)
284 WITH_CUR_LOCALE (error (4, errno,
285 _("cannot read data from locale archive")));
287 int res = memcmp (p1, p2, size);
288 free (p2);
289 return res;
293 static void
294 enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
296 struct stat64 st;
297 int fd;
298 struct locarhead newhead;
299 size_t total;
300 void *p;
301 unsigned int cnt, loccnt;
302 struct namehashent *oldnamehashtab;
303 struct locrecent *oldlocrectab;
304 struct locarhandle new_ah;
305 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
306 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
307 char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
309 if (output_prefix)
310 memcpy (archivefname, output_prefix, prefix_len);
311 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
312 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
314 /* Not all of the old file has to be mapped. Change this now this
315 we will have to access the whole content. */
316 if (fstat64 (ah->fd, &st) != 0)
317 enomap:
318 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
320 if (st.st_size < ah->reserved)
321 ah->addr = mmap64 (ah->addr, st.st_size, PROT_READ | PROT_WRITE,
322 MAP_SHARED | MAP_FIXED, ah->fd, 0);
323 else
325 munmap (ah->addr, ah->reserved);
326 ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
327 MAP_SHARED, ah->fd, 0);
328 ah->reserved = st.st_size;
329 head = ah->addr;
331 if (ah->addr == MAP_FAILED)
332 goto enomap;
333 ah->mmaped = st.st_size;
335 /* Create a temporary file in the correct directory. */
336 fd = mkstemp (fname);
337 if (fd == -1)
338 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
340 /* Copy the existing head information. */
341 newhead = *head;
343 /* Create the new archive header. The sizes of the various tables
344 should be double from what is currently used. */
345 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
346 newhead.namehash_size);
347 if (verbose)
348 printf ("name: size: %u, used: %d, new: size: %u\n",
349 head->namehash_size, head->namehash_used, newhead.namehash_size);
351 newhead.string_offset = (newhead.namehash_offset
352 + (newhead.namehash_size
353 * sizeof (struct namehashent)));
354 /* Keep the string table size aligned to 4 bytes, so that
355 all the struct { uint32_t } types following are happy. */
356 newhead.string_size = MAX ((2 * newhead.string_used + 3) & -4,
357 newhead.string_size);
359 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
360 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
361 newhead.locrectab_size);
363 newhead.sumhash_offset = (newhead.locrectab_offset
364 + (newhead.locrectab_size
365 * sizeof (struct locrecent)));
366 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
367 newhead.sumhash_size);
369 total = (newhead.sumhash_offset
370 + newhead.sumhash_size * sizeof (struct sumhashent));
372 /* The new file is empty now. */
373 newhead.namehash_used = 0;
374 newhead.string_used = 0;
375 newhead.locrectab_used = 0;
376 newhead.sumhash_used = 0;
378 /* Write out the header and create room for the other data structures. */
379 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
380 != sizeof (newhead))
382 int errval = errno;
383 unlink (fname);
384 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
387 if (ftruncate64 (fd, total) != 0)
389 int errval = errno;
390 unlink (fname);
391 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
394 /* To prepare for enlargements of the mmaped area reserve some
395 address space. */
396 size_t reserved = RESERVE_MMAP_SIZE;
397 int xflags = 0;
398 if (total < reserved
399 && ((p = mmap64 (NULL, reserved, PROT_NONE, MAP_PRIVATE | MAP_ANON,
400 -1, 0)) != MAP_FAILED))
401 xflags = MAP_FIXED;
402 else
404 p = NULL;
405 reserved = total;
408 /* Map the header and all the administration data structures. */
409 p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0);
410 if (p == MAP_FAILED)
412 int errval = errno;
413 unlink (fname);
414 error (EXIT_FAILURE, errval, _("cannot map archive header"));
417 /* Lock the new file. */
418 if (lockf64 (fd, F_LOCK, total) != 0)
420 int errval = errno;
421 unlink (fname);
422 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
425 new_ah.mmaped = total;
426 new_ah.addr = p;
427 new_ah.fd = fd;
428 new_ah.reserved = reserved;
430 /* Walk through the hash name hash table to find out what data is
431 still referenced and transfer it into the new file. */
432 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
433 + head->namehash_offset);
434 oldlocrectab = (struct locrecent *) ((char *) ah->addr
435 + head->locrectab_offset);
437 /* Sort the old locrec table in order of data position. */
438 struct oldlocrecent oldlocrecarray[head->namehash_size];
439 for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
440 if (oldnamehashtab[cnt].locrec_offset != 0)
442 oldlocrecarray[loccnt].cnt = cnt;
443 oldlocrecarray[loccnt++].locrec
444 = (struct locrecent *) ((char *) ah->addr
445 + oldnamehashtab[cnt].locrec_offset);
447 qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
448 oldlocrecentcmp);
450 uint32_t last_locrec_offset = 0;
451 for (cnt = 0; cnt < loccnt; ++cnt)
453 /* Insert this entry in the new hash table. */
454 locale_data_t old_data;
455 unsigned int idx;
456 struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
458 for (idx = 0; idx < __LC_LAST; ++idx)
459 if (idx != LC_ALL)
461 old_data[idx].size = oldlocrec->record[idx].len;
462 old_data[idx].addr
463 = ((char *) ah->addr + oldlocrec->record[idx].offset);
465 __md5_buffer (old_data[idx].addr, old_data[idx].size,
466 old_data[idx].sum);
469 if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec)
471 const char *oldname
472 = ((char *) ah->addr
473 + oldnamehashtab[oldlocrecarray[cnt - 1].cnt].name_offset);
475 add_alias (&new_ah,
476 ((char *) ah->addr
477 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
478 0, oldname, &last_locrec_offset);
479 continue;
482 last_locrec_offset =
483 add_locale (&new_ah,
484 ((char *) ah->addr
485 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
486 old_data, 0);
487 if (last_locrec_offset == 0)
488 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
491 /* Make the file globally readable. */
492 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
494 int errval = errno;
495 unlink (fname);
496 error (EXIT_FAILURE, errval,
497 _("cannot change mode of resized locale archive"));
500 /* Rename the new file. */
501 if (rename (fname, archivefname) != 0)
503 int errval = errno;
504 unlink (fname);
505 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
508 /* Close the old file. */
509 close_archive (ah);
511 /* Add the information for the new one. */
512 *ah = new_ah;
516 void
517 open_archive (struct locarhandle *ah, bool readonly)
519 struct stat64 st;
520 struct stat64 st2;
521 int fd;
522 struct locarhead head;
523 int retry = 0;
524 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
525 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
527 if (output_prefix)
528 memcpy (archivefname, output_prefix, prefix_len);
529 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
531 while (1)
533 /* Open the archive. We must have exclusive write access. */
534 fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
535 if (fd == -1)
537 /* Maybe the file does not yet exist. */
538 if (errno == ENOENT)
540 if (readonly)
542 static const struct locarhead nullhead =
544 .namehash_used = 0,
545 .namehash_offset = 0,
546 .namehash_size = 0
549 ah->addr = (void *) &nullhead;
550 ah->fd = -1;
552 else
553 create_archive (archivefname, ah);
555 return;
557 else
558 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
559 archivefname);
562 if (fstat64 (fd, &st) < 0)
563 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
564 archivefname);
566 if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
568 close (fd);
570 if (retry++ < max_locarchive_open_retry)
572 struct timespec req;
574 /* Wait for a bit. */
575 req.tv_sec = 0;
576 req.tv_nsec = 1000000 * (random () % 500 + 1);
577 (void) nanosleep (&req, NULL);
579 continue;
582 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
583 archivefname);
586 /* One more check. Maybe another process replaced the archive file
587 with a new, larger one since we opened the file. */
588 if (stat64 (archivefname, &st2) == -1
589 || st.st_dev != st2.st_dev
590 || st.st_ino != st2.st_ino)
592 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
593 close (fd);
594 continue;
597 /* Leave the loop. */
598 break;
601 /* Read the header. */
602 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
604 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
605 error (EXIT_FAILURE, errno, _("cannot read archive header"));
608 ah->fd = fd;
609 ah->mmaped = st.st_size;
611 /* To prepare for enlargements of the mmaped area reserve some
612 address space. */
613 size_t reserved = RESERVE_MMAP_SIZE;
614 int xflags = 0;
615 void *p;
616 if (st.st_size < reserved
617 && ((p = mmap64 (NULL, reserved, PROT_NONE, MAP_PRIVATE | MAP_ANON,
618 -1, 0)) != MAP_FAILED))
619 xflags = MAP_FIXED;
620 else
622 p = NULL;
623 reserved = st.st_size;
626 /* Map the entire file. We might need to compare the category data
627 in the file with the newly added data. */
628 ah->addr = mmap64 (p, st.st_size, PROT_READ | (readonly ? 0 : PROT_WRITE),
629 MAP_SHARED | xflags, fd, 0);
630 if (ah->addr == MAP_FAILED)
632 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
633 error (EXIT_FAILURE, errno, _("cannot map archive header"));
635 ah->reserved = reserved;
639 void
640 close_archive (struct locarhandle *ah)
642 if (ah->fd != -1)
644 munmap (ah->addr, ah->reserved);
645 close (ah->fd);
649 #include "../../intl/explodename.c"
650 #include "../../intl/l10nflist.c"
652 struct namehashent *
653 insert_name (struct locarhandle *ah,
654 const char *name, size_t name_len, bool replace)
656 const struct locarhead *const head = ah->addr;
657 struct namehashent *namehashtab
658 = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
659 unsigned int insert_idx, idx, incr;
661 /* Hash value of the locale name. */
662 uint32_t hval = archive_hashval (name, name_len);
664 insert_idx = -1;
665 idx = hval % head->namehash_size;
666 incr = 1 + hval % (head->namehash_size - 2);
668 /* If the name_offset field is zero this means this is a
669 deleted entry and therefore no entry can be found. */
670 while (namehashtab[idx].name_offset != 0)
672 if (namehashtab[idx].hashval == hval
673 && strcmp (name,
674 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
676 /* Found the entry. */
677 if (namehashtab[idx].locrec_offset != 0 && ! replace)
679 if (! be_quiet)
680 error (0, 0, _("locale '%s' already exists"), name);
681 return NULL;
684 break;
687 if (namehashtab[idx].hashval == hval && ! be_quiet)
689 error (0, 0, "hash collision (%u) %s, %s",
690 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
693 /* Remember the first place we can insert the new entry. */
694 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
695 insert_idx = idx;
697 idx += incr;
698 if (idx >= head->namehash_size)
699 idx -= head->namehash_size;
702 /* Add as early as possible. */
703 if (insert_idx != -1)
704 idx = insert_idx;
706 namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
707 return &namehashtab[idx];
710 void
711 add_alias (struct locarhandle *ah, const char *alias, bool replace,
712 const char *oldname, uint32_t *locrec_offset_p)
714 uint32_t locrec_offset = *locrec_offset_p;
715 struct locarhead *head = ah->addr;
716 const size_t name_len = strlen (alias);
717 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
718 replace);
719 if (namehashent == NULL && ! replace)
720 return;
722 if (namehashent->name_offset == 0)
724 /* We are adding a new hash entry for this alias.
725 Determine whether we have to resize the file. */
726 if (head->string_used + name_len + 1 > head->string_size
727 || 100 * head->namehash_used > 75 * head->namehash_size)
729 /* The current archive is not large enough. */
730 enlarge_archive (ah, head);
732 /* The locrecent might have moved, so we have to look up
733 the old name afresh. */
734 namehashent = insert_name (ah, oldname, strlen (oldname), true);
735 assert (namehashent->name_offset != 0);
736 assert (namehashent->locrec_offset != 0);
737 *locrec_offset_p = namehashent->locrec_offset;
739 /* Tail call to try the whole thing again. */
740 add_alias (ah, alias, replace, oldname, locrec_offset_p);
741 return;
744 /* Add the name string. */
745 memcpy (ah->addr + head->string_offset + head->string_used,
746 alias, name_len + 1);
747 namehashent->name_offset = head->string_offset + head->string_used;
748 head->string_used += name_len + 1;
750 ++head->namehash_used;
753 if (namehashent->locrec_offset != 0)
755 /* Replacing an existing entry.
756 Mark that we are no longer using the old locrecent. */
757 struct locrecent *locrecent
758 = (struct locrecent *) ((char *) ah->addr
759 + namehashent->locrec_offset);
760 --locrecent->refs;
763 /* Point this entry at the locrecent installed for the main name. */
764 namehashent->locrec_offset = locrec_offset;
767 static int /* qsort comparator used below */
768 cmpcategorysize (const void *a, const void *b)
770 if (*(const void **) a == NULL)
771 return 1;
772 if (*(const void **) b == NULL)
773 return -1;
774 return ((*(const struct locale_category_data **) a)->size
775 - (*(const struct locale_category_data **) b)->size);
778 /* Check the content of the archive for duplicates. Add the content
779 of the files if necessary. Returns the locrec_offset. */
780 static uint32_t
781 add_locale (struct locarhandle *ah,
782 const char *name, locale_data_t data, bool replace)
784 /* First look for the name. If it already exists and we are not
785 supposed to replace it don't do anything. If it does not exist
786 we have to allocate a new locale record. */
787 size_t name_len = strlen (name);
788 uint32_t file_offsets[__LC_LAST];
789 unsigned int num_new_offsets = 0;
790 struct sumhashent *sumhashtab;
791 uint32_t hval;
792 unsigned int cnt, idx;
793 struct locarhead *head;
794 struct namehashent *namehashent;
795 unsigned int incr;
796 struct locrecent *locrecent;
797 off64_t lastoffset;
798 char *ptr;
799 struct locale_category_data *size_order[__LC_LAST];
800 const size_t pagesz = getpagesize ();
801 int small_mask;
803 head = ah->addr;
804 sumhashtab = (struct sumhashent *) ((char *) ah->addr
805 + head->sumhash_offset);
807 memset (file_offsets, 0, sizeof (file_offsets));
809 size_order[LC_ALL] = NULL;
810 for (cnt = 0; cnt < __LC_LAST; ++cnt)
811 if (cnt != LC_ALL)
812 size_order[cnt] = &data[cnt];
814 /* Sort the array in ascending order of data size. */
815 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
817 small_mask = 0;
818 data[LC_ALL].size = 0;
819 for (cnt = 0; cnt < __LC_LAST; ++cnt)
820 if (size_order[cnt] != NULL)
822 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
823 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
825 /* This category makes the small-categories block
826 stop being small, so this is the end of the road. */
828 size_order[cnt++] = NULL;
829 while (cnt < __LC_LAST);
830 break;
832 data[LC_ALL].size += rounded_size;
833 small_mask |= 1 << (size_order[cnt] - data);
836 /* Copy the data for all the small categories into the LC_ALL
837 pseudo-category. */
839 data[LC_ALL].addr = alloca (data[LC_ALL].size);
840 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
842 ptr = data[LC_ALL].addr;
843 for (cnt = 0; cnt < __LC_LAST; ++cnt)
844 if (small_mask & (1 << cnt))
846 memcpy (ptr, data[cnt].addr, data[cnt].size);
847 ptr += (data[cnt].size + 15) & -16;
849 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
851 /* For each locale category data set determine whether the same data
852 is already somewhere in the archive. */
853 for (cnt = 0; cnt < __LC_LAST; ++cnt)
854 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
856 ++num_new_offsets;
858 /* Compute the hash value of the checksum to determine a
859 starting point for the search in the MD5 hash value
860 table. */
861 hval = archive_hashval (data[cnt].sum, 16);
863 idx = hval % head->sumhash_size;
864 incr = 1 + hval % (head->sumhash_size - 2);
866 while (sumhashtab[idx].file_offset != 0)
868 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
870 /* Check the content, there could be a collision of
871 the hash sum.
873 Unfortunately the sumhashent record does not include
874 the size of the stored data. So we have to search for
875 it. */
876 locrecent = (struct locrecent *) ((char *) ah->addr
877 + head->locrectab_offset);
878 size_t iloc;
879 for (iloc = 0; iloc < head->locrectab_used; ++iloc)
880 if (locrecent[iloc].refs != 0
881 && (locrecent[iloc].record[cnt].offset
882 == sumhashtab[idx].file_offset))
883 break;
885 if (iloc != head->locrectab_used
886 && data[cnt].size == locrecent[iloc].record[cnt].len
887 /* We have to compare the content. Either we can
888 have the data mmaped or we have to read from
889 the file. */
890 && (file_data_available_p (ah, sumhashtab[idx].file_offset,
891 data[cnt].size)
892 ? memcmp (data[cnt].addr,
893 (char *) ah->addr
894 + sumhashtab[idx].file_offset,
895 data[cnt].size) == 0
896 : compare_from_file (ah, data[cnt].addr,
897 sumhashtab[idx].file_offset,
898 data[cnt].size) == 0))
900 /* Found it. */
901 file_offsets[cnt] = sumhashtab[idx].file_offset;
902 --num_new_offsets;
903 break;
907 idx += incr;
908 if (idx >= head->sumhash_size)
909 idx -= head->sumhash_size;
913 /* Find a slot for the locale name in the hash table. */
914 namehashent = insert_name (ah, name, name_len, replace);
915 if (namehashent == NULL) /* Already exists and !REPLACE. */
916 return 0;
918 /* Determine whether we have to resize the file. */
919 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
920 || (namehashent->locrec_offset == 0
921 && (head->locrectab_used == head->locrectab_size
922 || head->string_used + name_len + 1 > head->string_size
923 || 100 * head->namehash_used > 75 * head->namehash_size)))
925 /* The current archive is not large enough. */
926 enlarge_archive (ah, head);
927 return add_locale (ah, name, data, replace);
930 /* Add the locale data which is not yet in the archive. */
931 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
932 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
933 && file_offsets[cnt] == 0)
935 /* The data for this section is not yet available in the
936 archive. Append it. */
937 off64_t lastpos;
938 uint32_t md5hval;
940 lastpos = lseek64 (ah->fd, 0, SEEK_END);
941 if (lastpos == (off64_t) -1)
942 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
944 /* If block of small categories would cross page boundary,
945 align it unless it immediately follows a large category. */
946 if (cnt == LC_ALL && lastoffset != lastpos
947 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
948 & -pagesz)
949 > ((data[cnt].size + pagesz - 1) & -pagesz)))
951 size_t sz = pagesz - (lastpos & (pagesz - 1));
952 char *zeros = alloca (sz);
954 memset (zeros, 0, sz);
955 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
956 error (EXIT_FAILURE, errno,
957 _("cannot add to locale archive"));
959 lastpos += sz;
962 /* Align all data to a 16 byte boundary. */
963 if ((lastpos & 15) != 0)
965 static const char zeros[15] = { 0, };
967 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
968 != 16 - (lastpos & 15))
969 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
971 lastpos += 16 - (lastpos & 15);
974 /* Remember the position. */
975 file_offsets[cnt] = lastpos;
976 lastoffset = lastpos + data[cnt].size;
978 /* Write the data. */
979 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
980 != data[cnt].size)
981 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
983 /* Add the hash value to the hash table. */
984 md5hval = archive_hashval (data[cnt].sum, 16);
986 idx = md5hval % head->sumhash_size;
987 incr = 1 + md5hval % (head->sumhash_size - 2);
989 while (sumhashtab[idx].file_offset != 0)
991 idx += incr;
992 if (idx >= head->sumhash_size)
993 idx -= head->sumhash_size;
996 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
997 sumhashtab[idx].file_offset = file_offsets[cnt];
999 ++head->sumhash_used;
1002 lastoffset = file_offsets[LC_ALL];
1003 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1004 if (small_mask & (1 << cnt))
1006 file_offsets[cnt] = lastoffset;
1007 lastoffset += (data[cnt].size + 15) & -16;
1010 if (namehashent->name_offset == 0)
1012 /* Add the name string. */
1013 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
1014 name, name_len + 1);
1015 namehashent->name_offset = head->string_offset + head->string_used;
1016 head->string_used += name_len + 1;
1017 ++head->namehash_used;
1020 if (namehashent->locrec_offset == 0)
1022 /* Allocate a name location record. */
1023 namehashent->locrec_offset = (head->locrectab_offset
1024 + (head->locrectab_used++
1025 * sizeof (struct locrecent)));
1026 locrecent = (struct locrecent *) ((char *) ah->addr
1027 + namehashent->locrec_offset);
1028 locrecent->refs = 1;
1030 else
1032 /* If there are other aliases pointing to this locrecent,
1033 we still need a new one. If not, reuse the old one. */
1035 locrecent = (struct locrecent *) ((char *) ah->addr
1036 + namehashent->locrec_offset);
1037 if (locrecent->refs > 1)
1039 --locrecent->refs;
1040 namehashent->locrec_offset = (head->locrectab_offset
1041 + (head->locrectab_used++
1042 * sizeof (struct locrecent)));
1043 locrecent = (struct locrecent *) ((char *) ah->addr
1044 + namehashent->locrec_offset);
1045 locrecent->refs = 1;
1049 /* Fill in the table with the locations of the locale data. */
1050 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1052 locrecent->record[cnt].offset = file_offsets[cnt];
1053 locrecent->record[cnt].len = data[cnt].size;
1056 return namehashent->locrec_offset;
1060 /* Check the content of the archive for duplicates. Add the content
1061 of the files if necessary. Add all the names, possibly overwriting
1062 old files. */
1064 add_locale_to_archive (ah, name, data, replace)
1065 struct locarhandle *ah;
1066 const char *name;
1067 locale_data_t data;
1068 bool replace;
1070 char *normalized_name = NULL;
1071 uint32_t locrec_offset;
1073 /* First analyze the name to decide how to archive it. */
1074 const char *language;
1075 const char *modifier;
1076 const char *territory;
1077 const char *codeset;
1078 const char *normalized_codeset;
1079 int mask = _nl_explode_name (strdupa (name),
1080 &language, &modifier, &territory,
1081 &codeset, &normalized_codeset);
1083 if (mask & XPG_NORM_CODESET)
1084 /* This name contains a codeset in unnormalized form.
1085 We will store it in the archive with a normalized name. */
1086 asprintf (&normalized_name, "%s%s%s.%s%s%s",
1087 language, territory == NULL ? "" : "_", territory ?: "",
1088 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
1089 modifier == NULL ? "" : "@", modifier ?: "");
1091 /* This call does the main work. */
1092 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
1093 if (locrec_offset == 0)
1095 free (normalized_name);
1096 if (mask & XPG_NORM_CODESET)
1097 free ((char *) normalized_codeset);
1098 return -1;
1101 if ((mask & XPG_CODESET) == 0)
1103 /* This name lacks a codeset, so determine the locale's codeset and
1104 add an alias for its name with normalized codeset appended. */
1106 const struct
1108 unsigned int magic;
1109 unsigned int nstrings;
1110 unsigned int strindex[0];
1111 } *filedata = data[LC_CTYPE].addr;
1112 codeset = (char *) filedata
1113 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
1114 char *normalized_codeset_name = NULL;
1116 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
1117 mask |= XPG_NORM_CODESET;
1119 asprintf (&normalized_codeset_name, "%s%s%s.%s%s%s",
1120 language, territory == NULL ? "" : "_", territory ?: "",
1121 normalized_codeset,
1122 modifier == NULL ? "" : "@", modifier ?: "");
1124 add_alias (ah, normalized_codeset_name, replace,
1125 normalized_name ?: name, &locrec_offset);
1126 free (normalized_codeset_name);
1129 /* Now read the locale.alias files looking for lines whose
1130 right hand side matches our name after normalization. */
1131 if (alias_file != NULL)
1133 FILE *fp;
1134 fp = fopen (alias_file, "rm");
1135 if (fp == NULL)
1136 error (1, errno, _("locale alias file `%s' not found"),
1137 alias_file);
1139 /* No threads present. */
1140 __fsetlocking (fp, FSETLOCKING_BYCALLER);
1142 while (! feof_unlocked (fp))
1144 /* It is a reasonable approach to use a fix buffer here
1145 because
1146 a) we are only interested in the first two fields
1147 b) these fields must be usable as file names and so must
1148 not be that long */
1149 char buf[BUFSIZ];
1150 char *alias;
1151 char *value;
1152 char *cp;
1154 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1155 /* EOF reached. */
1156 break;
1158 cp = buf;
1159 /* Ignore leading white space. */
1160 while (isspace (cp[0]) && cp[0] != '\n')
1161 ++cp;
1163 /* A leading '#' signals a comment line. */
1164 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
1166 alias = cp++;
1167 while (cp[0] != '\0' && !isspace (cp[0]))
1168 ++cp;
1169 /* Terminate alias name. */
1170 if (cp[0] != '\0')
1171 *cp++ = '\0';
1173 /* Now look for the beginning of the value. */
1174 while (isspace (cp[0]))
1175 ++cp;
1177 if (cp[0] != '\0')
1179 value = cp++;
1180 while (cp[0] != '\0' && !isspace (cp[0]))
1181 ++cp;
1182 /* Terminate value. */
1183 if (cp[0] == '\n')
1185 /* This has to be done to make the following
1186 test for the end of line possible. We are
1187 looking for the terminating '\n' which do not
1188 overwrite here. */
1189 *cp++ = '\0';
1190 *cp = '\n';
1192 else if (cp[0] != '\0')
1193 *cp++ = '\0';
1195 /* Does this alias refer to our locale? We will
1196 normalize the right hand side and compare the
1197 elements of the normalized form. */
1199 const char *rhs_language;
1200 const char *rhs_modifier;
1201 const char *rhs_territory;
1202 const char *rhs_codeset;
1203 const char *rhs_normalized_codeset;
1204 int rhs_mask = _nl_explode_name (value,
1205 &rhs_language,
1206 &rhs_modifier,
1207 &rhs_territory,
1208 &rhs_codeset,
1209 &rhs_normalized_codeset);
1210 if (!strcmp (language, rhs_language)
1211 && ((rhs_mask & XPG_CODESET)
1212 /* He has a codeset, it must match normalized. */
1213 ? !strcmp ((mask & XPG_NORM_CODESET)
1214 ? normalized_codeset : codeset,
1215 (rhs_mask & XPG_NORM_CODESET)
1216 ? rhs_normalized_codeset : rhs_codeset)
1217 /* He has no codeset, we must also have none. */
1218 : (mask & XPG_CODESET) == 0)
1219 /* Codeset (or lack thereof) matches. */
1220 && !strcmp (territory ?: "", rhs_territory ?: "")
1221 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1222 /* We have a winner. */
1223 add_alias (ah, alias, replace,
1224 normalized_name ?: name, &locrec_offset);
1225 if (rhs_mask & XPG_NORM_CODESET)
1226 free ((char *) rhs_normalized_codeset);
1231 /* Possibly not the whole line fits into the buffer.
1232 Ignore the rest of the line. */
1233 while (strchr (cp, '\n') == NULL)
1235 cp = buf;
1236 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1237 /* Make sure the inner loop will be left. The outer
1238 loop will exit at the `feof' test. */
1239 *cp = '\n';
1243 fclose (fp);
1246 free (normalized_name);
1248 if (mask & XPG_NORM_CODESET)
1249 free ((char *) normalized_codeset);
1251 return 0;
1256 add_locales_to_archive (nlist, list, replace)
1257 size_t nlist;
1258 char *list[];
1259 bool replace;
1261 struct locarhandle ah;
1262 int result = 0;
1264 /* Open the archive. This call never returns if we cannot
1265 successfully open the archive. */
1266 open_archive (&ah, false);
1268 while (nlist-- > 0)
1270 const char *fname = *list++;
1271 size_t fnamelen = strlen (fname);
1272 struct stat64 st;
1273 DIR *dirp;
1274 struct dirent64 *d;
1275 int seen;
1276 locale_data_t data;
1277 int cnt;
1279 if (! be_quiet)
1280 printf (_("Adding %s\n"), fname);
1282 /* First see whether this really is a directory and whether it
1283 contains all the require locale category files. */
1284 if (stat64 (fname, &st) < 0)
1286 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1287 strerror (errno));
1288 continue;
1290 if (!S_ISDIR (st.st_mode))
1292 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1293 continue;
1296 dirp = opendir (fname);
1297 if (dirp == NULL)
1299 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1300 fname, strerror (errno));
1301 continue;
1304 seen = 0;
1305 while ((d = readdir64 (dirp)) != NULL)
1307 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1308 if (cnt != LC_ALL)
1309 if (strcmp (d->d_name, locnames[cnt]) == 0)
1311 unsigned char d_type;
1313 /* We have an object of the required name. If it's
1314 a directory we have to look at a file with the
1315 prefix "SYS_". Otherwise we have found what we
1316 are looking for. */
1317 #ifdef _DIRENT_HAVE_D_TYPE
1318 d_type = d->d_type;
1320 if (d_type != DT_REG)
1321 #endif
1323 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1325 #ifdef _DIRENT_HAVE_D_TYPE
1326 if (d_type == DT_UNKNOWN)
1327 #endif
1329 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1330 d->d_name);
1332 if (stat64 (fullname, &st) == -1)
1333 /* We cannot stat the file, ignore it. */
1334 break;
1336 d_type = IFTODT (st.st_mode);
1339 if (d_type == DT_DIR)
1341 /* We have to do more tests. The file is a
1342 directory and it therefore must contain a
1343 regular file with the same name except a
1344 "SYS_" prefix. */
1345 char *t = stpcpy (stpcpy (fullname, fname), "/");
1346 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
1347 d->d_name);
1349 if (stat64 (fullname, &st) == -1)
1350 /* There is no SYS_* file or we cannot
1351 access it. */
1352 break;
1354 d_type = IFTODT (st.st_mode);
1358 /* If we found a regular file (eventually after
1359 following a symlink) we are successful. */
1360 if (d_type == DT_REG)
1361 ++seen;
1362 break;
1366 closedir (dirp);
1368 if (seen != __LC_LAST - 1)
1370 /* We don't have all locale category files. Ignore the name. */
1371 error (0, 0, _("incomplete set of locale files in \"%s\""),
1372 fname);
1373 continue;
1376 /* Add the files to the archive. To do this we first compute
1377 sizes and the MD5 sums of all the files. */
1378 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1379 if (cnt != LC_ALL)
1381 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1382 int fd;
1384 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1385 fd = open64 (fullname, O_RDONLY);
1386 if (fd == -1 || fstat64 (fd, &st) == -1)
1388 /* Cannot read the file. */
1389 if (fd != -1)
1390 close (fd);
1391 break;
1394 if (S_ISDIR (st.st_mode))
1396 char *t;
1397 close (fd);
1398 t = stpcpy (stpcpy (fullname, fname), "/");
1399 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
1400 locnames[cnt]);
1402 fd = open64 (fullname, O_RDONLY);
1403 if (fd == -1 || fstat64 (fd, &st) == -1
1404 || !S_ISREG (st.st_mode))
1406 if (fd != -1)
1407 close (fd);
1408 break;
1412 /* Map the file. */
1413 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1414 fd, 0);
1415 if (data[cnt].addr == MAP_FAILED)
1417 /* Cannot map it. */
1418 close (fd);
1419 break;
1422 data[cnt].size = st.st_size;
1423 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1425 /* We don't need the file descriptor anymore. */
1426 close (fd);
1429 if (cnt != __LC_LAST)
1431 while (cnt-- > 0)
1432 if (cnt != LC_ALL)
1433 munmap (data[cnt].addr, data[cnt].size);
1435 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1437 continue;
1440 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1442 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1443 if (cnt != LC_ALL)
1444 munmap (data[cnt].addr, data[cnt].size);
1447 /* We are done. */
1448 close_archive (&ah);
1450 return result;
1455 delete_locales_from_archive (nlist, list)
1456 size_t nlist;
1457 char *list[];
1459 struct locarhandle ah;
1460 struct locarhead *head;
1461 struct namehashent *namehashtab;
1463 /* Open the archive. This call never returns if we cannot
1464 successfully open the archive. */
1465 open_archive (&ah, false);
1467 head = ah.addr;
1468 namehashtab = (struct namehashent *) ((char *) ah.addr
1469 + head->namehash_offset);
1471 while (nlist-- > 0)
1473 const char *locname = *list++;
1474 uint32_t hval;
1475 unsigned int idx;
1476 unsigned int incr;
1478 /* Search for this locale in the archive. */
1479 hval = archive_hashval (locname, strlen (locname));
1481 idx = hval % head->namehash_size;
1482 incr = 1 + hval % (head->namehash_size - 2);
1484 /* If the name_offset field is zero this means this is no
1485 deleted entry and therefore no entry can be found. */
1486 while (namehashtab[idx].name_offset != 0)
1488 if (namehashtab[idx].hashval == hval
1489 && (strcmp (locname,
1490 (char *) ah.addr + namehashtab[idx].name_offset)
1491 == 0))
1493 /* Found the entry. Now mark it as removed by zero-ing
1494 the reference to the locale record. */
1495 namehashtab[idx].locrec_offset = 0;
1496 break;
1499 idx += incr;
1500 if (idx >= head->namehash_size)
1501 idx -= head->namehash_size;
1504 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1505 error (0, 0, _("locale \"%s\" not in archive"), locname);
1508 close_archive (&ah);
1510 return 0;
1514 struct nameent
1516 char *name;
1517 uint32_t locrec_offset;
1521 struct dataent
1523 const unsigned char *sum;
1524 uint32_t file_offset;
1525 uint32_t nlink;
1529 static int
1530 nameentcmp (const void *a, const void *b)
1532 return strcmp (((const struct nameent *) a)->name,
1533 ((const struct nameent *) b)->name);
1537 static int
1538 dataentcmp (const void *a, const void *b)
1540 if (((const struct dataent *) a)->file_offset
1541 < ((const struct dataent *) b)->file_offset)
1542 return -1;
1544 if (((const struct dataent *) a)->file_offset
1545 > ((const struct dataent *) b)->file_offset)
1546 return 1;
1548 return 0;
1552 void
1553 show_archive_content (int verbose)
1555 struct locarhandle ah;
1556 struct locarhead *head;
1557 struct namehashent *namehashtab;
1558 struct nameent *names;
1559 size_t cnt, used;
1561 /* Open the archive. This call never returns if we cannot
1562 successfully open the archive. */
1563 open_archive (&ah, true);
1565 head = ah.addr;
1567 names = (struct nameent *) xmalloc (head->namehash_used
1568 * sizeof (struct nameent));
1570 namehashtab = (struct namehashent *) ((char *) ah.addr
1571 + head->namehash_offset);
1572 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1573 if (namehashtab[cnt].locrec_offset != 0)
1575 assert (used < head->namehash_used);
1576 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1577 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
1580 /* Sort the names. */
1581 qsort (names, used, sizeof (struct nameent), nameentcmp);
1583 if (verbose)
1585 struct dataent *files;
1586 struct sumhashent *sumhashtab;
1587 int sumused;
1589 files = (struct dataent *) xmalloc (head->sumhash_used
1590 * sizeof (struct dataent));
1592 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1593 + head->sumhash_offset);
1594 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1595 if (sumhashtab[cnt].file_offset != 0)
1597 assert (sumused < head->sumhash_used);
1598 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1599 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1600 files[sumused++].nlink = 0;
1603 /* Sort by file locations. */
1604 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1606 /* Compute nlink fields. */
1607 for (cnt = 0; cnt < used; ++cnt)
1609 struct locrecent *locrec;
1610 int idx;
1612 locrec = (struct locrecent *) ((char *) ah.addr
1613 + names[cnt].locrec_offset);
1614 for (idx = 0; idx < __LC_LAST; ++idx)
1615 if (locrec->record[LC_ALL].offset != 0
1616 ? (idx == LC_ALL
1617 || (locrec->record[idx].offset
1618 < locrec->record[LC_ALL].offset)
1619 || (locrec->record[idx].offset + locrec->record[idx].len
1620 > (locrec->record[LC_ALL].offset
1621 + locrec->record[LC_ALL].len)))
1622 : idx != LC_ALL)
1624 struct dataent *data, dataent;
1626 dataent.file_offset = locrec->record[idx].offset;
1627 data = (struct dataent *) bsearch (&dataent, files, sumused,
1628 sizeof (struct dataent),
1629 dataentcmp);
1630 assert (data != NULL);
1631 ++data->nlink;
1635 /* Print it. */
1636 for (cnt = 0; cnt < used; ++cnt)
1638 struct locrecent *locrec;
1639 int idx, i;
1641 locrec = (struct locrecent *) ((char *) ah.addr
1642 + names[cnt].locrec_offset);
1643 for (idx = 0; idx < __LC_LAST; ++idx)
1644 if (idx != LC_ALL)
1646 struct dataent *data, dataent;
1648 dataent.file_offset = locrec->record[idx].offset;
1649 if (locrec->record[LC_ALL].offset != 0
1650 && dataent.file_offset >= locrec->record[LC_ALL].offset
1651 && (dataent.file_offset + locrec->record[idx].len
1652 <= (locrec->record[LC_ALL].offset
1653 + locrec->record[LC_ALL].len)))
1654 dataent.file_offset = locrec->record[LC_ALL].offset;
1656 data = (struct dataent *) bsearch (&dataent, files, sumused,
1657 sizeof (struct dataent),
1658 dataentcmp);
1659 printf ("%6d %7x %3d%c ",
1660 locrec->record[idx].len, locrec->record[idx].offset,
1661 data->nlink,
1662 dataent.file_offset == locrec->record[LC_ALL].offset
1663 ? '+' : ' ');
1664 for (i = 0; i < 16; i += 4)
1665 printf ("%02x%02x%02x%02x",
1666 data->sum[i], data->sum[i + 1],
1667 data->sum[i + 2], data->sum[i + 3]);
1668 printf (" %s/%s\n", names[cnt].name,
1669 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1670 : locnames[idx]);
1674 else
1675 for (cnt = 0; cnt < used; ++cnt)
1676 puts (names[cnt].name);
1678 close_archive (&ah);
1680 exit (EXIT_SUCCESS);