Back to index

glibc  2.9
locfile.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996-2004, 2005, 2006, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
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 <dirent.h>
00024 #include <errno.h>
00025 #include <fcntl.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <sys/param.h>
00030 #include <sys/stat.h>
00031 
00032 #include "../../crypt/md5.h"
00033 #include "localedef.h"
00034 #include "locfile.h"
00035 #include "simple-hash.h"
00036 
00037 #include "locfile-kw.h"
00038 
00039 
00040 /* Temporary storage of the locale data before writing it to the archive.  */
00041 static locale_data_t to_archive;
00042 
00043 
00044 int
00045 locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
00046 {
00047   const char *filename = result->name;
00048   const char *repertoire_name = result->repertoire_name;
00049   int locale_mask = result->needed & ~result->avail;
00050   struct linereader *ldfile;
00051   int not_here = ALL_LOCALES;
00052 
00053   /* If no repertoire name was specified use the global one.  */
00054   if (repertoire_name == NULL)
00055     repertoire_name = repertoire_global;
00056 
00057   /* Open the locale definition file.  */
00058   ldfile = lr_open (filename, locfile_hash);
00059   if (ldfile == NULL)
00060     {
00061       if (filename != NULL && filename[0] != '/')
00062        {
00063          char *i18npath = getenv ("I18NPATH");
00064          if (i18npath != NULL && *i18npath != '\0')
00065            {
00066              const size_t pathlen = strlen (i18npath);
00067              char i18npathbuf[pathlen + 1];
00068              char path[strlen (filename) + 1 + pathlen
00069                      + sizeof ("/locales/") - 1];
00070              char *next;
00071              i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
00072 
00073              while (ldfile == NULL
00074                    && (next = strsep (&i18npath, ":")) != NULL)
00075               {
00076                 stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
00077 
00078                 ldfile = lr_open (path, locfile_hash);
00079 
00080                 if (ldfile == NULL)
00081                   {
00082                     stpcpy (stpcpy (path, next), filename);
00083 
00084                     ldfile = lr_open (path, locfile_hash);
00085                   }
00086               }
00087            }
00088 
00089          /* Test in the default directory.  */
00090          if (ldfile == NULL)
00091            {
00092              char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
00093 
00094              stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
00095              ldfile = lr_open (path, locfile_hash);
00096            }
00097        }
00098 
00099       if (ldfile == NULL)
00100        return 1;
00101     }
00102 
00103     /* Parse locale definition file and store result in RESULT.  */
00104   while (1)
00105     {
00106       struct token *now = lr_token (ldfile, charmap, NULL, NULL, verbose);
00107       enum token_t nowtok = now->tok;
00108       struct token *arg;
00109 
00110       if (nowtok == tok_eof)
00111        break;
00112 
00113       if (nowtok == tok_eol)
00114        /* Ignore empty lines.  */
00115        continue;
00116 
00117       switch (nowtok)
00118        {
00119        case tok_escape_char:
00120        case tok_comment_char:
00121          /* We need an argument.  */
00122          arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
00123 
00124          if (arg->tok != tok_ident)
00125            {
00126              SYNTAX_ERROR (_("bad argument"));
00127              continue;
00128            }
00129 
00130          if (arg->val.str.lenmb != 1)
00131            {
00132              lr_error (ldfile, _("\
00133 argument to `%s' must be a single character"),
00134                      nowtok == tok_escape_char
00135                      ? "escape_char" : "comment_char");
00136 
00137              lr_ignore_rest (ldfile, 0);
00138              continue;
00139            }
00140 
00141          if (nowtok == tok_escape_char)
00142            ldfile->escape_char = *arg->val.str.startmb;
00143          else
00144            ldfile->comment_char = *arg->val.str.startmb;
00145          break;
00146 
00147        case tok_repertoiremap:
00148          /* We need an argument.  */
00149          arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
00150 
00151          if (arg->tok != tok_ident)
00152            {
00153              SYNTAX_ERROR (_("bad argument"));
00154              continue;
00155            }
00156 
00157          if (repertoire_name == NULL)
00158            {
00159              char *newp = alloca (arg->val.str.lenmb + 1);
00160 
00161              *((char *) mempcpy (newp, arg->val.str.startmb,
00162                               arg->val.str.lenmb)) = '\0';
00163              repertoire_name = newp;
00164            }
00165          break;
00166 
00167        case tok_lc_ctype:
00168          ctype_read (ldfile, result, charmap, repertoire_name,
00169                     (locale_mask & CTYPE_LOCALE) == 0);
00170          result->avail |= locale_mask & CTYPE_LOCALE;
00171          not_here ^= CTYPE_LOCALE;
00172          continue;
00173 
00174        case tok_lc_collate:
00175          collate_read (ldfile, result, charmap, repertoire_name,
00176                      (locale_mask & COLLATE_LOCALE) == 0);
00177          result->avail |= locale_mask & COLLATE_LOCALE;
00178          not_here ^= COLLATE_LOCALE;
00179          continue;
00180 
00181        case tok_lc_monetary:
00182          monetary_read (ldfile, result, charmap, repertoire_name,
00183                       (locale_mask & MONETARY_LOCALE) == 0);
00184          result->avail |= locale_mask & MONETARY_LOCALE;
00185          not_here ^= MONETARY_LOCALE;
00186          continue;
00187 
00188        case tok_lc_numeric:
00189          numeric_read (ldfile, result, charmap, repertoire_name,
00190                      (locale_mask & NUMERIC_LOCALE) == 0);
00191          result->avail |= locale_mask & NUMERIC_LOCALE;
00192          not_here ^= NUMERIC_LOCALE;
00193          continue;
00194 
00195        case tok_lc_time:
00196          time_read (ldfile, result, charmap, repertoire_name,
00197                    (locale_mask & TIME_LOCALE) == 0);
00198          result->avail |= locale_mask & TIME_LOCALE;
00199          not_here ^= TIME_LOCALE;
00200          continue;
00201 
00202        case tok_lc_messages:
00203          messages_read (ldfile, result, charmap, repertoire_name,
00204                       (locale_mask & MESSAGES_LOCALE) == 0);
00205          result->avail |= locale_mask & MESSAGES_LOCALE;
00206          not_here ^= MESSAGES_LOCALE;
00207          continue;
00208 
00209        case tok_lc_paper:
00210          paper_read (ldfile, result, charmap, repertoire_name,
00211                     (locale_mask & PAPER_LOCALE) == 0);
00212          result->avail |= locale_mask & PAPER_LOCALE;
00213          not_here ^= PAPER_LOCALE;
00214          continue;
00215 
00216        case tok_lc_name:
00217          name_read (ldfile, result, charmap, repertoire_name,
00218                    (locale_mask & NAME_LOCALE) == 0);
00219          result->avail |= locale_mask & NAME_LOCALE;
00220          not_here ^= NAME_LOCALE;
00221          continue;
00222 
00223        case tok_lc_address:
00224          address_read (ldfile, result, charmap, repertoire_name,
00225                      (locale_mask & ADDRESS_LOCALE) == 0);
00226          result->avail |= locale_mask & ADDRESS_LOCALE;
00227          not_here ^= ADDRESS_LOCALE;
00228          continue;
00229 
00230        case tok_lc_telephone:
00231          telephone_read (ldfile, result, charmap, repertoire_name,
00232                        (locale_mask & TELEPHONE_LOCALE) == 0);
00233          result->avail |= locale_mask & TELEPHONE_LOCALE;
00234          not_here ^= TELEPHONE_LOCALE;
00235          continue;
00236 
00237        case tok_lc_measurement:
00238          measurement_read (ldfile, result, charmap, repertoire_name,
00239                          (locale_mask & MEASUREMENT_LOCALE) == 0);
00240          result->avail |= locale_mask & MEASUREMENT_LOCALE;
00241          not_here ^= MEASUREMENT_LOCALE;
00242          continue;
00243 
00244        case tok_lc_identification:
00245          identification_read (ldfile, result, charmap, repertoire_name,
00246                             (locale_mask & IDENTIFICATION_LOCALE) == 0);
00247          result->avail |= locale_mask & IDENTIFICATION_LOCALE;
00248          not_here ^= IDENTIFICATION_LOCALE;
00249          continue;
00250 
00251        default:
00252          SYNTAX_ERROR (_("\
00253 syntax error: not inside a locale definition section"));
00254          continue;
00255        }
00256 
00257       /* The rest of the line must be empty.  */
00258       lr_ignore_rest (ldfile, 1);
00259     }
00260 
00261   /* We read all of the file.  */
00262   lr_close (ldfile);
00263 
00264   /* Mark the categories which are not contained in the file.  We assume
00265      them to be available and the default data will be used.  */
00266   result->avail |= not_here;
00267 
00268   return 0;
00269 }
00270 
00271 
00272 /* Semantic checking of locale specifications.  */
00273 
00274 static void (*const check_funcs[]) (struct localedef_t *,
00275                                 const struct charmap_t *) =
00276 {
00277   [LC_CTYPE] = ctype_finish,
00278   [LC_COLLATE] = collate_finish,
00279   [LC_MESSAGES] = messages_finish,
00280   [LC_MONETARY] = monetary_finish,
00281   [LC_NUMERIC] = numeric_finish,
00282   [LC_TIME] = time_finish,
00283   [LC_PAPER] = paper_finish,
00284   [LC_NAME] = name_finish,
00285   [LC_ADDRESS] = address_finish,
00286   [LC_TELEPHONE] = telephone_finish,
00287   [LC_MEASUREMENT] = measurement_finish,
00288   [LC_IDENTIFICATION] = identification_finish
00289 };
00290 
00291 void
00292 check_all_categories (struct localedef_t *definitions,
00293                     const struct charmap_t *charmap)
00294 {
00295   int cnt;
00296 
00297   for (cnt = 0; cnt < sizeof (check_funcs) / sizeof (check_funcs[0]); ++cnt)
00298     if (check_funcs[cnt] != NULL)
00299       check_funcs[cnt] (definitions, charmap);
00300 }
00301 
00302 
00303 /* Writing the locale data files.  All files use the same output_path.  */
00304 
00305 static void (*const write_funcs[]) (struct localedef_t *,
00306                                 const struct charmap_t *, const char *) =
00307 {
00308   [LC_CTYPE] = ctype_output,
00309   [LC_COLLATE] = collate_output,
00310   [LC_MESSAGES] = messages_output,
00311   [LC_MONETARY] = monetary_output,
00312   [LC_NUMERIC] = numeric_output,
00313   [LC_TIME] = time_output,
00314   [LC_PAPER] = paper_output,
00315   [LC_NAME] = name_output,
00316   [LC_ADDRESS] = address_output,
00317   [LC_TELEPHONE] = telephone_output,
00318   [LC_MEASUREMENT] = measurement_output,
00319   [LC_IDENTIFICATION] = identification_output
00320 };
00321 
00322 
00323 void
00324 write_all_categories (struct localedef_t *definitions,
00325                     const struct charmap_t *charmap, const char *locname,
00326                     const char *output_path)
00327 {
00328   int cnt;
00329 
00330   for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
00331     if (write_funcs[cnt] != NULL)
00332       write_funcs[cnt] (definitions, charmap, output_path);
00333 
00334   if (! no_archive)
00335     {
00336       /* The data has to be added to the archive.  Do this now.  */
00337       struct locarhandle ah;
00338 
00339       /* Open the archive.  This call never returns if we cannot
00340         successfully open the archive.  */
00341       open_archive (&ah, false);
00342 
00343       if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
00344        error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
00345 
00346       /* We are done.  */
00347       close_archive (&ah);
00348     }
00349 }
00350 
00351 
00352 /* Return a NULL terminated list of the directories next to output_path
00353    that have the same owner, group, permissions and device as output_path.  */
00354 static const char **
00355 siblings_uncached (const char *output_path)
00356 {
00357   size_t len;
00358   char *base, *p;
00359   struct stat output_stat;
00360   DIR *dirp;
00361   int nelems;
00362   const char **elems;
00363 
00364   /* Remove trailing slashes and trailing pathname component.  */
00365   len = strlen (output_path);
00366   base = (char *) alloca (len);
00367   memcpy (base, output_path, len);
00368   p = base + len;
00369   while (p > base && p[-1] == '/')
00370     p--;
00371   if (p == base)
00372     return NULL;
00373   do
00374     p--;
00375   while (p > base && p[-1] != '/');
00376   if (p == base)
00377     return NULL;
00378   *--p = '\0';
00379   len = p - base;
00380 
00381   /* Get the properties of output_path.  */
00382   if (lstat (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
00383     return NULL;
00384 
00385   /* Iterate through the directories in base directory.  */
00386   dirp = opendir (base);
00387   if (dirp == NULL)
00388     return NULL;
00389   nelems = 0;
00390   elems = NULL;
00391   for (;;)
00392     {
00393       struct dirent64 *other_dentry;
00394       const char *other_name;
00395       char *other_path;
00396       struct stat other_stat;
00397 
00398       other_dentry = readdir64 (dirp);
00399       if (other_dentry == NULL)
00400        break;
00401 
00402       other_name = other_dentry->d_name;
00403       if (strcmp (other_name, ".") == 0 || strcmp (other_name, "..") == 0)
00404        continue;
00405 
00406       other_path = (char *) xmalloc (len + 1 + strlen (other_name) + 2);
00407       memcpy (other_path, base, len);
00408       other_path[len] = '/';
00409       strcpy (other_path + len + 1, other_name);
00410 
00411       if (lstat (other_path, &other_stat) >= 0
00412          && S_ISDIR (other_stat.st_mode)
00413          && other_stat.st_uid == output_stat.st_uid
00414          && other_stat.st_gid == output_stat.st_gid
00415          && other_stat.st_mode == output_stat.st_mode
00416          && other_stat.st_dev == output_stat.st_dev)
00417        {
00418          /* Found a subdirectory.  Add a trailing slash and store it.  */
00419          p = other_path + len + 1 + strlen (other_name);
00420          *p++ = '/';
00421          *p = '\0';
00422          elems = (const char **) xrealloc ((char *) elems,
00423                                        (nelems + 2) * sizeof (char **));
00424          elems[nelems++] = other_path;
00425        }
00426       else
00427        free (other_path);
00428     }
00429   closedir (dirp);
00430 
00431   if (elems != NULL)
00432     elems[nelems] = NULL;
00433   return elems;
00434 }
00435 
00436 
00437 /* Return a NULL terminated list of the directories next to output_path
00438    that have the same owner, group, permissions and device as output_path.
00439    Cache the result for future calls.  */
00440 static const char **
00441 siblings (const char *output_path)
00442 {
00443   static const char *last_output_path;
00444   static const char **last_result;
00445 
00446   if (output_path != last_output_path)
00447     {
00448       if (last_result != NULL)
00449        {
00450          const char **p;
00451 
00452          for (p = last_result; *p != NULL; p++)
00453            free ((char *) *p);
00454          free (last_result);
00455        }
00456 
00457       last_output_path = output_path;
00458       last_result = siblings_uncached (output_path);
00459     }
00460   return last_result;
00461 }
00462 
00463 
00464 /* Read as many bytes from a file descriptor as possible.  */
00465 static ssize_t
00466 full_read (int fd, void *bufarea, size_t nbyte)
00467 {
00468   char *buf = (char *) bufarea;
00469 
00470   while (nbyte > 0)
00471     {
00472       ssize_t retval = read (fd, buf, nbyte);
00473 
00474       if (retval == 0)
00475        break;
00476       else if (retval > 0)
00477        {
00478          buf += retval;
00479          nbyte -= retval;
00480        }
00481       else if (errno != EINTR)
00482        return retval;
00483     }
00484   return buf - (char *) bufarea;
00485 }
00486 
00487 
00488 /* Compare the contents of two regular files of the same size.  Return 0
00489    if they are equal, 1 if they are different, or -1 if an error occurs.  */
00490 static int
00491 compare_files (const char *filename1, const char *filename2, size_t size,
00492               size_t blocksize)
00493 {
00494   int fd1, fd2;
00495   int ret = -1;
00496 
00497   fd1 = open (filename1, O_RDONLY);
00498   if (fd1 >= 0)
00499     {
00500       fd2 = open (filename2, O_RDONLY);
00501       if (fd2 >= 0)
00502        {
00503          char *buf1 = (char *) xmalloc (2 * blocksize);
00504          char *buf2 = buf1 + blocksize;
00505 
00506          ret = 0;
00507          while (size > 0)
00508            {
00509              size_t bytes = (size < blocksize ? size : blocksize);
00510 
00511              if (full_read (fd1, buf1, bytes) < (ssize_t) bytes)
00512               {
00513                 ret = -1;
00514                 break;
00515               }
00516              if (full_read (fd2, buf2, bytes) < (ssize_t) bytes)
00517               {
00518                 ret = -1;
00519                 break;
00520               }
00521              if (memcmp (buf1, buf2, bytes) != 0)
00522               {
00523                 ret = 1;
00524                 break;
00525               }
00526              size -= bytes;
00527            }
00528 
00529          free (buf1);
00530          close (fd2);
00531        }
00532       close (fd1);
00533     }
00534   return ret;
00535 }
00536 
00537 
00538 /* Write a locale file, with contents given by N_ELEM and VEC.  */
00539 void
00540 write_locale_data (const char *output_path, int catidx, const char *category,
00541                  size_t n_elem, struct iovec *vec)
00542 {
00543   size_t cnt, step, maxiov;
00544   int fd;
00545   char *fname;
00546   const char **other_paths;
00547 
00548   if (! no_archive)
00549     {
00550       /* The data will be added to the archive.  For now we simply
00551         generate the image which will be written.  First determine
00552         the size.  */
00553       int cnt;
00554       void *endp;
00555 
00556       to_archive[catidx].size = 0;
00557       for (cnt = 0; cnt < n_elem; ++cnt)
00558        to_archive[catidx].size += vec[cnt].iov_len;
00559 
00560       /* Allocate the memory for it.  */
00561       to_archive[catidx].addr = xmalloc (to_archive[catidx].size);
00562 
00563       /* Fill it in.  */
00564       for (cnt = 0, endp = to_archive[catidx].addr; cnt < n_elem; ++cnt)
00565        endp = mempcpy (endp, vec[cnt].iov_base, vec[cnt].iov_len);
00566 
00567       /* Compute the MD5 sum for the data.  */
00568       __md5_buffer (to_archive[catidx].addr, to_archive[catidx].size,
00569                   to_archive[catidx].sum);
00570 
00571       return;
00572     }
00573 
00574   fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
00575 
00576   /* Normally we write to the directory pointed to by the OUTPUT_PATH.
00577      But for LC_MESSAGES we have to take care for the translation
00578      data.  This means we need to have a directory LC_MESSAGES in
00579      which we place the file under the name SYS_LC_MESSAGES.  */
00580   sprintf (fname, "%s%s", output_path, category);
00581   fd = -2;
00582   if (strcmp (category, "LC_MESSAGES") == 0)
00583     {
00584       struct stat st;
00585 
00586       if (stat (fname, &st) < 0)
00587        {
00588          if (mkdir (fname, 0777) >= 0)
00589            {
00590              fd = -1;
00591              errno = EISDIR;
00592            }
00593        }
00594       else if (!S_ISREG (st.st_mode))
00595        {
00596          fd = -1;
00597          errno = EISDIR;
00598        }
00599     }
00600 
00601   /* Create the locale file with nlinks == 1; this avoids crashing processes
00602      which currently use the locale and damaging files belonging to other
00603      locales as well.  */
00604   if (fd == -2)
00605     {
00606       unlink (fname);
00607       fd = creat (fname, 0666);
00608     }
00609 
00610   if (fd == -1)
00611     {
00612       int save_err = errno;
00613 
00614       if (errno == EISDIR)
00615        {
00616          sprintf (fname, "%1$s%2$s/SYS_%2$s", output_path, category);
00617          unlink (fname);
00618          fd = creat (fname, 0666);
00619          if (fd == -1)
00620            save_err = errno;
00621        }
00622 
00623       if (fd == -1)
00624        {
00625          if (!be_quiet)
00626            WITH_CUR_LOCALE (error (0, save_err, _("\
00627 cannot open output file `%s' for category `%s'"), fname, category));
00628          free (fname);
00629          return;
00630        }
00631     }
00632 
00633 #ifdef UIO_MAXIOV
00634   maxiov = UIO_MAXIOV;
00635 #else
00636   maxiov = sysconf (_SC_UIO_MAXIOV);
00637 #endif
00638 
00639   /* Write the data using writev.  But we must take care for the
00640      limitation of the implementation.  */
00641   for (cnt = 0; cnt < n_elem; cnt += step)
00642     {
00643       step = n_elem - cnt;
00644       if (maxiov > 0)
00645        step = MIN (maxiov, step);
00646 
00647       if (writev (fd, &vec[cnt], step) < 0)
00648        {
00649          if (!be_quiet)
00650            WITH_CUR_LOCALE (error (0, errno, _("\
00651 failure while writing data for category `%s'"), category));
00652          break;
00653        }
00654     }
00655 
00656   close (fd);
00657 
00658   /* Compare the file with the locale data files for the same category in
00659      other locales, and see if we can reuse it, to save disk space.  */
00660   other_paths = siblings (output_path);
00661   if (other_paths != NULL)
00662     {
00663       struct stat fname_stat;
00664 
00665       if (lstat (fname, &fname_stat) >= 0
00666          && S_ISREG (fname_stat.st_mode))
00667        {
00668          const char *fname_tail = fname + strlen (output_path);
00669          const char **other_p;
00670          int seen_count;
00671          ino_t *seen_inodes;
00672 
00673          seen_count = 0;
00674          for (other_p = other_paths; *other_p; other_p++)
00675            seen_count++;
00676          seen_inodes = (ino_t *) xmalloc (seen_count * sizeof (ino_t));
00677          seen_count = 0;
00678 
00679          for (other_p = other_paths; *other_p; other_p++)
00680            {
00681              const char *other_path = *other_p;
00682              size_t other_path_len = strlen (other_path);
00683              char *other_fname;
00684              struct stat other_fname_stat;
00685 
00686              other_fname =
00687               (char *) xmalloc (other_path_len + strlen (fname_tail) + 1);
00688              memcpy (other_fname, other_path, other_path_len);
00689              strcpy (other_fname + other_path_len, fname_tail);
00690 
00691              if (lstat (other_fname, &other_fname_stat) >= 0
00692                 && S_ISREG (other_fname_stat.st_mode)
00693                 /* Consider only files on the same device.
00694                    Otherwise hard linking won't work anyway.  */
00695                 && other_fname_stat.st_dev == fname_stat.st_dev
00696                 /* Consider only files with the same permissions.
00697                    Otherwise there are security risks.  */
00698                 && other_fname_stat.st_uid == fname_stat.st_uid
00699                 && other_fname_stat.st_gid == fname_stat.st_gid
00700                 && other_fname_stat.st_mode == fname_stat.st_mode
00701                 /* Don't compare fname with itself.  */
00702                 && other_fname_stat.st_ino != fname_stat.st_ino
00703                 /* Files must have the same size, otherwise they
00704                    cannot be the same.  */
00705                 && other_fname_stat.st_size == fname_stat.st_size)
00706               {
00707                 /* Skip this file if we have already read it (under a
00708                    different name).  */
00709                 int i;
00710 
00711                 for (i = seen_count - 1; i >= 0; i--)
00712                   if (seen_inodes[i] == other_fname_stat.st_ino)
00713                     break;
00714                 if (i < 0)
00715                   {
00716                     /* Now compare fname and other_fname for real.  */
00717                     blksize_t blocksize;
00718 
00719 #ifdef _STATBUF_ST_BLKSIZE
00720                     blocksize = MAX (fname_stat.st_blksize,
00721                                    other_fname_stat.st_blksize);
00722                     if (blocksize > 8 * 1024)
00723                      blocksize = 8 * 1024;
00724 #else
00725                     blocksize = 8 * 1024;
00726 #endif
00727 
00728                     if (compare_files (fname, other_fname,
00729                                     fname_stat.st_size, blocksize) == 0)
00730                      {
00731                        /* Found! other_fname is identical to fname.  */
00732                        /* Link other_fname to fname.  But use a temporary
00733                           file, in case hard links don't work on the
00734                           particular filesystem.  */
00735                        char * tmp_fname =
00736                          (char *) xmalloc (strlen (fname) + 4 + 1);
00737 
00738                        strcpy (stpcpy (tmp_fname, fname), ".tmp");
00739 
00740                        if (link (other_fname, tmp_fname) >= 0)
00741                          {
00742                            unlink (fname);
00743                            if (rename (tmp_fname, fname) < 0)
00744                             {
00745                               if (!be_quiet)
00746                                 WITH_CUR_LOCALE (error (0, errno, _("\
00747 cannot create output file `%s' for category `%s'"), fname, category));
00748                             }
00749                            free (tmp_fname);
00750                            free (other_fname);
00751                            break;
00752                          }
00753                        free (tmp_fname);
00754                      }
00755 
00756                     /* Don't compare with this file a second time.  */
00757                     seen_inodes[seen_count++] = other_fname_stat.st_ino;
00758                   }
00759               }
00760              free (other_fname);
00761            }
00762          free (seen_inodes);
00763        }
00764     }
00765 
00766   free (fname);
00767 }
00768 
00769 
00770 /* General handling of `copy'.  */
00771 void
00772 handle_copy (struct linereader *ldfile, const struct charmap_t *charmap,
00773             const char *repertoire_name, struct localedef_t *result,
00774             enum token_t token, int locale, const char *locale_name,
00775             int ignore_content)
00776 {
00777   struct token *now;
00778   int warned = 0;
00779 
00780   now = lr_token (ldfile, charmap, result, NULL, verbose);
00781   if (now->tok != tok_string)
00782     lr_error (ldfile, _("expecting string argument for `copy'"));
00783   else if (!ignore_content)
00784     {
00785       if (now->val.str.startmb == NULL)
00786        lr_error (ldfile, _("\
00787 locale name should consist only of portable characters"));
00788       else
00789        {
00790          (void) add_to_readlist (locale, now->val.str.startmb,
00791                               repertoire_name, 1, NULL);
00792          result->copy_name[locale] = now->val.str.startmb;
00793        }
00794     }
00795 
00796   lr_ignore_rest (ldfile, now->tok == tok_string);
00797 
00798   /* The rest of the line must be empty and the next keyword must be
00799      `END xxx'.  */
00800   while ((now = lr_token (ldfile, charmap, result, NULL, verbose))->tok
00801         != tok_end && now->tok != tok_eof)
00802     {
00803       if (warned == 0)
00804        {
00805          lr_error (ldfile, _("\
00806 no other keyword shall be specified when `copy' is used"));
00807          warned = 1;
00808        }
00809 
00810       lr_ignore_rest (ldfile, 0);
00811     }
00812 
00813   if (now->tok != tok_eof)
00814     {
00815       /* Handle `END xxx'.  */
00816       now = lr_token (ldfile, charmap, result, NULL, verbose);
00817 
00818       if (now->tok != token)
00819        lr_error (ldfile, _("\
00820 `%1$s' definition does not end with `END %1$s'"), locale_name);
00821 
00822       lr_ignore_rest (ldfile, now->tok == token);
00823     }
00824   else
00825     /* When we come here we reached the end of the file.  */
00826     lr_error (ldfile, _("%s: premature end of file"), locale_name);
00827 }