(add_to_readlist): Take locale pointer as extra parameter from which
[kopensolaris-gnu/glibc.git] / locale / programs / localedef.c
index e9b934c..d0eb711 100644 (file)
@@ -1,6 +1,6 @@
-/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+/* Copyright (C) 1995,1996,1997,1998,1999,2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
 # include <config.h>
 #endif
 
+#include <argp.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <getopt.h>
 #include <libintl.h>
 #include <locale.h>
+#include <mcheck.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 
 #include "error.h"
-#include "charset.h"
+#include "charmap.h"
 #include "locfile.h"
-#include "../intl/loadinfo.h"
 
 /* Undefine the following line in the production version.  */
 /* #define NDEBUG 1 */
 #include <assert.h>
 
 
-/* List of locale definition files which are used in `copy' instructions.  */
-struct copy_def_list_t
-{
-  struct copy_def_list_t *next;
-
-  const char *name;
-  int mask;
-
-  struct localedef_t *locale;
-
-  struct
-  {
-    void *data;
-    size_t len;
-  } binary[6];
-};
-
-
 /* List of copied locales.  */
 struct copy_def_list_t *copy_list;
 
@@ -70,53 +52,93 @@ int posix_conformance;
 /* If not zero give a lot more messages.  */
 int verbose;
 
+/* If not zero suppress warnings and information messages.  */
+int be_quiet;
+
+/* If not zero force output even if warning were issued.  */
+static int force_output;
+
+/* Name of the character map file.  */
+static const char *charmap_file;
+
+/* Name of the locale definition file.  */
+static const char *input_file;
+
+/* Name of the repertoire map file.  */
+const char *repertoire_global;
+
+/* List of all locales.  */
+static struct localedef_t *locales;
+
+
+/* Name and version of program.  */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+#define OPT_POSIX 1
+#define OPT_QUIET 2
+
+/* Definitions of arguments for argp functions.  */
+static const struct argp_option options[] =
+{
+  { NULL, 0, NULL, 0, N_("Input Files:") },
+  { "charmap", 'f', "FILE", 0,
+    N_("Symbolic character names defined in FILE") },
+  { "inputfile", 'i', "FILE", 0, N_("Source definitions are found in FILE") },
+  { "repertoire-map", 'u', "FILE", 0,
+    N_("FILE contains mapping from symbolic names to UCS4 values") },
+
+  { NULL, 0, NULL, 0, N_("Output control:") },
+  { "force", 'c', NULL, 0,
+    N_("Create output even if warning messages were issued") },
+  { "posix", OPT_POSIX, NULL, 0, N_("Be strictly POSIX conform") },
+  { "quiet", OPT_QUIET, NULL, 0,
+    N_("Suppress warnings and information messages") },
+  { "verbose", 'v', NULL, 0, N_("Print more messages") },
+  { NULL, 0, NULL, 0, NULL }
+};
+
+/* Short description of program.  */
+static const char doc[] = N_("Compile locale specification");
+
+/* Strings for arguments in help texts.  */
+static const char args_doc[] = N_("NAME");
 
+/* Prototype for option handler.  */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
 
-/* Long options.  */
-static const struct option long_options[] =
+/* Function to print some extra text in the help message.  */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions.  */
+static struct argp argp =
 {
-  { "charmap", required_argument, NULL, 'f' },
-  { "code-set-name", required_argument, NULL, 'u' },
-  { "help", no_argument, NULL, 'h' },
-  { "force", no_argument, NULL, 'c' },
-  { "inputfile", required_argument, NULL, 'i' },
-  { "posix", no_argument, &posix_conformance, 1 },
-  { "verbose", no_argument, &verbose, 1},
-  { "version", no_argument, NULL, 'V' },
-  { NULL, 0, NULL, 0 }
+  options, parse_opt, args_doc, doc, NULL, more_help
 };
 
 
 /* Prototypes for global functions.  */
-void *xmalloc (size_t __n);
+extern void *xmalloc (size_t __n);
 
 /* Prototypes for local functions.  */
-static void usage (int status) __attribute__ ((noreturn));
 static void error_print (void);
 static const char *construct_output_path (char *path);
