Back to index

glibc  2.9
locarchive.c
Go to the documentation of this file.
00001 /* Copyright (C) 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published
00007    by the Free Software Foundation; version 2 of the License, or
00008    (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software Foundation,
00017    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 # include <config.h>
00021 #endif
00022 
00023 #include <assert.h>
00024 #include <dirent.h>
00025 #include <errno.h>
00026 #include <error.h>
00027 #include <fcntl.h>
00028 #include <inttypes.h>
00029 #include <libintl.h>
00030 #include <locale.h>
00031 #include <stdbool.h>
00032 #include <stdio.h>
00033 #include <stdio_ext.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <time.h>
00037 #include <unistd.h>
00038 #include <sys/mman.h>
00039 #include <sys/param.h>
00040 #include <sys/stat.h>
00041 
00042 #include "../../crypt/md5.h"
00043 #include "../localeinfo.h"
00044 #include "../locarchive.h"
00045 #include "localedef.h"
00046 
00047 /* Define the hash function.  We define the function as static inline.
00048    We must change the name so as not to conflict with simple-hash.h.  */
00049 #define compute_hashval static inline archive_hashval
00050 #define hashval_t uint32_t
00051 #include "hashval.h"
00052 #undef compute_hashval
00053 
00054 extern const char *output_prefix;
00055 
00056 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
00057 
00058 static const char *locnames[] =
00059   {
00060 #define DEFINE_CATEGORY(category, category_name, items, a) \
00061   [category] = category_name,
00062 #include "categories.def"
00063 #undef  DEFINE_CATEGORY
00064   };
00065 
00066 
00067 /* Size of the initial archive header.  */
00068 #define INITIAL_NUM_NAMES   900
00069 #define INITIAL_SIZE_STRINGS       7500
00070 #define INITIAL_NUM_LOCREC  420
00071 #define INITIAL_NUM_SUMS    2000
00072 
00073 
00074 static void
00075 create_archive (const char *archivefname, struct locarhandle *ah)
00076 {
00077   int fd;
00078   char fname[strlen (archivefname) + sizeof (".XXXXXX")];
00079   struct locarhead head;
00080   void *p;
00081   size_t total;
00082 
00083   strcpy (stpcpy (fname, archivefname), ".XXXXXX");
00084 
00085   /* Create a temporary file in the correct directory.  */
00086   fd = mkstemp (fname);
00087   if (fd == -1)
00088     error (EXIT_FAILURE, errno, _("cannot create temporary file"));
00089 
00090   /* Create the initial content of the archive.  */
00091   head.magic = AR_MAGIC;
00092   head.serial = 0;
00093   head.namehash_offset = sizeof (struct locarhead);
00094   head.namehash_used = 0;
00095   head.namehash_size = next_prime (INITIAL_NUM_NAMES);
00096 
00097   head.string_offset = (head.namehash_offset
00098                      + head.namehash_size * sizeof (struct namehashent));
00099   head.string_used = 0;
00100   head.string_size = INITIAL_SIZE_STRINGS;
00101 
00102   head.locrectab_offset = head.string_offset + head.string_size;
00103   head.locrectab_used = 0;
00104   head.locrectab_size = INITIAL_NUM_LOCREC;
00105 
00106   head.sumhash_offset = (head.locrectab_offset
00107                       + head.locrectab_size * sizeof (struct locrecent));
00108   head.sumhash_used = 0;
00109   head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
00110 
00111   total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
00112 
00113   /* Write out the header and create room for the other data structures.  */
00114   if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
00115     {
00116       int errval = errno;
00117       unlink (fname);
00118       error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
00119     }
00120 
00121   if (ftruncate64 (fd, total) != 0)
00122     {
00123       int errval = errno;
00124       unlink (fname);
00125       error (EXIT_FAILURE, errval, _("cannot resize archive file"));
00126     }
00127 
00128   /* Map the header and all the administration data structures.  */
00129   p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
00130   if (p == MAP_FAILED)
00131     {
00132       int errval = errno;
00133       unlink (fname);
00134       error (EXIT_FAILURE, errval, _("cannot map archive header"));
00135     }
00136 
00137   /* Now try to rename it.  We don't use the rename function since
00138      this would overwrite a file which has been created in
00139      parallel.  */
00140   if (link (fname, archivefname) == -1)
00141     {
00142       int errval = errno;
00143 
00144       /* We cannot use the just created file.  */
00145       close (fd);
00146       unlink (fname);
00147 
00148       if (errval == EEXIST)
00149        {
00150          /* There is already an archive.  Must have been a localedef run
00151             which happened in parallel.  Simply open this file then.  */
00152          open_archive (ah, false);
00153          return;
00154        }
00155 
00156       error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
00157     }
00158 
00159   /* Remove the temporary name.  */
00160   unlink (fname);
00161 
00162   /* Make the file globally readable.  */
00163   if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
00164     {
00165       int errval = errno;
00166       unlink (archivefname);
00167       error (EXIT_FAILURE, errval,
00168             _("cannot change mode of new locale archive"));
00169     }
00170 
00171   ah->fd = fd;
00172   ah->addr = p;
00173   ah->len = total;
00174 }
00175 
00176 
00177 /* This structure and qsort comparator function are used below to sort an
00178    old archive's locrec table in order of data position in the file.  */
00179 struct oldlocrecent
00180 {
00181   unsigned int cnt;
00182   struct locrecent *locrec;
00183 };
00184 
00185 static int
00186 oldlocrecentcmp (const void *a, const void *b)
00187 {
00188   struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
00189   struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
00190   uint32_t start_a = -1, end_a = 0;
00191   uint32_t start_b = -1, end_b = 0;
00192   int cnt;
00193 
00194   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00195     if (cnt != LC_ALL)
00196       {
00197        if (la->record[cnt].offset < start_a)
00198          start_a = la->record[cnt].offset;
00199        if (la->record[cnt].offset + la->record[cnt].len > end_a)
00200          end_a = la->record[cnt].offset + la->record[cnt].len;
00201       }
00202   assert (start_a != (uint32_t)-1);
00203   assert (end_a != 0);
00204 
00205   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00206     if (cnt != LC_ALL)
00207       {
00208        if (lb->record[cnt].offset < start_b)
00209          start_b = lb->record[cnt].offset;
00210        if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
00211          end_b = lb->record[cnt].offset + lb->record[cnt].len;
00212       }
00213   assert (start_b != (uint32_t)-1);
00214   assert (end_b != 0);
00215 
00216   if (start_a != start_b)
00217     return (int)start_a - (int)start_b;
00218   return (int)end_a - (int)end_b;
00219 }
00220 
00221 
00222 /* forward decls for below */
00223 static uint32_t add_locale (struct locarhandle *ah, const char *name,
00224                          locale_data_t data, bool replace);
00225 static void add_alias (struct locarhandle *ah, const char *alias,
00226                      bool replace, const char *oldname,
00227                      uint32_t *locrec_offset_p);
00228 
00229 static void
00230 enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
00231 {
00232   struct stat64 st;
00233   int fd;
00234   struct locarhead newhead;
00235   size_t total;
00236   void *p;
00237   unsigned int cnt, loccnt;
00238   struct namehashent *oldnamehashtab;
00239   struct locrecent *oldlocrectab;
00240   struct locarhandle new_ah;
00241   size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
00242   char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
00243   char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
00244 
00245   if (output_prefix)
00246     memcpy (archivefname, output_prefix, prefix_len);
00247   strcpy (archivefname + prefix_len, ARCHIVE_NAME);
00248   strcpy (stpcpy (fname, archivefname), ".XXXXXX");
00249 
00250   /* Not all of the old file has to be mapped.  Change this now this
00251      we will have to access the whole content.  */
00252   if (fstat64 (ah->fd, &st) != 0
00253       || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
00254                           MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
00255     error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
00256   ah->len = st.st_size;
00257 
00258   /* Create a temporary file in the correct directory.  */
00259   fd = mkstemp (fname);
00260   if (fd == -1)
00261     error (EXIT_FAILURE, errno, _("cannot create temporary file"));
00262 
00263   /* Copy the existing head information.  */
00264   newhead = *head;
00265 
00266   /* Create the new archive header.  The sizes of the various tables
00267      should be double from what is currently used.  */
00268   newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
00269                             newhead.namehash_size);
00270   if (verbose)
00271     printf ("name: size: %u, used: %d, new: size: %u\n",
00272            head->namehash_size, head->namehash_used, newhead.namehash_size);
00273 
00274   newhead.string_offset = (newhead.namehash_offset
00275                         + (newhead.namehash_size
00276                            * sizeof (struct namehashent)));
00277   /* Keep the string table size aligned to 4 bytes, so that
00278      all the struct { uint32_t } types following are happy.  */
00279   newhead.string_size = MAX ((2 * newhead.string_used + 3) & -4,
00280                           newhead.string_size);
00281 
00282   newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
00283   newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
00284                             newhead.locrectab_size);
00285 
00286   newhead.sumhash_offset = (newhead.locrectab_offset
00287                          + (newhead.locrectab_size
00288                             * sizeof (struct locrecent)));
00289   newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
00290                            newhead.sumhash_size);
00291 
00292   total = (newhead.sumhash_offset
00293           + newhead.sumhash_size * sizeof (struct sumhashent));
00294 
00295   /* The new file is empty now.  */
00296   newhead.namehash_used = 0;
00297   newhead.string_used = 0;
00298   newhead.locrectab_used = 0;
00299   newhead.sumhash_used = 0;
00300 
00301   /* Write out the header and create room for the other data structures.  */
00302   if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
00303       != sizeof (newhead))
00304     {
00305       int errval = errno;
00306       unlink (fname);
00307       error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
00308     }
00309 
00310   if (ftruncate64 (fd, total) != 0)
00311     {
00312       int errval = errno;
00313       unlink (fname);
00314       error (EXIT_FAILURE, errval, _("cannot resize archive file"));
00315     }
00316 
00317   /* Map the header and all the administration data structures.  */
00318   p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
00319   if (p == MAP_FAILED)
00320     {
00321       int errval = errno;
00322       unlink (fname);
00323       error (EXIT_FAILURE, errval, _("cannot map archive header"));
00324     }
00325 
00326   /* Lock the new file.  */
00327   if (lockf64 (fd, F_LOCK, total) != 0)
00328     {
00329       int errval = errno;
00330       unlink (fname);
00331       error (EXIT_FAILURE, errval, _("cannot lock new archive"));
00332     }
00333 
00334   new_ah.len = total;
00335   new_ah.addr = p;
00336   new_ah.fd = fd;
00337 
00338   /* Walk through the hash name hash table to find out what data is
00339      still referenced and transfer it into the new file.  */
00340   oldnamehashtab = (struct namehashent *) ((char *) ah->addr
00341                                       + head->namehash_offset);
00342   oldlocrectab = (struct locrecent *) ((char *) ah->addr
00343                                    + head->locrectab_offset);
00344 
00345   /* Sort the old locrec table in order of data position.  */
00346   struct oldlocrecent oldlocrecarray[head->namehash_size];
00347   for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
00348     if (oldnamehashtab[cnt].locrec_offset != 0)
00349       {
00350        oldlocrecarray[loccnt].cnt = cnt;
00351        oldlocrecarray[loccnt++].locrec
00352          = (struct locrecent *) ((char *) ah->addr
00353                               + oldnamehashtab[cnt].locrec_offset);
00354       }
00355   qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
00356         oldlocrecentcmp);
00357 
00358   uint32_t last_locrec_offset = 0;
00359   for (cnt = 0; cnt < loccnt; ++cnt)
00360     {
00361       /* Insert this entry in the new hash table.  */
00362       locale_data_t old_data;
00363       unsigned int idx;
00364       struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
00365 
00366       for (idx = 0; idx < __LC_LAST; ++idx)
00367        if (idx != LC_ALL)
00368          {
00369            old_data[idx].size = oldlocrec->record[idx].len;
00370            old_data[idx].addr
00371              = ((char *) ah->addr + oldlocrec->record[idx].offset);
00372 
00373            __md5_buffer (old_data[idx].addr, old_data[idx].size,
00374                        old_data[idx].sum);
00375          }
00376 
00377       if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec)
00378        {
00379          const char *oldname
00380            = ((char *) ah->addr
00381               + oldnamehashtab[oldlocrecarray[cnt - 1].cnt].name_offset);
00382 
00383          add_alias (&new_ah, 
00384                    ((char *) ah->addr
00385                     + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
00386                    0, oldname, &last_locrec_offset);
00387          continue;
00388        }
00389 
00390       last_locrec_offset =
00391        add_locale (&new_ah,
00392                   ((char *) ah->addr
00393                    + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
00394                   old_data, 0);
00395       if (last_locrec_offset == 0)
00396        error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
00397     }
00398 
00399   /* Make the file globally readable.  */
00400   if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
00401     {
00402       int errval = errno;
00403       unlink (fname);
00404       error (EXIT_FAILURE, errval,
00405             _("cannot change mode of resized locale archive"));
00406     }
00407 
00408   /* Rename the new file.  */
00409   if (rename (fname, archivefname) != 0)
00410     {
00411       int errval = errno;
00412       unlink (fname);
00413       error (EXIT_FAILURE, errval, _("cannot rename new archive"));
00414     }
00415 
00416   /* Close the old file.  */
00417   close_archive (ah);
00418 
00419   /* Add the information for the new one.  */
00420   *ah = new_ah;
00421 }
00422 
00423 
00424 void
00425 open_archive (struct locarhandle *ah, bool readonly)
00426 {
00427   struct stat64 st;
00428   struct stat64 st2;
00429   int fd;
00430   struct locarhead head;
00431   int retry = 0;
00432   size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
00433   char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
00434 
00435   if (output_prefix)
00436     memcpy (archivefname, output_prefix, prefix_len);
00437   strcpy (archivefname + prefix_len, ARCHIVE_NAME);
00438 
00439   while (1)
00440     {
00441       /* Open the archive.  We must have exclusive write access.  */
00442       fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
00443       if (fd == -1)
00444        {
00445          /* Maybe the file does not yet exist.  */
00446          if (errno == ENOENT)
00447            {
00448              if (readonly)
00449               {
00450                 static const struct locarhead nullhead =
00451                   {
00452                     .namehash_used = 0,
00453                     .namehash_offset = 0,
00454                     .namehash_size = 0
00455                   };
00456 
00457                 ah->addr = (void *) &nullhead;
00458                 ah->fd = -1;
00459               }
00460              else
00461               create_archive (archivefname, ah);
00462 
00463              return;
00464            }
00465          else
00466            error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
00467                  archivefname);
00468        }
00469 
00470       if (fstat64 (fd, &st) < 0)
00471        error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
00472               archivefname);
00473 
00474       if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
00475        {
00476          close (fd);
00477 
00478          if (retry++ < max_locarchive_open_retry)
00479            {
00480              struct timespec req;
00481 
00482              /* Wait for a bit.  */
00483              req.tv_sec = 0;
00484              req.tv_nsec = 1000000 * (random () % 500 + 1);
00485              (void) nanosleep (&req, NULL);
00486 
00487              continue;
00488            }
00489 
00490          error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
00491                archivefname);
00492        }
00493 
00494       /* One more check.  Maybe another process replaced the archive file
00495         with a new, larger one since we opened the file.  */
00496       if (stat64 (archivefname, &st2) == -1
00497          || st.st_dev != st2.st_dev
00498          || st.st_ino != st2.st_ino)
00499        {
00500          (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
00501          close (fd);
00502          continue;
00503        }
00504 
00505       /* Leave the loop.  */
00506       break;
00507     }
00508 
00509   /* Read the header.  */
00510   if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
00511     {
00512       (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
00513       error (EXIT_FAILURE, errno, _("cannot read archive header"));
00514     }
00515 
00516   ah->fd = fd;
00517   ah->len = (head.sumhash_offset
00518             + head.sumhash_size * sizeof (struct sumhashent));
00519 
00520   /* Now we know how large the administrative information part is.
00521      Map all of it.  */
00522   ah->addr = mmap64 (NULL, ah->len, PROT_READ | (readonly ? 0 : PROT_WRITE),
00523                    MAP_SHARED, fd, 0);
00524   if (ah->addr == MAP_FAILED)
00525     {
00526       (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
00527       error (EXIT_FAILURE, errno, _("cannot map archive header"));
00528     }
00529 }
00530 
00531 
00532 void
00533 close_archive (struct locarhandle *ah)
00534 {
00535   if (ah->fd != -1)
00536     {
00537       munmap (ah->addr, ah->len);
00538       close (ah->fd);
00539     }
00540 }
00541 
00542 #include "../../intl/explodename.c"
00543 #include "../../intl/l10nflist.c"
00544 
00545 static struct namehashent *
00546 insert_name (struct locarhandle *ah,
00547             const char *name, size_t name_len, bool replace)
00548 {
00549   const struct locarhead *const head = ah->addr;
00550   struct namehashent *namehashtab
00551     = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
00552   unsigned int insert_idx, idx, incr;
00553 
00554   /* Hash value of the locale name.  */
00555   uint32_t hval = archive_hashval (name, name_len);
00556 
00557   insert_idx = -1;
00558   idx = hval % head->namehash_size;
00559   incr = 1 + hval % (head->namehash_size - 2);
00560 
00561   /* If the name_offset field is zero this means this is a
00562      deleted entry and therefore no entry can be found.  */
00563   while (namehashtab[idx].name_offset != 0)
00564     {
00565       if (namehashtab[idx].hashval == hval
00566          && strcmp (name,
00567                    (char *) ah->addr + namehashtab[idx].name_offset) == 0)
00568        {
00569          /* Found the entry.  */
00570          if (namehashtab[idx].locrec_offset != 0 && ! replace)
00571            {
00572              if (! be_quiet)
00573               error (0, 0, _("locale '%s' already exists"), name);
00574              return NULL;
00575            }
00576 
00577          break;
00578        }
00579 
00580       if (namehashtab[idx].hashval == hval && ! be_quiet)
00581        {
00582          error (0, 0, "hash collision (%u) %s, %s",
00583                hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
00584        }
00585 
00586       /* Remember the first place we can insert the new entry.  */
00587       if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
00588        insert_idx = idx;
00589 
00590       idx += incr;
00591       if (idx >= head->namehash_size)
00592        idx -= head->namehash_size;
00593     }
00594 
00595   /* Add as early as possible.  */
00596   if (insert_idx != -1)
00597     idx = insert_idx;
00598 
00599   namehashtab[idx].hashval = hval; /* no-op if replacing an old entry.  */
00600   return &namehashtab[idx];
00601 }
00602 
00603 static void
00604 add_alias (struct locarhandle *ah, const char *alias, bool replace,
00605           const char *oldname, uint32_t *locrec_offset_p)
00606 {
00607   uint32_t locrec_offset = *locrec_offset_p;
00608   struct locarhead *head = ah->addr;
00609   const size_t name_len = strlen (alias);
00610   struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
00611                                            replace);
00612   if (namehashent == NULL && ! replace)
00613     return;
00614 
00615   if (namehashent->name_offset == 0)
00616     {
00617       /* We are adding a new hash entry for this alias.
00618         Determine whether we have to resize the file.  */
00619       if (head->string_used + name_len + 1 > head->string_size
00620          || 100 * head->namehash_used > 75 * head->namehash_size)
00621        {
00622          /* The current archive is not large enough.  */
00623          enlarge_archive (ah, head);
00624 
00625          /* The locrecent might have moved, so we have to look up
00626             the old name afresh.  */
00627          namehashent = insert_name (ah, oldname, strlen (oldname), true);
00628          assert (namehashent->name_offset != 0);
00629          assert (namehashent->locrec_offset != 0);
00630          *locrec_offset_p = namehashent->locrec_offset;
00631 
00632          /* Tail call to try the whole thing again.  */
00633          add_alias (ah, alias, replace, oldname, locrec_offset_p);
00634          return;
00635        }
00636 
00637       /* Add the name string.  */
00638       memcpy (ah->addr + head->string_offset + head->string_used,
00639              alias, name_len + 1);
00640       namehashent->name_offset = head->string_offset + head->string_used;
00641       head->string_used += name_len + 1;
00642 
00643       ++head->namehash_used;
00644     }
00645 
00646   if (namehashent->locrec_offset != 0)
00647     {
00648       /* Replacing an existing entry.
00649         Mark that we are no longer using the old locrecent.  */
00650       struct locrecent *locrecent
00651        = (struct locrecent *) ((char *) ah->addr
00652                             + namehashent->locrec_offset);
00653       --locrecent->refs;
00654     }
00655 
00656   /* Point this entry at the locrecent installed for the main name.  */
00657   namehashent->locrec_offset = locrec_offset;
00658 }
00659 
00660 static int                  /* qsort comparator used below */
00661 cmpcategorysize (const void *a, const void *b)
00662 {
00663   if (*(const void **) a == NULL)
00664     return 1;
00665   if (*(const void **) b == NULL)
00666     return -1;
00667   return ((*(const struct locale_category_data **) a)->size
00668          - (*(const struct locale_category_data **) b)->size);
00669 }
00670 
00671 /* Check the content of the archive for duplicates.  Add the content
00672    of the files if necessary.  Returns the locrec_offset.  */
00673 static uint32_t
00674 add_locale (struct locarhandle *ah,
00675            const char *name, locale_data_t data, bool replace)
00676 {
00677   /* First look for the name.  If it already exists and we are not
00678      supposed to replace it don't do anything.  If it does not exist
00679      we have to allocate a new locale record.  */
00680   size_t name_len = strlen (name);
00681   uint32_t file_offsets[__LC_LAST];
00682   unsigned int num_new_offsets = 0;
00683   struct sumhashent *sumhashtab;
00684   uint32_t hval;
00685   unsigned int cnt, idx;
00686   struct locarhead *head;
00687   struct namehashent *namehashent;
00688   unsigned int incr;
00689   struct locrecent *locrecent;
00690   off64_t lastoffset;
00691   char *ptr;
00692   struct locale_category_data *size_order[__LC_LAST];
00693   const size_t pagesz = getpagesize ();
00694   int small_mask;
00695 
00696   head = ah->addr;
00697   sumhashtab = (struct sumhashent *) ((char *) ah->addr
00698                                   + head->sumhash_offset);
00699 
00700   memset (file_offsets, 0, sizeof (file_offsets));
00701 
00702   size_order[LC_ALL] = NULL;
00703   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00704     if (cnt != LC_ALL)
00705       size_order[cnt] = &data[cnt];
00706 
00707   /* Sort the array in ascending order of data size.  */
00708   qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
00709 
00710   small_mask = 0;
00711   data[LC_ALL].size = 0;
00712   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00713     if (size_order[cnt] != NULL)
00714       {
00715        const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
00716        if (data[LC_ALL].size + rounded_size > 2 * pagesz)
00717          {
00718            /* This category makes the small-categories block
00719               stop being small, so this is the end of the road.  */
00720            do
00721              size_order[cnt++] = NULL;
00722            while (cnt < __LC_LAST);
00723            break;
00724          }
00725        data[LC_ALL].size += rounded_size;
00726        small_mask |= 1 << (size_order[cnt] - data);
00727       }
00728 
00729   /* Copy the data for all the small categories into the LC_ALL
00730      pseudo-category.  */
00731 
00732   data[LC_ALL].addr = alloca (data[LC_ALL].size);
00733   memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
00734 
00735   ptr = data[LC_ALL].addr;
00736   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00737     if (small_mask & (1 << cnt))
00738       {
00739        memcpy (ptr, data[cnt].addr, data[cnt].size);
00740        ptr += (data[cnt].size + 15) & -16;
00741       }
00742   __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
00743 
00744   /* For each locale category data set determine whether the same data
00745      is already somewhere in the archive.  */
00746   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00747     if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
00748       {
00749        ++num_new_offsets;
00750 
00751        /* Compute the hash value of the checksum to determine a
00752           starting point for the search in the MD5 hash value
00753           table.  */
00754        hval = archive_hashval (data[cnt].sum, 16);
00755 
00756        idx = hval % head->sumhash_size;
00757        incr = 1 + hval % (head->sumhash_size - 2);
00758 
00759        while (sumhashtab[idx].file_offset != 0)
00760          {
00761            if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
00762              {
00763               /* Found it.  */
00764               file_offsets[cnt] = sumhashtab[idx].file_offset;
00765               --num_new_offsets;
00766               break;
00767              }
00768 
00769            idx += incr;
00770            if (idx >= head->sumhash_size)
00771              idx -= head->sumhash_size;
00772          }
00773       }
00774 
00775   /* Find a slot for the locale name in the hash table.  */
00776   namehashent = insert_name (ah, name, name_len, replace);
00777   if (namehashent == NULL)  /* Already exists and !REPLACE.  */
00778     return 0;
00779 
00780   /* Determine whether we have to resize the file.  */
00781   if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
00782       || (namehashent->locrec_offset == 0
00783          && (head->locrectab_used == head->locrectab_size
00784              || head->string_used + name_len + 1 > head->string_size
00785              || 100 * head->namehash_used > 75 * head->namehash_size)))
00786     {
00787       /* The current archive is not large enough.  */
00788       enlarge_archive (ah, head);
00789       return add_locale (ah, name, data, replace);
00790     }
00791 
00792   /* Add the locale data which is not yet in the archive.  */
00793   for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
00794     if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
00795        && file_offsets[cnt] == 0)
00796       {
00797        /* The data for this section is not yet available in the
00798           archive.  Append it.  */
00799        off64_t lastpos;
00800        uint32_t md5hval;
00801 
00802        lastpos = lseek64 (ah->fd, 0, SEEK_END);
00803        if (lastpos == (off64_t) -1)
00804          error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
00805 
00806        /* If block of small categories would cross page boundary,
00807           align it unless it immediately follows a large category.  */
00808        if (cnt == LC_ALL && lastoffset != lastpos
00809            && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
00810                & -pagesz)
00811               > ((data[cnt].size + pagesz - 1) & -pagesz)))
00812          {
00813            size_t sz = pagesz - (lastpos & (pagesz - 1));
00814            char *zeros = alloca (sz);
00815 
00816            memset (zeros, 0, sz);
00817            if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
00818              error (EXIT_FAILURE, errno,
00819                    _("cannot add to locale archive"));
00820 
00821            lastpos += sz;
00822          }
00823 
00824        /* Align all data to a 16 byte boundary.  */
00825        if ((lastpos & 15) != 0)
00826          {
00827            static const char zeros[15] = { 0, };
00828 
00829            if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
00830               != 16 - (lastpos & 15))
00831              error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
00832 
00833            lastpos += 16 - (lastpos & 15);
00834          }
00835 
00836        /* Remember the position.  */
00837        file_offsets[cnt] = lastpos;
00838        lastoffset = lastpos + data[cnt].size;
00839 
00840        /* Write the data.  */
00841        if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
00842            != data[cnt].size)
00843          error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
00844 
00845        /* Add the hash value to the hash table.  */
00846        md5hval = archive_hashval (data[cnt].sum, 16);
00847 
00848        idx = md5hval % head->sumhash_size;
00849        incr = 1 + md5hval % (head->sumhash_size - 2);
00850 
00851        while (sumhashtab[idx].file_offset != 0)
00852          {
00853            idx += incr;
00854            if (idx >= head->sumhash_size)
00855              idx -= head->sumhash_size;
00856          }
00857 
00858        memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
00859        sumhashtab[idx].file_offset = file_offsets[cnt];
00860 
00861        ++head->sumhash_used;
00862       }
00863 
00864   lastoffset = file_offsets[LC_ALL];
00865   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00866     if (small_mask & (1 << cnt))
00867       {
00868        file_offsets[cnt] = lastoffset;
00869        lastoffset += (data[cnt].size + 15) & -16;
00870       }
00871 
00872   if (namehashent->name_offset == 0)
00873     {
00874       /* Add the name string.  */
00875       memcpy ((char *) ah->addr + head->string_offset + head->string_used,
00876              name, name_len + 1);
00877       namehashent->name_offset = head->string_offset + head->string_used;
00878       head->string_used += name_len + 1;
00879       ++head->namehash_used;
00880     }
00881 
00882   if (namehashent->locrec_offset == 0)
00883     {
00884       /* Allocate a name location record.  */
00885       namehashent->locrec_offset = (head->locrectab_offset
00886                                 + (head->locrectab_used++
00887                                    * sizeof (struct locrecent)));
00888       locrecent = (struct locrecent *) ((char *) ah->addr
00889                                    + namehashent->locrec_offset);
00890       locrecent->refs = 1;
00891     }
00892   else
00893     {
00894       /* If there are other aliases pointing to this locrecent,
00895         we still need a new one.  If not, reuse the old one.  */
00896 
00897       locrecent = (struct locrecent *) ((char *) ah->addr
00898                                    + namehashent->locrec_offset);
00899       if (locrecent->refs > 1)
00900        {
00901          --locrecent->refs;
00902          namehashent->locrec_offset = (head->locrectab_offset
00903                                    + (head->locrectab_used++
00904                                       * sizeof (struct locrecent)));
00905          locrecent = (struct locrecent *) ((char *) ah->addr
00906                                        + namehashent->locrec_offset);
00907          locrecent->refs = 1;
00908        }
00909     }
00910 
00911   /* Fill in the table with the locations of the locale data.  */
00912   for (cnt = 0; cnt < __LC_LAST; ++cnt)
00913     {
00914       locrecent->record[cnt].offset = file_offsets[cnt];
00915       locrecent->record[cnt].len = data[cnt].size;
00916     }
00917 
00918   return namehashent->locrec_offset;
00919 }
00920 
00921 
00922 /* Check the content of the archive for duplicates.  Add the content
00923    of the files if necessary.  Add all the names, possibly overwriting
00924    old files.  */
00925 int
00926 add_locale_to_archive (ah, name, data, replace)
00927      struct locarhandle *ah;
00928      const char *name;
00929      locale_data_t data;
00930      bool replace;
00931 {
00932   char *normalized_name = NULL;
00933   uint32_t locrec_offset;
00934 
00935   /* First analyze the name to decide how to archive it.  */
00936   const char *language;
00937   const char *modifier;
00938   const char *territory;
00939   const char *codeset;
00940   const char *normalized_codeset;
00941   int mask = _nl_explode_name (strdupa (name),
00942                             &language, &modifier, &territory,
00943                             &codeset, &normalized_codeset);
00944 
00945   if (mask & XPG_NORM_CODESET)
00946     /* This name contains a codeset in unnormalized form.
00947        We will store it in the archive with a normalized name.  */
00948     asprintf (&normalized_name, "%s%s%s.%s%s%s",
00949              language, territory == NULL ? "" : "_", territory ?: "",
00950              (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
00951              modifier == NULL ? "" : "@", modifier ?: "");
00952 
00953   /* This call does the main work.  */
00954   locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
00955   if (locrec_offset == 0)
00956     {
00957       free (normalized_name);
00958       if (mask & XPG_NORM_CODESET)
00959        free ((char *) normalized_codeset);
00960       return -1;
00961     }
00962 
00963   if ((mask & XPG_CODESET) == 0)
00964     {
00965       /* This name lacks a codeset, so determine the locale's codeset and
00966         add an alias for its name with normalized codeset appended.  */
00967 
00968       const struct
00969       {
00970        unsigned int magic;
00971        unsigned int nstrings;
00972        unsigned int strindex[0];
00973       } *filedata = data[LC_CTYPE].addr;
00974       codeset = (char *) filedata
00975        + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
00976       char *normalized_codeset_name = NULL;
00977 
00978       normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
00979       mask |= XPG_NORM_CODESET;
00980 
00981       asprintf (&normalized_codeset_name, "%s%s%s.%s%s%s",
00982               language, territory == NULL ? "" : "_", territory ?: "",
00983               normalized_codeset,
00984               modifier == NULL ? "" : "@", modifier ?: "");
00985 
00986       add_alias (ah, normalized_codeset_name, replace,
00987                normalized_name ?: name, &locrec_offset);
00988       free (normalized_codeset_name);
00989     }
00990 
00991   /* Now read the locale.alias files looking for lines whose
00992      right hand side matches our name after normalization.  */
00993   if (alias_file != NULL)
00994     {
00995       FILE *fp;
00996       fp = fopen (alias_file, "rm");
00997       if (fp == NULL)
00998        error (1, errno, _("locale alias file `%s' not found"),
00999               alias_file);
01000 
01001       /* No threads present.  */
01002       __fsetlocking (fp, FSETLOCKING_BYCALLER);
01003 
01004       while (! feof_unlocked (fp))
01005        {
01006          /* It is a reasonable approach to use a fix buffer here
01007             because
01008             a) we are only interested in the first two fields
01009             b) these fields must be usable as file names and so must
01010             not be that long  */
01011          char buf[BUFSIZ];
01012          char *alias;
01013          char *value;
01014          char *cp;
01015 
01016          if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
01017            /* EOF reached.  */
01018            break;
01019 
01020          cp = buf;
01021          /* Ignore leading white space.  */
01022          while (isspace (cp[0]) && cp[0] != '\n')
01023            ++cp;
01024 
01025          /* A leading '#' signals a comment line.  */
01026          if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
01027            {
01028              alias = cp++;
01029              while (cp[0] != '\0' && !isspace (cp[0]))
01030               ++cp;
01031              /* Terminate alias name.  */
01032              if (cp[0] != '\0')
01033               *cp++ = '\0';
01034 
01035              /* Now look for the beginning of the value.  */
01036              while (isspace (cp[0]))
01037               ++cp;
01038 
01039              if (cp[0] != '\0')
01040               {
01041                 value = cp++;
01042                 while (cp[0] != '\0' && !isspace (cp[0]))
01043                   ++cp;
01044                 /* Terminate value.  */
01045                 if (cp[0] == '\n')
01046                   {
01047                     /* This has to be done to make the following
01048                       test for the end of line possible.  We are
01049                       looking for the terminating '\n' which do not
01050                       overwrite here.  */
01051                     *cp++ = '\0';
01052                     *cp = '\n';
01053                   }
01054                 else if (cp[0] != '\0')
01055                   *cp++ = '\0';
01056 
01057                 /* Does this alias refer to our locale?  We will
01058                    normalize the right hand side and compare the
01059                    elements of the normalized form.  */
01060                 {
01061                   const char *rhs_language;
01062                   const char *rhs_modifier;
01063                   const char *rhs_territory;
01064                   const char *rhs_codeset;
01065                   const char *rhs_normalized_codeset;
01066                   int rhs_mask = _nl_explode_name (value,
01067                                                &rhs_language,
01068                                                &rhs_modifier,
01069                                                &rhs_territory,
01070                                                &rhs_codeset,
01071                                                &rhs_normalized_codeset);
01072                   if (!strcmp (language, rhs_language)
01073                      && ((rhs_mask & XPG_CODESET)
01074                          /* He has a codeset, it must match normalized.  */
01075                          ? !strcmp ((mask & XPG_NORM_CODESET)
01076                                    ? normalized_codeset : codeset,
01077                                    (rhs_mask & XPG_NORM_CODESET)
01078                                    ? rhs_normalized_codeset : rhs_codeset)
01079                          /* He has no codeset, we must also have none.  */
01080                          : (mask & XPG_CODESET) == 0)
01081                      /* Codeset (or lack thereof) matches.  */
01082                      && !strcmp (territory ?: "", rhs_territory ?: "")
01083                      && !strcmp (modifier ?: "", rhs_modifier ?: ""))
01084                     /* We have a winner.  */
01085                     add_alias (ah, alias, replace,
01086                              normalized_name ?: name, &locrec_offset);
01087                   if (rhs_mask & XPG_NORM_CODESET)
01088                     free ((char *) rhs_normalized_codeset);
01089                 }
01090               }
01091            }
01092 
01093          /* Possibly not the whole line fits into the buffer.
01094             Ignore the rest of the line.  */
01095          while (strchr (cp, '\n') == NULL)
01096            {
01097              cp = buf;
01098              if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
01099               /* Make sure the inner loop will be left.  The outer
01100                  loop will exit at the `feof' test.  */
01101               *cp = '\n';
01102            }
01103        }
01104 
01105       fclose (fp);
01106     }
01107 
01108   free (normalized_name);
01109 
01110   if (mask & XPG_NORM_CODESET)
01111     free ((char *) normalized_codeset);
01112 
01113   return 0;
01114 }
01115 
01116 
01117 int
01118 add_locales_to_archive (nlist, list, replace)
01119      size_t nlist;
01120      char *list[];
01121      bool replace;
01122 {
01123   struct locarhandle ah;
01124   int result = 0;
01125 
01126   /* Open the archive.  This call never returns if we cannot
01127      successfully open the archive.  */
01128   open_archive (&ah, false);
01129 
01130   while (nlist-- > 0)
01131     {
01132       const char *fname = *list++;
01133       size_t fnamelen = strlen (fname);
01134       struct stat64 st;
01135       DIR *dirp;
01136       struct dirent64 *d;
01137       int seen;
01138       locale_data_t data;
01139       int cnt;
01140 
01141       if (! be_quiet)
01142        printf (_("Adding %s\n"), fname);
01143 
01144       /* First see whether this really is a directory and whether it
01145         contains all the require locale category files.  */
01146       if (stat64 (fname, &st) < 0)
01147        {
01148          error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
01149                strerror (errno));
01150          continue;
01151        }
01152       if (!S_ISDIR (st.st_mode))
01153        {
01154          error (0, 0, _("\"%s\" is no directory; ignored"), fname);
01155          continue;
01156        }
01157 
01158       dirp = opendir (fname);
01159       if (dirp == NULL)
01160        {
01161          error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
01162                fname, strerror (errno));
01163          continue;
01164        }
01165 
01166       seen = 0;
01167       while ((d = readdir64 (dirp)) != NULL)
01168        {
01169          for (cnt = 0; cnt < __LC_LAST; ++cnt)
01170            if (cnt != LC_ALL)
01171              if (strcmp (d->d_name, locnames[cnt]) == 0)
01172               {
01173                 unsigned char d_type;
01174 
01175                 /* We have an object of the required name.  If it's
01176                    a directory we have to look at a file with the
01177                    prefix "SYS_".  Otherwise we have found what we
01178                    are looking for.  */
01179 #ifdef _DIRENT_HAVE_D_TYPE
01180                 d_type = d->d_type;
01181 
01182                 if (d_type != DT_REG)
01183 #endif
01184                   {
01185                     char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
01186 
01187 #ifdef _DIRENT_HAVE_D_TYPE
01188                     if (d_type == DT_UNKNOWN)
01189 #endif
01190                      {
01191                        strcpy (stpcpy (stpcpy (fullname, fname), "/"),
01192                               d->d_name);
01193 
01194                        if (stat64 (fullname, &st) == -1)
01195                          /* We cannot stat the file, ignore it.  */
01196                          break;
01197 
01198                        d_type = IFTODT (st.st_mode);
01199                      }
01200 
01201                     if (d_type == DT_DIR)
01202                      {
01203                        /* We have to do more tests.  The file is a
01204                           directory and it therefore must contain a
01205                           regular file with the same name except a
01206                           "SYS_" prefix.  */
01207                        char *t = stpcpy (stpcpy (fullname, fname), "/");
01208                        strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
01209                               d->d_name);
01210 
01211                        if (stat64 (fullname, &st) == -1)
01212                          /* There is no SYS_* file or we cannot
01213                             access it.  */
01214                          break;
01215 
01216                        d_type = IFTODT (st.st_mode);
01217                      }
01218                   }
01219 
01220                 /* If we found a regular file (eventually after
01221                    following a symlink) we are successful.  */
01222                 if (d_type == DT_REG)
01223                   ++seen;
01224                 break;
01225               }
01226        }
01227 
01228       closedir (dirp);
01229 
01230       if (seen != __LC_LAST - 1)
01231        {
01232          /* We don't have all locale category files.  Ignore the name.  */
01233          error (0, 0, _("incomplete set of locale files in \"%s\""),
01234                fname);
01235          continue;
01236        }
01237 
01238       /* Add the files to the archive.  To do this we first compute
01239         sizes and the MD5 sums of all the files.  */
01240       for (cnt = 0; cnt < __LC_LAST; ++cnt)
01241        if (cnt != LC_ALL)
01242          {
01243            char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
01244            int fd;
01245 
01246            strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
01247            fd = open64 (fullname, O_RDONLY);
01248            if (fd == -1 || fstat64 (fd, &st) == -1)
01249              {
01250               /* Cannot read the file.  */
01251               if (fd != -1)
01252                 close (fd);
01253               break;
01254              }
01255 
01256            if (S_ISDIR (st.st_mode))
01257              {
01258               char *t;
01259               close (fd);
01260               t = stpcpy (stpcpy (fullname, fname), "/");
01261               strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
01262                      locnames[cnt]);
01263 
01264               fd = open64 (fullname, O_RDONLY);
01265               if (fd == -1 || fstat64 (fd, &st) == -1
01266                   || !S_ISREG (st.st_mode))
01267                 {
01268                   if (fd != -1)
01269                     close (fd);
01270                   break;
01271                 }
01272              }
01273 
01274            /* Map the file.  */
01275            data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
01276                                  fd, 0);
01277            if (data[cnt].addr == MAP_FAILED)
01278              {
01279               /* Cannot map it.  */
01280               close (fd);
01281               break;
01282              }
01283 
01284            data[cnt].size = st.st_size;
01285            __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
01286 
01287            /* We don't need the file descriptor anymore.  */
01288            close (fd);
01289          }
01290 
01291       if (cnt != __LC_LAST)
01292        {
01293          while (cnt-- > 0)
01294            if (cnt != LC_ALL)
01295              munmap (data[cnt].addr, data[cnt].size);
01296 
01297          error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
01298 
01299          continue;
01300        }
01301 
01302       result |= add_locale_to_archive (&ah, basename (fname), data, replace);
01303 
01304       for (cnt = 0; cnt < __LC_LAST; ++cnt)
01305        if (cnt != LC_ALL)
01306          munmap (data[cnt].addr, data[cnt].size);
01307     }
01308 
01309   /* We are done.  */
01310   close_archive (&ah);
01311 
01312   return result;
01313 }
01314 
01315 
01316 int
01317 delete_locales_from_archive (nlist, list)
01318      size_t nlist;
01319      char *list[];
01320 {
01321   struct locarhandle ah;
01322   struct locarhead *head;
01323   struct namehashent *namehashtab;
01324 
01325   /* Open the archive.  This call never returns if we cannot
01326      successfully open the archive.  */
01327   open_archive (&ah, false);
01328 
01329   head = ah.addr;
01330   namehashtab = (struct namehashent *) ((char *) ah.addr
01331                                    + head->namehash_offset);
01332 
01333   while (nlist-- > 0)
01334     {
01335       const char *locname = *list++;
01336       uint32_t hval;
01337       unsigned int idx;
01338       unsigned int incr;
01339 
01340       /* Search for this locale in the archive.  */
01341       hval = archive_hashval (locname, strlen (locname));
01342 
01343       idx = hval % head->namehash_size;
01344       incr = 1 + hval % (head->namehash_size - 2);
01345 
01346       /* If the name_offset field is zero this means this is no
01347         deleted entry and therefore no entry can be found.  */
01348       while (namehashtab[idx].name_offset != 0)
01349        {
01350          if (namehashtab[idx].hashval == hval
01351              && (strcmp (locname,
01352                        (char *) ah.addr + namehashtab[idx].name_offset)
01353                 == 0))
01354            {
01355              /* Found the entry.  Now mark it as removed by zero-ing
01356                the reference to the locale record.  */
01357              namehashtab[idx].locrec_offset = 0;
01358              break;
01359            }
01360 
01361          idx += incr;
01362          if (idx >= head->namehash_size)
01363            idx -= head->namehash_size;
01364        }
01365 
01366       if (namehashtab[idx].name_offset == 0 && ! be_quiet)
01367        error (0, 0, _("locale \"%s\" not in archive"), locname);
01368     }
01369 
01370   close_archive (&ah);
01371 
01372   return 0;
01373 }
01374 
01375 
01376 struct nameent
01377 {
01378   char *name;
01379   uint32_t locrec_offset;
01380 };
01381 
01382 
01383 struct dataent
01384 {
01385   const unsigned char *sum;
01386   uint32_t file_offset;
01387   uint32_t nlink;
01388 };
01389 
01390 
01391 static int
01392 nameentcmp (const void *a, const void *b)
01393 {
01394   return strcmp (((const struct nameent *) a)->name,
01395                ((const struct nameent *) b)->name);
01396 }
01397 
01398 
01399 static int
01400 dataentcmp (const void *a, const void *b)
01401 {
01402   if (((const struct dataent *) a)->file_offset
01403       < ((const struct dataent *) b)->file_offset)
01404     return -1;
01405 
01406   if (((const struct dataent *) a)->file_offset
01407       > ((const struct dataent *) b)->file_offset)
01408     return 1;
01409 
01410   return 0;
01411 }
01412 
01413 
01414 void
01415 show_archive_content (int verbose)
01416 {
01417   struct locarhandle ah;
01418   struct locarhead *head;
01419   struct namehashent *namehashtab;
01420   struct nameent *names;
01421   size_t cnt, used;
01422 
01423   /* Open the archive.  This call never returns if we cannot
01424      successfully open the archive.  */
01425   open_archive (&ah, true);
01426 
01427   head = ah.addr;
01428 
01429   names = (struct nameent *) xmalloc (head->namehash_used
01430                                   * sizeof (struct nameent));
01431 
01432   namehashtab = (struct namehashent *) ((char *) ah.addr
01433                                    + head->namehash_offset);
01434   for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
01435     if (namehashtab[cnt].locrec_offset != 0)
01436       {
01437        assert (used < head->namehash_used);
01438        names[used].name = ah.addr + namehashtab[cnt].name_offset;
01439        names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
01440       }
01441 
01442   /* Sort the names.  */
01443   qsort (names, used, sizeof (struct nameent), nameentcmp);
01444 
01445   if (verbose)
01446     {
01447       struct dataent *files;
01448       struct sumhashent *sumhashtab;
01449       int sumused;
01450 
01451       files = (struct dataent *) xmalloc (head->sumhash_used
01452                                      * sizeof (struct dataent));
01453 
01454       sumhashtab = (struct sumhashent *) ((char *) ah.addr
01455                                      + head->sumhash_offset);
01456       for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
01457        if (sumhashtab[cnt].file_offset != 0)
01458          {
01459            assert (sumused < head->sumhash_used);
01460            files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
01461            files[sumused].file_offset = sumhashtab[cnt].file_offset;
01462            files[sumused++].nlink = 0;
01463          }
01464 
01465       /* Sort by file locations.  */
01466       qsort (files, sumused, sizeof (struct dataent), dataentcmp);
01467 
01468       /* Compute nlink fields.  */
01469       for (cnt = 0; cnt < used; ++cnt)
01470        {
01471          struct locrecent *locrec;
01472          int idx;
01473 
01474          locrec = (struct locrecent *) ((char *) ah.addr
01475                                     + names[cnt].locrec_offset);
01476          for (idx = 0; idx < __LC_LAST; ++idx)
01477            if (locrec->record[LC_ALL].offset != 0
01478               ? (idx == LC_ALL
01479                  || (locrec->record[idx].offset
01480                      < locrec->record[LC_ALL].offset)
01481                  || (locrec->record[idx].offset + locrec->record[idx].len
01482                      > (locrec->record[LC_ALL].offset
01483                        + locrec->record[LC_ALL].len)))
01484               : idx != LC_ALL)
01485              {
01486               struct dataent *data, dataent;
01487 
01488               dataent.file_offset = locrec->record[idx].offset;
01489               data = (struct dataent *) bsearch (&dataent, files, sumused,
01490                                              sizeof (struct dataent),
01491                                              dataentcmp);
01492               assert (data != NULL);
01493               ++data->nlink;
01494              }
01495        }
01496 
01497       /* Print it.  */
01498       for (cnt = 0; cnt < used; ++cnt)
01499        {
01500          struct locrecent *locrec;
01501          int idx, i;
01502 
01503          locrec = (struct locrecent *) ((char *) ah.addr
01504                                     + names[cnt].locrec_offset);
01505          for (idx = 0; idx < __LC_LAST; ++idx)
01506            if (idx != LC_ALL)
01507              {
01508               struct dataent *data, dataent;
01509 
01510               dataent.file_offset = locrec->record[idx].offset;
01511               if (locrec->record[LC_ALL].offset != 0
01512                   && dataent.file_offset >= locrec->record[LC_ALL].offset
01513                   && (dataent.file_offset + locrec->record[idx].len
01514                      <= (locrec->record[LC_ALL].offset
01515                          + locrec->record[LC_ALL].len)))
01516                 dataent.file_offset = locrec->record[LC_ALL].offset;
01517 
01518               data = (struct dataent *) bsearch (&dataent, files, sumused,
01519                                              sizeof (struct dataent),
01520                                              dataentcmp);
01521               printf ("%6d %7x %3d%c ",
01522                      locrec->record[idx].len, locrec->record[idx].offset,
01523                      data->nlink,
01524                      dataent.file_offset == locrec->record[LC_ALL].offset
01525                      ? '+' : ' ');
01526               for (i = 0; i < 16; i += 4)
01527                   printf ("%02x%02x%02x%02x",
01528                          data->sum[i], data->sum[i + 1],
01529                          data->sum[i + 2], data->sum[i + 3]);
01530               printf (" %s/%s\n", names[cnt].name,
01531                      idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
01532                      : locnames[idx]);
01533              }
01534        }
01535     }
01536   else
01537     for (cnt = 0; cnt < used; ++cnt)
01538       puts (names[cnt].name);
01539 
01540   close_archive (&ah);
01541 
01542   exit (EXIT_SUCCESS);
01543 }