Merged 1003.2 locale and localedef programs by Ulrich Drepper.
authorroland <roland>
Wed, 17 May 1995 21:36:25 +0000 (21:36 +0000)
committerroland <roland>
Wed, 17 May 1995 21:36:25 +0000 (21:36 +0000)
23 files changed:
locale/Makefile
locale/categories.def
locale/charmap.c [new file with mode: 0644]
locale/collate.c [new file with mode: 0644]
locale/config.h [new file with mode: 0644]
locale/ctype.c [new file with mode: 0644]
locale/ctypedump.c [new file with mode: 0644]
locale/hash.c [new file with mode: 0644]
locale/hash.h [new file with mode: 0644]
locale/iso-4217.def [new file with mode: 0644]
locale/keyword.gperf [new file with mode: 0644]
locale/keyword.h [new file with mode: 0644]
locale/langinfo.h
locale/libintl.h [new file with mode: 0644]
locale/locale.c [new file with mode: 0644]
locale/localedef.c [new file with mode: 0644]
locale/localedef.h [new file with mode: 0644]
locale/locfile-lex.c [new file with mode: 0644]
locale/locfile-parse.c [new file with mode: 0644]
locale/messages.c [new file with mode: 0644]
locale/monetary.c [new file with mode: 0644]
locale/numeric.c [new file with mode: 0644]
locale/token.h [new file with mode: 0644]

index 87a6706..c3abc2c 100644 (file)
 #
 subdir := locale
 
-headers                := locale.h
-distribute     := localeinfo.h categories.def
-routines       := setlocale loadlocale localeconv
-categories     := ctype messages monetary numeric time collate
+headers                = locale.h
+distribute     = localeinfo.h categories.def \
+                 $(localedef-modules:=.c) $(locale-modules:=.c) \
+                 $(lib-modules:=.c) config.h hash.h iso-4217.def \
+                 keyword.gperf keyword.h localedef.h token.h
+routines       = setlocale loadlocale localeconv nl_langinfo
+categories     = ctype messages monetary numeric time collate
 aux            = $(categories:%=lc-%) $(categories:%=C-%)
+others         = localedef locale
+install-bin    = localedef locale
+extra-objs     = $(localedef-modules:=.o) $(locale-modules:=.o) \
+                 $(lib-modules:=.o)
+
+localedef-modules      := charmap locfile-lex locfile-parse ctype \
+                          monetary messages collate numeric
+locale-modules         := ctypedump
+lib-modules            := error hash xmalloc
+
+
+GPERF = gperf
+GPERFFLAGS = -acCgopt -k1,2,5,$$
 
 include ../Rules
+
+keyword.h: keyword.gperf
+       $(GPERF) $(GPERFFLAGS) $< > $@.new
+       mv -f $@.new $@
+
+$(objpfx)localedef: $(localedef-modules:%=$(objpfx)%.o)
+$(objpfx)locale: $(locale-modules:%=$(objpfx)%.o)
+$(objpfx)localedef $(objpfx)locale: $(lib-modules:%=$(objpfx)%.o)
+
+CPPFLAGS += -DLOCALE_PATH='"$(localedir)"' -DCHARMAP_PATH='"$(nlsdir)/charmap"'
index 6bdad35..be3c6bc 100644 (file)
@@ -101,6 +101,10 @@ DEFINE_CATEGORY (LC_TIME, "LC_TIME",
                  { D_FMT,      "d_fmt",      std, string },
                  { T_FMT,      "t_fmt",      std, string },
                  { T_FMT_AMPM, "t_fmt_ampm", std, string },
+                 { ERA,        "era",        opt, string },
+                 { ERA_YEAR,   "era_year",   opt, string },
+                 { ERA_D_FMT,  "era_d_fmt",  opt, string },
+                 { ALT_DIGITS, "alt_digits", opt, stringarray,  0, 100 },
                  { 0 }
                  ), NO_POSTLOAD, NULL, NULL, NULL )
 
