1 /* Copyright (C) 2002-2023 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation; version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
31 #include <stdio_ext.h>
38 #include <sys/param.h>
42 #include <libc-mmap.h>
43 #include <libc-pointer-arith.h>
44 #include "../../crypt/md5.h"
45 #include "../localeinfo.h"
46 #include "../locarchive.h"
47 #include "localedef.h"
50 /* Define the hash function. We define the function as static inline.
51 We must change the name so as not to conflict with simple-hash.h. */
52 #define compute_hashval static archive_hashval
53 #define hashval_t uint32_t
55 #undef compute_hashval
57 extern const char *output_prefix
;
59 #define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
61 static const char *locnames
[] =
63 #define DEFINE_CATEGORY(category, category_name, items, a) \
64 [category] = category_name,
65 #include "categories.def"
66 #undef DEFINE_CATEGORY
70 /* Size of the initial archive header. */
71 #define INITIAL_NUM_NAMES 900
72 #define INITIAL_SIZE_STRINGS 7500
73 #define INITIAL_NUM_LOCREC 420
74 #define INITIAL_NUM_SUMS 2000
77 /* Get and set values (possibly endian-swapped) in structures mapped
78 from or written directly to locale archives. */
79 #define GET(FIELD) maybe_swap_uint32 (FIELD)
80 #define SET(FIELD, VALUE) ((FIELD) = maybe_swap_uint32 (VALUE))
81 #define INC(FIELD, INCREMENT) SET (FIELD, GET (FIELD) + (INCREMENT))
84 /* Size of the reserved address space area. */
85 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
87 /* To prepare for enlargements of the mmaped area reserve some address
88 space. On some machines, being a file mapping rather than an anonymous
89 mapping affects the address selection. So do this mapping from the
90 actual file, even though it's only a dummy to reserve address space. */
92 prepare_address_space (int fd
, size_t total
, size_t *reserved
, int *xflags
,
93 void **mmap_base
, size_t *mmap_len
)
95 if (total
< RESERVE_MMAP_SIZE
)
97 void *p
= mmap64 (NULL
, RESERVE_MMAP_SIZE
, PROT_NONE
, MAP_SHARED
, fd
, 0);
100 void *aligned_p
= PTR_ALIGN_UP (p
, MAP_FIXED_ALIGNMENT
);
101 size_t align_adjust
= aligned_p
- p
;
103 *mmap_len
= RESERVE_MMAP_SIZE
;
104 assert (align_adjust
< RESERVE_MMAP_SIZE
);
105 *reserved
= RESERVE_MMAP_SIZE
- align_adjust
;
120 create_archive (const char *archivefname
, struct locarhandle
*ah
)
123 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
124 struct locarhead head
;
127 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
129 /* Create a temporary file in the correct directory. */
130 fd
= mkstemp (fname
);
132 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
134 /* Create the initial content of the archive. */
135 SET (head
.magic
, AR_MAGIC
);
136 SET (head
.serial
, 0);
137 SET (head
.namehash_offset
, sizeof (struct locarhead
));
138 SET (head
.namehash_used
, 0);
139 SET (head
.namehash_size
, next_prime (INITIAL_NUM_NAMES
));
141 SET (head
.string_offset
,
142 (GET (head
.namehash_offset
)
143 + GET (head
.namehash_size
) * sizeof (struct namehashent
)));
144 SET (head
.string_used
, 0);
145 SET (head
.string_size
, INITIAL_SIZE_STRINGS
);
147 SET (head
.locrectab_offset
,
148 GET (head
.string_offset
) + GET (head
.string_size
));
149 SET (head
.locrectab_used
, 0);
150 SET (head
.locrectab_size
, INITIAL_NUM_LOCREC
);
152 SET (head
.sumhash_offset
,
153 (GET (head
.locrectab_offset
)
154 + GET (head
.locrectab_size
) * sizeof (struct locrecent
)));
155 SET (head
.sumhash_used
, 0);
156 SET (head
.sumhash_size
, next_prime (INITIAL_NUM_SUMS
));
158 total
= (GET (head
.sumhash_offset
)
159 + GET (head
.sumhash_size
) * sizeof (struct sumhashent
));
161 /* Write out the header and create room for the other data structures. */
162 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
166 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
169 if (ftruncate64 (fd
, total
) != 0)
173 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
176 size_t reserved
, mmap_len
;
179 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
182 /* Map the header and all the administration data structures. */
183 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
188 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
191 /* Now try to rename it. We don't use the rename function since
192 this would overwrite a file which has been created in
194 if (link (fname
, archivefname
) == -1)
198 /* We cannot use the just created file. */
202 if (errval
== EEXIST
)
204 /* There is already an archive. Must have been a localedef run
205 which happened in parallel. Simply open this file then. */
206 open_archive (ah
, false);
210 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
213 /* Remove the temporary name. */
216 /* Make the file globally readable. */
217 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
220 unlink (archivefname
);
221 error (EXIT_FAILURE
, errval
,
222 _("cannot change mode of new locale archive"));
227 ah
->mmap_base
= mmap_base
;
228 ah
->mmap_len
= mmap_len
;
231 ah
->reserved
= reserved
;
235 /* This structure and qsort comparator function are used below to sort an
236 old archive's locrec table in order of data position in the file. */
240 struct locrecent
*locrec
;
244 oldlocrecentcmp (const void *a
, const void *b
)
246 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
247 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
248 uint32_t start_a
= -1, end_a
= 0;
249 uint32_t start_b
= -1, end_b
= 0;
252 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
255 if (GET (la
->record
[cnt
].offset
) < start_a
)
256 start_a
= GET (la
->record
[cnt
].offset
);
257 if (GET (la
->record
[cnt
].offset
) + GET (la
->record
[cnt
].len
) > end_a
)
258 end_a
= GET (la
->record
[cnt
].offset
) + GET (la
->record
[cnt
].len
);
260 assert (start_a
!= (uint32_t)-1);
263 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
266 if (GET (lb
->record
[cnt
].offset
) < start_b
)
267 start_b
= GET (lb
->record
[cnt
].offset
);
268 if (GET (lb
->record
[cnt
].offset
) + GET (lb
->record
[cnt
].len
) > end_b
)
269 end_b
= GET (lb
->record
[cnt
].offset
) + GET (lb
->record
[cnt
].len
);
271 assert (start_b
!= (uint32_t)-1);
274 if (start_a
!= start_b
)
275 return (int)start_a
- (int)start_b
;
276 return (int)end_a
- (int)end_b
;
280 /* forward decls for below */
281 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
282 locale_data_t data
, bool replace
);
283 static void add_alias (struct locarhandle
*ah
, const char *alias
,
284 bool replace
, const char *oldname
,
285 uint32_t *locrec_offset_p
);
289 file_data_available_p (struct locarhandle
*ah
, uint32_t offset
, uint32_t size
)
291 if (offset
< ah
->mmaped
&& offset
+ size
<= ah
->mmaped
)
295 if (fstat64 (ah
->fd
, &st
) != 0)
298 if (st
.st_size
> ah
->reserved
)
301 size_t start
= ALIGN_DOWN (ah
->mmaped
, MAP_FIXED_ALIGNMENT
);
302 void *p
= mmap64 (ah
->addr
+ start
, st
.st_size
- start
,
303 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_FIXED
,
311 ah
->mmaped
= st
.st_size
;
317 compare_from_file (struct locarhandle
*ah
, void *p1
, uint32_t offset2
,
320 void *p2
= xmalloc (size
);
321 if (pread (ah
->fd
, p2
, size
, offset2
) != size
)
322 record_error (4, errno
,
323 _("cannot read data from locale archive"));
325 int res
= memcmp (p1
, p2
, size
);
332 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
336 struct locarhead newhead
;
338 unsigned int cnt
, loccnt
;
339 struct namehashent
*oldnamehashtab
;
340 struct locarhandle new_ah
;
341 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
342 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
343 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
346 memcpy (archivefname
, output_prefix
, prefix_len
);
347 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
348 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
350 /* Not all of the old file has to be mapped. Change this now this
351 we will have to access the whole content. */
352 if (fstat64 (ah
->fd
, &st
) != 0)
354 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
356 if (st
.st_size
< ah
->reserved
)
357 ah
->addr
= mmap64 (ah
->addr
, st
.st_size
, PROT_READ
| PROT_WRITE
,
358 MAP_SHARED
| MAP_FIXED
, ah
->fd
, 0);
362 munmap (ah
->mmap_base
, ah
->mmap_len
);
364 munmap (ah
->addr
, ah
->reserved
);
365 ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
366 MAP_SHARED
, ah
->fd
, 0);
367 ah
->reserved
= st
.st_size
;
368 ah
->mmap_base
= NULL
;
372 if (ah
->addr
== MAP_FAILED
)
374 ah
->mmaped
= st
.st_size
;
376 /* Create a temporary file in the correct directory. */
377 fd
= mkstemp (fname
);
379 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
381 /* Copy the existing head information. */
384 /* Create the new archive header. The sizes of the various tables
385 should be double from what is currently used. */
386 SET (newhead
.namehash_size
,
387 MAX (next_prime (2 * GET (newhead
.namehash_used
)),
388 GET (newhead
.namehash_size
)));
390 printf ("name: size: %u, used: %d, new: size: %u\n",
391 GET (head
->namehash_size
),
392 GET (head
->namehash_used
), GET (newhead
.namehash_size
));
394 SET (newhead
.string_offset
, (GET (newhead
.namehash_offset
)
395 + (GET (newhead
.namehash_size
)
396 * sizeof (struct namehashent
))));
397 /* Keep the string table size aligned to 4 bytes, so that
398 all the struct { uint32_t } types following are happy. */
399 SET (newhead
.string_size
, MAX ((2 * GET (newhead
.string_used
) + 3) & -4,
400 GET (newhead
.string_size
)));
402 SET (newhead
.locrectab_offset
,
403 GET (newhead
.string_offset
) + GET (newhead
.string_size
));
404 SET (newhead
.locrectab_size
, MAX (2 * GET (newhead
.locrectab_used
),
405 GET (newhead
.locrectab_size
)));
407 SET (newhead
.sumhash_offset
, (GET (newhead
.locrectab_offset
)
408 + (GET (newhead
.locrectab_size
)
409 * sizeof (struct locrecent
))));
410 SET (newhead
.sumhash_size
,
411 MAX (next_prime (2 * GET (newhead
.sumhash_used
)),
412 GET (newhead
.sumhash_size
)));
414 total
= (GET (newhead
.sumhash_offset
)
415 + GET (newhead
.sumhash_size
) * sizeof (struct sumhashent
));
417 /* The new file is empty now. */
418 SET (newhead
.namehash_used
, 0);
419 SET (newhead
.string_used
, 0);
420 SET (newhead
.locrectab_used
, 0);
421 SET (newhead
.sumhash_used
, 0);
423 /* Write out the header and create room for the other data structures. */
424 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
429 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
432 if (ftruncate64 (fd
, total
) != 0)
436 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
439 size_t reserved
, mmap_len
;
442 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
445 /* Map the header and all the administration data structures. */
446 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
451 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
454 /* Lock the new file. */
455 if (lockf64 (fd
, F_LOCK
, total
) != 0)
459 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
462 new_ah
.mmaped
= total
;
463 new_ah
.mmap_base
= mmap_base
;
464 new_ah
.mmap_len
= mmap_len
;
467 new_ah
.reserved
= reserved
;
469 /* Walk through the hash name hash table to find out what data is
470 still referenced and transfer it into the new file. */
471 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
472 + GET (head
->namehash_offset
));
474 /* Sort the old locrec table in order of data position. */
475 struct oldlocrecent oldlocrecarray
[GET (head
->namehash_size
)];
476 for (cnt
= 0, loccnt
= 0; cnt
< GET (head
->namehash_size
); ++cnt
)
477 if (GET (oldnamehashtab
[cnt
].locrec_offset
) != 0)
479 oldlocrecarray
[loccnt
].cnt
= cnt
;
480 oldlocrecarray
[loccnt
++].locrec
481 = (struct locrecent
*) ((char *) ah
->addr
482 + GET (oldnamehashtab
[cnt
].locrec_offset
));
484 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
487 uint32_t last_locrec_offset
= 0;
488 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
490 /* Insert this entry in the new hash table. */
491 locale_data_t old_data
;
493 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
495 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
498 old_data
[idx
].size
= GET (oldlocrec
->record
[idx
].len
);
500 = ((char *) ah
->addr
+ GET (oldlocrec
->record
[idx
].offset
));
502 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
506 if (cnt
> 0 && oldlocrecarray
[cnt
- 1].locrec
== oldlocrec
)
510 + GET (oldnamehashtab
[oldlocrecarray
[cnt
511 - 1].cnt
].name_offset
));
516 + GET (oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
)),
517 0, oldname
, &last_locrec_offset
);
525 + GET (oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
)),
527 if (last_locrec_offset
== 0)
528 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
531 /* Make the file globally readable. */
532 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
536 error (EXIT_FAILURE
, errval
,
537 _("cannot change mode of resized locale archive"));
540 /* Rename the new file. */
541 if (rename (fname
, archivefname
) != 0)
545 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
548 /* Close the old file. */
551 /* Add the information for the new one. */
557 open_archive (struct locarhandle
*ah
, bool readonly
)
562 struct locarhead head
;
564 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
565 char default_fname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
566 const char *archivefname
= ah
->fname
;
568 /* If ah has a non-NULL fname open that otherwise open the default. */
569 if (archivefname
== NULL
)
571 archivefname
= default_fname
;
573 memcpy (default_fname
, output_prefix
, prefix_len
);
574 strcpy (default_fname
+ prefix_len
, ARCHIVE_NAME
);
579 /* Open the archive. We must have exclusive write access. */
580 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
583 /* Maybe the file does not yet exist? If we are opening
584 the default locale archive we ignore the failure and
585 list an empty archive, otherwise we print an error
587 if (errno
== ENOENT
&& archivefname
== default_fname
)
591 static const struct locarhead nullhead
=
594 .namehash_offset
= 0,
598 ah
->addr
= (void *) &nullhead
;
602 create_archive (archivefname
, ah
);
607 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
611 if (fstat64 (fd
, &st
) < 0)
612 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
615 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
619 if (retry
++ < max_locarchive_open_retry
)
623 /* Wait for a bit. */
625 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
626 (void) nanosleep (&req
, NULL
);
631 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
635 /* One more check. Maybe another process replaced the archive file
636 with a new, larger one since we opened the file. */
637 if (stat64 (archivefname
, &st2
) == -1
638 || st
.st_dev
!= st2
.st_dev
639 || st
.st_ino
!= st2
.st_ino
)
641 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
646 /* Leave the loop. */
650 /* Read the header. */
651 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
653 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
654 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
657 /* Check the magic value */
658 if (GET (head
.magic
) != AR_MAGIC
)
660 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
661 error (EXIT_FAILURE
, 0, _("bad magic value in archive header"));
665 ah
->mmaped
= st
.st_size
;
667 size_t reserved
, mmap_len
;
670 void *p
= prepare_address_space (fd
, st
.st_size
, &reserved
, &xflags
,
671 &mmap_base
, &mmap_len
);
673 /* Map the entire file. We might need to compare the category data
674 in the file with the newly added data. */
675 ah
->addr
= mmap64 (p
, st
.st_size
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
676 MAP_SHARED
| xflags
, fd
, 0);
677 if (ah
->addr
== MAP_FAILED
)
679 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
680 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
682 ah
->reserved
= reserved
;
683 ah
->mmap_base
= mmap_base
;
684 ah
->mmap_len
= mmap_len
;
689 close_archive (struct locarhandle
*ah
)
694 munmap (ah
->mmap_base
, ah
->mmap_len
);
696 munmap (ah
->addr
, ah
->reserved
);
701 #include "../../intl/explodename.c"
702 #include "../../intl/l10nflist.c"
704 static struct namehashent
*
705 insert_name (struct locarhandle
*ah
,
706 const char *name
, size_t name_len
, bool replace
)
708 const struct locarhead
*const head
= ah
->addr
;
709 struct namehashent
*namehashtab
710 = (struct namehashent
*) ((char *) ah
->addr
711 + GET (head
->namehash_offset
));
712 unsigned int insert_idx
, idx
, incr
;
714 /* Hash value of the locale name. */
715 uint32_t hval
= archive_hashval (name
, name_len
);
718 idx
= hval
% GET (head
->namehash_size
);
719 incr
= 1 + hval
% (GET (head
->namehash_size
) - 2);
721 /* If the name_offset field is zero this means this is a
722 deleted entry and therefore no entry can be found. */
723 while (GET (namehashtab
[idx
].name_offset
) != 0)
725 if (GET (namehashtab
[idx
].hashval
) == hval
727 (char *) ah
->addr
+ GET (namehashtab
[idx
].name_offset
))
730 /* Found the entry. */
731 if (GET (namehashtab
[idx
].locrec_offset
) != 0 && ! replace
)
734 error (0, 0, _("locale '%s' already exists"), name
);
741 if (GET (namehashtab
[idx
].hashval
) == hval
&& ! be_quiet
)
743 error (0, 0, "hash collision (%u) %s, %s",
745 (char *) ah
->addr
+ GET (namehashtab
[idx
].name_offset
));
748 /* Remember the first place we can insert the new entry. */
749 if (GET (namehashtab
[idx
].locrec_offset
) == 0 && insert_idx
== -1)
753 if (idx
>= GET (head
->namehash_size
))
754 idx
-= GET (head
->namehash_size
);
757 /* Add as early as possible. */
758 if (insert_idx
!= -1)
761 SET (namehashtab
[idx
].hashval
, hval
); /* no-op if replacing an old entry. */
762 return &namehashtab
[idx
];
766 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
767 const char *oldname
, uint32_t *locrec_offset_p
)
769 uint32_t locrec_offset
= *locrec_offset_p
;
770 struct locarhead
*head
= ah
->addr
;
771 const size_t name_len
= strlen (alias
);
772 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
774 if (namehashent
== NULL
&& ! replace
)
777 if (GET (namehashent
->name_offset
) == 0)
779 /* We are adding a new hash entry for this alias.
780 Determine whether we have to resize the file. */
781 if (GET (head
->string_used
) + name_len
+ 1 > GET (head
->string_size
)
782 || (100 * GET (head
->namehash_used
)
783 > 75 * GET (head
->namehash_size
)))
785 /* The current archive is not large enough. */
786 enlarge_archive (ah
, head
);
788 /* The locrecent might have moved, so we have to look up
789 the old name afresh. */
790 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
791 assert (GET (namehashent
->name_offset
) != 0);
792 assert (GET (namehashent
->locrec_offset
) != 0);
793 *locrec_offset_p
= GET (namehashent
->locrec_offset
);
795 /* Tail call to try the whole thing again. */
796 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
800 /* Add the name string. */
801 memcpy (ah
->addr
+ GET (head
->string_offset
) + GET (head
->string_used
),
802 alias
, name_len
+ 1);
803 SET (namehashent
->name_offset
,
804 GET (head
->string_offset
) + GET (head
->string_used
));
805 INC (head
->string_used
, name_len
+ 1);
807 INC (head
->namehash_used
, 1);
810 if (GET (namehashent
->locrec_offset
) != 0)
812 /* Replacing an existing entry.
813 Mark that we are no longer using the old locrecent. */
814 struct locrecent
*locrecent
815 = (struct locrecent
*) ((char *) ah
->addr
816 + GET (namehashent
->locrec_offset
));
817 INC (locrecent
->refs
, -1);
820 /* Point this entry at the locrecent installed for the main name. */
821 SET (namehashent
->locrec_offset
, locrec_offset
);
824 static int /* qsort comparator used below */
825 cmpcategorysize (const void *a
, const void *b
)
827 if (*(const void **) a
== NULL
)
829 if (*(const void **) b
== NULL
)
831 return ((*(const struct locale_category_data
**) a
)->size
832 - (*(const struct locale_category_data
**) b
)->size
);
835 /* Check the content of the archive for duplicates. Add the content
836 of the files if necessary. Returns the locrec_offset. */
838 add_locale (struct locarhandle
*ah
,
839 const char *name
, locale_data_t data
, bool replace
)
841 /* First look for the name. If it already exists and we are not
842 supposed to replace it don't do anything. If it does not exist
843 we have to allocate a new locale record. */
844 size_t name_len
= strlen (name
);
845 uint32_t file_offsets
[__LC_LAST
];
846 unsigned int num_new_offsets
= 0;
847 struct sumhashent
*sumhashtab
;
849 unsigned int cnt
, idx
;
850 struct locarhead
*head
;
851 struct namehashent
*namehashent
;
853 struct locrecent
*locrecent
;
856 struct locale_category_data
*size_order
[__LC_LAST
];
857 /* Page size alignment is a minor optimization for locality; use a
858 common value here rather than making the localedef output depend
859 on the page size of the system on which localedef is run. See
860 <https://sourceware.org/glibc/wiki/Development_Todo/Master#Locale_archive_alignment>
861 for more discussion. */
862 const size_t pagesz
= 4096;
866 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
867 + GET (head
->sumhash_offset
));
869 memset (file_offsets
, 0, sizeof (file_offsets
));
871 size_order
[LC_ALL
] = NULL
;
872 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
874 size_order
[cnt
] = &data
[cnt
];
876 /* Sort the array in ascending order of data size. */
877 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
880 data
[LC_ALL
].size
= 0;
881 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
882 if (size_order
[cnt
] != NULL
)
884 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
885 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
887 /* This category makes the small-categories block
888 stop being small, so this is the end of the road. */
890 size_order
[cnt
++] = NULL
;
891 while (cnt
< __LC_LAST
);
894 data
[LC_ALL
].size
+= rounded_size
;
895 small_mask
|= 1 << (size_order
[cnt
] - data
);
898 /* Copy the data for all the small categories into the LC_ALL
901 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
902 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
904 ptr
= data
[LC_ALL
].addr
;
905 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
906 if (small_mask
& (1 << cnt
))
908 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
909 ptr
+= (data
[cnt
].size
+ 15) & -16;
911 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
913 /* For each locale category data set determine whether the same data
914 is already somewhere in the archive. */
915 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
916 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
920 /* Compute the hash value of the checksum to determine a
921 starting point for the search in the MD5 hash value
923 hval
= archive_hashval (data
[cnt
].sum
, 16);
925 idx
= hval
% GET (head
->sumhash_size
);
926 incr
= 1 + hval
% (GET (head
->sumhash_size
) - 2);
928 while (GET (sumhashtab
[idx
].file_offset
) != 0)
930 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
932 /* Check the content, there could be a collision of
935 Unfortunately the sumhashent record does not include
936 the size of the stored data. So we have to search for
939 = (struct locrecent
*) ((char *) ah
->addr
940 + GET (head
->locrectab_offset
));
942 for (iloc
= 0; iloc
< GET (head
->locrectab_used
); ++iloc
)
943 if (GET (locrecent
[iloc
].refs
) != 0
944 && (GET (locrecent
[iloc
].record
[cnt
].offset
)
945 == GET (sumhashtab
[idx
].file_offset
)))
948 if (iloc
!= GET (head
->locrectab_used
)
949 && data
[cnt
].size
== GET (locrecent
[iloc
].record
[cnt
].len
)
950 /* We have to compare the content. Either we can
951 have the data mmaped or we have to read from
953 && (file_data_available_p
954 (ah
, GET (sumhashtab
[idx
].file_offset
),
956 ? memcmp (data
[cnt
].addr
,
958 + GET (sumhashtab
[idx
].file_offset
),
960 : compare_from_file (ah
, data
[cnt
].addr
,
961 GET (sumhashtab
[idx
].file_offset
),
962 data
[cnt
].size
) == 0))
965 file_offsets
[cnt
] = GET (sumhashtab
[idx
].file_offset
);
972 if (idx
>= GET (head
->sumhash_size
))
973 idx
-= GET (head
->sumhash_size
);
977 /* Find a slot for the locale name in the hash table. */
978 namehashent
= insert_name (ah
, name
, name_len
, replace
);
979 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
982 /* Determine whether we have to resize the file. */
983 if ((100 * (GET (head
->sumhash_used
) + num_new_offsets
)
984 > 75 * GET (head
->sumhash_size
))
985 || (GET (namehashent
->locrec_offset
) == 0
986 && (GET (head
->locrectab_used
) == GET (head
->locrectab_size
)
987 || (GET (head
->string_used
) + name_len
+ 1
988 > GET (head
->string_size
))
989 || (100 * GET (head
->namehash_used
)
990 > 75 * GET (head
->namehash_size
)))))
992 /* The current archive is not large enough. */
993 enlarge_archive (ah
, head
);
994 return add_locale (ah
, name
, data
, replace
);
997 /* Add the locale data which is not yet in the archive. */
998 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
999 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
1000 && file_offsets
[cnt
] == 0)
1002 /* The data for this section is not yet available in the
1003 archive. Append it. */
1007 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
1008 if (lastpos
== (off64_t
) -1)
1009 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1011 /* If block of small categories would cross page boundary,
1012 align it unless it immediately follows a large category. */
1013 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
1014 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
1016 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
1018 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
1019 char *zeros
= alloca (sz
);
1021 memset (zeros
, 0, sz
);
1022 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
1023 error (EXIT_FAILURE
, errno
,
1024 _("cannot add to locale archive"));
1029 /* Align all data to a 16 byte boundary. */
1030 if ((lastpos
& 15) != 0)
1032 static const char zeros
[15] = { 0, };
1034 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
1035 != 16 - (lastpos
& 15))
1036 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1038 lastpos
+= 16 - (lastpos
& 15);
1041 /* Remember the position. */
1042 file_offsets
[cnt
] = lastpos
;
1043 lastoffset
= lastpos
+ data
[cnt
].size
;
1045 /* Write the data. */
1046 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
1048 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1050 /* Add the hash value to the hash table. */
1051 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
1053 idx
= md5hval
% GET (head
->sumhash_size
);
1054 incr
= 1 + md5hval
% (GET (head
->sumhash_size
) - 2);
1056 while (GET (sumhashtab
[idx
].file_offset
) != 0)
1059 if (idx
>= GET (head
->sumhash_size
))
1060 idx
-= GET (head
->sumhash_size
);
1063 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
1064 SET (sumhashtab
[idx
].file_offset
, file_offsets
[cnt
]);
1066 INC (head
->sumhash_used
, 1);
1069 lastoffset
= file_offsets
[LC_ALL
];
1070 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1071 if (small_mask
& (1 << cnt
))
1073 file_offsets
[cnt
] = lastoffset
;
1074 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
1077 if (GET (namehashent
->name_offset
) == 0)
1079 /* Add the name string. */
1080 memcpy ((char *) ah
->addr
+ GET (head
->string_offset
)
1081 + GET (head
->string_used
),
1082 name
, name_len
+ 1);
1083 SET (namehashent
->name_offset
,
1084 GET (head
->string_offset
) + GET (head
->string_used
));
1085 INC (head
->string_used
, name_len
+ 1);
1086 INC (head
->namehash_used
, 1);
1089 if (GET (namehashent
->locrec_offset
== 0))
1091 /* Allocate a name location record. */
1092 SET (namehashent
->locrec_offset
, (GET (head
->locrectab_offset
)
1093 + (GET (head
->locrectab_used
)
1094 * sizeof (struct locrecent
))));
1095 INC (head
->locrectab_used
, 1);
1096 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1097 + GET (namehashent
->locrec_offset
));
1098 SET (locrecent
->refs
, 1);
1102 /* If there are other aliases pointing to this locrecent,
1103 we still need a new one. If not, reuse the old one. */
1105 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1106 + GET (namehashent
->locrec_offset
));
1107 if (GET (locrecent
->refs
) > 1)
1109 INC (locrecent
->refs
, -1);
1110 SET (namehashent
->locrec_offset
, (GET (head
->locrectab_offset
)
1111 + (GET (head
->locrectab_used
)
1112 * sizeof (struct locrecent
))));
1113 INC (head
->locrectab_used
, 1);
1115 = (struct locrecent
*) ((char *) ah
->addr
1116 + GET (namehashent
->locrec_offset
));
1117 SET (locrecent
->refs
, 1);
1121 /* Fill in the table with the locations of the locale data. */
1122 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1124 SET (locrecent
->record
[cnt
].offset
, file_offsets
[cnt
]);
1125 SET (locrecent
->record
[cnt
].len
, data
[cnt
].size
);
1128 return GET (namehashent
->locrec_offset
);
1132 /* Check the content of the archive for duplicates. Add the content
1133 of the files if necessary. Add all the names, possibly overwriting
1136 add_locale_to_archive (struct locarhandle
*ah
, const char *name
,
1137 locale_data_t data
, bool replace
)
1139 char *normalized_name
= NULL
;
1140 uint32_t locrec_offset
;
1142 /* First analyze the name to decide how to archive it. */
1143 const char *language
;
1144 const char *modifier
;
1145 const char *territory
;
1146 const char *codeset
;
1147 const char *normalized_codeset
;
1148 int mask
= _nl_explode_name (strdupa (name
),
1149 &language
, &modifier
, &territory
,
1150 &codeset
, &normalized_codeset
);
1154 if (mask
& XPG_NORM_CODESET
)
1155 /* This name contains a codeset in unnormalized form.
1156 We will store it in the archive with a normalized name. */
1157 if (asprintf (&normalized_name
, "%s%s%s.%s%s%s",
1158 language
, territory
== NULL
? "" : "_", territory
?: "",
1160 modifier
== NULL
? "" : "@", modifier
?: "") < 0)
1162 free ((char *) normalized_codeset
);
1166 /* This call does the main work. */
1167 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
1168 if (locrec_offset
== 0)
1170 free (normalized_name
);
1171 if (mask
& XPG_NORM_CODESET
)
1172 free ((char *) normalized_codeset
);
1176 if ((mask
& XPG_CODESET
) == 0)
1178 /* This name lacks a codeset, so determine the locale's codeset and
1179 add an alias for its name with normalized codeset appended. */
1184 unsigned int nstrings
;
1185 unsigned int strindex
[0];
1186 } *filedata
= data
[LC_CTYPE
].addr
;
1187 codeset
= (char *) filedata
1188 + maybe_swap_uint32 (filedata
->strindex
[_NL_ITEM_INDEX
1189 (_NL_CTYPE_CODESET_NAME
)]);
1190 char *normalized_codeset_name
= NULL
;
1192 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
1193 mask
|= XPG_NORM_CODESET
;
1195 if (asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
1196 language
, territory
== NULL
? "" : "_", territory
?: "",
1198 modifier
== NULL
? "" : "@", modifier
?: "") < 0)
1200 free ((char *) normalized_codeset
);
1204 add_alias (ah
, normalized_codeset_name
, replace
,
1205 normalized_name
?: name
, &locrec_offset
);
1206 free (normalized_codeset_name
);
1209 /* Now read the locale.alias files looking for lines whose
1210 right hand side matches our name after normalization. */
1212 if (alias_file
!= NULL
)
1215 fp
= fopen (alias_file
, "rm");
1217 error (1, errno
, _("locale alias file `%s' not found"),
1220 /* No threads present. */
1221 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
1223 while (! feof_unlocked (fp
))
1225 /* It is a reasonable approach to use a fix buffer here
1227 a) we are only interested in the first two fields
1228 b) these fields must be usable as file names and so must
1235 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1240 /* Ignore leading white space. */
1241 while (isspace (cp
[0]) && cp
[0] != '\n')
1244 /* A leading '#' signals a comment line. */
1245 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1248 while (cp
[0] != '\0' && !isspace (cp
[0]))
1250 /* Terminate alias name. */
1254 /* Now look for the beginning of the value. */
1255 while (isspace (cp
[0]))
1261 while (cp
[0] != '\0' && !isspace (cp
[0]))
1263 /* Terminate value. */
1266 /* This has to be done to make the following
1267 test for the end of line possible. We are
1268 looking for the terminating '\n' which do not
1273 else if (cp
[0] != '\0')
1276 /* Does this alias refer to our locale? We will
1277 normalize the right hand side and compare the
1278 elements of the normalized form. */
1280 const char *rhs_language
;
1281 const char *rhs_modifier
;
1282 const char *rhs_territory
;
1283 const char *rhs_codeset
;
1284 const char *rhs_normalized_codeset
;
1285 int rhs_mask
= _nl_explode_name (value
,
1290 &rhs_normalized_codeset
);
1296 if (!strcmp (language
, rhs_language
)
1297 && ((rhs_mask
& XPG_CODESET
)
1298 /* He has a codeset, it must match normalized. */
1299 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1300 ? normalized_codeset
: codeset
,
1301 (rhs_mask
& XPG_NORM_CODESET
)
1302 ? rhs_normalized_codeset
: rhs_codeset
)
1303 /* He has no codeset, we must also have none. */
1304 : (mask
& XPG_CODESET
) == 0)
1305 /* Codeset (or lack thereof) matches. */
1306 && !strcmp (territory
?: "", rhs_territory
?: "")
1307 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1308 /* We have a winner. */
1309 add_alias (ah
, alias
, replace
,
1310 normalized_name
?: name
, &locrec_offset
);
1311 if (rhs_mask
& XPG_NORM_CODESET
)
1312 free ((char *) rhs_normalized_codeset
);
1317 /* Possibly not the whole line fits into the buffer.
1318 Ignore the rest of the line. */
1319 while (strchr (cp
, '\n') == NULL
)
1322 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1323 /* Make sure the inner loop will be left. The outer
1324 loop will exit at the `feof' test. */
1333 free (normalized_name
);
1335 if (mask
& XPG_NORM_CODESET
)
1336 free ((char *) normalized_codeset
);
1343 add_locales_to_archive (size_t nlist
, char *list
[], bool replace
)
1345 struct locarhandle ah
;
1348 /* Open the archive. This call never returns if we cannot
1349 successfully open the archive. */
1351 open_archive (&ah
, false);
1355 const char *fname
= *list
++;
1356 size_t fnamelen
= strlen (fname
);
1365 printf (_("Adding %s\n"), fname
);
1367 /* First see whether this really is a directory and whether it
1368 contains all the require locale category files. */
1369 if (stat64 (fname
, &st
) < 0)
1371 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1375 if (!S_ISDIR (st
.st_mode
))
1377 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1381 dirp
= opendir (fname
);
1384 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1385 fname
, strerror (errno
));
1390 while ((d
= readdir64 (dirp
)) != NULL
)
1392 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1394 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1396 unsigned char d_type
;
1398 /* We have an object of the required name. If it's
1399 a directory we have to look at a file with the
1400 prefix "SYS_". Otherwise we have found what we
1404 if (d_type
!= DT_REG
)
1406 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1408 if (d_type
== DT_UNKNOWN
|| d_type
== DT_LNK
)
1410 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1413 if (stat64 (fullname
, &st
) == -1)
1414 /* We cannot stat the file, ignore it. */
1417 d_type
= IFTODT (st
.st_mode
);
1420 if (d_type
== DT_DIR
)
1422 /* We have to do more tests. The file is a
1423 directory and it therefore must contain a
1424 regular file with the same name except a
1426 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1427 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1430 if (stat64 (fullname
, &st
) == -1)
1431 /* There is no SYS_* file or we cannot
1435 d_type
= IFTODT (st
.st_mode
);
1439 /* If we found a regular file (eventually after
1440 following a symlink) we are successful. */
1441 if (d_type
== DT_REG
)
1449 if (seen
!= __LC_LAST
- 1)
1451 /* We don't have all locale category files. Ignore the name. */
1452 error (0, 0, _("incomplete set of locale files in \"%s\""),
1457 /* Add the files to the archive. To do this we first compute
1458 sizes and the MD5 sums of all the files. */
1459 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1462 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1465 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1466 fd
= open64 (fullname
, O_RDONLY
);
1467 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1469 /* Cannot read the file. */
1475 if (S_ISDIR (st
.st_mode
))
1479 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1480 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1483 fd
= open64 (fullname
, O_RDONLY
);
1484 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1485 || !S_ISREG (st
.st_mode
))
1494 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1496 if (data
[cnt
].addr
== MAP_FAILED
)
1498 /* Cannot map it. */
1503 data
[cnt
].size
= st
.st_size
;
1504 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1506 /* We don't need the file descriptor anymore. */
1510 if (cnt
!= __LC_LAST
)
1514 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1516 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1521 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1523 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1525 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1529 close_archive (&ah
);
1536 delete_locales_from_archive (size_t nlist
, char *list
[])
1538 struct locarhandle ah
;
1539 struct locarhead
*head
;
1540 struct namehashent
*namehashtab
;
1542 /* Open the archive. This call never returns if we cannot
1543 successfully open the archive. */
1545 open_archive (&ah
, false);
1548 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1549 + GET (head
->namehash_offset
));
1553 const char *locname
= *list
++;
1558 /* Search for this locale in the archive. */
1559 hval
= archive_hashval (locname
, strlen (locname
));
1561 idx
= hval
% GET (head
->namehash_size
);
1562 incr
= 1 + hval
% (GET (head
->namehash_size
) - 2);
1564 /* If the name_offset field is zero this means this is no
1565 deleted entry and therefore no entry can be found. */
1566 while (GET (namehashtab
[idx
].name_offset
) != 0)
1568 if (GET (namehashtab
[idx
].hashval
) == hval
1569 && (strcmp (locname
,
1571 + GET (namehashtab
[idx
].name_offset
)))
1574 /* Found the entry. Now mark it as removed by zero-ing
1575 the reference to the locale record. */
1576 SET (namehashtab
[idx
].locrec_offset
, 0);
1581 if (idx
>= GET (head
->namehash_size
))
1582 idx
-= GET (head
->namehash_size
);
1585 if (GET (namehashtab
[idx
].name_offset
) == 0 && ! be_quiet
)
1586 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1589 close_archive (&ah
);
1598 uint32_t locrec_offset
;
1604 const unsigned char *sum
;
1605 uint32_t file_offset
;
1611 nameentcmp (const void *a
, const void *b
)
1613 return strcmp (((const struct nameent
*) a
)->name
,
1614 ((const struct nameent
*) b
)->name
);
1619 dataentcmp (const void *a
, const void *b
)
1621 if (((const struct dataent
*) a
)->file_offset
1622 < ((const struct dataent
*) b
)->file_offset
)
1625 if (((const struct dataent
*) a
)->file_offset
1626 > ((const struct dataent
*) b
)->file_offset
)
1634 show_archive_content (const char *fname
, int verbose
)
1636 struct locarhandle ah
;
1637 struct locarhead
*head
;
1638 struct namehashent
*namehashtab
;
1639 struct nameent
*names
;
1642 /* Open the archive. This call never returns if we cannot
1643 successfully open the archive. */
1645 open_archive (&ah
, true);
1649 names
= (struct nameent
*) xmalloc (GET (head
->namehash_used
)
1650 * sizeof (struct nameent
));
1652 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1653 + GET (head
->namehash_offset
));
1654 for (cnt
= used
= 0; cnt
< GET (head
->namehash_size
); ++cnt
)
1655 if (GET (namehashtab
[cnt
].locrec_offset
) != 0)
1657 assert (used
< GET (head
->namehash_used
));
1658 names
[used
].name
= ah
.addr
+ GET (namehashtab
[cnt
].name_offset
);
1659 names
[used
++].locrec_offset
= GET (namehashtab
[cnt
].locrec_offset
);
1662 /* Sort the names. */
1663 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1667 struct dataent
*files
;
1668 struct sumhashent
*sumhashtab
;
1671 files
= (struct dataent
*) xmalloc (GET (head
->sumhash_used
)
1672 * sizeof (struct dataent
));
1674 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1675 + GET (head
->sumhash_offset
));
1676 for (cnt
= sumused
= 0; cnt
< GET (head
->sumhash_size
); ++cnt
)
1677 if (GET (sumhashtab
[cnt
].file_offset
) != 0)
1679 assert (sumused
< GET (head
->sumhash_used
));
1680 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1681 files
[sumused
].file_offset
= GET (sumhashtab
[cnt
].file_offset
);
1682 files
[sumused
++].nlink
= 0;
1685 /* Sort by file locations. */
1686 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1688 /* Compute nlink fields. */
1689 for (cnt
= 0; cnt
< used
; ++cnt
)
1691 struct locrecent
*locrec
;
1694 locrec
= (struct locrecent
*) ((char *) ah
.addr
1695 + names
[cnt
].locrec_offset
);
1696 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1697 if (GET (locrec
->record
[LC_ALL
].offset
) != 0
1699 || (GET (locrec
->record
[idx
].offset
)
1700 < GET (locrec
->record
[LC_ALL
].offset
))
1701 || ((GET (locrec
->record
[idx
].offset
)
1702 + GET (locrec
->record
[idx
].len
))
1703 > (GET (locrec
->record
[LC_ALL
].offset
)
1704 + GET (locrec
->record
[LC_ALL
].len
))))
1707 struct dataent
*data
, dataent
;
1709 dataent
.file_offset
= GET (locrec
->record
[idx
].offset
);
1710 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1711 sizeof (struct dataent
),
1713 assert (data
!= NULL
);
1719 for (cnt
= 0; cnt
< used
; ++cnt
)
1721 struct locrecent
*locrec
;
1724 locrec
= (struct locrecent
*) ((char *) ah
.addr
1725 + names
[cnt
].locrec_offset
);
1726 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1729 struct dataent
*data
, dataent
;
1731 dataent
.file_offset
= GET (locrec
->record
[idx
].offset
);
1732 if (GET (locrec
->record
[LC_ALL
].offset
) != 0
1733 && (dataent
.file_offset
1734 >= GET (locrec
->record
[LC_ALL
].offset
))
1735 && (dataent
.file_offset
+ GET (locrec
->record
[idx
].len
)
1736 <= (GET (locrec
->record
[LC_ALL
].offset
)
1737 + GET (locrec
->record
[LC_ALL
].len
))))
1738 dataent
.file_offset
= GET (locrec
->record
[LC_ALL
].offset
);
1740 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1741 sizeof (struct dataent
),
1743 printf ("%6d %7x %3d%c ",
1744 GET (locrec
->record
[idx
].len
),
1745 GET (locrec
->record
[idx
].offset
),
1747 (dataent
.file_offset
1748 == GET (locrec
->record
[LC_ALL
].offset
))
1750 for (i
= 0; i
< 16; i
+= 4)
1751 printf ("%02x%02x%02x%02x",
1752 data
->sum
[i
], data
->sum
[i
+ 1],
1753 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1754 printf (" %s/%s\n", names
[cnt
].name
,
1755 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1762 for (cnt
= 0; cnt
< used
; ++cnt
)
1763 puts (names
[cnt
].name
);
1765 close_archive (&ah
);
1767 exit (EXIT_SUCCESS
);