Blame locale/programs/locfile.c

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