diff --git a/locale/charmap.c b/locale/charmap.c
new file mode 100644 (file)
index 0000000..ad1075e
--- /dev/null
@@ -0,0 +1,524 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "localedef.h"
+#include "hash.h"
+
+/* Data structure for representing charmap database.  */
+struct charmap charmap_data;
+
+/* Line number in charmap file.  */
+static unsigned int line_no;
+
+/* Prototypes for local functions.  */
+static void read_prolog (FILE *infile);
+static unsigned long read_body (FILE *infile);
+
+
+/* Read complete table of symbolic names for character set from file.  If
+   this file does not exist or is not readable a default file is tried.
+   If this also is not readable no character map is defined.  */
+void
+charmap_read (const char *filename)
+{
+  unsigned long max_char;
+  long path_max = pathconf (".", _PC_PATH_MAX);
+  char buf[path_max];
+  FILE *infile = NULL;
+
+  /* Initialize charmap data.  */
+  charmap_data.codeset_name = NULL;
+  charmap_data.mb_cur_max = -1;
+  charmap_data.mb_cur_min = -1;
+  charmap_data.escape_char = '\\';
+  charmap_data.comment_char = '#';
+
+  if (filename != NULL)
+    {
+      strcpy (buf, filename);
+      infile = fopen (filename, "r");
+      if (infile == NULL && filename[0] != '/')
+        {
+          snprintf (buf, path_max, "%s/%s", CHARMAP_PATH, filename);
+          infile = fopen (buf, "r");
+        }
+    }
+  if (infile == NULL)
+    {
+      if (filename != NULL)
+       error (0, errno, gettext ("input file `%s' not found"), filename);
+
+      snprintf (buf, path_max, "%s/%s", CHARMAP_PATH, DEFAULT_CHARMAP);
+      infile = fopen (buf, "r");
+
+      if (infile == NULL)
+       error (4, errno, gettext ("input file `%s' not found"), filename); 
+    }
+
+  charmap_data.filename = buf;
+  init_hash (&charmap_data.table, 500);
+  line_no = 0;
+
+  /* Read the prolog of the charmap file.  */
+  read_prolog (infile);
+
+  /* Last works on the charmap tables global data.  */
+  if (charmap_data.mb_cur_max == -1)
+    charmap_data.mb_cur_max = 1;
+  if (charmap_data.mb_cur_min == -1)
+    charmap_data.mb_cur_min = charmap_data.mb_cur_max;
+
+  if ((size_t) charmap_data.mb_cur_max > sizeof (long))
+    {
+      error (2, 0, gettext ("program limitation: for now only upto %Zu "
+                           "bytes per character are allowed"), sizeof (long));
+    }
+
+  /* Now process all entries.  */
+  max_char = read_body (infile);
+
+  /* We don't need the file anymore.  */
+  fclose (infile);
+
+
+  /* Determine the optimal table size when using the simple modulo hashing
+     function.  */
+  if (max_char >= 256)
+    {
+      int size;
+      /* Current best values, initialized to some never reached high value.  */
+      int best_count = 10000;
+      int best_size = 10000;
+      int best_product = best_count * best_size;
+
+      /* Give warning.  */
+      error (-1, 0, gettext ("computing character table size: this may take "
+                            "a while"));
+
+      for (size = 256; size <= best_product; ++size)
+       {
+         /* Array with slot counters.  */
+         int cnt[size];
+         /* Current character.  */
+         int ch;
+         /* Maximal number of characters in any slot.  */
+         int maxcnt = 0;
+         /* Product of current size and maximal count.  */
+         int product = 0;
+         /* Iteration pointer through hashing table.  */
+         char *ptr = NULL;
+
+         /* Initializes counters to zero.  */
+         memset(cnt, 0, size * sizeof (int));
+
+         /* Iterate through whole hashing table.  */
+         while (product < best_product
+                && iterate_table (&charmap_data.table, (void **) &ptr,
+                                  (void **) &ch))
+           {
+             /* Increment slot counter.  */
+             ++cnt[ch % size];
+             /* Test for current maximum.  */
+             if (cnt[ch % size] > maxcnt)
+               {
+                 maxcnt = cnt[ch % size];
+                 product = maxcnt * size;
+               }
+           }
+
+         if (product < best_product)
+           {
+             best_count = maxcnt;
+             best_size = size;
+             best_product = best_count * best_size;
+           }
+       }
+
+      charmap_data.hash_size = best_size;
+      charmap_data.hash_layers = best_count;
+    }
+  else
+    {
+      charmap_data.hash_size = 256;
+      charmap_data.hash_layers = 1;
+    }
+}
+
+
+#define SYNTAX_ERROR                                                        \
+  do { error (0, 0, gettext ("%s:%u: syntax error in charmap file"),        \
+             charmap_data.filename, line_no);                               \
+       goto end_of_loop; } while (0)
+
+/* Read the prolog of the charmap file until the line containing `CHARMAP'.
+   All possible entries are processed.  */
+static void
+read_prolog (FILE *infile)
+{
+  size_t bufsize = sysconf (_SC_LINE_MAX);
+  char buf[bufsize];
+
+  while (1)
+    {
+      char *cp = buf;
+      char len;
+
+      /* Read the next line.  */
+      fgets (buf, bufsize, infile);
+      len = strlen (buf);
+
+      /* On EOF simply return.  */
+      if (len == 0 || buf[len - 1] != '\n')
+       error (4, 0, gettext ("%s: unexpected end of file in charmap"),
+              charmap_data.filename);
+
+      /* This is the next line.  */
+      ++line_no;
+
+      /* Comments and empty lines are ignored.  */
+      if (len == 1 || buf[0] == charmap_data.comment_char)
+       continue;
+
+      buf[len - 1] = '\0';
+
+      /* Throw away leading white spaces.  This is not defined in POSIX.2
+        so don't do it if conformance is requested.  */
+      if (!posix_conformance)
+       while (isspace (*cp))
+         ++cp;
+
+      /* If `CHARMAP' is read the prolog is over.  */
+      if (strncmp (cp, "CHARMAP", 7) == 0
+         && (!posix_conformance || cp[7] == '\0'))
+       return;
+
+      /* Now it can be only one of special symbols defining the charmap
+        parameters.  All are beginning with '<'.  */
+      if (*cp != '<')
+       SYNTAX_ERROR;
+
+      ++cp;
+      if (strncmp (cp, "code_set_name>", 14) == 0)
+       {
+         char *startp;
+
+#define cp_to_arg(no,pred)                                                   \
+         cp += no;                                                           \
+         while (isspace (*cp))                                               \
+           ++cp;                                                             \
+         if (*cp == '\0' || !pred (*cp))                                     \
+            SYNTAX_ERROR;
+
+         cp_to_arg (14,isgraph)
+
+         if (charmap_data.codeset_name != NULL)
+           {
+             error (0, 0, gettext ("%s:%u: duplicate code set name "
+                                   "specification"),
+                    charmap_data.filename, line_no);
+             free (charmap_data.codeset_name);
+           }
+
+         startp = cp;
+         while (*cp != '\0' && isgraph (*cp) && !isspace (*cp))
+           ++cp;
+
+         charmap_data.codeset_name = (char *) xmalloc (cp - startp + 1);
+         strncpy (startp, startp, cp - startp);
+       }
+      else if (strncmp (cp, "mb_cur_max>", 11) == 0)
+       {
+          int new_val;
+         cp_to_arg (11,isdigit)
+
+         if (charmap_data.mb_cur_max != -1)
+           error (0, 0,
+                  gettext ("%s:%u: duplicate definition of mb_cur_max"),
+                  charmap_data.filename, line_no);
+
+         new_val = (int) strtol (cp, &cp, posix_conformance ? 10 : 0);
+         if (new_val < 1)
+           error (0, 0, gettext ("%s:%u: illegal value for mb_cur_max: %d"),
+                  charmap_data.filename, line_no, new_val);
+         else
+           charmap_data.mb_cur_max = new_val;
+       }
+      else if (strncmp (cp, "mb_cur_min>", 11) == 0)
+       {
+          int new_val;
+         cp_to_arg (11,isdigit)
+
+         if (charmap_data.mb_cur_max != -1)
+           error (0, 0,
+                  gettext ("%s:%u: duplicate definition of mb_cur_min"),
+                  charmap_data.filename, line_no);
+
+         new_val = (int) strtol (cp, &cp, posix_conformance ? 10 : 0);
+         if (new_val < 1)
+           error (0, 0, gettext ("%s:%u: illegal value for mb_cur_min: %d"),
+                  charmap_data.filename, line_no, new_val);
+         else
+           charmap_data.mb_cur_min = new_val;
+       }
+      else if (strncmp (cp, "escape_char>", 12) == 0)
+       {
+         cp_to_arg (12, isgraph)
+         charmap_data.escape_char = *cp;
+       }
+      else if (strncmp (cp, "comment_char>", 13) == 0)
+       {
+         cp_to_arg (13, isgraph)
+         charmap_data.comment_char = *cp;
+       }
+      else
+       SYNTAX_ERROR;
+      end_of_loop:
+    }
+}
+#undef cp_to_arg
+
+
+static unsigned long
+read_body (FILE *infile)
+{
+  unsigned long max_char = 0;
+  size_t bufsize = sysconf (_SC_LINE_MAX);
+  char buf[bufsize];
+  char name_str[bufsize / 2];
+  char code_str[bufsize / 2];
+
+  while (1)
+    {
+      char *cp = buf;
+      size_t len;
+
+      /* Read the next line.  */
+      fgets (buf, bufsize, infile);
+      len = strlen (buf);
+
+      /* On EOF simply return.  */
+      if (len == 0)
+       error (0, 0, gettext ("%s: `END CHARMAP' is missing"),
+              charmap_data.filename);
+
+      /* This is the next line.  */
+      ++line_no;
+
+      if (len == bufsize - 1)
+       {
+         error (0, 0, gettext ("%s:%u: line too long;  use `getconf "
+                               "LINE_MAX' to get the current maximum line"
+                               "length"), charmap_data.filename, line_no);
+         do
+           {
+             fgets (buf, bufsize, infile);
+             len = strlen (buf);
+           }
+         while (len == bufsize - 1);
+         continue;
+       }
+
+      /* Comments and empty lines are ignored.  */
+      if (len == 1 || buf[0] == charmap_data.comment_char)
+       continue;
+
+      buf[len - 1] = '\0';
+
+      /* Throw away leading white spaces.  This is not defined in POSIX.2
+        so don't do it if conformance is requested.  */
+      if (!posix_conformance)
+       while (isspace (*cp))
+         ++cp;
+
+      if (*cp == '<')
+       {
+         char *end1p, *end2p, *start2p;
+         size_t cnt = 0;
+         unsigned long char_value = 0;
+
+         if (sscanf (cp + 1, "%s %s", name_str, code_str) != 2)
+           SYNTAX_ERROR;
+
+         end1p = cp = name_str;
+         while (*cp != '\0' && *cp != '>')
+           {
+             if (*cp == charmap_data.escape_char)
+               if (*++cp == '\0')
+                 SYNTAX_ERROR;
+             *end1p++ = *cp++;
+           }
+         if (*cp == '\0')
+           /* No final '>'.  Make error condition.  */
+           end1p = name_str;
+         else
+           ++cp;
+
+         *end1p = '\0';
+         
+         if (*cp == '.' && *++cp == '.' && *++cp == '.' && *++cp == '<')
+           {
+             /* This might be the alternate form.  */
+             start2p = end2p = ++cp;
+             while (*cp != '\0' && *cp != '>')
+               {
+                 if (*cp == charmap_data.escape_char)
+                   if (*++cp == '\0')
+                     SYNTAX_ERROR;
+                 *end2p = *cp++;
+               }
+             if (*cp == '\0')
+               /* NO final '>'.  Make error condition.  */
+               end2p = start2p;
+             else
+               ++cp;
+           }
+         else
+           start2p = end2p = NULL;
+
+
+         if (end1p == name_str || (start2p != NULL && start2p != end2p)
+             || *cp != '\0'
+             || *code_str != charmap_data.escape_char)
+           SYNTAX_ERROR;
+
+         cp = code_str;
+         do
+           {
+             char *begin;
+             long val;
+
+             switch (*++cp)
+               {
+               case 'd':
+                 val = strtol ((begin = cp + 1), &cp, 10);
+                 break;
+               case 'x':
+                 val = strtol ((begin = cp + 1), &cp, 16);
+                 break;
+               default:
+                 val = strtol ((begin = cp), &cp, 8);
+                 break;
+               }
+             if (begin == cp)
+               SYNTAX_ERROR;
+
+             if (posix_conformance && cp - begin < 2)
+               error (0, 0, gettext ("%s:%u: byte constant has less than "
+                                     "two digits"),
+                      charmap_data.filename, line_no);
+
+             if (val < 0 || val > 255)
+               {
+                 error (0, 0, gettext ("%s:%u: character encoding must be "
+                                       "given in 8-bit bytes"),
+                        charmap_data.filename, line_no);
+                 goto end_of_loop;
+               }
+
+             if (cnt < (size_t) charmap_data.mb_cur_max)
+               {
+                 if (cnt < sizeof (long))  /* FIXME */
+                   char_value = (char_value << 8) | val;
+               }
+             else
+               {
+                 error (0, 0, gettext ("%s:%u: number of bytes in character "
+                                       "definition exceeds `mb_cur_max'"),
+                        charmap_data.filename, line_no);
+                 break;
+               }
+             ++cnt;
+           }
+         while (*cp == charmap_data.escape_char);
+
+         /* Ignore the rest of the line (comment).  */
+         if (end2p == NULL)
+           {
+             if (insert_entry (&charmap_data.table, name_str,
+                               end1p - name_str, (void *) char_value))
+               error (0, 0, gettext ("%s:%u: duplicate entry"),
+                      charmap_data.filename, line_no);
+
+             max_char = MAX (max_char, char_value);
+           }
+         else
+           {
+             char *en1, *en2, *start1p;
+             long n1, n2, n;
+
+             start1p = name_str;
+
+             while (*start1p == *start2p && !isdigit (*start1p)
+                    && start1p < end1p)
+                 ++start1p, ++start2p;
+
+             n1 = strtol (start1p, &en1, 10);
+             n2 = strtol (start2p, &en2, 10);
+
+             if (en1 - start1p != en2 - start2p || en1 != end1p
+                 || en2 != end2p)
+               SYNTAX_ERROR;
+
+             if (n1 > n2)
+               error (0, 0, gettext ("%s:%u: starting character is bigger "
+                                     "than last"),
+                      charmap_data.filename, line_no);
+
+             n = n1;
+             while (n <= n2)
+               {
+                 snprintf(start1p, en1 - start1p, "%0*d", en1 - start1p, n);
+
+                 if (insert_entry (&charmap_data.table, name_str,
+                                   en1 - name_str,
+                                   (void *) (char_value + n - n1)))
+                   error (0, 0, gettext ("%s:%u: duplicate entry"),
+                          charmap_data.filename, line_no);
+
+                 max_char = MAX (max_char, char_value + n - n1);
+                 ++n;
+               }
+           }
+       }
+      else
+       {
+         if (strncmp (cp, "END CHARMAP", 11) == 0)
+           return max_char;
+         
+         SYNTAX_ERROR;
+       }
+      end_of_loop:
+    }
+
+  return max_char;
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/collate.c b/locale/collate.c
new file mode 100644 (file)
index 0000000..f06964e
--- /dev/null
@@ -0,0 +1,212 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <langinfo.h>
+#include <libintl.h>
+
+#include "localedef.h"
+#include "token.h"
+
+
+/* defined in locfile-lex.c: flag to indicate that unknown element names
+   are allowed.  */
+extern int reject_new_char;
+
+
+#define SYNTAX_ERROR                                                         \
+    error (0, 0, gettext ("%s:%Zd: syntax error in locale definition file"), \
+          locfile_data.filename, locfile_data.line_no);
+
+void
+collate_input (int token)
+{
+  int read_order_start = 0;
+
+  while (1)
+    {
+      char *ptr;
+      int len;
+
+      if (token == TOK_END)
+       /* This is the end of the category.  */
+       {
+         token = xlocfile_lex (&ptr, &len);
+
+         if (token != _NL_NUM_LC_COLLATE)
+           {
+             error (0, 0, gettext ("%s:%Zd: category `%s' does not end "
+                                   "with `END %s'"), locfile_data.filename,
+                        locfile_data.line_no, "LC_COLLATE", "LC_COLLATE");
+             ignore_to_eol (0, 0);
+           }
+         else
+           ignore_to_eol (0, 1);
+
+         /* Start next category.  */
+         break;
+       }
+
+#if 0
+      /* Process line.  */
+      if (read_order_start == 0)
+       /* We're still in the preambel.  */
+       {
+         switch (token)
+           {
+           case TOK_COLLATING_ELEMENT:
+             reject_new_char = 0;
+             token = xlocfile_lex (&ptr, &len);
+             reject_new_char = 1;
+             if (token == TOK_CHAR)
+               {
+                 error (0, 0, gettext ("%s:%Zd: symbolic name must not be "
+                                       "duplicate name in charmap"),
+                        locfile_data.filename, locfile_data.line_no);
+                 ignore_to_eol (0, 0);
+                 break;
+               }
+             else if (token != TOK_ILL_CHAR)
+               {
+                 SYNTAX_ERROR;
+                 ignore_to_eol (0, 0);
+                 break;
+               }
+             else
+               {
+                 char elem_name[len + 1];
+                 memcpy (elem_name, ptr, len);
+                 elem_name[len] = '\0';
+
+                 /* Test whether defined in symbol table.  */
+
+                 token = xlocfile_lex (&ptr, &len);
+                 if (token != TOK_FROM)
+                   {
+                     SYNTAX_ERROR;
+                     ignore_to_eol (0, 0);
+                     break;
+                   }
+
+                 token  = xlocfile_lex (&ptr, &len);
+                  if (token != TOK_STRING)
+                   {
+                     SYNTAX_ERROR;
+                     ignore_to_eol (0, 0);
+                     break;
+                   }
+
+                 /* Insert collating element into table.  */
+
+                 /* Rest of the line should be empty.  */
+                 ignore_to_eol (0, 1);
+               }
+             break;
+           case TOK_COLLATING_SYMBOL:
+             reject_new_char = 0;
+             token = xlocfile_lex (&ptr, &len);
+             reject_new_char = 1;
+             if (token == TOK_CHAR)
+               {
+                 error (0, 0, gettext ("%s:%Zd: symbolic name must not "
+                                       "duplicate name in charmap"),
+                        locfile_data.filename, locfile_data.line_no);
+                 ignore_to_eol (0, 0);
+                 break;
+               }
+             else if (token != TOK_ILL_CHAR)
+               {
+                 SYNTAX_ERROR;
+                 ignore_to_eol (0, 0);
+                 break;
+               }
+             else
+               {
+                 /* Test whether defined in element table.  */
+
+                 /* Insert collating symbol into table.  */
+
+                 ignore_to_eol (0, 1);
+               }
+           case TOK_ORDER_START:
+             nsort_rules = 0;
+
+             do
+               {
+                 token = xlocfile_lex (&ptr, &len);
+
+                 if (nsort_rules == 0 && token == ENDOFLINE)
+                   break;
+
+                 if (token != TOK_BACKWARD && token != TOK_FORWARD
+                     && token != TOK_POSITION)
+                   {
+                     SYNTAX_ERROR;
+                     break;
+                   }
+
+                 switch (token)
+                   {
+                   case TOK_BACKWARD:
+                     if ((sort_rule[nsort_rules] & FORWARD_BIT) != 0)
+                       error (0, 0, gettext ("%s:%Zd: directives `forward' "
+                                             "and `backward' are mutually "
+                                             "exclusive"),
+                              locfile_data.filename, locfile_data.lineno);
+                     else
+                       sort_rule[nsort_rules] |= BACKWARD_BIT;
+                     break;
+                   case TOK_FORWARD:
+                     if ((sort_rule[nsort_rules] & BACKWARD_BIT) != 0)
+                       error (0, 0, gettext ("%s:%Zd: directives `forward' "
+                                             "and `backward' are mutually "
+                                             "exclusive"),
+                              locfile_data.filename, locfile_data.lineno);
+                      else
+                        sort_rule[nsort_rules] |= FORWARD_BIT;
+                     break;
+                   case TOK_POSITION:
+                     sort_rule[nsort_rules] |= POSITION_BIT;
+                     break;
+                   }
+
+                 ++nsort_rules;
+
+                 
+               }
+             break;
+           default:
+             SYNTAX_ERROR;
+             ignore_to_eol (token, 0);
+           }
+       }
+      else
+       {
+       }
+#endif
+
+      ignore_to_eol(token,0);
+      /* Get next token.  */
+      token = xlocfile_lex (&ptr, &len);
+    }
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/config.h b/locale/config.h
new file mode 100644 (file)
index 0000000..65dfb33
--- /dev/null
@@ -0,0 +1,45 @@
+/* config.h used by locale and localedef programs in GNU libc.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#define PACKAGE                "libc"
+#define VERSION                __libc_version
+extern const char __libc_version[];
+
+#define DEFAULT_CHARMAP        "POSIX"
+
+
+/* These are tested by xmalloc.c and error.c.  */
+#define HAVE_VPRINTF 1
+#define STDC_HEADERS 1
+#define HAVE_STRERROR 1
+
+#define program_name program_invocation_name
+
+typedef unsigned short u16;
+typedef int i32;
+typedef int u32;
+
+
+/* Get the global libc configuration info.  */
+#include_next <config.h>
+
+#endif /* config.h */
diff --git a/locale/ctype.c b/locale/ctype.c
new file mode 100644 (file)
index 0000000..1ce09ba
--- /dev/null
@@ -0,0 +1,817 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <alloca.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <locale.h>
+#include <localeinfo.h>
+#include <langinfo.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/uio.h>
+
+#include "localedef.h"
+#include "token.h"
+
+/* Arrays representing ctype tables.  They must be initialized for the
+   right size to hold the full charmap.  */
+static u16 *ctype_b;
+static i32 *names_b, *toupper_b, *tolower_b;
+
+/* For accessing the element of the (possibly sparse) array we use this
+   macro.  */
+#define ELEM(arr, idx)                                                      \
+  (arr)[({ int h = idx % charmap_data.hash_size;                            \
+           int n = 0;                                                       \
+           while (n < charmap_data.hash_layers                               \
+                 && names_b[n * charmap_data.hash_size + h] != idx)         \
+           ++n;                                                                     \
+           if (n >= charmap_data.hash_layers)                               \
+             error (6, 0, gettext ("internal error in %s, line %u"),         \
+                   __FUNCTION__, __LINE__);                                 \
+           n * charmap_data.hash_size + h; })]
+
+/* The bit used for representing a special class.  */
+#define BITPOS(class) ((class) - TOK_UPPER)
+#define BIT(class) (1 << BITPOS (class))
+
+/* Remember which class or conversion is already done.  */
+static unsigned short class_done = 0;
+static unsigned short toupper_done = 0;
+static unsigned short tolower_done = 0;
+
+#define SYNTAX_ERROR                                                         \
+    error (0, 0, gettext ("%s:%Zd: syntax error in locale definition file"), \
+                         locfile_data.filename, locfile_data.line_no);
+
+/* Prototypes for local functions.  */
+static void allocate_arrays (void);
+static void set_class_defaults (void);
+static int valid_char (int ch);
+
+
+/* Read CTYPE category.  The initial token is given as a parameter.  */
+void
+ctype_input (int token)
+{
+  char *ptr;
+  int len;
+
+  /* If necessary allocate arrays.  */
+  allocate_arrays ();
+
+  while (token != TOK_END)
+    {
+      switch (token)
+       {
+       case TOK_UPPER:  case TOK_LOWER: case TOK_ALPHA: case TOK_DIGIT:
+       case TOK_XDIGIT: case TOK_SPACE: case TOK_PRINT: case TOK_GRAPH:
+       case TOK_BLANK:  case TOK_CNTRL: case TOK_PUNCT:
+         {
+           /* TAKE CARE: the order of the tokens in "token.h" determines
+              the bit used to indicate the membership in the class.  This
+              also has to correspond to the values used in <ctype.h>.  */
+           int bit = BIT (token);
+           int was_ell = 0;
+           int last = -1;
+
+           if ((class_done & bit) != 0)
+             {
+               char tmp[len + 1];
+               memcpy (tmp, ptr, len);
+               tmp[len] = '\0';
+
+               error (0, 0, gettext ("%s:%Zd: duplicate definiton of item "
+                                     "`%s' in category `LC_CTYPE'"),
+                      locfile_data.filename, locfile_data.line_no, tmp);
+             }
+           class_done |= bit;
+
+           do
+             {
+               token = xlocfile_lex (&ptr, &len);
+
+               if (token == TOK_ENDOFLINE)
+                 {
+                   SYNTAX_ERROR;
+                   break;
+                 }
+
+               if (token == TOK_ELLIPSIS)
+                 {
+                   if (was_ell != 0 || last < 0)
+                     {
+                       error (0, 0, gettext ("%s:%Zd: illegal use of `...'"),
+                              locfile_data.filename, locfile_data.line_no);
+                       break;
+                     }
+                   was_ell = 1;
+                   continue;
+                 }
+
+               if (token != TOK_CHAR)
+                 {
+                   if (token != TOK_ILL_CHAR)
+                     SYNTAX_ERROR;
+                   was_ell = 0;
+                   last = -1;
+                   continue;
+                 }
+
+               if (len < 0 || !valid_char (len))
+                 {
+                   was_ell = 0;
+                   last = -1;
+                   continue;
+                 }
+
+               /* We have found a valid character.  Include it to
+                  the class' bit set.  */
+               if (was_ell == 0)
+                 {
+                   ELEM (ctype_b, len) |= bit;
+                   last = len;
+                 }
+               else
+                 {
+                   int i;
+
+                   if (last > len)
+                     {
+                       error (0, 0, gettext ("%s:%Zd: lower bound of "
+                                             "ellipsis not smaller"),
+                              locfile_data.filename, locfile_data.line_no);
+                       was_ell = 0;
+                       last = -1;
+                       continue;
+                     }
+
+                   for (i = last + 1; i <= len; ++i)
+                     ELEM (ctype_b, i) |= bit;
+
+                   last = -1;
+                 }
+               was_ell = 0;
+             }
+           while ((token = locfile_lex (&ptr, &len)) == TOK_CHAR
+                  && len == ';');
+
+           /* Rest of the line should be empty.  */
+           ignore_to_eol (token, 0);
+         }
+         break;
+       case TOK_TOUPPER: case TOK_TOLOWER:
+         {
+           int from;
+           int to = -1;
+           int is_upper = token == TOK_TOUPPER;
+
+           if (((is_upper ? toupper_done : tolower_done) & BIT (token)) != 0)
+             error (0, 0, gettext ("%s:%Zd: duplicate definition of item "
+                                   "`%s' in category `LC_CTYPE'"),
+                    locfile_data.filename, locfile_data.line_no,
+                    is_upper ? "toupper" : "tolower");
+           (is_upper ? toupper_done : tolower_done) |= BIT (token); 
+
+           do
+             {
+               int ignore;
+
+               token = xlocfile_lex (&ptr, &len);
+               if (token != TOK_CHAR || len != '(')
+                 {
+                   SYNTAX_ERROR;
+                   break;
+                 }
+
+               token = xlocfile_lex (&ptr, &len);
+               if (token != TOK_CHAR && token != TOK_ILL_CHAR)
+                 {
+                   SYNTAX_ERROR;
+                   break;
+                 }
+               from = len;
+               ignore = token == TOK_ILL_CHAR;
+
+               token = xlocfile_lex (&ptr, &len);
+               if (token != TOK_CHAR || len != ',')
+                 {
+                   SYNTAX_ERROR;
+                   break;
+                 }
+
+               token = xlocfile_lex (&ptr, &len);
+               if (token != TOK_CHAR && token != TOK_ILL_CHAR)
+                 {
+                   SYNTAX_ERROR;
+                   break;
+                 }
+               to = len;
+               ignore |= token == TOK_ILL_CHAR;
+             
+               token = xlocfile_lex (&ptr, &len);
+               if (token != TOK_CHAR || len != ')')
+                 {
+                   SYNTAX_ERROR;
+                   break;
+                 }
+
+               if (!ignore && valid_char (from) && valid_char (to))
+                 /* Have a valid pair.  */
+                 ELEM (is_upper ? toupper_b : tolower_b, from) = to;
+             }
+           while ((token = locfile_lex (&ptr, &len)) == TOK_CHAR
+                  && len == ';');
+
+           /* Rest of the line should be empty.  */
+           ignore_to_eol (token, 1);
+         }
+         break;
+       default:
+         SYNTAX_ERROR;
+         ignore_to_eol (0, 0);
+         break;
+       }
+
+      /* Read next token.  */
+      token = xlocfile_lex (&ptr, &len);
+    }
+
+  token = xlocfile_lex (&ptr, &len);
+
+  if (token != _NL_NUM_LC_CTYPE)
+    {
+      error (0, 0, gettext ("%s:%Zd: category `%s' does not end with "
+                           "`END %s'"), locfile_data.filename,
+            locfile_data.line_no, "LC_CTYPE", "LC_CTYPE");
+      ignore_to_eol (0, 0);
+    }
+  else
+    ignore_to_eol (0, posix_conformance);
+}
+
+
+void
+ctype_check(void)
+{
+  /* Here are a lot of things to check.  See POSIX.2, table 2-6.  */
+  #define NCLASS 11
+  static const struct
+    {
+      const char *name;
+      const char allow[NCLASS];
+    }
+  valid_table[NCLASS] =
+    {
+      /* The order is important.  See token.h for more information.
+         M = Always, D = Default, - = Permitted, X = Mutually exclusive  */
+      [BITPOS (TOK_UPPER)]  = { "upper",  "--MX-XDDXXX" },
+      [BITPOS (TOK_LOWER)]  = { "lower",  "--MX-XDDXXX" },
+      [BITPOS (TOK_ALPHA)]  = { "alpha",  "---X-XDDXXX" },
+      [BITPOS (TOK_DIGIT)]  = { "digit",  "XXX--XDDXXX" },
+      [BITPOS (TOK_XDIGIT)] = { "xdigit", "-----XDDXXX" },
+      [BITPOS (TOK_SPACE)]  = { "space",  "XXXXX------" },
+      [BITPOS (TOK_PRINT)]  = { "print",  "---------X-" },
+      [BITPOS (TOK_GRAPH)]  = { "graph",  "---------X-" },
+      [BITPOS (TOK_BLANK)]  = { "blank",  "XXXXXM-----" },
+      [BITPOS (TOK_CNTRL)]  = { "cntrl",  "XXXXX-XX--X" },
+      [BITPOS (TOK_PUNCT)]  = { "punct",  "XXXXX-DD-X-" }
+    };
+  int ch, cls1, cls2, eq, space_char;
+  u16 tmp;
+
+  /* Set default value for classes not specified.  */
+  set_class_defaults ();
+
+  /* Check according to table.  */
+  for (ch = 0; ch < charmap_data.hash_size * charmap_data.hash_layers; ++ch)
+    {
+      if (ch != 0 && names_b[ch] == 0)
+       continue;
+      tmp = ELEM (ctype_b, names_b[ch]);
+      for (cls1 = 0; cls1 < NCLASS; ++cls1)
+        if ((tmp & (1 << cls1)) != 0)
+          for (cls2 = 0; cls2 < NCLASS; ++cls2)
+            if (cls2 != cls1 && valid_table[cls1].allow[cls2] != '-')
+              {
+                eq = (tmp & (1 << cls2)) != 0;
+                switch (valid_table[cls1].allow[cls2])
+                  {
+                  case 'M':
+                    if (!eq)
+                      error (0, 0, gettext ("character '\\%o' in class `%s' "
+                                           "must be in class `%s'"), ch,
+                            valid_table[cls1].name, valid_table[cls2].name);
+                    break;
+                  case 'X':
+                    if (eq)
+                      error (0, 0, gettext ("character '\\%o' inc class `%s' "
+                                           "must not be in class `%s'"), ch,
+                            valid_table[cls1].name, valid_table[cls2].name);
+                    break;
+                  case 'D':
+                    ELEM (ctype_b, names_b[ch]) |= 1 << cls2;
+                    break;
+                  default:
+                    error (5, 0, gettext ("internal error in %s, line %u"),
+                          __FUNCTION__, __LINE__);
+                  }
+              }
+    }
+
+  /* ... and now test <SP>  as a special case.  */
+  if (find_entry (&charmap_data.table, "SP", 2, (void **) &space_char) == 0)
+    error (0, 0, gettext ("character <SP> not defined in character map"));
+  else if ((tmp = BITPOS (TOK_SPACE),
+            (ELEM (ctype_b, space_char) & BIT (TOK_SPACE)) == 0)
+           || (tmp = BITPOS (TOK_BLANK),
+               (ELEM (ctype_b, space_char) & BIT (TOK_BLANK)) == 0))
+    error (0, 0, gettext ("<SP> character not in class `%s'"),
+          valid_table[tmp].name);
+  else if ((tmp = BITPOS (TOK_PUNCT),
+            (ELEM (ctype_b, space_char) & BIT (TOK_PUNCT)) != 0)
+           || (tmp = BITPOS (TOK_GRAPH),
+               (ELEM (ctype_b, space_char) & BIT (TOK_GRAPH)) != 0))
+    error (0, 0, gettext ("<SP> character must not be in class `%s'"),
+          valid_table[tmp].name);
+  else
+    ELEM (ctype_b, space_char) |= BIT (TOK_PRINT);
+}
+
+
+/* These macros can change little to big endian and vice versa.  */
+#define SWAP16(v)                                                           \
+      ((u16) (((((unsigned short) (v)) & 0x00ff) << 8)                      \
+             | ((((unsigned short) (v)) & 0xff00) >> 8)))
+#define SWAP32(v)                                                           \
+       ((u32) (((((u32) (v)) & 0x000000ff) << 24)                           \
+               | ((((u32) (v)) & 0x0000ff00) << 8)                          \
+               | ((((u32) (v)) & 0x00ff0000) >> 8)                          \
+               | ((((u32) (v)) & 0xff000000) >> 24)))
+
+
+int
+ctype_output (void)
+{
+  char *path, *t;
+  int ch;
+  /* File descriptor for output file.  */
+  int fd;
+  /* Magic number.  */
+  i32 magic = LIMAGIC (LC_CTYPE);
+  /* Number of table.  */
+  int tables = 6;
+  /* Number ints in leading information table.  */
+#if 0
+  i32 n = 2 + 2 * tables;
+#else
+  i32 n = 5;
+#endif
+  /* Values describing the character set.  */
+  char mb_cur_min = (char) charmap_data.mb_cur_min;
+  char mb_cur_max = (char) charmap_data.mb_cur_max;
+  /* Optimal size of hashing table.  */
+  i32 hash_size = charmap_data.hash_size;
+  i32 hash_layers = charmap_data.hash_layers;
+  /* Number of elements in the tables.  */
+  int size = hash_size * charmap_data.hash_layers;
+  /* Positions of the tables.  */
+  i32 pos[14] =
+    {
+      /* No, no.  We don't play towers of Hanoi.  This is a more or less
+        readable table of the offsets of the different strings in the
+        produced file.  It is seperated in three columns which represent
+        the number of values with 1, 2, and 4 bytes.  */
+
+#if 0
+                                   4 *  (2 + n),
+      1 +                          4 *  (2 + n),
+      2 +                          4 *  (2 + n),
+      2 +                          4 *  (3 + n),
+      2 +                          4 *  (4 + n),
+      2 + 2 *      (128 + size)  + 4 *  (4 + n),
+      2 + 2 *      (128 + size)  + 4 * ((4 + n) +     (size + 128)),
+      2 + 2 *      (128 + size)  + 4 * ((4 + n) + 2 * (size + 128)),
+      2 + 2 *      (128 + size)  + 4 * ((4 + n) + 2 * (size + 128) + 1 * size),
+      2 + 2 *      (128 + size)  + 4 * ((5 + n) + 2 * (size + 128) + 1 * size),
+      2 + 2 *      (128 + size)  + 4 * ((6 + n) + 2 * (size + 128) + 1 * size),
+      2 + 2 * (2 * (128 + size)) + 4 * ((6 + n) + 2 * (size + 128) + 1 * size),
+      2 + 2 * (2 * (128 + size)) + 4 * ((6 + n) + 3 * (size + 128) + 1 * size),
+      2 + 2 * (2 * (128 + size)) + 4 * ((6 + n) + 4 * (size + 128) + 1 * size),
+#else
+                                   4 *  (2 + n),
+          2 *      (128 + size)  + 4 *  (2 + n),
+          2 *      (128 + size)  + 4 * ((2 + n) +     (size + 128)),
+          2 *      (128 + size)  + 4 * ((2 + n) + 2 * (size + 128)),
+         2 *      (128 + size)  + 4 * ((2 + n) + 3 * (size + 128)),
+#endif
+    };
+  /* Parameter to writev.  */
+  struct iovec iov[11] = 
+    { 
+      { &magic, sizeof (i32) },
+      { &n, sizeof (i32) },
+#if 0
+      { pos, sizeof (pos) },
+      { &mb_cur_min, 1 },
+      { &mb_cur_max, 1 },
+      { &hash_size, sizeof (i32) },
+      { &hash_layers, sizeof (i32) },
+#else
+      { pos, 5 * 4 },
+#endif
+      { ctype_b   - 128, (size + 128) * sizeof (u16) },
+      { toupper_b - 128, (size + 128) * sizeof (i32) },
+      { tolower_b - 128, (size + 128) * sizeof (i32) },
+      { names_b, size * sizeof (i32) }
+    };
+  int result = 0;
+  
+  /* Now we can bring the representations into the right form.  */
+  for (ch = -128; ch < -1; ++ch)
+    {
+      ctype_b[ch] = ctype_b[256 + ch];
+      toupper_b[ch] = toupper_b[256 + ch];
+      tolower_b[ch] = tolower_b[256 + ch];
+    }
+  /* Set value for EOF.  */
+  ctype_b[-1] = 0;
+  toupper_b[-1] = -1;
+  tolower_b[-1] = -1;
+
+  for (ch = -128; ch < size; ++ch)
+    ctype_b[ch] = htons (ctype_b[ch]);
+
+  /* Construct the output filename from the argument given to
+     localedef on the command line.  */
+  path = (char *) alloca (strlen (output_path) +
+                         strlen (category[LC_CTYPE].name) + 1);
+  t = stpcpy (path, output_path);
+  strcpy (t, category[LC_CTYPE].name);
+
+  fd = creat (path, 0666);
+  if (fd == -1)
+    {
+      error (0, 0, gettext ("cannot open output file `%s': %m"), path);
+      result = 1;
+    }
+  else
+    {
+      int idx;
+
+#if 0
+      if (writev (fd, iov, 10) == -1)
+#else
+      if (writev (fd, iov, 6) == -1)
+#endif
+       {
+         error (0, 0, gettext ("cannot write output file `%s': %m"), path);
+         result = 1;
+         goto close_and_return;
+       }
+
+      /* Now we have to write the three tables with different endianess.  */
+      hash_size = SWAP32 (hash_size);
+      for (idx = -128; idx < size; ++idx)
+       {
+         ctype_b[idx] = SWAP16 (ctype_b[idx]);
+         toupper_b[idx] = SWAP32 (toupper_b[idx]);
+         tolower_b[idx] = SWAP32 (tolower_b[idx]);
+         if (idx >= 0)
+           names_b[idx] = SWAP32 (names_b[idx]);
+       }
+
+#if 0
+      if (writev (fd, iov + 5, 6) == -1)
+#else
+      if (writev (fd, iov + 3, 2) == -1)
+#endif
+       {
+         error (0, 0, gettext ("cannot write output file `%s': %m"), path);
+         result = 1;
+       }
+
+      close_and_return:
+      close (fd);
+    }
+
+  return result;
+}
+
+
+/* If necessary allocate the memory for the arrays according to the
+   current character map.  */
+static void
+allocate_arrays (void)
+{
+  /* Init ctype data structures.  */
+  if (ctype_b == NULL)
+    /* All data structures are not initialized yet.  */
+    {
+      /* You wonder about this amount of memory?  This is only because
+        some users do not manage to address the array with unsigned
+        values or data types with range >= 256.  '\200' would result
+        in the array index -128.  To help these poor people we
+        duplicate the entries for 128 upto 255 below the entry for \0.  */
+      int ch, h, n;
+      char *ptr;
+      int size = charmap_data.hash_size * charmap_data.hash_layers;
+
+      ctype_b = (u16 *) xcalloc (size - (-128), sizeof (u16));
+      ctype_b += 128;
+
+
+      names_b = (i32 *) xcalloc (size, sizeof (i32));
+
+      toupper_b = (i32 *) xcalloc ((size - (-128)), sizeof  (i32));
+      toupper_b += 128;
+
+      tolower_b = (i32 *) xcalloc ((size - (-128)), sizeof (i32));
+      tolower_b += 128;
+
+      ptr = NULL;
+      /* Mark the place of the NUL character as occupied.  */
+      names_b[0] = 1;
+
+      while (iterate_table (&charmap_data.table, (void **) &ptr,
+                           (void **) &ch))
+       {
+         /* We already handled the NUL character.  */
+         if (ch == 0)
+           continue;
+
+         h = ch % charmap_data.hash_size;
+         n = 0;
+         while (names_b[h + n * charmap_data.hash_size] != 0)
+           ++n;
+
+         names_b[h + n * charmap_data.hash_size] = ch;
+         toupper_b[h + n * charmap_data.hash_size] = ch;
+         tolower_b[h + n * charmap_data.hash_size] = ch;
+       }
+      /* Correct the value for NUL character.  */
+      names_b[0] = 0;
+    }
+}
+
+static void
+set_class_defaults (void)
+{
+  /* These function defines the default values for the classes and conversions
+     according to POSIX.2 2.5.2.1.
+     It may seem that the order of these if-blocks is arbitrary but it is NOT.
+     Don't move them unless you know what you do!  */
+
+  void set_default (int bit, int from, int to)
+    {
+      char tmp[4];
+      int ch;
+      /* Define string.  */
+      strcpy (tmp, "<?>");
+
+      for (ch = from; ch <= to; ++ch)
+       {
+         int code;
+         tmp[1] = ch;
+
+         code = find_char (tmp + 1, 1);
+         if (code == -1)
+           error (5, 0, gettext ("character `%s' not defined while needed "
+                                 "as default value"), tmp);
+         ELEM (ctype_b, code) |= bit;
+       }
+    }
+
+  /* If necessary allocate arrays.  */
+  allocate_arrays ();
+
+  /* Set default values if keyword was not present.  */
+  if ((class_done & BIT (TOK_UPPER)) == 0)
+    /* "If this keyword [lower] is not specified, the lowercase letters
+        `A' through `Z', ..., shall automatically belong to this class,
+       with implementation defined character values."  */
+    set_default (BIT (TOK_UPPER), 'A', 'Z');
+
+  if ((class_done & BIT (TOK_LOWER)) == 0)
+    /* "If this keyword [lower] is not specified, the lowercase letters
+        `a' through `z', ..., shall automatically belong to this class,
+       with implementation defined character values."  */
+    set_default (BIT (TOK_LOWER), 'a', 'z');
+
+  if ((class_done & BIT (TOK_DIGIT)) == 0)
+    /* "If this keyword [digit] is not specified, the digits `0' through
+        `9', ..., shall automatically belong to this class, with
+       implementation-defined character values."  */        
+    set_default (BIT (TOK_DIGIT), '0', '9');
+
+  if ((class_done & BIT (TOK_SPACE)) == 0)
+    /* "If this keyword [space] is not specified, the characters <space>,
+        <form-feed>, <newline>, <carriage-return>, <tab>, and
+       <vertical-tab>, ..., shall automatically belong to this class,
+       with implementtation-defined character values."  */
+    {
+      int code;
+
+      code = find_char ("space", 5);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<space>");
+      ELEM (ctype_b, code) |= BIT (TOK_SPACE);
+
+      code = find_char ("form-feed", 9);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<form-feed>");
+      ELEM (ctype_b, code) |= BIT (TOK_SPACE);
+
+      code = find_char ("newline", 7);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<newline>");
+      ELEM (ctype_b, code) |= BIT (TOK_SPACE);
+
+      code = find_char ("carriage-return", 15);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<carriage-return>");
+      ELEM (ctype_b, code) |= BIT (TOK_SPACE);
+
+      code = find_char ("tab", 3);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<tab>");
+      ELEM (ctype_b, code) |= BIT (TOK_SPACE);
+
+      code = find_char ("vertical-tab", 11);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<vertical-tab>");
+      ELEM (ctype_b, code) |= BIT (TOK_SPACE);
+    }
+  
+  if ((class_done & BIT (TOK_XDIGIT)) == 0)
+    /* "If this keyword is not specified, the digits `0' to `9', the
+        uppercase letters `A' through `F', and the lowercase letters `a'
+       through `f', ..., shell automatically belong to this class, with
+       implementation defined character values."  */
+    {
+      if ((class_done & BIT (TOK_XDIGIT)) == 0)
+       set_default (BIT (TOK_XDIGIT), '0', '9');
+
+      if ((class_done & BIT (TOK_XDIGIT)) == 0)
+       set_default (BIT (TOK_XDIGIT), 'A', 'F');
+
+      if ((class_done & BIT (TOK_XDIGIT)) == 0)
+       set_default (BIT (TOK_XDIGIT), 'a', 'f');
+    }
+
+  if ((class_done & BIT (TOK_BLANK)) == 0)
+    /* "If this keyword [blank] is unspecified, the characters <space> and
+       <tab> shall belong to this character class."  */
+   {
+      int code;
+
+      code = find_char ("space", 5);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<space>");
+      ELEM (ctype_b, code) |= BIT (TOK_BLANK);
+
+      code = find_char ("tab", 3);
+      if (code == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<tab>");
+      ELEM (ctype_b, code) |= BIT (TOK_BLANK);
+    }
+
+  if ((class_done & BIT (TOK_GRAPH)) == 0)
+    /* "If this keyword [graph] is not specified, characters specified for
+        the keywords `upper', `lower', `alpha', `digit', `xdigit' and `punct',
+       shall belong to this character class."  */
+    {
+      int ch;
+      unsigned short int mask = BIT (TOK_UPPER) | BIT (TOK_LOWER) |
+       BIT (TOK_ALPHA) | BIT (TOK_DIGIT) | BIT (TOK_XDIGIT) | BIT (TOK_PUNCT);
+
+      for (ch = 0; ch < charmap_data.hash_size * charmap_data.hash_layers;
+                  ++ch)
+       {
+         if (ch != 0 && names_b[ch] == 0)
+           continue;
+         if ((ELEM (ctype_b, names_b[ch]) & mask) != 0)
+           ELEM (ctype_b, names_b[ch]) |= BIT (TOK_GRAPH);
+       }
+    }
+
+  if ((class_done & BIT (TOK_PRINT)) == 0)
+    /* "If this keyword [print] is not provided, characters specified for
+        the keywords `upper', `lower', `alpha', `digit', `xdigit', `punct',
+       and the <space> character shall belong to this character class."  */
+    {
+      int ch;
+      int space = find_char ("space", 5);
+      unsigned short int mask = BIT (TOK_UPPER) | BIT (TOK_LOWER) |
+       BIT (TOK_ALPHA) | BIT (TOK_DIGIT) | BIT (TOK_XDIGIT) | BIT (TOK_PUNCT);
+
+      if (space == -1)
+       error (5, 0, gettext ("character `%s' not defined while needed as "
+                             "default value"), "<space>");
+
+      for (ch = 0; ch < charmap_data.hash_size * charmap_data.hash_layers;
+                  ++ch)
+       {
+         if (ch != 0 && names_b[ch] == 0)
+           continue;
+         if ((ELEM (ctype_b, names_b[ch]) & mask) != 0)
+           ELEM (ctype_b, names_b[ch]) |= BIT (TOK_PRINT);
+       }
+      ELEM (ctype_b, space) |= BIT (TOK_PRINT);
+    }
+
+  if (toupper_done == 0)
+    /* "If this keyword [toupper] is not spcified, the lowercase letters
+        `a' through `z', and their corresponding uppercase letters `A' to
+       `Z', ..., shall automatically be included, with implementation-
+       defined character values."  */
+    {
+      char tmp[4];
+      int ch;
+
+      strcpy (tmp, "<?>");
+
+      for (ch = 'a'; ch <= 'z'; ++ch)
+       {
+         int code_to, code_from;
+
+         tmp[1] = ch;
+         code_from = find_char (tmp + 1, 1);
+         if (code_from == -1)
+           error (5, 0, gettext ("character `%s' not defined while needed "
+                                 "as default value"), tmp);
+
+         /* This conversion is implementation defined.  */
+         tmp[1] = ch + ('A' - 'a');
+         code_to = find_char (tmp + 1, 1);
+         if (code_to == -1)
+           error (5, 0, gettext ("character `%s' not defined while needed "
+                                 "as default value"), tmp);
+
+         ELEM (toupper_b, code_from) = code_to;
+       }
+    }
+
+  if (tolower_done == 0)
+    /* "If this keyword [tolower] is not specified, the mapping shall be
+        the reverse mapping of the one specified to `toupper'."  */
+    {
+      int ch;
+
+      for (ch = 0; ch < charmap_data.hash_size * charmap_data.hash_layers;
+                  ++ch)
+       {
+         if (ch != 0 && names_b[ch] == 0)
+           continue;
+
+         if (toupper_b[ch] != names_b[ch])
+           ELEM (tolower_b, toupper_b[ch]) = names_b[ch];
+       }
+    }
+}
+
+
+/* Test whether the given character is valid for the current charmap.  */
+static int
+valid_char (int ch)
+{
+  /* FIXME: this assumes 32-bit integers.  */
+  int ok = ch >= 0
+    && (charmap_data.mb_cur_max < 4
+       ? ch < 1 << (8 * charmap_data.mb_cur_max) : 1);
+
+  return ok;
+}
+
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/ctypedump.c b/locale/ctypedump.c
new file mode 100644 (file)
index 0000000..e00f3e0
--- /dev/null
@@ -0,0 +1,163 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <ctype.h>
+#include <endian.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <netinet/in.h>        /* Just for htons() */
+
+#include "localedef.h"
+#include "localeinfo.h"
+
+
+/* FIXME: these values should be part of the LC_CTYPE information.  */
+#define mb_cur_max 1
+#define mb_cur_min 1
+
+
+#define SWAP32(v)                                                           \
+       ((u32) (((((u32) (v)) & 0x000000ff) << 24)                           \
+               | ((((u32) (v)) & 0x0000ff00) << 8)                          \
+               | ((((u32) (v)) & 0x00ff0000) >> 8)                          \
+               | ((((u32) (v)) & 0xff000000) >> 24)))
+
+
+
+static inline void
+print_short_in_char (unsigned short val)
+{
+  const unsigned char *p = (const unsigned char *) &val;
+  printf ("\"\\%03o\\%03o\"", p[0], p[1]);
+}
+
+
+static inline void
+print_int_in_char (unsigned int val)
+{
+  const unsigned char *p = (const unsigned char *) &val;
+  printf ("\"\\%03o\\%03o\\%03o\\%03o\"", p[0], p[1], p[2], p[3]);
+}
+
+int
+ctype_output (void)
+{
+  int ch;
+  int result = 0;
+  const char *locname = (getenv ("LC_ALL") ?: getenv ("LC_CTYPE") ?: 
+                        getenv ("LANG") ?: "POSIX");
+
+  puts ("#include <endian.h>\n");
+
+  if (mb_cur_max == 1)
+    {
+      printf ("const char _nl_%s_LC_CTYPE_class[] = \n", locname);
+      for (ch = -128; ch < (1 << (8 * MB_CUR_MAX)); ++ch)
+        {
+         if (((ch + 128) % 6) == 0)
+           printf ("  /* 0x%02x */ ", ch < 0 ? 256 + ch : ch);
+         print_short_in_char (htons (__ctype_b [ch < 0 ? 256 + ch : ch]));
+         fputc (((ch + 128) % 6) == 5 ? '\n' : ' ', stdout);
+        }
+      puts (";");
+    }
+
+  printf ("#if BYTE_ORDER == %s\n",
+         BYTE_ORDER == LITTLE_ENDIAN ? "LITTLE_ENDIAN" : "BIG_ENDIAN");
+
+  if (mb_cur_max == 1)
+    {
+      printf ("const char _nl_%s_LC_CTYPE_toupper[] = \n", locname);
+      for (ch = -128; ch < (1 << (8 * MB_CUR_MAX)); ++ch)
+        {
+         if (((ch + 128) % 3) == 0)
+           printf ("  /* 0x%02x */ ", ch < 0 ? 256 + ch : ch);
+         print_int_in_char (__ctype_toupper[ch < 0 ? 256 + ch : ch]);
+         fputc (((ch + 128) % 3) == 2 ? '\n' : ' ', stdout);
+        }
+      puts (";");
+
+      printf ("const char _nl_%s_LC_CTYPE_tolower[] = \n", locname);
+      for (ch = -128; ch < (1 << (8 * MB_CUR_MAX)); ++ch)
+        {
+         if (((ch + 128) % 3) == 0)
+           printf ("  /* 0x%02x */ ", ch < 0 ? 256 + ch : ch);
+         print_int_in_char (__ctype_tolower[ch < 0 ? 256 + ch : ch]);
+         fputc (((ch + 128) % 3) == 2 ? '\n' : ' ', stdout);
+        }
+      puts (";");
+    }
+  else
+    /* not implemented */;
+
+  printf ("#elif BYTE_ORDER == %s\n",
+          BYTE_ORDER == LITTLE_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN");
+
+  if (mb_cur_max == 1)
+    {
+      printf ("const char _nl_%s_LC_CTYPE_toupper[] = \n", locname);
+      for (ch = -128; ch < (1 << (8 * MB_CUR_MAX)); ++ch)
+        {
+         if (((ch + 128) % 3) == 0)
+           printf ("  /* 0x%02x */ ", ch < 0 ? 256 + ch : ch);
+         print_int_in_char (SWAP32 (__ctype_toupper[ch < 0 ? 256 + ch : ch]));
+         fputc (((ch + 128) % 3) == 2 ? '\n' : ' ', stdout);
+        }
+      puts (";");
+
+      printf ("const char _nl_%s_LC_CTYPE_tolower[] = \n", locname);
+      for (ch = -128; ch < (1 << (8 * MB_CUR_MAX)); ++ch)
+        {
+         if (((ch + 128) % 3) == 0)
+           printf ("  /* 0x%02x */ ", ch < 0 ? 256 + ch : ch);
+         print_int_in_char (SWAP32 (__ctype_tolower[ch < 0 ? 256 + ch : ch]));
+         fputc (((ch + 128) % 3) == 2 ? '\n' : ' ', stdout);
+        }
+      puts (";");
+    }
+  else
+    /* not implemented */;
+
+  puts ("#else\n#error \"BYTE_ORDER\" BYTE_ORDER \" not handled.\"\n#endif\n");
+
+  printf("const struct locale_data _nl_%s_LC_CTYPE = \n\
+{\n\
+  NULL, 0, /* no file mapped */\n\
+  5,\n\
+  {\n\
+    _nl_C_LC_CTYPE_class,\n\
+#ifdef BYTE_ORDER == LITTLE_ENDIAN\n\
+    NULL, NULL,\n\
+#endif\n\
+    _nl_C_LC_CTYPE_toupper,\n\
+    _nl_C_LC_CTYPE_tolower,\n\
+#ifdef BYTE_ORDER == BIG_ENDIAN\n\
+    NULL, NULL,\n\
+#endif\n\
+  }\n\
+};\n", locname);
+
+  return result;
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/hash.c b/locale/hash.c
new file mode 100644 (file)
index 0000000..75cb77f
--- /dev/null
@@ -0,0 +1,254 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <obstack.h>
+#include <stdlib.h>
+#include <string.h>
+#include <values.h>
+
+#include "hash.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+void *xmalloc (size_t n);
+
+typedef struct hash_entry
+  {
+    int used;
+    char *key;
+    void *data;
+    struct hash_entry *next;
+  }
+hash_entry;
+
+/* Prototypes for local functions.  */
+static size_t lookup (hash_table *htab, const char *key, size_t keylen,
+                     unsigned long hval);
+static unsigned long compute_hashval(const char *key, size_t keylen);
+static unsigned long next_prime(unsigned long seed);
+static int is_prime(unsigned long candidate);
+
+
+int
+init_hash(hash_table *htab, unsigned long init_size)
+{
+  /* We need the size to be a prime.  */
+  init_size = next_prime (init_size);
+
+  /* Initialize the data structure.  */
+  htab->size = init_size;
+  htab->filled = 0;
+  htab->first = NULL;
+  htab->table = calloc (init_size + 1, sizeof (hash_entry));
+  obstack_init (&htab->mem_pool);
+
+  return htab->table == NULL;
+}
+
+
+int
+delete_hash(hash_table *htab)
+{
+  free (htab->table);
+  obstack_free (&htab->mem_pool, NULL);
+  return 0;
+}
+
+
+int
+insert_entry (hash_table *htab, const char *key, size_t keylen, void *data)
+{
+  unsigned long hval = compute_hashval (key, keylen);
+  hash_entry *table = (hash_entry *) htab->table;
+  size_t idx = lookup (htab, key, keylen, hval);
+
+  if (table[idx].used)
+    /* We don't want to overwrite the old value.  */
+    return 1;
+  else
+    {
+      hash_entry **p;
+
+      /* An empty bucket has been found.  */
+      table[idx].used = hval;
+      table[idx].key = obstack_copy0 (&htab->mem_pool, key, keylen);
+      table[idx].data = data;
+
+      /* List the new value in the ordered list.  */
+      for (p = (hash_entry **) &htab->first; *p != NULL && (*p)->data < data;
+          p = &(*p)->next);
+      if (*p == NULL || (*p)->data > data)
+       /* Insert new value in the list.  */
+       {
+         table[idx].next = *p;
+         *p = &table[idx];
+       }
+
+      ++htab->filled;
+      if (100 * htab->filled > 90 * htab->size)
+       {
+         /* Resize the table.  */
+         unsigned long old_size = htab->size;
+
+         htab->size = next_prime (htab->size * 2);
+         htab->filled = 0;
+         htab->first = NULL;
+         htab->table = calloc (htab->size, sizeof (hash_entry));
+
+         for (idx = 1; idx <= old_size; ++idx)
+           if (table[idx].used)
+             insert_entry (htab, table[idx].key, strlen(table[idx].key),
+                           table[idx].data);
+
+         free (table);
+       }
+      return 0;
+    }
+  /* NOTREACHED */
+}
+
+
+int
+find_entry (hash_table *htab, const char *key, size_t keylen, void **result)
+{
+  hash_entry *table = (hash_entry *) htab->table;
+  size_t idx = lookup (htab, key, keylen, compute_hashval (key, keylen));
+  int retval;
+
+  retval = table[idx].used;
+  *result = retval ? table[idx].data : NULL;
+
+  return retval;
+}
+
+
+int
+iterate_table (hash_table *htab, void **ptr, void **result)
+{
+  if (*ptr == NULL)
+    *ptr = (void *) htab->first;
+  else
+    {
+      *ptr = (void *) (((hash_entry *) *ptr)->next);
+      if (*ptr == NULL)
+       return 0;
+    }
+
+  *result = ((hash_entry *) *ptr)->data;
+  return 1;
+}
+
+
+static size_t
+lookup (hash_table *htab, const char *key, size_t keylen, unsigned long hval)
+{
+  unsigned long hash;
+  size_t idx;
+  hash_entry *table = (hash_entry *) htab->table;
+
+  /* First hash function: simply take the modul but prevent zero.  */
+  hash = 1 + hval % htab->size;
+
+  idx = hash;
+  
+  if (table[idx].used)
+    {
+      if (table[idx].used == hval && table[idx].key[keylen] == '\0'
+         && strncmp (key, table[idx].key, keylen) == 0)
+       return idx;
+
+      /* Second hash function as suggested in [Knuth].  */
+      hash = 1 + hash % (htab->size - 2);
+
+      do
+       {
+         if (idx <= hash)
+           idx = htab->size + idx - hash;
+         else
+           idx -= hash;
+
+         /* If entry is found use it.  */
+         if (table[idx].used == hval && table[idx].key[keylen] == '\0'
+             && strncmp (key, table[idx].key, keylen) == 0)
+           return idx;
+       }
+      while (table[idx].used);
+    }
+  return idx;
+}
+
+
+static unsigned long
+compute_hashval(const char *key, size_t keylen)
+{
+  size_t cnt;
+  unsigned long hval, g;
+  /* Compute the hash value for the given string.  */
+  cnt = 0;
+  hval = keylen;
+  while (cnt < keylen)
+    {
+      hval <<= 4;
+      hval += key[cnt++];
+      g = hval & (0xf << (LONGBITS - 4));
+      if (g != 0)
+       {
+         hval ^= g >> (LONGBITS - 8);
+         hval ^= g;
+       }
+    }
+  return hval;
+}
+
+
+static unsigned long
+next_prime(unsigned long seed)
+{
+  /* Make it definitely odd.  */
+  seed |= 1;
+
+  while (!is_prime (seed))
+    seed += 2;
+
+  return seed;
+}
+
+
+static int
+is_prime(unsigned long candidate)
+{
+  /* No even number and none less than 10 will be passwd here.  */
+  unsigned long div = 3;
+  unsigned long sq = div * div;
+
+  while (sq < candidate && candidate % div != 0)
+    {
+      ++div;
+      sq += 4 * div;
+      ++div;
+    }
+
+  return candidate % div != 0;
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/hash.h b/locale/hash.h
new file mode 100644 (file)
index 0000000..5f60a44
--- /dev/null
@@ -0,0 +1,50 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#include <obstack.h>
+
+typedef struct hash_table
+  {
+    unsigned long size;
+    unsigned long filled;
+    void *first;
+    void *table;
+    struct obstack mem_pool;
+  }
+hash_table;
+
+
+int init_hash (hash_table *htab, unsigned long init_size);
+int delete_hash(hash_table *htab);
+int insert_entry (hash_table *htab, const char *key, size_t keylen,
+                 void *data);
+int find_entry (hash_table *htab, const char *key, size_t keylen,
+               void **result);
+
+int iterate_table (hash_table *htab, void **ptr, void **result);
+
+#endif /* hash.h */
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
+
diff --git a/locale/iso-4217.def b/locale/iso-4217.def
new file mode 100644 (file)
index 0000000..8494203
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Defines the valid international currency symbols according to ISO-4217.
+ * This is used in monetary.c(monetary_check).
+ *
+ * !!! The list has to be sorted !!!
+ */
+DEFINE_INT_CURR("ATS ")                /* Austria  */
+DEFINE_INT_CURR("BEF ")                /* Belgium  */
+DEFINE_INT_CURR("CAD ")                /* Canada  */
+DEFINE_INT_CURR("CHF ")                /* Switzerland  */
+DEFINE_INT_CURR("DEM ")                /* Germany  */
+DEFINE_INT_CURR("DKK ")                /* Denmark  */
+DEFINE_INT_CURR("EEK ")                /* Estonia  */
+DEFINE_INT_CURR("ESP ")                /* Spain  */
+DEFINE_INT_CURR("FIM ")                /* Finland  */
+DEFINE_INT_CURR("FRF ")                /* France  */
+DEFINE_INT_CURR("GBP ")                /* Great Britain  */
+DEFINE_INT_CURR("GRD ")                /* Greece  */
+DEFINE_INT_CURR("HRD ")                /* Croatia  */
+DEFINE_INT_CURR("HUF ")                /* Hungary  */
+DEFINE_INT_CURR("IEP ")                /* Ireland  */
+DEFINE_INT_CURR("ILS ")                /* Israel  */
+DEFINE_INT_CURR("ISK ")                /* Iceland  */
+DEFINE_INT_CURR("ITL ")                /* Italy  */
+DEFINE_INT_CURR("LTL ")                /* Lithuania  */
+DEFINE_INT_CURR("LUF ")                /* Luxemburg  */
+DEFINE_INT_CURR("LVL ")                /* Latvia  */
+DEFINE_INT_CURR("NLG ")                /* Netherlands  */
+DEFINE_INT_CURR("NOK ")                /* Norway  */
+DEFINE_INT_CURR("PLZ ")                /* Poland  */
+DEFINE_INT_CURR("PTE ")                /* Portugal  */
+DEFINE_INT_CURR("ROL ")                /* Romania  */
+DEFINE_INT_CURR("RUR ")                /* Russia  */
+DEFINE_INT_CURR("SEK ")                /* Sweden  */
+DEFINE_INT_CURR("SIT ")                /* Slovenia  */
+DEFINE_INT_CURR("USD ")                /* United States  */
diff --git a/locale/keyword.gperf b/locale/keyword.gperf
new file mode 100644 (file)
index 0000000..20941bc
--- /dev/null
@@ -0,0 +1,77 @@
+%{
+/* `strncmp' is used for comparison.  */
+#include <string.h>
+
+/* This file defines `enum token'.  */
+#include "token.h"
+%}
+struct locale_keyword { char *name; enum token token_id; };
+%%
+END,               TOK_END
+IGNORE,            TOK_IGNORE
+LC_COLLATE,        _NL_NUM_LC_COLLATE
+LC_CTYPE,          _NL_NUM_LC_CTYPE
+LC_MESSAGES,       _NL_NUM_LC_MESSAGES
+LC_MONETARY,       _NL_NUM_LC_MONETARY
+LC_NUMERIC,        _NL_NUM_LC_NUMERIC
+LC_TIME,           _NL_NUM_LC_TIME
+UNDEFINED,         TOK_UNDEFINED
+abday,             ABDAY_1
+abmon,             ABMON_1
+alpha,             TOK_ALPHA
+alt_digits,        ALT_DIGITS
+am_pm,             AM_STR
+backward,          TOK_BACKWARD
+blank,             TOK_BLANK
+cntrl,             TOK_CNTRL
+collating_element, TOK_COLLATING_ELEMENT
+collating_symbol,  TOK_COLLATING_SYMBOL
+comment_char,      TOK_COMMENT_CHAR
+copy,              TOK_COPY
+currency_symbol,   CURRENCY_SYMBOL
+d_fmt,             D_FMT
+d_t_fmt,           D_T_FMT
+day,               DAY_1
+decimal_point,     DECIMAL_POINT
+digit,             TOK_DIGIT
+era,               ERA
+era_d_fmt,         ERA_D_FMT
+era_year,          ERA_YEAR
+escape_char,       TOK_ESCAPE_CHAR
+forward,           TOK_FORWARD
+frac_digits,       FRAC_DIGITS
+from,              TOK_FROM
+graph,             TOK_GRAPH
+grouping,          GROUPING
+int_curr_symbol,   INT_CURR_SYMBOL
+int_frac_digits,   INT_FRAC_DIGITS
+lower,             TOK_LOWER
+mon,               MON_1
+mon_decimal_point, MON_DECIMAL_POINT
+mon_grouping,      MON_GROUPING
+mon_thousands_sep, MON_THOUSANDS_SEP
+n_cs_precedes,     N_CS_PRECEDES
+n_sep_by_space,    N_SEP_BY_SPACE
+n_sign_posn,       N_SIGN_POSN
+negative_sign,     NEGATIVE_SIGN
+noexpr,            NOEXPR
+nostr,             NOSTR
+order_end,         TOK_ORDER_END
+order_start,       TOK_ORDER_START
+p_cs_precedes,     P_CS_PRECEDES
+p_sep_by_space,    P_SEP_BY_SPACE
+p_sign_posn,       P_SIGN_POSN
+position,          TOK_POSITION
+positive_sign,     POSITIVE_SIGN
+print,             TOK_PRINT
+punct,             TOK_PUNCT
+space,             TOK_SPACE
+t_fmt,             T_FMT
+t_fmt_ampm,        T_FMT_AMPM
+thousands_sep,     THOUSANDS_SEP
+tolower,           TOK_TOLOWER
+toupper,           TOK_TOUPPER
+upper,             TOK_UPPER
+xdigit,            TOK_XDIGIT
+yesexpr,           YESEXPR
+yesstr,            YESSTR
diff --git a/locale/keyword.h b/locale/keyword.h
new file mode 100644 (file)
index 0000000..1dc442a
--- /dev/null
@@ -0,0 +1,180 @@
+/* C code produced by gperf version 2.5 (GNU C++ version) */
+/* Command-line: gperf -acCgopt -k1,2,5, keyword.gperf  */
+/* `strncmp' is used for comparison.  */
+#include <string.h>
+
+/* This file defines `enum token'.  */
+#include "token.h"
+struct locale_keyword { char *name; enum token token_id; };
+
+#define TOTAL_KEYWORDS 68
+#define MIN_WORD_LENGTH 3
+#define MAX_WORD_LENGTH 17
+#define MIN_HASH_VALUE 4
+#define MAX_HASH_VALUE 140
+/* maximum key range = 137, duplicates = 0 */
+
+#ifdef __GNUC__
+inline
+#endif
+static unsigned int
+hash (register const char *str, register int len)
+{
+  static const unsigned char asso_values[] =
+    {
+     141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+     141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+     141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+     141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+     141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+     141, 141, 141, 141, 141, 141, 141, 141, 141, 141,
+     141, 141, 141, 141, 141, 141, 141,   0, 141,  65,
+       5,   0, 141,  30, 141, 141,   0, 141,   0,  95,
+     141, 141,   0, 141,  45,  10, 141, 141, 141, 141,
+     141, 141, 141, 141, 141,   5, 141,  10,  85,   0,
+      20,   0,  40,  35,  30,  10, 141,   0,  30,  15,
+      15,   0,   0, 141,  55,   0,   0,  80, 141,  15,
+      10,   0, 141, 141, 141, 141, 141, 141,
+    };
+  register int hval = len;
+
+  switch (hval)
+    {
+      default:
+      case 5:
+        hval += asso_values[str[4]];
+      case 4:
+      case 3:
+      case 2:
+        hval += asso_values[str[1]];
+      case 1:
+        hval += asso_values[str[0]];
+    }
+  return hval;
+}
+
+#ifdef __GNUC__
+inline
+#endif
+const struct locale_keyword *
+in_word_set (register const char *str, register int len)
+{
+  static const struct locale_keyword wordlist[] =
+    {
+      {"",}, {"",}, {"",}, {"",}, 
+      {"copy",               TOK_COPY},
+      {"space",              TOK_SPACE},
+      {"yesstr",             YESSTR},
+      {"toupper",            TOK_TOUPPER},
+      {"position",           TOK_POSITION},
+      {"",}, 
+      {"t_fmt",              T_FMT},
+      {"escape_char",        TOK_ESCAPE_CHAR},
+      {"comment_char",       TOK_COMMENT_CHAR},
+      {"positive_sign",      POSITIVE_SIGN},
+      {"",}, 
+      {"t_fmt_ampm",         T_FMT_AMPM},
+      {"",}, 
+      {"yesexpr",            YESEXPR},
+      {"mon",                MON_1},
+      {"p_sep_by_space",     P_SEP_BY_SPACE},
+      {"LC_NUMERIC",         _NL_NUM_LC_NUMERIC},
+      {"noexpr",             NOEXPR},
+      {"tolower",            TOK_TOLOWER},
+      {"p_cs_precedes",      P_CS_PRECEDES},
+      {"UNDEFINED",          TOK_UNDEFINED},
+      {"",}, 
+      {"collating_symbol",   TOK_COLLATING_SYMBOL},
+      {"collating_element",  TOK_COLLATING_ELEMENT},
+      {"negative_sign",      NEGATIVE_SIGN},
+      {"",}, 
+      {"d_fmt",              D_FMT},
+      {"",}, 
+      {"mon_thousands_sep",  MON_THOUSANDS_SEP},
+      {"day",                DAY_1},
+      {"n_sep_by_space",     N_SEP_BY_SPACE},
+      {"digit",              TOK_DIGIT},
+      {"IGNORE",             TOK_IGNORE},
+      {"LC_TIME",            _NL_NUM_LC_TIME},
+      {"n_cs_precedes",      N_CS_PRECEDES},
+      {"",}, 
+      {"int_curr_symbol",    INT_CURR_SYMBOL},
+      {"",}, {"",}, 
+      {"thousands_sep",      THOUSANDS_SEP},
+      {"",}, 
+      {"am_pm",              AM_STR},
+      {"xdigit",             TOK_XDIGIT},
+      {"",}, 
+      {"decimal_point",      DECIMAL_POINT},
+      {"",}, 
+      {"cntrl",              TOK_CNTRL},
+      {"p_sign_posn",        P_SIGN_POSN},
+      {"mon_decimal_point",  MON_DECIMAL_POINT},
+      {"LC_CTYPE",           _NL_NUM_LC_CTYPE},
+      {"",}, 
+      {"alpha",              TOK_ALPHA},
+      {"",}, 
+      {"forward",            TOK_FORWARD},
+      {"era",                ERA},
+      {"",}, 
+      {"print",              TOK_PRINT},
+      {"",}, 
+      {"mon_grouping",       MON_GROUPING},
+      {"era_year",           ERA_YEAR},
+      {"",}, {"",}, 
+      {"n_sign_posn",        N_SIGN_POSN},
+      {"",}, 
+      {"END",                TOK_END},
+      {"",}, 
+      {"alt_digits",         ALT_DIGITS},
+      {"",}, 
+      {"d_t_fmt",            D_T_FMT},
+      {"",}, {"",}, 
+      {"nostr",              NOSTR},
+      {"LC_MESSAGES",        _NL_NUM_LC_MESSAGES},
+      {"",}, {"",}, {"",}, 
+      {"int_frac_digits",    INT_FRAC_DIGITS},
+      {"",}, {"",}, {"",}, 
+      {"era_d_fmt",          ERA_D_FMT},
+      {"punct",              TOK_PUNCT},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"lower",              TOK_LOWER},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"currency_symbol",    CURRENCY_SYMBOL},
+      {"",}, {"",}, 
+      {"grouping",           GROUPING},
+      {"from",               TOK_FROM},
+      {"abday",              ABDAY_1},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"LC_COLLATE",         _NL_NUM_LC_COLLATE},
+      {"LC_MONETARY",        _NL_NUM_LC_MONETARY},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"frac_digits",        FRAC_DIGITS},
+      {"",}, {"",}, {"",}, 
+      {"abmon",              ABMON_1},
+      {"",}, {"",}, 
+      {"backward",           TOK_BACKWARD},
+      {"order_end",          TOK_ORDER_END},
+      {"blank",              TOK_BLANK},
+      {"order_start",        TOK_ORDER_START},
+      {"",}, {"",}, {"",}, 
+      {"graph",              TOK_GRAPH},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"upper",              TOK_UPPER},
+    };
+
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = hash (str, len);
+
+      if (key <= MAX_HASH_VALUE && key >= 0)
+        {
+          register const char *s = wordlist[key].name;
+
+          if (*s == *str && !strncmp (str + 1, s + 1, len - 1))
+            return &wordlist[key];
+        }
+    }
+  return 0;
+}
index e94be68..245dcf8 100644 (file)
@@ -145,8 +145,10 @@ typedef enum
   _NL_NUM_LC_MESSAGES,
 
   /* Stubs for unfinished categories.  */
