]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locarchive.c
Update.
[thirdparty/glibc.git] / locale / programs / locarchive.c
CommitLineData
a7b65cdc
UD
1/* Copyright (C) 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <assert.h>
25#include <dirent.h>
26#include <errno.h>
27#include <error.h>
28#include <fcntl.h>
29#include <inttypes.h>
30#include <libintl.h>
31#include <locale.h>
32#include <stdbool.h>
33#include <stdio.h>
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"
45#include "simple-hash.h"
46#include "localedef.h"
47
48
49static const char archivefname[] = LOCALEDIR "/locale-archive";
50
51static const char *locnames[] =
52 {
53#define DEFINE_CATEGORY(category, category_name, items, a) \
54 [category] = category_name,
55#include "categories.def"
56#undef DEFINE_CATEGORY
57 };
58
59
60/* Size of the initial archive header. */
61#define INITIAL_NUM_NANES 450
62#define INITIAL_SIZE_STRINGS 3500
63#define INITIAL_NUM_LOCREC 350
64#define INITIAL_NUM_SUMS 2000
65
66
67static void
68create_archive (struct locarhandle *ah)
69{
70 int fd;
71 char fname[] = LOCALEDIR "/locale-archive.XXXXXX";
72 struct locarhead head;
73 void *p;
74 size_t total;
75
76 /* Create a temporary file in the correct directory. */
77 fd = mkstemp (fname);
78 if (fd == -1)
79 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
80
81 /* Create the initial content of the archive. */
82 head.magic = AR_MAGIC;
83 head.namehash_offset = sizeof (struct locarhead);
84 head.namehash_used = 0;
85 head.namehash_size = next_prime (INITIAL_NUM_NANES);
86
87 head.string_offset = (head.namehash_offset
88 + head.namehash_size * sizeof (struct namehashent));
89 head.string_used = 0;
90 head.string_size = INITIAL_SIZE_STRINGS;
91
92 head.locrectab_offset = head.string_offset + head.string_size;
93 head.locrectab_used = 0;
94 head.locrectab_size = INITIAL_NUM_LOCREC;
95
96 head.sumhash_offset = (head.locrectab_offset
97 + head.locrectab_size * sizeof (struct locrecent));
98 head.sumhash_used = 0;
99 head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
100
101 total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
102
103 /* Write out the header and create room for the other data structures. */
104 if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
105 {
106 int errval = errno;
107 unlink (fname);
108 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
109 }
110
111 if (ftruncate64 (fd, total) != 0)
112 {
113 int errval = errno;
114 unlink (fname);
115 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
116 }
117
118 /* Map the header and all the administration data structures. */
119 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
120 if (p == MAP_FAILED)
121 {
122 int errval = errno;
123 unlink (fname);
124 error (EXIT_FAILURE, errval, _("cannot map archive header"));
125 }
126
127 /* Now try to rename it. We don't use the rename function since
128 this would overwrite a file which has been created in
129 parallel. */
130 if (link (fname, archivefname) == -1)
131 {
132 int errval = errno;
133
134 /* We cannot use the just created file. */
135 close (fd);
136 unlink (fname);
137
138 if (errval == EEXIST)
139 {
140 /* There is already an archive. Must have been a localedef run
141 which happened in parallel. Simply open this file then. */
142 open_archive (ah);
143 return;
144 }
145
146 error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
147 }
148
149 /* Remove the temporary name. */
150 unlink (fname);
151
152 /* Make the file globally readable. */
153 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
154 {
155 int errval = errno;
156 unlink (archivefname);
157 error (EXIT_FAILURE, errval,
158 _("cannot change mode of new locale archive"));
159 }
160
161 ah->fd = fd;
162 ah->addr = p;
163 ah->len = total;
164}
165
166
167static void
168enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
169{
170 struct stat64 st;
171 int fd;
172 char fname[] = LOCALEDIR "/locale-archive.XXXXXX";
173 struct locarhead newhead;
174 size_t total;
175 void *p;
176 unsigned int cnt;
177 struct namehashent *oldnamehashtab;
178 struct locrecent *oldlocrectab;
179 struct locarhandle new_ah;
180
181 /* Not all of the old file has to be mapped. Change this now this
182 we will have to access the whole content. */
183 if (fstat64 (ah->fd, &st) != 0
184 || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
185 MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
186 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
187 ah->len = st.st_size;
188
189 /* Create a temporary file in the correct directory. */
190 fd = mkstemp (fname);
191 if (fd == -1)
192 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
193
194 /* Copy the existing head information. */
195 newhead = *head;
196
197 /* Create the new archive header. The sizes of the various tables
198 should be double from what is currently used. */
199 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
200 newhead.namehash_size);
201 printf ("name: size: %u, used: %d, new: size: %u\n",
202 head->namehash_size, head->namehash_used, newhead.namehash_size);
203
204 newhead.string_offset = (newhead.namehash_offset
205 + (newhead.namehash_size
206 * sizeof (struct namehashent)));
207 newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
208
209 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
210 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
211 newhead.locrectab_size);
212
213 newhead.sumhash_offset = (newhead.locrectab_offset
214 + (newhead.locrectab_size
215 * sizeof (struct locrecent)));
216 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
217 newhead.sumhash_size);
218
219 total = (newhead.sumhash_offset
220 + newhead.sumhash_size * sizeof (struct sumhashent));
221
222 /* The new file is empty now. */
223 newhead.namehash_used = 0;
224 newhead.string_used = 0;
225 newhead.locrectab_used = 0;
226 newhead.sumhash_used = 0;
227
228 /* Write out the header and create room for the other data structures. */
229 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
230 != sizeof (newhead))
231 {
232 int errval = errno;
233 unlink (fname);
234 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
235 }
236
237 if (ftruncate64 (fd, total) != 0)
238 {
239 int errval = errno;
240 unlink (fname);
241 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
242 }
243
244 /* Map the header and all the administration data structures. */
245 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
246 if (p == MAP_FAILED)
247 {
248 int errval = errno;
249 unlink (fname);
250 error (EXIT_FAILURE, errval, _("cannot map archive header"));
251 }
252
253 /* Lock the new file. */
254 if (lockf64 (fd, F_LOCK, total) != 0)
255 {
256 int errval = errno;
257 unlink (fname);
258 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
259 }
260
261 new_ah.len = total;
262 new_ah.addr = p;
263 new_ah.fd = fd;
264
265 /* Walk through the hash name hash table to find out what data is
266 still referenced and transfer it into the new file. */
267 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
268 + head->namehash_offset);
269 oldlocrectab = (struct locrecent *) ((char *) ah->addr
270 + head->locrectab_offset);
271 for (cnt = 0; cnt < head->namehash_size; ++cnt)
272 if (oldnamehashtab[cnt].locrec_offset != 0)
273 {
274 /* Insert this entry in the new hash table. */
275 locale_data_t old_data;
276 unsigned int idx;
277 struct locrecent *oldlocrec;
278
279 oldlocrec = (struct locrecent *) ((char *) ah->addr
280 + oldnamehashtab[cnt].locrec_offset);
281
282 for (idx = 0; idx < __LC_LAST; ++idx)
283 if (idx != LC_ALL)
284 {
285 old_data[idx].size = oldlocrec->record[idx].len;
286 old_data[idx].addr
287 = ((char *) ah->addr + oldlocrec->record[idx].offset);
288
289 __md5_buffer (old_data[idx].addr, old_data[idx].size,
290 old_data[idx].sum);
291 }
292
293 if (add_locale_to_archive (&new_ah,
294 ((char *) ah->addr
295 + oldnamehashtab[cnt].name_offset),
296 old_data, 0) != 0)
297 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
298 }
299
300
301 /* Make the file globally readable. */
302 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
303 {
304 int errval = errno;
305 unlink (fname);
306 error (EXIT_FAILURE, errval,
307 _("cannot change mode of resized locale archive"));
308 }
309
310 /* Rename the new file. */
311 if (rename (fname, archivefname) != 0)
312 {
313 int errval = errno;
314 unlink (fname);
315 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
316 }
317
318 /* Close the old file. */
319 close_archive (ah);
320
321 /* Add the information for the new one. */
322 *ah = new_ah;
323}
324
325
326void
327open_archive (struct locarhandle *ah)
328{
329 struct stat64 st;
330 struct stat64 st2;
331 int fd;
332 struct locarhead head;
333 int retry = 0;
334
335 again:
336 /* Open the archive. We must have exclusive write access. */
337 fd = open64 (archivefname, O_RDWR);
338 if (fd == -1)
339 {
340 /* Maybe the file does not yet exist. */
341 if (errno == ENOENT)
342 {
343 create_archive (ah);
344 return;
345 }
346 else
347 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
348 archivefname);
349 }
350
351 if (fstat64 (fd, &st) < 0)
352 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
353 archivefname);
354
355 if (lockf64 (fd, F_LOCK, st.st_size) == -1)
356 {
357 close (fd);
358
359 if (retry++ < max_locarchive_open_retry)
360 {
361 struct timespec req;
362
363 /* Wait for a bit. */
364 req.tv_sec = 0;
365 req.tv_nsec = 1000000 * (random () % 500 + 1);
366 (void) nanosleep (&req, NULL);
367
368 goto again;
369 }
370
371 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
372 archivefname);
373 }
374
375 /* One more check. Maybe another process replaced the archive file
376 with a new, larger one since we opened the file. */
377 if (stat64 (archivefname, &st2) == -1
378 || st.st_dev != st2.st_dev
379 || st.st_ino != st2.st_ino)
380 {
381 close (fd);
382 goto again;
383 }
384
385 /* Read the header. */
386 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
387 error (EXIT_FAILURE, errno, _("cannot read archive header"));
388
389 ah->fd = fd;
390 ah->len = (head.sumhash_offset
391 + head.sumhash_size * sizeof (struct sumhashent));
392
393 /* Now we know how large the administrative information part is.
394 Map all of it. */
395 ah->addr = mmap64 (NULL, ah->len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
396 if (ah->addr == MAP_FAILED)
397 error (EXIT_FAILURE, errno, _("cannot map archive header"));
398}
399
400
401void
402close_archive (struct locarhandle *ah)
403{
404 munmap (ah->addr, ah->len);
405 close (ah->fd);
406}
407
408
409/* Check the content of the archive for duplicates. Add the content
410 of the files if necessary. Add all the names, possibly overwriting
411 old files. */
412int
413add_locale_to_archive (ah, name, data, replace)
414 struct locarhandle *ah;
415 const char *name;
416 locale_data_t data;
417 bool replace;
418{
419 /* First look for the name. If it already exists and we are not
420 supposed to replace it don't do anything. If it does not exist
421 we have to allocate a new locale record. */
422 size_t name_len = strlen (name);
423 uint32_t file_offsets[__LC_LAST];
424 unsigned int num_new_offsets = 0;
425 struct sumhashent *sumhashtab;
426 uint32_t hval;
427 unsigned int cnt;
428 unsigned int idx;
429 unsigned int insert_idx;
430 struct locarhead *head;
431 struct namehashent *namehashtab;
432 struct namehashent *namehashent;
433 unsigned int incr;
434 struct locrecent *locrecent;
435
436 head = ah->addr;
437 sumhashtab = (struct sumhashent *) ((char *) ah->addr
438 + head->sumhash_offset);
439 namehashtab = (struct namehashent *) ((char *) ah->addr
440 + head->namehash_offset);
441
442
443 /* For each locale category data set determine whether the same data
444 is already somewhere in the archive. */
445 for (cnt = 0; cnt < __LC_LAST; ++cnt)
446 if (cnt != LC_ALL)
447 {
448 /* By default signal that we have no data. */
449 file_offsets[cnt] = 0;
450 ++num_new_offsets;
451
452 /* Compute the hash value of the checksum to determine a
453 starting point for the search in the MD5 hash value
454 table. */
455 hval = compute_hashval (data[cnt].sum, 16);
456
457 idx = hval % head->sumhash_size;
458 incr = 1 + hval % (head->sumhash_size - 2);
459
460 while (sumhashtab[idx].file_offset != 0)
461 {
462 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
463 {
464 /* Found it. */
465 file_offsets[cnt] = sumhashtab[idx].file_offset;
466 --num_new_offsets;
467 break;
468 }
469
470 idx += incr;
471 if (idx >= head->sumhash_size)
472 idx -= head->sumhash_size;
473 }
474 }
475
476
477 /* Hash value of the locale name. */
478 hval = compute_hashval (name, name_len);
479
480 insert_idx = -1;
481 idx = hval % head->namehash_size;
482 incr = 1 + hval % (head->namehash_size - 2);
483
484 /* If the name_offset field is zero this means this is no
485 deleted entry and therefore no entry can be found. */
486 while (namehashtab[idx].name_offset != 0)
487 {
488 if (namehashtab[idx].hashval == hval
489 && strcmp (name,
490 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
491 {
492 /* Found the entry. */
493 if (! replace)
494 {
495 if (! be_quiet)
496 error (0, 0, _("locale '%s' already exists"), name);
497 return 1;
498 }
499
500 break;
501 }
502
503 /* Remember the first place we can insert the new entry. */
504 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
505 insert_idx = idx;
506
507 idx += incr;
508 if (idx >= head->namehash_size)
509 idx -= head->namehash_size;
510 }
511
512 /* Add as early as possible. */
513 if (insert_idx != -1)
514 idx = insert_idx;
515
516 namehashent = &namehashtab[idx];
517
518 /* Determine whether we have to resize the file. */
519 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
520 || (namehashent->locrec_offset == 0
521 && (head->locrectab_used == head->locrectab_size
522 || head->string_used + name_len + 1 > head->string_size
523 || 100 * head->namehash_used > 75 * head->namehash_size)))
524 {
525 /* The current archive is not large enough. */
526 enlarge_archive (ah, head);
527 return add_locale_to_archive (ah, name, data, replace);
528 }
529
530 /* Add the locale data which is not yet in the archive. */
531 for (cnt = 0; cnt < __LC_LAST; ++cnt)
532 if (cnt != LC_ALL && file_offsets[cnt] == 0)
533 {
534 /* The data for this section is not yet available in the
535 archive. Append it. */
536 off64_t lastpos;
537 uint32_t md5hval;
538
539 lastpos = lseek64 (ah->fd, 0, SEEK_END);
540 if (lastpos == (off64_t) -1)
541 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
542
543 /* Align all data to a 16 byte boundary. */
544 if ((lastpos & 15) != 0)
545 {
546 static const char zeros[15] = { 0, };
547
548 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
549 != 16 - (lastpos & 15))
550 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
551
552 lastpos += 16 - (lastpos & 15);
553 }
554
555 /* Remember the position. */
556 file_offsets[cnt] = lastpos;
557
558 /* Write the data. */
559 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
560 != data[cnt].size)
561 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
562
563 /* Add the hash value to the hash table. */
564 md5hval = compute_hashval (data[cnt].sum, 16);
565
566 idx = md5hval % head->sumhash_size;
567 incr = 1 + md5hval % (head->sumhash_size - 2);
568
569 while (sumhashtab[idx].file_offset != 0)
570 {
571 idx += incr;
572 if (idx >= head->sumhash_size)
573 idx -= head->sumhash_size;
574 }
575
576 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
577 sumhashtab[idx].file_offset = file_offsets[cnt];
578
579 ++head->sumhash_used;
580 }
581
582
583 if (namehashent->locrec_offset == 0)
584 {
585 /* Add the name string. */
586 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
587 name, name_len + 1);
588 namehashent->name_offset = head->string_offset + head->string_used;
589 head->string_used += name_len + 1;
590
591 /* Allocate a name location record. */
592 namehashent->locrec_offset = (head->locrectab_offset
593 + (head->locrectab_used++
594 * sizeof (struct locrecent)));
595
596 namehashent->hashval = hval;
597
598 ++head->namehash_used;
599 }
600
601
602 /* Fill in the table with the locations of the locale data. */
603 locrecent = (struct locrecent *) ((char *) ah->addr
604 + namehashent->locrec_offset);
605 for (cnt = 0; cnt < __LC_LAST; ++cnt)
606 if (cnt != LC_ALL)
607 {
608 locrecent->record[cnt].offset = file_offsets[cnt];
609 locrecent->record[cnt].len = data[cnt].size;
610 }
611
612
613 /* Read the locale.alias file to see whether any matching record is
614 found. If an entry is available check whether it is already in
615 the archive. If this is the case check whether the new locale's
616 name is more specific than the one currently referred to by the
617 alias. */
618
619
620 return 0;
621}
622
623
624int
625add_locales_to_archive (nlist, list, replace)
626 size_t nlist;
627 char *list[];
628 bool replace;
629{
630 struct locarhandle ah;
631 int result = 0;
632
633 /* Open the archive. This call never returns if we cannot
634 successfully open the archive. */
635 open_archive (&ah);
636
637 while (nlist-- > 0)
638 {
639 const char *fname = *list++;
640 size_t fnamelen = strlen (fname);
641 struct stat64 st;
642 DIR *dirp;
643 struct dirent64 *d;
644 int seen;
645 locale_data_t data;
646 int cnt;
647
648 if (! be_quiet)
649 printf (_("Adding %s\n"), fname);
650
651 /* First see whether this really is a directory and whether it
652 contains all the require locale category files. */
653 if (stat64 (fname, &st) < 0)
654 {
655 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
656 strerror (errno));
657 continue;
658 }
659 if (!S_ISDIR (st.st_mode))
660 {
661 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
662 continue;
663 }
664
665 dirp = opendir (fname);
666 if (dirp == NULL)
667 {
668 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
669 fname, strerror (errno));
670 continue;
671 }
672
673 seen = 0;
674 while ((d = readdir64 (dirp)) != NULL)
675 {
676 for (cnt = 0; cnt < __LC_LAST; ++cnt)
677 if (cnt != LC_ALL)
678 if (strcmp (d->d_name, locnames[cnt]) == 0)
679 {
680 unsigned char d_type;
681
682 /* We have an object of the required name. If it's
683 a directory we have to look at a file with the
684 prefix "SYS_". Otherwise we have found what we
685 are looking for. */
686#ifdef _DIRENT_HAVE_D_TYPE
687 d_type = d->d_type;
688
689 if (d_type != DT_REG)
690#endif
691 {
692 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
693
694#ifdef _DIRENT_HAVE_D_TYPE
695 if (d_type == DT_UNKNOWN)
696#endif
697 {
698 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
699 d->d_name);
700
701 if (stat64 (fullname, &st) == -1)
702 /* We cannot stat the file, ignore it. */
703 break;
704
705 d_type = IFTODT (st.st_mode);
706 }
707
708 if (d_type == DT_DIR)
709 {
710 /* We have to do more tests. The file is a
711 directory and it therefore must contain a
712 regular file with the same name except a
713 "SYS_" prefix. */
714 strcpy (stpcpy (stpcpy (stpcpy (stpcpy (fullname,
715 fname),
716 "/"),
717 d->d_name),
718 "/SYS_"),
719 d->d_name);
720
721 if (stat64 (fullname, &st) == -1)
722 /* There is no SYS_* file or we cannot
723 access it. */
724 break;
725
726 d_type = IFTODT (st.st_mode);
727 }
728 }
729
730 /* If we found a regular file (eventually after
731 following a symlink) we are successful. */
732 if (d_type == DT_REG)
733 ++seen;
734 break;
735 }
736 }
737
738 closedir (dirp);
739
740 if (seen != __LC_LAST - 1)
741 {
742 /* We don't have all locale category files. Ignore the name. */
743 error (0, 0, _("incomplete set of locale files in \"%s\""),
744 fname);
745 continue;
746 }
747
748 /* Add the files to the archive. To do this we first compute
749 sizes and the MD5 sums of all the files. */
750 for (cnt = 0; cnt < __LC_LAST; ++cnt)
751 if (cnt != LC_ALL)
752 {
753 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
754 int fd;
755
756 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
757 fd = open64 (fullname, O_RDONLY);
758 if (fd == -1 || fstat64 (fd, &st) == -1)
759 {
760 /* Cannot read the file. */
761 if (fd != -1)
762 close (fd);
763 break;
764 }
765
766 if (S_ISDIR (st.st_mode))
767 {
768 close (fd);
769 strcpy (stpcpy (stpcpy (stpcpy (stpcpy (fullname, fname),
770 "/"),
771 locnames[cnt]),
772 "/SYS_"),
773 locnames[cnt]);
774
775 fd = open64 (fullname, O_RDONLY);
776 if (fd == -1 || fstat64 (fd, &st) == -1
777 || !S_ISREG (st.st_mode))
778 {
779 if (fd != -1)
780 close (fd);
781 break;
782 }
783 }
784
785 /* Map the file. */
786 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
787 fd, 0);
788 if (data[cnt].addr == MAP_FAILED)
789 {
790 /* Cannot map it. */
791 close (fd);
792 break;
793 }
794
795 data[cnt].size = st.st_size;
796 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
797
798 /* We don't need the file descriptor anymore. */
799 close (fd);
800 }
801
802 if (cnt != __LC_LAST)
803 {
804 while (cnt-- > 0)
805 if (cnt != LC_ALL)
806 munmap (data[cnt].addr, data[cnt].size);
807
808 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
809
810 continue;
811 }
812
813 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
814
815 for (cnt = 0; cnt < __LC_LAST; ++cnt)
816 if (cnt != LC_ALL)
817 munmap (data[cnt].addr, data[cnt].size);
818 }
819
820 /* We are done. */
821 close_archive (&ah);
822
823 return result;
824}
825
826
827int
828delete_locales_from_archive (nlist, list)
829 size_t nlist;
830 char *list[];
831{
832 struct locarhandle ah;
833 struct locarhead *head;
834 struct namehashent *namehashtab;
835
836 /* Open the archive. This call never returns if we cannot
837 successfully open the archive. */
838 open_archive (&ah);
839
840 head = ah.addr;
841 namehashtab = (struct namehashent *) ((char *) ah.addr
842 + head->namehash_offset);
843
844 while (nlist-- > 0)
845 {
846 const char *locname = *list++;
847 uint32_t hval;
848 unsigned int idx;
849 unsigned int incr;
850
851 /* Search for this locale in the archive. */
852 hval = compute_hashval (locname, strlen (locname));
853
854 idx = hval % head->namehash_size;
855 incr = 1 + hval % (head->namehash_size - 2);
856
857 /* If the name_offset field is zero this means this is no
858 deleted entry and therefore no entry can be found. */
859 while (namehashtab[idx].name_offset != 0)
860 {
861 if (namehashtab[idx].hashval == hval
862 && (strcmp (locname,
863 (char *) ah.addr + namehashtab[idx].name_offset)
864 == 0))
865 {
866 /* Found the entry. Now mark it as removed by zero-ing
867 the reference to the locale record. */
868 namehashtab[idx].locrec_offset = 0;
869 --head->namehash_used;
870 break;
871 }
872
873 idx += incr;
874 if (idx >= head->namehash_size)
875 idx -= head->namehash_size;
876 }
877
878 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
879 error (0, 0, _("locale \"%s\" not in archive"), locname);
880 }
881
882 close_archive (&ah);
883
884 return 0;
885}
886
887
888static int
889xstrcmp (const void *a, const void *b)
890{
891 return strcmp (*(const char **) a, *(const char **) b);
892}
893
894
895void
896show_archive_content (void)
897{
898 struct locarhandle ah;
899 struct locarhead *head;
900 struct namehashent *namehashtab;
901 int cnt;
902 char **names;
903 int used;
904
905 /* Open the archive. This call never returns if we cannot
906 successfully open the archive. */
907 open_archive (&ah);
908
909 head = ah.addr;
910
911 names = (char **) xmalloc (head->namehash_used * sizeof (char *));
912
913 namehashtab = (struct namehashent *) ((char *) ah.addr
914 + head->namehash_offset);
915 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
916 if (namehashtab[cnt].locrec_offset != 0)
917 {
918 assert (used < head->namehash_used);
919 names[used++] = ah.addr + namehashtab[cnt].name_offset;
920 }
921
922 /* Sort the names. */
923 qsort (names, used, sizeof (char *), xstrcmp);
924
925 for (cnt = 0; cnt < used; ++cnt)
926 puts (names[cnt]);
927
928 close_archive (&ah);
929
930 exit (EXIT_SUCCESS);
931}