]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locarchive.c
Update.
[thirdparty/glibc.git] / locale / programs / locarchive.c
CommitLineData
a7b65cdc
UD
1/* Copyright (C) 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <assert.h>
25#include <dirent.h>
26#include <errno.h>
27#include <error.h>
28#include <fcntl.h>
29#include <inttypes.h>
30#include <libintl.h>
31#include <locale.h>
32#include <stdbool.h>
33#include <stdio.h>
cb09a2cd 34#include <stdio_ext.h>
a7b65cdc
UD
35#include <stdlib.h>
36#include <string.h>
37#include <time.h>
38#include <unistd.h>
39#include <sys/mman.h>
40#include <sys/param.h>
41#include <sys/stat.h>
42
43#include "../../crypt/md5.h"
44#include "../localeinfo.h"
45#include "../locarchive.h"
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];
ac8f8c53 241 struct oldlocrecent *oldlocrecarray;
531bafd8
UD
242
243 if (output_prefix)
244 memcpy (archivefname, output_prefix, prefix_len);
245 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
246 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
a7b65cdc
UD
247
248 /* Not all of the old file has to be mapped. Change this now this
249 we will have to access the whole content. */
250 if (fstat64 (ah->fd, &st) != 0
251 || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
252 MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
253 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
254 ah->len = st.st_size;
255
256 /* Create a temporary file in the correct directory. */
257 fd = mkstemp (fname);
258 if (fd == -1)
259 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
260
261 /* Copy the existing head information. */
262 newhead = *head;
263
264 /* Create the new archive header. The sizes of the various tables
265 should be double from what is currently used. */
266 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
267 newhead.namehash_size);
69f6a804
RM
268 if (! be_quiet)
269 printf ("name: size: %u, used: %d, new: size: %u\n",
270 head->namehash_size, head->namehash_used, newhead.namehash_size);
a7b65cdc
UD
271
272 newhead.string_offset = (newhead.namehash_offset
273 + (newhead.namehash_size
274 * sizeof (struct namehashent)));
275 newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
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. */
ac8f8c53
RM
341 oldlocrecarray = (struct oldlocrecent *)
342 alloca (head->namehash_size
343 * sizeof (struct oldlocrecent));
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,
586 const char *oldname, uint32_t locrec_offset)
587{
588 struct locarhead *head = ah->addr;
589 const size_t name_len = strlen (alias);
590 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
591 replace);
592 if (namehashent == NULL && ! replace)
593 return;
594
595 if (namehashent->name_offset == 0)
596 {
597 /* We are adding a new hash entry for this alias.
598 Determine whether we have to resize the file. */
599 if (head->string_used + name_len + 1 > head->string_size
600 || 100 * head->namehash_used > 75 * head->namehash_size)
601 {
602 /* The current archive is not large enough. */
603 enlarge_archive (ah, head);
604
605 /* The locrecent might have moved, so we have to look up
606 the old name afresh. */
607 namehashent = insert_name (ah, oldname, strlen (oldname), true);
608 assert (namehashent->name_offset != 0);
609 assert (namehashent->locrec_offset != 0);
610 locrec_offset = namehashent->locrec_offset;
611
612 /* Tail call to try the whole thing again. */
613 add_alias (ah, alias, replace, oldname, locrec_offset);
614 return;
615 }
616
617 /* Add the name string. */
618 memcpy (ah->addr + head->string_offset + head->string_used,
619 alias, name_len + 1);
620 namehashent->name_offset = head->string_offset + head->string_used;
621 head->string_used += name_len + 1;
622
623 ++head->namehash_used;
624 }
625
626 if (namehashent->locrec_offset != 0)
627 {
628 /* Replacing an existing entry.
629 Mark that we are no longer using the old locrecent. */
630 struct locrecent *locrecent
631 = (struct locrecent *) ((char *) ah->addr
632 + namehashent->locrec_offset);
633 --locrecent->refs;
634 }
635
636 /* Point this entry at the locrecent installed for the main name. */
637 namehashent->locrec_offset = locrec_offset;
638}
639
ac8f8c53
RM
640static int /* qsort comparator used below */
641cmpcategorysize (const void *a, const void *b)
642{
643 if (*(const void **) a == NULL)
644 return 1;
645 if (*(const void **) b == NULL)
646 return -1;
647 return ((*(const struct locale_category_data **) a)->size
648 - (*(const struct locale_category_data **) b)->size);
649}
a7b65cdc
UD
650
651/* Check the content of the archive for duplicates. Add the content
cb09a2cd
RM
652 of the files if necessary. Returns the locrec_offset. */
653static uint32_t
654add_locale (struct locarhandle *ah,
655 const char *name, locale_data_t data, bool replace)
a7b65cdc
UD
656{
657 /* First look for the name. If it already exists and we are not
658 supposed to replace it don't do anything. If it does not exist
659 we have to allocate a new locale record. */
660 size_t name_len = strlen (name);
661 uint32_t file_offsets[__LC_LAST];
662 unsigned int num_new_offsets = 0;
663 struct sumhashent *sumhashtab;
664 uint32_t hval;
ac8f8c53 665 unsigned int cnt, idx;
a7b65cdc 666 struct locarhead *head;
a7b65cdc
UD
667 struct namehashent *namehashent;
668 unsigned int incr;
669 struct locrecent *locrecent;
ac8f8c53
RM
670 off64_t lastoffset;
671 char *ptr;
672 struct locale_category_data *size_order[__LC_LAST];
673 const size_t pagesz = getpagesize ();
674 int small_mask;
a7b65cdc
UD
675
676 head = ah->addr;
677 sumhashtab = (struct sumhashent *) ((char *) ah->addr
678 + head->sumhash_offset);
a7b65cdc 679
ac8f8c53
RM
680 memset (file_offsets, 0, sizeof (file_offsets));
681
682 size_order[LC_ALL] = NULL;
683 for (cnt = 0; cnt < __LC_LAST; ++cnt)
684 if (cnt != LC_ALL)
685 size_order[cnt] = &data[cnt];
686
687 /* Sort the array in ascending order of data size. */
688 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
689
690 small_mask = 0;
691 data[LC_ALL].size = 0;
692 for (cnt = 0; cnt < __LC_LAST; ++cnt)
693 if (size_order[cnt] != NULL)
694 {
695 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
696 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
697 {
698 /* This category makes the small-categories block
699 stop being small, so this is the end of the road. */
700 do
701 size_order[cnt++] = NULL;
702 while (cnt < __LC_LAST);
703 break;
704 }
705 data[LC_ALL].size += rounded_size;
706 small_mask |= 1 << (size_order[cnt] - data);
707 }
708
709 /* Copy the data for all the small categories into the LC_ALL
710 pseudo-category. */
711
712 data[LC_ALL].addr = alloca (data[LC_ALL].size);
713 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
714
715 ptr = data[LC_ALL].addr;
716 for (cnt = 0; cnt < __LC_LAST; ++cnt)
717 if (small_mask & (1 << cnt))
718 {
719 memcpy (ptr, data[cnt].addr, data[cnt].size);
720 ptr += (data[cnt].size + 15) & -16;
721 }
722 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
a7b65cdc
UD
723
724 /* For each locale category data set determine whether the same data
725 is already somewhere in the archive. */
726 for (cnt = 0; cnt < __LC_LAST; ++cnt)
ac8f8c53 727 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
a7b65cdc 728 {
a7b65cdc
UD
729 ++num_new_offsets;
730
731 /* Compute the hash value of the checksum to determine a
732 starting point for the search in the MD5 hash value
733 table. */
a3f9038c 734 hval = archive_hashval (data[cnt].sum, 16);
a7b65cdc
UD
735
736 idx = hval % head->sumhash_size;
737 incr = 1 + hval % (head->sumhash_size - 2);
738
739 while (sumhashtab[idx].file_offset != 0)
740 {
741 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
742 {
743 /* Found it. */
744 file_offsets[cnt] = sumhashtab[idx].file_offset;
745 --num_new_offsets;
746 break;
747 }
748
749 idx += incr;
750 if (idx >= head->sumhash_size)
751 idx -= head->sumhash_size;
752 }
753 }
754
cb09a2cd
RM
755 /* Find a slot for the locale name in the hash table. */
756 namehashent = insert_name (ah, name, name_len, replace);
757 if (namehashent == NULL) /* Already exists and !REPLACE. */
758 return 0;
a7b65cdc
UD
759
760 /* Determine whether we have to resize the file. */
761 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
762 || (namehashent->locrec_offset == 0
763 && (head->locrectab_used == head->locrectab_size
764 || head->string_used + name_len + 1 > head->string_size
765 || 100 * head->namehash_used > 75 * head->namehash_size)))
766 {
767 /* The current archive is not large enough. */
768 enlarge_archive (ah, head);
cb09a2cd 769 return add_locale (ah, name, data, replace);
a7b65cdc
UD
770 }
771
772 /* Add the locale data which is not yet in the archive. */
ac8f8c53
RM
773 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
774 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
775 && file_offsets[cnt] == 0)
a7b65cdc
UD
776 {
777 /* The data for this section is not yet available in the
778 archive. Append it. */
779 off64_t lastpos;
780 uint32_t md5hval;
781
782 lastpos = lseek64 (ah->fd, 0, SEEK_END);
783 if (lastpos == (off64_t) -1)
784 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
785
ac8f8c53
RM
786 /* If block of small categories would cross page boundary,
787 align it unless it immediately follows a large category. */
788 if (cnt == LC_ALL && lastoffset != lastpos
789 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
790 & -pagesz)
791 > ((data[cnt].size + pagesz - 1) & -pagesz)))
792 {
793 size_t sz = pagesz - (lastpos & (pagesz - 1));
794 char *zeros = alloca (sz);
795
796 memset (zeros, 0, sz);
797 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
798 error (EXIT_FAILURE, errno,
799 _("cannot add to locale archive"));
800
801 lastpos += sz;
802 }
803
a7b65cdc
UD
804 /* Align all data to a 16 byte boundary. */
805 if ((lastpos & 15) != 0)
806 {
807 static const char zeros[15] = { 0, };
808
809 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
810 != 16 - (lastpos & 15))
811 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
812
813 lastpos += 16 - (lastpos & 15);
814 }
815
816 /* Remember the position. */
817 file_offsets[cnt] = lastpos;
ac8f8c53 818 lastoffset = lastpos + data[cnt].size;
a7b65cdc
UD
819
820 /* Write the data. */
821 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
822 != data[cnt].size)
823 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
824
825 /* Add the hash value to the hash table. */
a3f9038c 826 md5hval = archive_hashval (data[cnt].sum, 16);
a7b65cdc
UD
827
828 idx = md5hval % head->sumhash_size;
829 incr = 1 + md5hval % (head->sumhash_size - 2);
830
831 while (sumhashtab[idx].file_offset != 0)
832 {
833 idx += incr;
834 if (idx >= head->sumhash_size)
835 idx -= head->sumhash_size;
836 }
837
838 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
839 sumhashtab[idx].file_offset = file_offsets[cnt];
840
841 ++head->sumhash_used;
842 }
843
ac8f8c53
RM
844 lastoffset = file_offsets[LC_ALL];
845 for (cnt = 0; cnt < __LC_LAST; ++cnt)
846 if (small_mask & (1 << cnt))
847 {
848 file_offsets[cnt] = lastoffset;
849 lastoffset += (data[cnt].size + 15) & -16;
850 }
851
cb09a2cd 852 if (namehashent->name_offset == 0)
a7b65cdc
UD
853 {
854 /* Add the name string. */
855 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
856 name, name_len + 1);
857 namehashent->name_offset = head->string_offset + head->string_used;
858 head->string_used += name_len + 1;
cb09a2cd
RM
859 ++head->namehash_used;
860 }
a7b65cdc 861
cb09a2cd
RM
862 if (namehashent->locrec_offset == 0)
863 {
a7b65cdc
UD
864 /* Allocate a name location record. */
865 namehashent->locrec_offset = (head->locrectab_offset
866 + (head->locrectab_used++
867 * sizeof (struct locrecent)));
cb09a2cd
RM
868 locrecent = (struct locrecent *) ((char *) ah->addr
869 + namehashent->locrec_offset);
870 locrecent->refs = 1;
a7b65cdc 871 }
cb09a2cd
RM
872 else
873 {
874 /* If there are other aliases pointing to this locrecent,
875 we still need a new one. If not, reuse the old one. */
a7b65cdc 876
cb09a2cd
RM
877 locrecent = (struct locrecent *) ((char *) ah->addr
878 + namehashent->locrec_offset);
879 if (locrecent->refs > 1)
880 {
881 --locrecent->refs;
882 namehashent->locrec_offset = (head->locrectab_offset
883 + (head->locrectab_used++
884 * sizeof (struct locrecent)));
885 locrecent = (struct locrecent *) ((char *) ah->addr
886 + namehashent->locrec_offset);
887 locrecent->refs = 1;
888 }
889 }
a7b65cdc
UD
890
891 /* Fill in the table with the locations of the locale data. */
a7b65cdc 892 for (cnt = 0; cnt < __LC_LAST; ++cnt)
ac8f8c53
RM
893 {
894 locrecent->record[cnt].offset = file_offsets[cnt];
895 locrecent->record[cnt].len = data[cnt].size;
896 }
a7b65cdc 897
cb09a2cd
RM
898 return namehashent->locrec_offset;
899}
900
901
902/* Check the content of the archive for duplicates. Add the content
903 of the files if necessary. Add all the names, possibly overwriting
904 old files. */
905int
906add_locale_to_archive (ah, name, data, replace)
907 struct locarhandle *ah;
908 const char *name;
909 locale_data_t data;
910 bool replace;
911{
912 char *normalized_name = NULL;
913 uint32_t locrec_offset;
914
915 /* First analyze the name to decide how to archive it. */
916 const char *language;
917 const char *modifier;
918 const char *territory;
919 const char *codeset;
920 const char *normalized_codeset;
921 int mask = _nl_explode_name (strdupa (name),
922 &language, &modifier, &territory,
923 &codeset, &normalized_codeset);
924
925 if (mask & XPG_NORM_CODESET)
926 /* This name contains a codeset in unnormalized form.
927 We will store it in the archive with a normalized name. */
928 asprintf (&normalized_name, "%s%s%s.%s%s%s",
929 language, territory == NULL ? "" : "_", territory ?: "",
930 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
931 modifier == NULL ? "" : "@", modifier ?: "");
932
933 /* This call does the main work. */
934 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
935 free (normalized_name);
936 if (locrec_offset == 0)
937 {
938 if (mask & XPG_NORM_CODESET)
939 free ((char *) normalized_codeset);
940 return -1;
941 }
942
943 if ((mask & XPG_CODESET) == 0)
944 {
945 /* This name lacks a codeset, so determine the locale's codeset and
946 add an alias for its name with normalized codeset appended. */
947
948 const struct
949 {
950 unsigned int magic;
951 unsigned int nstrings;
952 unsigned int strindex[0];
953 } *filedata = data[LC_CTYPE].addr;
954 codeset = (char *) filedata
955 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
956
957 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
958 mask |= XPG_NORM_CODESET;
959
960 asprintf (&normalized_name, "%s%s%s.%s%s%s",
961 language, territory == NULL ? "" : "_", territory ?: "",
962 normalized_codeset,
963 modifier == NULL ? "" : "@", modifier ?: "");
964
965 add_alias (ah, normalized_name, replace, name, locrec_offset);
966 free (normalized_name);
967 }
968
969 /* Now read the locale.alias files looking for lines whose
970 right hand side matches our name after normalization. */
971 if (alias_file != NULL)
972 {
973 FILE *fp;
974 fp = fopen (alias_file, "r");
975 if (fp == NULL)
976 error (1, errno, _("locale alias file `%s' not found"),
977 alias_file);
978
979 /* No threads present. */
980 __fsetlocking (fp, FSETLOCKING_BYCALLER);
981
982 while (! feof_unlocked (fp))
983 {
984 /* It is a reasonable approach to use a fix buffer here
985 because
986 a) we are only interested in the first two fields
987 b) these fields must be usable as file names and so must
988 not be that long */
989 char buf[BUFSIZ];
990 char *alias;
991 char *value;
992 char *cp;
993
994 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
995 /* EOF reached. */
996 break;
997
998 cp = buf;
999 /* Ignore leading white space. */
1000 while (isspace (cp[0]) && cp[0] != '\n')
1001 ++cp;
1002
1003 /* A leading '#' signals a comment line. */
1004 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
1005 {
1006 alias = cp++;
1007 while (cp[0] != '\0' && !isspace (cp[0]))
1008 ++cp;
1009 /* Terminate alias name. */
1010 if (cp[0] != '\0')
1011 *cp++ = '\0';
1012
1013 /* Now look for the beginning of the value. */
1014 while (isspace (cp[0]))
1015 ++cp;
1016
1017 if (cp[0] != '\0')
1018 {
1019 value = cp++;
1020 while (cp[0] != '\0' && !isspace (cp[0]))
1021 ++cp;
1022 /* Terminate value. */
1023 if (cp[0] == '\n')
1024 {
1025 /* This has to be done to make the following
1026 test for the end of line possible. We are
1027 looking for the terminating '\n' which do not
1028 overwrite here. */
1029 *cp++ = '\0';
1030 *cp = '\n';
1031 }
1032 else if (cp[0] != '\0')
1033 *cp++ = '\0';
1034
1035 /* Does this alias refer to our locale? We will
1036 normalize the right hand side and compare the
1037 elements of the normalized form. */
1038 {
1039 const char *rhs_language;
1040 const char *rhs_modifier;
1041 const char *rhs_territory;
1042 const char *rhs_codeset;
1043 const char *rhs_normalized_codeset;
1044 int rhs_mask = _nl_explode_name (value,
1045 &rhs_language,
1046 &rhs_modifier,
1047 &rhs_territory,
1048 &rhs_codeset,
1049 &rhs_normalized_codeset);
1050 if (!strcmp (language, rhs_language)
1051 && ((rhs_mask & XPG_CODESET)
1052 /* He has a codeset, it must match normalized. */
1053 ? !strcmp ((mask & XPG_NORM_CODESET)
1054 ? normalized_codeset : codeset,
1055 (rhs_mask & XPG_NORM_CODESET)
1056 ? rhs_normalized_codeset : rhs_codeset)
1057 /* He has no codeset, we must also have none. */
1058 : (mask & XPG_CODESET) == 0)
1059 /* Codeset (or lack thereof) matches. */
1060 && !strcmp (territory ?: "", rhs_territory ?: "")
1061 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1062 /* We have a winner. */
1063 add_alias (ah, alias, replace,
1064 normalized_name ?: name, locrec_offset);
1065 if (rhs_mask & XPG_NORM_CODESET)
1066 free ((char *) rhs_normalized_codeset);
1067 }
1068 }
1069 }
1070
1071 /* Possibly not the whole line fits into the buffer.
1072 Ignore the rest of the line. */
1073 while (strchr (cp, '\n') == NULL)
1074 {
1075 cp = buf;
1076 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1077 /* Make sure the inner loop will be left. The outer
1078 loop will exit at the `feof' test. */
1079 *cp = '\n';
1080 }
1081 }
a7b65cdc 1082
cb09a2cd
RM
1083 fclose (fp);
1084 }
a7b65cdc 1085
cb09a2cd
RM
1086 if (mask & XPG_NORM_CODESET)
1087 free ((char *) normalized_codeset);
a7b65cdc
UD
1088
1089 return 0;
1090}
1091
1092
1093int
1094add_locales_to_archive (nlist, list, replace)
1095 size_t nlist;
1096 char *list[];
1097 bool replace;
1098{
1099 struct locarhandle ah;
1100 int result = 0;
1101
1102 /* Open the archive. This call never returns if we cannot
1103 successfully open the archive. */
b2bffca2 1104 open_archive (&ah, false);
a7b65cdc
UD
1105
1106 while (nlist-- > 0)
1107 {
1108 const char *fname = *list++;
1109 size_t fnamelen = strlen (fname);
1110 struct stat64 st;
1111 DIR *dirp;
1112 struct dirent64 *d;
1113 int seen;
1114 locale_data_t data;
1115 int cnt;
1116
1117 if (! be_quiet)
1118 printf (_("Adding %s\n"), fname);
1119
1120 /* First see whether this really is a directory and whether it
1121 contains all the require locale category files. */
1122 if (stat64 (fname, &st) < 0)
1123 {
1124 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1125 strerror (errno));
1126 continue;
1127 }
1128 if (!S_ISDIR (st.st_mode))
1129 {
1130 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1131 continue;
1132 }
1133
1134 dirp = opendir (fname);
1135 if (dirp == NULL)
1136 {
1137 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1138 fname, strerror (errno));
1139 continue;
1140 }
1141
1142 seen = 0;
1143 while ((d = readdir64 (dirp)) != NULL)
1144 {
1145 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1146 if (cnt != LC_ALL)
1147 if (strcmp (d->d_name, locnames[cnt]) == 0)
1148 {
1149 unsigned char d_type;
1150
1151 /* We have an object of the required name. If it's
1152 a directory we have to look at a file with the
1153 prefix "SYS_". Otherwise we have found what we
1154 are looking for. */
1155#ifdef _DIRENT_HAVE_D_TYPE
1156 d_type = d->d_type;
1157
1158 if (d_type != DT_REG)
1159#endif
1160 {
1161 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1162
1163#ifdef _DIRENT_HAVE_D_TYPE
1164 if (d_type == DT_UNKNOWN)
1165#endif
1166 {
1167 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1168 d->d_name);
1169
1170 if (stat64 (fullname, &st) == -1)
1171 /* We cannot stat the file, ignore it. */
1172 break;
1173
1174 d_type = IFTODT (st.st_mode);
1175 }
1176
1177 if (d_type == DT_DIR)
1178 {
1179 /* We have to do more tests. The file is a
1180 directory and it therefore must contain a
1181 regular file with the same name except a
1182 "SYS_" prefix. */
531bafd8
UD
1183 char *t = stpcpy (stpcpy (fullname, fname), "/");
1184 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
a7b65cdc
UD
1185 d->d_name);
1186
1187 if (stat64 (fullname, &st) == -1)
1188 /* There is no SYS_* file or we cannot
1189 access it. */
1190 break;
1191
1192 d_type = IFTODT (st.st_mode);
1193 }
1194 }
1195
1196 /* If we found a regular file (eventually after
1197 following a symlink) we are successful. */
1198 if (d_type == DT_REG)
1199 ++seen;
1200 break;
1201 }
1202 }
1203
1204 closedir (dirp);
1205
1206 if (seen != __LC_LAST - 1)
1207 {
1208 /* We don't have all locale category files. Ignore the name. */
1209 error (0, 0, _("incomplete set of locale files in \"%s\""),
1210 fname);
1211 continue;
1212 }
1213
1214 /* Add the files to the archive. To do this we first compute
1215 sizes and the MD5 sums of all the files. */
1216 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1217 if (cnt != LC_ALL)
1218 {
1219 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1220 int fd;
1221
1222 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1223 fd = open64 (fullname, O_RDONLY);
1224 if (fd == -1 || fstat64 (fd, &st) == -1)
1225 {
1226 /* Cannot read the file. */
1227 if (fd != -1)
1228 close (fd);
1229 break;
1230 }
1231
1232 if (S_ISDIR (st.st_mode))
1233 {
531bafd8 1234 char *t;
a7b65cdc 1235 close (fd);
531bafd8
UD
1236 t = stpcpy (stpcpy (fullname, fname), "/");
1237 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
a7b65cdc
UD
1238 locnames[cnt]);
1239
1240 fd = open64 (fullname, O_RDONLY);
1241 if (fd == -1 || fstat64 (fd, &st) == -1
1242 || !S_ISREG (st.st_mode))
1243 {
1244 if (fd != -1)
1245 close (fd);
1246 break;
1247 }
1248 }
1249
1250 /* Map the file. */
1251 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1252 fd, 0);
1253 if (data[cnt].addr == MAP_FAILED)
1254 {
1255 /* Cannot map it. */
1256 close (fd);
1257 break;
1258 }
1259
1260 data[cnt].size = st.st_size;
1261 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1262
1263 /* We don't need the file descriptor anymore. */
1264 close (fd);
1265 }
1266
1267 if (cnt != __LC_LAST)
1268 {
1269 while (cnt-- > 0)
1270 if (cnt != LC_ALL)
1271 munmap (data[cnt].addr, data[cnt].size);
1272
1273 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1274
1275 continue;
1276 }
1277
1278 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1279
1280 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1281 if (cnt != LC_ALL)
1282 munmap (data[cnt].addr, data[cnt].size);
1283 }
1284
1285 /* We are done. */
1286 close_archive (&ah);
1287
1288 return result;
1289}
1290
1291
1292int
1293delete_locales_from_archive (nlist, list)
1294 size_t nlist;
1295 char *list[];
1296{
1297 struct locarhandle ah;
1298 struct locarhead *head;
1299 struct namehashent *namehashtab;
1300
1301 /* Open the archive. This call never returns if we cannot
1302 successfully open the archive. */
b2bffca2 1303 open_archive (&ah, false);
a7b65cdc
UD
1304
1305 head = ah.addr;
1306 namehashtab = (struct namehashent *) ((char *) ah.addr
1307 + head->namehash_offset);
1308
1309 while (nlist-- > 0)
1310 {
1311 const char *locname = *list++;
1312 uint32_t hval;
1313 unsigned int idx;
1314 unsigned int incr;
1315
1316 /* Search for this locale in the archive. */
a3f9038c 1317 hval = archive_hashval (locname, strlen (locname));
a7b65cdc
UD
1318
1319 idx = hval % head->namehash_size;
1320 incr = 1 + hval % (head->namehash_size - 2);
1321
1322 /* If the name_offset field is zero this means this is no
1323 deleted entry and therefore no entry can be found. */
1324 while (namehashtab[idx].name_offset != 0)
1325 {
1326 if (namehashtab[idx].hashval == hval
1327 && (strcmp (locname,
1328 (char *) ah.addr + namehashtab[idx].name_offset)
1329 == 0))
1330 {
1331 /* Found the entry. Now mark it as removed by zero-ing
1332 the reference to the locale record. */
1333 namehashtab[idx].locrec_offset = 0;
a7b65cdc
UD
1334 break;
1335 }
1336
1337 idx += incr;
1338 if (idx >= head->namehash_size)
1339 idx -= head->namehash_size;
1340 }
1341
1342 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1343 error (0, 0, _("locale \"%s\" not in archive"), locname);
1344 }
1345
1346 close_archive (&ah);
1347
1348 return 0;
1349}
1350
1351
b2bffca2
UD
1352struct nameent
1353{
1354 char *name;
1355 uint32_t locrec_offset;
1356};
1357
1358
1359struct dataent
1360{
1361 const unsigned char *sum;
1362 uint32_t file_offset;
1363 uint32_t nlink;
1364};
1365
1366
1367static int
1368nameentcmp (const void *a, const void *b)
1369{
1370 return strcmp (((const struct nameent *) a)->name,
1371 ((const struct nameent *) b)->name);
1372}
1373
1374
a7b65cdc 1375static int
b2bffca2 1376dataentcmp (const void *a, const void *b)
a7b65cdc 1377{
b2bffca2
UD
1378 if (((const struct dataent *) a)->file_offset
1379 < ((const struct dataent *) b)->file_offset)
1380 return -1;
1381
1382 if (((const struct dataent *) a)->file_offset
1383 > ((const struct dataent *) b)->file_offset)
1384 return 1;
1385
1386 return 0;
a7b65cdc
UD
1387}
1388
1389
1390void
b2bffca2 1391show_archive_content (int verbose)
a7b65cdc
UD
1392{
1393 struct locarhandle ah;
1394 struct locarhead *head;
1395 struct namehashent *namehashtab;
b2bffca2 1396 struct nameent *names;
a7b65cdc 1397 int cnt;
a7b65cdc
UD
1398 int used;
1399
1400 /* Open the archive. This call never returns if we cannot
1401 successfully open the archive. */
b2bffca2 1402 open_archive (&ah, true);
a7b65cdc
UD
1403
1404 head = ah.addr;
1405
b2bffca2
UD
1406 names = (struct nameent *) xmalloc (head->namehash_used
1407 * sizeof (struct nameent));
a7b65cdc
UD
1408
1409 namehashtab = (struct namehashent *) ((char *) ah.addr
1410 + head->namehash_offset);
1411 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1412 if (namehashtab[cnt].locrec_offset != 0)
1413 {
1414 assert (used < head->namehash_used);
b2bffca2
UD
1415 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1416 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
a7b65cdc
UD
1417 }
1418
1419 /* Sort the names. */
b2bffca2
UD
1420 qsort (names, used, sizeof (struct nameent), nameentcmp);
1421
1422 if (verbose)
1423 {
1424 struct dataent *files;
1425 struct sumhashent *sumhashtab;
1426 int sumused;
1427
1428 files = (struct dataent *) xmalloc (head->sumhash_used
1429 * sizeof (struct sumhashent));
1430
1431 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1432 + head->sumhash_offset);
1433 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1434 if (sumhashtab[cnt].file_offset != 0)
1435 {
1436 assert (sumused < head->sumhash_used);
1437 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1438 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1439 files[sumused++].nlink = 0;
1440 }
1441
1442 /* Sort by file locations. */
1443 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1444
1445 /* Compute nlink fields. */
1446 for (cnt = 0; cnt < used; ++cnt)
1447 {
1448 struct locrecent *locrec;
1449 int idx;
1450
1451 locrec = (struct locrecent *) ((char *) ah.addr
1452 + names[cnt].locrec_offset);
1453 for (idx = 0; idx < __LC_LAST; ++idx)
ac8f8c53
RM
1454 if (locrec->record[LC_ALL].offset != 0
1455 ? (idx == LC_ALL
1456 || (locrec->record[idx].offset
1457 < locrec->record[LC_ALL].offset)
1458 || (locrec->record[idx].offset + locrec->record[idx].len
1459 > (locrec->record[LC_ALL].offset
1460 + locrec->record[LC_ALL].len)))
1461 : idx != LC_ALL)
b2bffca2
UD
1462 {
1463 struct dataent *data, dataent;
1464
1465 dataent.file_offset = locrec->record[idx].offset;
1466 data = (struct dataent *) bsearch (&dataent, files, sumused,
1467 sizeof (struct dataent),
1468 dataentcmp);
1469 assert (data != NULL);
1470 ++data->nlink;
1471 }
1472 }
a7b65cdc 1473
b2bffca2
UD
1474 /* Print it. */
1475 for (cnt = 0; cnt < used; ++cnt)
1476 {
1477 struct locrecent *locrec;
1478 int idx, i;
1479
1480 locrec = (struct locrecent *) ((char *) ah.addr
1481 + names[cnt].locrec_offset);
1482 for (idx = 0; idx < __LC_LAST; ++idx)
1483 if (idx != LC_ALL)
1484 {
1485 struct dataent *data, dataent;
1486
1487 dataent.file_offset = locrec->record[idx].offset;
ac8f8c53
RM
1488 if (locrec->record[LC_ALL].offset != 0
1489 && dataent.file_offset >= locrec->record[LC_ALL].offset
1490 && (dataent.file_offset + locrec->record[idx].len
1491 <= (locrec->record[LC_ALL].offset
1492 + locrec->record[LC_ALL].len)))
1493 dataent.file_offset = locrec->record[LC_ALL].offset;
1494
b2bffca2
UD
1495 data = (struct dataent *) bsearch (&dataent, files, sumused,
1496 sizeof (struct dataent),
1497 dataentcmp);
ac8f8c53 1498 printf ("%6d %7x %3d%c ",
b2bffca2 1499 locrec->record[idx].len, locrec->record[idx].offset,
ac8f8c53
RM
1500 data->nlink,
1501 dataent.file_offset == locrec->record[LC_ALL].offset
1502 ? '+' : ' ');
b2bffca2
UD
1503 for (i = 0; i < 16; i += 4)
1504 printf ("%02x%02x%02x%02x",
1505 data->sum[i], data->sum[i + 1],
1506 data->sum[i + 2], data->sum[i + 3]);
1507 printf (" %s/%s\n", names[cnt].name,
1508 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1509 : locnames[idx]);
1510 }
1511 }
1512 }
1513 else
1514 for (cnt = 0; cnt < used; ++cnt)
1515 puts (names[cnt].name);
a7b65cdc
UD
1516
1517 close_archive (&ah);
1518
1519 exit (EXIT_SUCCESS);
1520}