1 /* Copyright (C) 2002, 2003, 2005, 2007, 2009 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
33 #include <stdio_ext.h>
39 #include <sys/param.h>
42 #include "../../crypt/md5.h"
43 #include "../localeinfo.h"
44 #include "../locarchive.h"
45 #include "localedef.h"
47 /* Define the hash function. We define the function as static inline.
48 We must change the name so as not to conflict with simple-hash.h. */
49 #define compute_hashval static inline archive_hashval
50 #define hashval_t uint32_t
52 #undef compute_hashval
54 extern const char *output_prefix
;
56 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
58 static const char *locnames
[] =
60 #define DEFINE_CATEGORY(category, category_name, items, a) \
61 [category] = category_name,
62 #include "categories.def"
63 #undef DEFINE_CATEGORY
67 /* Size of the initial archive header. */
68 #define INITIAL_NUM_NAMES 900
69 #define INITIAL_SIZE_STRINGS 7500
70 #define INITIAL_NUM_LOCREC 420
71 #define INITIAL_NUM_SUMS 2000
74 /* Size of the reserved address space area. */
75 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
79 create_archive (const char *archivefname
, struct locarhandle
*ah
)
82 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
83 struct locarhead head
;
87 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
89 /* Create a temporary file in the correct directory. */
92 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
94 /* Create the initial content of the archive. */
95 head
.magic
= AR_MAGIC
;
97 head
.namehash_offset
= sizeof (struct locarhead
);
98 head
.namehash_used
= 0;
99 head
.namehash_size
= next_prime (INITIAL_NUM_NAMES
);
101 head
.string_offset
= (head
.namehash_offset
102 + head
.namehash_size
* sizeof (struct namehashent
));
103 head
.string_used
= 0;
104 head
.string_size
= INITIAL_SIZE_STRINGS
;
106 head
.locrectab_offset
= head
.string_offset
+ head
.string_size
;
107 head
.locrectab_used
= 0;
108 head
.locrectab_size
= INITIAL_NUM_LOCREC
;
110 head
.sumhash_offset
= (head
.locrectab_offset
111 + head
.locrectab_size
* sizeof (struct locrecent
));
112 head
.sumhash_used
= 0;
113 head
.sumhash_size
= next_prime (INITIAL_NUM_SUMS
);
115 total
= head
.sumhash_offset
+ head
.sumhash_size
* sizeof (struct sumhashent
);
117 /* Write out the header and create room for the other data structures. */
118 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
122 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
125 if (ftruncate64 (fd
, total
) != 0)
129 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
132 /* To prepare for enlargements of the mmaped area reserve some
134 size_t reserved
= RESERVE_MMAP_SIZE
;
137 && ((p
= mmap64 (NULL
, reserved
, PROT_NONE
, MAP_PRIVATE
| MAP_ANON
,
138 -1, 0)) != MAP_FAILED
))
146 /* Map the header and all the administration data structures. */
147 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
152 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
155 /* Now try to rename it. We don't use the rename function since
156 this would overwrite a file which has been created in
158 if (link (fname
, archivefname
) == -1)
162 /* We cannot use the just created file. */
166 if (errval
== EEXIST
)
168 /* There is already an archive. Must have been a localedef run
169 which happened in parallel. Simply open this file then. */
170 open_archive (ah
, false);
174 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
177 /* Remove the temporary name. */
180 /* Make the file globally readable. */
181 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
184 unlink (archivefname
);
185 error (EXIT_FAILURE
, errval
,
186 _("cannot change mode of new locale archive"));
192 ah
->reserved
= reserved
;
196 /* This structure and qsort comparator function are used below to sort an
197 old archive's locrec table in order of data position in the file. */
201 struct locrecent
*locrec
;
205 oldlocrecentcmp (const void *a
, const void *b
)
207 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
208 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
209 uint32_t start_a
= -1, end_a
= 0;
210 uint32_t start_b
= -1, end_b
= 0;
213 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
216 if (la
->record
[cnt
].offset
< start_a
)
217 start_a
= la
->record
[cnt
].offset
;
218 if (la
->record
[cnt
].offset
+ la
->record
[cnt
].len
> end_a
)
219 end_a
= la
->record
[cnt
].offset
+ la
->record
[cnt
].len
;
221 assert (start_a
!= (uint32_t)-1);
224 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
227 if (lb
->record
[cnt
].offset
< start_b
)
228 start_b
= lb
->record
[cnt
].offset
;
229 if (lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
> end_b
)
230 end_b
= lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
;
232 assert (start_b
!= (uint32_t)-1);
235 if (start_a
!= start_b
)
236 return (int)start_a
- (int)start_b
;
237 return (int)end_a
- (int)end_b
;
241 /* forward decls for below */
242 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
243 locale_data_t data
, bool replace
);
244 void add_alias (struct locarhandle
*ah
, const char *alias
,
245 bool replace
, const char *oldname
,
246 uint32_t *locrec_offset_p
);
250 file_data_available_p (struct locarhandle
*ah
, uint32_t offset
, uint32_t size
)
252 if (offset
< ah
->mmaped
&& offset
+ size
<= ah
->mmaped
)
256 if (fstat64 (ah
->fd
, &st
) != 0)
259 if (st
.st_size
> ah
->reserved
)
262 const size_t pagesz
= getpagesize ();
263 size_t start
= ah
->mmaped
& ~(pagesz
- 1);
264 void *p
= mmap64 (ah
->addr
+ start
, st
.st_size
- start
,
265 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_FIXED
,
273 ah
->mmaped
= st
.st_size
;
279 compare_from_file (struct locarhandle
*ah
, void *p1
, uint32_t offset2
,
282 void *p2
= xmalloc (size
);
283 if (pread (ah
->fd
, p2
, size
, offset2
) != size
)
284 WITH_CUR_LOCALE (error (4, errno
,
285 _("cannot read data from locale archive")));
287 int res
= memcmp (p1
, p2
, size
);
294 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
298 struct locarhead newhead
;
301 unsigned int cnt
, loccnt
;
302 struct namehashent
*oldnamehashtab
;
303 struct locrecent
*oldlocrectab
;
304 struct locarhandle new_ah
;
305 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
306 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
307 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
310 memcpy (archivefname
, output_prefix
, prefix_len
);
311 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
312 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
314 /* Not all of the old file has to be mapped. Change this now this
315 we will have to access the whole content. */
316 if (fstat64 (ah
->fd
, &st
) != 0)
318 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
320 if (st
.st_size
< ah
->reserved
)
321 ah
->addr
= mmap64 (ah
->addr
, st
.st_size
, PROT_READ
| PROT_WRITE
,
322 MAP_SHARED
| MAP_FIXED
, ah
->fd
, 0);
325 munmap (ah
->addr
, ah
->reserved
);
326 ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
327 MAP_SHARED
, ah
->fd
, 0);
328 ah
->reserved
= st
.st_size
;
331 if (ah
->addr
== MAP_FAILED
)
333 ah
->mmaped
= st
.st_size
;
335 /* Create a temporary file in the correct directory. */
336 fd
= mkstemp (fname
);
338 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
340 /* Copy the existing head information. */
343 /* Create the new archive header. The sizes of the various tables
344 should be double from what is currently used. */
345 newhead
.namehash_size
= MAX (next_prime (2 * newhead
.namehash_used
),
346 newhead
.namehash_size
);
348 printf ("name: size: %u, used: %d, new: size: %u\n",
349 head
->namehash_size
, head
->namehash_used
, newhead
.namehash_size
);
351 newhead
.string_offset
= (newhead
.namehash_offset
352 + (newhead
.namehash_size
353 * sizeof (struct namehashent
)));
354 /* Keep the string table size aligned to 4 bytes, so that
355 all the struct { uint32_t } types following are happy. */
356 newhead
.string_size
= MAX ((2 * newhead
.string_used
+ 3) & -4,
357 newhead
.string_size
);
359 newhead
.locrectab_offset
= newhead
.string_offset
+ newhead
.string_size
;
360 newhead
.locrectab_size
= MAX (2 * newhead
.locrectab_used
,
361 newhead
.locrectab_size
);
363 newhead
.sumhash_offset
= (newhead
.locrectab_offset
364 + (newhead
.locrectab_size
365 * sizeof (struct locrecent
)));
366 newhead
.sumhash_size
= MAX (next_prime (2 * newhead
.sumhash_used
),
367 newhead
.sumhash_size
);
369 total
= (newhead
.sumhash_offset
370 + newhead
.sumhash_size
* sizeof (struct sumhashent
));
372 /* The new file is empty now. */
373 newhead
.namehash_used
= 0;
374 newhead
.string_used
= 0;
375 newhead
.locrectab_used
= 0;
376 newhead
.sumhash_used
= 0;
378 /* Write out the header and create room for the other data structures. */
379 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
384 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
387 if (ftruncate64 (fd
, total
) != 0)
391 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
394 /* To prepare for enlargements of the mmaped area reserve some
396 size_t reserved
= RESERVE_MMAP_SIZE
;
399 && ((p
= mmap64 (NULL
, reserved
, PROT_NONE
, MAP_PRIVATE
| MAP_ANON
,
400 -1, 0)) != MAP_FAILED
))
408 /* Map the header and all the administration data structures. */
409 p
= mmap64 (p
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
| xflags
, fd
, 0);
414 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
417 /* Lock the new file. */
418 if (lockf64 (fd
, F_LOCK
, total
) != 0)
422 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
425 new_ah
.mmaped
= total
;
428 new_ah
.reserved
= reserved
;
430 /* Walk through the hash name hash table to find out what data is
431 still referenced and transfer it into the new file. */
432 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
433 + head
->namehash_offset
);
434 oldlocrectab
= (struct locrecent
*) ((char *) ah
->addr
435 + head
->locrectab_offset
);
437 /* Sort the old locrec table in order of data position. */
438 struct oldlocrecent oldlocrecarray
[head
->namehash_size
];
439 for (cnt
= 0, loccnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
440 if (oldnamehashtab
[cnt
].locrec_offset
!= 0)
442 oldlocrecarray
[loccnt
].cnt
= cnt
;
443 oldlocrecarray
[loccnt
++].locrec
444 = (struct locrecent
*) ((char *) ah
->addr
445 + oldnamehashtab
[cnt
].locrec_offset
);
447 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
450 uint32_t last_locrec_offset
= 0;
451 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
453 /* Insert this entry in the new hash table. */
454 locale_data_t old_data
;
456 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
458 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
461 old_data
[idx
].size
= oldlocrec
->record
[idx
].len
;
463 = ((char *) ah
->addr
+ oldlocrec
->record
[idx
].offset
);
465 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
469 if (cnt
> 0 && oldlocrecarray
[cnt
- 1].locrec
== oldlocrec
)
473 + oldnamehashtab
[oldlocrecarray
[cnt
- 1].cnt
].name_offset
);
477 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
478 0, oldname
, &last_locrec_offset
);
485 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
487 if (last_locrec_offset
== 0)
488 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
491 /* Make the file globally readable. */
492 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
496 error (EXIT_FAILURE
, errval
,
497 _("cannot change mode of resized locale archive"));
500 /* Rename the new file. */
501 if (rename (fname
, archivefname
) != 0)
505 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
508 /* Close the old file. */
511 /* Add the information for the new one. */
517 open_archive (struct locarhandle
*ah
, bool readonly
)
522 struct locarhead head
;
524 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
525 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
528 memcpy (archivefname
, output_prefix
, prefix_len
);
529 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
533 /* Open the archive. We must have exclusive write access. */
534 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
537 /* Maybe the file does not yet exist. */
542 static const struct locarhead nullhead
=
545 .namehash_offset
= 0,
549 ah
->addr
= (void *) &nullhead
;
553 create_archive (archivefname
, ah
);
558 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
562 if (fstat64 (fd
, &st
) < 0)
563 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
566 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
570 if (retry
++ < max_locarchive_open_retry
)
574 /* Wait for a bit. */
576 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
577 (void) nanosleep (&req
, NULL
);
582 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
586 /* One more check. Maybe another process replaced the archive file
587 with a new, larger one since we opened the file. */
588 if (stat64 (archivefname
, &st2
) == -1
589 || st
.st_dev
!= st2
.st_dev
590 || st
.st_ino
!= st2
.st_ino
)
592 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
597 /* Leave the loop. */
601 /* Read the header. */
602 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
604 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
605 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
609 ah
->mmaped
= st
.st_size
;
611 /* To prepare for enlargements of the mmaped area reserve some
613 size_t reserved
= RESERVE_MMAP_SIZE
;
616 if (st
.st_size
< reserved
617 && ((p
= mmap64 (NULL
, reserved
, PROT_NONE
, MAP_PRIVATE
| MAP_ANON
,
618 -1, 0)) != MAP_FAILED
))
623 reserved
= st
.st_size
;
626 /* Map the entire file. We might need to compare the category data
627 in the file with the newly added data. */
628 ah
->addr
= mmap64 (p
, st
.st_size
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
629 MAP_SHARED
| xflags
, fd
, 0);
630 if (ah
->addr
== MAP_FAILED
)
632 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
633 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
635 ah
->reserved
= reserved
;
640 close_archive (struct locarhandle
*ah
)
644 munmap (ah
->addr
, ah
->reserved
);
649 #include "../../intl/explodename.c"
650 #include "../../intl/l10nflist.c"
653 insert_name (struct locarhandle
*ah
,
654 const char *name
, size_t name_len
, bool replace
)
656 const struct locarhead
*const head
= ah
->addr
;
657 struct namehashent
*namehashtab
658 = (struct namehashent
*) ((char *) ah
->addr
+ head
->namehash_offset
);
659 unsigned int insert_idx
, idx
, incr
;
661 /* Hash value of the locale name. */
662 uint32_t hval
= archive_hashval (name
, name_len
);
665 idx
= hval
% head
->namehash_size
;
666 incr
= 1 + hval
% (head
->namehash_size
- 2);
668 /* If the name_offset field is zero this means this is a
669 deleted entry and therefore no entry can be found. */
670 while (namehashtab
[idx
].name_offset
!= 0)
672 if (namehashtab
[idx
].hashval
== hval
674 (char *) ah
->addr
+ namehashtab
[idx
].name_offset
) == 0)
676 /* Found the entry. */
677 if (namehashtab
[idx
].locrec_offset
!= 0 && ! replace
)
680 error (0, 0, _("locale '%s' already exists"), name
);
687 if (namehashtab
[idx
].hashval
== hval
&& ! be_quiet
)
689 error (0, 0, "hash collision (%u) %s, %s",
690 hval
, name
, (char *) ah
->addr
+ namehashtab
[idx
].name_offset
);
693 /* Remember the first place we can insert the new entry. */
694 if (namehashtab
[idx
].locrec_offset
== 0 && insert_idx
== -1)
698 if (idx
>= head
->namehash_size
)
699 idx
-= head
->namehash_size
;
702 /* Add as early as possible. */
703 if (insert_idx
!= -1)
706 namehashtab
[idx
].hashval
= hval
; /* no-op if replacing an old entry. */
707 return &namehashtab
[idx
];
711 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
712 const char *oldname
, uint32_t *locrec_offset_p
)
714 uint32_t locrec_offset
= *locrec_offset_p
;
715 struct locarhead
*head
= ah
->addr
;
716 const size_t name_len
= strlen (alias
);
717 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
719 if (namehashent
== NULL
&& ! replace
)
722 if (namehashent
->name_offset
== 0)
724 /* We are adding a new hash entry for this alias.
725 Determine whether we have to resize the file. */
726 if (head
->string_used
+ name_len
+ 1 > head
->string_size
727 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)
729 /* The current archive is not large enough. */
730 enlarge_archive (ah
, head
);
732 /* The locrecent might have moved, so we have to look up
733 the old name afresh. */
734 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
735 assert (namehashent
->name_offset
!= 0);
736 assert (namehashent
->locrec_offset
!= 0);
737 *locrec_offset_p
= namehashent
->locrec_offset
;
739 /* Tail call to try the whole thing again. */
740 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
744 /* Add the name string. */
745 memcpy (ah
->addr
+ head
->string_offset
+ head
->string_used
,
746 alias
, name_len
+ 1);
747 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
748 head
->string_used
+= name_len
+ 1;
750 ++head
->namehash_used
;
753 if (namehashent
->locrec_offset
!= 0)
755 /* Replacing an existing entry.
756 Mark that we are no longer using the old locrecent. */
757 struct locrecent
*locrecent
758 = (struct locrecent
*) ((char *) ah
->addr
759 + namehashent
->locrec_offset
);
763 /* Point this entry at the locrecent installed for the main name. */
764 namehashent
->locrec_offset
= locrec_offset
;
767 static int /* qsort comparator used below */
768 cmpcategorysize (const void *a
, const void *b
)
770 if (*(const void **) a
== NULL
)
772 if (*(const void **) b
== NULL
)
774 return ((*(const struct locale_category_data
**) a
)->size
775 - (*(const struct locale_category_data
**) b
)->size
);
778 /* Check the content of the archive for duplicates. Add the content
779 of the files if necessary. Returns the locrec_offset. */
781 add_locale (struct locarhandle
*ah
,
782 const char *name
, locale_data_t data
, bool replace
)
784 /* First look for the name. If it already exists and we are not
785 supposed to replace it don't do anything. If it does not exist
786 we have to allocate a new locale record. */
787 size_t name_len
= strlen (name
);
788 uint32_t file_offsets
[__LC_LAST
];
789 unsigned int num_new_offsets
= 0;
790 struct sumhashent
*sumhashtab
;
792 unsigned int cnt
, idx
;
793 struct locarhead
*head
;
794 struct namehashent
*namehashent
;
796 struct locrecent
*locrecent
;
799 struct locale_category_data
*size_order
[__LC_LAST
];
800 const size_t pagesz
= getpagesize ();
804 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
805 + head
->sumhash_offset
);
807 memset (file_offsets
, 0, sizeof (file_offsets
));
809 size_order
[LC_ALL
] = NULL
;
810 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
812 size_order
[cnt
] = &data
[cnt
];
814 /* Sort the array in ascending order of data size. */
815 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
818 data
[LC_ALL
].size
= 0;
819 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
820 if (size_order
[cnt
] != NULL
)
822 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
823 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
825 /* This category makes the small-categories block
826 stop being small, so this is the end of the road. */
828 size_order
[cnt
++] = NULL
;
829 while (cnt
< __LC_LAST
);
832 data
[LC_ALL
].size
+= rounded_size
;
833 small_mask
|= 1 << (size_order
[cnt
] - data
);
836 /* Copy the data for all the small categories into the LC_ALL
839 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
840 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
842 ptr
= data
[LC_ALL
].addr
;
843 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
844 if (small_mask
& (1 << cnt
))
846 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
847 ptr
+= (data
[cnt
].size
+ 15) & -16;
849 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
851 /* For each locale category data set determine whether the same data
852 is already somewhere in the archive. */
853 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
854 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
858 /* Compute the hash value of the checksum to determine a
859 starting point for the search in the MD5 hash value
861 hval
= archive_hashval (data
[cnt
].sum
, 16);
863 idx
= hval
% head
->sumhash_size
;
864 incr
= 1 + hval
% (head
->sumhash_size
- 2);
866 while (sumhashtab
[idx
].file_offset
!= 0)
868 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
870 /* Check the content, there could be a collision of
873 Unfortunately the sumhashent record does not include
874 the size of the stored data. So we have to search for
876 locrecent
= (struct locrecent
*) ((char *) ah
->addr
877 + head
->locrectab_offset
);
879 for (iloc
= 0; iloc
< head
->locrectab_used
; ++iloc
)
880 if (locrecent
[iloc
].refs
!= 0
881 && (locrecent
[iloc
].record
[cnt
].offset
882 == sumhashtab
[idx
].file_offset
))
885 if (iloc
!= head
->locrectab_used
886 && data
[cnt
].size
== locrecent
[iloc
].record
[cnt
].len
887 /* We have to compare the content. Either we can
888 have the data mmaped or we have to read from
890 && (file_data_available_p (ah
, sumhashtab
[idx
].file_offset
,
892 ? memcmp (data
[cnt
].addr
,
894 + sumhashtab
[idx
].file_offset
,
896 : compare_from_file (ah
, data
[cnt
].addr
,
897 sumhashtab
[idx
].file_offset
,
898 data
[cnt
].size
) == 0))
901 file_offsets
[cnt
] = sumhashtab
[idx
].file_offset
;
908 if (idx
>= head
->sumhash_size
)
909 idx
-= head
->sumhash_size
;
913 /* Find a slot for the locale name in the hash table. */
914 namehashent
= insert_name (ah
, name
, name_len
, replace
);
915 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
918 /* Determine whether we have to resize the file. */
919 if (100 * (head
->sumhash_used
+ num_new_offsets
) > 75 * head
->sumhash_size
920 || (namehashent
->locrec_offset
== 0
921 && (head
->locrectab_used
== head
->locrectab_size
922 || head
->string_used
+ name_len
+ 1 > head
->string_size
923 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)))
925 /* The current archive is not large enough. */
926 enlarge_archive (ah
, head
);
927 return add_locale (ah
, name
, data
, replace
);
930 /* Add the locale data which is not yet in the archive. */
931 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
932 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
933 && file_offsets
[cnt
] == 0)
935 /* The data for this section is not yet available in the
936 archive. Append it. */
940 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
941 if (lastpos
== (off64_t
) -1)
942 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
944 /* If block of small categories would cross page boundary,
945 align it unless it immediately follows a large category. */
946 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
947 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
949 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
951 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
952 char *zeros
= alloca (sz
);
954 memset (zeros
, 0, sz
);
955 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
956 error (EXIT_FAILURE
, errno
,
957 _("cannot add to locale archive"));
962 /* Align all data to a 16 byte boundary. */
963 if ((lastpos
& 15) != 0)
965 static const char zeros
[15] = { 0, };
967 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
968 != 16 - (lastpos
& 15))
969 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
971 lastpos
+= 16 - (lastpos
& 15);
974 /* Remember the position. */
975 file_offsets
[cnt
] = lastpos
;
976 lastoffset
= lastpos
+ data
[cnt
].size
;
978 /* Write the data. */
979 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
981 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
983 /* Add the hash value to the hash table. */
984 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
986 idx
= md5hval
% head
->sumhash_size
;
987 incr
= 1 + md5hval
% (head
->sumhash_size
- 2);
989 while (sumhashtab
[idx
].file_offset
!= 0)
992 if (idx
>= head
->sumhash_size
)
993 idx
-= head
->sumhash_size
;
996 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
997 sumhashtab
[idx
].file_offset
= file_offsets
[cnt
];
999 ++head
->sumhash_used
;
1002 lastoffset
= file_offsets
[LC_ALL
];
1003 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1004 if (small_mask
& (1 << cnt
))
1006 file_offsets
[cnt
] = lastoffset
;
1007 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
1010 if (namehashent
->name_offset
== 0)
1012 /* Add the name string. */
1013 memcpy ((char *) ah
->addr
+ head
->string_offset
+ head
->string_used
,
1014 name
, name_len
+ 1);
1015 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
1016 head
->string_used
+= name_len
+ 1;
1017 ++head
->namehash_used
;
1020 if (namehashent
->locrec_offset
== 0)
1022 /* Allocate a name location record. */
1023 namehashent
->locrec_offset
= (head
->locrectab_offset
1024 + (head
->locrectab_used
++
1025 * sizeof (struct locrecent
)));
1026 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1027 + namehashent
->locrec_offset
);
1028 locrecent
->refs
= 1;
1032 /* If there are other aliases pointing to this locrecent,
1033 we still need a new one. If not, reuse the old one. */
1035 locrecent
= (struct locrecent
*) ((char *) ah
->addr
1036 + namehashent
->locrec_offset
);
1037 if (locrecent
->refs
> 1)
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 /* Fill in the table with the locations of the locale data. */
1050 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1052 locrecent
->record
[cnt
].offset
= file_offsets
[cnt
];
1053 locrecent
->record
[cnt
].len
= data
[cnt
].size
;
1056 return namehashent
->locrec_offset
;
1060 /* Check the content of the archive for duplicates. Add the content
1061 of the files if necessary. Add all the names, possibly overwriting
1064 add_locale_to_archive (ah
, name
, data
, replace
)
1065 struct locarhandle
*ah
;
1070 char *normalized_name
= NULL
;
1071 uint32_t locrec_offset
;
1073 /* First analyze the name to decide how to archive it. */
1074 const char *language
;
1075 const char *modifier
;
1076 const char *territory
;
1077 const char *codeset
;
1078 const char *normalized_codeset
;
1079 int mask
= _nl_explode_name (strdupa (name
),
1080 &language
, &modifier
, &territory
,
1081 &codeset
, &normalized_codeset
);
1083 if (mask
& XPG_NORM_CODESET
)
1084 /* This name contains a codeset in unnormalized form.
1085 We will store it in the archive with a normalized name. */
1086 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
1087 language
, territory
== NULL
? "" : "_", territory
?: "",
1088 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
1089 modifier
== NULL
? "" : "@", modifier
?: "");
1091 /* This call does the main work. */
1092 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
1093 if (locrec_offset
== 0)
1095 free (normalized_name
);
1096 if (mask
& XPG_NORM_CODESET
)
1097 free ((char *) normalized_codeset
);
1101 if ((mask
& XPG_CODESET
) == 0)
1103 /* This name lacks a codeset, so determine the locale's codeset and
1104 add an alias for its name with normalized codeset appended. */
1109 unsigned int nstrings
;
1110 unsigned int strindex
[0];
1111 } *filedata
= data
[LC_CTYPE
].addr
;
1112 codeset
= (char *) filedata
1113 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)];
1114 char *normalized_codeset_name
= NULL
;
1116 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
1117 mask
|= XPG_NORM_CODESET
;
1119 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
1120 language
, territory
== NULL
? "" : "_", territory
?: "",
1122 modifier
== NULL
? "" : "@", modifier
?: "");
1124 add_alias (ah
, normalized_codeset_name
, replace
,
1125 normalized_name
?: name
, &locrec_offset
);
1126 free (normalized_codeset_name
);
1129 /* Now read the locale.alias files looking for lines whose
1130 right hand side matches our name after normalization. */
1131 if (alias_file
!= NULL
)
1134 fp
= fopen (alias_file
, "rm");
1136 error (1, errno
, _("locale alias file `%s' not found"),
1139 /* No threads present. */
1140 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
1142 while (! feof_unlocked (fp
))
1144 /* It is a reasonable approach to use a fix buffer here
1146 a) we are only interested in the first two fields
1147 b) these fields must be usable as file names and so must
1154 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1159 /* Ignore leading white space. */
1160 while (isspace (cp
[0]) && cp
[0] != '\n')
1163 /* A leading '#' signals a comment line. */
1164 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1167 while (cp
[0] != '\0' && !isspace (cp
[0]))
1169 /* Terminate alias name. */
1173 /* Now look for the beginning of the value. */
1174 while (isspace (cp
[0]))
1180 while (cp
[0] != '\0' && !isspace (cp
[0]))
1182 /* Terminate value. */
1185 /* This has to be done to make the following
1186 test for the end of line possible. We are
1187 looking for the terminating '\n' which do not
1192 else if (cp
[0] != '\0')
1195 /* Does this alias refer to our locale? We will
1196 normalize the right hand side and compare the
1197 elements of the normalized form. */
1199 const char *rhs_language
;
1200 const char *rhs_modifier
;
1201 const char *rhs_territory
;
1202 const char *rhs_codeset
;
1203 const char *rhs_normalized_codeset
;
1204 int rhs_mask
= _nl_explode_name (value
,
1209 &rhs_normalized_codeset
);
1210 if (!strcmp (language
, rhs_language
)
1211 && ((rhs_mask
& XPG_CODESET
)
1212 /* He has a codeset, it must match normalized. */
1213 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1214 ? normalized_codeset
: codeset
,
1215 (rhs_mask
& XPG_NORM_CODESET
)
1216 ? rhs_normalized_codeset
: rhs_codeset
)
1217 /* He has no codeset, we must also have none. */
1218 : (mask
& XPG_CODESET
) == 0)
1219 /* Codeset (or lack thereof) matches. */
1220 && !strcmp (territory
?: "", rhs_territory
?: "")
1221 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1222 /* We have a winner. */
1223 add_alias (ah
, alias
, replace
,
1224 normalized_name
?: name
, &locrec_offset
);
1225 if (rhs_mask
& XPG_NORM_CODESET
)
1226 free ((char *) rhs_normalized_codeset
);
1231 /* Possibly not the whole line fits into the buffer.
1232 Ignore the rest of the line. */
1233 while (strchr (cp
, '\n') == NULL
)
1236 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1237 /* Make sure the inner loop will be left. The outer
1238 loop will exit at the `feof' test. */
1246 free (normalized_name
);
1248 if (mask
& XPG_NORM_CODESET
)
1249 free ((char *) normalized_codeset
);
1256 add_locales_to_archive (nlist
, list
, replace
)
1261 struct locarhandle ah
;
1264 /* Open the archive. This call never returns if we cannot
1265 successfully open the archive. */
1266 open_archive (&ah
, false);
1270 const char *fname
= *list
++;
1271 size_t fnamelen
= strlen (fname
);
1280 printf (_("Adding %s\n"), fname
);
1282 /* First see whether this really is a directory and whether it
1283 contains all the require locale category files. */
1284 if (stat64 (fname
, &st
) < 0)
1286 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1290 if (!S_ISDIR (st
.st_mode
))
1292 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1296 dirp
= opendir (fname
);
1299 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1300 fname
, strerror (errno
));
1305 while ((d
= readdir64 (dirp
)) != NULL
)
1307 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1309 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1311 unsigned char d_type
;
1313 /* We have an object of the required name. If it's
1314 a directory we have to look at a file with the
1315 prefix "SYS_". Otherwise we have found what we
1317 #ifdef _DIRENT_HAVE_D_TYPE
1320 if (d_type
!= DT_REG
)
1323 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1325 #ifdef _DIRENT_HAVE_D_TYPE
1326 if (d_type
== DT_UNKNOWN
)
1329 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1332 if (stat64 (fullname
, &st
) == -1)
1333 /* We cannot stat the file, ignore it. */
1336 d_type
= IFTODT (st
.st_mode
);
1339 if (d_type
== DT_DIR
)
1341 /* We have to do more tests. The file is a
1342 directory and it therefore must contain a
1343 regular file with the same name except a
1345 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1346 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1349 if (stat64 (fullname
, &st
) == -1)
1350 /* There is no SYS_* file or we cannot
1354 d_type
= IFTODT (st
.st_mode
);
1358 /* If we found a regular file (eventually after
1359 following a symlink) we are successful. */
1360 if (d_type
== DT_REG
)
1368 if (seen
!= __LC_LAST
- 1)
1370 /* We don't have all locale category files. Ignore the name. */
1371 error (0, 0, _("incomplete set of locale files in \"%s\""),
1376 /* Add the files to the archive. To do this we first compute
1377 sizes and the MD5 sums of all the files. */
1378 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1381 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1384 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1385 fd
= open64 (fullname
, O_RDONLY
);
1386 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1388 /* Cannot read the file. */
1394 if (S_ISDIR (st
.st_mode
))
1398 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1399 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1402 fd
= open64 (fullname
, O_RDONLY
);
1403 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1404 || !S_ISREG (st
.st_mode
))
1413 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1415 if (data
[cnt
].addr
== MAP_FAILED
)
1417 /* Cannot map it. */
1422 data
[cnt
].size
= st
.st_size
;
1423 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1425 /* We don't need the file descriptor anymore. */
1429 if (cnt
!= __LC_LAST
)
1433 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1435 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1440 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1442 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1444 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1448 close_archive (&ah
);
1455 delete_locales_from_archive (nlist
, list
)
1459 struct locarhandle ah
;
1460 struct locarhead
*head
;
1461 struct namehashent
*namehashtab
;
1463 /* Open the archive. This call never returns if we cannot
1464 successfully open the archive. */
1465 open_archive (&ah
, false);
1468 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1469 + head
->namehash_offset
);
1473 const char *locname
= *list
++;
1478 /* Search for this locale in the archive. */
1479 hval
= archive_hashval (locname
, strlen (locname
));
1481 idx
= hval
% head
->namehash_size
;
1482 incr
= 1 + hval
% (head
->namehash_size
- 2);
1484 /* If the name_offset field is zero this means this is no
1485 deleted entry and therefore no entry can be found. */
1486 while (namehashtab
[idx
].name_offset
!= 0)
1488 if (namehashtab
[idx
].hashval
== hval
1489 && (strcmp (locname
,
1490 (char *) ah
.addr
+ namehashtab
[idx
].name_offset
)
1493 /* Found the entry. Now mark it as removed by zero-ing
1494 the reference to the locale record. */
1495 namehashtab
[idx
].locrec_offset
= 0;
1500 if (idx
>= head
->namehash_size
)
1501 idx
-= head
->namehash_size
;
1504 if (namehashtab
[idx
].name_offset
== 0 && ! be_quiet
)
1505 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1508 close_archive (&ah
);
1517 uint32_t locrec_offset
;
1523 const unsigned char *sum
;
1524 uint32_t file_offset
;
1530 nameentcmp (const void *a
, const void *b
)
1532 return strcmp (((const struct nameent
*) a
)->name
,
1533 ((const struct nameent
*) b
)->name
);
1538 dataentcmp (const void *a
, const void *b
)
1540 if (((const struct dataent
*) a
)->file_offset
1541 < ((const struct dataent
*) b
)->file_offset
)
1544 if (((const struct dataent
*) a
)->file_offset
1545 > ((const struct dataent
*) b
)->file_offset
)
1553 show_archive_content (int verbose
)
1555 struct locarhandle ah
;
1556 struct locarhead
*head
;
1557 struct namehashent
*namehashtab
;
1558 struct nameent
*names
;
1561 /* Open the archive. This call never returns if we cannot
1562 successfully open the archive. */
1563 open_archive (&ah
, true);
1567 names
= (struct nameent
*) xmalloc (head
->namehash_used
1568 * sizeof (struct nameent
));
1570 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1571 + head
->namehash_offset
);
1572 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
1573 if (namehashtab
[cnt
].locrec_offset
!= 0)
1575 assert (used
< head
->namehash_used
);
1576 names
[used
].name
= ah
.addr
+ namehashtab
[cnt
].name_offset
;
1577 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
1580 /* Sort the names. */
1581 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1585 struct dataent
*files
;
1586 struct sumhashent
*sumhashtab
;
1589 files
= (struct dataent
*) xmalloc (head
->sumhash_used
1590 * sizeof (struct dataent
));
1592 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1593 + head
->sumhash_offset
);
1594 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
1595 if (sumhashtab
[cnt
].file_offset
!= 0)
1597 assert (sumused
< head
->sumhash_used
);
1598 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1599 files
[sumused
].file_offset
= sumhashtab
[cnt
].file_offset
;
1600 files
[sumused
++].nlink
= 0;
1603 /* Sort by file locations. */
1604 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1606 /* Compute nlink fields. */
1607 for (cnt
= 0; cnt
< used
; ++cnt
)
1609 struct locrecent
*locrec
;
1612 locrec
= (struct locrecent
*) ((char *) ah
.addr
1613 + names
[cnt
].locrec_offset
);
1614 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1615 if (locrec
->record
[LC_ALL
].offset
!= 0
1617 || (locrec
->record
[idx
].offset
1618 < locrec
->record
[LC_ALL
].offset
)
1619 || (locrec
->record
[idx
].offset
+ locrec
->record
[idx
].len
1620 > (locrec
->record
[LC_ALL
].offset
1621 + locrec
->record
[LC_ALL
].len
)))
1624 struct dataent
*data
, dataent
;
1626 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1627 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1628 sizeof (struct dataent
),
1630 assert (data
!= NULL
);
1636 for (cnt
= 0; cnt
< used
; ++cnt
)
1638 struct locrecent
*locrec
;
1641 locrec
= (struct locrecent
*) ((char *) ah
.addr
1642 + names
[cnt
].locrec_offset
);
1643 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1646 struct dataent
*data
, dataent
;
1648 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1649 if (locrec
->record
[LC_ALL
].offset
!= 0
1650 && dataent
.file_offset
>= locrec
->record
[LC_ALL
].offset
1651 && (dataent
.file_offset
+ locrec
->record
[idx
].len
1652 <= (locrec
->record
[LC_ALL
].offset
1653 + locrec
->record
[LC_ALL
].len
)))
1654 dataent
.file_offset
= locrec
->record
[LC_ALL
].offset
;
1656 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1657 sizeof (struct dataent
),
1659 printf ("%6d %7x %3d%c ",
1660 locrec
->record
[idx
].len
, locrec
->record
[idx
].offset
,
1662 dataent
.file_offset
== locrec
->record
[LC_ALL
].offset
1664 for (i
= 0; i
< 16; i
+= 4)
1665 printf ("%02x%02x%02x%02x",
1666 data
->sum
[i
], data
->sum
[i
+ 1],
1667 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1668 printf (" %s/%s\n", names
[cnt
].name
,
1669 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1675 for (cnt
= 0; cnt
< used
; ++cnt
)
1676 puts (names
[cnt
].name
);
1678 close_archive (&ah
);
1680 exit (EXIT_SUCCESS
);