+static const char *normalize_codeset (const char *codeset, size_t name_len);
 
 
 int
 main (int argc, char *argv[])
 {
-  int optchar;
-  int do_help = 0;
-  int do_version = 0;
-  int force_output = 0;
-  const char *charmap_file = NULL;
-  const char *input_file = NULL;
-  const char *ucs_csn = NULL;
   const char *output_path;
   int cannot_write_why;
-  struct charset_t *charset;
-  struct localedef_t *localedef;
-  struct copy_def_list_t *act_add_locdef;
+  struct charmap_t *charmap;
+  struct localedef_t global;
+  int remaining;
 
   /* Set initial values for global variables.  */
   copy_list = NULL;
   posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
   error_print_progname = error_print;
-  verbose = 0;
 
   /* Set locale.  Do not set LC_ALL because the other categories must
      not be affected (according to POSIX.2).  */
@@ -126,80 +148,25 @@ main (int argc, char *argv[])
   /* Initialize the message catalog.  */
   textdomain (_libc_intl_domainname);
 
-  while ((optchar = getopt_long (argc, argv, "cf:hi:u:vV", long_options, NULL))
-         != EOF)
-    switch (optchar)
-      {
-      case '\0':               /* Long option.  */
-        break;
-
-      case 'c':
-       force_output = 1;
-       break;
-
-      case 'f':
-        charmap_file = optarg;
-        break;
-
-      case 'h':
-        do_help = 1;
-        break;
-
-      case 'i':
-       input_file = optarg;
-        break;
-
-      case 'u':
-       ucs_csn = optarg;
-       break;
-
-       case 'v':
-        verbose = 1;
-        break;
-
-      case 'V':
-        do_version = 1;
-        break;
-
-      default:
-        usage (4);     /* A value >3 is forced by POSIX.  */
-        break;
-      }
+  /* Parse and process arguments.  */
+  argp_err_exit_status = 4;
+  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
 
   /* POSIX.2 requires to be verbose about missing characters in the
      character map.  */
   verbose |= posix_conformance;
 
-  /* Version information is requested.  */
-  if (do_version)
+  if (argc - remaining != 1)
     {
-      printf ("localedef (GNU %s) %s\n", PACKAGE, VERSION);
-      printf (_("\
-Copyright (C) %s Free Software Foundation, Inc.\n\
-This is free software; see the source for copying conditions.  There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "1995, 1996");
-      printf (_("Written by %s.\n"), "Ulrich Drepper");
-
-      exit (0);
+      /* We need exactly one non-option parameter.  */
+      argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+                program_invocation_short_name);
+      exit (4);
     }
 
-  /* Help is requested.  */
-  if (do_help)
-    /* Possible violation: POSIX.2 4.35.8 defines the return value 0 as
-       "No errors occurred and the locale(s) were successfully created."
-       But giving a other value than 0 does not make sense here.  It
-       is perhaps not that important because POSIX does not specify the
-       -h option for localedef.  */
-    usage (0);
-
-  if (argc - optind != 1)
-    /* We need exactly one non-option parameter.  */
-    usage (4);
-
   /* The parameter describes the output path of the constructed files.
      If the described files cannot be written return a NULL pointer.  */
-  output_path  = construct_output_path (argv[optind]);
+  output_path  = construct_output_path (argv[remaining]);
   cannot_write_why = errno;
 
   /* Now that the parameters are processed we have to reset the local
@@ -213,118 +180,37 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
     error (3, 0, _("FATAL: system does not define `_POSIX2_LOCALEDEF'"));
 
   /* Process charmap file.  */
-  charset = charmap_read (charmap_file);
+  charmap = charmap_read (charmap_file);
+
+  /* Add the first entry in the locale list.  */
+  memset (&global, '\0', sizeof (struct localedef_t));
+  global.name = input_file;
+  global.needed = ALL_LOCALES;
+  locales = &global;
 
   /* Now read the locale file.  */
-  localedef = locfile_read (input_file, charset);
-  if (localedef->failed != 0)
+  if (locfile_read (&global, charmap) != 0)
     error (4, errno, _("cannot open locale definition file `%s'"), input_file);
 
-  /* Perhaps we saw some `copy' instructions.  Process the given list.
-     We use a very simple algorithm: we look up the list from the
-     beginning every time.  */
-  do
+  /* Perhaps we saw some `copy' instructions.  */
+  while (1)
     {
-      int cat;
+      struct localedef_t *runp = locales;
 
-      for (act_add_locdef = copy_list; act_add_locdef != NULL;
-          act_add_locdef = act_add_locdef->next)
-       {
-         for (cat = LC_CTYPE; cat <= LC_MESSAGES; ++cat)
-           if ((act_add_locdef->mask & (1 << cat)) != 0)
-             {
-               act_add_locdef->mask &= ~(1 << cat);
-               break;
-             }
-         if (cat <= LC_MESSAGES)
-           break;
-       }
+      while (runp != NULL && (runp->needed & runp->avail) == runp->needed)
+       runp = runp->next;
 
-      if (act_add_locdef != NULL)
-       {
-         int avail = 0;
-
-         if (act_add_locdef->locale == NULL)
-           act_add_locdef->locale = locfile_read (act_add_locdef->name,
-                                                  charset);
-
-         if (! act_add_locdef->locale->failed)
-           {
-             avail = act_add_locdef->locale->categories[cat].generic != NULL;
-             if (avail)
-               localedef->categories[cat].generic
-                 = act_add_locdef->locale->categories[cat].generic;
-           }
-
-         if (! avail)
-           {
-             const char *locale_names[] = { "LC_COLLATE", "LC_CTYPE",
-                                            "LC_MONETARY", "LC_NUMERIC",
-                                            "LC_TIME", "LC_MESSAGES" };
-             char *fname;
-             int fd;
-             struct stat st;
-
-             asprintf (&fname, LOCALE_PATH "/%s/%s", act_add_locdef->name,
-                       locale_names[cat]);
-             fd = open (fname, O_RDONLY);
-             if (fd == -1)
-               {
-                 free (fname);
-
-                 asprintf (&fname, LOCALE_PATH "/%s/%s/SYS_%s",
-                           act_add_locdef->name, locale_names[cat],
-                           locale_names[cat]);
-
-                 fd = open (fname, O_RDONLY);
-                 if (fd == -1)
-                   error (5, 0, _("\
-locale file `%s', used in `copy' statement, not found"),
-                          act_add_locdef->name);
-               }
-
-             if (fstat (fd, &st) < 0)
-               error (5, errno, _("\
-cannot `stat' locale file `%s'"),
-                      fname);
-
-             localedef->len[cat] = st.st_size;
-             localedef->categories[cat].generic
-               = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-
-             if (localedef->categories[cat].generic == (void *) -1)
-               {
-                 size_t left = st.st_size;
-                 void *read_ptr;
-
-                 localedef->categories[cat].generic
-                   = xmalloc (st.st_size);
-                 read_ptr = localedef->categories[cat].generic;
-
-                 do
-                   {
-                     long int n;
-                     n = read (fd, read_ptr, left);
-                     if (n == -1)
-                       error (5, errno, _("cannot read locale file `%s'"),
-                              fname);
-                     read_ptr += n;
-                     left -= n;
-                   }
-                 while (left > 0);
-               }
-
-             close (fd);
-             free (fname);
-
-             localedef->binary |= 1 << cat;
-           }
-       }
+      if (runp == NULL)
+       /* Everything read.  */
+       break;
+
+      if (locfile_read (runp, charmap) != 0)
+       error (4, errno, _("cannot open locale definition file `%s'"),
+              runp->name);
     }
-  while (act_add_locdef != NULL);
 
   /* Check the categories we processed in source form.  */
-  check_all_categories (localedef, charset);
+  check_all_categories (locales, charmap);
 
   /* We are now able to write the data files.  If warning were given we
      do it only if it is explicitly requested (--force).  */
@@ -334,7 +220,7 @@ cannot `stat' locale file `%s'"),
        error (4, cannot_write_why, _("cannot write output files to `%s'"),
               output_path);
       else
-       write_all_categories (localedef, charset, output_path);
+       write_all_categories (locales, charmap, output_path);
     }
   else
     error (4, 0, _("no output file produced because warning were issued"));
@@ -344,82 +230,82 @@ cannot `stat' locale file `%s'"),
 }
 
 
