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