Updated to fedora-glibc-20090424T1908
[glibc/history.git] / locale / programs / locarchive.c
blobe2ccda01b74d6d6ece745c17df945b77ebff1481
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_ANON, -1, 0))
138 != 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 void *p = mremap (ah->addr, ah->mmaped, st.st_size,
263 MREMAP_FIXED | MREMAP_MAYMOVE, ah->addr);
264 if (p == MAP_FAILED)
265 return false;
267 ah->mmaped = st.st_size;
268 return true;
272 static int
273 compare_from_file (struct locarhandle *ah, void *p1, uint32_t offset2,
274 uint32_t size)
276 void *p2 = xmalloc (size);
277 if (pread (ah->fd, p2, size, offset2) != size)
278 WITH_CUR_LOCALE (error (4, errno,
279 _("cannot read data from locale archive")));
281 int res = memcmp (p1, p2, size);
282 free (p2);
283 return res;
287 static void
288 enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
290 struct stat64 st;
291 int fd;
292 struct locarhead newhead;
293 size_t total;
294 void *p;
295 unsigned int cnt, loccnt;
296 struct namehashent *oldnamehashtab;
297 struct locrecent *oldlocrectab;
298 struct locarhandle new_ah;
299 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
300 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
301 char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
303 if (output_prefix)
304 memcpy (archivefname, output_prefix, prefix_len);
305 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
306 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
308 /* Not all of the old file has to be mapped. Change this now this
309 we will have to access the whole content. */
310 if (fstat64 (ah->fd, &st) != 0)
311 enomap:
312 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
314 if (st.st_size < ah->reserved)
315 ah->addr = mremap (ah->addr, ah->mmaped, st.st_size,
316 MREMAP_MAYMOVE | MREMAP_FIXED, ah->addr);
317 else
319 munmap (ah->addr, ah->reserved);
320 ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
321 MAP_SHARED, ah->fd, 0);
322 ah->reserved = st.st_size;
324 if (ah->addr == MAP_FAILED)
325 goto enomap;
326 ah->mmaped = st.st_size;
328 /* Create a temporary file in the correct directory. */
329 fd = mkstemp (fname);
330 if (fd == -1)
331 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
333 /* Copy the existing head information. */
334 newhead = *head;
336 /* Create the new archive header. The sizes of the various tables
337 should be double from what is currently used. */
338 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
339 newhead.namehash_size);
340 if (verbose)
341 printf ("name: size: %u, used: %d, new: size: %u\n",
342 head->namehash_size, head->namehash_used, newhead.namehash_size);
344 newhead.string_offset = (newhead.namehash_offset
345 + (newhead.namehash_size
346 * sizeof (struct namehashent)));
347 /* Keep the string table size aligned to 4 bytes, so that
348 all the struct { uint32_t } types following are happy. */
349 newhead.string_size = MAX ((2 * newhead.string_used + 3) & -4,
350 newhead.string_size);
352 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
353 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
354 newhead.locrectab_size);
356 newhead.sumhash_offset = (newhead.locrectab_offset
357 + (newhead.locrectab_size
358 * sizeof (struct locrecent)));
359 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
360 newhead.sumhash_size);
362 total = (newhead.sumhash_offset
363 + newhead.sumhash_size * sizeof (struct sumhashent));
365 /* The new file is empty now. */
366 newhead.namehash_used = 0;
367 newhead.string_used = 0;
368 newhead.locrectab_used = 0;
369 newhead.sumhash_used = 0;
371 /* Write out the header and create room for the other data structures. */
372 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
373 != sizeof (newhead))
375 int errval = errno;
376 unlink (fname);
377 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
380 if (ftruncate64 (fd, total) != 0)
382 int errval = errno;
383 unlink (fname);
384 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
387 /* Map the header and all the administration data structures. */
388 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
389 if (p == MAP_FAILED)
391 int errval = errno;
392 unlink (fname);
393 error (EXIT_FAILURE, errval, _("cannot map archive header"));
396 /* Lock the new file. */
397 if (lockf64 (fd, F_LOCK, total) != 0)
399 int errval = errno;
400 unlink (fname);
401 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
404 new_ah.mmaped = total;
405 new_ah.addr = p;
406 new_ah.fd = fd;
407 new_ah.reserved = total;
409 /* Walk through the hash name hash table to find out what data is
410 still referenced and transfer it into the new file. */
411 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
412 + head->namehash_offset);
413 oldlocrectab = (struct locrecent *) ((char *) ah->addr
414 + head->locrectab_offset);
416 /* Sort the old locrec table in order of data position. */
417 struct oldlocrecent oldlocrecarray[head->namehash_size];
418 for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
419 if (oldnamehashtab[cnt].locrec_offset != 0)
421 oldlocrecarray[loccnt].cnt = cnt;
422 oldlocrecarray[loccnt++].locrec
423 = (struct locrecent *) ((char *) ah->addr
424 + oldnamehashtab[cnt].locrec_offset);
426 qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
427 oldlocrecentcmp);
429 uint32_t last_locrec_offset = 0;
430 for (cnt = 0; cnt < loccnt; ++cnt)
432 /* Insert this entry in the new hash table. */
433 locale_data_t old_data;
434 unsigned int idx;
435 struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
437 for (idx = 0; idx < __LC_LAST; ++idx)
438 if (idx != LC_ALL)
440 old_data[idx].size = oldlocrec->record[idx].len;
441 old_data[idx].addr
442 = ((char *) ah->addr + oldlocrec->record[idx].offset);
444 __md5_buffer (old_data[idx].addr, old_data[idx].size,
445 old_data[idx].sum);
448 if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec)
450 const char *oldname
451 = ((char *) ah->addr
452 + oldnamehashtab[oldlocrecarray[cnt - 1].cnt].name_offset);
454 add_alias (&new_ah,
455 ((char *) ah->addr
456 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
457 0, oldname, &last_locrec_offset);
458 continue;
461 last_locrec_offset =
462 add_locale (&new_ah,
463 ((char *) ah->addr
464 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
465 old_data, 0);
466 if (last_locrec_offset == 0)
467 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
470 /* Make the file globally readable. */
471 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
473 int errval = errno;
474 unlink (fname);
475 error (EXIT_FAILURE, errval,
476 _("cannot change mode of resized locale archive"));
479 /* Rename the new file. */
480 if (rename (fname, archivefname) != 0)
482 int errval = errno;
483 unlink (fname);
484 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
487 /* Close the old file. */
488 close_archive (ah);
490 /* Add the information for the new one. */
491 *ah = new_ah;
495 void
496 open_archive (struct locarhandle *ah, bool readonly)
498 struct stat64 st;
499 struct stat64 st2;
500 int fd;
501 struct locarhead head;
502 int retry = 0;
503 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
504 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
506 if (output_prefix)
507 memcpy (archivefname, output_prefix, prefix_len);
508 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
510 while (1)
512 /* Open the archive. We must have exclusive write access. */
513 fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
514 if (fd == -1)
516 /* Maybe the file does not yet exist. */
517 if (errno == ENOENT)
519 if (readonly)
521 static const struct locarhead nullhead =
523 .namehash_used = 0,
524 .namehash_offset = 0,
525 .namehash_size = 0
528 ah->addr = (void *) &nullhead;
529 ah->fd = -1;
531 else
532 create_archive (archivefname, ah);
534 return;
536 else
537 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
538 archivefname);
541 if (fstat64 (fd, &st) < 0)
542 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
543 archivefname);
545 if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
547 close (fd);
549 if (retry++ < max_locarchive_open_retry)
551 struct timespec req;
553 /* Wait for a bit. */
554 req.tv_sec = 0;
555 req.tv_nsec = 1000000 * (random () % 500 + 1);
556 (void) nanosleep (&req, NULL);
558 continue;
561 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
562 archivefname);
565 /* One more check. Maybe another process replaced the archive file
566 with a new, larger one since we opened the file. */
567 if (stat64 (archivefname, &st2) == -1
568 || st.st_dev != st2.st_dev
569 || st.st_ino != st2.st_ino)
571 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
572 close (fd);
573 continue;
576 /* Leave the loop. */
577 break;
580 /* Read the header. */
581 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
583 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
584 error (EXIT_FAILURE, errno, _("cannot read archive header"));
587 ah->fd = fd;
588 ah->mmaped = st.st_size;
590 /* To prepare for enlargements of the mmaped area reserve some
591 address space. */
592 size_t reserved = RESERVE_MMAP_SIZE;
593 int xflags = 0;
594 void *p;
595 if (st.st_size < reserved
596 && ((p = mmap64 (NULL, RESERVE_MMAP_SIZE, PROT_NONE, MAP_ANON, -1, 0))
597 != MAP_FAILED))
598 xflags = MAP_FIXED;
599 else
601 p = NULL;
602 reserved = st.st_size;
605 /* Map the entire file. We might need to compare the category data
606 in the file with the newly added data. */
607 ah->addr = mmap64 (p, st.st_size, PROT_READ | (readonly ? 0 : PROT_WRITE),
608 MAP_SHARED | xflags, fd, 0);
609 if (ah->addr == MAP_FAILED)
611 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
612 error (EXIT_FAILURE, errno, _("cannot map archive header"));
614 ah->reserved = reserved;
618 void
619 close_archive (struct locarhandle *ah)
621 if (ah->fd != -1)
623 munmap (ah->addr, ah->reserved);
624 close (ah->fd);
628 #include "../../intl/explodename.c"
629 #include "../../intl/l10nflist.c"
631 struct namehashent *
632 insert_name (struct locarhandle *ah,
633 const char *name, size_t name_len, bool replace)
635 const struct locarhead *const head = ah->addr;
636 struct namehashent *namehashtab
637 = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
638 unsigned int insert_idx, idx, incr;
640 /* Hash value of the locale name. */
641 uint32_t hval = archive_hashval (name, name_len);
643 insert_idx = -1;
644 idx = hval % head->namehash_size;
645 incr = 1 + hval % (head->namehash_size - 2);
647 /* If the name_offset field is zero this means this is a
648 deleted entry and therefore no entry can be found. */
649 while (namehashtab[idx].name_offset != 0)
651 if (namehashtab[idx].hashval == hval
652 && strcmp (name,
653 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
655 /* Found the entry. */
656 if (namehashtab[idx].locrec_offset != 0 && ! replace)
658 if (! be_quiet)
659 error (0, 0, _("locale '%s' already exists"), name);
660 return NULL;
663 break;
666 if (namehashtab[idx].hashval == hval && ! be_quiet)
668 error (0, 0, "hash collision (%u) %s, %s",
669 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
672 /* Remember the first place we can insert the new entry. */
673 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
674 insert_idx = idx;
676 idx += incr;
677 if (idx >= head->namehash_size)
678 idx -= head->namehash_size;
681 /* Add as early as possible. */
682 if (insert_idx != -1)
683 idx = insert_idx;
685 namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
686 return &namehashtab[idx];
689 void
690 add_alias (struct locarhandle *ah, const char *alias, bool replace,
691 const char *oldname, uint32_t *locrec_offset_p)
693 uint32_t locrec_offset = *locrec_offset_p;
694 struct locarhead *head = ah->addr;
695 const size_t name_len = strlen (alias);
696 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
697 replace);
698 if (namehashent == NULL && ! replace)
699 return;
701 if (namehashent->name_offset == 0)
703 /* We are adding a new hash entry for this alias.
704 Determine whether we have to resize the file. */
705 if (head->string_used + name_len + 1 > head->string_size
706 || 100 * head->namehash_used > 75 * head->namehash_size)
708 /* The current archive is not large enough. */
709 enlarge_archive (ah, head);
711 /* The locrecent might have moved, so we have to look up
712 the old name afresh. */
713 namehashent = insert_name (ah, oldname, strlen (oldname), true);
714 assert (namehashent->name_offset != 0);
715 assert (namehashent->locrec_offset != 0);
716 *locrec_offset_p = namehashent->locrec_offset;
718 /* Tail call to try the whole thing again. */
719 add_alias (ah, alias, replace, oldname, locrec_offset_p);
720 return;
723 /* Add the name string. */
724 memcpy (ah->addr + head->string_offset + head->string_used,
725 alias, name_len + 1);
726 namehashent->name_offset = head->string_offset + head->string_used;
727 head->string_used += name_len + 1;
729 ++head->namehash_used;
732 if (namehashent->locrec_offset != 0)
734 /* Replacing an existing entry.
735 Mark that we are no longer using the old locrecent. */
736 struct locrecent *locrecent
737 = (struct locrecent *) ((char *) ah->addr
738 + namehashent->locrec_offset);
739 --locrecent->refs;
742 /* Point this entry at the locrecent installed for the main name. */
743 namehashent->locrec_offset = locrec_offset;
746 static int /* qsort comparator used below */
747 cmpcategorysize (const void *a, const void *b)
749 if (*(const void **) a == NULL)
750 return 1;
751 if (*(const void **) b == NULL)
752 return -1;
753 return ((*(const struct locale_category_data **) a)->size
754 - (*(const struct locale_category_data **) b)->size);
757 /* Check the content of the archive for duplicates. Add the content
758 of the files if necessary. Returns the locrec_offset. */
759 static uint32_t
760 add_locale (struct locarhandle *ah,
761 const char *name, locale_data_t data, bool replace)
763 /* First look for the name. If it already exists and we are not
764 supposed to replace it don't do anything. If it does not exist
765 we have to allocate a new locale record. */
766 size_t name_len = strlen (name);
767 uint32_t file_offsets[__LC_LAST];
768 unsigned int num_new_offsets = 0;
769 struct sumhashent *sumhashtab;
770 uint32_t hval;
771 unsigned int cnt, idx;
772 struct locarhead *head;
773 struct namehashent *namehashent;
774 unsigned int incr;
775 struct locrecent *locrecent;
776 off64_t lastoffset;
777 char *ptr;
778 struct locale_category_data *size_order[__LC_LAST];
779 const size_t pagesz = getpagesize ();
780 int small_mask;
782 head = ah->addr;
783 sumhashtab = (struct sumhashent *) ((char *) ah->addr
784 + head->sumhash_offset);
786 memset (file_offsets, 0, sizeof (file_offsets));
788 size_order[LC_ALL] = NULL;
789 for (cnt = 0; cnt < __LC_LAST; ++cnt)
790 if (cnt != LC_ALL)
791 size_order[cnt] = &data[cnt];
793 /* Sort the array in ascending order of data size. */
794 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
796 small_mask = 0;
797 data[LC_ALL].size = 0;
798 for (cnt = 0; cnt < __LC_LAST; ++cnt)
799 if (size_order[cnt] != NULL)
801 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
802 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
804 /* This category makes the small-categories block
805 stop being small, so this is the end of the road. */
807 size_order[cnt++] = NULL;
808 while (cnt < __LC_LAST);
809 break;
811 data[LC_ALL].size += rounded_size;
812 small_mask |= 1 << (size_order[cnt] - data);
815 /* Copy the data for all the small categories into the LC_ALL
816 pseudo-category. */
818 data[LC_ALL].addr = alloca (data[LC_ALL].size);
819 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
821 ptr = data[LC_ALL].addr;
822 for (cnt = 0; cnt < __LC_LAST; ++cnt)
823 if (small_mask & (1 << cnt))
825 memcpy (ptr, data[cnt].addr, data[cnt].size);
826 ptr += (data[cnt].size + 15) & -16;
828 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
830 /* For each locale category data set determine whether the same data
831 is already somewhere in the archive. */
832 for (cnt = 0; cnt < __LC_LAST; ++cnt)
833 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
835 ++num_new_offsets;
837 /* Compute the hash value of the checksum to determine a
838 starting point for the search in the MD5 hash value
839 table. */
840 hval = archive_hashval (data[cnt].sum, 16);
842 idx = hval % head->sumhash_size;
843 incr = 1 + hval % (head->sumhash_size - 2);
845 while (sumhashtab[idx].file_offset != 0)
847 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
849 /* Check the content, there could be a collision of
850 the hash sum.
852 Unfortunately the sumhashent record does not include
853 the size of the stored data. So we have to search for
854 it. */
855 locrecent = (struct locrecent *) ((char *) ah->addr
856 + head->locrectab_offset);
857 size_t iloc;
858 for (iloc = 0; iloc < head->locrectab_used; ++iloc)
859 if (locrecent[iloc].refs != 0
860 && (locrecent[iloc].record[cnt].offset
861 == sumhashtab[idx].file_offset))
862 break;
864 if (iloc != head->locrectab_used
865 && data[cnt].size == locrecent[iloc].record[cnt].len
866 /* We have to compare the content. Either we can
867 have the data mmaped or we have to read from
868 the file. */
869 && (file_data_available_p (ah, sumhashtab[idx].file_offset,
870 data[cnt].size)
871 ? memcmp (data[cnt].addr,
872 (char *) ah->addr
873 + sumhashtab[idx].file_offset,
874 data[cnt].size) == 0
875 : compare_from_file (ah, data[cnt].addr,
876 sumhashtab[idx].file_offset,
877 data[cnt].size) == 0))
879 /* Found it. */
880 file_offsets[cnt] = sumhashtab[idx].file_offset;
881 --num_new_offsets;
882 break;
886 idx += incr;
887 if (idx >= head->sumhash_size)
888 idx -= head->sumhash_size;
892 /* Find a slot for the locale name in the hash table. */
893 namehashent = insert_name (ah, name, name_len, replace);
894 if (namehashent == NULL) /* Already exists and !REPLACE. */
895 return 0;
897 /* Determine whether we have to resize the file. */
898 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
899 || (namehashent->locrec_offset == 0
900 && (head->locrectab_used == head->locrectab_size
901 || head->string_used + name_len + 1 > head->string_size
902 || 100 * head->namehash_used > 75 * head->namehash_size)))
904 /* The current archive is not large enough. */
905 enlarge_archive (ah, head);
906 return add_locale (ah, name, data, replace);
909 /* Add the locale data which is not yet in the archive. */
910 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
911 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
912 && file_offsets[cnt] == 0)
914 /* The data for this section is not yet available in the
915 archive. Append it. */
916 off64_t lastpos;
917 uint32_t md5hval;
919 lastpos = lseek64 (ah->fd, 0, SEEK_END);
920 if (lastpos == (off64_t) -1)
921 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
923 /* If block of small categories would cross page boundary,
924 align it unless it immediately follows a large category. */
925 if (cnt == LC_ALL && lastoffset != lastpos
926 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
927 & -pagesz)
928 > ((data[cnt].size + pagesz - 1) & -pagesz)))
930 size_t sz = pagesz - (lastpos & (pagesz - 1));
931 char *zeros = alloca (sz);
933 memset (zeros, 0, sz);
934 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
935 error (EXIT_FAILURE, errno,
936 _("cannot add to locale archive"));
938 lastpos += sz;
941 /* Align all data to a 16 byte boundary. */
942 if ((lastpos & 15) != 0)
944 static const char zeros[15] = { 0, };
946 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
947 != 16 - (lastpos & 15))
948 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
950 lastpos += 16 - (lastpos & 15);
953 /* Remember the position. */
954 file_offsets[cnt] = lastpos;
955 lastoffset = lastpos + data[cnt].size;
957 /* Write the data. */
958 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
959 != data[cnt].size)
960 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
962 /* Add the hash value to the hash table. */
963 md5hval = archive_hashval (data[cnt].sum, 16);
965 idx = md5hval % head->sumhash_size;
966 incr = 1 + md5hval % (head->sumhash_size - 2);
968 while (sumhashtab[idx].file_offset != 0)
970 idx += incr;
971 if (idx >= head->sumhash_size)
972 idx -= head->sumhash_size;
975 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
976 sumhashtab[idx].file_offset = file_offsets[cnt];
978 ++head->sumhash_used;
981 lastoffset = file_offsets[LC_ALL];
982 for (cnt = 0; cnt < __LC_LAST; ++cnt)
983 if (small_mask & (1 << cnt))
985 file_offsets[cnt] = lastoffset;
986 lastoffset += (data[cnt].size + 15) & -16;
989 if (namehashent->name_offset == 0)
991 /* Add the name string. */
992 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
993 name, name_len + 1);
994 namehashent->name_offset = head->string_offset + head->string_used;
995 head->string_used += name_len + 1;
996 ++head->namehash_used;
999 if (namehashent->locrec_offset == 0)
1001 /* Allocate a name location record. */
1002 namehashent->locrec_offset = (head->locrectab_offset
1003 + (head->locrectab_used++
1004 * sizeof (struct locrecent)));
1005 locrecent = (struct locrecent *) ((char *) ah->addr
1006 + namehashent->locrec_offset);
1007 locrecent->refs = 1;
1009 else
1011 /* If there are other aliases pointing to this locrecent,
1012 we still need a new one. If not, reuse the old one. */
1014 locrecent = (struct locrecent *) ((char *) ah->addr
1015 + namehashent->locrec_offset);
1016 if (locrecent->refs > 1)
1018 --locrecent->refs;
1019 namehashent->locrec_offset = (head->locrectab_offset
1020 + (head->locrectab_used++
1021 * sizeof (struct locrecent)));
1022 locrecent = (struct locrecent *) ((char *) ah->addr
1023 + namehashent->locrec_offset);
1024 locrecent->refs = 1;
1028 /* Fill in the table with the locations of the locale data. */
1029 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1031 locrecent->record[cnt].offset = file_offsets[cnt];
1032 locrecent->record[cnt].len = data[cnt].size;
1035 return namehashent->locrec_offset;
1039 /* Check the content of the archive for duplicates. Add the content
1040 of the files if necessary. Add all the names, possibly overwriting
1041 old files. */
1043 add_locale_to_archive (ah, name, data, replace)
1044 struct locarhandle *ah;
1045 const char *name;
1046 locale_data_t data;
1047 bool replace;
1049 char *normalized_name = NULL;
1050 uint32_t locrec_offset;
1052 /* First analyze the name to decide how to archive it. */
1053 const char *language;
1054 const char *modifier;
1055 const char *territory;
1056 const char *codeset;
1057 const char *normalized_codeset;
1058 int mask = _nl_explode_name (strdupa (name),
1059 &language, &modifier, &territory,
1060 &codeset, &normalized_codeset);
1062 if (mask & XPG_NORM_CODESET)
1063 /* This name contains a codeset in unnormalized form.
1064 We will store it in the archive with a normalized name. */
1065 asprintf (&normalized_name, "%s%s%s.%s%s%s",
1066 language, territory == NULL ? "" : "_", territory ?: "",
1067 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
1068 modifier == NULL ? "" : "@", modifier ?: "");
1070 /* This call does the main work. */
1071 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
1072 if (locrec_offset == 0)
1074 free (normalized_name);
1075 if (mask & XPG_NORM_CODESET)
1076 free ((char *) normalized_codeset);
1077 return -1;
1080 if ((mask & XPG_CODESET) == 0)
1082 /* This name lacks a codeset, so determine the locale's codeset and
1083 add an alias for its name with normalized codeset appended. */
1085 const struct
1087 unsigned int magic;
1088 unsigned int nstrings;
1089 unsigned int strindex[0];
1090 } *filedata = data[LC_CTYPE].addr;
1091 codeset = (char *) filedata
1092 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
1093 char *normalized_codeset_name = NULL;
1095 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
1096 mask |= XPG_NORM_CODESET;
1098 asprintf (&normalized_codeset_name, "%s%s%s.%s%s%s",
1099 language, territory == NULL ? "" : "_", territory ?: "",
1100 normalized_codeset,
1101 modifier == NULL ? "" : "@", modifier ?: "");
1103 add_alias (ah, normalized_codeset_name, replace,
1104 normalized_name ?: name, &locrec_offset);
1105 free (normalized_codeset_name);
1108 /* Now read the locale.alias files looking for lines whose
1109 right hand side matches our name after normalization. */
1110 if (alias_file != NULL)
1112 FILE *fp;
1113 fp = fopen (alias_file, "rm");
1114 if (fp == NULL)
1115 error (1, errno, _("locale alias file `%s' not found"),
1116 alias_file);
1118 /* No threads present. */
1119 __fsetlocking (fp, FSETLOCKING_BYCALLER);
1121 while (! feof_unlocked (fp))
1123 /* It is a reasonable approach to use a fix buffer here
1124 because
1125 a) we are only interested in the first two fields
1126 b) these fields must be usable as file names and so must
1127 not be that long */
1128 char buf[BUFSIZ];
1129 char *alias;
1130 char *value;
1131 char *cp;
1133 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1134 /* EOF reached. */
1135 break;
1137 cp = buf;
1138 /* Ignore leading white space. */
1139 while (isspace (cp[0]) && cp[0] != '\n')
1140 ++cp;
1142 /* A leading '#' signals a comment line. */
1143 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
1145 alias = cp++;
1146 while (cp[0] != '\0' && !isspace (cp[0]))
1147 ++cp;
1148 /* Terminate alias name. */
1149 if (cp[0] != '\0')
1150 *cp++ = '\0';
1152 /* Now look for the beginning of the value. */
1153 while (isspace (cp[0]))
1154 ++cp;
1156 if (cp[0] != '\0')
1158 value = cp++;
1159 while (cp[0] != '\0' && !isspace (cp[0]))
1160 ++cp;
1161 /* Terminate value. */
1162 if (cp[0] == '\n')
1164 /* This has to be done to make the following
1165 test for the end of line possible. We are
1166 looking for the terminating '\n' which do not
1167 overwrite here. */
1168 *cp++ = '\0';
1169 *cp = '\n';
1171 else if (cp[0] != '\0')
1172 *cp++ = '\0';
1174 /* Does this alias refer to our locale? We will
1175 normalize the right hand side and compare the
1176 elements of the normalized form. */
1178 const char *rhs_language;
1179 const char *rhs_modifier;
1180 const char *rhs_territory;
1181 const char *rhs_codeset;
1182 const char *rhs_normalized_codeset;
1183 int rhs_mask = _nl_explode_name (value,
1184 &rhs_language,
1185 &rhs_modifier,
1186 &rhs_territory,
1187 &rhs_codeset,
1188 &rhs_normalized_codeset);
1189 if (!strcmp (language, rhs_language)
1190 && ((rhs_mask & XPG_CODESET)
1191 /* He has a codeset, it must match normalized. */
1192 ? !strcmp ((mask & XPG_NORM_CODESET)
1193 ? normalized_codeset : codeset,
1194 (rhs_mask & XPG_NORM_CODESET)
1195 ? rhs_normalized_codeset : rhs_codeset)
1196 /* He has no codeset, we must also have none. */
1197 : (mask & XPG_CODESET) == 0)
1198 /* Codeset (or lack thereof) matches. */
1199 && !strcmp (territory ?: "", rhs_territory ?: "")
1200 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1201 /* We have a winner. */
1202 add_alias (ah, alias, replace,
1203 normalized_name ?: name, &locrec_offset);
1204 if (rhs_mask & XPG_NORM_CODESET)
1205 free ((char *) rhs_normalized_codeset);
1210 /* Possibly not the whole line fits into the buffer.
1211 Ignore the rest of the line. */
1212 while (strchr (cp, '\n') == NULL)
1214 cp = buf;
1215 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1216 /* Make sure the inner loop will be left. The outer
1217 loop will exit at the `feof' test. */
1218 *cp = '\n';
1222 fclose (fp);
1225 free (normalized_name);
1227 if (mask & XPG_NORM_CODESET)
1228 free ((char *) normalized_codeset);
1230 return 0;
1235 add_locales_to_archive (nlist, list, replace)
1236 size_t nlist;
1237 char *list[];
1238 bool replace;
1240 struct locarhandle ah;
1241 int result = 0;
1243 /* Open the archive. This call never returns if we cannot
1244 successfully open the archive. */
1245 open_archive (&ah, false);
1247 while (nlist-- > 0)
1249 const char *fname = *list++;
1250 size_t fnamelen = strlen (fname);
1251 struct stat64 st;
1252 DIR *dirp;
1253 struct dirent64 *d;
1254 int seen;
1255 locale_data_t data;
1256 int cnt;
1258 if (! be_quiet)
1259 printf (_("Adding %s\n"), fname);
1261 /* First see whether this really is a directory and whether it
1262 contains all the require locale category files. */
1263 if (stat64 (fname, &st) < 0)
1265 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1266 strerror (errno));
1267 continue;
1269 if (!S_ISDIR (st.st_mode))
1271 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1272 continue;
1275 dirp = opendir (fname);
1276 if (dirp == NULL)
1278 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1279 fname, strerror (errno));
1280 continue;
1283 seen = 0;
1284 while ((d = readdir64 (dirp)) != NULL)
1286 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1287 if (cnt != LC_ALL)
1288 if (strcmp (d->d_name, locnames[cnt]) == 0)
1290 unsigned char d_type;
1292 /* We have an object of the required name. If it's
1293 a directory we have to look at a file with the
1294 prefix "SYS_". Otherwise we have found what we
1295 are looking for. */
1296 #ifdef _DIRENT_HAVE_D_TYPE
1297 d_type = d->d_type;
1299 if (d_type != DT_REG)
1300 #endif
1302 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1304 #ifdef _DIRENT_HAVE_D_TYPE
1305 if (d_type == DT_UNKNOWN)
1306 #endif
1308 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1309 d->d_name);
1311 if (stat64 (fullname, &st) == -1)
1312 /* We cannot stat the file, ignore it. */
1313 break;
1315 d_type = IFTODT (st.st_mode);
1318 if (d_type == DT_DIR)
1320 /* We have to do more tests. The file is a
1321 directory and it therefore must contain a
1322 regular file with the same name except a
1323 "SYS_" prefix. */
1324 char *t = stpcpy (stpcpy (fullname, fname), "/");
1325 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
1326 d->d_name);
1328 if (stat64 (fullname, &st) == -1)
1329 /* There is no SYS_* file or we cannot
1330 access it. */
1331 break;
1333 d_type = IFTODT (st.st_mode);
1337 /* If we found a regular file (eventually after
1338 following a symlink) we are successful. */
1339 if (d_type == DT_REG)
1340 ++seen;
1341 break;
1345 closedir (dirp);
1347 if (seen != __LC_LAST - 1)
1349 /* We don't have all locale category files. Ignore the name. */
1350 error (0, 0, _("incomplete set of locale files in \"%s\""),
1351 fname);
1352 continue;
1355 /* Add the files to the archive. To do this we first compute
1356 sizes and the MD5 sums of all the files. */
1357 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1358 if (cnt != LC_ALL)
1360 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1361 int fd;
1363 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1364 fd = open64 (fullname, O_RDONLY);
1365 if (fd == -1 || fstat64 (fd, &st) == -1)
1367 /* Cannot read the file. */
1368 if (fd != -1)
1369 close (fd);
1370 break;
1373 if (S_ISDIR (st.st_mode))
1375 char *t;
1376 close (fd);
1377 t = stpcpy (stpcpy (fullname, fname), "/");
1378 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
1379 locnames[cnt]);
1381 fd = open64 (fullname, O_RDONLY);
1382 if (fd == -1 || fstat64 (fd, &st) == -1
1383 || !S_ISREG (st.st_mode))
1385 if (fd != -1)
1386 close (fd);
1387 break;
1391 /* Map the file. */
1392 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1393 fd, 0);
1394 if (data[cnt].addr == MAP_FAILED)
1396 /* Cannot map it. */
1397 close (fd);
1398 break;
1401 data[cnt].size = st.st_size;
1402 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1404 /* We don't need the file descriptor anymore. */
1405 close (fd);
1408 if (cnt != __LC_LAST)
1410 while (cnt-- > 0)
1411 if (cnt != LC_ALL)
1412 munmap (data[cnt].addr, data[cnt].size);
1414 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1416 continue;
1419 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1421 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1422 if (cnt != LC_ALL)
1423 munmap (data[cnt].addr, data[cnt].size);
1426 /* We are done. */
1427 close_archive (&ah);
1429 return result;
1434 delete_locales_from_archive (nlist, list)
1435 size_t nlist;
1436 char *list[];
1438 struct locarhandle ah;
1439 struct locarhead *head;
1440 struct namehashent *namehashtab;
1442 /* Open the archive. This call never returns if we cannot
1443 successfully open the archive. */
1444 open_archive (&ah, false);
1446 head = ah.addr;
1447 namehashtab = (struct namehashent *) ((char *) ah.addr
1448 + head->namehash_offset);
1450 while (nlist-- > 0)
1452 const char *locname = *list++;
1453 uint32_t hval;
1454 unsigned int idx;
1455 unsigned int incr;
1457 /* Search for this locale in the archive. */
1458 hval = archive_hashval (locname, strlen (locname));
1460 idx = hval % head->namehash_size;
1461 incr = 1 + hval % (head->namehash_size - 2);
1463 /* If the name_offset field is zero this means this is no
1464 deleted entry and therefore no entry can be found. */
1465 while (namehashtab[idx].name_offset != 0)
1467 if (namehashtab[idx].hashval == hval
1468 && (strcmp (locname,
1469 (char *) ah.addr + namehashtab[idx].name_offset)
1470 == 0))
1472 /* Found the entry. Now mark it as removed by zero-ing
1473 the reference to the locale record. */
1474 namehashtab[idx].locrec_offset = 0;
1475 break;
1478 idx += incr;
1479 if (idx >= head->namehash_size)
1480 idx -= head->namehash_size;
1483 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1484 error (0, 0, _("locale \"%s\" not in archive"), locname);
1487 close_archive (&ah);
1489 return 0;
1493 struct nameent
1495 char *name;
1496 uint32_t locrec_offset;
1500 struct dataent
1502 const unsigned char *sum;
1503 uint32_t file_offset;
1504 uint32_t nlink;
1508 static int
1509 nameentcmp (const void *a, const void *b)
1511 return strcmp (((const struct nameent *) a)->name,
1512 ((const struct nameent *) b)->name);
1516 static int
1517 dataentcmp (const void *a, const void *b)
1519 if (((const struct dataent *) a)->file_offset
1520 < ((const struct dataent *) b)->file_offset)
1521 return -1;
1523 if (((const struct dataent *) a)->file_offset
1524 > ((const struct dataent *) b)->file_offset)
1525 return 1;
1527 return 0;
1531 void
1532 show_archive_content (int verbose)
1534 struct locarhandle ah;
1535 struct locarhead *head;
1536 struct namehashent *namehashtab;
1537 struct nameent *names;
1538 size_t cnt, used;
1540 /* Open the archive. This call never returns if we cannot
1541 successfully open the archive. */
1542 open_archive (&ah, true);
1544 head = ah.addr;
1546 names = (struct nameent *) xmalloc (head->namehash_used
1547 * sizeof (struct nameent));
1549 namehashtab = (struct namehashent *) ((char *) ah.addr
1550 + head->namehash_offset);
1551 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1552 if (namehashtab[cnt].locrec_offset != 0)
1554 assert (used < head->namehash_used);
1555 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1556 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
1559 /* Sort the names. */
1560 qsort (names, used, sizeof (struct nameent), nameentcmp);
1562 if (verbose)
1564 struct dataent *files;
1565 struct sumhashent *sumhashtab;
1566 int sumused;
1568 files = (struct dataent *) xmalloc (head->sumhash_used
1569 * sizeof (struct dataent));
1571 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1572 + head->sumhash_offset);
1573 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1574 if (sumhashtab[cnt].file_offset != 0)
1576 assert (sumused < head->sumhash_used);
1577 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1578 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1579 files[sumused++].nlink = 0;
1582 /* Sort by file locations. */
1583 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1585 /* Compute nlink fields. */
1586 for (cnt = 0; cnt < used; ++cnt)
1588 struct locrecent *locrec;
1589 int idx;
1591 locrec = (struct locrecent *) ((char *) ah.addr
1592 + names[cnt].locrec_offset);
1593 for (idx = 0; idx < __LC_LAST; ++idx)
1594 if (locrec->record[LC_ALL].offset != 0
1595 ? (idx == LC_ALL
1596 || (locrec->record[idx].offset
1597 < locrec->record[LC_ALL].offset)
1598 || (locrec->record[idx].offset + locrec->record[idx].len
1599 > (locrec->record[LC_ALL].offset
1600 + locrec->record[LC_ALL].len)))
1601 : idx != LC_ALL)
1603 struct dataent *data, dataent;
1605 dataent.file_offset = locrec->record[idx].offset;
1606 data = (struct dataent *) bsearch (&dataent, files, sumused,
1607 sizeof (struct dataent),
1608 dataentcmp);
1609 assert (data != NULL);
1610 ++data->nlink;
1614 /* Print it. */
1615 for (cnt = 0; cnt < used; ++cnt)
1617 struct locrecent *locrec;
1618 int idx, i;
1620 locrec = (struct locrecent *) ((char *) ah.addr
1621 + names[cnt].locrec_offset);
1622 for (idx = 0; idx < __LC_LAST; ++idx)
1623 if (idx != LC_ALL)
1625 struct dataent *data, dataent;
1627 dataent.file_offset = locrec->record[idx].offset;
1628 if (locrec->record[LC_ALL].offset != 0
1629 && dataent.file_offset >= locrec->record[LC_ALL].offset
1630 && (dataent.file_offset + locrec->record[idx].len
1631 <= (locrec->record[LC_ALL].offset
1632 + locrec->record[LC_ALL].len)))
1633 dataent.file_offset = locrec->record[LC_ALL].offset;
1635 data = (struct dataent *) bsearch (&dataent, files, sumused,
1636 sizeof (struct dataent),
1637 dataentcmp);
1638 printf ("%6d %7x %3d%c ",
1639 locrec->record[idx].len, locrec->record[idx].offset,
1640 data->nlink,
1641 dataent.file_offset == locrec->record[LC_ALL].offset
1642 ? '+' : ' ');
1643 for (i = 0; i < 16; i += 4)
1644 printf ("%02x%02x%02x%02x",
1645 data->sum[i], data->sum[i + 1],
1646 data->sum[i + 2], data->sum[i + 3]);
1647 printf (" %s/%s\n", names[cnt].name,
1648 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1649 : locnames[idx]);
1653 else
1654 for (cnt = 0; cnt < used; ++cnt)
1655 puts (names[cnt].name);
1657 close_archive (&ah);
1659 exit (EXIT_SUCCESS);