Blame locale/programs/locfile.c

Packit Service 82fcde
/* Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
Packit Service 82fcde
Packit Service 82fcde
   This program is free software; you can redistribute it and/or modify
Packit Service 82fcde
   it under the terms of the GNU General Public License as published
Packit Service 82fcde
   by the Free Software Foundation; version 2 of the License, or
Packit Service 82fcde
   (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   This program is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 82fcde
   GNU General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU General Public License
Packit Service 82fcde
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#ifdef HAVE_CONFIG_H
Packit Service 82fcde
# include <config.h>
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
#include <dirent.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <stdbool.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sys/param.h>
Packit Service 82fcde
#include <sys/stat.h>
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <wchar.h>
Packit Service 82fcde
Packit Service 82fcde
#include "../../crypt/md5.h"
Packit Service 82fcde
#include "localedef.h"
Packit Service 82fcde
#include "localeinfo.h"
Packit Service 82fcde
#include "locfile.h"
Packit Service 82fcde
#include "simple-hash.h"
Packit Service 82fcde
Packit Service 82fcde
#include "locfile-kw.h"
Packit Service 82fcde
Packit Service 82fcde
#define obstack_chunk_alloc xmalloc
Packit Service 82fcde
#define obstack_chunk_free free
Packit Service 82fcde
Packit Service 82fcde
/* Temporary storage of the locale data before writing it to the archive.  */
Packit Service 82fcde
static locale_data_t to_archive;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
Packit Service 82fcde
{
Packit Service 82fcde
  const char *filename = result->name;
Packit Service 82fcde
  const char *repertoire_name = result->repertoire_name;
Packit Service 82fcde
  int locale_mask = result->needed & ~result->avail;
Packit Service 82fcde
  struct linereader *ldfile;
Packit Service 82fcde
  int not_here = ALL_LOCALES;
Packit Service 82fcde
Packit Service 82fcde
  /* If no repertoire name was specified use the global one.  */
Packit Service 82fcde
  if (repertoire_name == NULL)
Packit Service 82fcde
    repertoire_name = repertoire_global;
Packit Service 82fcde
Packit Service 82fcde
  /* Open the locale definition file.  */
Packit Service 82fcde
  ldfile = lr_open (filename, locfile_hash);
Packit Service 82fcde
  if (ldfile == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (filename != NULL && filename[0] != '/')
Packit Service 82fcde
	{
Packit Service 82fcde
	  char *i18npath = getenv ("I18NPATH");
Packit Service 82fcde
	  if (i18npath != NULL && *i18npath != '\0')
Packit Service 82fcde
	    {
Packit Service 82fcde
	      const size_t pathlen = strlen (i18npath);
Packit Service 82fcde
	      char i18npathbuf[pathlen + 1];
Packit Service 82fcde
	      char path[strlen (filename) + 1 + pathlen
Packit Service 82fcde
			+ sizeof ("/locales/") - 1];
Packit Service 82fcde
	      char *next;
Packit Service 82fcde
	      i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
Packit Service 82fcde
Packit Service 82fcde
	      while (ldfile == NULL
Packit Service 82fcde
		     && (next = strsep (&i18npath, ":")) != NULL)
Packit Service 82fcde
		{
Packit Service 82fcde
		  stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
Packit Service 82fcde
Packit Service 82fcde
		  ldfile = lr_open (path, locfile_hash);
Packit Service 82fcde
Packit Service 82fcde
		  if (ldfile == NULL)
Packit Service 82fcde
		    {
Packit Service 82fcde
		      stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
Packit Service 82fcde
Packit Service 82fcde
		      ldfile = lr_open (path, locfile_hash);
Packit Service 82fcde
		    }
Packit Service 82fcde
		}
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  /* Test in the default directory.  */
Packit Service 82fcde
	  if (ldfile == NULL)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
Packit Service 82fcde
Packit Service 82fcde
	      stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
Packit Service 82fcde
	      ldfile = lr_open (path, locfile_hash);
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (ldfile == NULL)
Packit Service 82fcde
	return 1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
    /* Parse locale definition file and store result in RESULT.  */
Packit Service 82fcde
  while (1)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct token *now = lr_token (ldfile, charmap, NULL, NULL, verbose);
Packit Service 82fcde
      enum token_t nowtok = now->tok;
Packit Service 82fcde
      struct token *arg;
Packit Service 82fcde
Packit Service 82fcde
      if (nowtok == tok_eof)
Packit Service 82fcde
	break;
Packit Service 82fcde
Packit Service 82fcde
      if (nowtok == tok_eol)
Packit Service 82fcde
	/* Ignore empty lines.  */
Packit Service 82fcde
	continue;
Packit Service 82fcde
Packit Service 82fcde
      switch (nowtok)
Packit Service 82fcde
	{
Packit Service 82fcde
	case tok_escape_char:
Packit Service 82fcde
	case tok_comment_char:
Packit Service 82fcde
	  /* We need an argument.  */
Packit Service 82fcde
	  arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
Packit Service 82fcde
Packit Service 82fcde
	  if (arg->tok != tok_ident)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      SYNTAX_ERROR (_("bad argument"));
Packit Service 82fcde
	      continue;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (arg->val.str.lenmb != 1)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      lr_error (ldfile, _("\
Packit Service 82fcde
argument to `%s' must be a single character"),
Packit Service 82fcde
			nowtok == tok_escape_char
Packit Service 82fcde
			? "escape_char" : "comment_char");
Packit Service 82fcde
Packit Service 82fcde
	      lr_ignore_rest (ldfile, 0);
Packit Service 82fcde
	      continue;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (nowtok == tok_escape_char)
Packit Service 82fcde
	    ldfile->escape_char = *arg->val.str.startmb;
Packit Service 82fcde
	  else
Packit Service 82fcde
	    ldfile->comment_char = *arg->val.str.startmb;
Packit Service 82fcde
	  break;
Packit Service 82fcde
Packit Service 82fcde
	case tok_repertoiremap:
Packit Service 82fcde
	  /* We need an argument.  */
Packit Service 82fcde
	  arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
Packit Service 82fcde
Packit Service 82fcde
	  if (arg->tok != tok_ident)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      SYNTAX_ERROR (_("bad argument"));
Packit Service 82fcde
	      continue;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (repertoire_name == NULL)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      char *newp = alloca (arg->val.str.lenmb + 1);
Packit Service 82fcde
Packit Service 82fcde
	      *((char *) mempcpy (newp, arg->val.str.startmb,
Packit Service 82fcde
				  arg->val.str.lenmb)) = '\0';
Packit Service 82fcde
	      repertoire_name = newp;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  break;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_ctype:
Packit Service 82fcde
	  ctype_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
		      (locale_mask & CTYPE_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & CTYPE_LOCALE;
Packit Service 82fcde
	  not_here ^= CTYPE_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_collate:
Packit Service 82fcde
	  collate_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			(locale_mask & COLLATE_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & COLLATE_LOCALE;
Packit Service 82fcde
	  not_here ^= COLLATE_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_monetary:
Packit Service 82fcde
	  monetary_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			 (locale_mask & MONETARY_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & MONETARY_LOCALE;
Packit Service 82fcde
	  not_here ^= MONETARY_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_numeric:
Packit Service 82fcde
	  numeric_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			(locale_mask & NUMERIC_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & NUMERIC_LOCALE;
Packit Service 82fcde
	  not_here ^= NUMERIC_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_time:
Packit Service 82fcde
	  time_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
		     (locale_mask & TIME_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & TIME_LOCALE;
Packit Service 82fcde
	  not_here ^= TIME_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_messages:
Packit Service 82fcde
	  messages_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			 (locale_mask & MESSAGES_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & MESSAGES_LOCALE;
Packit Service 82fcde
	  not_here ^= MESSAGES_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_paper:
Packit Service 82fcde
	  paper_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
		      (locale_mask & PAPER_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & PAPER_LOCALE;
Packit Service 82fcde
	  not_here ^= PAPER_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_name:
Packit Service 82fcde
	  name_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
		     (locale_mask & NAME_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & NAME_LOCALE;
Packit Service 82fcde
	  not_here ^= NAME_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_address:
Packit Service 82fcde
	  address_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			(locale_mask & ADDRESS_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & ADDRESS_LOCALE;
Packit Service 82fcde
	  not_here ^= ADDRESS_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_telephone:
Packit Service 82fcde
	  telephone_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			  (locale_mask & TELEPHONE_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & TELEPHONE_LOCALE;
Packit Service 82fcde
	  not_here ^= TELEPHONE_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_measurement:
Packit Service 82fcde
	  measurement_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			    (locale_mask & MEASUREMENT_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & MEASUREMENT_LOCALE;
Packit Service 82fcde
	  not_here ^= MEASUREMENT_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	case tok_lc_identification:
Packit Service 82fcde
	  identification_read (ldfile, result, charmap, repertoire_name,
Packit Service 82fcde
			       (locale_mask & IDENTIFICATION_LOCALE) == 0);
Packit Service 82fcde
	  result->avail |= locale_mask & IDENTIFICATION_LOCALE;
Packit Service 82fcde
	  not_here ^= IDENTIFICATION_LOCALE;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
Packit Service 82fcde
	default:
Packit Service 82fcde
	  SYNTAX_ERROR (_("\
Packit Service 82fcde
syntax error: not inside a locale definition section"));
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* The rest of the line must be empty.  */
Packit Service 82fcde
      lr_ignore_rest (ldfile, 1);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* We read all of the file.  */
Packit Service 82fcde
  lr_close (ldfile);
Packit Service 82fcde
Packit Service 82fcde
  /* Mark the categories which are not contained in the file.  We assume
Packit Service 82fcde
     them to be available and the default data will be used.  */
Packit Service 82fcde
  result->avail |= not_here;
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Semantic checking of locale specifications.  */
Packit Service 82fcde
Packit Service 82fcde
static void (*const check_funcs[]) (struct localedef_t *,
Packit Service 82fcde
				    const struct charmap_t *) =
Packit Service 82fcde
{
Packit Service 82fcde
  [LC_CTYPE] = ctype_finish,
Packit Service 82fcde
  [LC_COLLATE] = collate_finish,
Packit Service 82fcde
  [LC_MESSAGES] = messages_finish,
Packit Service 82fcde
  [LC_MONETARY] = monetary_finish,
Packit Service 82fcde
  [LC_NUMERIC] = numeric_finish,
Packit Service 82fcde
  [LC_TIME] = time_finish,
Packit Service 82fcde
  [LC_PAPER] = paper_finish,
Packit Service 82fcde
  [LC_NAME] = name_finish,
Packit Service 82fcde
  [LC_ADDRESS] = address_finish,
Packit Service 82fcde
  [LC_TELEPHONE] = telephone_finish,
Packit Service 82fcde
  [LC_MEASUREMENT] = measurement_finish,
Packit Service 82fcde
  [LC_IDENTIFICATION] = identification_finish
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
check_all_categories (struct localedef_t *definitions,
Packit Service 82fcde
		      const struct charmap_t *charmap)
Packit Service 82fcde
{
Packit Service 82fcde
  int cnt;
Packit Service 82fcde
Packit Service 82fcde
  for (cnt = 0; cnt < sizeof (check_funcs) / sizeof (check_funcs[0]); ++cnt)
Packit Service 82fcde
    if (check_funcs[cnt] != NULL)
Packit Service 82fcde
      check_funcs[cnt] (definitions, charmap);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Writing the locale data files.  All files use the same output_path.  */
Packit Service 82fcde
Packit Service 82fcde
static void (*const write_funcs[]) (struct localedef_t *,
Packit Service 82fcde
				    const struct charmap_t *, const char *) =
Packit Service 82fcde
{
Packit Service 82fcde
  [LC_CTYPE] = ctype_output,
Packit Service 82fcde
  [LC_COLLATE] = collate_output,
Packit Service 82fcde
  [LC_MESSAGES] = messages_output,
Packit Service 82fcde
  [LC_MONETARY] = monetary_output,
Packit Service 82fcde
  [LC_NUMERIC] = numeric_output,
Packit Service 82fcde
  [LC_TIME] = time_output,
Packit Service 82fcde
  [LC_PAPER] = paper_output,
Packit Service 82fcde
  [LC_NAME] = name_output,
Packit Service 82fcde
  [LC_ADDRESS] = address_output,
Packit Service 82fcde
  [LC_TELEPHONE] = telephone_output,
Packit Service 82fcde
  [LC_MEASUREMENT] = measurement_output,
Packit Service 82fcde
  [LC_IDENTIFICATION] = identification_output
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
write_all_categories (struct localedef_t *definitions,
Packit Service 82fcde
		      const struct charmap_t *charmap, const char *locname,
Packit Service 82fcde
		      const char *output_path)
Packit Service 82fcde
{
Packit Service 82fcde
  int cnt;
Packit Service 82fcde
Packit Service 82fcde
  for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
Packit Service 82fcde
    if (write_funcs[cnt] != NULL)
Packit Service 82fcde
      write_funcs[cnt] (definitions, charmap, output_path);
Packit Service 82fcde
Packit Service 82fcde
  if (! no_archive)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The data has to be added to the archive.  Do this now.  */
Packit Service 82fcde
      struct locarhandle ah;
Packit Service 82fcde
Packit Service 82fcde
      /* Open the archive.  This call never returns if we cannot
Packit Service 82fcde
	 successfully open the archive.  */
Packit Service 82fcde
      ah.fname = NULL;
Packit Service 82fcde
      open_archive (&ah, false);
Packit Service 82fcde
Packit Service 82fcde
      if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
Packit Service 82fcde
	error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
Packit Service 82fcde
Packit Service 82fcde
      /* We are done.  */
Packit Service 82fcde
      close_archive (&ah;;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Return a NULL terminated list of the directories next to output_path
Packit Service 82fcde
   that have the same owner, group, permissions and device as output_path.  */
Packit Service 82fcde
static const char **
Packit Service 82fcde
siblings_uncached (const char *output_path)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t len;
Packit Service 82fcde
  char *base, *p;
Packit Service 82fcde
  struct stat64 output_stat;
Packit Service 82fcde
  DIR *dirp;
Packit Service 82fcde
  int nelems;
Packit Service 82fcde
  const char **elems;
Packit Service 82fcde
Packit Service 82fcde
  /* Remove trailing slashes and trailing pathname component.  */
Packit Service 82fcde
  len = strlen (output_path);
Packit Service 82fcde
  base = (char *) alloca (len);
Packit Service 82fcde
  memcpy (base, output_path, len);
Packit Service 82fcde
  p = base + len;
Packit Service 82fcde
  while (p > base && p[-1] == '/')
Packit Service 82fcde
    p--;
Packit Service 82fcde
  if (p == base)
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
  do
Packit Service 82fcde
    p--;
Packit Service 82fcde
  while (p > base && p[-1] != '/');
Packit Service 82fcde
  if (p == base)
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
  *--p = '\0';
Packit Service 82fcde
  len = p - base;
Packit Service 82fcde
Packit Service 82fcde
  /* Get the properties of output_path.  */
Packit Service 82fcde
  if (lstat64 (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  /* Iterate through the directories in base directory.  */
Packit Service 82fcde
  dirp = opendir (base);
Packit Service 82fcde
  if (dirp == NULL)
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
  nelems = 0;
Packit Service 82fcde
  elems = NULL;
Packit Service 82fcde
  for (;;)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct dirent64 *other_dentry;
Packit Service 82fcde
      const char *other_name;
Packit Service 82fcde
      char *other_path;
Packit Service 82fcde
      struct stat64 other_stat;
Packit Service 82fcde
Packit Service 82fcde
      other_dentry = readdir64 (dirp);
Packit Service 82fcde
      if (other_dentry == NULL)
Packit Service 82fcde
	break;
Packit Service 82fcde
Packit Service 82fcde
      other_name = other_dentry->d_name;
Packit Service 82fcde
      if (strcmp (other_name, ".") == 0 || strcmp (other_name, "..") == 0)
Packit Service 82fcde
	continue;
Packit Service 82fcde
Packit Service 82fcde
      other_path = (char *) xmalloc (len + 1 + strlen (other_name) + 2);
Packit Service 82fcde
      memcpy (other_path, base, len);
Packit Service 82fcde
      other_path[len] = '/';
Packit Service 82fcde
      strcpy (other_path + len + 1, other_name);
Packit Service 82fcde
Packit Service 82fcde
      if (lstat64 (other_path, &other_stat) >= 0
Packit Service 82fcde
	  && S_ISDIR (other_stat.st_mode)
Packit Service 82fcde
	  && other_stat.st_uid == output_stat.st_uid
Packit Service 82fcde
	  && other_stat.st_gid == output_stat.st_gid
Packit Service 82fcde
	  && other_stat.st_mode == output_stat.st_mode
Packit Service 82fcde
	  && other_stat.st_dev == output_stat.st_dev)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Found a subdirectory.  Add a trailing slash and store it.  */
Packit Service 82fcde
	  p = other_path + len + 1 + strlen (other_name);
Packit Service 82fcde
	  *p++ = '/';
Packit Service 82fcde
	  *p = '\0';
Packit Service 82fcde
	  elems = (const char **) xrealloc ((char *) elems,
Packit Service 82fcde
					    (nelems + 2) * sizeof (char **));
Packit Service 82fcde
	  elems[nelems++] = other_path;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	free (other_path);
Packit Service 82fcde
    }
Packit Service 82fcde
  closedir (dirp);
Packit Service 82fcde
Packit Service 82fcde
  if (elems != NULL)
Packit Service 82fcde
    elems[nelems] = NULL;
Packit Service 82fcde
  return elems;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Return a NULL terminated list of the directories next to output_path
Packit Service 82fcde
   that have the same owner, group, permissions and device as output_path.
Packit Service 82fcde
   Cache the result for future calls.  */
Packit Service 82fcde
static const char **
Packit Service 82fcde
siblings (const char *output_path)
Packit Service 82fcde
{
Packit Service 82fcde
  static const char *last_output_path;
Packit Service 82fcde
  static const char **last_result;
Packit Service 82fcde
Packit Service 82fcde
  if (output_path != last_output_path)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (last_result != NULL)
Packit Service 82fcde
	{
Packit Service 82fcde
	  const char **p;
Packit Service 82fcde
Packit Service 82fcde
	  for (p = last_result; *p != NULL; p++)
Packit Service 82fcde
	    free ((char *) *p);
Packit Service 82fcde
	  free (last_result);
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      last_output_path = output_path;
Packit Service 82fcde
      last_result = siblings_uncached (output_path);
Packit Service 82fcde
    }
Packit Service 82fcde
  return last_result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Read as many bytes from a file descriptor as possible.  */
Packit Service 82fcde
static ssize_t
Packit Service 82fcde
full_read (int fd, void *bufarea, size_t nbyte)
Packit Service 82fcde
{
Packit Service 82fcde
  char *buf = (char *) bufarea;
Packit Service 82fcde
Packit Service 82fcde
  while (nbyte > 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      ssize_t retval = read (fd, buf, nbyte);
Packit Service 82fcde
Packit Service 82fcde
      if (retval == 0)
Packit Service 82fcde
	break;
Packit Service 82fcde
      else if (retval > 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  buf += retval;
Packit Service 82fcde
	  nbyte -= retval;
Packit Service 82fcde
	}
Packit Service 82fcde
      else if (errno != EINTR)
Packit Service 82fcde
	return retval;
Packit Service 82fcde
    }
Packit Service 82fcde
  return buf - (char *) bufarea;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Compare the contents of two regular files of the same size.  Return 0
Packit Service 82fcde
   if they are equal, 1 if they are different, or -1 if an error occurs.  */
Packit Service 82fcde
static int
Packit Service 82fcde
compare_files (const char *filename1, const char *filename2, size_t size,
Packit Service 82fcde
	       size_t blocksize)
Packit Service 82fcde
{
Packit Service 82fcde
  int fd1, fd2;
Packit Service 82fcde
  int ret = -1;
Packit Service 82fcde
Packit Service 82fcde
  fd1 = open (filename1, O_RDONLY);
Packit Service 82fcde
  if (fd1 >= 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      fd2 = open (filename2, O_RDONLY);
Packit Service 82fcde
      if (fd2 >= 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  char *buf1 = (char *) xmalloc (2 * blocksize);
Packit Service 82fcde
	  char *buf2 = buf1 + blocksize;
Packit Service 82fcde
Packit Service 82fcde
	  ret = 0;
Packit Service 82fcde
	  while (size > 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      size_t bytes = (size < blocksize ? size : blocksize);
Packit Service 82fcde
Packit Service 82fcde
	      if (full_read (fd1, buf1, bytes) < (ssize_t) bytes)
Packit Service 82fcde
		{
Packit Service 82fcde
		  ret = -1;
Packit Service 82fcde
		  break;
Packit Service 82fcde
		}
Packit Service 82fcde
	      if (full_read (fd2, buf2, bytes) < (ssize_t) bytes)
Packit Service 82fcde
		{
Packit Service 82fcde
		  ret = -1;
Packit Service 82fcde
		  break;
Packit Service 82fcde
		}
Packit Service 82fcde
	      if (memcmp (buf1, buf2, bytes) != 0)
Packit Service 82fcde
		{
Packit Service 82fcde
		  ret = 1;
Packit Service 82fcde
		  break;
Packit Service 82fcde
		}
Packit Service 82fcde
	      size -= bytes;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  free (buf1);
Packit Service 82fcde
	  close (fd2);
Packit Service 82fcde
	}
Packit Service 82fcde
      close (fd1);
Packit Service 82fcde
    }
Packit Service 82fcde
  return ret;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* True if the locale files use the opposite endianness to the
Packit Service 82fcde
   machine running localedef.  */
Packit Service 82fcde
bool swap_endianness_p;
Packit Service 82fcde
Packit Service 82fcde
/* When called outside a start_locale_structure/end_locale_structure
Packit Service 82fcde
   or start_locale_prelude/end_locale_prelude block, record that the
Packit Service 82fcde
   next byte in FILE's obstack will be the first byte of a new element.
Packit Service 82fcde
   Do likewise for the first call inside a start_locale_structure/
Packit Service 82fcde
   end_locale_structure block.  */
Packit Service 82fcde
static void
Packit Service 82fcde
record_offset (struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  if (file->structure_stage < 2)
Packit Service 82fcde
    {
Packit Service 82fcde
      assert (file->next_element < file->n_elements);
Packit Service 82fcde
      file->offsets[file->next_element++]
Packit Service 82fcde
	= (obstack_object_size (&file->data)
Packit Service 82fcde
	   + (file->n_elements + 2) * sizeof (uint32_t));
Packit Service 82fcde
      if (file->structure_stage == 1)
Packit Service 82fcde
	file->structure_stage = 2;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Initialize FILE for a new output file.  N_ELEMENTS is the number
Packit Service 82fcde
   of elements in the file.  */
Packit Service 82fcde
void
Packit Service 82fcde
init_locale_data (struct locale_file *file, size_t n_elements)
Packit Service 82fcde
{
Packit Service 82fcde
  file->n_elements = n_elements;
Packit Service 82fcde
  file->next_element = 0;
Packit Service 82fcde
  file->offsets = xmalloc (sizeof (uint32_t) * n_elements);
Packit Service 82fcde
  obstack_init (&file->data);
Packit Service 82fcde
  file->structure_stage = 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Align the size of FILE's obstack object to BOUNDARY bytes.  */
Packit Service 82fcde
void
Packit Service 82fcde
align_locale_data (struct locale_file *file, size_t boundary)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t size = -obstack_object_size (&file->data) & (boundary - 1);
Packit Service 82fcde
  obstack_blank (&file->data, size);
Packit Service 82fcde
  memset (obstack_next_free (&file->data) - size, 0, size);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Record that FILE's next element contains no data.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_empty (struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Record that FILE's next element consists of SIZE bytes starting at DATA.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_raw_data (struct locale_file *file, const void *data, size_t size)
Packit Service 82fcde
{
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  obstack_grow (&file->data, data, size);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Finish the current object on OBSTACK and use it as the data for FILE's
Packit Service 82fcde
   next element.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_raw_obstack (struct locale_file *file, struct obstack *obstack)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t size = obstack_object_size (obstack);
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  obstack_grow (&file->data, obstack_finish (obstack), size);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Use STRING as FILE's next element.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_string (struct locale_file *file, const char *string)
Packit Service 82fcde
{
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  obstack_grow (&file->data, string, strlen (string) + 1);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Likewise for wide strings.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_wstring (struct locale_file *file, const uint32_t *string)
Packit Service 82fcde
{
Packit Service 82fcde
  add_locale_uint32_array (file, string, wcslen ((const wchar_t *) string) + 1);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Record that FILE's next element is the 32-bit integer VALUE.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_uint32 (struct locale_file *file, uint32_t value)
Packit Service 82fcde
{
Packit Service 82fcde
  align_locale_data (file, LOCFILE_ALIGN);
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  value = maybe_swap_uint32 (value);
Packit Service 82fcde
  obstack_grow (&file->data, &value, sizeof (value));
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Record that FILE's next element is an array of N_ELEMS integers
Packit Service 82fcde
   starting at DATA.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_uint32_array (struct locale_file *file,
Packit Service 82fcde
			 const uint32_t *data, size_t n_elems)
Packit Service 82fcde
{
Packit Service 82fcde
  align_locale_data (file, LOCFILE_ALIGN);
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
Packit Service 82fcde
  maybe_swap_uint32_obstack (&file->data, n_elems);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Record that FILE's next element is the single byte given by VALUE.  */
Packit Service 82fcde
void
Packit Service 82fcde
add_locale_char (struct locale_file *file, char value)
Packit Service 82fcde
{
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  obstack_1grow (&file->data, value);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Start building an element that contains several different pieces of data.
Packit Service 82fcde
   Subsequent calls to add_locale_* will add data to the same element up
Packit Service 82fcde
   till the next call to end_locale_structure.  The element's alignment
Packit Service 82fcde
   is dictated by the first piece of data added to it.  */
Packit Service 82fcde
void
Packit Service 82fcde
start_locale_structure (struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  assert (file->structure_stage == 0);
Packit Service 82fcde
  file->structure_stage = 1;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Finish a structure element that was started by start_locale_structure.
Packit Service 82fcde
   Empty structures are OK and behave like add_locale_empty.  */
Packit Service 82fcde
void
Packit Service 82fcde
end_locale_structure (struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  record_offset (file);
Packit Service 82fcde
  assert (file->structure_stage == 2);
Packit Service 82fcde
  file->structure_stage = 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Start building data that goes before the next element's recorded offset.
Packit Service 82fcde
   Subsequent calls to add_locale_* will add data to the file without
Packit Service 82fcde
   treating any of it as the start of a new element.  Calling
Packit Service 82fcde
   end_locale_prelude switches back to the usual behavior.  */
Packit Service 82fcde
void
Packit Service 82fcde
start_locale_prelude (struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  assert (file->structure_stage == 0);
Packit Service 82fcde
  file->structure_stage = 3;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* End a block started by start_locale_prelude.  */
Packit Service 82fcde
void
Packit Service 82fcde
end_locale_prelude (struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  assert (file->structure_stage == 3);
Packit Service 82fcde
  file->structure_stage = 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Write a locale file, with contents given by FILE.  */
Packit Service 82fcde
void
Packit Service 82fcde
write_locale_data (const char *output_path, int catidx, const char *category,
Packit Service 82fcde
		   struct locale_file *file)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t cnt, step, maxiov;
Packit Service 82fcde
  int fd;
Packit Service 82fcde
  char *fname;
Packit Service 82fcde
  const char **other_paths;
Packit Service 82fcde
  uint32_t header[2];
Packit Service 82fcde
  size_t n_elem;
Packit Service 82fcde
  struct iovec vec[3];
Packit Service 82fcde
Packit Service 82fcde
  assert (file->n_elements == file->next_element);
Packit Service 82fcde
  header[0] = LIMAGIC (catidx);
Packit Service 82fcde
  header[1] = file->n_elements;
Packit Service 82fcde
  vec[0].iov_len = sizeof (header);
Packit Service 82fcde
  vec[0].iov_base = header;
Packit Service 82fcde
  vec[1].iov_len = sizeof (uint32_t) * file->n_elements;
Packit Service 82fcde
  vec[1].iov_base = file->offsets;
Packit Service 82fcde
  vec[2].iov_len = obstack_object_size (&file->data);
Packit Service 82fcde
  vec[2].iov_base = obstack_finish (&file->data);
Packit Service 82fcde
  maybe_swap_uint32_array (vec[0].iov_base, 2);
Packit Service 82fcde
  maybe_swap_uint32_array (vec[1].iov_base, file->n_elements);
Packit Service 82fcde
  n_elem = 3;
Packit Service 82fcde
  if (! no_archive)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The data will be added to the archive.  For now we simply
Packit Service 82fcde
	 generate the image which will be written.  First determine
Packit Service 82fcde
	 the size.  */
Packit Service 82fcde
      int cnt;
Packit Service 82fcde
      void *endp;
Packit Service 82fcde
Packit Service 82fcde
      to_archive[catidx].size = 0;
Packit Service 82fcde
      for (cnt = 0; cnt < n_elem; ++cnt)
Packit Service 82fcde
	to_archive[catidx].size += vec[cnt].iov_len;
Packit Service 82fcde
Packit Service 82fcde
      /* Allocate the memory for it.  */
Packit Service 82fcde
      to_archive[catidx].addr = xmalloc (to_archive[catidx].size);
Packit Service 82fcde
Packit Service 82fcde
      /* Fill it in.  */
Packit Service 82fcde
      for (cnt = 0, endp = to_archive[catidx].addr; cnt < n_elem; ++cnt)
Packit Service 82fcde
	endp = mempcpy (endp, vec[cnt].iov_base, vec[cnt].iov_len);
Packit Service 82fcde
Packit Service 82fcde
      /* Compute the MD5 sum for the data.  */
Packit Service 82fcde
      __md5_buffer (to_archive[catidx].addr, to_archive[catidx].size,
Packit Service 82fcde
		    to_archive[catidx].sum);
Packit Service 82fcde
Packit Service 82fcde
      return;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
Packit Service 82fcde
Packit Service 82fcde
  /* Normally we write to the directory pointed to by the OUTPUT_PATH.
Packit Service 82fcde
     But for LC_MESSAGES we have to take care for the translation
Packit Service 82fcde
     data.  This means we need to have a directory LC_MESSAGES in
Packit Service 82fcde
     which we place the file under the name SYS_LC_MESSAGES.  */
Packit Service 82fcde
  sprintf (fname, "%s%s", output_path, category);
Packit Service 82fcde
  fd = -2;
Packit Service 82fcde
  if (strcmp (category, "LC_MESSAGES") == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct stat64 st;
Packit Service 82fcde
Packit Service 82fcde
      if (stat64 (fname, &st) < 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (mkdir (fname, 0777) >= 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      fd = -1;
Packit Service 82fcde
	      errno = EISDIR;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      else if (!S_ISREG (st.st_mode))
Packit Service 82fcde
	{
Packit Service 82fcde
	  fd = -1;
Packit Service 82fcde
	  errno = EISDIR;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Create the locale file with nlinks == 1; this avoids crashing processes
Packit Service 82fcde
     which currently use the locale and damaging files belonging to other
Packit Service 82fcde
     locales as well.  */
Packit Service 82fcde
  if (fd == -2)
Packit Service 82fcde
    {
Packit Service 82fcde
      unlink (fname);
Packit Service 82fcde
      fd = creat (fname, 0666);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (fd == -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      int save_err = errno;
Packit Service 82fcde
Packit Service 82fcde
      if (errno == EISDIR)
Packit Service 82fcde
	{
Packit Service 82fcde
	  sprintf (fname, "%1$s%2$s/SYS_%2$s", output_path, category);
Packit Service 82fcde
	  unlink (fname);
Packit Service 82fcde
	  fd = creat (fname, 0666);
Packit Service 82fcde
	  if (fd == -1)
Packit Service 82fcde
	    save_err = errno;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (fd == -1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  record_error (0, save_err, _("\
Packit Service 82fcde
cannot open output file `%s' for category `%s'"), fname, category);
Packit Service 82fcde
	  free (fname);
Packit Service 82fcde
	  return;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
#ifdef UIO_MAXIOV
Packit Service 82fcde
  maxiov = UIO_MAXIOV;
Packit Service 82fcde
#else
Packit Service 82fcde
  maxiov = sysconf (_SC_UIO_MAXIOV);
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  /* Write the data using writev.  But we must take care for the
Packit Service 82fcde
     limitation of the implementation.  */
Packit Service 82fcde
  for (cnt = 0; cnt < n_elem; cnt += step)
Packit Service 82fcde
    {
Packit Service 82fcde
      step = n_elem - cnt;
Packit Service 82fcde
      if (maxiov > 0)
Packit Service 82fcde
	step = MIN (maxiov, step);
Packit Service 82fcde
Packit Service 82fcde
      if (writev (fd, &vec[cnt], step) < 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  record_error (0, errno, _("\
Packit Service 82fcde
failure while writing data for category `%s'"), category);
Packit Service 82fcde
	  break;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  close (fd);
Packit Service 82fcde
Packit Service 82fcde
  /* Compare the file with the locale data files for the same category in
Packit Service 82fcde
     other locales, and see if we can reuse it, to save disk space.  */
Packit Service 82fcde
  other_paths = siblings (output_path);
Packit Service 82fcde
  if (other_paths != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct stat64 fname_stat;
Packit Service 82fcde
Packit Service 82fcde
      if (lstat64 (fname, &fname_stat) >= 0
Packit Service 82fcde
	  && S_ISREG (fname_stat.st_mode))
Packit Service 82fcde
	{
Packit Service 82fcde
	  const char *fname_tail = fname + strlen (output_path);
Packit Service 82fcde
	  const char **other_p;
Packit Service 82fcde
	  int seen_count;
Packit Service 82fcde
	  ino_t *seen_inodes;
Packit Service 82fcde
Packit Service 82fcde
	  seen_count = 0;
Packit Service 82fcde
	  for (other_p = other_paths; *other_p; other_p++)
Packit Service 82fcde
	    seen_count++;
Packit Service 82fcde
	  seen_inodes = (ino_t *) xmalloc (seen_count * sizeof (ino_t));
Packit Service 82fcde
	  seen_count = 0;
Packit Service 82fcde
Packit Service 82fcde
	  for (other_p = other_paths; *other_p; other_p++)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      const char *other_path = *other_p;
Packit Service 82fcde
	      size_t other_path_len = strlen (other_path);
Packit Service 82fcde
	      char *other_fname;
Packit Service 82fcde
	      struct stat64 other_fname_stat;
Packit Service 82fcde
Packit Service 82fcde
	      other_fname =
Packit Service 82fcde
		(char *) xmalloc (other_path_len + strlen (fname_tail) + 1);
Packit Service 82fcde
	      memcpy (other_fname, other_path, other_path_len);
Packit Service 82fcde
	      strcpy (other_fname + other_path_len, fname_tail);
Packit Service 82fcde
Packit Service 82fcde
	      if (lstat64 (other_fname, &other_fname_stat) >= 0
Packit Service 82fcde
		  && S_ISREG (other_fname_stat.st_mode)
Packit Service 82fcde
		  /* Consider only files on the same device.
Packit Service 82fcde
		     Otherwise hard linking won't work anyway.  */
Packit Service 82fcde
		  && other_fname_stat.st_dev == fname_stat.st_dev
Packit Service 82fcde
		  /* Consider only files with the same permissions.
Packit Service 82fcde
		     Otherwise there are security risks.  */
Packit Service 82fcde
		  && other_fname_stat.st_uid == fname_stat.st_uid
Packit Service 82fcde
		  && other_fname_stat.st_gid == fname_stat.st_gid
Packit Service 82fcde
		  && other_fname_stat.st_mode == fname_stat.st_mode
Packit Service 82fcde
		  /* Don't compare fname with itself.  */
Packit Service 82fcde
		  && other_fname_stat.st_ino != fname_stat.st_ino
Packit Service 82fcde
		  /* Files must have the same size, otherwise they
Packit Service 82fcde
		     cannot be the same.  */
Packit Service 82fcde
		  && other_fname_stat.st_size == fname_stat.st_size)
Packit Service 82fcde
		{
Packit Service 82fcde
		  /* Skip this file if we have already read it (under a
Packit Service 82fcde
		     different name).  */
Packit Service 82fcde
		  int i;
Packit Service 82fcde
Packit Service 82fcde
		  for (i = seen_count - 1; i >= 0; i--)
Packit Service 82fcde
		    if (seen_inodes[i] == other_fname_stat.st_ino)
Packit Service 82fcde
		      break;
Packit Service 82fcde
		  if (i < 0)
Packit Service 82fcde
		    {
Packit Service 82fcde
		      /* Now compare fname and other_fname for real.  */
Packit Service 82fcde
		      blksize_t blocksize;
Packit Service 82fcde
Packit Service 82fcde
#ifdef _STATBUF_ST_BLKSIZE
Packit Service 82fcde
		      blocksize = MAX (fname_stat.st_blksize,
Packit Service 82fcde
				       other_fname_stat.st_blksize);
Packit Service 82fcde
		      if (blocksize > 8 * 1024)
Packit Service 82fcde
			blocksize = 8 * 1024;
Packit Service 82fcde
#else
Packit Service 82fcde
		      blocksize = 8 * 1024;
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
		      if (compare_files (fname, other_fname,
Packit Service 82fcde
					 fname_stat.st_size, blocksize) == 0)
Packit Service 82fcde
			{
Packit Service 82fcde
			  /* Found! other_fname is identical to fname.  */
Packit Service 82fcde
			  /* Link other_fname to fname.  But use a temporary
Packit Service 82fcde
			     file, in case hard links don't work on the
Packit Service 82fcde
			     particular filesystem.  */
Packit Service 82fcde
			  char * tmp_fname =
Packit Service 82fcde
			    (char *) xmalloc (strlen (fname) + 4 + 1);
Packit Service 82fcde
Packit Service 82fcde
			  strcpy (stpcpy (tmp_fname, fname), ".tmp");
Packit Service 82fcde
Packit Service 82fcde
			  if (link (other_fname, tmp_fname) >= 0)
Packit Service 82fcde
			    {
Packit Service 82fcde
			      unlink (fname);
Packit Service 82fcde
			      if (rename (tmp_fname, fname) < 0)
Packit Service 82fcde
				{
Packit Service 82fcde
				  record_error (0, errno, _("\
Packit Service 82fcde
cannot create output file `%s' for category `%s'"), fname, category);
Packit Service 82fcde
				}
Packit Service 82fcde
			      free (tmp_fname);
Packit Service 82fcde
			      free (other_fname);
Packit Service 82fcde
			      break;
Packit Service 82fcde
			    }
Packit Service 82fcde
			  free (tmp_fname);
Packit Service 82fcde
			}
Packit Service 82fcde
Packit Service 82fcde
		      /* Don't compare with this file a second time.  */
Packit Service 82fcde
		      seen_inodes[seen_count++] = other_fname_stat.st_ino;
Packit Service 82fcde
		    }
Packit Service 82fcde
		}
Packit Service 82fcde
	      free (other_fname);
Packit Service 82fcde
	    }
Packit Service 82fcde
	  free (seen_inodes);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  free (fname);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* General handling of `copy'.  */
Packit Service 82fcde
void
Packit Service 82fcde
handle_copy (struct linereader *ldfile, const struct charmap_t *charmap,
Packit Service 82fcde
	     const char *repertoire_name, struct localedef_t *result,
Packit Service 82fcde
	     enum token_t token, int locale, const char *locale_name,
Packit Service 82fcde
	     int ignore_content)
Packit Service 82fcde
{
Packit Service 82fcde
  struct token *now;
Packit Service 82fcde
  int warned = 0;
Packit Service 82fcde
Packit Service 82fcde
  now = lr_token (ldfile, charmap, result, NULL, verbose);
Packit Service 82fcde
  if (now->tok != tok_string)
Packit Service 82fcde
    lr_error (ldfile, _("expecting string argument for `copy'"));
Packit Service 82fcde
  else if (!ignore_content)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (now->val.str.startmb == NULL)
Packit Service 82fcde
	lr_error (ldfile, _("\
Packit Service 82fcde
locale name should consist only of portable characters"));
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  (void) add_to_readlist (locale, now->val.str.startmb,
Packit Service 82fcde
				  repertoire_name, 1, NULL);
Packit Service 82fcde
	  result->copy_name[locale] = now->val.str.startmb;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  lr_ignore_rest (ldfile, now->tok == tok_string);
Packit Service 82fcde
Packit Service 82fcde
  /* The rest of the line must be empty and the next keyword must be
Packit Service 82fcde
     `END xxx'.  */
Packit Service 82fcde
  while ((now = lr_token (ldfile, charmap, result, NULL, verbose))->tok
Packit Service 82fcde
	 != tok_end && now->tok != tok_eof)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (warned == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  lr_error (ldfile, _("\
Packit Service 82fcde
no other keyword shall be specified when `copy' is used"));
Packit Service 82fcde
	  warned = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      lr_ignore_rest (ldfile, 0);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (now->tok != tok_eof)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Handle `END xxx'.  */
Packit Service 82fcde
      now = lr_token (ldfile, charmap, result, NULL, verbose);
Packit Service 82fcde
Packit Service 82fcde
      if (now->tok != token)
Packit Service 82fcde
	lr_error (ldfile, _("\
Packit Service 82fcde
`%1$s' definition does not end with `END %1$s'"), locale_name);
Packit Service 82fcde
Packit Service 82fcde
      lr_ignore_rest (ldfile, now->tok == token);
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    /* When we come here we reached the end of the file.  */
Packit Service 82fcde
    lr_error (ldfile, _("%s: premature end of file"), locale_name);
Packit Service 82fcde
}