-void
-def_to_process (const char *name, int category)
+/* Handle program arguments.  */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
 {
-  struct copy_def_list_t *new, **rp;
-
-  for (rp = &copy_list; *rp != NULL; rp = &(*rp)->next)
-    if (strcmp (name, (*rp)->name) == 0)
-      break;
-
-  if (*rp == NULL)
+  switch (key)
     {
-      size_t cnt;
-
-      *rp = (struct copy_def_list_t *) xmalloc (sizeof (**rp));
-
-      (*rp)->next = NULL;
-      (*rp)->name = name;
-      (*rp)->mask = 0;
-      (*rp)->locale = NULL;
-
-      for (cnt = 0; cnt < 6; ++cnt)
-       {
-         (*rp)->binary[cnt].data = NULL;
-         (*rp)->binary[cnt].len = 0;
-       }
+    case OPT_QUIET:
+      be_quiet = 1;
+      break;
+    case OPT_POSIX:
+      posix_conformance = 1;
+      break;
+    case 'c':
+      force_output = 1;
+      break;
+    case 'f':
+      charmap_file = arg;
+      break;
+    case 'i':
+      input_file = arg;
+      break;
+    case 'u':
+      repertoire_global = arg;
+      break;
+    case 'v':
+      verbose = 1;
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
     }
-  new = *rp;
-
-  if ((new->mask & category) != 0)
-    /* We already have the information.  This cannot happen.  */
-    error (5, 0, _("\
-category data requested more than once: should not happen"));
-
-  new->mask |= category;
+  return 0;
 }
 
 
