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