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