-/* Display usage information and exit.  */
-static void
-usage (int status)
+static char *
+more_help (int key, const char *text, void *input)
 {
-  if (status != 0)
-    fprintf (stderr, _("Try `%s --help' for more information.\n"),
-            program_invocation_name);
-  else
+  char *cp;
+
+  switch (key)
     {
-      printf (_("\
-Usage: %s [OPTION]... name\n\
-Mandatory arguments to long options are mandatory for short options too.\n\
-  -c, --force               create output even if warning messages were issued\n\
-  -h, --help                display this help and exit\n\
-  -f, --charmap=FILE        symbolic character names defined in FILE\n\
-  -i, --inputfile=FILE      source definitions are found in FILE\n\
-  -u, --code-set-name=NAME  specify code set for mapping ISO 10646 elements\n\
-  -v, --verbose             print more messages\n\
-  -V, --version             output version information and exit\n\
-      --posix               be strictly POSIX conform\n\
-\n\
-System's directory for character maps: %s\n\
-                       locale files  : %s\n"),
-             program_invocation_name, CHARMAP_PATH, LOCALE_PATH);
-      fputs (gettext ("Report bugs to <bug-glibc@prep.ai.mit.edu>.\n"),
-            stdout);
+    case ARGP_KEY_HELP_EXTRA:
+      /* We print some extra information.  */
+      asprintf (&cp, gettext ("\
+System's directory for character maps : %s\n\
+                       repertoire maps: %s\n\
+                       locale path    : %s\n\
+%s"),
+               CHARMAP_PATH, REPERTOIREMAP_PATH, LOCALE_PATH, gettext ("\
+Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
+      return cp;
+    default:
+      break;
     }
+  return (char *) text;
+}
 
-  exit (status);
+/* Print the version information.  */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+  fprintf (stream, "localedef (GNU %s) %s\n", PACKAGE, VERSION);
+  fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions.  There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2000");
+  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
 }
 
 
 /* The address of this function will be assigned to the hook in the error
    functions.  */
 static void
-error_print ()
+error_print (void)
 {
-  /* We don't want the program name to be printed in messages.  Emacs'
-     compile.el does not like this.  */
 }
 
 
@@ -431,13 +317,15 @@ construct_output_path (char *path)
 {
   const char *normal = NULL;
   char *result;
+  char *endp;
 
   if (strchr (path, '/') == NULL)
     {
       /* This is a system path.  First examine whether the locale name
         contains a reference to the codeset.  This should be
         normalized.  */
-      char *startp, *endp;
+      char *startp;
+      size_t n;
 
       startp = path;
       /* We must be prepared for finding a CEN name or a location of
@@ -453,7 +341,7 @@ construct_output_path (char *path)
            ++endp;
 
          if (endp > startp)
-           normal = _nl_normalize_codeset (startp, endp - startp);
+           normal = normalize_codeset (startp, endp - startp);
        }
       else
        /* This is to keep gcc quiet.  */
@@ -463,17 +351,20 @@ construct_output_path (char *path)
         the end of the function we need another byte for the trailing
         '/'.  */
       if (normal == NULL)
-       asprintf (&result, "%s/%s%c", LOCALEDIR, path, '\0');
+       n = asprintf (&result, "%s/%s%c", LOCALEDIR, path, '\0');
       else
-       asprintf (&result, "%s/%.*s%s%s%c", LOCALEDIR, startp - path, path,
-                 normal, endp, '\0');
+       n = asprintf (&result, "%s/%.*s%s%s%c", LOCALEDIR,
+                     (int) (startp - path), path, normal, endp, '\0');
+
+      endp = result + n;
     }
   else
     {
       /* This is a user path.  Please note the additional byte in the
         memory allocation.  */
-      result = xmalloc (strlen (path) + 2);
-      strcpy (result, path);
+      size_t len = strlen (path) + 1;
+      result = xmalloc (len + 1);
+      endp = mempcpy (result, path, len) - 1;
     }
 
   errno = 0;
@@ -486,7 +377,154 @@ construct_output_path (char *path)
        mkdir (result, 0777);
       }
 
-  strcat (result, "/");
+  *endp++ = '/';
+  *endp = '\0';
 
   return result;
 }
