(root)/
glibc-2.38/
locale/
programs/
locfile.c
       1  /* Copyright (C) 1996-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     This program is free software; you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published
       6     by the Free Software Foundation; version 2 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #ifdef HAVE_CONFIG_H
      18  # include <config.h>
      19  #endif
      20  
      21  #include <dirent.h>
      22  #include <errno.h>
      23  #include <fcntl.h>
      24  #include <stdbool.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <unistd.h>
      28  #include <sys/param.h>
      29  #include <sys/stat.h>
      30  #include <assert.h>
      31  #include <wchar.h>
      32  
      33  #include "../../crypt/md5.h"
      34  #include "localedef.h"
      35  #include "localeinfo.h"
      36  #include "locfile.h"
      37  #include "simple-hash.h"
      38  
      39  #include "locfile-kw.h"
      40  
      41  #define obstack_chunk_alloc xmalloc
      42  #define obstack_chunk_free free
      43  
      44  /* Temporary storage of the locale data before writing it to the archive.  */
      45  static locale_data_t to_archive;
      46  
      47  
      48  int
      49  locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
      50  {
      51    const char *filename = result->name;
      52    const char *repertoire_name = result->repertoire_name;
      53    int locale_mask = result->needed & ~result->avail;
      54    struct linereader *ldfile;
      55    int not_here = ALL_LOCALES;
      56  
      57    /* If no repertoire name was specified use the global one.  */
      58    if (repertoire_name == NULL)
      59      repertoire_name = repertoire_global;
      60  
      61    /* Open the locale definition file.  */
      62    ldfile = lr_open (filename, locfile_hash);
      63    if (ldfile == NULL)
      64      {
      65        if (filename != NULL && filename[0] != '/')
      66  	{
      67  	  char *i18npath = getenv ("I18NPATH");
      68  	  if (i18npath != NULL && *i18npath != '\0')
      69  	    {
      70  	      const size_t pathlen = strlen (i18npath);
      71  	      char i18npathbuf[pathlen + 1];
      72  	      char path[strlen (filename) + 1 + pathlen
      73  			+ sizeof ("/locales/") - 1];
      74  	      char *next;
      75  	      i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
      76  
      77  	      while (ldfile == NULL
      78  		     && (next = strsep (&i18npath, ":")) != NULL)
      79  		{
      80  		  stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
      81  
      82  		  ldfile = lr_open (path, locfile_hash);
      83  
      84  		  if (ldfile == NULL)
      85  		    {
      86  		      stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
      87  
      88  		      ldfile = lr_open (path, locfile_hash);
      89  		    }
      90  		}
      91  	    }
      92  
      93  	  /* Test in the default directory.  */
      94  	  if (ldfile == NULL)
      95  	    {
      96  	      char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
      97  
      98  	      stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
      99  	      ldfile = lr_open (path, locfile_hash);
     100  	    }
     101  	}
     102  
     103        if (ldfile == NULL)
     104  	return 1;
     105      }
     106  
     107      /* Parse locale definition file and store result in RESULT.  */
     108    while (1)
     109      {
     110        struct token *now = lr_token (ldfile, charmap, NULL, NULL, verbose);
     111        enum token_t nowtok = now->tok;
     112        struct token *arg;
     113  
     114        if (nowtok == tok_eof)
     115  	break;
     116  
     117        if (nowtok == tok_eol)
     118  	/* Ignore empty lines.  */
     119  	continue;
     120  
     121        switch (nowtok)
     122  	{
     123  	case tok_escape_char:
     124  	case tok_comment_char:
     125  	  /* We need an argument.  */
     126  	  arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
     127  
     128  	  if (arg->tok != tok_ident)
     129  	    {
     130  	      SYNTAX_ERROR (_("bad argument"));
     131  	      continue;
     132  	    }
     133  
     134  	  if (arg->val.str.lenmb != 1)
     135  	    {
     136  	      lr_error (ldfile, _("\
     137  argument to `%s' must be a single character"),
     138  			nowtok == tok_escape_char
     139  			? "escape_char" : "comment_char");
     140  
     141  	      lr_ignore_rest (ldfile, 0);
     142  	      continue;
     143  	    }
     144  
     145  	  if (nowtok == tok_escape_char)
     146  	    ldfile->escape_char = *arg->val.str.startmb;
     147  	  else
     148  	    ldfile->comment_char = *arg->val.str.startmb;
     149  	  break;
     150  
     151  	case tok_repertoiremap:
     152  	  /* We need an argument.  */
     153  	  arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
     154  
     155  	  if (arg->tok != tok_ident)
     156  	    {
     157  	      SYNTAX_ERROR (_("bad argument"));
     158  	      continue;
     159  	    }
     160  
     161  	  if (repertoire_name == NULL)
     162  	    {
     163  	      char *newp = alloca (arg->val.str.lenmb + 1);
     164  
     165  	      *((char *) mempcpy (newp, arg->val.str.startmb,
     166  				  arg->val.str.lenmb)) = '\0';
     167  	      repertoire_name = newp;
     168  	    }
     169  	  break;
     170  
     171  	case tok_lc_ctype:
     172  	  ctype_read (ldfile, result, charmap, repertoire_name,
     173  		      (locale_mask & CTYPE_LOCALE) == 0);
     174  	  result->avail |= locale_mask & CTYPE_LOCALE;
     175  	  not_here ^= CTYPE_LOCALE;
     176  	  continue;
     177  
     178  	case tok_lc_collate:
     179  	  collate_read (ldfile, result, charmap, repertoire_name,
     180  			(locale_mask & COLLATE_LOCALE) == 0);
     181  	  result->avail |= locale_mask & COLLATE_LOCALE;
     182  	  not_here ^= COLLATE_LOCALE;
     183  	  continue;
     184  
     185  	case tok_lc_monetary:
     186  	  monetary_read (ldfile, result, charmap, repertoire_name,
     187  			 (locale_mask & MONETARY_LOCALE) == 0);
     188  	  result->avail |= locale_mask & MONETARY_LOCALE;
     189  	  not_here ^= MONETARY_LOCALE;
     190  	  continue;
     191  
     192  	case tok_lc_numeric:
     193  	  numeric_read (ldfile, result, charmap, repertoire_name,
     194  			(locale_mask & NUMERIC_LOCALE) == 0);
     195  	  result->avail |= locale_mask & NUMERIC_LOCALE;
     196  	  not_here ^= NUMERIC_LOCALE;
     197  	  continue;
     198  
     199  	case tok_lc_time:
     200  	  time_read (ldfile, result, charmap, repertoire_name,
     201  		     (locale_mask & TIME_LOCALE) == 0);
     202  	  result->avail |= locale_mask & TIME_LOCALE;
     203  	  not_here ^= TIME_LOCALE;
     204  	  continue;
     205  
     206  	case tok_lc_messages:
     207  	  messages_read (ldfile, result, charmap, repertoire_name,
     208  			 (locale_mask & MESSAGES_LOCALE) == 0);
     209  	  result->avail |= locale_mask & MESSAGES_LOCALE;
     210  	  not_here ^= MESSAGES_LOCALE;
     211  	  continue;
     212  
     213  	case tok_lc_paper:
     214  	  paper_read (ldfile, result, charmap, repertoire_name,
     215  		      (locale_mask & PAPER_LOCALE) == 0);
     216  	  result->avail |= locale_mask & PAPER_LOCALE;
     217  	  not_here ^= PAPER_LOCALE;
     218  	  continue;
     219  
     220  	case tok_lc_name:
     221  	  name_read (ldfile, result, charmap, repertoire_name,
     222  		     (locale_mask & NAME_LOCALE) == 0);
     223  	  result->avail |= locale_mask & NAME_LOCALE;
     224  	  not_here ^= NAME_LOCALE;
     225  	  continue;
     226  
     227  	case tok_lc_address:
     228  	  address_read (ldfile, result, charmap, repertoire_name,
     229  			(locale_mask & ADDRESS_LOCALE) == 0);
     230  	  result->avail |= locale_mask & ADDRESS_LOCALE;
     231  	  not_here ^= ADDRESS_LOCALE;
     232  	  continue;
     233  
     234  	case tok_lc_telephone:
     235  	  telephone_read (ldfile, result, charmap, repertoire_name,
     236  			  (locale_mask & TELEPHONE_LOCALE) == 0);
     237  	  result->avail |= locale_mask & TELEPHONE_LOCALE;
     238  	  not_here ^= TELEPHONE_LOCALE;
     239  	  continue;
     240  
     241  	case tok_lc_measurement:
     242  	  measurement_read (ldfile, result, charmap, repertoire_name,
     243  			    (locale_mask & MEASUREMENT_LOCALE) == 0);
     244  	  result->avail |= locale_mask & MEASUREMENT_LOCALE;
     245  	  not_here ^= MEASUREMENT_LOCALE;
     246  	  continue;
     247  
     248  	case tok_lc_identification:
     249  	  identification_read (ldfile, result, charmap, repertoire_name,
     250  			       (locale_mask & IDENTIFICATION_LOCALE) == 0);
     251  	  result->avail |= locale_mask & IDENTIFICATION_LOCALE;
     252  	  not_here ^= IDENTIFICATION_LOCALE;
     253  	  continue;
     254  
     255  	default:
     256  	  SYNTAX_ERROR (_("\
     257  syntax error: not inside a locale definition section"));
     258  	  continue;
     259  	}
     260  
     261        /* The rest of the line must be empty.  */
     262        lr_ignore_rest (ldfile, 1);
     263      }
     264  
     265    /* We read all of the file.  */
     266    lr_close (ldfile);
     267  
     268    /* Mark the categories which are not contained in the file.  We assume
     269       them to be available and the default data will be used.  */
     270    result->avail |= not_here;
     271  
     272    return 0;
     273  }
     274  
     275  
     276  /* Semantic checking of locale specifications.  */
     277  
     278  static void (*const check_funcs[]) (struct localedef_t *,
     279  				    const struct charmap_t *) =
     280  {
     281    [LC_CTYPE] = ctype_finish,
     282    [LC_COLLATE] = collate_finish,
     283    [LC_MESSAGES] = messages_finish,
     284    [LC_MONETARY] = monetary_finish,
     285    [LC_NUMERIC] = numeric_finish,
     286    [LC_TIME] = time_finish,
     287    [LC_PAPER] = paper_finish,
     288    [LC_NAME] = name_finish,
     289    [LC_ADDRESS] = address_finish,
     290    [LC_TELEPHONE] = telephone_finish,
     291    [LC_MEASUREMENT] = measurement_finish,
     292    [LC_IDENTIFICATION] = identification_finish
     293  };
     294  
     295  void
     296  check_all_categories (struct localedef_t *definitions,
     297  		      const struct charmap_t *charmap)
     298  {
     299    int cnt;
     300  
     301    for (cnt = 0; cnt < sizeof (check_funcs) / sizeof (check_funcs[0]); ++cnt)
     302      if (check_funcs[cnt] != NULL)
     303        check_funcs[cnt] (definitions, charmap);
     304  }
     305  
     306  
     307  /* Writing the locale data files.  All files use the same output_path.  */
     308  
     309  static void (*const write_funcs[]) (struct localedef_t *,
     310  				    const struct charmap_t *, const char *) =
     311  {
     312    [LC_CTYPE] = ctype_output,
     313    [LC_COLLATE] = collate_output,
     314    [LC_MESSAGES] = messages_output,
     315    [LC_MONETARY] = monetary_output,
     316    [LC_NUMERIC] = numeric_output,
     317    [LC_TIME] = time_output,
     318    [LC_PAPER] = paper_output,
     319    [LC_NAME] = name_output,
     320    [LC_ADDRESS] = address_output,
     321    [LC_TELEPHONE] = telephone_output,
     322    [LC_MEASUREMENT] = measurement_output,
     323    [LC_IDENTIFICATION] = identification_output
     324  };
     325  
     326  
     327  void
     328  write_all_categories (struct localedef_t *definitions,
     329  		      const struct charmap_t *charmap, const char *locname,
     330  		      const char *output_path)
     331  {
     332    int cnt;
     333  
     334    for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
     335      if (write_funcs[cnt] != NULL)
     336        write_funcs[cnt] (definitions, charmap, output_path);
     337  
     338    if (! no_archive)
     339      {
     340        /* The data has to be added to the archive.  Do this now.  */
     341        struct locarhandle ah;
     342  
     343        /* Open the archive.  This call never returns if we cannot
     344  	 successfully open the archive.  */
     345        ah.fname = NULL;
     346        open_archive (&ah, false);
     347  
     348        if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
     349  	error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
     350  
     351        /* We are done.  */
     352        close_archive (&ah);
     353      }
     354  }
     355  
     356  
     357  /* Return a NULL terminated list of the directories next to output_path
     358     that have the same owner, group, permissions and device as output_path.  */
     359  static const char **
     360  siblings_uncached (const char *output_path)
     361  {
     362    size_t len;
     363    char *base, *p;
     364    struct stat64 output_stat;
     365    DIR *dirp;
     366    int nelems;
     367    const char **elems;
     368  
     369    /* Remove trailing slashes and trailing pathname component.  */
     370    len = strlen (output_path);
     371    base = (char *) alloca (len);
     372    memcpy (base, output_path, len);
     373    p = base + len;
     374    while (p > base && p[-1] == '/')
     375      p--;
     376    if (p == base)
     377      return NULL;
     378    do
     379      p--;
     380    while (p > base && p[-1] != '/');
     381    if (p == base)
     382      return NULL;
     383    *--p = '\0';
     384    len = p - base;
     385  
     386    /* Get the properties of output_path.  */
     387    if (lstat64 (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
     388      return NULL;
     389  
     390    /* Iterate through the directories in base directory.  */
     391    dirp = opendir (base);
     392    if (dirp == NULL)
     393      return NULL;
     394    nelems = 0;
     395    elems = NULL;
     396    for (;;)
     397      {
     398        struct dirent64 *other_dentry;
     399        const char *other_name;
     400        char *other_path;
     401        struct stat64 other_stat;
     402  
     403        other_dentry = readdir64 (dirp);
     404        if (other_dentry == NULL)
     405  	break;
     406  
     407        other_name = other_dentry->d_name;
     408        if (strcmp (other_name, ".") == 0 || strcmp (other_name, "..") == 0)
     409  	continue;
     410  
     411        other_path = (char *) xmalloc (len + 1 + strlen (other_name) + 2);
     412        memcpy (other_path, base, len);
     413        other_path[len] = '/';
     414        strcpy (other_path + len + 1, other_name);
     415  
     416        if (lstat64 (other_path, &other_stat) >= 0
     417  	  && S_ISDIR (other_stat.st_mode)
     418  	  && other_stat.st_uid == output_stat.st_uid
     419  	  && other_stat.st_gid == output_stat.st_gid
     420  	  && other_stat.st_mode == output_stat.st_mode
     421  	  && other_stat.st_dev == output_stat.st_dev)
     422  	{
     423  	  /* Found a subdirectory.  Add a trailing slash and store it.  */
     424  	  p = other_path + len + 1 + strlen (other_name);
     425  	  *p++ = '/';
     426  	  *p = '\0';
     427  	  elems = (const char **) xrealloc ((char *) elems,
     428  					    (nelems + 2) * sizeof (char **));
     429  	  elems[nelems++] = other_path;
     430  	}
     431        else
     432  	free (other_path);
     433      }
     434    closedir (dirp);
     435  
     436    if (elems != NULL)
     437      elems[nelems] = NULL;
     438    return elems;
     439  }
     440  
     441  
     442  /* Return a NULL terminated list of the directories next to output_path
     443     that have the same owner, group, permissions and device as output_path.
     444     Cache the result for future calls.  */
     445  static const char **
     446  siblings (const char *output_path)
     447  {
     448    static const char *last_output_path;
     449    static const char **last_result;
     450  
     451    if (output_path != last_output_path)
     452      {
     453        if (last_result != NULL)
     454  	{
     455  	  const char **p;
     456  
     457  	  for (p = last_result; *p != NULL; p++)
     458  	    free ((char *) *p);
     459  	  free (last_result);
     460  	}
     461  
     462        last_output_path = output_path;
     463        last_result = siblings_uncached (output_path);
     464      }
     465    return last_result;
     466  }
     467  
     468  
     469  /* Read as many bytes from a file descriptor as possible.  */
     470  static ssize_t
     471  full_read (int fd, void *bufarea, size_t nbyte)
     472  {
     473    char *buf = (char *) bufarea;
     474  
     475    while (nbyte > 0)
     476      {
     477        ssize_t retval = read (fd, buf, nbyte);
     478  
     479        if (retval == 0)
     480  	break;
     481        else if (retval > 0)
     482  	{
     483  	  buf += retval;
     484  	  nbyte -= retval;
     485  	}
     486        else if (errno != EINTR)
     487  	return retval;
     488      }
     489    return buf - (char *) bufarea;
     490  }
     491  
     492  
     493  /* Compare the contents of two regular files of the same size.  Return 0
     494     if they are equal, 1 if they are different, or -1 if an error occurs.  */
     495  static int
     496  compare_files (const char *filename1, const char *filename2, size_t size,
     497  	       size_t blocksize)
     498  {
     499    int fd1, fd2;
     500    int ret = -1;
     501  
     502    fd1 = open (filename1, O_RDONLY);
     503    if (fd1 >= 0)
     504      {
     505        fd2 = open (filename2, O_RDONLY);
     506        if (fd2 >= 0)
     507  	{
     508  	  char *buf1 = (char *) xmalloc (2 * blocksize);
     509  	  char *buf2 = buf1 + blocksize;
     510  
     511  	  ret = 0;
     512  	  while (size > 0)
     513  	    {
     514  	      size_t bytes = (size < blocksize ? size : blocksize);
     515  
     516  	      if (full_read (fd1, buf1, bytes) < (ssize_t) bytes)
     517  		{
     518  		  ret = -1;
     519  		  break;
     520  		}
     521  	      if (full_read (fd2, buf2, bytes) < (ssize_t) bytes)
     522  		{
     523  		  ret = -1;
     524  		  break;
     525  		}
     526  	      if (memcmp (buf1, buf2, bytes) != 0)
     527  		{
     528  		  ret = 1;
     529  		  break;
     530  		}
     531  	      size -= bytes;
     532  	    }
     533  
     534  	  free (buf1);
     535  	  close (fd2);
     536  	}
     537        close (fd1);
     538      }
     539    return ret;
     540  }
     541  
     542  /* True if the locale files use the opposite endianness to the
     543     machine running localedef.  */
     544  bool swap_endianness_p;
     545  
     546  /* When called outside a start_locale_structure/end_locale_structure
     547     or start_locale_prelude/end_locale_prelude block, record that the
     548     next byte in FILE's obstack will be the first byte of a new element.
     549     Do likewise for the first call inside a start_locale_structure/
     550     end_locale_structure block.  */
     551  static void
     552  record_offset (struct locale_file *file)
     553  {
     554    if (file->structure_stage < 2)
     555      {
     556        assert (file->next_element < file->n_elements);
     557        file->offsets[file->next_element++]
     558  	= (obstack_object_size (&file->data)
     559  	   + (file->n_elements + 2) * sizeof (uint32_t));
     560        if (file->structure_stage == 1)
     561  	file->structure_stage = 2;
     562      }
     563  }
     564  
     565  /* Initialize FILE for a new output file.  N_ELEMENTS is the number
     566     of elements in the file.  */
     567  void
     568  init_locale_data (struct locale_file *file, size_t n_elements)
     569  {
     570    file->n_elements = n_elements;
     571    file->next_element = 0;
     572    file->offsets = xmalloc (sizeof (uint32_t) * n_elements);
     573    obstack_init (&file->data);
     574    file->structure_stage = 0;
     575  }
     576  
     577  /* Align the size of FILE's obstack object to BOUNDARY bytes.  */
     578  void
     579  align_locale_data (struct locale_file *file, size_t boundary)
     580  {
     581    size_t size = -obstack_object_size (&file->data) & (boundary - 1);
     582    obstack_blank (&file->data, size);
     583    memset (obstack_next_free (&file->data) - size, 0, size);
     584  }
     585  
     586  /* Record that FILE's next element contains no data.  */
     587  void
     588  add_locale_empty (struct locale_file *file)
     589  {
     590    record_offset (file);
     591  }
     592  
     593  /* Record that FILE's next element consists of SIZE bytes starting at DATA.  */
     594  void
     595  add_locale_raw_data (struct locale_file *file, const void *data, size_t size)
     596  {
     597    record_offset (file);
     598    obstack_grow (&file->data, data, size);
     599  }
     600  
     601  /* Finish the current object on OBSTACK and use it as the data for FILE's
     602     next element.  */
     603  void
     604  add_locale_raw_obstack (struct locale_file *file, struct obstack *obstack)
     605  {
     606    size_t size = obstack_object_size (obstack);
     607    record_offset (file);
     608    obstack_grow (&file->data, obstack_finish (obstack), size);
     609  }
     610  
     611  /* Use STRING as FILE's next element.  */
     612  void
     613  add_locale_string (struct locale_file *file, const char *string)
     614  {
     615    record_offset (file);
     616    obstack_grow (&file->data, string, strlen (string) + 1);
     617  }
     618  
     619  /* Likewise for wide strings.  */
     620  void
     621  add_locale_wstring (struct locale_file *file, const uint32_t *string)
     622  {
     623    add_locale_uint32_array (file, string, wcslen ((const wchar_t *) string) + 1);
     624  }
     625  
     626  /* Record that FILE's next element is the 32-bit integer VALUE.  */
     627  void
     628  add_locale_uint32 (struct locale_file *file, uint32_t value)
     629  {
     630    align_locale_data (file, LOCFILE_ALIGN);
     631    record_offset (file);
     632    value = maybe_swap_uint32 (value);
     633    obstack_grow (&file->data, &value, sizeof (value));
     634  }
     635  
     636  /* Record that FILE's next element is an array of N_ELEMS integers
     637     starting at DATA.  */
     638  void
     639  add_locale_uint32_array (struct locale_file *file,
     640  			 const uint32_t *data, size_t n_elems)
     641  {
     642    align_locale_data (file, LOCFILE_ALIGN);
     643    record_offset (file);
     644    obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
     645    maybe_swap_uint32_obstack (&file->data, n_elems);
     646  }
     647  
     648  /* Record that FILE's next element is the single byte given by VALUE.  */
     649  void
     650  add_locale_char (struct locale_file *file, char value)
     651  {
     652    record_offset (file);
     653    obstack_1grow (&file->data, value);
     654  }
     655  
     656  /* Start building an element that contains several different pieces of data.
     657     Subsequent calls to add_locale_* will add data to the same element up
     658     till the next call to end_locale_structure.  The element's alignment
     659     is dictated by the first piece of data added to it.  */
     660  void
     661  start_locale_structure (struct locale_file *file)
     662  {
     663    assert (file->structure_stage == 0);
     664    file->structure_stage = 1;
     665  }
     666  
     667  /* Finish a structure element that was started by start_locale_structure.
     668     Empty structures are OK and behave like add_locale_empty.  */
     669  void
     670  end_locale_structure (struct locale_file *file)
     671  {
     672    record_offset (file);
     673    assert (file->structure_stage == 2);
     674    file->structure_stage = 0;
     675  }
     676  
     677  /* Start building data that goes before the next element's recorded offset.
     678     Subsequent calls to add_locale_* will add data to the file without
     679     treating any of it as the start of a new element.  Calling
     680     end_locale_prelude switches back to the usual behavior.  */
     681  void
     682  start_locale_prelude (struct locale_file *file)
     683  {
     684    assert (file->structure_stage == 0);
     685    file->structure_stage = 3;
     686  }
     687  
     688  /* End a block started by start_locale_prelude.  */
     689  void
     690  end_locale_prelude (struct locale_file *file)
     691  {
     692    assert (file->structure_stage == 3);
     693    file->structure_stage = 0;
     694  }
     695  
     696  /* Write a locale file, with contents given by FILE.  */
     697  void
     698  write_locale_data (const char *output_path, int catidx, const char *category,
     699  		   struct locale_file *file)
     700  {
     701    size_t cnt, step, maxiov;
     702    int fd;
     703    char *fname;
     704    const char **other_paths = NULL;
     705    uint32_t header[2];
     706    size_t n_elem;
     707    struct iovec vec[3];
     708  
     709    assert (file->n_elements == file->next_element);
     710    header[0] = LIMAGIC (catidx);
     711    header[1] = file->n_elements;
     712    vec[0].iov_len = sizeof (header);
     713    vec[0].iov_base = header;
     714    vec[1].iov_len = sizeof (uint32_t) * file->n_elements;
     715    vec[1].iov_base = file->offsets;
     716    vec[2].iov_len = obstack_object_size (&file->data);
     717    vec[2].iov_base = obstack_finish (&file->data);
     718    maybe_swap_uint32_array (vec[0].iov_base, 2);
     719    maybe_swap_uint32_array (vec[1].iov_base, file->n_elements);
     720    n_elem = 3;
     721    if (! no_archive)
     722      {
     723        /* The data will be added to the archive.  For now we simply
     724  	 generate the image which will be written.  First determine
     725  	 the size.  */
     726        int cnt;
     727        void *endp;
     728  
     729        to_archive[catidx].size = 0;
     730        for (cnt = 0; cnt < n_elem; ++cnt)
     731  	to_archive[catidx].size += vec[cnt].iov_len;
     732  
     733        /* Allocate the memory for it.  */
     734        to_archive[catidx].addr = xmalloc (to_archive[catidx].size);
     735  
     736        /* Fill it in.  */
     737        for (cnt = 0, endp = to_archive[catidx].addr; cnt < n_elem; ++cnt)
     738  	endp = mempcpy (endp, vec[cnt].iov_base, vec[cnt].iov_len);
     739  
     740        /* Compute the MD5 sum for the data.  */
     741        __md5_buffer (to_archive[catidx].addr, to_archive[catidx].size,
     742  		    to_archive[catidx].sum);
     743  
     744        return;
     745      }
     746  
     747    fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
     748  
     749    /* Normally we write to the directory pointed to by the OUTPUT_PATH.
     750       But for LC_MESSAGES we have to take care for the translation
     751       data.  This means we need to have a directory LC_MESSAGES in
     752       which we place the file under the name SYS_LC_MESSAGES.  */
     753    sprintf (fname, "%s%s", output_path, category);
     754    fd = -2;
     755    if (strcmp (category, "LC_MESSAGES") == 0)
     756      {
     757        struct stat64 st;
     758  
     759        if (stat64 (fname, &st) < 0)
     760  	{
     761  	  if (mkdir (fname, 0777) >= 0)
     762  	    {
     763  	      fd = -1;
     764  	      errno = EISDIR;
     765  	    }
     766  	}
     767        else if (!S_ISREG (st.st_mode))
     768  	{
     769  	  fd = -1;
     770  	  errno = EISDIR;
     771  	}
     772      }
     773  
     774    /* Create the locale file with nlinks == 1; this avoids crashing processes
     775       which currently use the locale and damaging files belonging to other
     776       locales as well.  */
     777    if (fd == -2)
     778      {
     779        unlink (fname);
     780        fd = creat (fname, 0666);
     781      }
     782  
     783    if (fd == -1)
     784      {
     785        int save_err = errno;
     786  
     787        if (errno == EISDIR)
     788  	{
     789  	  sprintf (fname, "%1$s%2$s/SYS_%2$s", output_path, category);
     790  	  unlink (fname);
     791  	  fd = creat (fname, 0666);
     792  	  if (fd == -1)
     793  	    save_err = errno;
     794  	}
     795  
     796        if (fd == -1)
     797  	{
     798  	  record_error (0, save_err, _("\
     799  cannot open output file `%s' for category `%s'"), fname, category);
     800  	  free (fname);
     801  	  return;
     802  	}
     803      }
     804  
     805  #ifdef UIO_MAXIOV
     806    maxiov = UIO_MAXIOV;
     807  #else
     808    maxiov = sysconf (_SC_UIO_MAXIOV);
     809  #endif
     810  
     811    /* Write the data using writev.  But we must take care for the
     812       limitation of the implementation.  */
     813    for (cnt = 0; cnt < n_elem; cnt += step)
     814      {
     815        step = n_elem - cnt;
     816        if (maxiov > 0)
     817  	step = MIN (maxiov, step);
     818  
     819        if (writev (fd, &vec[cnt], step) < 0)
     820  	{
     821  	  record_error (0, errno, _("\
     822  failure while writing data for category `%s'"), category);
     823  	  break;
     824  	}
     825      }
     826  
     827    close (fd);
     828  
     829    /* Compare the file with the locale data files for the same category
     830       in other locales, and see if we can reuse it, to save disk space.
     831       If the user specified --no-hard-links to localedef then hard_links
     832       is false, other_paths remains NULL and we skip the optimization
     833       below.  The use of --no-hard-links is distribution specific since
     834       some distros have post-processing hard-link steps and so doing this
     835       here is a waste of time.  Worse than a waste of time in rpm-based
     836       distributions it can result in build determinism issues from
     837       build-to-build since some files may get a hard link in one pass but
     838       not in another (if the files happened to be created in parallel).  */
     839    if (hard_links)
     840      other_paths = siblings (output_path);
     841  
     842    /* If there are other paths, then walk the sibling paths looking for
     843       files with the same content so we can hard link and reduce disk
     844       space usage.  */
     845    if (other_paths != NULL)
     846      {
     847        struct stat64 fname_stat;
     848  
     849        if (lstat64 (fname, &fname_stat) >= 0
     850  	  && S_ISREG (fname_stat.st_mode))
     851  	{
     852  	  const char *fname_tail = fname + strlen (output_path);
     853  	  const char **other_p;
     854  	  int seen_count;
     855  	  ino_t *seen_inodes;
     856  
     857  	  seen_count = 0;
     858  	  for (other_p = other_paths; *other_p; other_p++)
     859  	    seen_count++;
     860  	  seen_inodes = (ino_t *) xmalloc (seen_count * sizeof (ino_t));
     861  	  seen_count = 0;
     862  
     863  	  for (other_p = other_paths; *other_p; other_p++)
     864  	    {
     865  	      const char *other_path = *other_p;
     866  	      size_t other_path_len = strlen (other_path);
     867  	      char *other_fname;
     868  	      struct stat64 other_fname_stat;
     869  
     870  	      other_fname =
     871  		(char *) xmalloc (other_path_len + strlen (fname_tail) + 1);
     872  	      memcpy (other_fname, other_path, other_path_len);
     873  	      strcpy (other_fname + other_path_len, fname_tail);
     874  
     875  	      if (lstat64 (other_fname, &other_fname_stat) >= 0
     876  		  && S_ISREG (other_fname_stat.st_mode)
     877  		  /* Consider only files on the same device.
     878  		     Otherwise hard linking won't work anyway.  */
     879  		  && other_fname_stat.st_dev == fname_stat.st_dev
     880  		  /* Consider only files with the same permissions.
     881  		     Otherwise there are security risks.  */
     882  		  && other_fname_stat.st_uid == fname_stat.st_uid
     883  		  && other_fname_stat.st_gid == fname_stat.st_gid
     884  		  && other_fname_stat.st_mode == fname_stat.st_mode
     885  		  /* Don't compare fname with itself.  */
     886  		  && other_fname_stat.st_ino != fname_stat.st_ino
     887  		  /* Files must have the same size, otherwise they
     888  		     cannot be the same.  */
     889  		  && other_fname_stat.st_size == fname_stat.st_size)
     890  		{
     891  		  /* Skip this file if we have already read it (under a
     892  		     different name).  */
     893  		  int i;
     894  
     895  		  for (i = seen_count - 1; i >= 0; i--)
     896  		    if (seen_inodes[i] == other_fname_stat.st_ino)
     897  		      break;
     898  		  if (i < 0)
     899  		    {
     900  		      /* Now compare fname and other_fname for real.  */
     901  		      blksize_t blocksize;
     902  
     903  #ifdef _STATBUF_ST_BLKSIZE
     904  		      blocksize = MAX (fname_stat.st_blksize,
     905  				       other_fname_stat.st_blksize);
     906  		      if (blocksize > 8 * 1024)
     907  			blocksize = 8 * 1024;
     908  #else
     909  		      blocksize = 8 * 1024;
     910  #endif
     911  
     912  		      if (compare_files (fname, other_fname,
     913  					 fname_stat.st_size, blocksize) == 0)
     914  			{
     915  			  /* Found! other_fname is identical to fname.  */
     916  			  /* Link other_fname to fname.  But use a temporary
     917  			     file, in case hard links don't work on the
     918  			     particular filesystem.  */
     919  			  char * tmp_fname =
     920  			    (char *) xmalloc (strlen (fname) + 4 + 1);
     921  
     922  			  strcpy (stpcpy (tmp_fname, fname), ".tmp");
     923  
     924  			  if (link (other_fname, tmp_fname) >= 0)
     925  			    {
     926  			      unlink (fname);
     927  			      if (rename (tmp_fname, fname) < 0)
     928  				{
     929  				  record_error (0, errno, _("\
     930  cannot create output file `%s' for category `%s'"), fname, category);
     931  				}
     932  			      free (tmp_fname);
     933  			      free (other_fname);
     934  			      break;
     935  			    }
     936  			  free (tmp_fname);
     937  			}
     938  
     939  		      /* Don't compare with this file a second time.  */
     940  		      seen_inodes[seen_count++] = other_fname_stat.st_ino;
     941  		    }
     942  		}
     943  	      free (other_fname);
     944  	    }
     945  	  free (seen_inodes);
     946  	}
     947      }
     948  
     949    free (fname);
     950  }
     951  
     952  
     953  /* General handling of `copy'.  */
     954  void
     955  handle_copy (struct linereader *ldfile, const struct charmap_t *charmap,
     956  	     const char *repertoire_name, struct localedef_t *result,
     957  	     enum token_t token, int locale, const char *locale_name,
     958  	     int ignore_content)
     959  {
     960    struct token *now;
     961    int warned = 0;
     962  
     963    now = lr_token (ldfile, charmap, result, NULL, verbose);
     964    if (now->tok != tok_string)
     965      lr_error (ldfile, _("expecting string argument for `copy'"));
     966    else if (!ignore_content)
     967      {
     968        if (now->val.str.startmb == NULL)
     969  	lr_error (ldfile, _("\
     970  locale name should consist only of portable characters"));
     971        else
     972  	{
     973  	  (void) add_to_readlist (locale, now->val.str.startmb,
     974  				  repertoire_name, 1, NULL);
     975  	  result->copy_name[locale] = now->val.str.startmb;
     976  	}
     977      }
     978  
     979    lr_ignore_rest (ldfile, now->tok == tok_string);
     980  
     981    /* The rest of the line must be empty and the next keyword must be
     982       `END xxx'.  */
     983    while ((now = lr_token (ldfile, charmap, result, NULL, verbose))->tok
     984  	 != tok_end && now->tok != tok_eof)
     985      {
     986        if (warned == 0)
     987  	{
     988  	  lr_error (ldfile, _("\
     989  no other keyword shall be specified when `copy' is used"));
     990  	  warned = 1;
     991  	}
     992  
     993        lr_ignore_rest (ldfile, 0);
     994      }
     995  
     996    if (now->tok != tok_eof)
     997      {
     998        /* Handle `END xxx'.  */
     999        now = lr_token (ldfile, charmap, result, NULL, verbose);
    1000  
    1001        if (now->tok != token)
    1002  	lr_error (ldfile, _("\
    1003  `%1$s' definition does not end with `END %1$s'"), locale_name);
    1004  
    1005        lr_ignore_rest (ldfile, now->tok == token);
    1006      }
    1007    else
    1008      /* When we come here we reached the end of the file.  */
    1009      lr_error (ldfile, _("%s: premature end of file"), locale_name);
    1010  }