]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locarchive.c
* locale/locarchive.h (struct locale_category_data): Give this name to
[thirdparty/glibc.git] / locale / programs / locarchive.c
CommitLineData
a7b65cdc
UD
1/* Copyright (C) 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library 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 GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <assert.h>
25#include <dirent.h>
26#include <errno.h>
27#include <error.h>
28#include <fcntl.h>
29#include <inttypes.h>
30#include <libintl.h>
31#include <locale.h>
32#include <stdbool.h>
33#include <stdio.h>
cb09a2cd 34#include <stdio_ext.h>
a7b65cdc
UD
35#include <stdlib.h>
36#include <string.h>
37#include <time.h>
38#include <unistd.h>
39#include <sys/mman.h>
40#include <sys/param.h>
41#include <sys/stat.h>
42
43#include "../../crypt/md5.h"
44#include "../localeinfo.h"
45#include "../locarchive.h"
46#include "simple-hash.h"
47#include "localedef.h"
48
531bafd8 49extern const char *output_prefix;
a7b65cdc 50
531bafd8 51#define ARCHIVE_NAME LOCALEDIR "/locale-archive"
a7b65cdc
UD
52
53static const char *locnames[] =
54 {
55#define DEFINE_CATEGORY(category, category_name, items, a) \
56 [category] = category_name,
57#include "categories.def"
58#undef DEFINE_CATEGORY
59 };
60
61
62/* Size of the initial archive header. */
cb09a2cd 63#define INITIAL_NUM_NAMES 450
a7b65cdc
UD
64#define INITIAL_SIZE_STRINGS 3500
65#define INITIAL_NUM_LOCREC 350
66#define INITIAL_NUM_SUMS 2000
67
68
69static void
531bafd8 70create_archive (const char *archivefname, struct locarhandle *ah)
a7b65cdc
UD
71{
72 int fd;
531bafd8 73 char fname[strlen (archivefname) + sizeof (".XXXXXX")];
a7b65cdc
UD
74 struct locarhead head;
75 void *p;
76 size_t total;
77
531bafd8
UD
78 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
79
a7b65cdc
UD
80 /* Create a temporary file in the correct directory. */
81 fd = mkstemp (fname);
82 if (fd == -1)
83 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
84
85 /* Create the initial content of the archive. */
86 head.magic = AR_MAGIC;
87 head.namehash_offset = sizeof (struct locarhead);
88 head.namehash_used = 0;
cb09a2cd 89 head.namehash_size = next_prime (INITIAL_NUM_NAMES);
a7b65cdc
UD
90
91 head.string_offset = (head.namehash_offset
92 + head.namehash_size * sizeof (struct namehashent));
93 head.string_used = 0;
94 head.string_size = INITIAL_SIZE_STRINGS;
95
96 head.locrectab_offset = head.string_offset + head.string_size;
97 head.locrectab_used = 0;
98 head.locrectab_size = INITIAL_NUM_LOCREC;
99
100 head.sumhash_offset = (head.locrectab_offset
101 + head.locrectab_size * sizeof (struct locrecent));
102 head.sumhash_used = 0;
103 head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
104
105 total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
106
107 /* Write out the header and create room for the other data structures. */
108 if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
109 {
110 int errval = errno;
111 unlink (fname);
112 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
113 }
114
115 if (ftruncate64 (fd, total) != 0)
116 {
117 int errval = errno;
118 unlink (fname);
119 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
120 }
121
122 /* Map the header and all the administration data structures. */
123 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
124 if (p == MAP_FAILED)
125 {
126 int errval = errno;
127 unlink (fname);
128 error (EXIT_FAILURE, errval, _("cannot map archive header"));
129 }
130
131 /* Now try to rename it. We don't use the rename function since
132 this would overwrite a file which has been created in
133 parallel. */
134 if (link (fname, archivefname) == -1)
135 {
136 int errval = errno;
137
138 /* We cannot use the just created file. */
139 close (fd);
140 unlink (fname);
141
142 if (errval == EEXIST)
143 {
144 /* There is already an archive. Must have been a localedef run
145 which happened in parallel. Simply open this file then. */
b2bffca2 146 open_archive (ah, false);
a7b65cdc
UD
147 return;
148 }
149
150 error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
151 }
152
153 /* Remove the temporary name. */
154 unlink (fname);
155
156 /* Make the file globally readable. */
157 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
158 {
159 int errval = errno;
160 unlink (archivefname);
161 error (EXIT_FAILURE, errval,
162 _("cannot change mode of new locale archive"));
163 }
164
165 ah->fd = fd;
166 ah->addr = p;
167 ah->len = total;
168}
169
ac8f8c53
RM
170
171struct oldlocrecent
172{
173 unsigned int cnt;
174 struct locrecent *locrec;
175};
176
177static int
178oldlocrecentcmp (const void *a, const void *b)
179{
180 struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
181 struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
182
183 if (la->record[LC_ALL].offset < lb->record[LC_ALL].offset)
184 return -1;
185 if (la->record[LC_ALL].offset > lb->record[LC_ALL].offset)
186 return 1;
187
188 if (la->record[LC_CTYPE].offset < lb->record[LC_CTYPE].offset)
189 return -1;
190 if (la->record[LC_CTYPE].offset > lb->record[LC_CTYPE].offset)
191 return 1;
192
193 if (la->record[LC_COLLATE].offset < lb->record[LC_COLLATE].offset)
194 return -1;
195 if (la->record[LC_COLLATE].offset > lb->record[LC_COLLATE].offset)
196 return 1;
197
198 if (((const struct oldlocrecent *) a)->cnt
199 < ((const struct oldlocrecent *) b)->cnt)
200 return -1;
201
202 if (((const struct oldlocrecent *) a)->cnt
203 > ((const struct oldlocrecent *) b)->cnt)
204 return 1;
205
206 return 0;
207}
208
209
cb09a2cd
RM
210/* forward decl for below */
211static uint32_t add_locale (struct locarhandle *ah, const char *name,
212 locale_data_t data, bool replace);
a7b65cdc
UD
213
214static void
215enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
216{
217 struct stat64 st;
218 int fd;
a7b65cdc
UD
219 struct locarhead newhead;
220 size_t total;
221 void *p;
ac8f8c53 222 unsigned int cnt, loccnt;
a7b65cdc
UD
223 struct namehashent *oldnamehashtab;
224 struct locrecent *oldlocrectab;
225 struct locarhandle new_ah;
531bafd8
UD
226 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
227 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
228 char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
ac8f8c53 229 struct oldlocrecent *oldlocrecarray;
531bafd8
UD
230
231 if (output_prefix)
232 memcpy (archivefname, output_prefix, prefix_len);
233 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
234 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
a7b65cdc
UD
235
236 /* Not all of the old file has to be mapped. Change this now this
237 we will have to access the whole content. */
238 if (fstat64 (ah->fd, &st) != 0
239 || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
240 MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
241 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
242 ah->len = st.st_size;
243
244 /* Create a temporary file in the correct directory. */
245 fd = mkstemp (fname);
246 if (fd == -1)
247 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
248
249 /* Copy the existing head information. */
250 newhead = *head;
251
252 /* Create the new archive header. The sizes of the various tables
253 should be double from what is currently used. */
254 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
255 newhead.namehash_size);
256 printf ("name: size: %u, used: %d, new: size: %u\n",
257 head->namehash_size, head->namehash_used, newhead.namehash_size);
258
259 newhead.string_offset = (newhead.namehash_offset
260 + (newhead.namehash_size
261 * sizeof (struct namehashent)));
262 newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
263
264 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
265 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
266 newhead.locrectab_size);
267
268 newhead.sumhash_offset = (newhead.locrectab_offset
269 + (newhead.locrectab_size
270 * sizeof (struct locrecent)));
271 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
272 newhead.sumhash_size);
273
274 total = (newhead.sumhash_offset
275 + newhead.sumhash_size * sizeof (struct sumhashent));
276
277 /* The new file is empty now. */
278 newhead.namehash_used = 0;
279 newhead.string_used = 0;
280 newhead.locrectab_used = 0;
281 newhead.sumhash_used = 0;
282
283 /* Write out the header and create room for the other data structures. */
284 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
285 != sizeof (newhead))
286 {
287 int errval = errno;
288 unlink (fname);
289 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
290 }
291
292 if (ftruncate64 (fd, total) != 0)
293 {
294 int errval = errno;
295 unlink (fname);
296 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
297 }
298
299 /* Map the header and all the administration data structures. */
300 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
301 if (p == MAP_FAILED)
302 {
303 int errval = errno;
304 unlink (fname);
305 error (EXIT_FAILURE, errval, _("cannot map archive header"));
306 }
307
308 /* Lock the new file. */
309 if (lockf64 (fd, F_LOCK, total) != 0)
310 {
311 int errval = errno;
312 unlink (fname);
313 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
314 }
315
316 new_ah.len = total;
317 new_ah.addr = p;
318 new_ah.fd = fd;
319
320 /* Walk through the hash name hash table to find out what data is
321 still referenced and transfer it into the new file. */
322 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
323 + head->namehash_offset);
324 oldlocrectab = (struct locrecent *) ((char *) ah->addr
325 + head->locrectab_offset);
ac8f8c53
RM
326 oldlocrecarray = (struct oldlocrecent *)
327 alloca (head->namehash_size
328 * sizeof (struct oldlocrecent));
329
330 for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
a7b65cdc
UD
331 if (oldnamehashtab[cnt].locrec_offset != 0)
332 {
ac8f8c53
RM
333 oldlocrecarray[loccnt].cnt = cnt;
334 oldlocrecarray[loccnt++].locrec
335 = (struct locrecent *) ((char *) ah->addr
336 + oldnamehashtab[cnt].locrec_offset);
337 }
a7b65cdc 338
ac8f8c53
RM
339 qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
340 oldlocrecentcmp);
a7b65cdc 341
ac8f8c53
RM
342 for (cnt = 0; cnt < loccnt; ++cnt)
343 {
344 /* Insert this entry in the new hash table. */
345 locale_data_t old_data;
346 unsigned int idx;
347 struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
a7b65cdc 348
ac8f8c53
RM
349 for (idx = 0; idx < __LC_LAST; ++idx)
350 if (idx != LC_ALL)
351 {
352 old_data[idx].size = oldlocrec->record[idx].len;
353 old_data[idx].addr
354 = ((char *) ah->addr + oldlocrec->record[idx].offset);
a7b65cdc 355
ac8f8c53
RM
356 __md5_buffer (old_data[idx].addr, old_data[idx].size,
357 old_data[idx].sum);
358 }
a7b65cdc 359
ac8f8c53
RM
360 if (add_locale (&new_ah,
361 ((char *) ah->addr
362 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
363 old_data, 0) == 0)
364 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
365 }
a7b65cdc
UD
366
367 /* Make the file globally readable. */
368 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
369 {
370 int errval = errno;
371 unlink (fname);
372 error (EXIT_FAILURE, errval,
373 _("cannot change mode of resized locale archive"));
374 }
375
376 /* Rename the new file. */
377 if (rename (fname, archivefname) != 0)
378 {
379 int errval = errno;
380 unlink (fname);
381 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
382 }
383
384 /* Close the old file. */
385 close_archive (ah);
386
387 /* Add the information for the new one. */
388 *ah = new_ah;
389}
390
391
392void
b2bffca2 393open_archive (struct locarhandle *ah, bool readonly)
a7b65cdc
UD
394{
395 struct stat64 st;
396 struct stat64 st2;
397 int fd;
398 struct locarhead head;
399 int retry = 0;
531bafd8
UD
400 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
401 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
402
403 if (output_prefix)
404 memcpy (archivefname, output_prefix, prefix_len);
405 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
a7b65cdc
UD
406
407 again:
408 /* Open the archive. We must have exclusive write access. */
b2bffca2 409 fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
a7b65cdc
UD
410 if (fd == -1)
411 {
412 /* Maybe the file does not yet exist. */
413 if (errno == ENOENT)
414 {
b2bffca2
UD
415 if (readonly)
416 {
417 static const struct locarhead nullhead =
418 {
419 .namehash_used = 0,
420 .namehash_offset = 0,
421 .namehash_size = 0
422 };
423
424 ah->addr = (void *) &nullhead;
425 ah->fd = -1;
426 }
427 else
428 create_archive (archivefname, ah);
429
a7b65cdc
UD
430 return;
431 }
432 else
433 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
434 archivefname);
435 }
436
437 if (fstat64 (fd, &st) < 0)
438 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
439 archivefname);
440
b2bffca2 441 if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
a7b65cdc
UD
442 {
443 close (fd);
444
445 if (retry++ < max_locarchive_open_retry)
446 {
447 struct timespec req;
448
449 /* Wait for a bit. */
450 req.tv_sec = 0;
451 req.tv_nsec = 1000000 * (random () % 500 + 1);
452 (void) nanosleep (&req, NULL);
453
454 goto again;
455 }
456
457 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
458 archivefname);
459 }
460
461 /* One more check. Maybe another process replaced the archive file
462 with a new, larger one since we opened the file. */
463 if (stat64 (archivefname, &st2) == -1
464 || st.st_dev != st2.st_dev
465 || st.st_ino != st2.st_ino)
466 {
b2bffca2 467 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
a7b65cdc
UD
468 close (fd);
469 goto again;
470 }
471
472 /* Read the header. */
473 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
b2bffca2
UD
474 {
475 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
476 error (EXIT_FAILURE, errno, _("cannot read archive header"));
477 }
a7b65cdc
UD
478
479 ah->fd = fd;
480 ah->len = (head.sumhash_offset
481 + head.sumhash_size * sizeof (struct sumhashent));
482
483 /* Now we know how large the administrative information part is.
484 Map all of it. */
b2bffca2
UD
485 ah->addr = mmap64 (NULL, ah->len, PROT_READ | (readonly ? 0 : PROT_WRITE),
486 MAP_SHARED, fd, 0);
a7b65cdc 487 if (ah->addr == MAP_FAILED)
b2bffca2
UD
488 {
489 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
490 error (EXIT_FAILURE, errno, _("cannot map archive header"));
491 }
a7b65cdc
UD
492}
493
494
495void
496close_archive (struct locarhandle *ah)
497{
b2bffca2
UD
498 if (ah->fd != -1)
499 {
500 munmap (ah->addr, ah->len);
501 close (ah->fd);
502 }
a7b65cdc
UD
503}
504
cb09a2cd
RM
505#include "../../intl/explodename.c"
506#include "../../intl/l10nflist.c"
507
508static struct namehashent *
509insert_name (struct locarhandle *ah,
510 const char *name, size_t name_len, bool replace)
511{
512 const struct locarhead *const head = ah->addr;
513 struct namehashent *namehashtab
514 = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
515 unsigned int insert_idx, idx, incr;
516
517 /* Hash value of the locale name. */
518 uint32_t hval = compute_hashval (name, name_len);
519
520 insert_idx = -1;
521 idx = hval % head->namehash_size;
522 incr = 1 + hval % (head->namehash_size - 2);
523
524 /* If the name_offset field is zero this means this is a
525 deleted entry and therefore no entry can be found. */
526 while (namehashtab[idx].name_offset != 0)
527 {
528 if (namehashtab[idx].hashval == hval
529 && strcmp (name,
530 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
531 {
532 /* Found the entry. */
533 if (namehashtab[idx].locrec_offset != 0 && ! replace)
534 {
535 if (! be_quiet)
536 error (0, 0, _("locale '%s' already exists"), name);
537 return NULL;
538 }
539
540 break;
541 }
542
ac8f8c53
RM
543 if (namehashtab[idx].hashval == hval)
544 {
545 error (0, 0, "hash collision (%u) %s, %s",
546 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
547 }
548
549
cb09a2cd
RM
550 /* Remember the first place we can insert the new entry. */
551 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
552 insert_idx = idx;
553
554 idx += incr;
555 if (idx >= head->namehash_size)
556 idx -= head->namehash_size;
557 }
558
559 /* Add as early as possible. */
560 if (insert_idx != -1)
561 idx = insert_idx;
562
563 namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
564 return &namehashtab[idx];
565}
566
567static void
568add_alias (struct locarhandle *ah, const char *alias, bool replace,
569 const char *oldname, uint32_t locrec_offset)
570{
571 struct locarhead *head = ah->addr;
572 const size_t name_len = strlen (alias);
573 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
574 replace);
575 if (namehashent == NULL && ! replace)
576 return;
577
578 if (namehashent->name_offset == 0)
579 {
580 /* We are adding a new hash entry for this alias.
581 Determine whether we have to resize the file. */
582 if (head->string_used + name_len + 1 > head->string_size
583 || 100 * head->namehash_used > 75 * head->namehash_size)
584 {
585 /* The current archive is not large enough. */
586 enlarge_archive (ah, head);
587
588 /* The locrecent might have moved, so we have to look up
589 the old name afresh. */
590 namehashent = insert_name (ah, oldname, strlen (oldname), true);
591 assert (namehashent->name_offset != 0);
592 assert (namehashent->locrec_offset != 0);
593 locrec_offset = namehashent->locrec_offset;
594
595 /* Tail call to try the whole thing again. */
596 add_alias (ah, alias, replace, oldname, locrec_offset);
597 return;
598 }
599
600 /* Add the name string. */
601 memcpy (ah->addr + head->string_offset + head->string_used,
602 alias, name_len + 1);
603 namehashent->name_offset = head->string_offset + head->string_used;
604 head->string_used += name_len + 1;
605
606 ++head->namehash_used;
607 }
608
609 if (namehashent->locrec_offset != 0)
610 {
611 /* Replacing an existing entry.
612 Mark that we are no longer using the old locrecent. */
613 struct locrecent *locrecent
614 = (struct locrecent *) ((char *) ah->addr
615 + namehashent->locrec_offset);
616 --locrecent->refs;
617 }
618
619 /* Point this entry at the locrecent installed for the main name. */
620 namehashent->locrec_offset = locrec_offset;
621}
622
ac8f8c53
RM
623static int /* qsort comparator used below */
624cmpcategorysize (const void *a, const void *b)
625{
626 if (*(const void **) a == NULL)
627 return 1;
628 if (*(const void **) b == NULL)
629 return -1;
630 return ((*(const struct locale_category_data **) a)->size
631 - (*(const struct locale_category_data **) b)->size);
632}
a7b65cdc
UD
633
634/* Check the content of the archive for duplicates. Add the content
cb09a2cd
RM
635 of the files if necessary. Returns the locrec_offset. */
636static uint32_t
637add_locale (struct locarhandle *ah,
638 const char *name, locale_data_t data, bool replace)
a7b65cdc
UD
639{
640 /* First look for the name. If it already exists and we are not
641 supposed to replace it don't do anything. If it does not exist
642 we have to allocate a new locale record. */
643 size_t name_len = strlen (name);
644 uint32_t file_offsets[__LC_LAST];
645 unsigned int num_new_offsets = 0;
646 struct sumhashent *sumhashtab;
647 uint32_t hval;
ac8f8c53 648 unsigned int cnt, idx;
a7b65cdc 649 struct locarhead *head;
a7b65cdc
UD
650 struct namehashent *namehashent;
651 unsigned int incr;
652 struct locrecent *locrecent;
ac8f8c53
RM
653 off64_t lastoffset;
654 char *ptr;
655 struct locale_category_data *size_order[__LC_LAST];
656 const size_t pagesz = getpagesize ();
657 int small_mask;
a7b65cdc
UD
658
659 head = ah->addr;
660 sumhashtab = (struct sumhashent *) ((char *) ah->addr
661 + head->sumhash_offset);
a7b65cdc 662
ac8f8c53
RM
663 memset (file_offsets, 0, sizeof (file_offsets));
664
665 size_order[LC_ALL] = NULL;
666 for (cnt = 0; cnt < __LC_LAST; ++cnt)
667 if (cnt != LC_ALL)
668 size_order[cnt] = &data[cnt];
669
670 /* Sort the array in ascending order of data size. */
671 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
672
673 small_mask = 0;
674 data[LC_ALL].size = 0;
675 for (cnt = 0; cnt < __LC_LAST; ++cnt)
676 if (size_order[cnt] != NULL)
677 {
678 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
679 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
680 {
681 /* This category makes the small-categories block
682 stop being small, so this is the end of the road. */
683 do
684 size_order[cnt++] = NULL;
685 while (cnt < __LC_LAST);
686 break;
687 }
688 data[LC_ALL].size += rounded_size;
689 small_mask |= 1 << (size_order[cnt] - data);
690 }
691
692 /* Copy the data for all the small categories into the LC_ALL
693 pseudo-category. */
694
695 data[LC_ALL].addr = alloca (data[LC_ALL].size);
696 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
697
698 ptr = data[LC_ALL].addr;
699 for (cnt = 0; cnt < __LC_LAST; ++cnt)
700 if (small_mask & (1 << cnt))
701 {
702 memcpy (ptr, data[cnt].addr, data[cnt].size);
703 ptr += (data[cnt].size + 15) & -16;
704 }
705 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
a7b65cdc
UD
706
707 /* For each locale category data set determine whether the same data
708 is already somewhere in the archive. */
709 for (cnt = 0; cnt < __LC_LAST; ++cnt)
ac8f8c53 710 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
a7b65cdc 711 {
a7b65cdc
UD
712 ++num_new_offsets;
713
714 /* Compute the hash value of the checksum to determine a
715 starting point for the search in the MD5 hash value
716 table. */
717 hval = compute_hashval (data[cnt].sum, 16);
718
719 idx = hval % head->sumhash_size;
720 incr = 1 + hval % (head->sumhash_size - 2);
721
722 while (sumhashtab[idx].file_offset != 0)
723 {
724 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
725 {
726 /* Found it. */
727 file_offsets[cnt] = sumhashtab[idx].file_offset;
728 --num_new_offsets;
729 break;
730 }
731
732 idx += incr;
733 if (idx >= head->sumhash_size)
734 idx -= head->sumhash_size;
735 }
736 }
737
cb09a2cd
RM
738 /* Find a slot for the locale name in the hash table. */
739 namehashent = insert_name (ah, name, name_len, replace);
740 if (namehashent == NULL) /* Already exists and !REPLACE. */
741 return 0;
a7b65cdc
UD
742
743 /* Determine whether we have to resize the file. */
744 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
745 || (namehashent->locrec_offset == 0
746 && (head->locrectab_used == head->locrectab_size
747 || head->string_used + name_len + 1 > head->string_size
748 || 100 * head->namehash_used > 75 * head->namehash_size)))
749 {
750 /* The current archive is not large enough. */
751 enlarge_archive (ah, head);
cb09a2cd 752 return add_locale (ah, name, data, replace);
a7b65cdc
UD
753 }
754
755 /* Add the locale data which is not yet in the archive. */
ac8f8c53
RM
756 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
757 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
758 && file_offsets[cnt] == 0)
a7b65cdc
UD
759 {
760 /* The data for this section is not yet available in the
761 archive. Append it. */
762 off64_t lastpos;
763 uint32_t md5hval;
764
765 lastpos = lseek64 (ah->fd, 0, SEEK_END);
766 if (lastpos == (off64_t) -1)
767 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
768
ac8f8c53
RM
769 /* If block of small categories would cross page boundary,
770 align it unless it immediately follows a large category. */
771 if (cnt == LC_ALL && lastoffset != lastpos
772 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
773 & -pagesz)
774 > ((data[cnt].size + pagesz - 1) & -pagesz)))
775 {
776 size_t sz = pagesz - (lastpos & (pagesz - 1));
777 char *zeros = alloca (sz);
778
779 memset (zeros, 0, sz);
780 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
781 error (EXIT_FAILURE, errno,
782 _("cannot add to locale archive"));
783
784 lastpos += sz;
785 }
786
a7b65cdc
UD
787 /* Align all data to a 16 byte boundary. */
788 if ((lastpos & 15) != 0)
789 {
790 static const char zeros[15] = { 0, };
791
792 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
793 != 16 - (lastpos & 15))
794 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
795
796 lastpos += 16 - (lastpos & 15);
797 }
798
799 /* Remember the position. */
800 file_offsets[cnt] = lastpos;
ac8f8c53 801 lastoffset = lastpos + data[cnt].size;
a7b65cdc
UD
802
803 /* Write the data. */
804 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
805 != data[cnt].size)
806 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
807
808 /* Add the hash value to the hash table. */
809 md5hval = compute_hashval (data[cnt].sum, 16);
810
811 idx = md5hval % head->sumhash_size;
812 incr = 1 + md5hval % (head->sumhash_size - 2);
813
814 while (sumhashtab[idx].file_offset != 0)
815 {
816 idx += incr;
817 if (idx >= head->sumhash_size)
818 idx -= head->sumhash_size;
819 }
820
821 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
822 sumhashtab[idx].file_offset = file_offsets[cnt];
823
824 ++head->sumhash_used;
825 }
826
ac8f8c53
RM
827 lastoffset = file_offsets[LC_ALL];
828 for (cnt = 0; cnt < __LC_LAST; ++cnt)
829 if (small_mask & (1 << cnt))
830 {
831 file_offsets[cnt] = lastoffset;
832 lastoffset += (data[cnt].size + 15) & -16;
833 }
834
cb09a2cd 835 if (namehashent->name_offset == 0)
a7b65cdc
UD
836 {
837 /* Add the name string. */
838 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
839 name, name_len + 1);
840 namehashent->name_offset = head->string_offset + head->string_used;
841 head->string_used += name_len + 1;
cb09a2cd
RM
842 ++head->namehash_used;
843 }
a7b65cdc 844
cb09a2cd
RM
845 if (namehashent->locrec_offset == 0)
846 {
a7b65cdc
UD
847 /* Allocate a name location record. */
848 namehashent->locrec_offset = (head->locrectab_offset
849 + (head->locrectab_used++
850 * sizeof (struct locrecent)));
cb09a2cd
RM
851 locrecent = (struct locrecent *) ((char *) ah->addr
852 + namehashent->locrec_offset);
853 locrecent->refs = 1;
a7b65cdc 854 }
cb09a2cd
RM
855 else
856 {
857 /* If there are other aliases pointing to this locrecent,
858 we still need a new one. If not, reuse the old one. */
a7b65cdc 859
cb09a2cd
RM
860 locrecent = (struct locrecent *) ((char *) ah->addr
861 + namehashent->locrec_offset);
862 if (locrecent->refs > 1)
863 {
864 --locrecent->refs;
865 namehashent->locrec_offset = (head->locrectab_offset
866 + (head->locrectab_used++
867 * sizeof (struct locrecent)));
868 locrecent = (struct locrecent *) ((char *) ah->addr
869 + namehashent->locrec_offset);
870 locrecent->refs = 1;
871 }
872 }
a7b65cdc
UD
873
874 /* Fill in the table with the locations of the locale data. */
a7b65cdc 875 for (cnt = 0; cnt < __LC_LAST; ++cnt)
ac8f8c53
RM
876 {
877 locrecent->record[cnt].offset = file_offsets[cnt];
878 locrecent->record[cnt].len = data[cnt].size;
879 }
a7b65cdc 880
cb09a2cd
RM
881 return namehashent->locrec_offset;
882}
883
884
885/* Check the content of the archive for duplicates. Add the content
886 of the files if necessary. Add all the names, possibly overwriting
887 old files. */
888int
889add_locale_to_archive (ah, name, data, replace)
890 struct locarhandle *ah;
891 const char *name;
892 locale_data_t data;
893 bool replace;
894{
895 char *normalized_name = NULL;
896 uint32_t locrec_offset;
897
898 /* First analyze the name to decide how to archive it. */
899 const char *language;
900 const char *modifier;
901 const char *territory;
902 const char *codeset;
903 const char *normalized_codeset;
904 int mask = _nl_explode_name (strdupa (name),
905 &language, &modifier, &territory,
906 &codeset, &normalized_codeset);
907
908 if (mask & XPG_NORM_CODESET)
909 /* This name contains a codeset in unnormalized form.
910 We will store it in the archive with a normalized name. */
911 asprintf (&normalized_name, "%s%s%s.%s%s%s",
912 language, territory == NULL ? "" : "_", territory ?: "",
913 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
914 modifier == NULL ? "" : "@", modifier ?: "");
915
916 /* This call does the main work. */
917 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
918 free (normalized_name);
919 if (locrec_offset == 0)
920 {
921 if (mask & XPG_NORM_CODESET)
922 free ((char *) normalized_codeset);
923 return -1;
924 }
925
926 if ((mask & XPG_CODESET) == 0)
927 {
928 /* This name lacks a codeset, so determine the locale's codeset and
929 add an alias for its name with normalized codeset appended. */
930
931 const struct
932 {
933 unsigned int magic;
934 unsigned int nstrings;
935 unsigned int strindex[0];
936 } *filedata = data[LC_CTYPE].addr;
937 codeset = (char *) filedata
938 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
939
940 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
941 mask |= XPG_NORM_CODESET;
942
943 asprintf (&normalized_name, "%s%s%s.%s%s%s",
944 language, territory == NULL ? "" : "_", territory ?: "",
945 normalized_codeset,
946 modifier == NULL ? "" : "@", modifier ?: "");
947
948 add_alias (ah, normalized_name, replace, name, locrec_offset);
949 free (normalized_name);
950 }
951
952 /* Now read the locale.alias files looking for lines whose
953 right hand side matches our name after normalization. */
954 if (alias_file != NULL)
955 {
956 FILE *fp;
957 fp = fopen (alias_file, "r");
958 if (fp == NULL)
959 error (1, errno, _("locale alias file `%s' not found"),
960 alias_file);
961
962 /* No threads present. */
963 __fsetlocking (fp, FSETLOCKING_BYCALLER);
964
965 while (! feof_unlocked (fp))
966 {
967 /* It is a reasonable approach to use a fix buffer here
968 because
969 a) we are only interested in the first two fields
970 b) these fields must be usable as file names and so must
971 not be that long */
972 char buf[BUFSIZ];
973 char *alias;
974 char *value;
975 char *cp;
976
977 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
978 /* EOF reached. */
979 break;
980
981 cp = buf;
982 /* Ignore leading white space. */
983 while (isspace (cp[0]) && cp[0] != '\n')
984 ++cp;
985
986 /* A leading '#' signals a comment line. */
987 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
988 {
989 alias = cp++;
990 while (cp[0] != '\0' && !isspace (cp[0]))
991 ++cp;
992 /* Terminate alias name. */
993 if (cp[0] != '\0')
994 *cp++ = '\0';
995
996 /* Now look for the beginning of the value. */
997 while (isspace (cp[0]))
998 ++cp;
999
1000 if (cp[0] != '\0')
1001 {
1002 value = cp++;
1003 while (cp[0] != '\0' && !isspace (cp[0]))
1004 ++cp;
1005 /* Terminate value. */
1006 if (cp[0] == '\n')
1007 {
1008 /* This has to be done to make the following
1009 test for the end of line possible. We are
1010 looking for the terminating '\n' which do not
1011 overwrite here. */
1012 *cp++ = '\0';
1013 *cp = '\n';
1014 }
1015 else if (cp[0] != '\0')
1016 *cp++ = '\0';
1017
1018 /* Does this alias refer to our locale? We will
1019 normalize the right hand side and compare the
1020 elements of the normalized form. */
1021 {
1022 const char *rhs_language;
1023 const char *rhs_modifier;
1024 const char *rhs_territory;
1025 const char *rhs_codeset;
1026 const char *rhs_normalized_codeset;
1027 int rhs_mask = _nl_explode_name (value,
1028 &rhs_language,
1029 &rhs_modifier,
1030 &rhs_territory,
1031 &rhs_codeset,
1032 &rhs_normalized_codeset);
1033 if (!strcmp (language, rhs_language)
1034 && ((rhs_mask & XPG_CODESET)
1035 /* He has a codeset, it must match normalized. */
1036 ? !strcmp ((mask & XPG_NORM_CODESET)
1037 ? normalized_codeset : codeset,
1038 (rhs_mask & XPG_NORM_CODESET)
1039 ? rhs_normalized_codeset : rhs_codeset)
1040 /* He has no codeset, we must also have none. */
1041 : (mask & XPG_CODESET) == 0)
1042 /* Codeset (or lack thereof) matches. */
1043 && !strcmp (territory ?: "", rhs_territory ?: "")
1044 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1045 /* We have a winner. */
1046 add_alias (ah, alias, replace,
1047 normalized_name ?: name, locrec_offset);
1048 if (rhs_mask & XPG_NORM_CODESET)
1049 free ((char *) rhs_normalized_codeset);
1050 }
1051 }
1052 }
1053
1054 /* Possibly not the whole line fits into the buffer.
1055 Ignore the rest of the line. */
1056 while (strchr (cp, '\n') == NULL)
1057 {
1058 cp = buf;
1059 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1060 /* Make sure the inner loop will be left. The outer
1061 loop will exit at the `feof' test. */
1062 *cp = '\n';
1063 }
1064 }
a7b65cdc 1065
cb09a2cd
RM
1066 fclose (fp);
1067 }
a7b65cdc 1068
cb09a2cd
RM
1069 if (mask & XPG_NORM_CODESET)
1070 free ((char *) normalized_codeset);
a7b65cdc
UD
1071
1072 return 0;
1073}
1074
1075
1076int
1077add_locales_to_archive (nlist, list, replace)
1078 size_t nlist;
1079 char *list[];
1080 bool replace;
1081{
1082 struct locarhandle ah;
1083 int result = 0;
1084
1085 /* Open the archive. This call never returns if we cannot
1086 successfully open the archive. */
b2bffca2 1087 open_archive (&ah, false);
a7b65cdc
UD
1088
1089 while (nlist-- > 0)
1090 {
1091 const char *fname = *list++;
1092 size_t fnamelen = strlen (fname);
1093 struct stat64 st;
1094 DIR *dirp;
1095 struct dirent64 *d;
1096 int seen;
1097 locale_data_t data;
1098 int cnt;
1099
1100 if (! be_quiet)
1101 printf (_("Adding %s\n"), fname);
1102
1103 /* First see whether this really is a directory and whether it
1104 contains all the require locale category files. */
1105 if (stat64 (fname, &st) < 0)
1106 {
1107 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1108 strerror (errno));
1109 continue;
1110 }
1111 if (!S_ISDIR (st.st_mode))
1112 {
1113 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1114 continue;
1115 }
1116
1117 dirp = opendir (fname);
1118 if (dirp == NULL)
1119 {
1120 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1121 fname, strerror (errno));
1122 continue;
1123 }
1124
1125 seen = 0;
1126 while ((d = readdir64 (dirp)) != NULL)
1127 {
1128 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1129 if (cnt != LC_ALL)
1130 if (strcmp (d->d_name, locnames[cnt]) == 0)
1131 {
1132 unsigned char d_type;
1133
1134 /* We have an object of the required name. If it's
1135 a directory we have to look at a file with the
1136 prefix "SYS_". Otherwise we have found what we
1137 are looking for. */
1138#ifdef _DIRENT_HAVE_D_TYPE
1139 d_type = d->d_type;
1140
1141 if (d_type != DT_REG)
1142#endif
1143 {
1144 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1145
1146#ifdef _DIRENT_HAVE_D_TYPE
1147 if (d_type == DT_UNKNOWN)
1148#endif
1149 {
1150 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1151 d->d_name);
1152
1153 if (stat64 (fullname, &st) == -1)
1154 /* We cannot stat the file, ignore it. */
1155 break;
1156
1157 d_type = IFTODT (st.st_mode);
1158 }
1159
1160 if (d_type == DT_DIR)
1161 {
1162 /* We have to do more tests. The file is a
1163 directory and it therefore must contain a
1164 regular file with the same name except a
1165 "SYS_" prefix. */
531bafd8
UD
1166 char *t = stpcpy (stpcpy (fullname, fname), "/");
1167 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
a7b65cdc
UD
1168 d->d_name);
1169
1170 if (stat64 (fullname, &st) == -1)
1171 /* There is no SYS_* file or we cannot
1172 access it. */
1173 break;
1174
1175 d_type = IFTODT (st.st_mode);
1176 }
1177 }
1178
1179 /* If we found a regular file (eventually after
1180 following a symlink) we are successful. */
1181 if (d_type == DT_REG)
1182 ++seen;
1183 break;
1184 }
1185 }
1186
1187 closedir (dirp);
1188
1189 if (seen != __LC_LAST - 1)
1190 {
1191 /* We don't have all locale category files. Ignore the name. */
1192 error (0, 0, _("incomplete set of locale files in \"%s\""),
1193 fname);
1194 continue;
1195 }
1196
1197 /* Add the files to the archive. To do this we first compute
1198 sizes and the MD5 sums of all the files. */
1199 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1200 if (cnt != LC_ALL)
1201 {
1202 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1203 int fd;
1204
1205 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1206 fd = open64 (fullname, O_RDONLY);
1207 if (fd == -1 || fstat64 (fd, &st) == -1)
1208 {
1209 /* Cannot read the file. */
1210 if (fd != -1)
1211 close (fd);
1212 break;
1213 }
1214
1215 if (S_ISDIR (st.st_mode))
1216 {
531bafd8 1217 char *t;
a7b65cdc 1218 close (fd);
531bafd8
UD
1219 t = stpcpy (stpcpy (fullname, fname), "/");
1220 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
a7b65cdc
UD
1221 locnames[cnt]);
1222
1223 fd = open64 (fullname, O_RDONLY);
1224 if (fd == -1 || fstat64 (fd, &st) == -1
1225 || !S_ISREG (st.st_mode))
1226 {
1227 if (fd != -1)
1228 close (fd);
1229 break;
1230 }
1231 }
1232
1233 /* Map the file. */
1234 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1235 fd, 0);
1236 if (data[cnt].addr == MAP_FAILED)
1237 {
1238 /* Cannot map it. */
1239 close (fd);
1240 break;
1241 }
1242
1243 data[cnt].size = st.st_size;
1244 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1245
1246 /* We don't need the file descriptor anymore. */
1247 close (fd);
1248 }
1249
1250 if (cnt != __LC_LAST)
1251 {
1252 while (cnt-- > 0)
1253 if (cnt != LC_ALL)
1254 munmap (data[cnt].addr, data[cnt].size);
1255
1256 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1257
1258 continue;
1259 }
1260
1261 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1262
1263 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1264 if (cnt != LC_ALL)
1265 munmap (data[cnt].addr, data[cnt].size);
1266 }
1267
1268 /* We are done. */
1269 close_archive (&ah);
1270
1271 return result;
1272}
1273
1274
1275int
1276delete_locales_from_archive (nlist, list)
1277 size_t nlist;
1278 char *list[];
1279{
1280 struct locarhandle ah;
1281 struct locarhead *head;
1282 struct namehashent *namehashtab;
1283
1284 /* Open the archive. This call never returns if we cannot
1285 successfully open the archive. */
b2bffca2 1286 open_archive (&ah, false);
a7b65cdc
UD
1287
1288 head = ah.addr;
1289 namehashtab = (struct namehashent *) ((char *) ah.addr
1290 + head->namehash_offset);
1291
1292 while (nlist-- > 0)
1293 {
1294 const char *locname = *list++;
1295 uint32_t hval;
1296 unsigned int idx;
1297 unsigned int incr;
1298
1299 /* Search for this locale in the archive. */
1300 hval = compute_hashval (locname, strlen (locname));
1301
1302 idx = hval % head->namehash_size;
1303 incr = 1 + hval % (head->namehash_size - 2);
1304
1305 /* If the name_offset field is zero this means this is no
1306 deleted entry and therefore no entry can be found. */
1307 while (namehashtab[idx].name_offset != 0)
1308 {
1309 if (namehashtab[idx].hashval == hval
1310 && (strcmp (locname,
1311 (char *) ah.addr + namehashtab[idx].name_offset)
1312 == 0))
1313 {
1314 /* Found the entry. Now mark it as removed by zero-ing
1315 the reference to the locale record. */
1316 namehashtab[idx].locrec_offset = 0;
a7b65cdc
UD
1317 break;
1318 }
1319
1320 idx += incr;
1321 if (idx >= head->namehash_size)
1322 idx -= head->namehash_size;
1323 }
1324
1325 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1326 error (0, 0, _("locale \"%s\" not in archive"), locname);
1327 }
1328
1329 close_archive (&ah);
1330
1331 return 0;
1332}
1333
1334
b2bffca2
UD
1335struct nameent
1336{
1337 char *name;
1338 uint32_t locrec_offset;
1339};
1340
1341
1342struct dataent
1343{
1344 const unsigned char *sum;
1345 uint32_t file_offset;
1346 uint32_t nlink;
1347};
1348
1349
1350static int
1351nameentcmp (const void *a, const void *b)
1352{
1353 return strcmp (((const struct nameent *) a)->name,
1354 ((const struct nameent *) b)->name);
1355}
1356
1357
a7b65cdc 1358static int
b2bffca2 1359dataentcmp (const void *a, const void *b)
a7b65cdc 1360{
b2bffca2
UD
1361 if (((const struct dataent *) a)->file_offset
1362 < ((const struct dataent *) b)->file_offset)
1363 return -1;
1364
1365 if (((const struct dataent *) a)->file_offset
1366 > ((const struct dataent *) b)->file_offset)
1367 return 1;
1368
1369 return 0;
a7b65cdc
UD
1370}
1371
1372
1373void
b2bffca2 1374show_archive_content (int verbose)
a7b65cdc
UD
1375{
1376 struct locarhandle ah;
1377 struct locarhead *head;
1378 struct namehashent *namehashtab;
b2bffca2 1379 struct nameent *names;
a7b65cdc 1380 int cnt;
a7b65cdc
UD
1381 int used;
1382
1383 /* Open the archive. This call never returns if we cannot
1384 successfully open the archive. */
b2bffca2 1385 open_archive (&ah, true);
a7b65cdc
UD
1386
1387 head = ah.addr;
1388
b2bffca2
UD
1389 names = (struct nameent *) xmalloc (head->namehash_used
1390 * sizeof (struct nameent));
a7b65cdc
UD
1391
1392 namehashtab = (struct namehashent *) ((char *) ah.addr
1393 + head->namehash_offset);
1394 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1395 if (namehashtab[cnt].locrec_offset != 0)
1396 {
1397 assert (used < head->namehash_used);
b2bffca2
UD
1398 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1399 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
a7b65cdc
UD
1400 }
1401
1402 /* Sort the names. */
b2bffca2
UD
1403 qsort (names, used, sizeof (struct nameent), nameentcmp);
1404
1405 if (verbose)
1406 {
1407 struct dataent *files;
1408 struct sumhashent *sumhashtab;
1409 int sumused;
1410
1411 files = (struct dataent *) xmalloc (head->sumhash_used
1412 * sizeof (struct sumhashent));
1413
1414 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1415 + head->sumhash_offset);
1416 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1417 if (sumhashtab[cnt].file_offset != 0)
1418 {
1419 assert (sumused < head->sumhash_used);
1420 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1421 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1422 files[sumused++].nlink = 0;
1423 }
1424
1425 /* Sort by file locations. */
1426 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1427
1428 /* Compute nlink fields. */
1429 for (cnt = 0; cnt < used; ++cnt)
1430 {
1431 struct locrecent *locrec;
1432 int idx;
1433
1434 locrec = (struct locrecent *) ((char *) ah.addr
1435 + names[cnt].locrec_offset);
1436 for (idx = 0; idx < __LC_LAST; ++idx)
ac8f8c53
RM
1437 if (locrec->record[LC_ALL].offset != 0
1438 ? (idx == LC_ALL
1439 || (locrec->record[idx].offset
1440 < locrec->record[LC_ALL].offset)
1441 || (locrec->record[idx].offset + locrec->record[idx].len
1442 > (locrec->record[LC_ALL].offset
1443 + locrec->record[LC_ALL].len)))
1444 : idx != LC_ALL)
b2bffca2
UD
1445 {
1446 struct dataent *data, dataent;
1447
1448 dataent.file_offset = locrec->record[idx].offset;
1449 data = (struct dataent *) bsearch (&dataent, files, sumused,
1450 sizeof (struct dataent),
1451 dataentcmp);
1452 assert (data != NULL);
1453 ++data->nlink;
1454 }
1455 }
a7b65cdc 1456
b2bffca2
UD
1457 /* Print it. */
1458 for (cnt = 0; cnt < used; ++cnt)
1459 {
1460 struct locrecent *locrec;
1461 int idx, i;
1462
1463 locrec = (struct locrecent *) ((char *) ah.addr
1464 + names[cnt].locrec_offset);
1465 for (idx = 0; idx < __LC_LAST; ++idx)
1466 if (idx != LC_ALL)
1467 {
1468 struct dataent *data, dataent;
1469
1470 dataent.file_offset = locrec->record[idx].offset;
ac8f8c53
RM
1471 if (locrec->record[LC_ALL].offset != 0
1472 && dataent.file_offset >= locrec->record[LC_ALL].offset
1473 && (dataent.file_offset + locrec->record[idx].len
1474 <= (locrec->record[LC_ALL].offset
1475 + locrec->record[LC_ALL].len)))
1476 dataent.file_offset = locrec->record[LC_ALL].offset;
1477
b2bffca2
UD
1478 data = (struct dataent *) bsearch (&dataent, files, sumused,
1479 sizeof (struct dataent),
1480 dataentcmp);
ac8f8c53 1481 printf ("%6d %7x %3d%c ",
b2bffca2 1482 locrec->record[idx].len, locrec->record[idx].offset,
ac8f8c53
RM
1483 data->nlink,
1484 dataent.file_offset == locrec->record[LC_ALL].offset
1485 ? '+' : ' ');
b2bffca2
UD
1486 for (i = 0; i < 16; i += 4)
1487 printf ("%02x%02x%02x%02x",
1488 data->sum[i], data->sum[i + 1],
1489 data->sum[i + 2], data->sum[i + 3]);
1490 printf (" %s/%s\n", names[cnt].name,
1491 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1492 : locnames[idx]);
1493 }
1494 }
1495 }
1496 else
1497 for (cnt = 0; cnt < used; ++cnt)
1498 puts (names[cnt].name);
a7b65cdc
UD
1499
1500 close_archive (&ah);
1501
1502 exit (EXIT_SUCCESS);
1503}