about summary refs log tree commit diff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/Makefile34
-rw-r--r--locale/categories.def4
-rw-r--r--locale/charmap.c524
-rw-r--r--locale/collate.c212
-rw-r--r--locale/config.h45
-rw-r--r--locale/ctype.c817
-rw-r--r--locale/ctypedump.c163
-rw-r--r--locale/hash.c254
-rw-r--r--locale/hash.h50
-rw-r--r--locale/iso-4217.def36
-rw-r--r--locale/keyword.gperf77
-rw-r--r--locale/keyword.h180
-rw-r--r--locale/langinfo.h4
-rw-r--r--locale/libintl.h77
-rw-r--r--locale/loadlocale.c9
-rw-r--r--locale/locale.c538
-rw-r--r--locale/localedef.c261
-rw-r--r--locale/localedef.h196
-rw-r--r--locale/locfile-lex.c533
-rw-r--r--locale/locfile-parse.c820
-rw-r--r--locale/messages.c76
-rw-r--r--locale/monetary.c132
-rw-r--r--locale/numeric.c62
-rw-r--r--locale/token.h54
24 files changed, 5149 insertions, 9 deletions
diff --git a/locale/Makefile b/locale/Makefile
index 87a6706e05..c3abc2c62e 100644
--- a/locale/Makefile
+++ b/locale/Makefile
@@ -21,10 +21,36 @@
 #
 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"'
diff --git a/locale/categories.def b/locale/categories.def
index 6bdad35e17..be3c6bc549 100644
--- a/locale/categories.def
+++ b/locale/categories.def
@@ -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
index 0000000000..ad1075e5bc
--- /dev/null
+++ b/locale/charmap.c
@@ -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
index 0000000000..f06964efcb
--- /dev/null
+++ b/locale/collate.c
@@ -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
index 0000000000..65dfb33e9a
--- /dev/null
+++ b/locale/config.h
@@ -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
index 0000000000..1ce09bab2b
--- /dev/null
+++ b/locale/ctype.c
@@ -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
index 0000000000..e00f3e0f11
--- /dev/null
+++ b/locale/ctypedump.c
@@ -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
index 0000000000..75cb77f882
--- /dev/null
+++ b/locale/hash.c
@@ -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
index 0000000000..5f60a44a5b
--- /dev/null
+++ b/locale/hash.h
@@ -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
index 0000000000..849420365e
--- /dev/null
+++ b/locale/iso-4217.def
@@ -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
index 0000000000..20941bc03d
--- /dev/null
+++ b/locale/keyword.gperf
@@ -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
index 0000000000..1dc442aee5
--- /dev/null
+++ b/locale/keyword.h
@@ -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;
+}
diff --git a/locale/langinfo.h b/locale/langinfo.h
index e94be68430..245dcf8b92 100644
--- a/locale/langinfo.h
+++ b/locale/langinfo.h
@@ -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
index 0000000000..802bd45492
--- /dev/null
+++ b/locale/libintl.h
@@ -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/loadlocale.c b/locale/loadlocale.c
index 5073bd4be3..7e29c97205 100644
--- a/locale/loadlocale.c
+++ b/locale/loadlocale.c
@@ -69,13 +69,14 @@ _nl_load_locale (int category, char **name)
     }
 
   {
-    const char localedir[] = "/share/locale/"; /* XXX */
     const char *catname = _nl_category_names[category];
     size_t namelen = strlen (*name);
     size_t catlen = strlen (catname);
-    char file[sizeof localedir + namelen + catlen * 2 + 4];
-    sprintf (file, "%s%s/%s",
-	     strchr (*name, '/') != NULL ? "" : localedir, *name, catname);
+    char file[sizeof LOCALE_PATH + 1 + namelen + catlen * 2 + 4];
+    if (strchr (*name, '/') != NULL)
+      sprintf (file, "%s/%s", *name, catname);
+    else
+      sprintf (file, "%s/%s/%s", LOCALE_PATH, *name, catname);
     fd = __open (file, O_RDONLY);
     if (fd < 0)
       return NULL;
diff --git a/locale/locale.c b/locale/locale.c
new file mode 100644
index 0000000000..20385f07d8
--- /dev/null
+++ b/locale/locale.c
@@ -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
index 0000000000..c331e11888
--- /dev/null
+++ b/locale/localedef.c
@@ -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
index 0000000000..5958a9c5d2
--- /dev/null
+++ b/locale/localedef.h
@@ -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
index 0000000000..20e4f0f9cd
--- /dev/null
+++ b/locale/locfile-lex.c
@@ -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
index 0000000000..15e25c7afe
--- /dev/null
+++ b/locale/locfile-parse.c
@@ -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
index 0000000000..842e5ef5b2
--- /dev/null
+++ b/locale/messages.c
@@ -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
index 0000000000..2683eb2bf0
--- /dev/null
+++ b/locale/monetary.c
@@ -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
index 0000000000..93f4bc481e
--- /dev/null
+++ b/locale/numeric.c
@@ -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
index 0000000000..1227920083
--- /dev/null
+++ b/locale/token.h
@@ -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 */