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-internal.h>
44 #include <libc-mmap.h>
45 #include "../../crypt/md5.h"
46 #include "../localeinfo.h"
47 #include "../locarchive.h"
48 #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 LOCALEDIR "/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 /* Size of the reserved address space area. */
78 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
80 /* To prepare for enlargements of the mmaped area reserve some address
81 space. On some machines, being a file mapping rather than an anonymous
82 mapping affects the address selection. So do this mapping from the
83 actual file, even though it's only a dummy to reserve address space. */
85 prepare_address_space (int fd
, size_t total
, size_t *reserved
, int *xflags
,
86 void **mmap_base
, size_t *mmap_len
)
88 if (total
< RESERVE_MMAP_SIZE
)
90 void *p
= mmap64 (NULL
, RESERVE_MMAP_SIZE
, PROT_NONE
, MAP_SHARED
, fd
, 0);
93 void *aligned_p
= PTR_ALIGN_UP (p
, MAP_FIXED_ALIGNMENT
);
94 size_t align_adjust
= aligned_p
- p
;
96 *mmap_len
= RESERVE_MMAP_SIZE
;
97 assert (align_adjust
< RESERVE_MMAP_SIZE
);
98 *reserved
= RESERVE_MMAP_SIZE
- align_adjust
;
113 create_archive (const char *archivefname
, struct locarhandle
*ah
)
116 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
117 struct locarhead head
;
120 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
122 /* Create a temporary file in the correct directory. */
123 fd
= mkstemp (fname
);
125 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
127 /* Create the initial content of the archive. */
128 head
.magic
= AR_MAGIC
;
130 head
.namehash_offset
= sizeof (struct locarhead
);
131 head
.namehash_used
= 0;
132 head
.namehash_size
= next_prime (INITIAL_NUM_NAMES
);
134 head
.string_offset
= (head
.namehash_offset
135 + head
.namehash_size
* sizeof (struct namehashent
));
136 head
.string_used
= 0;
137 head
.string_size
= INITIAL_SIZE_STRINGS
;
139 head
.locrectab_offset
= head
.string_offset
+ head
.string_size
;
140 head
.locrectab_used
= 0;
141 head
.locrectab_size
= INITIAL_NUM_LOCREC
;
143 head
.sumhash_offset
= (head
.locrectab_offset
144 + head
.locrectab_size
* sizeof (struct locrecent
));
145 head
.sumhash_used
= 0;
146 head
.sumhash_size
= next_prime (INITIAL_NUM_SUMS
);
148 total
= head
.sumhash_offset
+ head
.sumhash_size
* sizeof (struct sumhashent
);
150 /* Write out the header and create room for the other data structures. */
151 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
155 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
158 if (ftruncate64 (fd
, total
) != 0)
162 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
165 size_t reserved
, mmap_len
;
168 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
171 /* Map the header and all the administration data structures. */
172 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
177 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
180 /* Now try to rename it. We don't use the rename function since
181 this would overwrite a file which has been created in
183 if (link (fname
, archivefname
) == -1)
187 /* We cannot use the just created file. */
191 if (errval
== EEXIST
)
193 /* There is already an archive. Must have been a localedef run
194 which happened in parallel. Simply open this file then. */
195 open_archive (ah
, false);
199 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
202 /* Remove the temporary name. */
205 /* Make the file globally readable. */
206 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
209 unlink (archivefname
);
210 error (EXIT_FAILURE
, errval
,
211 _("cannot change mode of new locale archive"));
215 ah
->mmap_base
= mmap_base
;
216 ah
->mmap_len
= mmap_len
;
219 ah
->reserved
= reserved
;
223 /* This structure and qsort comparator function are used below to sort an
224 old archive's locrec table in order of data position in the file. */
228 struct locrecent
*locrec
;
232 oldlocrecentcmp (const void *a
, const void *b
)
234 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
235 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
236 uint32_t start_a
= -1, end_a
= 0;
237 uint32_t start_b
= -1, end_b
= 0;
240 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
243 if (la
->record
[cnt
].offset
< start_a
)
244 start_a
= la
->record
[cnt
].offset
;
245 if (la
->record
[cnt
].offset
+ la
->record
[cnt
].len
> end_a
)
246 end_a
= la
->record
[cnt
].offset
+ la
->record
[cnt
].len
;
248 assert (start_a
!= (uint32_t)-1);
251 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
254 if (lb
->record
[cnt
].offset
< start_b
)
255 start_b
= lb
->record
[cnt
].offset
;
256 if (lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
> end_b
)
257 end_b
= lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
;
259 assert (start_b
!= (uint32_t)-1);
262 if (start_a
!= start_b
)
263 return (int)start_a
- (int)start_b
;
264 return (int)end_a
- (int)end_b
;
268 /* forward decls for below */
269 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
270 locale_data_t data
, bool replace
);
271 static void add_alias (struct locarhandle
*ah
, const char *alias
,
272 bool replace
, const char *oldname
,
273 uint32_t *locrec_offset_p
);
277 file_data_available_p (struct locarhandle
*ah
, uint32_t offset
, uint32_t size
)
279 if (offset
< ah
->mmaped
&& offset
+ size
<= ah
->mmaped
)
283 if (fstat64 (ah
->fd
, &st
) != 0)
286 if (st
.st_size
> ah
->reserved
)
289 size_t start
= ALIGN_DOWN (ah
->mmaped
, MAP_FIXED_ALIGNMENT
);
290 void *p
= mmap64 (ah
->addr
+ start
, st
.st_size
- start
,
291 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_FIXED
,
299 ah
->mmaped
= st
.st_size
;
305 compare_from_file (struct locarhandle
*ah
, void *p1
, uint32_t offset2
,
308 void *p2
= xmalloc (size
);
309 if (pread (ah
->fd
, p2
, size
, offset2
) != size
)
310 WITH_CUR_LOCALE (error (4, errno
,
311 _("cannot read data from locale archive")));
313 int res
= memcmp (p1
, p2
, size
);
320 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
324 struct locarhead newhead
;
326 unsigned int cnt
, loccnt
;
327 struct namehashent
*oldnamehashtab
;
328 struct locarhandle new_ah
;
329 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
330 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
331 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
334 memcpy (archivefname
, output_prefix
, prefix_len
);
335 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
336 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
338 /* Not all of the old file has to be mapped. Change this now this
339 we will have to access the whole content. */
340 if (fstat64 (ah
->fd
, &st
) != 0)
342 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
344 if (st
.st_size
< ah
->reserved
)
345 ah
->addr
= mmap64 (ah
->addr
, st
.st_size
, PROT_READ
| PROT_WRITE
,
346 MAP_SHARED
| MAP_FIXED
, ah
->fd
, 0);
350 munmap (ah
->mmap_base
, ah
->mmap_len
);
352 munmap (ah
->addr
, ah
->reserved
);
353 ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
354 MAP_SHARED
, ah
->fd
, 0);
355 ah
->reserved
= st
.st_size
;
356 ah
->mmap_base
= NULL
;
360 if (ah
->addr
== MAP_FAILED
)
362 ah
->mmaped
= st
.st_size
;
364 /* Create a temporary file in the correct directory. */
365 fd
= mkstemp (fname
);
367 error (EXIT_FAILURE
, errno
, _("cannot create temporary file: %s"), fname
);
369 /* Copy the existing head information. */
372 /* Create the new archive header. The sizes of the various tables
373 should be double from what is currently used. */
374 newhead
.namehash_size
= MAX (next_prime (2 * newhead
.namehash_used
),
375 newhead
.namehash_size
);
377 printf ("name: size: %u, used: %d, new: size: %u\n",
378 head
->namehash_size
, head
->namehash_used
, newhead
.namehash_size
);
380 newhead
.string_offset
= (newhead
.namehash_offset
381 + (newhead
.namehash_size
382 * sizeof (struct namehashent
)));
383 /* Keep the string table size aligned to 4 bytes, so that
384 all the struct { uint32_t } types following are happy. */
385 newhead
.string_size
= MAX ((2 * newhead
.string_used
+ 3) & -4,
386 newhead
.string_size
);
388 newhead
.locrectab_offset
= newhead
.string_offset
+ newhead
.string_size
;
389 newhead
.locrectab_size
= MAX (2 * newhead
.locrectab_used
,
390 newhead
.locrectab_size
);
392 newhead
.sumhash_offset
= (newhead
.locrectab_offset
393 + (newhead
.locrectab_size
394 * sizeof (struct locrecent
)));
395 newhead
.sumhash_size
= MAX (next_prime (2 * newhead
.sumhash_used
),
396 newhead
.sumhash_size
);
398 total
= (newhead
.sumhash_offset
399 + newhead
.sumhash_size
* sizeof (struct sumhashent
));
401 /* The new file is empty now. */
402 newhead
.namehash_used
= 0;
403 newhead
.string_used
= 0;
404 newhead
.locrectab_used
= 0;
405 newhead
.sumhash_used
= 0;
407 /* Write out the header and create room for the other data structures. */
408 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
413 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
416 if (ftruncate64 (fd
, total
) != 0)
420 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
423 size_t reserved
, mmap_len
;
426 void *p
= prepare_address_space (fd
, total
, &reserved
, &xflags
, &mmap_base
,
429 /* Map the header and all the administration data structures. */
430 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
435 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
438 /* Lock the new file. */
439 if (lockf64 (fd
, F_LOCK
, total
) != 0)
443 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
446 new_ah
.mmaped
= total
;
447 new_ah
.mmap_base
= mmap_base
;
448 new_ah
.mmap_len
= mmap_len
;
451 new_ah
.reserved
= reserved
;
453 /* Walk through the hash name hash table to find out what data is
454 still referenced and transfer it into the new file. */
455 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
456 + head
->namehash_offset
);
458 /* Sort the old locrec table in order of data position. */
459 struct oldlocrecent oldlocrecarray
[head
->namehash_size
];
460 for (cnt
= 0, loccnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
461 if (oldnamehashtab
[cnt
].locrec_offset
!= 0)
463 oldlocrecarray
[loccnt
].cnt
= cnt
;
464 oldlocrecarray
[loccnt
++].locrec
465 = (struct locrecent
*) ((char *) ah
->addr
466 + oldnamehashtab
[cnt
].locrec_offset
);
468 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
471 uint32_t last_locrec_offset
= 0;
472 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
474 /* Insert this entry in the new hash table. */
475 locale_data_t old_data
;
477 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
479 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
482 old_data
[idx
].size
= oldlocrec
->record
[idx
].len
;
484 = ((char *) ah
->addr
+ oldlocrec
->record
[idx
].offset
);
486 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
490 if (cnt
> 0 && oldlocrecarray
[cnt
- 1].locrec
== oldlocrec
)
494 + oldnamehashtab
[oldlocrecarray
[cnt
- 1].cnt
].name_offset
);
498 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
499 0, oldname
, &last_locrec_offset
);
506 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
508 if (last_locrec_offset
== 0)
509 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
512 /* Make the file globally readable. */
513 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
517 error (EXIT_FAILURE
, errval
,
518 _("cannot change mode of resized locale archive"));
521 /* Rename the new file. */
522 if (rename (fname
, archivefname
) != 0)
526 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
529 /* Close the old file. */
532 /* Add the information for the new one. */
538 open_archive (struct locarhandle
*ah
, bool readonly
)
543 struct locarhead head
;
545 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
546 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
549 memcpy (archivefname
, output_prefix
, prefix_len
);
550 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
554 /* Open the archive. We must have exclusive write access. */
555 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
558 /* Maybe the file does not yet exist. */
563 static const struct locarhead nullhead
=
566 .namehash_offset
= 0,
570 ah
->addr
= (void *) &nullhead
;
574 create_archive (archivefname
, ah
);
579 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
583 if (fstat64 (fd
, &st
) < 0)
584 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
587 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
591 if (retry
++ < max_locarchive_open_retry
)
595 /* Wait for a bit. */
597 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
598 (void) nanosleep (&req
, NULL
);
603 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
607 /* One more check. Maybe another process replaced the archive file
608 with a new, larger one since we opened the file. */
609 if (stat64 (archivefname
, &st2
) == -1
610 || st
.st_dev
!= st2
.st_dev
611 || st
.st_ino
!= st2
.st_ino
)
613 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
618 /* Leave the loop. */
622 /* Read the header. */
623 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
625 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
626 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
630 ah
->mmaped
= st
.st_size
;
632 size_t reserved
, mmap_len
;
635 void *p
= prepare_address_space (fd
, st
.st_size
, &reserved
, &xflags
,
636 &mmap_base
, &mmap_len
);
638 /* Map the entire file. We might need to compare the category data
639 in the file with the newly added data. */
640 ah
->addr
= mmap64 (p
, st
.st_size
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
641 MAP_SHARED
| xflags
, fd
, 0);
642 if (ah
->addr
== MAP_FAILED
)
644 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
645 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
647 ah
->reserved
= reserved
;
648 ah
->mmap_base
= mmap_base
;
649 ah
->mmap_len
= mmap_len
;
654 close_archive (struct locarhandle
*ah
)
659 munmap (ah
->mmap_base
, ah
->mmap_len
);
661 munmap (ah
->addr
, ah
->reserved
);
666 #include "../../intl/explodename.c"
667 #include "../../intl/l10nflist.c"
669 static struct namehashent
*
670 insert_name (struct locarhandle
*ah
,
671 const char *name
, size_t name_len
, bool replace
)
673 const struct locarhead
*const head
= ah
->addr
;
674 struct namehashent
*namehashtab
675 = (struct namehashent
*) ((char *) ah
->addr
+ head
->namehash_offset
);
676 unsigned int insert_idx
, idx
, incr
;
678 /* Hash value of the locale name. */
679 uint32_t hval
= archive_hashval (name
, name_len
);
682 idx
= hval
% head
->namehash_size
;
683 incr
= 1 + hval
% (head
->namehash_size
- 2);
685 /* If the name_offset field is zero this means this is a
686 deleted entry and therefore no entry can be found. */
687 while (namehashtab
[idx
].name_offset
!= 0)
689 if (namehashtab
[idx
].hashval
== hval
691 (char *) ah
->addr
+ namehashtab
[idx
].name_offset
) == 0)
693 /* Found the entry. */
694 if (namehashtab
[idx
].locrec_offset
!= 0 && ! replace
)
697 error (0, 0, _("locale '%s' already exists"), name
);
704 if (namehashtab
[idx
].hashval
== hval
&& ! be_quiet
)
706 error (0, 0, "hash collision (%u) %s, %s",
707 hval
, name
, (char *) ah
->addr
+ namehashtab
[idx
].name_offset
);
710 /* Remember the first place we can insert the new entry. */
711 if (namehashtab
[idx
].locrec_offset
== 0 && insert_idx
== -1)
715 if (idx
>= head
->namehash_size
)
716 idx
-= head
->namehash_size
;
719 /* Add as early as possible. */
720 if (insert_idx
!= -1)
723 namehashtab
[idx
].hashval
= hval
; /* no-op if replacing an old entry. */
724 return &namehashtab
[idx
];
728 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
729 const char *oldname
, uint32_t *locrec_offset_p
)
731 uint32_t locrec_offset
= *locrec_offset_p
;
732 struct locarhead
*head
= ah
->addr
;
733 const size_t name_len
= strlen (alias
);
734 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
736 if (namehashent
== NULL
&& ! replace
)
739 if (namehashent
->name_offset
== 0)
741 /* We are adding a new hash entry for this alias.
742 Determine whether we have to resize the file. */
743 if (head
->string_used
+ name_len
+ 1 > head
->string_size
744 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)
746 /* The current archive is not large enough. */
747 enlarge_archive (ah
, head
);
749 /* The locrecent might have moved, so we have to look up
750 the old name afresh. */
751 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
752 assert (namehashent
->name_offset
!= 0);
753 assert (namehashent
->locrec_offset
!= 0);
754 *locrec_offset_p
= namehashent
->locrec_offset
;
756 /* Tail call to try the whole thing again. */
757 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
761 /* Add the name string. */
762 memcpy (ah
->addr
+ head
->string_offset
+ head
->string_used
,
763 alias
, name_len
+ 1);
764 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
765 head
->string_used
+= name_len
+ 1;
767 ++head
->namehash_used
;
770 if (namehashent
->locrec_offset
!= 0)
772 /* Replacing an existing entry.
773 Mark that we are no longer using the old locrecent. */
774 struct locrecent
*locrecent
775 = (struct locrecent
*) ((char *) ah
->addr
776 + namehashent
->locrec_offset
);
780 /* Point this entry at the locrecent installed for the main name. */
781 namehashent
->locrec_offset
= locrec_offset
;
784 static int /* qsort comparator used below */
785 cmpcategorysize (const void *a
, const void *b
)
787 if (*(const void **) a
== NULL
)
789 if (*(const void **) b
== NULL
)
791 return ((*(const struct locale_category_data
**) a
)->size
792 - (*(const struct locale_category_data
**) b
)->size
);
795 /* Check the content of the archive for duplicates. Add the content
796 of the files if necessary. Returns the locrec_offset. */
798 add_locale (struct locarhandle
*ah
,
799 const char *name
, locale_data_t data
, bool replace
)
801 /* First look for the name. If it already exists and we are not
802 supposed to replace it don't do anything. If it does not exist
803 we have to allocate a new locale record. */
804 size_t name_len
= strlen (name
);
805 uint32_t file_offsets
[__LC_LAST
];
806 unsigned int num_new_offsets
= 0;
807 struct sumhashent
*sumhashtab
;
809 unsigned int cnt
, idx
;
810 struct locarhead
*head
;
811 struct namehashent
*namehashent
;
813 struct locrecent
*locrecent
;
816 struct locale_category_data
*size_order
[__LC_LAST
];
817 const size_t pagesz
= getpagesize ();
821 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
822 + head
->sumhash_offset
);
824 memset (file_offsets
, 0, sizeof (file_offsets
));
826 size_order
[LC_ALL
] = NULL
;
827 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
829 size_order
[cnt
] = &data
[cnt
];
831 /* Sort the array in ascending order of data size. */
832 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
835 data
[LC_ALL
].size
= 0;
836 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
837 if (size_order
[cnt
] != NULL
)
839 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
840 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
842 /* This category makes the small-categories block
843 stop being small, so this is the end of the road. */
845 size_order
[cnt
++] = NULL
;
846 while (cnt
< __LC_LAST
);
849 data
[LC_ALL
].size
+= rounded_size
;
850 small_mask
|= 1 << (size_order
[cnt
] - data
);
853 /* Copy the data for all the small categories into the LC_ALL
856 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
857 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
859 ptr
= data
[LC_ALL
].addr
;
860 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
861 if (small_mask
& (1 << cnt
))
863 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
864 ptr
+= (data
[cnt
].size
+ 15) & -16;
866 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
868 /* For each locale category data set determine whether the same data
869 is already somewhere in the archive. */
870 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
871 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
875 /* Compute the hash value of the checksum to determine a
876 starting point for the search in the MD5 hash value
878 hval
= archive_hashval (data
[cnt
].sum
, 16);
880 idx
= hval
% head
->sumhash_size
;
881 incr
= 1 + hval
% (head
->sumhash_size
- 2);
883 while (sumhashtab
[idx
].file_offset
!= 0)
885 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
887 /* Check the content, there could be a collision of
890 Unfortunately the sumhashent record does not include
891 the size of the stored data. So we have to search for
893 locrecent
= (struct locrecent
*) ((char *) ah
->addr
894 + head
->locrectab_offset
);
896 for (iloc
= 0; iloc
< head
->locrectab_used
; ++iloc
)
897 if (locrecent
[iloc
].refs
!= 0
898 && (locrecent
[iloc
].record
[cnt
].offset
899 == sumhashtab
[idx
].file_offset
))
902 if (iloc
!= head
->locrectab_used
903 && data
[cnt
].size
== locrecent
[iloc
].record
[cnt
].len
904 /* We have to compare the content. Either we can
905 have the data mmaped or we have to read from
907 && (file_data_available_p (ah
, sumhashtab
[idx
].file_offset
,
909 ? memcmp (data
[cnt
].addr
,
911 + sumhashtab
[idx
].file_offset
,
913 : compare_from_file (ah
, data
[cnt
].addr
,
914 sumhashtab
[idx
].file_offset
,
915 data
[cnt
].size
) == 0))
918 file_offsets
[cnt
] = sumhashtab
[idx
].file_offset
;
925 if (idx
>= head
->sumhash_size
)
926 idx
-= head
->sumhash_size
;
930 /* Find a slot for the locale name in the hash table. */
931 namehashent
= insert_name (ah
, name
, name_len
, replace
);
932 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
935 /* Determine whether we have to resize the file. */
936 if (100 * (head
->sumhash_used
+ num_new_offsets
) > 75 * head
->sumhash_size
937 || (namehashent
->locrec_offset
== 0
938 && (head
->locrectab_used
== head
->locrectab_size
939 || head
->string_used
+ name_len
+ 1 > head
->string_size
940 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)))
942 /* The current archive is not large enough. */
943 enlarge_archive (ah
, head
);
944 return add_locale (ah
, name
, data
, replace
);
947 /* Add the locale data which is not yet in the archive. */
948 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
949 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
950 && file_offsets
[cnt
] == 0)
952 /* The data for this section is not yet available in the
953 archive. Append it. */
957 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
958 if (lastpos
== (off64_t
) -1)
959 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
961 /* If block of small categories would cross page boundary,
962 align it unless it immediately follows a large category. */
963 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
964 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
966 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
968 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
969 char *zeros
= alloca (sz
);
971 memset (zeros
, 0, sz
);
972 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
973 error (EXIT_FAILURE
, errno
,
974 _("cannot add to locale archive"));
979 /* Align all data to a 16 byte boundary. */
980 if ((lastpos
& 15) != 0)
982 static const char zeros
[15] = { 0, };
984 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
985 != 16 - (lastpos
& 15))
986 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
988 lastpos
+= 16 - (lastpos
& 15);
991 /* Remember the position. */
992 file_offsets
[cnt
] = lastpos
;
993 lastoffset
= lastpos
+ data
[cnt
].size
;
995 /* Write the data. */
996 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
998 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
1000 /* Add the hash value to the hash table. */
1001 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
1003 idx
= md5hval
% head
->sumhash_size
;
1004 incr
= 1 + md5hval
% (head
->sumhash_size
- 2);
1006 while (sumhashtab
[idx
].file_offset
!= 0)
1009 if (idx
>= head
->sumhash_size
)
1010 idx
-= head
->sumhash_size
;
1013 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
1014 sumhashtab
[idx
].file_offset
= file_offsets
[cnt
];
1016 ++head
->sumhash_used
;
1019 lastoffset
= file_offsets
[LC_ALL
];
1020 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1021 if (small_mask
& (1 << cnt
))
1023 file_offsets
[cnt
] = lastoffset
;
1024 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
1027 if (namehashent
->name_offset
== 0)
1029 /* Add the name string. */
1030 memcpy ((char *) ah
->addr
+ head
->string_offset
+ head
->string_used
,
1031 name
, name_len
+ 1);
1032 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
1033 head
->string_used
+= name_len
+ 1;
1034 ++head
->namehash_used
;
1037 if (namehashent
->locrec_offset
== 0)
1039 /* Allocate a name location record. */
1040 namehashent
->locrec_offset
= (head
->locrectab_offset
1041 + (head
->locrectab_used
++
1042 * sizeof (struct locrecent
)));
1043 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1044 + namehashent
->locrec_offset
);
1045 locrecent
->refs
= 1;
1049 /* If there are other aliases pointing to this locrecent,
1050 we still need a new one. If not, reuse the old one. */
1052 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1053 + namehashent
->locrec_offset
);
1054 if (locrecent
->refs
> 1)
1057 namehashent
->locrec_offset
= (head
->locrectab_offset
1058 + (head
->locrectab_used
++
1059 * sizeof (struct locrecent
)));
1060 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1061 + namehashent
->locrec_offset
);
1062 locrecent
->refs
= 1;
1066 /* Fill in the table with the locations of the locale data. */
1067 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1069 locrecent
->record
[cnt
].offset
= file_offsets
[cnt
];
1070 locrecent
->record
[cnt
].len
= data
[cnt
].size
;
1073 return namehashent
->locrec_offset
;
1077 /* Check the content of the archive for duplicates. Add the content
1078 of the files if necessary. Add all the names, possibly overwriting
1081 add_locale_to_archive (ah
, name
, data
, replace
)
1082 struct locarhandle
*ah
;
1087 char *normalized_name
= NULL
;
1088 uint32_t locrec_offset
;
1090 /* First analyze the name to decide how to archive it. */
1091 const char *language
;
1092 const char *modifier
;
1093 const char *territory
;
1094 const char *codeset
;
1095 const char *normalized_codeset
;
1096 int mask
= _nl_explode_name (strdupa (name
),
1097 &language
, &modifier
, &territory
,
1098 &codeset
, &normalized_codeset
);
1102 if (mask
& XPG_NORM_CODESET
)
1103 /* This name contains a codeset in unnormalized form.
1104 We will store it in the archive with a normalized name. */
1105 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
1106 language
, territory
== NULL
? "" : "_", territory
?: "",
1107 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
1108 modifier
== NULL
? "" : "@", modifier
?: "");
1110 /* This call does the main work. */
1111 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
1112 if (locrec_offset
== 0)
1114 free (normalized_name
);
1115 if (mask
& XPG_NORM_CODESET
)
1116 free ((char *) normalized_codeset
);
1120 if ((mask
& XPG_CODESET
) == 0)
1122 /* This name lacks a codeset, so determine the locale's codeset and
1123 add an alias for its name with normalized codeset appended. */
1128 unsigned int nstrings
;
1129 unsigned int strindex
[0];
1130 } *filedata
= data
[LC_CTYPE
].addr
;
1131 codeset
= (char *) filedata
1132 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)];
1133 char *normalized_codeset_name
= NULL
;
1135 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
1136 mask
|= XPG_NORM_CODESET
;
1138 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
1139 language
, territory
== NULL
? "" : "_", territory
?: "",
1141 modifier
== NULL
? "" : "@", modifier
?: "");
1143 add_alias (ah
, normalized_codeset_name
, replace
,
1144 normalized_name
?: name
, &locrec_offset
);
1145 free (normalized_codeset_name
);
1148 /* Now read the locale.alias files looking for lines whose
1149 right hand side matches our name after normalization. */
1151 if (alias_file
!= NULL
)
1154 fp
= fopen (alias_file
, "rm");
1156 error (1, errno
, _("locale alias file `%s' not found"),
1159 /* No threads present. */
1160 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
1162 while (! feof_unlocked (fp
))
1164 /* It is a reasonable approach to use a fix buffer here
1166 a) we are only interested in the first two fields
1167 b) these fields must be usable as file names and so must
1174 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1179 /* Ignore leading white space. */
1180 while (isspace (cp
[0]) && cp
[0] != '\n')
1183 /* A leading '#' signals a comment line. */
1184 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1187 while (cp
[0] != '\0' && !isspace (cp
[0]))
1189 /* Terminate alias name. */
1193 /* Now look for the beginning of the value. */
1194 while (isspace (cp
[0]))
1200 while (cp
[0] != '\0' && !isspace (cp
[0]))
1202 /* Terminate value. */
1205 /* This has to be done to make the following
1206 test for the end of line possible. We are
1207 looking for the terminating '\n' which do not
1212 else if (cp
[0] != '\0')
1215 /* Does this alias refer to our locale? We will
1216 normalize the right hand side and compare the
1217 elements of the normalized form. */
1219 const char *rhs_language
;
1220 const char *rhs_modifier
;
1221 const char *rhs_territory
;
1222 const char *rhs_codeset
;
1223 const char *rhs_normalized_codeset
;
1224 int rhs_mask
= _nl_explode_name (value
,
1229 &rhs_normalized_codeset
);
1235 if (!strcmp (language
, rhs_language
)
1236 && ((rhs_mask
& XPG_CODESET
)
1237 /* He has a codeset, it must match normalized. */
1238 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1239 ? normalized_codeset
: codeset
,
1240 (rhs_mask
& XPG_NORM_CODESET
)
1241 ? rhs_normalized_codeset
: rhs_codeset
)
1242 /* He has no codeset, we must also have none. */
1243 : (mask
& XPG_CODESET
) == 0)
1244 /* Codeset (or lack thereof) matches. */
1245 && !strcmp (territory
?: "", rhs_territory
?: "")
1246 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1247 /* We have a winner. */
1248 add_alias (ah
, alias
, replace
,
1249 normalized_name
?: name
, &locrec_offset
);
1250 if (rhs_mask
& XPG_NORM_CODESET
)
1251 free ((char *) rhs_normalized_codeset
);
1256 /* Possibly not the whole line fits into the buffer.
1257 Ignore the rest of the line. */
1258 while (strchr (cp
, '\n') == NULL
)
1261 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1262 /* Make sure the inner loop will be left. The outer
1263 loop will exit at the `feof' test. */
1272 free (normalized_name
);
1274 if (mask
& XPG_NORM_CODESET
)
1275 free ((char *) normalized_codeset
);
1282 add_locales_to_archive (nlist
, list
, replace
)
1287 struct locarhandle ah
;
1290 /* Open the archive. This call never returns if we cannot
1291 successfully open the archive. */
1292 open_archive (&ah
, false);
1296 const char *fname
= *list
++;
1297 size_t fnamelen
= strlen (fname
);
1306 printf (_("Adding %s\n"), fname
);
1308 /* First see whether this really is a directory and whether it
1309 contains all the require locale category files. */
1310 if (stat64 (fname
, &st
) < 0)
1312 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1316 if (!S_ISDIR (st
.st_mode
))
1318 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1322 dirp
= opendir (fname
);
1325 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1326 fname
, strerror (errno
));
1331 while ((d
= readdir64 (dirp
)) != NULL
)
1333 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1335 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1337 unsigned char d_type
;
1339 /* We have an object of the required name. If it's
1340 a directory we have to look at a file with the
1341 prefix "SYS_". Otherwise we have found what we
1343 #ifdef _DIRENT_HAVE_D_TYPE
1346 if (d_type
!= DT_REG
)
1349 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1351 #ifdef _DIRENT_HAVE_D_TYPE
1352 if (d_type
== DT_UNKNOWN
)
1355 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1358 if (stat64 (fullname
, &st
) == -1)
1359 /* We cannot stat the file, ignore it. */
1362 d_type
= IFTODT (st
.st_mode
);
1365 if (d_type
== DT_DIR
)
1367 /* We have to do more tests. The file is a
1368 directory and it therefore must contain a
1369 regular file with the same name except a
1371 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1372 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1375 if (stat64 (fullname
, &st
) == -1)
1376 /* There is no SYS_* file or we cannot
1380 d_type
= IFTODT (st
.st_mode
);
1384 /* If we found a regular file (eventually after
1385 following a symlink) we are successful. */
1386 if (d_type
== DT_REG
)
1394 if (seen
!= __LC_LAST
- 1)
1396 /* We don't have all locale category files. Ignore the name. */
1397 error (0, 0, _("incomplete set of locale files in \"%s\""),
1402 /* Add the files to the archive. To do this we first compute
1403 sizes and the MD5 sums of all the files. */
1404 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1407 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1410 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1411 fd
= open64 (fullname
, O_RDONLY
);
1412 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1414 /* Cannot read the file. */
1420 if (S_ISDIR (st
.st_mode
))
1424 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1425 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1428 fd
= open64 (fullname
, O_RDONLY
);
1429 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1430 || !S_ISREG (st
.st_mode
))
1439 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1441 if (data
[cnt
].addr
== MAP_FAILED
)
1443 /* Cannot map it. */
1448 data
[cnt
].size
= st
.st_size
;
1449 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1451 /* We don't need the file descriptor anymore. */
1455 if (cnt
!= __LC_LAST
)
1459 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1461 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1466 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1468 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1470 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1474 close_archive (&ah
);
1481 delete_locales_from_archive (nlist
, list
)
1485 struct locarhandle ah
;
1486 struct locarhead
*head
;
1487 struct namehashent
*namehashtab
;
1489 /* Open the archive. This call never returns if we cannot
1490 successfully open the archive. */
1491 open_archive (&ah
, false);
1494 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1495 + head
->namehash_offset
);
1499 const char *locname
= *list
++;
1504 /* Search for this locale in the archive. */
1505 hval
= archive_hashval (locname
, strlen (locname
));
1507 idx
= hval
% head
->namehash_size
;
1508 incr
= 1 + hval
% (head
->namehash_size
- 2);
1510 /* If the name_offset field is zero this means this is no
1511 deleted entry and therefore no entry can be found. */
1512 while (namehashtab
[idx
].name_offset
!= 0)
1514 if (namehashtab
[idx
].hashval
== hval
1515 && (strcmp (locname
,
1516 (char *) ah
.addr
+ namehashtab
[idx
].name_offset
)
1519 /* Found the entry. Now mark it as removed by zero-ing
1520 the reference to the locale record. */
1521 namehashtab
[idx
].locrec_offset
= 0;
1526 if (idx
>= head
->namehash_size
)
1527 idx
-= head
->namehash_size
;
1530 if (namehashtab
[idx
].name_offset
== 0 && ! be_quiet
)
1531 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1534 close_archive (&ah
);
1543 uint32_t locrec_offset
;
1549 const unsigned char *sum
;
1550 uint32_t file_offset
;
1556 nameentcmp (const void *a
, const void *b
)
1558 return strcmp (((const struct nameent
*) a
)->name
,
1559 ((const struct nameent
*) b
)->name
);
1564 dataentcmp (const void *a
, const void *b
)
1566 if (((const struct dataent
*) a
)->file_offset
1567 < ((const struct dataent
*) b
)->file_offset
)
1570 if (((const struct dataent
*) a
)->file_offset
1571 > ((const struct dataent
*) b
)->file_offset
)
1579 show_archive_content (int verbose
)
1581 struct locarhandle ah
;
1582 struct locarhead
*head
;
1583 struct namehashent
*namehashtab
;
1584 struct nameent
*names
;
1587 /* Open the archive. This call never returns if we cannot
1588 successfully open the archive. */
1589 open_archive (&ah
, true);
1593 names
= (struct nameent
*) xmalloc (head
->namehash_used
1594 * sizeof (struct nameent
));
1596 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1597 + head
->namehash_offset
);
1598 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
1599 if (namehashtab
[cnt
].locrec_offset
!= 0)
1601 assert (used
< head
->namehash_used
);
1602 names
[used
].name
= ah
.addr
+ namehashtab
[cnt
].name_offset
;
1603 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
1606 /* Sort the names. */
1607 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1611 struct dataent
*files
;
1612 struct sumhashent
*sumhashtab
;
1615 files
= (struct dataent
*) xmalloc (head
->sumhash_used
1616 * sizeof (struct dataent
));
1618 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1619 + head
->sumhash_offset
);
1620 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
1621 if (sumhashtab
[cnt
].file_offset
!= 0)
1623 assert (sumused
< head
->sumhash_used
);
1624 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1625 files
[sumused
].file_offset
= sumhashtab
[cnt
].file_offset
;
1626 files
[sumused
++].nlink
= 0;
1629 /* Sort by file locations. */
1630 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1632 /* Compute nlink fields. */
1633 for (cnt
= 0; cnt
< used
; ++cnt
)
1635 struct locrecent
*locrec
;
1638 locrec
= (struct locrecent
*) ((char *) ah
.addr
1639 + names
[cnt
].locrec_offset
);
1640 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1641 if (locrec
->record
[LC_ALL
].offset
!= 0
1643 || (locrec
->record
[idx
].offset
1644 < locrec
->record
[LC_ALL
].offset
)
1645 || (locrec
->record
[idx
].offset
+ locrec
->record
[idx
].len
1646 > (locrec
->record
[LC_ALL
].offset
1647 + locrec
->record
[LC_ALL
].len
)))
1650 struct dataent
*data
, dataent
;
1652 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1653 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1654 sizeof (struct dataent
),
1656 assert (data
!= NULL
);
1662 for (cnt
= 0; cnt
< used
; ++cnt
)
1664 struct locrecent
*locrec
;
1667 locrec
= (struct locrecent
*) ((char *) ah
.addr
1668 + names
[cnt
].locrec_offset
);
1669 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1672 struct dataent
*data
, dataent
;
1674 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1675 if (locrec
->record
[LC_ALL
].offset
!= 0
1676 && dataent
.file_offset
>= locrec
->record
[LC_ALL
].offset
1677 && (dataent
.file_offset
+ locrec
->record
[idx
].len
1678 <= (locrec
->record
[LC_ALL
].offset
1679 + locrec
->record
[LC_ALL
].len
)))
1680 dataent
.file_offset
= locrec
->record
[LC_ALL
].offset
;
1682 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1683 sizeof (struct dataent
),
1685 printf ("%6d %7x %3d%c ",
1686 locrec
->record
[idx
].len
, locrec
->record
[idx
].offset
,
1688 dataent
.file_offset
== locrec
->record
[LC_ALL
].offset
1690 for (i
= 0; i
< 16; i
+= 4)
1691 printf ("%02x%02x%02x%02x",
1692 data
->sum
[i
], data
->sum
[i
+ 1],
1693 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1694 printf (" %s/%s\n", names
[cnt
].name
,
1695 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1701 for (cnt
= 0; cnt
< used
; ++cnt
)
1702 puts (names
[cnt
].name
);
1704 close_archive (&ah
);
1706 exit (EXIT_SUCCESS
);