Blame locale/programs/localedef.c

Packit 6c4009
/* Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
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 <argp.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
Packit 6c4009
#include "localedef.h"
Packit 6c4009
#include "charmap.h"
Packit 6c4009
#include "locfile.h"
Packit 6c4009
Packit 6c4009
/* Undefine the following line in the production version.  */
Packit 6c4009
/* #define NDEBUG 1 */
Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* List of copied locales.  */
Packit 6c4009
struct copy_def_list_t *copy_list;
Packit 6c4009
Packit 6c4009
/* If this is defined be POSIX conform.  */
Packit 6c4009
int posix_conformance;
Packit 6c4009
Packit 6c4009
/* If not zero force output even if warning were issued.  */
Packit 6c4009
static int force_output;
Packit 6c4009
Packit 6c4009
/* Prefix for output files.  */
Packit 6c4009
const char *output_prefix;
Packit 6c4009
Packit 6c4009
/* Name of the character map file.  */
Packit 6c4009
static const char *charmap_file;
Packit 6c4009
Packit 6c4009
/* Name of the locale definition file.  */
Packit 6c4009
static const char *input_file;
Packit 6c4009
Packit 6c4009
/* Name of the repertoire map file.  */
Packit 6c4009
const char *repertoire_global;
Packit 6c4009
Packit 6c4009
/* Name of the locale.alias file.  */
Packit 6c4009
const char *alias_file;
Packit 6c4009
Packit 6c4009
/* List of all locales.  */
Packit 6c4009
static struct localedef_t *locales;
Packit 6c4009
Packit 6c4009
/* If true don't add locale data to archive.  */
Packit 6c4009
bool no_archive;
Packit 6c4009
Packit 6c4009
/* If true add named locales to archive.  */
Packit 6c4009
static bool add_to_archive;
Packit 6c4009
Packit 6c4009
/* If true delete named locales from archive.  */
Packit 6c4009
static bool delete_from_archive;
Packit 6c4009
Packit 6c4009
/* If true replace archive content when adding.  */
Packit 6c4009
static bool replace_archive;
Packit 6c4009
Packit 6c4009
/* If true list archive content.  */
Packit 6c4009
static bool list_archive;
Packit 6c4009
Packit 6c4009
/* Maximum number of retries when opening the locale archive.  */
Packit 6c4009
int max_locarchive_open_retry = 10;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Name and version of program.  */
Packit 6c4009
static void print_version (FILE *stream, struct argp_state *state);
Packit 6c4009
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
Packit 6c4009
Packit 6c4009
#define OPT_POSIX 301
Packit 6c4009
#define OPT_QUIET 302
Packit 6c4009
#define OPT_PREFIX 304
Packit 6c4009
#define OPT_NO_ARCHIVE 305
Packit 6c4009
#define OPT_ADD_TO_ARCHIVE 306
Packit 6c4009
#define OPT_REPLACE 307
Packit 6c4009
#define OPT_DELETE_FROM_ARCHIVE 308
Packit 6c4009
#define OPT_LIST_ARCHIVE 309
Packit 6c4009
#define OPT_LITTLE_ENDIAN 400
Packit 6c4009
#define OPT_BIG_ENDIAN 401
Packit 6c4009
#define OPT_NO_WARN 402
Packit 6c4009
#define OPT_WARN 403
Packit 6c4009
Packit 6c4009
/* Definitions of arguments for argp functions.  */
Packit 6c4009
static const struct argp_option options[] =
Packit 6c4009
{
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Input Files:") },
Packit 6c4009
  { "charmap", 'f', N_("FILE"), 0,
Packit 6c4009
    N_("Symbolic character names defined in FILE") },
Packit 6c4009
  { "inputfile", 'i', N_("FILE"), 0,
Packit 6c4009
    N_("Source definitions are found in FILE") },
Packit 6c4009
  { "repertoire-map", 'u', N_("FILE"), 0,
Packit 6c4009
    N_("FILE contains mapping from symbolic names to UCS4 values") },
Packit 6c4009
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Output control:") },
Packit 6c4009
  { "force", 'c', NULL, 0,
Packit 6c4009
    N_("Create output even if warning messages were issued") },
Packit 6c4009
  { "prefix", OPT_PREFIX, N_("PATH"), 0, N_("Optional output file prefix") },
Packit 6c4009
  { "posix", OPT_POSIX, NULL, 0, N_("Strictly conform to POSIX") },
Packit 6c4009
  { "quiet", OPT_QUIET, NULL, 0,
Packit 6c4009
    N_("Suppress warnings and information messages") },
Packit 6c4009
  { "verbose", 'v', NULL, 0, N_("Print more messages") },
Packit 6c4009
  { "no-warnings", OPT_NO_WARN, N_("<warnings>"), 0,
Packit 6c4009
    N_("Comma-separated list of warnings to disable; "
Packit 6c4009
       "supported warnings are: ascii, intcurrsym") },
Packit 6c4009
  { "warnings", OPT_WARN, N_("<warnings>"), 0,
Packit 6c4009
    N_("Comma-separated list of warnings to enable; "
Packit 6c4009
       "supported warnings are: ascii, intcurrsym") },
Packit 6c4009
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Archive control:") },
Packit 6c4009
  { "no-archive", OPT_NO_ARCHIVE, NULL, 0,
Packit 6c4009
    N_("Don't add new data to archive") },
Packit 6c4009
  { "add-to-archive", OPT_ADD_TO_ARCHIVE, NULL, 0,
Packit 6c4009
    N_("Add locales named by parameters to archive") },
Packit 6c4009
  { "replace", OPT_REPLACE, NULL, 0, N_("Replace existing archive content") },
Packit 6c4009
  { "delete-from-archive", OPT_DELETE_FROM_ARCHIVE, NULL, 0,
Packit 6c4009
    N_("Remove locales named by parameters from archive") },
Packit 6c4009
  { "list-archive", OPT_LIST_ARCHIVE, NULL, 0, N_("List content of archive") },
Packit 6c4009
  { "alias-file", 'A', N_("FILE"), 0,
Packit 6c4009
    N_("locale.alias file to consult when making archive")},
Packit 6c4009
  { "little-endian", OPT_LITTLE_ENDIAN, NULL, 0,
Packit 6c4009
    N_("Generate little-endian output") },
Packit 6c4009
  { "big-endian", OPT_BIG_ENDIAN, NULL, 0,
Packit 6c4009
    N_("Generate big-endian output") },
Packit 6c4009
  { NULL, 0, NULL, 0, NULL }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Short description of program.  */
Packit 6c4009
static const char doc[] = N_("Compile locale specification");
Packit 6c4009
Packit 6c4009
/* Strings for arguments in help texts.  */
Packit 6c4009
static const char args_doc[] = N_("\
Packit 6c4009
NAME\n\
Packit 6c4009
[--add-to-archive|--delete-from-archive] FILE...\n\
Packit 6c4009
--list-archive [FILE]");
Packit 6c4009
Packit 6c4009
/* Prototype for option handler.  */
Packit 6c4009
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 6c4009
Packit 6c4009
/* Function to print some extra text in the help message.  */
Packit 6c4009
static char *more_help (int key, const char *text, void *input);
Packit 6c4009
Packit 6c4009
/* Data structure to communicate with argp functions.  */
Packit 6c4009
static struct argp argp =
Packit 6c4009
{
Packit 6c4009
  options, parse_opt, args_doc, doc, NULL, more_help
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Prototypes for local functions.  */
Packit 6c4009
static void error_print (void);
Packit Service 537b3b
static char *construct_output_path (char *path);
Packit Service 537b3b
static char *normalize_codeset (const char *codeset, size_t name_len);
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (int argc, char *argv[])
Packit 6c4009
{
Packit Service 537b3b
  char *output_path;
Packit 6c4009
  int cannot_write_why;
Packit 6c4009
  struct charmap_t *charmap;
Packit 6c4009
  struct localedef_t global;
Packit 6c4009
  int remaining;
Packit 6c4009
Packit 6c4009
  /* Set initial values for global variables.  */
Packit 6c4009
  copy_list = NULL;
Packit 6c4009
  posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
Packit 6c4009
  error_print_progname = error_print;
Packit 6c4009
Packit 6c4009
  /* Set locale.  Do not set LC_ALL because the other categories must
Packit 6c4009
     not be affected (according to POSIX.2).  */
Packit 6c4009
  setlocale (LC_MESSAGES, "");
Packit 6c4009
  setlocale (LC_CTYPE, "");
Packit 6c4009
Packit 6c4009
  /* Initialize the message catalog.  */
Packit 6c4009
  textdomain (_libc_intl_domainname);
Packit 6c4009
Packit 6c4009
  /* Parse and process arguments.  */
Packit 6c4009
  argp_err_exit_status = 4;
Packit 6c4009
  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 6c4009
Packit 6c4009
  /* Handle a few special cases.  */
Packit 6c4009
  if (list_archive)
Packit 6c4009
    show_archive_content (remaining > 1 ? argv[remaining] : NULL, verbose);
Packit 6c4009
  if (add_to_archive)
Packit 6c4009
    return add_locales_to_archive (argc - remaining, &argv[remaining],
Packit 6c4009
				   replace_archive);
Packit 6c4009
  if (delete_from_archive)
Packit 6c4009
    return delete_locales_from_archive (argc - remaining, &argv[remaining]);
Packit 6c4009
Packit 6c4009
  /* POSIX.2 requires to be verbose about missing characters in the
Packit 6c4009
     character map.  */
Packit 6c4009
  verbose |= posix_conformance;
Packit 6c4009
Packit 6c4009
  if (argc - remaining != 1)
Packit 6c4009
    {
Packit 6c4009
      /* We need exactly one non-option parameter.  */
Packit 6c4009
      argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
Packit 6c4009
		 program_invocation_short_name);
Packit 6c4009
      exit (4);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* The parameter describes the output path of the constructed files.
Packit Service 537b3b
     If the described files cannot be written return a NULL pointer.
Packit Service 537b3b
     We don't free output_path because we will exit.  */
Packit 6c4009
  output_path  = construct_output_path (argv[remaining]);
Packit 6c4009
  if (output_path == NULL && ! no_archive)
Packit 6c4009
    error (4, errno, _("cannot create directory for output files"));
Packit 6c4009
  cannot_write_why = errno;
Packit 6c4009
Packit 6c4009
  /* Now that the parameters are processed we have to reset the local
Packit 6c4009
     ctype locale.  (P1003.2 4.35.5.2)  */
Packit 6c4009
  setlocale (LC_CTYPE, "POSIX");
Packit 6c4009
Packit 6c4009
  /* Look whether the system really allows locale definitions.  POSIX
Packit 6c4009
     defines error code 3 for this situation so I think it must be
Packit 6c4009
     a fatal error (see P1003.2 4.35.8).  */
Packit 6c4009
  if (sysconf (_SC_2_LOCALEDEF) < 0)
Packit 6c4009
    record_error (3, 0, _("\
Packit 6c4009
FATAL: system does not define `_POSIX2_LOCALEDEF'"));
Packit 6c4009
Packit 6c4009
  /* Process charmap file.  */
Packit 6c4009
  charmap = charmap_read (charmap_file, verbose, 1, be_quiet, 1);
Packit 6c4009
Packit 6c4009
  /* Add the first entry in the locale list.  */
Packit 6c4009
  memset (&global, '\0', sizeof (struct localedef_t));
Packit 6c4009
  global.name = input_file ?: "/dev/stdin";
Packit 6c4009
  global.needed = ALL_LOCALES;
Packit 6c4009
  locales = &global;
Packit 6c4009
Packit 6c4009
  /* Now read the locale file.  */
Packit 6c4009
  if (locfile_read (&global, charmap) != 0)
Packit 6c4009
    record_error (4, errno, _("\
Packit 6c4009
cannot open locale definition file `%s'"), input_file);
Packit 6c4009
Packit 6c4009
  /* Perhaps we saw some `copy' instructions.  */
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      struct localedef_t *runp = locales;
Packit 6c4009
Packit 6c4009
      while (runp != NULL && (runp->needed & runp->avail) == runp->needed)
Packit 6c4009
	runp = runp->next;
Packit 6c4009
Packit 6c4009
      if (runp == NULL)
Packit 6c4009
	/* Everything read.  */
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      if (locfile_read (runp, charmap) != 0)
Packit 6c4009
	record_error (4, errno, _("\
Packit 6c4009
cannot open locale definition file `%s'"), runp->name);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Check the categories we processed in source form.  */
Packit 6c4009
  check_all_categories (locales, charmap);
Packit 6c4009
Packit 6c4009
  /* What we do next depends on the number of errors and warnings we
Packit 6c4009
     have generated in processing the input files.
Packit 6c4009
Packit 6c4009
     * No errors: Write the output file.
Packit 6c4009
Packit 6c4009
     * Some warnings: Write the output file and exit with status 1 to
Packit 6c4009
     indicate there may be problems using the output file e.g. missing
Packit 6c4009
     data that makes it difficult to use
Packit 6c4009
Packit 6c4009
     * Errors: We don't write the output file and we exit with status 4
Packit 6c4009
     to indicate no output files were written.
Packit 6c4009
Packit 6c4009
     The use of -c|--force writes the output file even if errors were
Packit 6c4009
     seen.  */
Packit 6c4009
  if (recorded_error_count == 0 || force_output != 0)
Packit 6c4009
    {
Packit 6c4009
      if (cannot_write_why != 0)
Packit 6c4009
	record_error (4, cannot_write_why, _("\
Packit 6c4009
cannot write output files to `%s'"), output_path ? : argv[remaining]);
Packit 6c4009
      else
Packit 6c4009
	write_all_categories (locales, charmap, argv[remaining], output_path);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    record_error (4, 0, _("\
Packit 6c4009
no output file produced because errors were issued"));
Packit 6c4009
Packit 6c4009
  /* This exit status is prescribed by POSIX.2 4.35.7.  */
Packit 6c4009
  exit (recorded_warning_count != 0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Search warnings for matching warnings and if found enable those
Packit 6c4009
   warnings if ENABLED is true, otherwise disable the warnings.  */
Packit 6c4009
static void
Packit 6c4009
set_warnings (char *warnings, bool enabled)
Packit 6c4009
{
Packit 6c4009
  char *tok = warnings;
Packit 6c4009
  char *copy = (char *) malloc (strlen (warnings) + 1);
Packit 6c4009
  char *save = copy;
Packit 6c4009
Packit 6c4009
  /* As we make a copy of the warnings list we remove all spaces from
Packit 6c4009
     the warnings list to make the processing a more robust.  We don't
Packit 6c4009
     support spaces in a warning name.  */
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      while (isspace (*tok) != 0)
Packit 6c4009
        tok++;
Packit 6c4009
    }
Packit 6c4009
  while ((*save++ = *tok++) != '\0');
Packit 6c4009
Packit 6c4009
  warnings = copy;
Packit 6c4009
Packit 6c4009
  /* Tokenize the input list of warnings to set, compare them to
Packit 6c4009
     known warnings, and set the warning.  We purposely ignore unknown
Packit 6c4009
     warnings, and are thus forward compatible, users can attempt to
Packit 6c4009
     disable whaterver new warnings they know about, but we will only
Packit 6c4009
     disable those *we* known about.  */
Packit 6c4009
  while ((tok = strtok_r (warnings, ",", &save)) != NULL)
Packit 6c4009
    {
Packit 6c4009
      warnings = NULL;
Packit 6c4009
      if (strcmp (tok, "ascii") == 0)
Packit 6c4009
	warn_ascii = enabled;
Packit 6c4009
      else if (strcmp (tok, "intcurrsym") == 0)
Packit 6c4009
	warn_int_curr_symbol = enabled;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  free (copy);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Handle program arguments.  */
Packit 6c4009
static error_t
Packit 6c4009
parse_opt (int key, char *arg, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case OPT_QUIET:
Packit 6c4009
      be_quiet = 1;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_POSIX:
Packit 6c4009
      posix_conformance = 1;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_PREFIX:
Packit 6c4009
      output_prefix = arg;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_NO_ARCHIVE:
Packit 6c4009
      no_archive = true;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_ADD_TO_ARCHIVE:
Packit 6c4009
      add_to_archive = true;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_REPLACE:
Packit 6c4009
      replace_archive = true;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_DELETE_FROM_ARCHIVE:
Packit 6c4009
      delete_from_archive = true;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_LIST_ARCHIVE:
Packit 6c4009
      list_archive = true;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_LITTLE_ENDIAN:
Packit 6c4009
      set_big_endian (false);
Packit 6c4009
      break;
Packit 6c4009
    case OPT_BIG_ENDIAN:
Packit 6c4009
      set_big_endian (true);
Packit 6c4009
      break;
Packit 6c4009
    case OPT_NO_WARN:
Packit 6c4009
      /* Disable the warnings.  */
Packit 6c4009
      set_warnings (arg, false);
Packit 6c4009
      break;
Packit 6c4009
    case OPT_WARN:
Packit 6c4009
      /* Enable the warnings.  */
Packit 6c4009
      set_warnings (arg, true);
Packit 6c4009
      break;
Packit 6c4009
    case 'c':
Packit 6c4009
      force_output = 1;
Packit 6c4009
      break;
Packit 6c4009
    case 'f':
Packit 6c4009
      charmap_file = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 'A':
Packit 6c4009
      alias_file = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 'i':
Packit 6c4009
      input_file = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 'u':
Packit 6c4009
      repertoire_global = arg;
Packit 6c4009
      break;
Packit 6c4009
    case 'v':
Packit 6c4009
      verbose = 1;
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      return ARGP_ERR_UNKNOWN;
Packit 6c4009
    }
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static char *
Packit 6c4009
more_help (int key, const char *text, void *input)
Packit 6c4009
{
Packit 6c4009
  char *cp;
Packit 6c4009
  char *tp;
Packit 6c4009
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case ARGP_KEY_HELP_EXTRA:
Packit 6c4009
      /* We print some extra information.  */
Packit Service 537b3b
      tp = xasprintf (gettext ("\
Packit 6c4009
For bug reporting instructions, please see:\n\
Packit Service 537b3b
%s.\n"), REPORT_BUGS_TO);
Packit Service 537b3b
      cp = xasprintf (gettext ("\
Packit 6c4009
System's directory for character maps : %s\n\
Packit 6c4009
		       repertoire maps: %s\n\
Packit 6c4009
		       locale path    : %s\n\
Packit 6c4009
%s"),
Packit Service 537b3b
		    CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, tp);
Packit Service 537b3b
      free (tp);
Packit 6c4009
      return cp;
Packit 6c4009
    default:
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  return (char *) text;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Print the version information.  */
Packit 6c4009
static void
Packit 6c4009
print_version (FILE *stream, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  fprintf (stream, "localedef %s%s\n", PKGVERSION, VERSION);
Packit 6c4009
  fprintf (stream, gettext ("\
Packit 6c4009
Copyright (C) %s Free Software Foundation, Inc.\n\
Packit 6c4009
This is free software; see the source for copying conditions.  There is NO\n\
Packit 6c4009
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Packit 6c4009
"), "2018");
Packit 6c4009
  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The address of this function will be assigned to the hook in the error
Packit 6c4009
   functions.  */
Packit 6c4009
static void
Packit 6c4009
error_print (void)
Packit 6c4009
{
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service 537b3b
/* The parameter to localedef describes the output path.  If it does contain a
Packit Service 537b3b
   '/' character it is a relative path.  Otherwise it names the locale this
Packit Service 537b3b
   definition is for.   The returned path must be freed by the caller. */
Packit Service 537b3b
static char *
Packit 6c4009
construct_output_path (char *path)
Packit 6c4009
{
Packit 6c4009
  char *result;
Packit 6c4009
Packit 6c4009
  if (strchr (path, '/') == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* This is a system path.  First examine whether the locale name
Packit 6c4009
	 contains a reference to the codeset.  This should be
Packit 6c4009
	 normalized.  */
Packit 6c4009
      char *startp;
Packit Service 537b3b
      char *endp = NULL;
Packit Service 537b3b
      char *normal = NULL;
Packit 6c4009
Packit 6c4009
      startp = path;
Packit Service 537b3b
      /* Either we have a '@' which starts a CEN name or '.' which starts the
Packit Service 537b3b
	 codeset specification.  The CEN name starts with '@' and may also have
Packit Service 537b3b
	 a codeset specification, but we do not normalize the string after '@'.
Packit Service 537b3b
	 If we only find the codeset specification then we normalize only the codeset
Packit Service 537b3b
	 specification (but not anything after a subsequent '@').  */
Packit 6c4009
      while (*startp != '\0' && *startp != '@' && *startp != '.')
Packit 6c4009
	++startp;
Packit 6c4009
      if (*startp == '.')
Packit 6c4009
	{
Packit 6c4009
	  /* We found a codeset specification.  Now find the end.  */
Packit 6c4009
	  endp = ++startp;
Packit Service 537b3b
Packit Service 537b3b
	  /* Stop at the first '@', and don't normalize anything past that.  */
Packit 6c4009
	  while (*endp != '\0' && *endp != '@')
Packit 6c4009
	    ++endp;
Packit 6c4009
Packit 6c4009
	  if (endp > startp)
Packit 6c4009
	    normal = normalize_codeset (startp, endp - startp);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (normal == NULL)
Packit Service 537b3b
	result = xasprintf ("%s%s/%s/", output_prefix ?: "",
Packit Service 537b3b
			    COMPLOCALEDIR, path);
Packit 6c4009
      else
Packit Service 537b3b
	result = xasprintf ("%s%s/%.*s%s%s/",
Packit Service 537b3b
			    output_prefix ?: "", COMPLOCALEDIR,
Packit Service 537b3b
			    (int) (startp - path), path, normal, endp ?: "");
Packit Service 537b3b
      /* Free the allocated normalized codeset name.  */
Packit Service 537b3b
      free (normal);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit Service 537b3b
      /* This is a user path.  */
Packit Service 537b3b
      result = xasprintf ("%s/", path);
Packit 6c4009
Packit 6c4009
      /* If the user specified an output path we cannot add the output
Packit 6c4009
	 to the archive.  */
Packit 6c4009
      no_archive = true;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  errno = 0;
Packit 6c4009
Packit 6c4009
  if (no_archive && euidaccess (result, W_OK) == -1)
Packit Service 537b3b
    {
Packit Service 537b3b
      /* Perhaps the directory does not exist now.  Try to create it.  */
Packit Service 537b3b
      if (errno == ENOENT)
Packit Service 537b3b
	{
Packit Service 537b3b
	  errno = 0;
Packit Service 537b3b
	  if (mkdir (result, 0777) < 0)
Packit Service 537b3b
	    {
Packit Service 537b3b
	      record_verbose (stderr,
Packit Service 537b3b
			      _("cannot create output path \'%s\': %s"),
Packit Service 537b3b
			      result, strerror (errno));
Packit Service 537b3b
	      free (result);
Packit Service 537b3b
	      return NULL;
Packit Service 537b3b
	    }
Packit Service 537b3b
	}
Packit Service 537b3b
      else
Packit Service 537b3b
	record_verbose (stderr,
Packit Service 537b3b
			_("no write permission to output path \'%s\': %s"),
Packit Service 537b3b
			result, strerror (errno));
Packit Service 537b3b
    }
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service 537b3b
/* Normalize codeset name.  There is no standard for the codeset names.
Packit Service 537b3b
   Normalization allows the user to use any of the common names e.g. UTF-8,
Packit Service 537b3b
   utf-8, utf8, UTF8 etc.
Packit Service 537b3b
Packit Service 537b3b
   We normalize using the following rules:
Packit Service 537b3b
   - Remove all non-alpha-numeric characters
Packit Service 537b3b
   - Lowercase all characters.
Packit Service 537b3b
   - If there are only digits assume it's an ISO standard and prefix with 'iso'
Packit Service 537b3b
Packit Service 537b3b
   We return the normalized string which needs to be freed by free.  */
Packit Service 537b3b
static char *
Packit 6c4009
normalize_codeset (const char *codeset, size_t name_len)
Packit 6c4009
{
Packit 6c4009
  int len = 0;
Packit 6c4009
  int only_digit = 1;
Packit 6c4009
  char *retval;
Packit 6c4009
  char *wp;
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit Service 537b3b
  /* Compute the length of only the alpha-numeric characters.  */
Packit 6c4009
  for (cnt = 0; cnt < name_len; ++cnt)
Packit 6c4009
    if (isalnum (codeset[cnt]))
Packit 6c4009
      {
Packit 6c4009
	++len;
Packit 6c4009
Packit 6c4009
	if (isalpha (codeset[cnt]))
Packit 6c4009
	  only_digit = 0;
Packit 6c4009
      }
Packit 6c4009
Packit Service 537b3b
  /* If there were only digits we assume it's an ISO standard and we will
Packit Service 537b3b
     prefix with 'iso' so include space for that.  We fill in the required
Packit Service 537b3b
     space from codeset up to the converted length.  */
Packit Service 537b3b
  wp = retval = xasprintf ("%s%.*s", only_digit ? "iso" : "", len, codeset);
Packit 6c4009
Packit Service 537b3b
  /* Skip "iso".  */
Packit Service 537b3b
  if (only_digit)
Packit Service 537b3b
    wp += 3;
Packit Service 562438
Packit Service 537b3b
  /* Lowercase all characters. */
Packit Service 537b3b
  for (cnt = 0; cnt < name_len; ++cnt)
Packit Service 537b3b
    if (isalpha (codeset[cnt]))
Packit Service 537b3b
      *wp++ = tolower (codeset[cnt]);
Packit Service 537b3b
    else if (isdigit (codeset[cnt]))
Packit Service 537b3b
      *wp++ = codeset[cnt];
Packit 6c4009
Packit Service 537b3b
  /* Return allocated and converted name for caller to free.  */
Packit Service 537b3b
  return retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct localedef_t *
Packit 6c4009
add_to_readlist (int category, const char *name, const char *repertoire_name,
Packit 6c4009
		 int generate, struct localedef_t *copy_locale)
Packit 6c4009
{
Packit 6c4009
  struct localedef_t *runp = locales;
Packit 6c4009
Packit 6c4009
  while (runp != NULL && strcmp (name, runp->name) != 0)
Packit 6c4009
    runp = runp->next;
Packit 6c4009
Packit 6c4009
  if (runp == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Add a new entry at the end.  */
Packit 6c4009
      struct localedef_t *newp;
Packit 6c4009
Packit 6c4009
      assert (generate == 1);
Packit 6c4009
Packit 6c4009
      newp = xcalloc (1, sizeof (struct localedef_t));
Packit 6c4009
      newp->name = name;
Packit 6c4009
      newp->repertoire_name = repertoire_name;
Packit 6c4009
Packit 6c4009
      if (locales == NULL)
Packit 6c4009
	runp = locales = newp;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  runp = locales;
Packit 6c4009
	  while (runp->next != NULL)
Packit 6c4009
	    runp = runp->next;
Packit 6c4009
	  runp = runp->next = newp;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (generate
Packit 6c4009
      && (runp->needed & (1 << category)) != 0
Packit 6c4009
      && (runp->avail & (1 << category)) == 0)
Packit 6c4009
    record_error (5, 0, _("\
Packit 6c4009
circular dependencies between locale definitions"));
Packit 6c4009
Packit 6c4009
  if (copy_locale != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (runp->categories[category].generic != NULL)
Packit 6c4009
	record_error (5, 0, _("\
Packit 6c4009
cannot add already read locale `%s' a second time"), name);
Packit 6c4009
      else
Packit 6c4009
	runp->categories[category].generic =
Packit 6c4009
	  copy_locale->categories[category].generic;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  runp->needed |= 1 << category;
Packit 6c4009
Packit 6c4009
  return runp;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct localedef_t *
Packit 6c4009
find_locale (int category, const char *name, const char *repertoire_name,
Packit 6c4009
	     const struct charmap_t *charmap)
Packit 6c4009
{
Packit 6c4009
  struct localedef_t *result;
Packit 6c4009
Packit 6c4009
  /* Find the locale, but do not generate it since this would be a bug.  */
Packit 6c4009
  result = add_to_readlist (category, name, repertoire_name, 0, NULL);
Packit 6c4009
Packit 6c4009
  assert (result != NULL);
Packit 6c4009
Packit 6c4009
  if ((result->avail & (1 << category)) == 0
Packit 6c4009
      && locfile_read (result, charmap) != 0)
Packit 6c4009
    record_error (4, errno, _("\
Packit 6c4009
cannot open locale definition file `%s'"), result->name);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct localedef_t *
Packit 6c4009
load_locale (int category, const char *name, const char *repertoire_name,
Packit 6c4009
	     const struct charmap_t *charmap, struct localedef_t *copy_locale)
Packit 6c4009
{
Packit 6c4009
  struct localedef_t *result;
Packit 6c4009
Packit 6c4009
  /* Generate the locale if it does not exist.  */
Packit 6c4009
  result = add_to_readlist (category, name, repertoire_name, 1, copy_locale);
Packit 6c4009
Packit 6c4009
  assert (result != NULL);
Packit 6c4009
Packit 6c4009
  if ((result->avail & (1 << category)) == 0
Packit 6c4009
      && locfile_read (result, charmap) != 0)
Packit 6c4009
    record_error (4, errno, _("\
Packit 6c4009
cannot open locale definition file `%s'"), result->name);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}