-  _NL_NUM_LC_COLLATE = 0,
+  _NL_NUM_LC_COLLATE = _NL_ITEM (LC_COLLATE, 0),
 
+  /* This marks the highest value used.  */
+  _NL_NUM
 } nl_item;
 
 
diff --git a/locale/libintl.h b/locale/libintl.h
new file mode 100644 (file)
index 0000000..802bd45
--- /dev/null
@@ -0,0 +1,77 @@
+/* libintl.h -- Message catalogs for internationalization.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef _LIBINTL_H
+#define        _LIBINTL_H      1
+
+#include <locale.h>
+
+
+/* Look up MSGID in the current default message catalog for the current
+   LC_MESSAGES locale.  If not found, returns MSGID itself (the default
+   text).  */
+extern char *gettext __P ((const char *__msgid));
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current
+   LC_MESSAGES locale.  */
+extern char *dgettext __P ((const char *__domainname, const char *__msgid));
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
+   locale.  */
+extern char *__dcgettext __P ((const char *__domainname, const char *__msgid,
+                              int __category));
+extern char *dcgettext __P ((const char *__domainname, const char *__msgid,
+                            int __category));
+
+
+/* Set the current default message catalog to DOMAINNAME.
+   If DOMAINNAME is null, return the current default.
+   If DOMAINNAME is "", reset to the default of "messages".  */
+extern char *textdomain __P ((const char *__domainname));
+
+/* Specify that the DOMAINNAME message catalog will be found
+   in DIRNAME rather than in the system locale data base.  */
+extern char *bindtextdomain __P ((const char *__domainname,
+                                 const char *__dirname));
+
+
+#if 1 /* XXX stub for the moment */
+#define gettext(msgid) (msgid)
+#define textdomain(domain) (void)(domain)
+#else
+#define gettext(msgid)                 __gettext (msgid)
+#define __gettext(msgid)               __dgettext (NULL, (msgid))
+
+#define        dgettext(domainname, msgid)     __dgettext (domainname, msgid)
+#define        __dgettext(domainname, msgid)   \
+  __dcgettext (NULL, (msgid), LC_MESSAGES)
+
+#ifdef __GNUC__
+#define        __dcgettext(domainname, msgid, category)                              \
+  (__extension__                                                             \
+   ({                                                                        \
+     static char *__translation__;                                           \
+     if (! __translation__)                                                  \
+       __translation__ = (__dcgettext) ((domainname), (msgid), (category));   \
+     __translation__;                                                        \
+    }))
+#endif
+#endif
+
+#endif /* libintl.h */
diff --git a/locale/locale.c b/locale/locale.c
new file mode 100644 (file)
index 0000000..20385f0
--- /dev/null
@@ -0,0 +1,538 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <dirent.h>
+#include <getopt.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "localedef.h"
+
+
+/* If set dump C code describing the current locale.  */
+static int do_dump;
+
+/* If set print the name of the category.  */
+static int show_category_name;
+
+/* If set print the name of the item.  */
+static int show_keyword_name;
+
+/* Long options.  */
+static const struct option long_options[] =
+  {
+    { "all-locales", no_argument, NULL, 'a' },
+    { "category-name", no_argument, &show_category_name, 1 },
+    { "charmaps", no_argument, NULL, 'm' },
+    { "dump", no_argument, &do_dump, 1 },
+    { "help", no_argument, NULL, 'h' },
+    { "keyword-name", no_argument, &show_keyword_name, 1 },
+    { "version", no_argument, NULL, 'v' },
+    { NULL, 0, NULL, 0 }
+  };
+
+
+/* We don't have these constants defined because we don't use them.  Give
+   default values.  */
+#define CTYPE_MB_CUR_MIN 0
+#define CTYPE_MB_CUR_MAX 0
+#define CTYPE_HASH_SIZE 0
+#define CTYPE_HASH_LAYERS 0
+#define CTYPE_CLASS 0
+#define CTYPE_TOUPPER_EB 0
+#define CTYPE_TOLOWER_EB 0
+#define CTYPE_TOUPPER_EL 0
+#define CTYPE_TOLOWER_EL 0
+
+/* We have all categories defined in `categories.def'.  Now construct
+   the description and data structure used for all categories.  */
+#define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
+    static struct cat_item category##_desc[] =                               \
+      {                                                                              \
+        NO_PAREN items                                                       \
+      };
+
+#include "categories.def"
+#undef DEFINE_CATEGORY
+
+static struct category category[] =
+  {
+#define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
+    { _NL_NUM_##category, name, NELEMS (category##_desc) - 1,                 \
+      category##_desc, NULL, NULL, NULL, out },
+#include "categories.def"
+#undef DEFINE_CATEGORY
+  };
+#define NCATEGORIES NELEMS (category)
+
+
+/* Prototypes for local functions.  */
+static void usage (int status) __attribute__ ((noreturn));
+static void write_locales (void);
+static void write_charmaps (void);
+static void show_locale_vars (void);
+static void show_info (const char *name);
+static void dump_category (const char *name);
+
+
+int
+main (int argc, char *argv[])
+{
+  int optchar;
+  int do_all = 0;
+  int do_help = 0;
+  int do_version = 0;
+  int do_charmaps = 0;
+
+  /* Set initial values for global varaibles.  */
+  do_dump = 0;
+  show_category_name = 0;
+  show_keyword_name = 0;
+
+  /* Set locale.  Do not set LC_ALL because the other categories must
+     not be affected (acccording to POSIX.2).  */
+  setlocale (LC_CTYPE, "");
+  setlocale (LC_MESSAGES, "");
+
+  /* Initialize the message catalog.  */
+  textdomain (PACKAGE);
+
+  while ((optchar = getopt_long (argc, argv, "achkmv", long_options, NULL))
+         != EOF)
+    switch (optchar)
+      {
+      case '\0':
+       break;
+      case 'a':
+       do_all = 1;
+       break;
+      case 'c':
+       show_category_name = 1;
+       break;
+      case 'h':
+       do_help = 1;
+       break;
+      case 'k':
+       show_keyword_name = 1;
+       break;
+      case 'm':
+       do_charmaps = 1;
+       break;
+      case 'v':
+       do_version = 1;
+       break;
+      default:
+       error (1, 0, gettext ("illegal option \"%s\""), optarg);
+       break;
+      }
+
+  /* Version information is requested.  */
+  if (do_version)
+    {
+      fprintf (stderr, "GNU %s %s\n", PACKAGE, VERSION);
+      exit (EXIT_SUCCESS);
+    }
+
+  /* Help is requested.  */
+  if (do_help)
+    usage (EXIT_SUCCESS);
+
+  /* Dump C code.  */
+  if (do_dump)
+    {
+      printf ("\
+/* Generated by GNU %s %s.  */\n\
+\n\
+#include \"localeinfo.h\"\n", program_invocation_name, VERSION);
+
+      while (optind < argc)
+       dump_category (argv[optind++]);
+
+      exit (EXIT_SUCCESS);
+    }
+
+  /* `-a' requests the names of all available locales.  */
+  if (do_all != 0)
+    {
+      write_locales ();
+      exit (EXIT_SUCCESS);
+    }
+
+  /* `m' requests the names of all available charmaps.  The names can be
+     used for the -f argument to localedef(3).  */
+  if (do_charmaps != 0)
+    {
+      write_charmaps ();
+      exit (EXIT_SUCCESS);
+    }
+
+  /* If no real argument is given we have to print the contents of the
+     current locale definition variables.  These are LANG and the LC_*.  */
+  if (optind == argc && show_keyword_name == 0 && show_category_name == 0)
+    {
+      show_locale_vars ();
+      exit (EXIT_SUCCESS);
+    }
+
+  /* Process all given names.  */
+  while (optind <  argc)
+    show_info (argv[optind++]);
+
+  exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit.  */
+static void
+usage(int status)
+{
+  if (status != EXIT_SUCCESS)
+    fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
+             program_invocation_name);
+  else
+    printf(gettext ("\
+Usage: %s [OPTION]... name\n\
+Mandatory arguments to long options are mandatory for short options too.\n\
+  -h, --help            display this help and exit\n\
+  -v, --version         output version information and exit\n\
+\n\
+  -a, --all-locales     write names of available locales\n\
+  -m, --charmaps        write names of available charmaps\n\
+\n\
+  -c, --category-name   write names of selected categories\n\
+  -k, --keyword-name    write names of selected keywords\n\
+\n\
+      --dump            dump C code describing the current locale\n\
+                        (this code can be used in the C library)\n\
+"), program_invocation_name);
+
+  exit (status);
+}
+
+
+/* Write the names of all available locales to stdout.  */
+static void
+write_locales (void)
+{
+  DIR *dir;
+  struct dirent *dirent;
+
+  /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
+  puts ("POSIX");
+
+  dir = opendir (LOCALE_PATH);
+  if (dir == NULL)
+    {
+      error (1, errno, gettext ("cannot read locale directory `%s'"),
+            LOCALE_PATH);
+      return;
+    }
+
+  /* Now we can look for all files in the directory.  */
+  while ((dirent = readdir (dir)) != NULL)
+    if (strcmp (dirent->d_name, ".") != 0
+       && strcmp (dirent->d_name, "..") != 0)
+      puts (dirent->d_name);
+
+  closedir (dir);
+}
+
+
+/* Write the names of all available character maps to stdout.  */
+static void
+write_charmaps (void)
+{
+  DIR *dir;
+  struct dirent *dirent;
+
+  dir = opendir (CHARMAP_PATH);
+  if (dir == NULL)
+    {
+      error (1, errno, gettext ("cannot read character map directory `%s'"),
+            CHARMAP_PATH);
+      return;
+    }
+
+  /* Now we can look for all files in the directory.  */
+  while ((dirent = readdir (dir)) != NULL)
+    if (strcmp (dirent->d_name, ".") != 0
+       && strcmp (dirent->d_name, "..") != 0)
+      puts (dirent->d_name);
+
+  closedir (dir);
+}
+
+
+/* We have to show the contents of the environments determining the
+   locale.  */
+static void
+show_locale_vars (void)
+{
+  size_t cat_no;
+  const char *lcall = getenv ("LC_ALL");
+  const char *lang = getenv ("LANG") ? : "POSIX";
+
+  void get_source (const char *name)
+    {
+      char *val = getenv (name);
+
+      if (lcall != NULL || val == NULL)
+       printf ("%s=\"%s\"\n", name, lcall ? : lang);
+      else
+       printf ("%s=%s\n", name, val);
+    }
+
+  /* LANG has to be the first value.  */
+  printf ("LANG=%s\n", lang);
+
+  /* Now all categories in an unspecified order.  */
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    get_source (category[cat_no].name);
+
+  /* The last is the LC_ALL value.  */
+  printf ("LC_ALL=%s\n", lcall ? : "");
+}
+
+
+/* Show the information request for NAME.  */
+static void
+show_info (const char *name)
+{
+  size_t cat_no;
+
+  void print_item (struct cat_item *item)
+    {
+      if (show_keyword_name != 0)
+       printf ("%s=", item->name);
+
+      switch (item->value_type)
+       {
+       case string:
+         printf ("%s%s%s", show_keyword_name ? "\"" : "",
+                 nl_langinfo (item->item_id) ? : "",
+                 show_keyword_name ? "\"" : "");
+         break;
+       case stringarray:
+         {
+           int cnt;
+           const char *val;
+
+           if (show_keyword_name)
+             putchar ('"');
+
+           for (cnt = 0; cnt < item->max - 1; ++cnt)
+             {
+               val = nl_langinfo (item->item_id + cnt);
+               printf ("%s;", val ? : "");
+             }
+
+           val = nl_langinfo (item->item_id + cnt);
+           printf ("%s", val ? : "");
+
+           if (show_keyword_name)
+             putchar ('"');
+         }
+         break;
+       case byte:
+         {
+           const char *val = nl_langinfo (item->item_id);
+
+           if (val != NULL)
+             printf ("%d", *val == CHAR_MAX ? -1 : *val);
+         }
+         break;
+       case bytearray:
+         {
+           const char *val = nl_langinfo (item->item_id);
+           int cnt = val ? strlen (val) : 0;
+
+           while (cnt > 1)
+             {
+               printf ("%d;", *val == CHAR_MAX ? -1 : *val);
+                --cnt;
+               ++val;
+             }
+
+           printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val);
+         }
+         break;
+       default:
+       }
+      putchar ('\n');
+    }
+
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    {
+      size_t item_no;
+
+      if (category[cat_no].outfct != NULL)
+       /* Categories which need special handling of the output are
+          not written.  This is especially for LC_CTYPE and LC_COLLATE.
+          It does not make sense to have this large number of cryptic
+          characters displayed.  */
+       continue;
+
+      if (strcmp (name, category[cat_no].name) == 0)
+       /* Print the whole category.  */
+       {
+         if (show_category_name != 0)
+           puts (category[cat_no].name);
+
+         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+           print_item (&category[cat_no].item_desc[item_no]);
+
+         return;
+       }
+      
+      for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+       if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
+         {
+           if (show_category_name != 0)
+             puts (category[cat_no].name);
+
+           print_item (&category[cat_no].item_desc[item_no]);
+           return;
+         }
+    }
+}
+
+
+static void
+dump_category (const char *name)
+{
+  char *locname;
+  size_t cat_no, item_no, nstrings;
+
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    if (strcmp (name, category[cat_no].name) == 0)
+      break;
+
+  if (cat_no >= NCATEGORIES)
+    return;
+
+  /* The NAME specifies a correct locale category.  */
+  if (category[cat_no].outfct != NULL)
+    {
+      category[cat_no].outfct ();
+      return;
+    }
+
+  locname = (getenv ("LC_ALL") ?: getenv (name) ?:
+            getenv ("LANG") ?: (char *) "POSIX");
+
+  /* Determine the number of strings in advance.  */
+  nstrings = 0;
+  for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+    switch (category[cat_no].item_desc[item_no].value_type)
+      {
+      case string:
+      case byte:
+      case bytearray:
+       ++nstrings;
+       break;
+      case stringarray:
+       nstrings += category[cat_no].item_desc[item_no].max;
+      default:
+      }
+
+  printf ("\nconst struct locale_data _nl_%s_%s =\n{\n"
+         "  NULL, 0, /* no file mapped */\n  %Zu,\n  {\n",
+         locname, name, nstrings);
+
+  for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+    switch (category[cat_no].item_desc[item_no].value_type)
+      {
+      case string:
+       {
+         const char *val = nl_langinfo (
+           category[cat_no].item_desc[item_no].item_id);
+
+         if (val != NULL)
+           printf ("    \"%s\",\n", val);
+         else
+           puts ("    NULL,");
+       }
+       break;
+      case stringarray:
+       {
+         const char *val;
+         int cnt;
+
+         for (cnt = 0; cnt < category[cat_no].item_desc[item_no].max; ++cnt)
+           {
+             val = nl_langinfo (
+               category[cat_no].item_desc[item_no].item_id + cnt);
+
+             if (val != NULL)
+               printf ("    \"%s\",\n", val);
+             else
+                puts ("    NULL,");
+           }
+       }
+       break;
+      case byte:
+       {
+         const char *val = nl_langinfo (
+           category[cat_no].item_desc[item_no].item_id);
+
+         if (val != NULL)
+           printf ("    \"\\%o\",\n",
+                   *(unsigned char *) val ? : UCHAR_MAX);
+         else
+           puts ("    NULL,");
+       }
+       break;
+      case bytearray:
+       {
+         const char *bytes = nl_langinfo (
+           category[cat_no].item_desc[item_no].item_id);
+
+         if (bytes != NULL)
+           {
+             fputs ("    \"", stdout);
+             if (*bytes != '\0')
+               do
+                 printf ("\\%o", *(unsigned char *) bytes++);
+               while (*bytes != '\0');
+             else
+               printf ("\\%o", UCHAR_MAX);
+
+             puts ("\",");
+           }
+         else
+           puts ("    NULL,");
+       }
+       break;
+      default:
+       break;
+      }
+
+  puts ("  }\n};");
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/localedef.c b/locale/localedef.c
new file mode 100644 (file)
index 0000000..c331e11
--- /dev/null
@@ -0,0 +1,261 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <getopt.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "localedef.h"
+
+/* The charmap file used.  If none given DEFAULT_CHARMAP is used.  */
+static char *charmap_file;
+
+/* If set output is always written, even when warning are given.  */
+static int force_output;
+
+/* The input file name.  */
+static char *input_file;
+
+/* Path leading to the destination directory for the produced files.  */
+char *output_path;
+
+/* If this is defined be POSIX conform.  */
+int posix_conformance;
+
+/* If not zero give a lot more messages.  */
+int verbose;
+
+/* Long options.  */
+static const struct option long_options[] =
+  {
+    { "charmap", required_argument, NULL, 'f' },
+    { "debug", no_argument, NULL, 'd' },
+    { "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 }
+  };
+
+
+/* This is defined in error-msg.h.  */
+extern int warning_cntr;
+
+
+/* Prototypes for local functions.  */
+static void usage (int status) __attribute__ ((noreturn));
+static int construct_output_path (const char *path);
+
+int
+main(int argc, char *argv[])
+{
+  int optchar;
+  int cannot_write;
+  int do_help = 0;
+  int do_version = 0;
+
+  /* Set initial values for global varaibles.  */
+  charmap_file = NULL;
+  force_output = 0;
+  input_file = 0;
+  posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
+  verbose = 0;
+
+  /* Set locale.  Do not set LC_ALL because the other categories must
+     not be affected (acccording to POSIX.2).  */
+  setlocale (LC_MESSAGES, "");
+  setlocale (LC_CTYPE, "");
+
+  /* Initialize the message catalog.  */
+  textdomain (PACKAGE);
+
+  while ((optchar = getopt_long (argc, argv, "cdf:hi:vV", long_options, NULL))
+        != EOF)
+    switch (optchar)
+      {
+      case '\0':
+       break;
+      case 'c':
+       force_output = 1;
+       break;
+      case 'f':
+       if (charmap_file != NULL)
+         error (0, 0, gettext ("\"%s %s\" overwrites old option \"%s\""),
+                "-f", optarg, charmap_file);
+       charmap_file = optarg;
+       break;
+      case 'h':
+       do_help = 1;
+       break;
+      case 'i':
+       if (input_file != NULL)
+         error (0, 0, gettext ("\"%s %s\" overwrites old option \"%s\""),
+                "-i", optarg, input_file);
+       input_file = optarg;
+       break;
+      case 'v':
+       verbose = 1;
+       break;
+      case 'V':
+       do_version = 1;
+       break;
+      default:
+       usage (4);
+       break;
+      }
+
+  /* POSIX.2 requires to be verbose about missing characters in the
+     character map.  */
+  verbose |= posix_conformance;
+
+  /* Version information is requested.  */
+  if (do_version)
+    {
+      fprintf (stderr, "GNU %s %s\n", PACKAGE, VERSION);
+      exit (EXIT_SUCCESS);
+    }
+
+  /* Help is requested.  */
+  if (do_help)
+    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 files cannot be written return a non-zero value.  */
+  cannot_write = construct_output_path (argv[optind]);
+
+  /* Now that the parameters are processed we have to reset the local
+     ctype locale.  (POSIX.2 4.35.5.2)  */
+  setlocale (LC_CTYPE, "POSIX");
+
+  /* Look whether the system really allows locale definitions.  */
+  if (sysconf (_SC_2_LOCALEDEF) < 0)
+    error (3, 0,
+          gettext ("warning: system does not define `_POSIX2_LOCALEDEF'"));
+
+  /* Process charmap file.  */
+  charmap_read (charmap_file);
+
+  /* Now read the locale file.  */
+  locfile_read (input_file);
+
+  /* Check all categories for consistency.  */
+  categories_check ();
+
+  /* We are now able to write the data files.  If warning were given we
+     do it only if it is explicitly requested (--force).  */
+  if (warning_cntr == 0 || force_output != 0)
+    if (cannot_write != 0)
+      error (0, 0, gettext ("cannot write output file `%s': %s"),
+            output_path, strerror (cannot_write));
+    else
+      categories_write ();
+  else
+    error (0, 0,
+          gettext ("no output file produced because warning were issued"));
+
+  exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit.  */
+static void
+usage(int status)
+{
+  if (status != EXIT_SUCCESS)
+    fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
+            program_invocation_name);
+  else
+    printf(gettext ("\
+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 have been issued\n\
+  -h, --help            display this help and exit\n\
+  -V, --version         output version information and exit\n\
+\n\
+  -i, --inputfile=FILE  source definitions are found in FILE\n\
+  -f, --charmap=FILE    symbolic character names defined in FILE\n\
+\n\
+  -v, --verbose         print more messages\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);
+
+  exit (status);
+}
+
+
+/* The parameter to localedef describes the output path.  If it does
+   contain a '/' character it is a relativ path.  Otherwise it names the
+   locale this definition is for.  */
+static int
+construct_output_path (const char *path)
+{
+  int result = 0;
+
+  if (strchr (path, '/') == NULL)
+    {
+      /* This is a system path.  */
+      int path_max_len = pathconf (LOCALE_PATH, _PC_PATH_MAX) + 1;
+      output_path = (char *) xmalloc (path_max_len);
+
+      snprintf (output_path, path_max_len, "%s/%s", LOCALE_PATH, path);
+    }
+  else
+    {
+      char *t;
+      /* This is a user path.  */
+      output_path = malloc (strlen (path) + 2);
+      t = stpcpy (output_path, path);
+      *t = '\0';
+    }
+
+  if (euidaccess (output_path, W_OK) == -1)
+    /* Perhaps the directory does not exist now.  Try to create it.  */
+    if (errno == ENOENT)
+      {
+       if (mkdir (output_path, 0777) == -1)
+         result = errno;
+      }
+    else
+      result = errno;
+
+  if (result == 0)
+    strcat (output_path, "/");
+
+  return result;
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/localedef.h b/locale/localedef.h
new file mode 100644 (file)
index 0000000..5958a9c
--- /dev/null
@@ -0,0 +1,196 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef _LOCALEDEF_H
+#define _LOCALEDEF_H 1
+
+#define __need_wchar_t
+#include <stddef.h>
+
+#include "config.h"
+
+#include "hash.h"
+
+
+/* Needed always.  */
+#define MAX(a, b) ({typeof (a) _a = (a); typeof (b) _b = (b);               \
+                    _a > _b ? _a : _b; })
+#define MIN(a, b) ({typeof (a) _a = (a); typeof (b) _b = (b);               \
+                    _a < _b ? _a : _b; })
+
+/* Determine number of elements in ARR.  */
+#define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
+
+/* I simply love these GCC features ... :) */
+#define NO_PAREN(arg, rest...) arg, ##rest
+
+
+/* The character set used in the locale is defined in a character map file.
+   The information of the file is stored in the following struct.  */
+struct charmap
+  {
+    char *filename;
+    char *codeset_name;
+    int mb_cur_min;
+    int mb_cur_max;
+    char escape_char;
+    char comment_char;
+    hash_table table;
+    int hash_size;
+    int hash_layers;
+  };
+
+/* Data structure for representing charmap database.  */
+extern struct charmap charmap_data;
+
+
+/* We can map the types of the entries into four categories.  */
+enum value_type { none, string, stringarray, byte, bytearray, integer };
+
+/* Definition of the data structure which represents a category and its
+   items.  */
+struct category
+{
+  int cat_id;
+  const char *name;
+  size_t number;
+  struct cat_item
+  {
+    int item_id;
+    const char *name;
+    enum { std, opt } status;
+    enum value_type value_type;
+    int min;
+    int max;
+  } *item_desc;
+  char **item_value;
+  void (*infct)(int);
+  void (*checkfct)(void);
+  int (*outfct)(void);
+  int filled;
+  char *copy_locale;
+};
+
+/* This a the structure which contains all information about all
+   categories.  */
+extern struct category category[];
+
+
+/* The function used to load the contents of a charmap file into the
+   the global variable `charmap_data'.  */
+void charmap_read (const char *filename);
+
+/* Find a character constant given by its name in the hash table.  */
+static inline wchar_t find_char (const char *name, size_t len)
+{
+  wchar_t retval;
+  if (find_entry (&charmap_data.table, name, len, (void **) &retval) != 0)
+    return retval;
+  else
+    return -1;
+}
+
+/* Path to the directory the output files are written in.  */
+extern char *output_path;
+
+/* If this is defined be POSIX conform.  */
+extern int posix_conformance;
+
+/* If not zero give a lot more messages.  */
+extern int verbose;
+
+/* This structure contains all informations about the status of of
+   reading the locale definition file.  */
+struct locfile_data
+  {
+    const char *filename;
+    char escape_char;
+    char comment_char;
+    size_t bufsize;
+    char *buf;
+    char *strbuf;
+    size_t buf_ptr;
+    int continue_line;
+    size_t returned_tokens;
+    size_t line_no;
+  };
+
+/* The status variable.  */
+extern struct locfile_data locfile_data;
+
+/* Open the locale definition file.  */
+void locfile_open (const char *fname);
+
+/* Return the next token from the locale definition file.  */
+int locfile_lex (char **token, int *token_len);
+/* Dito, but check for EOF.  */
+int xlocfile_lex (char **token, int *token_len);
+
+/* Ignore the rest of the line.  First TOKEN given if != 0.  Warn about
+   anything other than end of line if WARN_FLAG nonzero.  */
+void ignore_to_eol (int token, int warn_flag);
+
+/* Code a character with UTF-8 if the character map has multi-byte
+   characters.  */
+int char_to_utf (char *buf, int char_val);
+
+
+/* Read the locale defintion file FNAME and fill the appropriate
+   data structures.  */
+void locfile_read (const char *fname);
+
+/* Check categories for consistency.  */
+void categories_check (void);
+
+/* Write out the binary representation of the category data.  */
+void categories_write (void);
+
+
+/* Treat reading the LC_COLLATE definition.  */
+void collate_input (int token);
+
+/* Treat reading the LC_CTYPE definition.  */
+void ctype_input (int token);
+void ctype_check (void);
+int ctype_output (void);
+
+/* Treat reading the LC_MONETARY definition.  */
+void monetary_check (void);
+
+/* Treat reading the LC_MESSAGES definition.  */
+void messages_check (void);
+
+/* Treat reading the LC_NUMERIC definition.  */
+void numeric_check (void);
+
+
+/* Print an error message, possibly with NLS.  */
+void error (int status, int errnum, const char *format, ...)
+     __attribute__ ((format (printf, 3, 4)));
+
+/* Library functions.  */
+void *xmalloc (size_t n);
+void *xcalloc (size_t n, size_t s);
+void *xrealloc (void *p, size_t n);
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
+#endif /* _LOCALEDEF_H */
diff --git a/locale/locfile-lex.c b/locale/locfile-lex.c
new file mode 100644 (file)
index 0000000..20e4f0f
--- /dev/null
@@ -0,0 +1,533 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <ctype.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "localedef.h"
+#include "token.h"
+
+
+/* Include the hashing table for the keywords.  */
+const struct locale_keyword* in_word_set (register const char *str,
+                                          register int len);
+#include "keyword.h"
+
+
+/* Contains the status of reading the locale definition file.  */
+struct locfile_data locfile_data;
+
+/* This is a flag used while collation input.  This is the only place
+   where element names beside the ones defined in the character map are
+   allowed.  There we must not give error messages.  */
+int reject_new_char = 1;
+
+/* Prototypes for local functions.  */
+static int get_char (void);
+
+
+#define LD locfile_data
+
+/* Opens the locale definition file and initializes the status data structure
+   for following calls of `locfile_lex'.  */
+void
+locfile_open (const char *fname)
+{
+  if (fname == NULL)
+    /* We read from stdin.  */
+    LD.filename = "<stdin>";
+  else
+    {
+      if (freopen (fname, "r", stdin) == NULL)
+       error (4, 0, gettext ("input file `%s' not found"), fname);
+      LD.filename = fname;
+    }
+
+  /* Set default values.  */
+  LD.escape_char = '\\';
+  LD.comment_char = '#';
+
+  LD.bufsize = sysconf (_SC_LINE_MAX);
+  LD.buf = (char *) xmalloc (LD.bufsize);
+  LD.strbuf = (char *) xmalloc (LD.bufsize);
+
+  LD.buf_ptr = LD.returned_tokens = LD.line_no = 0;
+
+  /* Now sign that we want immediately read a line.  */
+  LD.continue_line = 1;
+  LD.buf[LD.buf_ptr] = '\0';
+}
+
+
+int
+xlocfile_lex (char **token, int *token_len)
+{
+  int retval = locfile_lex (token, token_len);
+
+  if (retval == 0)
+    /* I.e. end of file.  */
+    error (4, 0, gettext ("%s: unexpected end of file in locale defintion "
+                         "file"), locfile_data.filename);
+
+  return retval;
+}
+
+int
+locfile_lex (char **token, int *token_len)
+{
+  int start_again;
+  int retval = 0;
+
+  do
+    {
+      int start_ptr;
+
+      start_again = 0;
+
+      /* Read the next line.  Skip over empty lines and comments.  */
+      if ((LD.buf[LD.buf_ptr] == '\0' && LD.continue_line != 0)
+         || LD.buf_ptr >= LD.bufsize
+         || (posix_conformance == 0 && LD.buf[LD.buf_ptr] == LD.comment_char))
+       do
+         {
+           size_t linelen;
+
+           LD.buf_ptr = 0;
+
+           if (fgets (LD.buf, LD.bufsize, stdin) == NULL)
+             {
+               /* This makes subsequent calls also return EOF.  */
+               LD.buf[0] = '\0';
+               return 0;
+             }
+
+           /* Increment line number counter.  */
+           ++LD.line_no;
+
+           /* We now have to look whether this line is continued and
+              whether it at all fits into our buffer.  */
+           linelen = strlen (LD.buf);
+
+           if (linelen == LD.bufsize - 1)
+             /* The did not fit into the buffer.  */
+             error (2, 0, gettext ("%s:%Zd: line too long;  use "
+                                   "`getconf LINE_MAX' to get the maximum "
+                                   "line length"), LD.filename, LD.line_no);
+
+           /* Remove '\n' at end of line.  */
+           if (LD.buf[linelen - 1] == '\n')
+             LD.buf[--linelen] = '\0';
+
+           if (linelen > 0 && LD.buf[linelen - 1] == LD.escape_char)
+             {
+               LD.buf[--linelen] = '\0';
+               LD.continue_line = 1;
+             }
+           else
+             LD.continue_line = 0;
+
+           while (isspace (LD.buf[LD.buf_ptr]))
+             ++LD.buf_ptr;
+
+           /* We are not so restrictive and allow white spaces before
+              a comment.  */
+           if (posix_conformance == 0
+               && LD.buf[LD.buf_ptr] == LD.comment_char
+               && LD.buf_ptr != 0)
+             error (0, 0, gettext ("%s:%Zd: comment does not start in "
+                                   "column 1"), LD.filename, LD.line_no);
+         }
+       while (LD.buf[LD.buf_ptr] == '\0'
+              || LD.buf[LD.buf_ptr] == LD.comment_char);
+
+
+      /* Get information for return values.  */
+      *token = LD.buf + LD.buf_ptr;
+      start_ptr = LD.buf_ptr;
+
+      /* If no further character is in the line this is the end of a logical
+        line.  This information is needed in the parser.  */
+      if (LD.buf[LD.buf_ptr] == '\0')
+       {
+         LD.buf_ptr = LD.bufsize;
+         retval = TOK_ENDOFLINE;
+       }
+      else if (isalpha (LD.buf[LD.buf_ptr]))
+       /* The token is an identifier.  The POSIX standard does not say
+          what characters might be contained but offical POSIX locale
+          definition files contain beside alnum characters '_', '-' and
+          '+'.  */
+       {
+         const struct locale_keyword *kw;
+
+         do
+           ++LD.buf_ptr;
+         while (isalnum (LD.buf[LD.buf_ptr]) || LD.buf[LD.buf_ptr] == '_'
+                || LD.buf[LD.buf_ptr] == '-' || LD.buf[LD.buf_ptr] == '+');
+
+         /* Look in table of keywords.  */
+         kw = in_word_set (*token, LD.buf_ptr - start_ptr);
+         if (kw == NULL)
+           retval = TOK_IDENT;
+         else
+           {
+             if (kw->token_id == TOK_ESCAPE_CHAR
+                 || kw->token_id == TOK_COMMENT_CHAR)
+               /* `escape_char' and `comment_char' are keywords for the
+                  lexer.  Do not give them to the parser.  */
+               {
+                 start_again = 1;
+
+                 if (!isspace (LD.buf[LD.buf_ptr])
+                     || (posix_conformance && LD.returned_tokens > 0))
+                   error (0, 0, gettext ("%s:%Zd: syntax error in locale "
+                                         "definition file"),
+                          LD.filename, LD.line_no);
+
+                 do
+                   ++LD.buf_ptr;
+                 while (isspace (LD.buf[LD.buf_ptr]));
+
+                 kw->token_id == TOK_ESCAPE_CHAR
+                   ? LD.escape_char
+                   : LD.comment_char = LD.buf[LD.buf_ptr++];
+
+                 ignore_to_eol (0, posix_conformance);
+               }
+             else
+               /* It is one of the normal keywords.  */
+               retval = kw->token_id;
+           }
+
+         *token_len = LD.buf_ptr - start_ptr;
+       }
+      else if (LD.buf[LD.buf_ptr] == '"')
+       /* Read a string.  All symbolic character descriptions are expanded.
+          This has to be done in a local buffer because a simple symbolic
+          character like <A> may expand to upto 6 bytes.  */
+       {
+         char *last = LD.strbuf;
+
+         ++LD.buf_ptr;
+         while (LD.buf[LD.buf_ptr] != '"')
+           {
+             int pre = LD.buf_ptr;
+             int char_val = get_char (); /* token, token_len); */
+
+             if (char_val == 0)
+               {
+                 error (4, 0, gettext ("%s:%Zd: unterminated string at end "
+                                       "of line"), LD.filename, LD.line_no);
+                 /* NOTREACHED */
+               }
+
+             if (char_val > 0)
+               /* Unknown characters are simply not stored.  */
+               last += char_to_utf (last, char_val);
+             else
+               {
+                 char tmp[LD.buf_ptr - pre + 1];
+                 memcpy (tmp, &LD.buf[pre], LD.buf_ptr - pre);
+                 tmp[LD.buf_ptr - pre] = '\0';
+                 error (0, 0, gettext ("%s:%Zd: character `%s' not defined"),
+                        LD.filename, LD.line_no, tmp);
+               }
+           }
+         if (LD.buf[LD.buf_ptr] != '\0')
+           ++LD.buf_ptr;
+
+         *last = '\0';
+         *token = LD.strbuf;
+         *token_len = last  - LD.strbuf;
+         retval = TOK_STRING;
+       }
+      else if (LD.buf[LD.buf_ptr] == '.' && LD.buf[LD.buf_ptr + 1] == '.'
+              && LD.buf[LD.buf_ptr + 2] == '.')
+       {
+         LD.buf_ptr += 3;
+         retval = TOK_ELLIPSIS;
+       }
+      else if (LD.buf[LD.buf_ptr] == LD.escape_char)
+       {
+         char *endp;
+
+         ++LD.buf_ptr;
+         switch (LD.buf[LD.buf_ptr])
+           {
+           case 'x':
+             if (isdigit (LD.buf[++LD.buf_ptr]))
+               {
+                 retval = strtol (&LD.buf[LD.buf_ptr], &endp, 16);
+                 if (endp - (LD.buf + LD.buf_ptr) < 2 || retval > 255)
+                   retval = 'x';
+                 else
+                   LD.buf_ptr = endp - LD.buf;
+               }
+             else
+               retval = 'x';
+             break;
+           case 'd':
+             if (isdigit (LD.buf[++LD.buf_ptr]))
+               {
+                 retval = strtol (&LD.buf[LD.buf_ptr], &endp, 10);
+                 if (endp - (LD.buf + LD.buf_ptr) < 2 || retval > 255)
+                   retval = 'd';
+                 else
+                   LD.buf_ptr = endp - LD.buf;
+               }
+             else
+               retval = 'd';
+             break;
+           case '0'...'9':
+             retval = strtol (&LD.buf[LD.buf_ptr], &endp, 8);
+             if (endp - (LD.buf + LD.buf_ptr) < 2 || retval > 255)
+               retval = LD.buf[LD.buf_ptr++];
+             else
+               LD.buf_ptr = endp - LD.buf;
+             break;
+           case 'a':
+             retval = '\a';
+             ++LD.buf_ptr;
+             break;
+           case 'b':
+             retval = '\b';
+             ++LD.buf_ptr;
+             break;
+           case 'f':
+             retval = '\f';
+             ++LD.buf_ptr;
+             break;
+           case 'n':
+             retval = '\n';
+             ++LD.buf_ptr;
+             break;
+           case 'r':
+             retval = '\r';
+             ++LD.buf_ptr;
+             break;
+           case 't':
+             retval = '\t';
+             ++LD.buf_ptr;
+             break;
+           case 'v':
+             retval = '\v';
+             ++LD.buf_ptr;
+             break;
+           default:
+             retval = LD.buf[LD.buf_ptr++];
+             break;
+           }
+       }
+      else if (isdigit (LD.buf[LD.buf_ptr]))
+       {
+         char *endp;
+
+         *token_len = strtol (&LD.buf[LD.buf_ptr], &endp, 10);
+         LD.buf_ptr = endp - LD.buf;
+         retval = TOK_NUMBER;
+       }
+      else if (LD.buf[LD.buf_ptr] == '-' && LD.buf[LD.buf_ptr + 1] == '1')
+       {
+         LD.buf_ptr += 2;
+         retval = TOK_MINUS1;
+       }
+      else
+       {
+         int ch = get_char (); /* token, token_len); */
+         if (ch != -1)
+           {
+             *token_len = ch;
+             retval = TOK_CHAR;
+           }
+         else
+           retval = TOK_ILL_CHAR;
+       }
+
+      /* Ignore white space.  */
+      while (isspace (LD.buf[LD.buf_ptr]))
+       ++LD.buf_ptr;
+    }
+  while (start_again != 0);
+
+  ++LD.returned_tokens;
+  return retval;
+}
+
+
+/* Code a character with UTF-8 if the character map has multi-byte
+   characters.  */
+int
+char_to_utf (char *buf, int char_val)
+{
+  if (charmap_data.mb_cur_max == 1)
+    {
+      *buf++ = char_val;
+      return 1;
+    }
+  else
+    {
+/* The number of bits coded in each character.  */
+#define CBPC 6
+      static struct coding_tab
+        {
+          int mask;
+          int val;
+        }
+      tab[] =
+        {
+          { 0x7f,       0x00 },
+          { 0x7ff,      0xc0 },
+          { 0xffff,     0xe0 },
+          { 0x1fffff,   0xf0 },
+          { 0x3ffffff,  0xf8 },
+          { 0x7fffffff, 0xfc },
+          { 0, }
+        };
+      struct coding_tab *t;
+      int c;
+      int cnt = 1;
+
+      for (t = tab; char_val > t->mask; ++t, ++cnt)
+       ;
+
+      c = cnt;
+
+      buf += cnt;
+      while (c > 1)
+       {
+         *--buf = 0x80 | (char_val & ((1 << CBPC) - 1));
+         char_val >>= CBPC;
+         --c;
+       }
+
+      *--buf = t->val | char_val;
+
+      return cnt;
+    }
+}
+
+
+/* Ignore rest of line upto ENDOFLINE token, starting with given token.
+   If WARN_FLAG is set warn about any token but ENDOFLINE.  */
+void
+ignore_to_eol (int token, int warn_flag)
+{
+  if (token == TOK_ENDOFLINE)
+    return;
+
+  if (LD.buf[LD.buf_ptr] != '\0' && warn_flag)
+    error (0, 0, gettext ("%s:%Zd: trailing garbage at end of line"),
+          locfile_data.filename, locfile_data.line_no);
+
+  while (LD.continue_line)
+    {
+      LD.continue_line = 0;
+
+      /* Increment line number counter.  */
+      ++LD.line_no;
+
+      if (fgets (LD.buf, LD.bufsize, stdin) != NULL)
+       {
+         /* We now have to look whether this line is continued and
+            whether it at all fits into our buffer.  */
+         int linelen = strlen (LD.buf);
+
+         if (linelen == LD.bufsize - 1)
+           /* The did not fit into the buffer.  */
+           error (2, 0, gettext ("%s:%Zd: line too long;  use `getconf "
+                                 "LINE_MAX' to get the current maximum "
+                                 "line length"), LD.filename, LD.line_no);
+
+         /* Remove '\n' at end of line.  */
+         if (LD.buf[linelen - 1] == '\n')
+           --linelen;
+
+         if (LD.buf[linelen - 1] == LD.escape_char)
+           LD.continue_line = 1;
+       }
+    }
+  /* This causes to begin the next line.  */
+  LD.buf_ptr = LD.bufsize;
+}
+
+
+/* Return the value of the character at the beginning of the input buffer.
+   Symbolic character constants are expanded.  */
+static int
+get_char (void)
+{
+  if (LD.buf[LD.buf_ptr] == '<')
+    /* This is a symbolic character name.  */
+    {
+      int char_val;
+      char *startp = LD.buf + (++LD.buf_ptr);
+      char *endp = startp;
+
+      while (LD.buf[LD.buf_ptr] != '>' && isprint (LD.buf[LD.buf_ptr]))
+       {
+         if (LD.buf[LD.buf_ptr] == '\0'
+             || (LD.buf[LD.buf_ptr] == LD.escape_char
+                 && LD.buf[++LD.buf_ptr] == '\0'))
+           break;
+
+         *endp++ = LD.buf[LD.buf_ptr++];
+       }
+
+      if (LD.buf[LD.buf_ptr] != '>' && LD.buf[LD.buf_ptr] == '\0')
+       {
+         error (0, 0, gettext ("%s:%Zd: end of line in character symbol"),
+                LD.filename, LD.line_no);
+
+         if (startp == endp)
+           return -1;
+       }
+      else
+       ++LD.buf_ptr;
+
+      char_val = find_char (startp, endp - startp);
+      if (char_val == -1 && verbose != 0 && reject_new_char != 0)
+       {
+         /* Locale defintions are often given very general.  Missing
+            characters are only reported when explicitely requested.  */
+         char tmp[endp - startp + 3];
+
+         tmp[0] = '<';
+         memcpy (tmp + 1, startp, endp - startp);
+         tmp[endp - startp + 1] = '>';
+         tmp[endp - startp + 2] = '\0';
+
+         error (0, 0, gettext ("%s:%Zd: character `%s' not defined"),
+                LD.filename, LD.line_no, tmp);
+       }
+      
+      return char_val;
+    }
+  else
+    return (int) LD.buf[LD.buf_ptr++];
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/locfile-parse.c b/locale/locfile-parse.c
new file mode 100644 (file)
index 0000000..15e25c7
--- /dev/null
@@ -0,0 +1,820 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <limits.h>
+#include <obstack.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include "localedef.h"
+#include "localeinfo.h"
+#include "token.h"
+
+/* We don't have these constants defined because we don't use them.  Give
+   default values.  */
+#define CTYPE_MB_CUR_MIN 0
+#define CTYPE_MB_CUR_MAX 0
+#define CTYPE_HASH_SIZE 0
+#define CTYPE_HASH_LAYERS 0
+#define CTYPE_CLASS 0
+#define CTYPE_TOUPPER_EB 0
+#define CTYPE_TOLOWER_EB 0
+#define CTYPE_TOUPPER_EL 0
+#define CTYPE_TOLOWER_EL 0
+
+/* We have all categories defined in `categories.def'.  Now construct
+   the description and data structure used for all categories.  */
+#define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
+    struct cat_item category##_desc[] =                                              \
+      {                                                                              \
+        NO_PAREN items                                                       \
+      };                                                                     \
+                                                                             \
+    char *category##_values[NELEMS (category##_desc) - 1] = { NULL, };
+#include "categories.def"
+#undef DEFINE_CATEGORY
+
+struct category category[] =
+  {
+#define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
+    [category] = { _NL_NUM_##category, name, NELEMS (category##_desc) - 1,    \
+                   category##_desc, category##_values, in, check, out },
+#include "categories.def"
+#undef DEFINE_CATEGORY
+  };
+#define NCATEGORIES NELEMS (category)
+
+
+#define SYNTAX_ERROR                                                         \
+    error (0, 0, gettext ("%s:%Zd: syntax error in locale definition file"),  \
+          locfile_data.filename, locfile_data.line_no)
+
+
+/* Prototypes for local functions.  */
+static int get_byte (char *byte_ptr);
+static char *is_locale_name (int cat_no, const char *str, int len);
+
+
+/* Read a locale definition file FILE.  The format is defined in
+   POSIX.2 2.5.3.  */
+void
+locfile_read (const char *fname)
+{
+  /* Pointer to text of last token.  */
+  char *ptr;
+  /* Length of last token (or if NUMBER the value itself).  */
+  int len;
+  /* The last returned token.  */
+  int token;
+  /* For error correction we remember whether the last token was correct.  */
+  int correct_token = 1;
+
+  /* Open the desired input file on stdin.  */
+  locfile_open (fname);
+
+  while ((token = locfile_lex (&ptr, &len)) != 0)
+    {
+      int cat_no;
+
+      for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+       if (token == category[cat_no].cat_id)
+         break;
+
+      if (cat_no >= NCATEGORIES)
+       /* A syntax error occured.  No valid category defintion starts.  */
+       {
+         if (correct_token != 0)
+           error (0, 0, gettext ("%s:%Zd: locale category start expected"),
+                  locfile_data.filename, locfile_data.line_no);
+
+         /* To prevent following errors mark as error case.  */
+         correct_token = 0;
+
+         /* Synchronization point is the beginning of a new category.
+            Overread all line upto this silently.  */
+         ignore_to_eol (0, 0);
+         continue;
+       }
+
+      /* Rest of the line should be empty.  */
+      ignore_to_eol (0, 1);
+
+      /* Perhaps these category is already specified.  We simply give a
+         warning and overwrite the values.  */
+      if (category[cat_no].filled != 0)
+       error (0, 0, gettext ("%s:%Zd: multiple definition of locale "
+                             "category %s"), locfile_data.filename,
+              locfile_data.line_no, category[cat_no].name);
+
+      /* We read the first token because this could be the copy statement.  */
+      token = xlocfile_lex (&ptr, &len);
+
+      if (token == TOK_COPY)
+       /* Copying the definitions from an existing locale is requested.  */
+       {
+         char *str;
+
+         /* Get the name of the locale to copy from.  */
+         token = xlocfile_lex (&ptr, &len);
+         if (token != TOK_IDENT && token != TOK_STRING)
+           /* No name, then mark error and ignore everything upto next
+              start of an category section.  */
+           {
+             /* To prevent following errors mark as error case.  */
+             correct_token = 0;
+
+             /* Synchronization point is the beginning of a new category.
+                Overread all line upto this silently.  */
+             ignore_to_eol (0, 0);
+           }
+         else if ((str = is_locale_name (cat_no, ptr, len)) != NULL)
+           /* Yes the name really names an existing locale file.  We are
+              returned the complete file name.  Store it so that we can
+              copy it in the output phase.  */
+           {
+             category[cat_no].copy_locale = str;
+             category[cat_no].filled = 1;
+             
+             ignore_to_eol (0, 1);
+           }
+         else
+           /* No, the name does not address a valid locale file.  Mark
+              error case and ignore rest of category.  */
+           {
+             char tmp[len + 1];
+             memcpy (tmp, ptr, len);
+             tmp[len] = '\0';
+             error (0, 0, gettext ("%s:%Zd: invalid locale `%s' in copy "
+                                   "statement"), locfile_data.filename,
+                        locfile_data.line_no, tmp);
+             correct_token = 0;
+             ignore_to_eol (0, 0);
+           }
+
+         /* This should END as the next token.  */
+         token = xlocfile_lex (&ptr, &len);
+
+         if (token == TOK_END)
+           /* This is the end of the category.  */
+           {
+             token = xlocfile_lex (&ptr, &len);
+
+             if (token != category[cat_no].cat_id)
+               /* Wrong category name after END.  */
+               {
+                 error (0, 0, gettext ("%s:%Zd: category `%s' does not "
+                                       "end with `END %s'"),
+                        locfile_data.filename, locfile_data.line_no,
+                        category[cat_no].name, category[cat_no].name);
+                 ignore_to_eol (0, 0);
+               }
+             else
+               ignore_to_eol (0, 1);
+
+             correct_token = 1;
+           }
+         else
+           /* No END following copy.  Give error while not in error case.  */
+           {
+             if (correct_token != 0)
+               error (0, 0, gettext ("%s:%Zd: `copy' must be sole rule"),
+                      locfile_data.filename, locfile_data.line_no);
+             correct_token = 0;
+             ignore_to_eol (0, 0);
+           }
+
+         continue;
+       }
+
+      /* Now it's time to mark as mentioned in the locale file.  */
+      category[cat_no].filled = 1;
+
+      if (category[cat_no].infct != NULL)
+       /* The category needs a special input handling.  */
+       {
+         category[cat_no].infct(token);
+         continue;
+       }
+
+      /* Now process the given items.  */
+      while (1)
+       {
+         int item_no;
+
+         if (token == TOK_END)
+           /* This is the end of the category.  */
+           {
+             token = xlocfile_lex (&ptr, &len);
+
+             if (token != category[cat_no].cat_id)
+               {
+                 error (0, 0, gettext ("%s:%Zd: category `%s' does not end "
+                                       "with `END %s'"),
+                        locfile_data.filename, locfile_data.line_no,
+                        category[cat_no].name, category[cat_no].name);
+                 ignore_to_eol (0, 0);
+               }
+             else
+               ignore_to_eol (0, 1);
+
+             /* Start next category.  */
+             break;
+           }
+
+         /* All other lines should describe valid items of the category.  */
+         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+           if (category[cat_no].item_desc[item_no].item_id == token)
+             break;
+
+         if (item_no >= category[cat_no].number)
+           /* This is not a valid item of the category.  */
+           {
+             SYNTAX_ERROR;
+             ignore_to_eol (0, 0);
+
+             token = xlocfile_lex (&ptr, &len);
+
+             /* And process next item.  */
+             continue;
+           }
+
+         /* Test whether already a value is defined.  */
+         if (category[cat_no].item_value[item_no] != NULL)
+           error (0, 0, gettext ("%s:%Zd: category item `%s' already "
+                                 "defined"),
+                  locfile_data.filename, locfile_data.line_no,
+                  category[cat_no].item_desc[item_no].name);
+
+         switch (category[cat_no].item_desc[item_no].value_type)
+           {
+           case string:
+             /* Get next token.  This is the argument to the item.  */
+             token = xlocfile_lex (&ptr, &len);
+
+             if (token != TOK_STRING)
+               SYNTAX_ERROR;
+             else
+               category[cat_no].item_value[item_no] = strdup (ptr);
+             ignore_to_eol (0, ptr != NULL);
+             break;
+           case stringarray:
+             /* This is a difficult case.  The number of strings in
+                the array may vary.  But for now its only necessary
+                with ALT_DIGITS from LC_TIME.  This item is the last
+                so be can solve it by storing the number of string in
+                the first place and the string indeces following
+                that.  */
+             {
+               int cnt;
+               char **buffer;
+               if (category[cat_no].item_value[item_no] != NULL)
+                 buffer = (char **) category[cat_no].item_value[item_no];
+               else
+                 buffer = (char **) xmalloc (
+                   sizeof (char *) * category[cat_no].item_desc[item_no].max);
+
+               category[cat_no].item_value[item_no] = (char *) buffer;
+
+               /* As explained we may need a place to store the real number
+                  of strings.  */
+               if (category[cat_no].item_desc[item_no].min
+                   != category[cat_no].item_desc[item_no].max)
+                 ++buffer;
+
+               cnt = 0;
+               do
+                 {
+                   token = xlocfile_lex (&ptr, &len);
+                   if (token != TOK_STRING)
+                     {
+                       SYNTAX_ERROR;
+                       break;
+                     }
+
+                   if (cnt >= category[cat_no].item_desc[item_no].max)
+                     {
+                       error (0, 0, gettext ("%s:%Zd: too many elements "
+                                             "for item `%s`"),
+                              locfile_data.filename, locfile_data.line_no,
+                              category[cat_no].item_desc[item_no].name);
+                       break;
+                     }
+
+                   buffer[cnt++] = strdup (ptr);
+
+                   token = locfile_lex (&ptr, &len);
+                 }
+               while (token == TOK_CHAR && len == ';');
+
+               ignore_to_eol (token, ptr != NULL);
+
+               if (cnt < category[cat_no].item_desc[item_no].min)
+                 error (0, 0, gettext ("%s:%Zd: too few elements for item "
+                                       "`%s'"),
+                        locfile_data.filename, locfile_data.line_no,
+                        category[cat_no].item_desc[item_no].name);
+
+               if (category[cat_no].item_desc[item_no].min
+                   != category[cat_no].item_desc[item_no].max)
+                 *(int *) category[cat_no].item_value[item_no] = cnt;
+             }
+             break;
+           case byte:
+             {
+               int ok;
+               category[cat_no].item_value[item_no] = (char *) xmalloc (
+                 __alignof__ (char));
+               ok = get_byte (category[cat_no].item_value[item_no]);
+               ignore_to_eol (0, ok);
+             }
+             break;
+           case bytearray:
+             {
+               char *buffer;
+               int maxsize;
+               int cnt;
+               char byte;
+               int ok;
+
+               buffer = (char *) xmalloc ((maxsize = 30));
+               cnt = 0;
+
+               while ((ok = get_byte (&byte)))
+                 {
+                   if (cnt >= maxsize)
+                     buffer = (char *) xmalloc ((maxsize *= 2));
+
+                   buffer[cnt++] = byte;
+
+                   token = locfile_lex (&ptr, &len);
+                   if (token != TOK_CHAR || len != ';')
+                     break;
+                 }
+
+               buffer[cnt] = '\0';
+               category[cat_no].item_value[item_no] = buffer;
+               ignore_to_eol (token, ok);
+             }
+             break;
+           default:
+             error (5, 0, gettext ("internal error in %s, line %u"),
+                    __FUNCTION__, __LINE__);
+             /* NOTREACHED */
+           }
+
+         /* Get next token.  */
+         token = xlocfile_lex (&ptr, &len);
+       } /* while (1) */
+    }
+}
+
+
+/* Check given values for categories for consistency.  */
+void
+categories_check (void)
+{
+  int cat_no;
+
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    if (category[cat_no].copy_locale == NULL)
+      if (category[cat_no].filled != 0)
+       if (category[cat_no].checkfct)
+         category[cat_no].checkfct();
+       else
+         {
+           int item_no;
+
+           for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+             if (category[cat_no].item_value[item_no] == NULL)
+               {
+                 int errcode;
+
+                 /* If the item is defined in the standard is it an error to
+                    have it not defined.  */
+                 errcode = category[cat_no].item_desc[item_no].status == std
+                           ? 5 : 0;
+
+                 error (errcode, 0, gettext ("item `%s' of category `%s' "
+                                             "undefined"),
+                        category[cat_no].item_desc[item_no].name,
+                        category[cat_no].name);
+               }
+         }
+      else
+       error (0, 0, gettext ("category `%s' not defined"),
+              category[cat_no].name);
+}
+
+
+/* Write out the binary representation of the category data which can be
+   loaded by setlocale(1).  */
+void
+categories_write (void)
+{
+  struct locale_file
+  {
+      int magic;
+      int n;
+      int idx[0];
+  } *data;
+  struct obstack obstk;
+  int cat_no;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+  obstack_init (&obstk);
+
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    {
+      int result = 0;
+
+      if (category[cat_no].copy_locale != NULL)
+       /* Simply copy the addressed locale file of the specified
+          category.  Please note that this is tried before the distinction
+          between categories which need special handling is made.  */
+       {
+         int source;
+
+         /* Open source file.  */
+         source = open (category[cat_no].copy_locale, O_RDONLY);
+         if (source < 0)
+           error (0, 0, gettext ("cannot copy locale definition file `%s'"),
+                  category[cat_no].copy_locale);
+         else
+           {
+             /* Construct file name of output file and open for writing.  */
+             char path[strlen (output_path)
+                        + strlen(category[cat_no].name) + 1];
+             int dest;
+             char *t;
+
+             t = stpcpy (path, output_path);
+             strcpy (t, category[cat_no].name);
+
+             dest = creat (path, 0666);
+             if (dest == -1)
+               error (0, 0, gettext ("cannot open output file `%s': %m"),
+                      path);
+             else
+               {
+                 char buffer[BUFSIZ];
+                 int size;
+                 
+                 /* Copy the files.  */
+                 do
+                   {
+                     size = read (source, buffer, BUFSIZ);
+                     write (dest, buffer, size);
+                   }
+                 while (size > 0);
+
+                 close (dest);
+
+                 /* Show success.  */
+                 puts (category[cat_no].name);
+               }
+             close (source);
+           }
+
+         /* Next category.   */
+         continue;
+       }
+
+      if (category[cat_no].outfct)
+       result = category[cat_no].outfct();
+      else
+       {
+         char *path, *t;
+         int fd;
+         struct iovec *iov;
+         int item_no, len, slen, cnt;
+         int elems = 0;
+
+         /* Count number of elements.  */
+         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+           {
+             switch (category[cat_no].item_desc[item_no].value_type)
+               {
+               case string:
+               case byte:
+               case bytearray:
+                 ++elems;
+                 break;
+               case stringarray:
+                 elems += category[cat_no].item_desc[item_no].max;
+                 break;
+               default:
+                 error (5, 0, gettext ("internal error in %s, line %u"),
+                        __FUNCTION__, __LINE__);
+                 /* NOTREACHED */
+               }
+           }
+
+         /* We now have the number of elements.  We build the structure
+            and a helper structure for writing all out.  */
+         len = sizeof (struct locale_file) + elems * sizeof (int);
+         data = obstack_alloc (&obstk, len);
+         iov = obstack_alloc (&obstk, (elems + 1) * sizeof (struct iovec));
+
+         data->magic = LIMAGIC (cat_no);
+         data->n = elems;
+         iov[0].iov_base = data;
+         iov[0].iov_len = len;
+
+         cnt = 0;
+         for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+           if (category[cat_no].item_value[item_no] == NULL)
+             {
+               switch (category[cat_no].item_desc[item_no].value_type)
+                 {
+                 case string:
+                 case byte:
+                 case bytearray:
+                   data->idx[cnt] = len;
+                   ++len;  /* We reserve one single byte for this entry.  */
+                   iov[1 + cnt].iov_base = (char *) "";
+                   iov[1 + cnt].iov_len = 1;
+                   ++cnt;
+                   break;
+                 case stringarray:
+                   {
+                     int max;
+                     int nstr;
+
+                     max = category[cat_no].item_desc[item_no].max;
+
+                     for (nstr = 0; nstr < max; ++nstr)
+                       {
+                         data->idx[cnt] = len;
+                         ++len;
+                         iov[1 + cnt].iov_base = (char *) "";
+                         iov[1 + cnt].iov_len = 1;
+                         ++cnt;
+                       }
+                   }
+                 }
+             }
+           else
+             switch (category[cat_no].item_desc[item_no].value_type)
+               {
+               case string:
+               case bytearray:
+                 data->idx[cnt] = len;
+                 slen = strlen (category[cat_no].item_value[item_no]) + 1;
+                 len += slen;
+                 iov[1 + cnt].iov_base = category[cat_no].item_value[item_no];
+                 iov[1 + cnt].iov_len = slen;
+                 ++cnt;
+                 break;
+               case byte:
+                 data->idx[cnt] = len;
+                 slen = 1;
+                 len += slen;
+                 iov[1 + cnt].iov_base = category[cat_no].item_value[item_no];
+                 iov[1 + cnt].iov_len = slen;
+                 ++cnt;
+                 break;
+               case stringarray:
+                 {
+                   int nstr, nact;
+                   char **first;
+                 
+                   if (category[cat_no].item_desc[item_no].min
+                       == category[cat_no].item_desc[item_no].max)
+                     {
+                       nstr = category[cat_no].item_desc[item_no].min;
+                       first = (char **) category[cat_no].item_value[item_no];
+                     }
+                   else
+                     {
+                       nstr = *(int *) category[cat_no].item_value[item_no];
+                       first =
+                         ((char **) category[cat_no].item_value[item_no]) + 1;
+                     }
+                   nact = nstr;
+                   while (nstr > 0)
+                     {
+                       data->idx[cnt] = len;
+                       if (*first != NULL)
+                         {
+                           slen = strlen (*first) + 1;
+                           iov[1 + cnt].iov_base = first;
+                         }
+                       else
+                         {
+                           slen = 1;
+                           iov[1 + cnt].iov_base = (char *) "";
+                         }
+                       len += slen;
+                       iov[1 + cnt].iov_len = slen;
+                       ++cnt;
+                       ++first;
+                       --nstr;
+                     }
+                   while (nact < category[cat_no].item_desc[item_no].max)
+                     {
+                       data->idx[cnt] = len;
+                       len += 1;
+                       iov[1 + cnt].iov_base = (char *) "";
+                       iov[1 + cnt].iov_len = 1;
+                       ++cnt;
+                       ++nact;
+                     }
+                 }
+                 break;
+               default:
+                 /* Cannot happen.  */
+                 break;
+               }
+         assert (cnt <= elems);
+
+         /* Construct the output filename from the argument given to
+            localedef on the command line.  */
+         path = (char *) obstack_alloc (&obstk, strlen (output_path) +
+                                        strlen (category[cat_no].name) + 1);
+         t = stpcpy (path, output_path);
+         strcpy (t, category[cat_no].name);
+
+         fd = creat (path, 0666);
+         if (fd == -1)
+           {
+             error (0, 0, gettext ("cannot open output file `%s': %m"), path);
+             result = 1;
+           }
+         else
+           {
+             if (writev (fd, iov, cnt + 1) == -1)
+               {
+                 error (0, 0, gettext ("cannot write output file `%s': %m"),
+                        path);
+                 result = 1;
+               }
+
+if (elems==0) write(fd, &elems, 1);
+
+             close (fd);
+           }
+         /* The old data is not needed anymore, but keep the obstack
+            intact.  */
+         obstack_free (&obstk, data);
+       }
+
+      if (result == 0)
+       puts (category[cat_no].name);
+    }
+  /* Now the whole obstack can be removed.  */
+  obstack_free (&obstk, NULL);
+}
+
+
+/* Get the representation of a number.  This is a positive integer or
+   the number -1 which is handled as a special symbol by the scanner.  */
+static int
+get_byte (char *byte_ptr)
+{
+  int token;
+  char *ptr;
+  int len;
+
+  token = locfile_lex (&ptr, &len);
+  if (token != TOK_NUMBER && token != TOK_MINUS1)
+    /* None of the valid number format.  */
+    {
+      error (0, 0, gettext ("%s:%Zd: number expected"),
+            locfile_data.filename, locfile_data.line_no);
+      *byte_ptr = 0;
+      return 0;
+    }
+
+  if (token == TOK_MINUS1)
+    {
+      *byte_ptr = CHAR_MAX;
+      return 1;
+    }
+
+  if (len > CHAR_MAX)
+    /* The value of the numbers has to be less than CHAR_MAX.  This is
+       ok for the information they have to express.  */
+    {
+      error (0, 0, gettext ("%s:%Zd: invalid number"),
+            locfile_data.filename, locfile_data.line_no);
+      *byte_ptr = 0;
+      return 0;
+    }
+
+  *byte_ptr = len;
+  return 1;
+}
+
+
+/* Test whether the string STR with length LEN is the name of an existing
+   locale and whether a file for category CAT_NO is found in this directory.
+   This categories are looked for in the system locale definition file
+   directory.
+   Return the complete file name for the category file.  */
+static char *
+is_locale_name (int cat_no, const char *str, int len)
+{
+  static char **locale_names = NULL;
+  static int max_count = 0;
+  static int locale_count = 0;
+  int cnt, exist, fd;
+  char *fname;
+  struct stat st;
+
+  if (locale_names == NULL)
+    /* Read in the list of all available locales.  */
+    {
+      DIR *dir;
+      struct dirent *dirent;
+      
+      /* LOCALE_NAMES is not NULL anymore, but LOCALE_COUNT == 0.  */
+      ++locale_names;
+         
+      dir = opendir (LOCALE_PATH);
+      if (dir == NULL)
+       {
+         error (1, errno, gettext ("cannot read locale directory `%s'"),
+                LOCALE_PATH);
+
+         return NULL;
+       }
+
+      /* Now we can look for all files in the directory.  */
+      while ((dirent = readdir (dir)) != NULL)
+       if (strcmp (dirent->d_name, ".") != 0
+           && strcmp (dirent->d_name, "..") != 0)
+         {
+           if (max_count == 0)
+             locale_names = (char **) xmalloc ((max_count = 10)
+                                               * sizeof (char *));
+           else
+             locale_names = (char **) xrealloc (locale_names,
+                                                (max_count *= 2)
+                                                * sizeof (char *));
+           locale_names[locale_count++] = strdup (dirent->d_name);
+         }
+      closedir (dir);
+    }
+
+  for (cnt = 0; cnt < locale_count; ++cnt)
+    if (strncmp (str, locale_names[cnt], len) == 0
+       && locale_names[cnt][len] == '\0')
+      break;
+  
+  if (cnt >= locale_count)
+    return NULL;
+  
+  /* Now search for this specific locale file.  */
+  asprintf (&fname, "%s/%s/%s", LOCALE_PATH, locale_names[cnt],
+           category[cat_no].name);
+  
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    {
+      free (fname);
+      return NULL;
+    }
+  
+  exist = fstat (fd, &st);
+  close (fd);
+  
+  if (exist < 0)
+    {
+      free (fname);
+      return NULL;
+    }
+
+  return fname;
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/messages.c b/locale/messages.c
new file mode 100644 (file)
index 0000000..842e5ef
--- /dev/null
@@ -0,0 +1,76 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <langinfo.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <regex.h>
+
+#include "localedef.h"
+
+/* These are defined in locfile-parse.c.   */
+extern struct cat_item LC_MESSAGES_desc[];
+extern char *LC_MESSAGES_values[];
+
+void
+messages_check(void)
+{
+  int item_no;
+
+  /* First general check for existence.  */
+  for (item_no = 0; item_no < category[LC_MESSAGES].number; ++item_no)
+    if (LC_MESSAGES_values[item_no] == NULL)
+      {
+       int errcode;
+
+       errcode = LC_MESSAGES_desc[item_no].status == std ? 5 : 0;
+
+       error (errcode, 0, gettext ("item `%s' of category `%s' undefined"),
+              LC_MESSAGES_desc[item_no].name, "LC_MESSAGES");
+      }
+    else
+      {
+       /* Some fields need special tests.  */
+       if (LC_MESSAGES_desc[item_no].item_id == YESEXPR
+           || LC_MESSAGES_desc[item_no].item_id == NOEXPR)
+         /* The expression has to be a POSIX extended regular expression.  */
+         {
+           regex_t re;
+           int result;
+
+           result = regcomp (&re, LC_MESSAGES_values[item_no], REG_EXTENDED);
+
+           if (result != 0)
+             {
+               char errbuf[BUFSIZ];
+
+               (void) regerror (result, &re, errbuf, BUFSIZ);
+               error (0, 0, gettext ("no correct regular expression for "
+                                     "item `%s' in category `%s': %s"),
+                      LC_MESSAGES_desc[item_no].name, "LC_MESSAGES", errbuf);
+             }
+         }
+      }
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/monetary.c b/locale/monetary.c
new file mode 100644 (file)
index 0000000..2683eb2
--- /dev/null
@@ -0,0 +1,132 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <langinfo.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "localedef.h"
+#include "token.h"
+
+
+/* The content iof the field int_curr_symbol has to be taken from
+   ISO-4217.  We test for correct values.  */
+#define DEFINE_INT_CURR(str) str,
+static const char *const valid_int_curr[] =
+  {
+#   include "iso-4217.def"
+  };
+#define NVALID_INT_CURR ((sizeof (valid_int_curr) \
+                        / sizeof (valid_int_curr[0])))
+#undef DEFINE_INT_CURR
+
+
+/* These are defined in locfile-parse.c.  */
+extern struct cat_item LC_MONETARY_desc[];
+extern char *LC_MONETARY_values[];
+
+static int _curr_strcmp(const char *s1, const char **s2);
+
+
+
+void
+monetary_check(void)
+{
+  int item_no, val;
+
+  for (item_no = 0; LC_MONETARY_desc[item_no].item_id != 0; ++item_no)
+    /* Test whether the entry has been defined.  Byte values are simply
+       stored.  */
+    if (LC_MONETARY_values[item_no] == NULL)
+      {
+       int errcode;
+
+       errcode = LC_MONETARY_desc[item_no].status = std ? 5 : 0;
+
+       error (errcode, 0, gettext ("item `%s' of category `%s' undefined"),
+              LC_MONETARY_desc[item_no].name, "LC_MONETARY");
+      }
+    else
+      switch (LC_MONETARY_desc[item_no].item_id)
+       {
+       case INT_CURR_SYMBOL:
+         if (strlen (LC_MONETARY_values[item_no]) != 4)
+           error (0, 0,
+                  gettext ("item `%s' of category `%s' has wrong length"),
+                  LC_MONETARY_desc[item_no].name, "LC_MONETARY");
+         else if (bsearch (LC_MONETARY_values[item_no], valid_int_curr,
+                      NVALID_INT_CURR, sizeof (char *),
+                      (comparison_fn_t) _curr_strcmp) == NULL)
+           error (0, 0, gettext ("item `%s' does not correspond to any "
+                                 "valid name in ISO-4217"),
+                  LC_MONETARY_desc[item_no].name);
+         break;
+       case P_CS_PRECEDES:
+       case P_SEP_BY_SPACE:
+       case N_CS_PRECEDES:
+       case N_SEP_BY_SPACE:
+       case  P_SIGN_POSN:
+       case N_SIGN_POSN:
+         val = (int) *(char *) LC_MONETARY_values[item_no];
+         if (val < LC_MONETARY_desc[item_no].min
+             || val > LC_MONETARY_desc[item_no].max)
+           error (0, 0, gettext ("value for item `%s' in category `%s' "
+                                 "must be in range %d...%d"),
+                  LC_MONETARY_desc[item_no].name, "LC_MONETARY",
+                  LC_MONETARY_desc[item_no].min,
+                  LC_MONETARY_desc[item_no].max);
+         break;
+       case MON_DECIMAL_POINT:
+         /* The decimal point must not be empty.  This is not said
+            explicitly in POSIX but ANSI C (ISO/IEC 9899) says in
+            4.4.2.1 it has to be != "".  */
+         if (LC_MONETARY_values[item_no][0] == '\0')
+           error (0, 0,
+                  gettext ("item `%s' in category `%s' must not be empty"),
+                  LC_MONETARY_desc[item_no].name, "LC_MONETARY");
+         break;
+       case CURRENCY_SYMBOL:
+       case MON_THOUSANDS_SEP:
+       case MON_GROUPING:
+       case POSITIVE_SIGN:
+       case NEGATIVE_SIGN:
+       case INT_FRAC_DIGITS:
+       case FRAC_DIGITS:
+         /* Everything is ok for these values.  */
+         break;
+       default:
+         error (5, 0, gettext ("Internal error in %s, line %u"),
+                __FUNCTION__, __LINE__);
+         /* NOTREACHED */
+       }
+
+}
+
+
+static int
+_curr_strcmp(const char *s1, const char **s2)
+{
+  return strcmp (s1, *s2);
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/numeric.c b/locale/numeric.c
new file mode 100644 (file)
index 0000000..93f4bc4
--- /dev/null
@@ -0,0 +1,62 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <langinfo.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "localedef.h"
+
+/* These are defined in locfile-parse.c.   */
+extern struct cat_item LC_NUMERIC_desc[];
+extern char *LC_NUMERIC_values[];
+
+void
+numeric_check(void)
+{
+  int item_no;
+
+  /* First general check for existence.  */
+  for (item_no = 0; item_no < category[LC_NUMERIC].number; ++item_no)
+    if (LC_NUMERIC_values[item_no] == NULL)
+      {
+       int errcode;
+
+       errcode = LC_NUMERIC_desc[item_no].status = std ? 5 : 0;
+
+       error (errcode, 0, gettext ("item `%s' of category `%s' undefined"),
+              LC_NUMERIC_desc[item_no].name, "LC_NUMERIC");
+      }
+    else
+      {
+       if (LC_NUMERIC_desc[item_no].item_id == DECIMAL_POINT
+           && LC_NUMERIC_values[item_no][0] == '\0')
+         /* The decimal point must not be empty.  This is not said
+            explicitly in POSIX but ANSI C (ISO/IEC 9899) says in
+            4.4.2.1 it has to be != "".  */
+         error (0, 0,
+                gettext ("item `%s' in category `%s' must not be empty"),
+                LC_NUMERIC_desc[item_no].name, "LC_NUMERIC");
+      }
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
diff --git a/locale/token.h b/locale/token.h
new file mode 100644 (file)
index 0000000..1227920
--- /dev/null
@@ -0,0 +1,54 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+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
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef _TOKEN_H
+#define _TOKEN_H 1
+
+/* Define those keywords which does not correspond directly to any 
+   item in a category.  Those already have values.  */
+
+enum token
+  {
+    /* We must make sure that these values do not overlap with the
+       other possible return values of the lexer.  */
+    TOK_LAST_USED = _NL_NUM,
+
+    /* General tokens.  */
+    TOK_END, TOK_COMMENT_CHAR, TOK_COPY, TOK_ESCAPE_CHAR, TOK_FROM,
+    TOK_ENDOFLINE, TOK_IDENT, TOK_STRING, TOK_ELLIPSIS, TOK_CHAR,
+    TOK_ILL_CHAR, TOK_NUMBER, TOK_MINUS1,
+
+    /* Tokens from the collate category.  */
+    TOK_IGNORE, TOK_UNDEFINED, TOK_BACKWARD, TOK_FORWARD, TOK_POSITION,
+    TOK_COLLATING_ELEMENT, TOK_COLLATING_SYMBOL, TOK_ORDER_END,
+    TOK_ORDER_START,
+
+    /* Tokens from the ctype category.  */
+    TOK_TOLOWER, TOK_TOUPPER,
+    /* The order here is important.  It must correspond to the bits
+       used for indicating the class membership (ctype.h).  */
+    TOK_UPPER, TOK_LOWER, TOK_ALPHA, TOK_DIGIT, TOK_XDIGIT, TOK_SPACE,
+    TOK_PRINT, TOK_GRAPH, TOK_BLANK, TOK_CNTRL, TOK_PUNCT,
+  };
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */
+#endif /* token.h */