+
+
+/* Normalize codeset name.  There is no standard for the codeset
+   names.  Normalization allows the user to use any of the common
+   names.  */
+static const char *
+normalize_codeset (codeset, name_len)
+     const char *codeset;
+     size_t name_len;
+{
+  int len = 0;
+  int only_digit = 1;
+  char *retval;
+  char *wp;
+  size_t cnt;
+
+  for (cnt = 0; cnt < name_len; ++cnt)
+    if (isalnum (codeset[cnt]))
+      {
+       ++len;
+
+       if (isalpha (codeset[cnt]))
+         only_digit = 0;
+      }
+
+  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
+
+  if (retval != NULL)
+    {
+      if (only_digit)
+       wp = stpcpy (retval, "iso");
+      else
+       wp = retval;
+
+      for (cnt = 0; cnt < name_len; ++cnt)
+       if (isalpha (codeset[cnt]))
+         *wp++ = tolower (codeset[cnt]);
+       else if (isdigit (codeset[cnt]))
+         *wp++ = codeset[cnt];
+
+      *wp = '\0';
+    }
+
+  return (const char *) retval;
+}
+
+
+struct localedef_t *
+add_to_readlist (int locale, const char *name, const char *repertoire_name,
+                int generate, struct localedef_t *copy_locale)
+{
+  struct localedef_t *runp = locales;
+
+  while (runp != NULL && strcmp (name, runp->name) != 0)
+    runp = runp->next;
+
+  if (runp == NULL)
+    {
+      /* Add a new entry at the end.  */
+      struct localedef_t *newp;
+
+      assert (generate == 1);
+
+      newp = xcalloc (1, sizeof (struct localedef_t));
+      newp->name = name;
+      newp->repertoire_name = repertoire_name;
+
+      if (locales == NULL)
+       runp = locales = newp;
+      else
+       {
+         runp = locales;
+         while (runp->next != NULL)
+           runp = runp->next;
+         runp = runp->next = newp;
+       }
+    }
+
+  if (generate && (runp->needed & (1 << locale)) != 0)
+    error (5, 0, _("circular dependencies between locale definitions"));
+
+  if (copy_locale != NULL)
+    {
+      if (runp->categories[locale].generic != NULL)
+       error (5, 0, _("cannot add already read locale `%s' a second time"),
+              name);
+      else
+       runp->categories[locale].generic =
+         copy_locale->categories[locale].generic;
+    }
+
+  runp->needed |= 1 << locale;
+
+  return runp;
+}
+
+
+struct localedef_t *
+find_locale (int locale, const char *name, const char *repertoire_name,
+            struct charmap_t *charmap)
+{
+  struct localedef_t *result;
+
+  /* Find the locale, but do not generate it since this would be a bug.  */
+  result = add_to_readlist (locale, name, repertoire_name, 0, NULL);
+
+  assert (result != NULL);
+
+  if ((result->avail & (1 << locale)) == 0
+      && locfile_read (result, charmap) != 0)
+    error (4, errno, _("cannot open locale definition file `%s'"),
+          result->name);
+
+  return result;
+}
+
+
+struct localedef_t *
+load_locale (int locale, const char *name, const char *repertoire_name,
+            struct charmap_t *charmap, struct localedef_t *copy_locale)
+{
+  struct localedef_t *result;
+
+  /* Generate the locale if it does not exist.  */
+  result = add_to_readlist (locale, name, repertoire_name, 1, copy_locale);
+
+  assert (result != NULL);
+
+  if ((result->avail & (1 << locale)) == 0
+      && locfile_read (result, charmap) != 0)
+    error (4, errno, _("cannot open locale definition file `%s'"),
+          result->name);
+
+  return result;
+}
+
+static void
+turn_on_mcheck (void)
+{
+  /* Enable `malloc' debugging.  */
+  mcheck (NULL);
+  /* Use the following line for a more thorough but much slower testing.  */
+  /* mcheck_pedantic (NULL); */
+}
+
+void (*__malloc_initialize_hook) (void) = turn_on_mcheck;