1 /* Copyright (C) 2002-2017 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, see <http://www.gnu.org/licenses/>. */
32 #include <stdio_ext.h>
39 #include <sys/param.h>
43 #include <libc-mmap.h>
44 #include <libc-pointer-arith.h>
45 #include "../../crypt/md5.h"
46 #include "../localeinfo.h"
47 #include "../locarchive.h"
48 #include "localedef.h"
51 /* Define the hash function. We define the function as static inline.
52 We must change the name so as not to conflict with simple-hash.h. */
53 #define compute_hashval static archive_hashval
54 #define hashval_t uint32_t
56 #undef compute_hashval
58 extern const char *output_prefix
;
60 #define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
62 static const char *locnames
[] =
64 #define DEFINE_CATEGORY(category, category_name, items, a) \
65 [category] = category_name,
66 #include "categories.def"
67 #undef DEFINE_CATEGORY
71 /* Size of the initial archive header. */
72 #define INITIAL_NUM_NAMES 900
73 #define INITIAL_SIZE_STRINGS 7500
74 #define INITIAL_NUM_LOCREC 420
75 #define INITIAL_NUM_SUMS 2000
78 /* Get and set values (possibly endian-swapped) in structures mapped
79 from or written directly to locale archives. */
80 #define GET(FIELD) maybe_swap_uint32 (FIELD)
81 #define SET(FIELD, VALUE) ((FIELD) = maybe_swap_uint32 (VALUE))
82 #define INC(FIELD, INCREMENT) SET (FIELD, GET (FIELD) + (INCREMENT))
85 /* Size of the reserved address space area. */
86 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
88 /* To prepare for enlargements of the mmaped area reserve some address
89 space. On some machines, being a file mapping rather than an anonymous
90 mapping affects the address selection. So do this mapping from the
91 actual file, even though it's only a dummy to reserve address space. */
93 prepare_address_space (int fd
, size_t total
, size_t *reserved
, int *xflags
,
94 void **mmap_base
, size_t *mmap_len
)
96 if (total
< RESERVE_MMAP_SIZE
)
98 void *p
= mmap64 (NULL
, RESERVE_MMAP_SIZE
, PROT_NONE
, MAP_SHARED
, fd
, 0);
101 void *aligned_p
= PTR_ALIGN_UP (p
, MAP_FIXED_ALIGNMENT
);
102 size_t align_adjust
= aligned_p
- p
;
104 *mmap_len
= RESERVE_MMAP_SIZE
;
105 assert (align_adjust
< RESERVE_MMAP_SIZE
);
106 *reserved
= RESERVE_MMAP_SIZE
- align_adjust
;
121 create_archive (const char *archivefname
, struct locarhandle
*ah
)
124 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
125 struct locarhead head
;
128 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
130 /* Create a temporary file in the correct directory. */
131 fd
= mkstemp (fname
);
133 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
135 /* Create the initial content of the archive. */
136 SET (head
.magic
, AR_MAGIC
);
137 SET (head
.serial
, 0);
138 SET (head
.namehash_offset
, sizeof (struct locarhead
));
139 SET (head
.namehash_used
, 0);
140 SET (head
.namehash_size
, next_prime (INITIAL_NUM_NAMES
));
142 SET (head
.string_offset
,
143 (GET (head
.namehash_offset
)
144 + GET (head
.namehash_size
) * sizeof (struct namehashent
)));
145 SET (head
.string_used
, 0);
146 SET (head
.string_size
, INITIAL_SIZE_STRINGS
);
148 SET (head
.locrectab_offset
,
149 GET (head
.string_offset
) + GET (head
.string_size
));
150 SET (head
.locrectab_used
, 0);
151 SET (head
.locrectab_size
, INITIAL_NUM_LOCREC
);
153 SET (head
.sumhash_offset
,
154 (GET (head
.locrectab_offset
)
155 + GET (head
.locrectab_size
) * sizeof (struct locrecent
)));
156 SET (head
.sumhash_used
, 0);
157 SET (head
.sumhash_size
, next_prime (INITIAL_NUM_SUMS
));
159 total
= (GET (head
.sumhash_offset
)
160 + GET (head
.sumhash_size
) * sizeof (struct sumhashent
));
162 /* Write out the header and create room for the other data structures. */
163 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
167 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
170 if (ftruncate64 (fd
, total
) != 0)
174 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
177 size_t reserved
, mmap_len
;
180 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
183 /* Map the header and all the administration data structures. */
184 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
189 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
192 /* Now try to rename it. We don't use the rename function since
193 this would overwrite a file which has been created in
195 if (link (fname
, archivefname
) == -1)
199 /* We cannot use the just created file. */
203 if (errval
== EEXIST
)
205 /* There is already an archive. Must have been a localedef run
206 which happened in parallel. Simply open this file then. */
207 open_archive (ah
, false);
211 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
214 /* Remove the temporary name. */
217 /* Make the file globally readable. */
218 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
221 unlink (archivefname
);
222 error (EXIT_FAILURE
, errval
,
223 _("cannot change mode of new locale archive"));
228 ah
->mmap_base
= mmap_base
;
229 ah
->mmap_len
= mmap_len
;
232 ah
->reserved
= reserved
;
236 /* This structure and qsort comparator function are used below to sort an
237 old archive's locrec table in order of data position in the file. */
241 struct locrecent
*locrec
;
245 oldlocrecentcmp (const void *a
, const void *b
)
247 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
248 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
249 uint32_t start_a
= -1, end_a
= 0;
250 uint32_t start_b
= -1, end_b
= 0;
253 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
256 if (GET (la
->record
[cnt
].offset
) < start_a
)
257 start_a
= GET (la
->record
[cnt
].offset
);
258 if (GET (la
->record
[cnt
].offset
) + GET (la
->record
[cnt
].len
) > end_a
)
259 end_a
= GET (la
->record
[cnt
].offset
) + GET (la
->record
[cnt
].len
);
261 assert (start_a
!= (uint32_t)-1);
264 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
267 if (GET (lb
->record
[cnt
].offset
) < start_b
)
268 start_b
= GET (lb
->record
[cnt
].offset
);
269 if (GET (lb
->record
[cnt
].offset
) + GET (lb
->record
[cnt
].len
) > end_b
)
270 end_b
= GET (lb
->record
[cnt
].offset
) + GET (lb
->record
[cnt
].len
);
272 assert (start_b
!= (uint32_t)-1);
275 if (start_a
!= start_b
)
276 return (int)start_a
- (int)start_b
;
277 return (int)end_a
- (int)end_b
;
281 /* forward decls for below */
282 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
283 locale_data_t data
, bool replace
);
284 static void add_alias (struct locarhandle
*ah
, const char *alias
,
285 bool replace
, const char *oldname
,
286 uint32_t *locrec_offset_p
);
290 file_data_available_p (struct locarhandle
*ah
, uint32_t offset
, uint32_t size
)
292 if (offset
< ah
->mmaped
&& offset
+ size
<= ah
->mmaped
)
296 if (fstat64 (ah
->fd
, &st
) != 0)
299 if (st
.st_size
> ah
->reserved
)
302 size_t start
= ALIGN_DOWN (ah
->mmaped
, MAP_FIXED_ALIGNMENT
);
303 void *p
= mmap64 (ah
->addr
+ start
, st
.st_size
- start
,
304 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_FIXED
,
312 ah
->mmaped
= st
.st_size
;
318 compare_from_file (struct locarhandle
*ah
, void *p1
, uint32_t offset2
,
321 void *p2
= xmalloc (size
);
322 if (pread (ah
->fd
, p2
, size
, offset2
) != size
)
323 WITH_CUR_LOCALE (error (4, errno
,
324 _("cannot read data from locale archive")));
326 int res
= memcmp (p1
, p2
, size
);
333 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
337 struct locarhead newhead
;
339 unsigned int cnt
, loccnt
;
340 struct namehashent
*oldnamehashtab
;
341 struct locarhandle new_ah
;
342 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
343 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
344 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
347 memcpy (archivefname
, output_prefix
, prefix_len
);
348 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
349 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
351 /* Not all of the old file has to be mapped. Change this now this
352 we will have to access the whole content. */
353 if (fstat64 (ah
->fd
, &st
) != 0)
355 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
357 if (st
.st_size
< ah
->reserved
)
358 ah
->addr
= mmap64 (ah
->addr
, st
.st_size
, PROT_READ
| PROT_WRITE
,
359 MAP_SHARED
| MAP_FIXED
, ah
->fd
, 0);
363 munmap (ah
->mmap_base
, ah
->mmap_len
);
365 munmap (ah
->addr
, ah
->reserved
);
366 ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
367 MAP_SHARED
, ah
->fd
, 0);
368 ah
->reserved
= st
.st_size
;
369 ah
->mmap_base
= NULL
;
373 if (ah
->addr
== MAP_FAILED
)
375 ah
->mmaped
= st
.st_size
;
377 /* Create a temporary file in the correct directory. */
378 fd
= mkstemp (fname
);
380 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
382 /* Copy the existing head information. */
385 /* Create the new archive header. The sizes of the various tables
386 should be double from what is currently used. */
387 SET (newhead
.namehash_size
,
388 MAX (next_prime (2 * GET (newhead
.namehash_used
)),
389 GET (newhead
.namehash_size
)));
391 printf ("name: size: %u, used: %d, new: size: %u\n",
392 GET (head
->namehash_size
),
393 GET (head
->namehash_used
), GET (newhead
.namehash_size
));
395 SET (newhead
.string_offset
, (GET (newhead
.namehash_offset
)
396 + (GET (newhead
.namehash_size
)
397 * sizeof (struct namehashent
))));
398 /* Keep the string table size aligned to 4 bytes, so that
399 all the struct { uint32_t } types following are happy. */
400 SET (newhead
.string_size
, MAX ((2 * GET (newhead
.string_used
) + 3) & -4,
401 GET (newhead
.string_size
)));
403 SET (newhead
.locrectab_offset
,
404 GET (newhead
.string_offset
) + GET (newhead
.string_size
));
405 SET (newhead
.locrectab_size
, MAX (2 * GET (newhead
.locrectab_used
),
406 GET (newhead
.locrectab_size
)));
408 SET (newhead
.sumhash_offset
, (GET (newhead
.locrectab_offset
)
409 + (GET (newhead
.locrectab_size
)
410 * sizeof (struct locrecent
))));
411 SET (newhead
.sumhash_size
,
412 MAX (next_prime (2 * GET (newhead
.sumhash_used
)),
413 GET (newhead
.sumhash_size
)));
415 total
= (GET (newhead
.sumhash_offset
)
416 + GET (newhead
.sumhash_size
) * sizeof (struct sumhashent
));
418 /* The new file is empty now. */
419 SET (newhead
.namehash_used
, 0);
420 SET (newhead
.string_used
, 0);
421 SET (newhead
.locrectab_used
, 0);
422 SET (newhead
.sumhash_used
, 0);
424 /* Write out the header and create room for the other data structures. */
425 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
430 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
433 if (ftruncate64 (fd
, total
) != 0)
437 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
440 size_t reserved
, mmap_len
;
443 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
446 /* Map the header and all the administration data structures. */
447 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
452 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
455 /* Lock the new file. */
456 if (lockf64 (fd
, F_LOCK
, total
) != 0)
460 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
463 new_ah
.mmaped
= total
;
464 new_ah
.mmap_base
= mmap_base
;
465 new_ah
.mmap_len
= mmap_len
;
468 new_ah
.reserved
= reserved
;
470 /* Walk through the hash name hash table to find out what data is
471 still referenced and transfer it into the new file. */
472 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
473 + GET (head
->namehash_offset
));
475 /* Sort the old locrec table in order of data position. */
476 struct oldlocrecent oldlocrecarray
[GET (head
->namehash_size
)];
477 for (cnt
= 0, loccnt
= 0; cnt
< GET (head
->namehash_size
); ++cnt
)
478 if (GET (oldnamehashtab
[cnt
].locrec_offset
) != 0)
480 oldlocrecarray
[loccnt
].cnt
= cnt
;
481 oldlocrecarray
[loccnt
++].locrec
482 = (struct locrecent
*) ((char *) ah
->addr
483 + GET (oldnamehashtab
[cnt
].locrec_offset
));
485 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
488 uint32_t last_locrec_offset
= 0;
489 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
491 /* Insert this entry in the new hash table. */
492 locale_data_t old_data
;
494 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
496 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
499 old_data
[idx
].size
= GET (oldlocrec
->record
[idx
].len
);
501 = ((char *) ah
->addr
+ GET (oldlocrec
->record
[idx
].offset
));
503 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
507 if (cnt
> 0 && oldlocrecarray
[cnt
- 1].locrec
== oldlocrec
)
511 + GET (oldnamehashtab
[oldlocrecarray
[cnt
512 - 1].cnt
].name_offset
));
517 + GET (oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
)),
518 0, oldname
, &last_locrec_offset
);
526 + GET (oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
)),
528 if (last_locrec_offset
== 0)
529 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
532 /* Make the file globally readable. */
533 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
537 error (EXIT_FAILURE
, errval
,
538 _("cannot change mode of resized locale archive"));
541 /* Rename the new file. */
542 if (rename (fname
, archivefname
) != 0)
546 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
549 /* Close the old file. */
552 /* Add the information for the new one. */
558 open_archive (struct locarhandle
*ah
, bool readonly
)
563 struct locarhead head
;
565 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
566 char default_fname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
567 const char *archivefname
= ah
->fname
;
569 /* If ah has a non-NULL fname open that otherwise open the default. */
570 if (archivefname
== NULL
)
572 archivefname
= default_fname
;
574 memcpy (default_fname
, output_prefix
, prefix_len
);
575 strcpy (default_fname
+ prefix_len
, ARCHIVE_NAME
);
580 /* Open the archive. We must have exclusive write access. */
581 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
584 /* Maybe the file does not yet exist? If we are opening
585 the default locale archive we ignore the failure and
586 list an empty archive, otherwise we print an error
588 if (errno
== ENOENT
&& archivefname
== default_fname
)
592 static const struct locarhead nullhead
=
595 .namehash_offset
= 0,
599 ah
->addr
= (void *) &nullhead
;
603 create_archive (archivefname
, ah
);
608 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
612 if (fstat64 (fd
, &st
) < 0)
613 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
616 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
620 if (retry
++ < max_locarchive_open_retry
)
624 /* Wait for a bit. */
626 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
627 (void) nanosleep (&req
, NULL
);
632 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
636 /* One more check. Maybe another process replaced the archive file
637 with a new, larger one since we opened the file. */
638 if (stat64 (archivefname
, &st2
) == -1
639 || st
.st_dev
!= st2
.st_dev
640 || st
.st_ino
!= st2
.st_ino
)
642 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
647 /* Leave the loop. */
651 /* Read the header. */
652 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
654 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
655 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
659 ah
->mmaped
= st
.st_size
;
661 size_t reserved
, mmap_len
;
664 void *p
= prepare_address_space (fd
, st
.st_size
, &reserved
, &xflags
,
665 &mmap_base
, &mmap_len
);
667 /* Map the entire file. We might need to compare the category data
668 in the file with the newly added data. */
669 ah
->addr
= mmap64 (p
, st
.st_size
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
670 MAP_SHARED
| xflags
, fd
, 0);
671 if (ah
->addr
== MAP_FAILED
)
673 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
674 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
676 ah
->reserved
= reserved
;
677 ah
->mmap_base
= mmap_base
;
678 ah
->mmap_len
= mmap_len
;
683 close_archive (struct locarhandle
*ah
)
688 munmap (ah
->mmap_base
, ah
->mmap_len
);
690 munmap (ah
->addr
, ah
->reserved
);
695 #include "../../intl/explodename.c"
696 #include "../../intl/l10nflist.c"
698 static struct namehashent
*
699 insert_name (struct locarhandle
*ah
,
700 const char *name
, size_t name_len
, bool replace
)
702 const struct locarhead
*const head
= ah
->addr
;
703 struct namehashent
*namehashtab
704 = (struct namehashent
*) ((char *) ah
->addr
705 + GET (head
->namehash_offset
));
706 unsigned int insert_idx
, idx
, incr
;
708 /* Hash value of the locale name. */
709 uint32_t hval
= archive_hashval (name
, name_len
);
712 idx
= hval
% GET (head
->namehash_size
);
713 incr
= 1 + hval
% (GET (head
->namehash_size
) - 2);
715 /* If the name_offset field is zero this means this is a
716 deleted entry and therefore no entry can be found. */
717 while (GET (namehashtab
[idx
].name_offset
) != 0)
719 if (GET (namehashtab
[idx
].hashval
) == hval
721 (char *) ah
->addr
+ GET (namehashtab
[idx
].name_offset
))
724 /* Found the entry. */
725 if (GET (namehashtab
[idx
].locrec_offset
) != 0 && ! replace
)
728 error (0, 0, _("locale '%s' already exists"), name
);
735 if (GET (namehashtab
[idx
].hashval
) == hval
&& ! be_quiet
)
737 error (0, 0, "hash collision (%u) %s, %s",
739 (char *) ah
->addr
+ GET (namehashtab
[idx
].name_offset
));
742 /* Remember the first place we can insert the new entry. */
743 if (GET (namehashtab
[idx
].locrec_offset
) == 0 && insert_idx
== -1)
747 if (idx
>= GET (head
->namehash_size
))
748 idx
-= GET (head
->namehash_size
);
751 /* Add as early as possible. */
752 if (insert_idx
!= -1)
755 SET (namehashtab
[idx
].hashval
, hval
); /* no-op if replacing an old entry. */
756 return &namehashtab
[idx
];
760 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
761 const char *oldname
, uint32_t *locrec_offset_p
)
763 uint32_t locrec_offset
= *locrec_offset_p
;
764 struct locarhead
*head
= ah
->addr
;
765 const size_t name_len
= strlen (alias
);
766 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
768 if (namehashent
== NULL
&& ! replace
)
771 if (GET (namehashent
->name_offset
) == 0)
773 /* We are adding a new hash entry for this alias.
774 Determine whether we have to resize the file. */
775 if (GET (head
->string_used
) + name_len
+ 1 > GET (head
->string_size
)
776 || (100 * GET (head
->namehash_used
)
777 > 75 * GET (head
->namehash_size
)))
779 /* The current archive is not large enough. */
780 enlarge_archive (ah
, head
);
782 /* The locrecent might have moved, so we have to look up
783 the old name afresh. */
784 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
785 assert (GET (namehashent
->name_offset
) != 0);
786 assert (GET (namehashent
->locrec_offset
) != 0);
787 *locrec_offset_p
= GET (namehashent
->locrec_offset
);
789 /* Tail call to try the whole thing again. */
790 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
794 /* Add the name string. */
795 memcpy (ah
->addr
+ GET (head
->string_offset
) + GET (head
->string_used
),
796 alias
, name_len
+ 1);
797 SET (namehashent
->name_offset
,
798 GET (head
->string_offset
) + GET (head
->string_used
));
799 INC (head
->string_used
, name_len
+ 1);
801 INC (head
->namehash_used
, 1);
804 if (GET (namehashent
->locrec_offset
) != 0)
806 /* Replacing an existing entry.
807 Mark that we are no longer using the old locrecent. */
808 struct locrecent
*locrecent
809 = (struct locrecent
*) ((char *) ah
->addr
810 + GET (namehashent
->locrec_offset
));
811 INC (locrecent
->refs
, -1);
814 /* Point this entry at the locrecent installed for the main name. */
815 SET (namehashent
->locrec_offset
, locrec_offset
);
818 static int /* qsort comparator used below */
819 cmpcategorysize (const void *a
, const void *b
)
821 if (*(const void **) a
== NULL
)
823 if (*(const void **) b
== NULL
)
825 return ((*(const struct locale_category_data
**) a
)->size
826 - (*(const struct locale_category_data
**) b
)->size
);
829 /* Check the content of the archive for duplicates. Add the content
830 of the files if necessary. Returns the locrec_offset. */
832 add_locale (struct locarhandle
*ah
,
833 const char *name
, locale_data_t data
, bool replace
)
835 /* First look for the name. If it already exists and we are not
836 supposed to replace it don't do anything. If it does not exist
837 we have to allocate a new locale record. */
838 size_t name_len
= strlen (name
);
839 uint32_t file_offsets
[__LC_LAST
];
840 unsigned int num_new_offsets
= 0;
841 struct sumhashent
*sumhashtab
;
843 unsigned int cnt
, idx
;
844 struct locarhead
*head
;
845 struct namehashent
*namehashent
;
847 struct locrecent
*locrecent
;
850 struct locale_category_data
*size_order
[__LC_LAST
];
851 /* Page size alignment is a minor optimization for locality; use a
852 common value here rather than making the localedef output depend
853 on the page size of the system on which localedef is run. See
854 <https://sourceware.org/glibc/wiki/Development_Todo/Master#Locale_archive_alignment>
855 for more discussion. */
856 const size_t pagesz
= 4096;
860 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
861 + GET (head
->sumhash_offset
));
863 memset (file_offsets
, 0, sizeof (file_offsets
));
865 size_order
[LC_ALL
] = NULL
;
866 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
868 size_order
[cnt
] = &data
[cnt
];
870 /* Sort the array in ascending order of data size. */
871 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
874 data
[LC_ALL
].size
= 0;
875 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
876 if (size_order
[cnt
] != NULL
)
878 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
879 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
881 /* This category makes the small-categories block
882 stop being small, so this is the end of the road. */
884 size_order
[cnt
++] = NULL
;
885 while (cnt
< __LC_LAST
);
888 data
[LC_ALL
].size
+= rounded_size
;
889 small_mask
|= 1 << (size_order
[cnt
] - data
);
892 /* Copy the data for all the small categories into the LC_ALL
895 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
896 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
898 ptr
= data
[LC_ALL
].addr
;
899 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
900 if (small_mask
& (1 << cnt
))
902 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
903 ptr
+= (data
[cnt
].size
+ 15) & -16;
905 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
907 /* For each locale category data set determine whether the same data
908 is already somewhere in the archive. */
909 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
910 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
914 /* Compute the hash value of the checksum to determine a
915 starting point for the search in the MD5 hash value
917 hval
= archive_hashval (data
[cnt
].sum
, 16);
919 idx
= hval
% GET (head
->sumhash_size
);
920 incr
= 1 + hval
% (GET (head
->sumhash_size
) - 2);
922 while (GET (sumhashtab
[idx
].file_offset
) != 0)
924 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
926 /* Check the content, there could be a collision of
929 Unfortunately the sumhashent record does not include
930 the size of the stored data. So we have to search for
933 = (struct locrecent
*) ((char *) ah
->addr
934 + GET (head
->locrectab_offset
));
936 for (iloc
= 0; iloc
< GET (head
->locrectab_used
); ++iloc
)
937 if (GET (locrecent
[iloc
].refs
) != 0
938 && (GET (locrecent
[iloc
].record
[cnt
].offset
)
939 == GET (sumhashtab
[idx
].file_offset
)))
942 if (iloc
!= GET (head
->locrectab_used
)
943 && data
[cnt
].size
== GET (locrecent
[iloc
].record
[cnt
].len
)
944 /* We have to compare the content. Either we can
945 have the data mmaped or we have to read from
947 && (file_data_available_p
948 (ah
, GET (sumhashtab
[idx
].file_offset
),
950 ? memcmp (data
[cnt
].addr
,
952 + GET (sumhashtab
[idx
].file_offset
),
954 : compare_from_file (ah
, data
[cnt
].addr
,
955 GET (sumhashtab
[idx
].file_offset
),
956 data
[cnt
].size
) == 0))
959 file_offsets
[cnt
] = GET (sumhashtab
[idx
].file_offset
);
966 if (idx
>= GET (head
->sumhash_size
))
967 idx
-= GET (head
->sumhash_size
);
971 /* Find a slot for the locale name in the hash table. */
972 namehashent
= insert_name (ah
, name
, name_len
, replace
);
973 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
976 /* Determine whether we have to resize the file. */
977 if ((100 * (GET (head
->sumhash_used
) + num_new_offsets
)
978 > 75 * GET (head
->sumhash_size
))
979 || (GET (namehashent
->locrec_offset
) == 0
980 && (GET (head
->locrectab_used
) == GET (head
->locrectab_size
)
981 || (GET (head
->string_used
) + name_len
+ 1
982 > GET (head
->string_size
))
983 || (100 * GET (head
->namehash_used
)
984 > 75 * GET (head
->namehash_size
)))))
986 /* The current archive is not large enough. */
987 enlarge_archive (ah
, head
);
988 return add_locale (ah
, name
, data
, replace
);
991 /* Add the locale data which is not yet in the archive. */
992 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
993 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
994 && file_offsets
[cnt
] == 0)
996 /* The data for this section is not yet available in the
997 archive. Append it. */
1001 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
1002 if (lastpos
== (off64_t
) -1)
1003 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1005 /* If block of small categories would cross page boundary,
1006 align it unless it immediately follows a large category. */
1007 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
1008 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
1010 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
1012 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
1013 char *zeros
= alloca (sz
);
1015 memset (zeros
, 0, sz
);
1016 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
1017 error (EXIT_FAILURE
, errno
,
1018 _("cannot add to locale archive"));
1023 /* Align all data to a 16 byte boundary. */
1024 if ((lastpos
& 15) != 0)
1026 static const char zeros
[15] = { 0, };
1028 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
1029 != 16 - (lastpos
& 15))
1030 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1032 lastpos
+= 16 - (lastpos
& 15);
1035 /* Remember the position. */
1036 file_offsets
[cnt
] = lastpos
;
1037 lastoffset
= lastpos
+ data
[cnt
].size
;
1039 /* Write the data. */
1040 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
1042 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1044 /* Add the hash value to the hash table. */
1045 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
1047 idx
= md5hval
% GET (head
->sumhash_size
);
1048 incr
= 1 + md5hval
% (GET (head
->sumhash_size
) - 2);
1050 while (GET (sumhashtab
[idx
].file_offset
) != 0)
1053 if (idx
>= GET (head
->sumhash_size
))
1054 idx
-= GET (head
->sumhash_size
);
1057 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
1058 SET (sumhashtab
[idx
].file_offset
, file_offsets
[cnt
]);
1060 INC (head
->sumhash_used
, 1);
1063 lastoffset
= file_offsets
[LC_ALL
];
1064 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1065 if (small_mask
& (1 << cnt
))
1067 file_offsets
[cnt
] = lastoffset
;
1068 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
1071 if (GET (namehashent
->name_offset
) == 0)
1073 /* Add the name string. */
1074 memcpy ((char *) ah
->addr
+ GET (head
->string_offset
)
1075 + GET (head
->string_used
),
1076 name
, name_len
+ 1);
1077 SET (namehashent
->name_offset
,
1078 GET (head
->string_offset
) + GET (head
->string_used
));
1079 INC (head
->string_used
, name_len
+ 1);
1080 INC (head
->namehash_used
, 1);
1083 if (GET (namehashent
->locrec_offset
== 0))
1085 /* Allocate a name location record. */
1086 SET (namehashent
->locrec_offset
, (GET (head
->locrectab_offset
)
1087 + (GET (head
->locrectab_used
)
1088 * sizeof (struct locrecent
))));
1089 INC (head
->locrectab_used
, 1);
1090 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1091 + GET (namehashent
->locrec_offset
));
1092 SET (locrecent
->refs
, 1);
1096 /* If there are other aliases pointing to this locrecent,
1097 we still need a new one. If not, reuse the old one. */
1099 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1100 + GET (namehashent
->locrec_offset
));
1101 if (GET (locrecent
->refs
) > 1)
1103 INC (locrecent
->refs
, -1);
1104 SET (namehashent
->locrec_offset
, (GET (head
->locrectab_offset
)
1105 + (GET (head
->locrectab_used
)
1106 * sizeof (struct locrecent
))));
1107 INC (head
->locrectab_used
, 1);
1109 = (struct locrecent
*) ((char *) ah
->addr
1110 + GET (namehashent
->locrec_offset
));
1111 SET (locrecent
->refs
, 1);
1115 /* Fill in the table with the locations of the locale data. */
1116 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1118 SET (locrecent
->record
[cnt
].offset
, file_offsets
[cnt
]);
1119 SET (locrecent
->record
[cnt
].len
, data
[cnt
].size
);
1122 return GET (namehashent
->locrec_offset
);
1126 /* Check the content of the archive for duplicates. Add the content
1127 of the files if necessary. Add all the names, possibly overwriting
1130 add_locale_to_archive (struct locarhandle
*ah
, const char *name
,
1131 locale_data_t data
, bool replace
)
1133 char *normalized_name
= NULL
;
1134 uint32_t locrec_offset
;
1136 /* First analyze the name to decide how to archive it. */
1137 const char *language
;
1138 const char *modifier
;
1139 const char *territory
;
1140 const char *codeset
;
1141 const char *normalized_codeset
;
1142 int mask
= _nl_explode_name (strdupa (name
),
1143 &language
, &modifier
, &territory
,
1144 &codeset
, &normalized_codeset
);
1148 if (mask
& XPG_NORM_CODESET
)
1149 /* This name contains a codeset in unnormalized form.
1150 We will store it in the archive with a normalized name. */
1151 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
1152 language
, territory
== NULL
? "" : "_", territory
?: "",
1153 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
1154 modifier
== NULL
? "" : "@", modifier
?: "");
1156 /* This call does the main work. */
1157 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
1158 if (locrec_offset
== 0)
1160 free (normalized_name
);
1161 if (mask
& XPG_NORM_CODESET
)
1162 free ((char *) normalized_codeset
);
1166 if ((mask
& XPG_CODESET
) == 0)
1168 /* This name lacks a codeset, so determine the locale's codeset and
1169 add an alias for its name with normalized codeset appended. */
1174 unsigned int nstrings
;
1175 unsigned int strindex
[0];
1176 } *filedata
= data
[LC_CTYPE
].addr
;
1177 codeset
= (char *) filedata
1178 + maybe_swap_uint32 (filedata
->strindex
[_NL_ITEM_INDEX
1179 (_NL_CTYPE_CODESET_NAME
)]);
1180 char *normalized_codeset_name
= NULL
;
1182 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
1183 mask
|= XPG_NORM_CODESET
;
1185 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
1186 language
, territory
== NULL
? "" : "_", territory
?: "",
1188 modifier
== NULL
? "" : "@", modifier
?: "");
1190 add_alias (ah
, normalized_codeset_name
, replace
,
1191 normalized_name
?: name
, &locrec_offset
);
1192 free (normalized_codeset_name
);
1195 /* Now read the locale.alias files looking for lines whose
1196 right hand side matches our name after normalization. */
1198 if (alias_file
!= NULL
)
1201 fp
= fopen (alias_file
, "rm");
1203 error (1, errno
, _("locale alias file `%s' not found"),
1206 /* No threads present. */
1207 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
1209 while (! feof_unlocked (fp
))
1211 /* It is a reasonable approach to use a fix buffer here
1213 a) we are only interested in the first two fields
1214 b) these fields must be usable as file names and so must
1221 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1226 /* Ignore leading white space. */
1227 while (isspace (cp
[0]) && cp
[0] != '\n')
1230 /* A leading '#' signals a comment line. */
1231 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1234 while (cp
[0] != '\0' && !isspace (cp
[0]))
1236 /* Terminate alias name. */
1240 /* Now look for the beginning of the value. */
1241 while (isspace (cp
[0]))
1247 while (cp
[0] != '\0' && !isspace (cp
[0]))
1249 /* Terminate value. */
1252 /* This has to be done to make the following
1253 test for the end of line possible. We are
1254 looking for the terminating '\n' which do not
1259 else if (cp
[0] != '\0')
1262 /* Does this alias refer to our locale? We will
1263 normalize the right hand side and compare the
1264 elements of the normalized form. */
1266 const char *rhs_language
;
1267 const char *rhs_modifier
;
1268 const char *rhs_territory
;
1269 const char *rhs_codeset
;
1270 const char *rhs_normalized_codeset
;
1271 int rhs_mask
= _nl_explode_name (value
,
1276 &rhs_normalized_codeset
);
1282 if (!strcmp (language
, rhs_language
)
1283 && ((rhs_mask
& XPG_CODESET
)
1284 /* He has a codeset, it must match normalized. */
1285 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1286 ? normalized_codeset
: codeset
,
1287 (rhs_mask
& XPG_NORM_CODESET
)
1288 ? rhs_normalized_codeset
: rhs_codeset
)
1289 /* He has no codeset, we must also have none. */
1290 : (mask
& XPG_CODESET
) == 0)
1291 /* Codeset (or lack thereof) matches. */
1292 && !strcmp (territory
?: "", rhs_territory
?: "")
1293 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1294 /* We have a winner. */
1295 add_alias (ah
, alias
, replace
,
1296 normalized_name
?: name
, &locrec_offset
);
1297 if (rhs_mask
& XPG_NORM_CODESET
)
1298 free ((char *) rhs_normalized_codeset
);
1303 /* Possibly not the whole line fits into the buffer.
1304 Ignore the rest of the line. */
1305 while (strchr (cp
, '\n') == NULL
)
1308 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1309 /* Make sure the inner loop will be left. The outer
1310 loop will exit at the `feof' test. */
1319 free (normalized_name
);
1321 if (mask
& XPG_NORM_CODESET
)
1322 free ((char *) normalized_codeset
);
1329 add_locales_to_archive (size_t nlist
, char *list
[], bool replace
)
1331 struct locarhandle ah
;
1334 /* Open the archive. This call never returns if we cannot
1335 successfully open the archive. */
1337 open_archive (&ah
, false);
1341 const char *fname
= *list
++;
1342 size_t fnamelen
= strlen (fname
);
1351 printf (_("Adding %s\n"), fname
);
1353 /* First see whether this really is a directory and whether it
1354 contains all the require locale category files. */
1355 if (stat64 (fname
, &st
) < 0)
1357 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1361 if (!S_ISDIR (st
.st_mode
))
1363 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1367 dirp
= opendir (fname
);
1370 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1371 fname
, strerror (errno
));
1376 while ((d
= readdir64 (dirp
)) != NULL
)
1378 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1380 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1382 unsigned char d_type
;
1384 /* We have an object of the required name. If it's
1385 a directory we have to look at a file with the
1386 prefix "SYS_". Otherwise we have found what we
1388 #ifdef _DIRENT_HAVE_D_TYPE
1391 if (d_type
!= DT_REG
)
1394 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1396 #ifdef _DIRENT_HAVE_D_TYPE
1397 if (d_type
== DT_UNKNOWN
)
1400 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1403 if (stat64 (fullname
, &st
) == -1)
1404 /* We cannot stat the file, ignore it. */
1407 d_type
= IFTODT (st
.st_mode
);
1410 if (d_type
== DT_DIR
)
1412 /* We have to do more tests. The file is a
1413 directory and it therefore must contain a
1414 regular file with the same name except a
1416 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1417 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1420 if (stat64 (fullname
, &st
) == -1)
1421 /* There is no SYS_* file or we cannot
1425 d_type
= IFTODT (st
.st_mode
);
1429 /* If we found a regular file (eventually after
1430 following a symlink) we are successful. */
1431 if (d_type
== DT_REG
)
1439 if (seen
!= __LC_LAST
- 1)
1441 /* We don't have all locale category files. Ignore the name. */
1442 error (0, 0, _("incomplete set of locale files in \"%s\""),
1447 /* Add the files to the archive. To do this we first compute
1448 sizes and the MD5 sums of all the files. */
1449 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1452 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1455 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1456 fd
= open64 (fullname
, O_RDONLY
);
1457 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1459 /* Cannot read the file. */
1465 if (S_ISDIR (st
.st_mode
))
1469 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1470 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1473 fd
= open64 (fullname
, O_RDONLY
);
1474 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1475 || !S_ISREG (st
.st_mode
))
1484 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1486 if (data
[cnt
].addr
== MAP_FAILED
)
1488 /* Cannot map it. */
1493 data
[cnt
].size
= st
.st_size
;
1494 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1496 /* We don't need the file descriptor anymore. */
1500 if (cnt
!= __LC_LAST
)
1504 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1506 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1511 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1513 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1515 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1519 close_archive (&ah
);
1526 delete_locales_from_archive (size_t nlist
, char *list
[])
1528 struct locarhandle ah
;
1529 struct locarhead
*head
;
1530 struct namehashent
*namehashtab
;
1532 /* Open the archive. This call never returns if we cannot
1533 successfully open the archive. */
1535 open_archive (&ah
, false);
1538 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1539 + GET (head
->namehash_offset
));
1543 const char *locname
= *list
++;
1548 /* Search for this locale in the archive. */
1549 hval
= archive_hashval (locname
, strlen (locname
));
1551 idx
= hval
% GET (head
->namehash_size
);
1552 incr
= 1 + hval
% (GET (head
->namehash_size
) - 2);
1554 /* If the name_offset field is zero this means this is no
1555 deleted entry and therefore no entry can be found. */
1556 while (GET (namehashtab
[idx
].name_offset
) != 0)
1558 if (GET (namehashtab
[idx
].hashval
) == hval
1559 && (strcmp (locname
,
1561 + GET (namehashtab
[idx
].name_offset
)))
1564 /* Found the entry. Now mark it as removed by zero-ing
1565 the reference to the locale record. */
1566 SET (namehashtab
[idx
].locrec_offset
, 0);
1571 if (idx
>= GET (head
->namehash_size
))
1572 idx
-= GET (head
->namehash_size
);
1575 if (GET (namehashtab
[idx
].name_offset
) == 0 && ! be_quiet
)
1576 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1579 close_archive (&ah
);
1588 uint32_t locrec_offset
;
1594 const unsigned char *sum
;
1595 uint32_t file_offset
;
1601 nameentcmp (const void *a
, const void *b
)
1603 return strcmp (((const struct nameent
*) a
)->name
,
1604 ((const struct nameent
*) b
)->name
);
1609 dataentcmp (const void *a
, const void *b
)
1611 if (((const struct dataent
*) a
)->file_offset
1612 < ((const struct dataent
*) b
)->file_offset
)
1615 if (((const struct dataent
*) a
)->file_offset
1616 > ((const struct dataent
*) b
)->file_offset
)
1624 show_archive_content (const char *fname
, int verbose
)
1626 struct locarhandle ah
;
1627 struct locarhead
*head
;
1628 struct namehashent
*namehashtab
;
1629 struct nameent
*names
;
1632 /* Open the archive. This call never returns if we cannot
1633 successfully open the archive. */
1635 open_archive (&ah
, true);
1639 names
= (struct nameent
*) xmalloc (GET (head
->namehash_used
)
1640 * sizeof (struct nameent
));
1642 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1643 + GET (head
->namehash_offset
));
1644 for (cnt
= used
= 0; cnt
< GET (head
->namehash_size
); ++cnt
)
1645 if (GET (namehashtab
[cnt
].locrec_offset
) != 0)
1647 assert (used
< GET (head
->namehash_used
));
1648 names
[used
].name
= ah
.addr
+ GET (namehashtab
[cnt
].name_offset
);
1649 names
[used
++].locrec_offset
= GET (namehashtab
[cnt
].locrec_offset
);
1652 /* Sort the names. */
1653 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1657 struct dataent
*files
;
1658 struct sumhashent
*sumhashtab
;
1661 files
= (struct dataent
*) xmalloc (GET (head
->sumhash_used
)
1662 * sizeof (struct dataent
));
1664 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1665 + GET (head
->sumhash_offset
));
1666 for (cnt
= sumused
= 0; cnt
< GET (head
->sumhash_size
); ++cnt
)
1667 if (GET (sumhashtab
[cnt
].file_offset
) != 0)
1669 assert (sumused
< GET (head
->sumhash_used
));
1670 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1671 files
[sumused
].file_offset
= GET (sumhashtab
[cnt
].file_offset
);
1672 files
[sumused
++].nlink
= 0;
1675 /* Sort by file locations. */
1676 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1678 /* Compute nlink fields. */
1679 for (cnt
= 0; cnt
< used
; ++cnt
)
1681 struct locrecent
*locrec
;
1684 locrec
= (struct locrecent
*) ((char *) ah
.addr
1685 + names
[cnt
].locrec_offset
);
1686 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1687 if (GET (locrec
->record
[LC_ALL
].offset
) != 0
1689 || (GET (locrec
->record
[idx
].offset
)
1690 < GET (locrec
->record
[LC_ALL
].offset
))
1691 || ((GET (locrec
->record
[idx
].offset
)
1692 + GET (locrec
->record
[idx
].len
))
1693 > (GET (locrec
->record
[LC_ALL
].offset
)
1694 + GET (locrec
->record
[LC_ALL
].len
))))
1697 struct dataent
*data
, dataent
;
1699 dataent
.file_offset
= GET (locrec
->record
[idx
].offset
);
1700 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1701 sizeof (struct dataent
),
1703 assert (data
!= NULL
);
1709 for (cnt
= 0; cnt
< used
; ++cnt
)
1711 struct locrecent
*locrec
;
1714 locrec
= (struct locrecent
*) ((char *) ah
.addr
1715 + names
[cnt
].locrec_offset
);
1716 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1719 struct dataent
*data
, dataent
;
1721 dataent
.file_offset
= GET (locrec
->record
[idx
].offset
);
1722 if (GET (locrec
->record
[LC_ALL
].offset
) != 0
1723 && (dataent
.file_offset
1724 >= GET (locrec
->record
[LC_ALL
].offset
))
1725 && (dataent
.file_offset
+ GET (locrec
->record
[idx
].len
)
1726 <= (GET (locrec
->record
[LC_ALL
].offset
)
1727 + GET (locrec
->record
[LC_ALL
].len
))))
1728 dataent
.file_offset
= GET (locrec
->record
[LC_ALL
].offset
);
1730 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1731 sizeof (struct dataent
),
1733 printf ("%6d %7x %3d%c ",
1734 GET (locrec
->record
[idx
].len
),
1735 GET (locrec
->record
[idx
].offset
),
1737 (dataent
.file_offset
1738 == GET (locrec
->record
[LC_ALL
].offset
))
1740 for (i
= 0; i
< 16; i
+= 4)
1741 printf ("%02x%02x%02x%02x",
1742 data
->sum
[i
], data
->sum
[i
+ 1],
1743 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1744 printf (" %s/%s\n", names
[cnt
].name
,
1745 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1751 for (cnt
= 0; cnt
< used
; ++cnt
)
1752 puts (names
[cnt
].name
);
1754 close_archive (&ah
);
1756 exit (EXIT_SUCCESS
);