1 /* Copyright (C) 2002-2013 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 "../../crypt/md5.h"
45 #include "../localeinfo.h"
46 #include "../locarchive.h"
47 #include "localedef.h"
49 /* Define the hash function. We define the function as static inline.
50 We must change the name so as not to conflict with simple-hash.h. */
51 #define compute_hashval static archive_hashval
52 #define hashval_t uint32_t
54 #undef compute_hashval
56 extern const char *output_prefix
;
58 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
60 static const char *locnames
[] =
62 #define DEFINE_CATEGORY(category, category_name, items, a) \
63 [category] = category_name,
64 #include "categories.def"
65 #undef DEFINE_CATEGORY
69 /* Size of the initial archive header. */
70 #define INITIAL_NUM_NAMES 900
71 #define INITIAL_SIZE_STRINGS 7500
72 #define INITIAL_NUM_LOCREC 420
73 #define INITIAL_NUM_SUMS 2000
76 /* Size of the reserved address space area. */
77 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
79 /* To prepare for enlargements of the mmaped area reserve some address
80 space. On some machines, being a file mapping rather than an anonymous
81 mapping affects the address selection. So do this mapping from the
82 actual file, even though it's only a dummy to reserve address space. */
84 prepare_address_space (int fd
, size_t total
, size_t *reserved
, int *xflags
,
85 void **mmap_base
, size_t *mmap_len
)
87 if (total
< RESERVE_MMAP_SIZE
)
89 void *p
= mmap64 (NULL
, RESERVE_MMAP_SIZE
, PROT_NONE
, MAP_SHARED
, fd
, 0);
92 void *aligned_p
= PTR_ALIGN_UP (p
, MAP_FIXED_ALIGNMENT
);
93 size_t align_adjust
= aligned_p
- p
;
95 *mmap_len
= RESERVE_MMAP_SIZE
;
96 assert (align_adjust
< RESERVE_MMAP_SIZE
);
97 *reserved
= RESERVE_MMAP_SIZE
- align_adjust
;
112 create_archive (const char *archivefname
, struct locarhandle
*ah
)
115 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
116 struct locarhead head
;
119 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
121 /* Create a temporary file in the correct directory. */
122 fd
= mkstemp (fname
);
124 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
126 /* Create the initial content of the archive. */
127 head
.magic
= AR_MAGIC
;
129 head
.namehash_offset
= sizeof (struct locarhead
);
130 head
.namehash_used
= 0;
131 head
.namehash_size
= next_prime (INITIAL_NUM_NAMES
);
133 head
.string_offset
= (head
.namehash_offset
134 + head
.namehash_size
* sizeof (struct namehashent
));
135 head
.string_used
= 0;
136 head
.string_size
= INITIAL_SIZE_STRINGS
;
138 head
.locrectab_offset
= head
.string_offset
+ head
.string_size
;
139 head
.locrectab_used
= 0;
140 head
.locrectab_size
= INITIAL_NUM_LOCREC
;
142 head
.sumhash_offset
= (head
.locrectab_offset
143 + head
.locrectab_size
* sizeof (struct locrecent
));
144 head
.sumhash_used
= 0;
145 head
.sumhash_size
= next_prime (INITIAL_NUM_SUMS
);
147 total
= head
.sumhash_offset
+ head
.sumhash_size
* sizeof (struct sumhashent
);
149 /* Write out the header and create room for the other data structures. */
150 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
154 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
157 if (ftruncate64 (fd
, total
) != 0)
161 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
164 size_t reserved
, mmap_len
;
167 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
170 /* Map the header and all the administration data structures. */
171 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
176 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
179 /* Now try to rename it. We don't use the rename function since
180 this would overwrite a file which has been created in
182 if (link (fname
, archivefname
) == -1)
186 /* We cannot use the just created file. */
190 if (errval
== EEXIST
)
192 /* There is already an archive. Must have been a localedef run
193 which happened in parallel. Simply open this file then. */
194 open_archive (ah
, false);
198 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
201 /* Remove the temporary name. */
204 /* Make the file globally readable. */
205 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
208 unlink (archivefname
);
209 error (EXIT_FAILURE
, errval
,
210 _("cannot change mode of new locale archive"));
214 ah
->mmap_base
= mmap_base
;
215 ah
->mmap_len
= mmap_len
;
218 ah
->reserved
= reserved
;
222 /* This structure and qsort comparator function are used below to sort an
223 old archive's locrec table in order of data position in the file. */
227 struct locrecent
*locrec
;
231 oldlocrecentcmp (const void *a
, const void *b
)
233 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
234 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
235 uint32_t start_a
= -1, end_a
= 0;
236 uint32_t start_b
= -1, end_b
= 0;
239 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
242 if (la
->record
[cnt
].offset
< start_a
)
243 start_a
= la
->record
[cnt
].offset
;
244 if (la
->record
[cnt
].offset
+ la
->record
[cnt
].len
> end_a
)
245 end_a
= la
->record
[cnt
].offset
+ la
->record
[cnt
].len
;
247 assert (start_a
!= (uint32_t)-1);
250 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
253 if (lb
->record
[cnt
].offset
< start_b
)
254 start_b
= lb
->record
[cnt
].offset
;
255 if (lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
> end_b
)
256 end_b
= lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
;
258 assert (start_b
!= (uint32_t)-1);
261 if (start_a
!= start_b
)
262 return (int)start_a
- (int)start_b
;
263 return (int)end_a
- (int)end_b
;
267 /* forward decls for below */
268 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
269 locale_data_t data
, bool replace
);
270 static void add_alias (struct locarhandle
*ah
, const char *alias
,
271 bool replace
, const char *oldname
,
272 uint32_t *locrec_offset_p
);
276 file_data_available_p (struct locarhandle
*ah
, uint32_t offset
, uint32_t size
)
278 if (offset
< ah
->mmaped
&& offset
+ size
<= ah
->mmaped
)
282 if (fstat64 (ah
->fd
, &st
) != 0)
285 if (st
.st_size
> ah
->reserved
)
288 size_t start
= ALIGN_DOWN (ah
->mmaped
, MAP_FIXED_ALIGNMENT
);
289 void *p
= mmap64 (ah
->addr
+ start
, st
.st_size
- start
,
290 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_FIXED
,
298 ah
->mmaped
= st
.st_size
;
304 compare_from_file (struct locarhandle
*ah
, void *p1
, uint32_t offset2
,
307 void *p2
= xmalloc (size
);
308 if (pread (ah
->fd
, p2
, size
, offset2
) != size
)
309 WITH_CUR_LOCALE (error (4, errno
,
310 _("cannot read data from locale archive")));
312 int res
= memcmp (p1
, p2
, size
);
319 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
323 struct locarhead newhead
;
325 unsigned int cnt
, loccnt
;
326 struct namehashent
*oldnamehashtab
;
327 struct locarhandle new_ah
;
328 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
329 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
330 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
333 memcpy (archivefname
, output_prefix
, prefix_len
);
334 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
335 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
337 /* Not all of the old file has to be mapped. Change this now this
338 we will have to access the whole content. */
339 if (fstat64 (ah
->fd
, &st
) != 0)
341 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
343 if (st
.st_size
< ah
->reserved
)
344 ah
->addr
= mmap64 (ah
->addr
, st
.st_size
, PROT_READ
| PROT_WRITE
,
345 MAP_SHARED
| MAP_FIXED
, ah
->fd
, 0);
349 munmap (ah
->mmap_base
, ah
->mmap_len
);
351 munmap (ah
->addr
, ah
->reserved
);
352 ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
353 MAP_SHARED
, ah
->fd
, 0);
354 ah
->reserved
= st
.st_size
;
355 ah
->mmap_base
= NULL
;
359 if (ah
->addr
== MAP_FAILED
)
361 ah
->mmaped
= st
.st_size
;
363 /* Create a temporary file in the correct directory. */
364 fd
= mkstemp (fname
);
366 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
368 /* Copy the existing head information. */
371 /* Create the new archive header. The sizes of the various tables
372 should be double from what is currently used. */
373 newhead
.namehash_size
= MAX (next_prime (2 * newhead
.namehash_used
),
374 newhead
.namehash_size
);
376 printf ("name: size: %u, used: %d, new: size: %u\n",
377 head
->namehash_size
, head
->namehash_used
, newhead
.namehash_size
);
379 newhead
.string_offset
= (newhead
.namehash_offset
380 + (newhead
.namehash_size
381 * sizeof (struct namehashent
)));
382 /* Keep the string table size aligned to 4 bytes, so that
383 all the struct { uint32_t } types following are happy. */
384 newhead
.string_size
= MAX ((2 * newhead
.string_used
+ 3) & -4,
385 newhead
.string_size
);
387 newhead
.locrectab_offset
= newhead
.string_offset
+ newhead
.string_size
;
388 newhead
.locrectab_size
= MAX (2 * newhead
.locrectab_used
,
389 newhead
.locrectab_size
);
391 newhead
.sumhash_offset
= (newhead
.locrectab_offset
392 + (newhead
.locrectab_size
393 * sizeof (struct locrecent
)));
394 newhead
.sumhash_size
= MAX (next_prime (2 * newhead
.sumhash_used
),
395 newhead
.sumhash_size
);
397 total
= (newhead
.sumhash_offset
398 + newhead
.sumhash_size
* sizeof (struct sumhashent
));
400 /* The new file is empty now. */
401 newhead
.namehash_used
= 0;
402 newhead
.string_used
= 0;
403 newhead
.locrectab_used
= 0;
404 newhead
.sumhash_used
= 0;
406 /* Write out the header and create room for the other data structures. */
407 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
412 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
415 if (ftruncate64 (fd
, total
) != 0)
419 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
422 size_t reserved
, mmap_len
;
425 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
428 /* Map the header and all the administration data structures. */
429 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
434 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
437 /* Lock the new file. */
438 if (lockf64 (fd
, F_LOCK
, total
) != 0)
442 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
445 new_ah
.mmaped
= total
;
446 new_ah
.mmap_base
= mmap_base
;
447 new_ah
.mmap_len
= mmap_len
;
450 new_ah
.reserved
= reserved
;
452 /* Walk through the hash name hash table to find out what data is
453 still referenced and transfer it into the new file. */
454 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
455 + head
->namehash_offset
);
457 /* Sort the old locrec table in order of data position. */
458 struct oldlocrecent oldlocrecarray
[head
->namehash_size
];
459 for (cnt
= 0, loccnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
460 if (oldnamehashtab
[cnt
].locrec_offset
!= 0)
462 oldlocrecarray
[loccnt
].cnt
= cnt
;
463 oldlocrecarray
[loccnt
++].locrec
464 = (struct locrecent
*) ((char *) ah
->addr
465 + oldnamehashtab
[cnt
].locrec_offset
);
467 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
470 uint32_t last_locrec_offset
= 0;
471 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
473 /* Insert this entry in the new hash table. */
474 locale_data_t old_data
;
476 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
478 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
481 old_data
[idx
].size
= oldlocrec
->record
[idx
].len
;
483 = ((char *) ah
->addr
+ oldlocrec
->record
[idx
].offset
);
485 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
489 if (cnt
> 0 && oldlocrecarray
[cnt
- 1].locrec
== oldlocrec
)
493 + oldnamehashtab
[oldlocrecarray
[cnt
- 1].cnt
].name_offset
);
497 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
498 0, oldname
, &last_locrec_offset
);
505 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
507 if (last_locrec_offset
== 0)
508 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
511 /* Make the file globally readable. */
512 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
516 error (EXIT_FAILURE
, errval
,
517 _("cannot change mode of resized locale archive"));
520 /* Rename the new file. */
521 if (rename (fname
, archivefname
) != 0)
525 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
528 /* Close the old file. */
531 /* Add the information for the new one. */
537 open_archive (struct locarhandle
*ah
, bool readonly
)
542 struct locarhead head
;
544 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
545 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
548 memcpy (archivefname
, output_prefix
, prefix_len
);
549 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
553 /* Open the archive. We must have exclusive write access. */
554 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
557 /* Maybe the file does not yet exist. */
562 static const struct locarhead nullhead
=
565 .namehash_offset
= 0,
569 ah
->addr
= (void *) &nullhead
;
573 create_archive (archivefname
, ah
);
578 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
582 if (fstat64 (fd
, &st
) < 0)
583 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
586 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
590 if (retry
++ < max_locarchive_open_retry
)
594 /* Wait for a bit. */
596 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
597 (void) nanosleep (&req
, NULL
);
602 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
606 /* One more check. Maybe another process replaced the archive file
607 with a new, larger one since we opened the file. */
608 if (stat64 (archivefname
, &st2
) == -1
609 || st
.st_dev
!= st2
.st_dev
610 || st
.st_ino
!= st2
.st_ino
)
612 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
617 /* Leave the loop. */
621 /* Read the header. */
622 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
624 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
625 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
629 ah
->mmaped
= st
.st_size
;
631 size_t reserved
, mmap_len
;
634 void *p
= prepare_address_space (fd
, st
.st_size
, &reserved
, &xflags
,
635 &mmap_base
, &mmap_len
);
637 /* Map the entire file. We might need to compare the category data
638 in the file with the newly added data. */
639 ah
->addr
= mmap64 (p
, st
.st_size
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
640 MAP_SHARED
| xflags
, fd
, 0);
641 if (ah
->addr
== MAP_FAILED
)
643 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
644 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
646 ah
->reserved
= reserved
;
647 ah
->mmap_base
= mmap_base
;
648 ah
->mmap_len
= mmap_len
;
653 close_archive (struct locarhandle
*ah
)
658 munmap (ah
->mmap_base
, ah
->mmap_len
);
660 munmap (ah
->addr
, ah
->reserved
);
665 #include "../../intl/explodename.c"
666 #include "../../intl/l10nflist.c"
668 static struct namehashent
*
669 insert_name (struct locarhandle
*ah
,
670 const char *name
, size_t name_len
, bool replace
)
672 const struct locarhead
*const head
= ah
->addr
;
673 struct namehashent
*namehashtab
674 = (struct namehashent
*) ((char *) ah
->addr
+ head
->namehash_offset
);
675 unsigned int insert_idx
, idx
, incr
;
677 /* Hash value of the locale name. */
678 uint32_t hval
= archive_hashval (name
, name_len
);
681 idx
= hval
% head
->namehash_size
;
682 incr
= 1 + hval
% (head
->namehash_size
- 2);
684 /* If the name_offset field is zero this means this is a
685 deleted entry and therefore no entry can be found. */
686 while (namehashtab
[idx
].name_offset
!= 0)
688 if (namehashtab
[idx
].hashval
== hval
690 (char *) ah
->addr
+ namehashtab
[idx
].name_offset
) == 0)
692 /* Found the entry. */
693 if (namehashtab
[idx
].locrec_offset
!= 0 && ! replace
)
696 error (0, 0, _("locale '%s' already exists"), name
);
703 if (namehashtab
[idx
].hashval
== hval
&& ! be_quiet
)
705 error (0, 0, "hash collision (%u) %s, %s",
706 hval
, name
, (char *) ah
->addr
+ namehashtab
[idx
].name_offset
);
709 /* Remember the first place we can insert the new entry. */
710 if (namehashtab
[idx
].locrec_offset
== 0 && insert_idx
== -1)
714 if (idx
>= head
->namehash_size
)
715 idx
-= head
->namehash_size
;
718 /* Add as early as possible. */
719 if (insert_idx
!= -1)
722 namehashtab
[idx
].hashval
= hval
; /* no-op if replacing an old entry. */
723 return &namehashtab
[idx
];
727 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
728 const char *oldname
, uint32_t *locrec_offset_p
)
730 uint32_t locrec_offset
= *locrec_offset_p
;
731 struct locarhead
*head
= ah
->addr
;
732 const size_t name_len
= strlen (alias
);
733 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
735 if (namehashent
== NULL
&& ! replace
)
738 if (namehashent
->name_offset
== 0)
740 /* We are adding a new hash entry for this alias.
741 Determine whether we have to resize the file. */
742 if (head
->string_used
+ name_len
+ 1 > head
->string_size
743 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)
745 /* The current archive is not large enough. */
746 enlarge_archive (ah
, head
);
748 /* The locrecent might have moved, so we have to look up
749 the old name afresh. */
750 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
751 assert (namehashent
->name_offset
!= 0);
752 assert (namehashent
->locrec_offset
!= 0);
753 *locrec_offset_p
= namehashent
->locrec_offset
;
755 /* Tail call to try the whole thing again. */
756 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
760 /* Add the name string. */
761 memcpy (ah
->addr
+ head
->string_offset
+ head
->string_used
,
762 alias
, name_len
+ 1);
763 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
764 head
->string_used
+= name_len
+ 1;
766 ++head
->namehash_used
;
769 if (namehashent
->locrec_offset
!= 0)
771 /* Replacing an existing entry.
772 Mark that we are no longer using the old locrecent. */
773 struct locrecent
*locrecent
774 = (struct locrecent
*) ((char *) ah
->addr
775 + namehashent
->locrec_offset
);
779 /* Point this entry at the locrecent installed for the main name. */
780 namehashent
->locrec_offset
= locrec_offset
;
783 static int /* qsort comparator used below */
784 cmpcategorysize (const void *a
, const void *b
)
786 if (*(const void **) a
== NULL
)
788 if (*(const void **) b
== NULL
)
790 return ((*(const struct locale_category_data
**) a
)->size
791 - (*(const struct locale_category_data
**) b
)->size
);
794 /* Check the content of the archive for duplicates. Add the content
795 of the files if necessary. Returns the locrec_offset. */
797 add_locale (struct locarhandle
*ah
,
798 const char *name
, locale_data_t data
, bool replace
)
800 /* First look for the name. If it already exists and we are not
801 supposed to replace it don't do anything. If it does not exist
802 we have to allocate a new locale record. */
803 size_t name_len
= strlen (name
);
804 uint32_t file_offsets
[__LC_LAST
];
805 unsigned int num_new_offsets
= 0;
806 struct sumhashent
*sumhashtab
;
808 unsigned int cnt
, idx
;
809 struct locarhead
*head
;
810 struct namehashent
*namehashent
;
812 struct locrecent
*locrecent
;
815 struct locale_category_data
*size_order
[__LC_LAST
];
816 const size_t pagesz
= getpagesize ();
820 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
821 + head
->sumhash_offset
);
823 memset (file_offsets
, 0, sizeof (file_offsets
));
825 size_order
[LC_ALL
] = NULL
;
826 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
828 size_order
[cnt
] = &data
[cnt
];
830 /* Sort the array in ascending order of data size. */
831 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
834 data
[LC_ALL
].size
= 0;
835 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
836 if (size_order
[cnt
] != NULL
)
838 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
839 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
841 /* This category makes the small-categories block
842 stop being small, so this is the end of the road. */
844 size_order
[cnt
++] = NULL
;
845 while (cnt
< __LC_LAST
);
848 data
[LC_ALL
].size
+= rounded_size
;
849 small_mask
|= 1 << (size_order
[cnt
] - data
);
852 /* Copy the data for all the small categories into the LC_ALL
855 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
856 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
858 ptr
= data
[LC_ALL
].addr
;
859 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
860 if (small_mask
& (1 << cnt
))
862 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
863 ptr
+= (data
[cnt
].size
+ 15) & -16;
865 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
867 /* For each locale category data set determine whether the same data
868 is already somewhere in the archive. */
869 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
870 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
874 /* Compute the hash value of the checksum to determine a
875 starting point for the search in the MD5 hash value
877 hval
= archive_hashval (data
[cnt
].sum
, 16);
879 idx
= hval
% head
->sumhash_size
;
880 incr
= 1 + hval
% (head
->sumhash_size
- 2);
882 while (sumhashtab
[idx
].file_offset
!= 0)
884 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
886 /* Check the content, there could be a collision of
889 Unfortunately the sumhashent record does not include
890 the size of the stored data. So we have to search for
892 locrecent
= (struct locrecent
*) ((char *) ah
->addr
893 + head
->locrectab_offset
);
895 for (iloc
= 0; iloc
< head
->locrectab_used
; ++iloc
)
896 if (locrecent
[iloc
].refs
!= 0
897 && (locrecent
[iloc
].record
[cnt
].offset
898 == sumhashtab
[idx
].file_offset
))
901 if (iloc
!= head
->locrectab_used
902 && data
[cnt
].size
== locrecent
[iloc
].record
[cnt
].len
903 /* We have to compare the content. Either we can
904 have the data mmaped or we have to read from
906 && (file_data_available_p (ah
, sumhashtab
[idx
].file_offset
,
908 ? memcmp (data
[cnt
].addr
,
910 + sumhashtab
[idx
].file_offset
,
912 : compare_from_file (ah
, data
[cnt
].addr
,
913 sumhashtab
[idx
].file_offset
,
914 data
[cnt
].size
) == 0))
917 file_offsets
[cnt
] = sumhashtab
[idx
].file_offset
;
924 if (idx
>= head
->sumhash_size
)
925 idx
-= head
->sumhash_size
;
929 /* Find a slot for the locale name in the hash table. */
930 namehashent
= insert_name (ah
, name
, name_len
, replace
);
931 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
934 /* Determine whether we have to resize the file. */
935 if (100 * (head
->sumhash_used
+ num_new_offsets
) > 75 * head
->sumhash_size
936 || (namehashent
->locrec_offset
== 0
937 && (head
->locrectab_used
== head
->locrectab_size
938 || head
->string_used
+ name_len
+ 1 > head
->string_size
939 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)))
941 /* The current archive is not large enough. */
942 enlarge_archive (ah
, head
);
943 return add_locale (ah
, name
, data
, replace
);
946 /* Add the locale data which is not yet in the archive. */
947 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
948 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
949 && file_offsets
[cnt
] == 0)
951 /* The data for this section is not yet available in the
952 archive. Append it. */
956 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
957 if (lastpos
== (off64_t
) -1)
958 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
960 /* If block of small categories would cross page boundary,
961 align it unless it immediately follows a large category. */
962 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
963 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
965 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
967 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
968 char *zeros
= alloca (sz
);
970 memset (zeros
, 0, sz
);
971 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
972 error (EXIT_FAILURE
, errno
,
973 _("cannot add to locale archive"));
978 /* Align all data to a 16 byte boundary. */
979 if ((lastpos
& 15) != 0)
981 static const char zeros
[15] = { 0, };
983 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
984 != 16 - (lastpos
& 15))
985 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
987 lastpos
+= 16 - (lastpos
& 15);
990 /* Remember the position. */
991 file_offsets
[cnt
] = lastpos
;
992 lastoffset
= lastpos
+ data
[cnt
].size
;
994 /* Write the data. */
995 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
997 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
999 /* Add the hash value to the hash table. */
1000 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
1002 idx
= md5hval
% head
->sumhash_size
;
1003 incr
= 1 + md5hval
% (head
->sumhash_size
- 2);
1005 while (sumhashtab
[idx
].file_offset
!= 0)
1008 if (idx
>= head
->sumhash_size
)
1009 idx
-= head
->sumhash_size
;
1012 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
1013 sumhashtab
[idx
].file_offset
= file_offsets
[cnt
];
1015 ++head
->sumhash_used
;
1018 lastoffset
= file_offsets
[LC_ALL
];
1019 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1020 if (small_mask
& (1 << cnt
))
1022 file_offsets
[cnt
] = lastoffset
;
1023 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
1026 if (namehashent
->name_offset
== 0)
1028 /* Add the name string. */
1029 memcpy ((char *) ah
->addr
+ head
->string_offset
+ head
->string_used
,
1030 name
, name_len
+ 1);
1031 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
1032 head
->string_used
+= name_len
+ 1;
1033 ++head
->namehash_used
;
1036 if (namehashent
->locrec_offset
== 0)
1038 /* Allocate a name location record. */
1039 namehashent
->locrec_offset
= (head
->locrectab_offset
1040 + (head
->locrectab_used
++
1041 * sizeof (struct locrecent
)));
1042 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1043 + namehashent
->locrec_offset
);
1044 locrecent
->refs
= 1;
1048 /* If there are other aliases pointing to this locrecent,
1049 we still need a new one. If not, reuse the old one. */
1051 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1052 + namehashent
->locrec_offset
);
1053 if (locrecent
->refs
> 1)
1056 namehashent
->locrec_offset
= (head
->locrectab_offset
1057 + (head
->locrectab_used
++
1058 * sizeof (struct locrecent
)));
1059 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1060 + namehashent
->locrec_offset
);
1061 locrecent
->refs
= 1;
1065 /* Fill in the table with the locations of the locale data. */
1066 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1068 locrecent
->record
[cnt
].offset
= file_offsets
[cnt
];
1069 locrecent
->record
[cnt
].len
= data
[cnt
].size
;
1072 return namehashent
->locrec_offset
;
1076 /* Check the content of the archive for duplicates. Add the content
1077 of the files if necessary. Add all the names, possibly overwriting
1080 add_locale_to_archive (ah
, name
, data
, replace
)
1081 struct locarhandle
*ah
;
1086 char *normalized_name
= NULL
;
1087 uint32_t locrec_offset
;
1089 /* First analyze the name to decide how to archive it. */
1090 const char *language
;
1091 const char *modifier
;
1092 const char *territory
;
1093 const char *codeset
;
1094 const char *normalized_codeset
;
1095 int mask
= _nl_explode_name (strdupa (name
),
1096 &language
, &modifier
, &territory
,
1097 &codeset
, &normalized_codeset
);
1101 if (mask
& XPG_NORM_CODESET
)
1102 /* This name contains a codeset in unnormalized form.
1103 We will store it in the archive with a normalized name. */
1104 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
1105 language
, territory
== NULL
? "" : "_", territory
?: "",
1106 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
1107 modifier
== NULL
? "" : "@", modifier
?: "");
1109 /* This call does the main work. */
1110 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
1111 if (locrec_offset
== 0)
1113 free (normalized_name
);
1114 if (mask
& XPG_NORM_CODESET
)
1115 free ((char *) normalized_codeset
);
1119 if ((mask
& XPG_CODESET
) == 0)
1121 /* This name lacks a codeset, so determine the locale's codeset and
1122 add an alias for its name with normalized codeset appended. */
1127 unsigned int nstrings
;
1128 unsigned int strindex
[0];
1129 } *filedata
= data
[LC_CTYPE
].addr
;
1130 codeset
= (char *) filedata
1131 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)];
1132 char *normalized_codeset_name
= NULL
;
1134 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
1135 mask
|= XPG_NORM_CODESET
;
1137 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
1138 language
, territory
== NULL
? "" : "_", territory
?: "",
1140 modifier
== NULL
? "" : "@", modifier
?: "");
1142 add_alias (ah
, normalized_codeset_name
, replace
,
1143 normalized_name
?: name
, &locrec_offset
);
1144 free (normalized_codeset_name
);
1147 /* Now read the locale.alias files looking for lines whose
1148 right hand side matches our name after normalization. */
1150 if (alias_file
!= NULL
)
1153 fp
= fopen (alias_file
, "rm");
1155 error (1, errno
, _("locale alias file `%s' not found"),
1158 /* No threads present. */
1159 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
1161 while (! feof_unlocked (fp
))
1163 /* It is a reasonable approach to use a fix buffer here
1165 a) we are only interested in the first two fields
1166 b) these fields must be usable as file names and so must
1173 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1178 /* Ignore leading white space. */
1179 while (isspace (cp
[0]) && cp
[0] != '\n')
1182 /* A leading '#' signals a comment line. */
1183 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1186 while (cp
[0] != '\0' && !isspace (cp
[0]))
1188 /* Terminate alias name. */
1192 /* Now look for the beginning of the value. */
1193 while (isspace (cp
[0]))
1199 while (cp
[0] != '\0' && !isspace (cp
[0]))
1201 /* Terminate value. */
1204 /* This has to be done to make the following
1205 test for the end of line possible. We are
1206 looking for the terminating '\n' which do not
1211 else if (cp
[0] != '\0')
1214 /* Does this alias refer to our locale? We will
1215 normalize the right hand side and compare the
1216 elements of the normalized form. */
1218 const char *rhs_language
;
1219 const char *rhs_modifier
;
1220 const char *rhs_territory
;
1221 const char *rhs_codeset
;
1222 const char *rhs_normalized_codeset
;
1223 int rhs_mask
= _nl_explode_name (value
,
1228 &rhs_normalized_codeset
);
1234 if (!strcmp (language
, rhs_language
)
1235 && ((rhs_mask
& XPG_CODESET
)
1236 /* He has a codeset, it must match normalized. */
1237 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1238 ? normalized_codeset
: codeset
,
1239 (rhs_mask
& XPG_NORM_CODESET
)
1240 ? rhs_normalized_codeset
: rhs_codeset
)
1241 /* He has no codeset, we must also have none. */
1242 : (mask
& XPG_CODESET
) == 0)
1243 /* Codeset (or lack thereof) matches. */
1244 && !strcmp (territory
?: "", rhs_territory
?: "")
1245 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1246 /* We have a winner. */
1247 add_alias (ah
, alias
, replace
,
1248 normalized_name
?: name
, &locrec_offset
);
1249 if (rhs_mask
& XPG_NORM_CODESET
)
1250 free ((char *) rhs_normalized_codeset
);
1255 /* Possibly not the whole line fits into the buffer.
1256 Ignore the rest of the line. */
1257 while (strchr (cp
, '\n') == NULL
)
1260 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1261 /* Make sure the inner loop will be left. The outer
1262 loop will exit at the `feof' test. */
1271 free (normalized_name
);
1273 if (mask
& XPG_NORM_CODESET
)
1274 free ((char *) normalized_codeset
);
1281 add_locales_to_archive (nlist
, list
, replace
)
1286 struct locarhandle ah
;
1289 /* Open the archive. This call never returns if we cannot
1290 successfully open the archive. */
1291 open_archive (&ah
, false);
1295 const char *fname
= *list
++;
1296 size_t fnamelen
= strlen (fname
);
1305 printf (_("Adding %s\n"), fname
);
1307 /* First see whether this really is a directory and whether it
1308 contains all the require locale category files. */
1309 if (stat64 (fname
, &st
) < 0)
1311 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1315 if (!S_ISDIR (st
.st_mode
))
1317 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1321 dirp
= opendir (fname
);
1324 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1325 fname
, strerror (errno
));
1330 while ((d
= readdir64 (dirp
)) != NULL
)
1332 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1334 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1336 unsigned char d_type
;
1338 /* We have an object of the required name. If it's
1339 a directory we have to look at a file with the
1340 prefix "SYS_". Otherwise we have found what we
1342 #ifdef _DIRENT_HAVE_D_TYPE
1345 if (d_type
!= DT_REG
)
1348 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1350 #ifdef _DIRENT_HAVE_D_TYPE
1351 if (d_type
== DT_UNKNOWN
)
1354 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1357 if (stat64 (fullname
, &st
) == -1)
1358 /* We cannot stat the file, ignore it. */
1361 d_type
= IFTODT (st
.st_mode
);
1364 if (d_type
== DT_DIR
)
1366 /* We have to do more tests. The file is a
1367 directory and it therefore must contain a
1368 regular file with the same name except a
1370 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1371 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1374 if (stat64 (fullname
, &st
) == -1)
1375 /* There is no SYS_* file or we cannot
1379 d_type
= IFTODT (st
.st_mode
);
1383 /* If we found a regular file (eventually after
1384 following a symlink) we are successful. */
1385 if (d_type
== DT_REG
)
1393 if (seen
!= __LC_LAST
- 1)
1395 /* We don't have all locale category files. Ignore the name. */
1396 error (0, 0, _("incomplete set of locale files in \"%s\""),
1401 /* Add the files to the archive. To do this we first compute
1402 sizes and the MD5 sums of all the files. */
1403 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1406 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1409 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1410 fd
= open64 (fullname
, O_RDONLY
);
1411 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1413 /* Cannot read the file. */
1419 if (S_ISDIR (st
.st_mode
))
1423 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1424 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1427 fd
= open64 (fullname
, O_RDONLY
);
1428 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1429 || !S_ISREG (st
.st_mode
))
1438 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1440 if (data
[cnt
].addr
== MAP_FAILED
)
1442 /* Cannot map it. */
1447 data
[cnt
].size
= st
.st_size
;
1448 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1450 /* We don't need the file descriptor anymore. */
1454 if (cnt
!= __LC_LAST
)
1458 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1460 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1465 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1467 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1469 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1473 close_archive (&ah
);
1480 delete_locales_from_archive (nlist
, list
)
1484 struct locarhandle ah
;
1485 struct locarhead
*head
;
1486 struct namehashent
*namehashtab
;
1488 /* Open the archive. This call never returns if we cannot
1489 successfully open the archive. */
1490 open_archive (&ah
, false);
1493 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1494 + head
->namehash_offset
);
1498 const char *locname
= *list
++;
1503 /* Search for this locale in the archive. */
1504 hval
= archive_hashval (locname
, strlen (locname
));
1506 idx
= hval
% head
->namehash_size
;
1507 incr
= 1 + hval
% (head
->namehash_size
- 2);
1509 /* If the name_offset field is zero this means this is no
1510 deleted entry and therefore no entry can be found. */
1511 while (namehashtab
[idx
].name_offset
!= 0)
1513 if (namehashtab
[idx
].hashval
== hval
1514 && (strcmp (locname
,
1515 (char *) ah
.addr
+ namehashtab
[idx
].name_offset
)
1518 /* Found the entry. Now mark it as removed by zero-ing
1519 the reference to the locale record. */
1520 namehashtab
[idx
].locrec_offset
= 0;
1525 if (idx
>= head
->namehash_size
)
1526 idx
-= head
->namehash_size
;
1529 if (namehashtab
[idx
].name_offset
== 0 && ! be_quiet
)
1530 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1533 close_archive (&ah
);
1542 uint32_t locrec_offset
;
1548 const unsigned char *sum
;
1549 uint32_t file_offset
;
1555 nameentcmp (const void *a
, const void *b
)
1557 return strcmp (((const struct nameent
*) a
)->name
,
1558 ((const struct nameent
*) b
)->name
);
1563 dataentcmp (const void *a
, const void *b
)
1565 if (((const struct dataent
*) a
)->file_offset
1566 < ((const struct dataent
*) b
)->file_offset
)
1569 if (((const struct dataent
*) a
)->file_offset
1570 > ((const struct dataent
*) b
)->file_offset
)
1578 show_archive_content (int verbose
)
1580 struct locarhandle ah
;
1581 struct locarhead
*head
;
1582 struct namehashent
*namehashtab
;
1583 struct nameent
*names
;
1586 /* Open the archive. This call never returns if we cannot
1587 successfully open the archive. */
1588 open_archive (&ah
, true);
1592 names
= (struct nameent
*) xmalloc (head
->namehash_used
1593 * sizeof (struct nameent
));
1595 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1596 + head
->namehash_offset
);
1597 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
1598 if (namehashtab
[cnt
].locrec_offset
!= 0)
1600 assert (used
< head
->namehash_used
);
1601 names
[used
].name
= ah
.addr
+ namehashtab
[cnt
].name_offset
;
1602 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
1605 /* Sort the names. */
1606 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1610 struct dataent
*files
;
1611 struct sumhashent
*sumhashtab
;
1614 files
= (struct dataent
*) xmalloc (head
->sumhash_used
1615 * sizeof (struct dataent
));
1617 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1618 + head
->sumhash_offset
);
1619 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
1620 if (sumhashtab
[cnt
].file_offset
!= 0)
1622 assert (sumused
< head
->sumhash_used
);
1623 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1624 files
[sumused
].file_offset
= sumhashtab
[cnt
].file_offset
;
1625 files
[sumused
++].nlink
= 0;
1628 /* Sort by file locations. */
1629 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1631 /* Compute nlink fields. */
1632 for (cnt
= 0; cnt
< used
; ++cnt
)
1634 struct locrecent
*locrec
;
1637 locrec
= (struct locrecent
*) ((char *) ah
.addr
1638 + names
[cnt
].locrec_offset
);
1639 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1640 if (locrec
->record
[LC_ALL
].offset
!= 0
1642 || (locrec
->record
[idx
].offset
1643 < locrec
->record
[LC_ALL
].offset
)
1644 || (locrec
->record
[idx
].offset
+ locrec
->record
[idx
].len
1645 > (locrec
->record
[LC_ALL
].offset
1646 + locrec
->record
[LC_ALL
].len
)))
1649 struct dataent
*data
, dataent
;
1651 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1652 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1653 sizeof (struct dataent
),
1655 assert (data
!= NULL
);
1661 for (cnt
= 0; cnt
< used
; ++cnt
)
1663 struct locrecent
*locrec
;
1666 locrec
= (struct locrecent
*) ((char *) ah
.addr
1667 + names
[cnt
].locrec_offset
);
1668 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1671 struct dataent
*data
, dataent
;
1673 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1674 if (locrec
->record
[LC_ALL
].offset
!= 0
1675 && dataent
.file_offset
>= locrec
->record
[LC_ALL
].offset
1676 && (dataent
.file_offset
+ locrec
->record
[idx
].len
1677 <= (locrec
->record
[LC_ALL
].offset
1678 + locrec
->record
[LC_ALL
].len
)))
1679 dataent
.file_offset
= locrec
->record
[LC_ALL
].offset
;
1681 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1682 sizeof (struct dataent
),
1684 printf ("%6d %7x %3d%c ",
1685 locrec
->record
[idx
].len
, locrec
->record
[idx
].offset
,
1687 dataent
.file_offset
== locrec
->record
[LC_ALL
].offset
1689 for (i
= 0; i
< 16; i
+= 4)
1690 printf ("%02x%02x%02x%02x",
1691 data
->sum
[i
], data
->sum
[i
+ 1],
1692 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1693 printf (" %s/%s\n", names
[cnt
].name
,
1694 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1700 for (cnt
= 0; cnt
< used
; ++cnt
)
1701 puts (names
[cnt
].name
);
1703 close_archive (&ah
);
1705 exit (EXIT_SUCCESS
);