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