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