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