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