summaryrefslogtreecommitdiff
path: root/locale/programs
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1996-03-28 08:30:38 +0000
committerRoland McGrath <roland@gnu.org>1996-03-28 08:30:38 +0000
commit19bc17a90548ee427035994bbc4b14395723ff1f (patch)
treee7a17eda196c2610ca4be26c9e7985815162eafb /locale/programs
parent53f770e0f9d405ea8d1888254c6f7ce431b04c6e (diff)
Thu Mar 28 03:25:10 1996 Roland McGrath <roland@charlie-brown.gnu.ai.mit.edu>
* intl/Makefile (copysrc): Add missing > in sed cmd. Sat Mar 23 17:52:49 1996 Ulrich Drepper <drepper@gnu.ai.mit.edu> * Makeconfig: Rename Makefile variable nlsdir to i18ndir and change value to $(datadir)/i18n. `nls' is not an appropriate name. * Makefile (subdirs): Add new subdir wctype. * ctype/ctype-info.c: Add new global variable __ctype_names and initialize from _nl_C_LC_CTYPE. * ctype/ctype.h: In P1003.3b/D11 `alnum' is a separate character class. Use bit 11. [_ISbit]: Protect definition of bitmasks because they are also used in wctype.h. * libio/genops.c (_IO_sputbackc, _IO_sungetc): Clear EOF flag after successfully pushing back a character. Fundamental changes in locale implementation. Almost nothing from the old code is used anymore. * locale/charmap.c, locale/collate.c, locale/config.h, locale/ctypedump.c, locale/hash.h, locale/keyword.gperf, locale/keyword.h, locale/loadlocale.c, locale/locale-ctype.c, locale/locale.c locale/localeconv.c, locale/localedef.c, locale/localedef.h, locale/locfile-hash.c, locale/locfile-lex.c, locale/locfile-parse.c, locale/messages.c, locale/monetary.c, locale/numeric.c, locale/setlocale.c, locale/token.h, locale/xmalloc.c: Removed. * locale/Makefile: Update for new locale implementation with program source code distributed in subdir. * locale/categories.def, locale/iso-4217.def: Updated file for new locale implementation. * locale/langinfo.h: Updated for new locale implementation. (ERA_D_T_FMT, ERA_T_FMT): New official values according to P1003.2b/D11. (_NL_COLLATE_NRULES, _NL_COLLATE_RULES, _NL_COLLATE_HASH_SIZE, _NL_COLLATE_HASH_LAYERS, _NL_COLLATE_TABLE_EB, _NL_COLLATE_TABLE_EL, _NL_COLLATE_UNDEFINED, _NL_COLLATE_EXTRA_EB, _NL_COLLATE_EXTRA_EL, _NL_CTYPE_NAMES_EB, _NL_CTYPE_NAMES_EL, _NL_CTYPE_HASH_SIZE, _NL_CTYPE_HASH_LAYERS, _NL_CTYPE_CLASS_NAMES, _NL_CTYPE_MAP_NAMES, _NL_CTYPE_WIDTH): New internal values for extended LC_CTYPE and LC_COLLATE implementation. * locale/simple-hash.c, locale/simple-hash.h, locale/xmalloc.c, locale/xstrdup.c: Helper functions for locale related programs. * locale/C-collate.c, locale/C-ctype.c, locale/C-messages.c, locale/C-monetary.c, locale/C-numeric.c, locale/C-time.c, locale/lc-collate.c, locale/lc-ctype.c, locale/lc-messages.c, locale/lc-monetary.c, locale/lc-numeric.c, locale/lc-time.c: New implementation of locale functions, and new generated "C" locale data. * locale/loadlocale.c: Now handles word fields in locale binary automatically by changing the endianess if necessary. * locale/localeinfo.h (LIMAGIC): Changed magic number because of incompatible changes. (locale_data): Changed definition to allow word as a value type. (coll_sort_rule): Values for collation sorting mode. (_NL_CURRENT_WORD): New macro to access word value of locale entry. (__collate_table, __collate_extra): Declare new global variables for collation tables. * locale/programs/charmap-kw.gperf, locale/programs/charmap-kw.h, locale/programs/charmap.c, locale/programs/charset.c, locale/programs/charset.h, locale/programs/config.h, locale/programs/ctypedump.c, locale/programs/ld-collate.c, locale/programs/ld-ctype.c, locale/programs/ld-messages.c, locale/programs/ld-monetary.c, locale/programs/ld-numeric.c, locale/programs/ld-time.c, locale/programs/linereader.c, locale/programs/linereader.h, locale/programs/locale.c, locale/programs/localedef.c, locale/programs/locales.h, locale/programs/locfile-kw.gperf, locale/programs/locfile-kw.h, locale/programs/locfile-token.h, locale/programs/locfile.c, locale/programs/locfile.h, locale/programs/stringtrans.c, locale/programs/stringtrans.h: Implementation of locale related programs. * locale/weight.h: Functions to access collation tables. * posix/unistd.h: Define _POSIX2_LOCALEDEF. * stdio-common/printf_fp.c: Fix bug with printing certain numbers < 10^-1. Reported by Bill Metzenthen. * stdio-common/tfformat.c: Add new test for above bug. * string/strcoll.c, string/strxfrm.c: Real implementation of string collation according to ISO C. * wctype/Makefile, wctype/cname-lookup.h, wctype/iswctype.c, wctype/test_wctype.c, wctype/towctrans.c, wctype/wcfuncs.c, wctype/wctrans.c, wctype/wctype.c, wctype/wctype.h: New files. Implementation of wide character classes and mapping.
Diffstat (limited to 'locale/programs')
-rw-r--r--locale/programs/charmap-kw.gperf40
-rw-r--r--locale/programs/charmap-kw.h117
-rw-r--r--locale/programs/charmap.c593
-rw-r--r--locale/programs/charset.c132
-rw-r--r--locale/programs/charset.h61
-rw-r--r--locale/programs/config.h33
-rw-r--r--locale/programs/ctypedump.c163
-rw-r--r--locale/programs/ld-collate.c1549
-rw-r--r--locale/programs/ld-ctype.c1310
-rw-r--r--locale/programs/ld-messages.c237
-rw-r--r--locale/programs/ld-monetary.c385
-rw-r--r--locale/programs/ld-numeric.c208
-rw-r--r--locale/programs/ld-time.c310
-rw-r--r--locale/programs/linereader.c579
-rw-r--r--locale/programs/linereader.h158
-rw-r--r--locale/programs/locale.c544
-rw-r--r--locale/programs/localedef.c461
-rw-r--r--locale/programs/locales.h207
-rw-r--r--locale/programs/locfile-kw.gperf99
-rw-r--r--locale/programs/locfile-kw.h211
-rw-r--r--locale/programs/locfile-token.h147
-rw-r--r--locale/programs/locfile.c979
-rw-r--r--locale/programs/locfile.h75
-rw-r--r--locale/programs/stringtrans.c146
-rw-r--r--locale/programs/stringtrans.h38
25 files changed, 8782 insertions, 0 deletions
diff --git a/locale/programs/charmap-kw.gperf b/locale/programs/charmap-kw.gperf
new file mode 100644
index 0000000000..8e00103882
--- /dev/null
+++ b/locale/programs/charmap-kw.gperf
@@ -0,0 +1,40 @@
+%{
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+
+#include "locfile-token.h"
+%}
+struct keyword_t ;
+%%
+code_set_name, tok_code_set_name, 1
+mb_cur_max, tok_mb_cur_max, 1
+mb_cur_min, tok_mb_cur_min, 1
+escape_char, tok_escape_char, 1
+comment_char, tok_comment_char, 1
+g0esc, tok_g0esc, 1
+g1esc, tok_g1esc, 1
+g2esc, tok_g2esc, 1
+g3esc, tok_g3esc, 1
+CHARMAP, tok_charmap, 0
+END, tok_end, 0
+WIDTH, tok_width, 0
+WIDTH_VARIABLE, tok_width_variable, 0
+WIDTH_DEFAULT, tok_width_default, 0
diff --git a/locale/programs/charmap-kw.h b/locale/programs/charmap-kw.h
new file mode 100644
index 0000000000..93326d0382
--- /dev/null
+++ b/locale/programs/charmap-kw.h
@@ -0,0 +1,117 @@
+/* C code produced by gperf version 2.5 (GNU C++ version) */
+/* Command-line: gperf -acCgopt -k1,2,5,$ -N charmap_hash programs/charmap-kw.gperf */
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+
+#include "locfile-token.h"
+struct keyword_t ;
+
+#define TOTAL_KEYWORDS 14
+#define MIN_WORD_LENGTH 3
+#define MAX_WORD_LENGTH 14
+#define MIN_HASH_VALUE 3
+#define MAX_HASH_VALUE 25
+/* maximum key range = 23, duplicates = 0 */
+
+#ifdef __GNUC__
+inline
+#endif
+static unsigned int
+hash (register const char *str, register int len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 14, 10,
+ 15, 4, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 0, 0, 0,
+ 26, 26, 0, 0, 26, 26, 26, 0, 0, 26,
+ 0, 26, 26, 26, 5, 26, 26, 0, 26, 26,
+ 26, 26, 26, 26, 26, 0, 26, 26, 0, 0,
+ 26, 0, 26, 0, 26, 26, 26, 26, 26, 0,
+ 15, 0, 0, 26, 0, 0, 26, 0, 26, 26,
+ 0, 26, 26, 26, 26, 26, 26, 26,
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ case 5:
+ hval += asso_values[(int) str[4]];
+ case 4:
+ case 3:
+ case 2:
+ hval += asso_values[(int) str[1]];
+ case 1:
+ hval += asso_values[(int) str[0]];
+ break;
+ }
+ return hval + asso_values[(int) str[len - 1]];
+}
+
+#ifdef __GNUC__
+inline
+#endif
+const struct keyword_t *
+charmap_hash (register const char *str, register int len)
+{
+ static const struct keyword_t wordlist[] =
+ {
+ {"",}, {"",}, {"",},
+ {"END", tok_end, 0},
+ {"",},
+ {"WIDTH", tok_width, 0},
+ {"",},
+ {"CHARMAP", tok_charmap, 0},
+ {"",},
+ {"g3esc", tok_g3esc, 1},
+ {"mb_cur_max", tok_mb_cur_max, 1},
+ {"escape_char", tok_escape_char, 1},
+ {"comment_char", tok_comment_char, 1},
+ {"code_set_name", tok_code_set_name, 1},
+ {"WIDTH_VARIABLE", tok_width_variable, 0},
+ {"g1esc", tok_g1esc, 1},
+ {"",}, {"",},
+ {"WIDTH_DEFAULT", tok_width_default, 0},
+ {"g0esc", tok_g0esc, 1},
+ {"g2esc", tok_g2esc, 1},
+ {"",}, {"",}, {"",}, {"",},
+ {"mb_cur_min", tok_mb_cur_min, 1},
+ };
+
+ 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/programs/charmap.c b/locale/programs/charmap.c
new file mode 100644
index 0000000000..2b71821ec0
--- /dev/null
+++ b/locale/programs/charmap.c
@@ -0,0 +1,593 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <obstack.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "error.h"
+#include "linereader.h"
+#include "charset.h"
+
+
+/* Uncomment following line for production version. */
+/* define NDEBUG 1 */
+#include <assert.h>
+
+
+/* Define the lookup function. */
+#include "charmap-kw.h"
+
+
+void *xmalloc (size_t __n);
+
+/* Prototypes for local functions. */
+static struct charset_t *parse_charmap (const char *filename);
+
+
+
+struct charset_t *
+charmap_read (const char *filename)
+{
+ const char *pathnfile;
+ struct charset_t *result = NULL;
+
+ if (filename != NULL)
+ {
+ if (euidaccess (filename, R_OK) >= 0)
+ pathnfile = filename;
+ else
+ {
+ char *cp = xmalloc (strlen (filename) + sizeof CHARMAP_PATH + 1);
+ stpcpy (stpcpy (stpcpy (cp, CHARMAP_PATH), "/"), filename);
+
+ pathnfile = (const char *) cp;
+ }
+
+ result = parse_charmap (pathnfile);
+
+ if (result == NULL)
+ error (0, errno, _("character map file `%s' not found"), filename);
+ }
+
+ if (result == NULL)
+ {
+ pathnfile = CHARMAP_PATH "/" DEFAULT_CHARMAP;
+
+ result = parse_charmap (pathnfile);
+
+ if (result == NULL)
+ error (4, errno, _("default character map file `%s' not found"),
+ DEFAULT_CHARMAP);
+ }
+
+ return result;
+}
+
+
+static struct charset_t *
+parse_charmap (const char *filename)
+{
+ struct linereader *cmfile;
+ struct charset_t *result;
+ int state;
+ enum token_t expected_tok = tok_error;
+ const char *expected_str = NULL;
+ char *from_name = NULL;
+ char *to_name = NULL;
+
+ /* Determine path. */
+ cmfile = lr_open (filename, charmap_hash);
+ if (cmfile == NULL)
+ {
+ if (strchr (filename, '/') == NULL)
+ {
+ /* Look in the systems charmap directory. */
+ char *buf = xmalloc (strlen (filename) + 1 + sizeof (CHARMAP_PATH));
+
+ stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), filename);
+ cmfile = lr_open (buf, charmap_hash);
+
+ if (cmfile == NULL)
+ free (buf);
+ }
+
+ if (cmfile == NULL)
+ return NULL;
+ }
+
+ /* Allocate room for result. */
+ result = (struct charset_t *) xmalloc (sizeof (struct charset_t));
+ memset (result, '\0', sizeof (struct charset_t));
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+ obstack_init (&result->mem_pool);
+
+ if (init_hash (&result->char_table, 256))
+ {
+ free (result);
+ return NULL;
+ }
+
+ /* We use a state machine to describe the charmap description file
+ format. */
+ state = 1;
+ while (1)
+ {
+ /* What's on? */
+ struct token *now = lr_token (cmfile, NULL);
+ enum token_t nowtok = now->tok;
+ struct token *arg;
+
+ if (nowtok == tok_eof)
+ break;
+
+ switch (state)
+ {
+ case 1:
+ /* The beginning. We expect the special declarations, EOL or
+ `CHARMAP'. */
+ if (nowtok == tok_eol)
+ /* Ignore empty lines. */
+ continue;
+
+ if (nowtok == tok_charmap)
+ {
+ from_name = NULL;
+ to_name = NULL;
+
+ /* We have to set up the real work. Fill in some
+ default values. */
+ if (result->mb_cur_max == 0)
+ result->mb_cur_max = 1;
+ if (result->mb_cur_min == 0)
+ result->mb_cur_min = result->mb_cur_max;
+ if (result->mb_cur_min > result->mb_cur_max)
+ {
+ error (0, 0, _("\
+%s: <mb_cur_max> must be greater than <mb_cur_min>\n"),
+ cmfile->fname);
+
+ result->mb_cur_min = result->mb_cur_max;
+ }
+
+ lr_ignore_rest (cmfile, 1);
+
+ state = 2;
+ continue;
+ }
+
+ if (nowtok != tok_code_set_name && nowtok != tok_mb_cur_max
+ && nowtok != tok_mb_cur_min && nowtok != tok_escape_char
+ && nowtok != tok_comment_char && nowtok != tok_g0esc
+ && nowtok != tok_g1esc && nowtok != tok_g2esc
+ && nowtok != tok_g3esc)
+ {
+ lr_error (cmfile, _("syntax error in prolog: %s"),
+ _("illegal definition"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ /* We know that we need an argument. */
+ arg = lr_token (cmfile, NULL);
+
+ switch (nowtok)
+ {
+ case tok_code_set_name:
+ if (arg->tok != tok_ident)
+ {
+ badarg:
+ lr_error (cmfile, _("syntax error in prolog: %s"),
+ _("bad argument"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ result->code_set_name = obstack_copy0 (&result->mem_pool,
+ arg->val.str.start,
+ arg->val.str.len);
+
+ lr_ignore_rest (cmfile, 1);
+ continue;
+
+ case tok_mb_cur_max:
+ case tok_mb_cur_min:
+ if (arg->tok != tok_number)
+ goto badarg;
+
+ if (arg->val.num < 1 || arg->val.num > 4)
+ {
+ lr_error (cmfile,
+ _("value for <%s> must lie between 1 and 4"),
+ nowtok == tok_mb_cur_min ? "mb_cur_min"
+ : "mb_cur_max");
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+ if ((nowtok == tok_mb_cur_max && result->mb_cur_min != 0
+ && arg->val.num < result->mb_cur_min)
+ || (nowtok == tok_mb_cur_min && result->mb_cur_max != 0
+ && arg->val.num > result->mb_cur_max))
+ {
+ lr_error (cmfile, _("\
+value of <mb_cur_max> must be greater than the value of <mb_cur_min>"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ if (nowtok == tok_mb_cur_max)
+ result->mb_cur_max = arg->val.num;
+ else
+ result->mb_cur_min = arg->val.num;
+
+ lr_ignore_rest (cmfile, 1);
+ continue;
+
+ case tok_escape_char:
+ case tok_comment_char:
+ if (arg->tok != tok_ident)
+ goto badarg;
+
+ if (arg->val.str.len != 1)
+ {
+ lr_error (cmfile, _("\
+argument to <%s> must be a single character"),
+ nowtok == tok_escape_char ? "escape_char"
+ : "comment_char");
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ if (nowtok == tok_escape_char)
+ cmfile->escape_char = *arg->val.str.start;
+ else
+ cmfile->comment_char = *arg->val.str.start;
+
+ lr_ignore_rest (cmfile, 1);
+ continue;
+
+ case tok_g0esc:
+ case tok_g1esc:
+ case tok_g2esc:
+ case tok_g3esc:
+ lr_ignore_rest (cmfile, 0); /* XXX */
+ continue;
+
+ default:
+ /* Cannot happen. */
+ assert (! "Should not happen");
+ }
+ break;
+
+ case 2:
+ /* We have seen `CHARMAP' and now are in the body. Each line
+ must have the format "%s %s %s\n" or "%s...%s %s %s\n". */
+ if (nowtok == tok_eol)
+ /* Ignore empty lines. */
+ continue;
+
+ if (nowtok == tok_end)
+ {
+ expected_tok = tok_charmap;
+ expected_str = "CHARMAP";
+ state = 90;
+ continue;
+ }
+
+ if (nowtok != tok_bsymbol)
+ {
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "CHARMAP", _("no symbolic name given"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ /* If the previous line was not completely correct free the
+ used memory. */
+ if (from_name != NULL)
+ obstack_free (&result->mem_pool, from_name);
+
+ from_name = (char *) obstack_copy0 (&result->mem_pool,
+ now->val.str.start,
+ now->val.str.len);
+ to_name = NULL;
+
+ state = 3;
+ continue;
+
+ case 3:
+ /* We have two possibilities: We can see an ellipsis or an
+ encoding value. */
+ if (nowtok == tok_ellipsis)
+ {
+ state = 4;
+ continue;
+ }
+ /* FALLTHROUGH */
+
+ case 5:
+ if (nowtok != tok_charcode && nowtok != tok_ucs2
+ && nowtok != tok_ucs4)
+ {
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "CHARMAP", _("illegal encoding given"));
+
+ lr_ignore_rest (cmfile, 0);
+
+ state = 2;
+ continue;
+ }
+
+ if (nowtok == tok_charcode)
+ /* Write char value in table. */
+ charset_new_char (cmfile, result, now->val.charcode.nbytes,
+ now->val.charcode.val, from_name, to_name);
+ else
+ /* Determine ISO 10646 value and write into table. */
+ charset_new_unicode (cmfile, result, now->val.charcode.nbytes,
+ now->val.charcode.val, from_name, to_name);
+
+ /* Ignore trailing comment silently. */
+ lr_ignore_rest (cmfile, 0);
+
+ from_name = NULL;
+ to_name = NULL;
+
+ state = 2;
+ continue;
+
+ case 4:
+ if (nowtok != tok_bsymbol)
+ {
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "CHARMAP",
+ _("no symbolic name given for end of range"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ /* If the previous line was not completely correct free the
+ used memory. */
+ to_name = (char *) obstack_copy0 (&result->mem_pool,
+ cmfile->token.val.str.start,
+ cmfile->token.val.str.len);
+
+ state = 3;
+ continue;
+
+ case 90:
+ if (nowtok != expected_tok)
+ lr_error (cmfile, _("\
+`%1$s' definition does not end with `END %1$s'"), expected_str);
+
+ lr_ignore_rest (cmfile, nowtok == expected_tok);
+ state = 91;
+ continue;
+
+ case 91:
+ /* Waiting for WIDTH... */
+ if (nowtok == tok_width_default)
+ {
+ state = 92;
+ continue;
+ }
+
+ if (nowtok == tok_width)
+ {
+ lr_ignore_rest (cmfile, 1);
+ state = 93;
+ continue;
+ }
+
+ if (nowtok == tok_width_variable)
+ {
+ lr_ignore_rest (cmfile, 1);
+ state = 98;
+ continue;
+ }
+
+ lr_error (cmfile, _("\
+only WIDTH definitions are allowed to follow the CHARMAP definition"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+
+ case 92:
+ if (nowtok != tok_number)
+ lr_error (cmfile, _("value for %s must be an integer"),
+ "WIDTH_DEFAULT");
+ else
+ result->width_default = now->val.num;
+
+ lr_ignore_rest (cmfile, nowtok == tok_number);
+
+ state = 91;
+ continue;
+
+ case 93:
+ /* We now expect `END WIDTH' or lines of the format "%s %d\n" or
+ "%s...%s %d\n". */
+ if (nowtok == tok_eol)
+ /* ignore empty lines. */
+ continue;
+
+ if (nowtok == tok_end)
+ {
+ expected_tok = tok_width;
+ expected_str = "WIDTH";
+ state = 90;
+ continue;
+ }
+
+ if (nowtok != tok_bsymbol)
+ {
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "WIDTH", _("no symbolic name given"));
+
+ lr_ignore_rest (cmfile, 0);
+ continue;
+ }
+
+ if (from_name != NULL)
+ obstack_free (&result->mem_pool, from_name);
+
+ from_name = (char *) obstack_copy0 (&result->mem_pool,
+ now->val.str.start,
+ now->val.str.len);
+ to_name = NULL;
+
+ state = 94;
+ continue;
+
+ case 94:
+ if (nowtok == tok_ellipsis)
+ state = 95;
+
+ case 96:
+ if (nowtok != tok_number)
+ lr_error (cmfile, _("value for %s must be an integer"),
+ "WIDTH");
+ else
+ {
+ /* XXX Store width for chars. */
+ from_name = NULL;
+ }
+
+ lr_ignore_rest (cmfile, nowtok == tok_number);
+
+ state = 93;
+ continue;
+
+ case 95:
+ if (nowtok != tok_bsymbol)
+ {
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "WIDTH", _("no symbolic name given for end of range"));
+
+ lr_ignore_rest (cmfile, 0);
+
+ state = 93;
+ continue;
+ }
+
+ to_name = (char *) obstack_copy0 (&result->mem_pool,
+ now->val.str.start,
+ now->val.str.len);
+
+ lr_ignore_rest (cmfile, 1);
+
+ state = 96;
+ continue;
+
+ case 98:
+ /* We now expect `END WIDTH_VARIABLE' or lines of the format
+ "%s\n" or "%s...%s\n". */
+ if (nowtok == tok_eol)
+ /* ignore empty lines. */
+ continue;
+
+ if (nowtok == tok_end)
+ {
+ expected_tok = tok_width_variable;
+ expected_str = "WIDTH_VARIABLE";
+ state = 90;
+ continue;
+ }
+
+ if (nowtok != tok_bsymbol)
+ {
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "WIDTH_VARIABLE", _("no symbolic name given"));
+
+ lr_ignore_rest (cmfile, 0);
+
+ continue;
+ }
+
+ if (from_name != NULL)
+ obstack_free (&result->mem_pool, from_name);
+
+ from_name = (char *) obstack_copy0 (&result->mem_pool,
+ now->val.str.start,
+ now->val.str.len);
+ to_name = NULL;
+
+ state = 99;
+ continue;
+
+ case 99:
+ if (nowtok == tok_ellipsis)
+ state = 100;
+
+ /* Store info. */
+ from_name = NULL;
+
+ /* Warn */
+ state = 98;
+ continue;
+
+ case 100:
+ if (nowtok != tok_bsymbol)
+ lr_error (cmfile, _("syntax error in %s definition: %s"),
+ "WIDTH_VARIABLE",
+ _("no symbolic name given for end of range"));
+ else
+ {
+ to_name = (char *) obstack_copy0 (&result->mem_pool,
+ now->val.str.start,
+ now->val.str.len);
+ /* XXX Enter value into table. */
+ }
+
+ lr_ignore_rest (cmfile, nowtok == tok_bsymbol);
+
+ state = 98;
+ continue;
+
+ default:
+ error (5, 0, _("%s: error in state machine"), __FILE__);
+ /* NOTREACHED */
+ }
+ break;
+ }
+
+ if (state != 91)
+ error (0, 0, _("%s: premature end of file"), cmfile->fname);
+
+ lr_close (cmfile);
+
+ return result;
+}
diff --git a/locale/programs/charset.c b/locale/programs/charset.c
new file mode 100644
index 0000000000..2e2f63bd9a
--- /dev/null
+++ b/locale/programs/charset.c
@@ -0,0 +1,132 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <alloca.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "charset.h"
+
+
+static void
+insert_char (struct linereader *lr, struct charset_t *cs, int bytes,
+ unsigned int value, const char *from, const char *to);
+
+
+void
+charset_new_char (struct linereader *lr, struct charset_t *cs, int bytes,
+ unsigned int value, const char *from, const char *to)
+{
+ if (bytes < cs->mb_cur_min)
+ lr_error (lr, _("too few bytes in character encoding"));
+ else if (bytes > cs->mb_cur_max)
+ lr_error (lr, _("too many bytes in character encoding"));
+ else
+ insert_char (lr, cs, bytes, value, from, to);
+}
+
+
+void
+charset_new_unicode (struct linereader *lr, struct charset_t *cs, int bytes,
+ unsigned int value, const char *from, const char *to)
+{
+ /* For now: perhaps <Uxxxx> support will be removed again... */
+ insert_char (lr, cs, bytes, value, from, to);
+}
+
+
+unsigned int
+charset_find_value (const struct charset_t *cs, const char *name, size_t len)
+{
+ void *result;
+
+ if (find_entry ((hash_table *) &cs->char_table, name, len, &result) < 0)
+ return ILLEGAL_CHAR_VALUE;
+
+ return (unsigned int) result;
+}
+
+
+static void
+insert_char (struct linereader *lr, struct charset_t *cs, int bytes,
+ unsigned int value, const char *from, const char *to)
+{
+ const char *cp;
+ char *buf;
+ int prefix_len, len1, len2;
+ unsigned int from_nr, to_nr, cnt;
+
+ if (to == NULL)
+ {
+ if (insert_entry (&cs->char_table, from, strlen (from), (void *) value)
+ < 0)
+ lr_error (lr, _("duplicate character name `%s'"), from);
+
+ return;
+ }
+
+ /* We have a range: the names must have names with equal prefixes
+ and an equal number of digits, where the second number is greater
+ or equal than the first. */
+ len1 = strlen (from);
+ len2 = strlen (to);
+
+ if (len1 != len2)
+ {
+ illegal_range:
+ lr_error (lr, _("illegal names for character range"));
+ return;
+ }
+
+ cp = &from[len1 - 1];
+ while (isdigit (*cp) && cp >= from)
+ --cp;
+
+ prefix_len = (cp - from) + 1;
+
+ if (cp == &from[len1 - 1] || strncmp (from, to, prefix_len) != 0)
+ goto illegal_range;
+
+ from_nr = strtoul (&from[prefix_len], NULL, 10);
+ to_nr = strtoul (&to[prefix_len], NULL, 10);
+
+ if (from_nr > to_nr)
+ {
+ lr_error (lr, _("upper limit in range is not smaller then lower limit"));
+ return;
+ }
+
+ buf = alloca (len1 + 1);
+ memcpy (buf, from, prefix_len);
+
+ for (cnt = from_nr; cnt <= to_nr; ++cnt)
+ {
+ sprintf (&buf[prefix_len], "%0d", cnt);
+
+ if (insert_entry (&cs->char_table, buf, len1, (void *) cnt) < 0)
+ lr_error (lr, _("duplicate character name `%s'"), buf);
+ }
+}
diff --git a/locale/programs/charset.h b/locale/programs/charset.h
new file mode 100644
index 0000000000..222d468407
--- /dev/null
+++ b/locale/programs/charset.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifndef _CHARSET_H
+#define _CHARSET_H
+
+#include <obstack.h>
+
+#include "simple-hash.h"
+#include "linereader.h"
+
+
+struct charset_t
+{
+ const char *code_set_name;
+ int mb_cur_min;
+ int mb_cur_max;
+ int width_default;
+
+ struct obstack mem_pool;
+ hash_table char_table;
+};
+
+
+/* We need one value to mark the error case. Let's use 0xffffffff.
+ I.e., it is placed in the last page of ISO 10646. For now only the
+ first is used and we have plenty of room. */
+#define ILLEGAL_CHAR_VALUE 0xffffffffu
+
+
+/* Prototypes for charmap handling functions. */
+struct charset_t *charmap_read (const char *filename);
+
+/* Prototypes for funciton to insert new character. */
+void charset_new_char (struct linereader *lr, struct charset_t *cs, int bytes,
+ unsigned int value, const char *from, const char *to);
+
+void charset_new_unicode (struct linereader *lr, struct charset_t *cs,
+ int bytes, unsigned int value, const char *from,
+ const char *to);
+
+unsigned int charset_find_value (const struct charset_t *__cs,
+ const char *__name, size_t __len);
+
+#endif /* charset.h */
diff --git a/locale/programs/config.h b/locale/programs/config.h
new file mode 100644
index 0000000000..64054657cb
--- /dev/null
+++ b/locale/programs/config.h
@@ -0,0 +1,33 @@
+#ifndef _LD_CONFIG_H
+#define _LD_CONFIG_H
+
+/* Use the internal textdomain used for libc messages. */
+#define PACKAGE _libc_intl_domainname
+#ifndef VERSION
+/* Get libc version number. */
+#include "../../version.h"
+#endif
+
+#define DEFAULT_CHARMAP "POSIX"
+
+#ifndef PARAMS
+# if __STDC__
+# define PARAMS(args) args
+# else
+# define PARAMS(args) ()
+# endif
+#endif
+
+
+
+#define HAVE_VPRINTF 1
+
+
+typedef int wint_t;
+typedef unsigned short int u16_t;
+
+
+
+int euidaccess (__const char *__name, int __type);
+
+#endif
diff --git a/locale/programs/ctypedump.c b/locale/programs/ctypedump.c
new file mode 100644
index 0000000000..2a6753495e
--- /dev/null
+++ b/locale/programs/ctypedump.c
@@ -0,0 +1,163 @@
+/* Copyright (C) 1995, 1996 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_t) (((((u32_t) (v)) & 0x000000ff) << 24) \
+ | ((((u32_t) (v)) & 0x0000ff00) << 8) \
+ | ((((u32_t) (v)) & 0x00ff0000) >> 8) \
+ | ((((u32_t) (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/programs/ld-collate.c b/locale/programs/ld-collate.c
new file mode 100644
index 0000000000..0f3bcbca33
--- /dev/null
+++ b/locale/programs/ld-collate.c
@@ -0,0 +1,1549 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <obstack.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wcstr.h>
+
+#include "localeinfo.h"
+#include "locales.h"
+#include "simple-hash.h"
+#include "stringtrans.h"
+
+/* Uncomment the following line in the production version. */
+/* define NDEBUG 1 */
+#include <assert.h>
+
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define SWAPU32(w) \
+ (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
+
+
+/* What kind of symbols get defined? */
+enum coll_symbol
+{
+ undefined,
+ ellipsis,
+ character,
+ element,
+ symbol
+};
+
+
+typedef struct patch_t
+{
+ const char *fname;
+ size_t lineno;
+ const char *token;
+ union
+ {
+ unsigned int *pos;
+ size_t idx;
+ } where;
+ struct patch_t *next;
+} patch_t;
+
+
+typedef struct element_t
+{
+ const wchar_t *name;
+ unsigned int this_weight;
+
+ struct element_t *next;
+
+ unsigned int *ordering;
+ size_t ordering_len;
+} element_t;
+
+
+/* The real definition of the struct for the LC_CTYPE locale. */
+struct locale_collate_t
+{
+ /* Collate symbol table. Simple mapping to number. */
+ hash_table symbols;
+
+ /* The collation elements. */
+ hash_table elements;
+ struct obstack element_mem;
+
+ /* The result table. */
+ hash_table result;
+
+ /* Sorting rules given in order_start line. */
+ int nrules;
+ int nrules_max;
+ enum coll_sort_rule *rules;
+
+ /* Used while recognizing symbol composed of multiple tokens
+ (collating-element). */
+ const char *combine_token;
+ size_t combine_token_len;
+
+ /* How many sorting order specifications so far. */
+ unsigned int order_cnt;
+
+ /* Was lastline ellipsis? */
+ int was_ellipsis;
+ /* Value of last entry if was character. */
+ wchar_t last_char;
+ /* Current element. */
+ element_t *current_element;
+ /* What kind of symbol is current element. */
+ enum coll_symbol kind;
+
+ /* While collecting the weigths we need some temporary space. */
+ unsigned int current_order;
+ int *weight_cnt;
+ int weight_idx;
+ unsigned int *weight;
+ int nweight;
+ int nweight_max;
+
+ /* Patch lists. */
+ patch_t *current_patch;
+ patch_t *all_patches;
+
+ /* Room for the UNDEFINED information. */
+ element_t undefined;
+ unsigned int undefined_len;
+};
+
+
+/* Be verbose? Defined in localedef.c. */
+extern int verbose;
+
+
+void *xmalloc (size_t __n);
+void *xrealloc (void *__p, size_t __n);
+
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+
+void
+collate_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *charset)
+{
+ struct locale_collate_t *collate;
+
+ /* It is important that we always use UCS4 encoding for strings now. */
+ encoding_method = ENC_UCS4;
+
+ /* Allocate the needed room. */
+ locale->categories[LC_COLLATE].collate = collate =
+ (struct locale_collate_t *) xmalloc (sizeof (struct locale_collate_t));
+
+ /* Allocate hash table for collating elements. */
+ if (init_hash (&collate->elements, 512))
+ error (4, 0, _("memory exhausted"));
+ collate->combine_token = NULL;
+ obstack_init (&collate->element_mem);
+
+ /* Allocate hash table for collating elements. */
+ if (init_hash (&collate->symbols, 64))
+ error (4, 0, _("memory exhausted"));
+
+ /* Allocate hash table for result. */
+ if (init_hash (&collate->result, 512))
+ error (4, 0, _("memory exhausted"));
+
+ collate->nrules = 0;
+ collate->nrules_max = 10;
+ collate->rules
+ = (enum coll_sort_rule *) xmalloc (collate->nrules_max
+ * sizeof (enum coll_sort_rule));
+
+ collate->order_cnt = 1; /* The smallest weight is 2. */
+
+ collate->was_ellipsis = 0;
+ collate->last_char = L'\0'; /* 0 because leading ellipsis is allowed. */
+
+ collate->all_patches = NULL;
+
+ /* This tells us no UNDEFINED entry was found until now. */
+ collate->undefined.this_weight = 0;
+
+ lr->translate_strings = 0;
+}
+
+
+void
+collate_finish (struct localedef_t *locale, struct charset_t *charset)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ patch_t *patch;
+ size_t cnt;
+
+ /* Patch the constructed table so that forward references are
+ correctly filled. */
+ for (patch = collate->all_patches; patch != NULL; patch = patch->next)
+ {
+ wchar_t wch;
+ size_t toklen = strlen (patch->token);
+ void *ptmp;
+ unsigned int value = 0;
+
+ wch = charset_find_value (charset, patch->token, toklen);
+ if (wch != ILLEGAL_CHAR_VALUE)
+ {
+ element_t *runp;
+
+ if (find_entry (&collate->result, &wch, sizeof (wchar_t),
+ (void *) &runp) < 0)
+ runp = NULL;
+ for (; runp != NULL; runp = runp->next)
+ if (runp->name[0] == wch && runp->name[1] == L'\0')
+ break;
+
+ value = runp == NULL ? 0 : runp->this_weight;
+ }
+ else if (find_entry (&collate->elements, patch->token, toklen, &ptmp)
+ >= 0)
+ {
+ value = ((element_t *) ptmp)->this_weight;
+ }
+ else if (find_entry (&collate->symbols, patch->token, toklen, &ptmp)
+ >= 0)
+ {
+ value = (unsigned int) ptmp;
+ }
+ else
+ value = 0;
+
+ if (value == 0)
+ error_with_loc (0, 0, patch->fname, patch->lineno,
+ _("no weight defined for symbol `%s'"), patch->token);
+ else
+ *patch->where.pos = value;
+ }
+
+ /* If no definition for UNDEFINED is given, all characters in the
+ given charset must be specified. */
+ if (collate->undefined.ordering == NULL)
+ {
+ /**************************************************************\
+ |* XXX We should test whether really an unspecified character *|
+ |* exists before giving the message. *|
+ \**************************************************************/
+ u32_t weight;
+
+ error (0, 0, _("no definition of `UNDEFINED'"));
+
+ collate->undefined.ordering_len = collate->nrules;
+ weight = ++collate->order_cnt;
+
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ {
+ u32_t one = 1;
+ obstack_grow (&collate->element_mem, &one, sizeof (one));
+ }
+
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ obstack_grow (&collate->element_mem, &weight, sizeof (weight));
+
+ collate->undefined.ordering = obstack_finish (&collate->element_mem);
+ }
+
+ collate->undefined_len = 2; /* For the name: 1 x wchar_t + L'\0'. */
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ collate->undefined_len += 1 + collate->undefined.ordering[cnt];
+
+ /* Collating symbols are not used anymore. */
+ (void) delete_hash (&collate->symbols);
+}
+
+
+
+void
+collate_output (struct localedef_t *locale, const char *output_path)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ u32_t table_size, table_best, level_best, sum_best;
+ void *last;
+ element_t *pelem;
+ wchar_t *name;
+ size_t len;
+ const size_t nelems = _NL_ITEM_INDEX (_NL_NUM_LC_COLLATE);
+ struct iovec iov[2 + nelems];
+ struct locale_file data;
+ u32_t idx[nelems];
+ struct obstack non_simple;
+ size_t cnt, entry_size;
+ u32_t undefined_offset = UINT_MAX;
+ u32_t *table, *extra, *table2, *extra2;
+ size_t extra_len;
+
+ sum_best = UINT_MAX;
+ table_best = 0xffff;
+ level_best = 0xffff;
+
+ /* Compute table size. */
+ fputs (_("\
+Computing table size for collation information might take a while..."),
+ stderr);
+ for (table_size = 256; table_size < sum_best; ++table_size)
+ {
+ size_t hits[table_size];
+ unsigned int worst = 1;
+ size_t cnt;
+
+ last = NULL;
+
+ for (cnt = 0; cnt < 256; ++cnt)
+ hits[cnt] = 1;
+ memset (&hits[256], '\0', sizeof (hits) - 256 * sizeof (size_t));
+
+ while (iterate_table (&collate->result, &last, (const void **) &name,
+ &len, (void **) &pelem) >= 0)
+ if (pelem->ordering != NULL && pelem->name[0] > 0xff)
+ if (++hits[(unsigned int) pelem->name[0] % table_size] > worst)
+ {
+ worst = hits[(unsigned int) pelem->name[0] % table_size];
+ if (table_size * worst > sum_best)
+ break;
+ }
+
+ if (table_size * worst < sum_best)
+ {
+ sum_best = table_size * worst;
+ table_best = table_size;
+ level_best = worst;
+ }
+ }
+ assert (table_best != 0xffff || level_best != 0xffff);
+ fputs (_(" done\n"), stderr);
+
+ obstack_init (&non_simple);
+
+ data.magic = LIMAGIC (LC_COLLATE);
+ data.n = nelems;
+ iov[0].iov_base = (void *) &data;
+ iov[0].iov_len = sizeof (data);
+
+ iov[1].iov_base = (void *) idx;
+ iov[1].iov_len = sizeof (idx);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_NRULES)].iov_base = &collate->nrules;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_NRULES)].iov_len = sizeof (u32_t);
+
+ table = (u32_t *) alloca (collate->nrules * sizeof (u32_t));
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_RULES)].iov_base = table;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_RULES)].iov_len
+ = collate->nrules * sizeof (u32_t);
+ /* Another trick here. Describing the collation method needs only a
+ few bits (3, to be exact). But the binary file should be
+ accessible by maschines with both endianesses and so we store both
+ information in the same word. */
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ table[cnt] = collate->rules[cnt] | SWAPU32 (collate->rules[cnt]);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_HASH_SIZE)].iov_base = &table_best;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_HASH_SIZE)].iov_len = sizeof (u32_t);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_HASH_LAYERS)].iov_base = &level_best;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_HASH_LAYERS)].iov_len = sizeof (u32_t);
+
+ entry_size = 1 + MAX (collate->nrules, 2);
+
+ table = (u32_t *) alloca (table_best * level_best * entry_size
+ * sizeof (table[0]));
+ memset (table, '\0', table_best * level_best * entry_size
+ * sizeof (table[0]));
+
+
+ /* Macros for inserting in output table. */
+#define ADD_VALUE(expr) \
+ do { \
+ u32_t to_write = (u32_t) expr; \
+ obstack_grow (&non_simple, &to_write, sizeof (to_write)); \
+ } while (0)
+
+#define ADD_ELEMENT(pelem, len) \
+ do { \
+ size_t cnt, idx; \
+ \
+ ADD_VALUE (len); \
+ \
+ wlen = wcslen (pelem->name); \
+ obstack_grow (&non_simple, pelem->name, (wlen + 1) * sizeof (u32_t)); \
+ \
+ idx = collate->nrules; \
+ for (cnt = 0; cnt < collate->nrules; ++cnt) \
+ { \
+ size_t disp; \
+ \
+ ADD_VALUE (pelem->ordering[cnt]); \
+ for (disp = 0; disp < pelem->ordering[cnt]; ++disp) \
+ ADD_VALUE (pelem->ordering[idx++]); \
+ } \
+ } while (0)
+
+#define ADD_FORWARD(pelem) \
+ do { \
+ /* We leave a reference in the main table and put all \
+ information in the table for the extended entries. */ \
+ element_t *runp; \
+ element_t *has_simple = NULL; \
+ size_t wlen; \
+ \
+ table[(level * table_best + slot) * entry_size + 1] \
+ = FORWARD_CHAR; \
+ table[(level * table_best + slot) * entry_size + 2] \
+ = obstack_object_size (&non_simple) / sizeof (u32_t); \
+ \
+ /* Here we have to construct the non-simple table entry. First \
+ compute the total length of this entry. */ \
+ for (runp = (pelem); runp != NULL; runp = runp->next) \
+ if (runp->ordering != NULL) \
+ { \
+ u32_t value; \
+ size_t cnt; \
+ \
+ value = 1 + wcslen (runp->name) + 1; \
+ \
+ for (cnt = 0; cnt < collate->nrules; ++cnt) \
+ /* We have to take care for entries without ordering \
+ information. While reading them they get inserted in the \
+ table and later not removed when something goes wrong with \
+ reading its weights. */ \
+ { \
+ value += 1 + runp->ordering[cnt]; \
+ \
+ if (runp->name[1] == L'\0') \
+ has_simple = runp; \
+ } \
+ \
+ ADD_ELEMENT (runp, value); \
+ } \
+ \
+ if (has_simple == NULL) \
+ { \
+ size_t idx, cnt; \
+ \
+ ADD_VALUE (collate->undefined_len + 1); \
+ \
+ /* Add the name. */ \
+ ADD_VALUE ((pelem)->name[0]); \
+ ADD_VALUE (0); \
+ \
+ idx = collate->nrules; \
+ for (cnt = 0; cnt < collate->nrules; ++cnt) \
+ { \
+ size_t disp; \
+ \
+ ADD_VALUE (collate->undefined.ordering[cnt]); \
+ for (disp = 0; disp < collate->undefined.ordering[cnt]; ++disp) \
+ { \
+ if (collate->undefined.ordering[idx] == ELLIPSIS_CHAR) \
+ ADD_VALUE ((pelem)->name[0]); \
+ else \
+ ADD_VALUE (collate->undefined.ordering[idx++]); \
+ ++idx; \
+ } \
+ } \
+ } \
+ } while (0)
+
+
+
+ /* Fill the table now. First we look for all the characters which
+ fit into one single byte. This speeds up the 8-bit string
+ functions. */
+ last = NULL;
+ while (iterate_table (&collate->result, &last, (const void **) &name,
+ &len, (void **) &pelem) >= 0)
+ if (pelem->name[0] <= 0xff)
+ {
+ /* We have a single byte name. Now we must distinguish
+ between entries in simple form (i.e., only one value per
+ weight and no collation element starting with the same
+ character) and those which are not. */
+ size_t slot = ((size_t) pelem->name[0]);
+ const size_t level = 0;
+
+ table[slot * entry_size] = pelem->name[0];
+
+ if (pelem->name[1] == L'\0' && pelem->next == NULL
+ && pelem->ordering_len == collate->nrules)
+ {
+ /* Yes, we have a simple one. Lucky us. */
+ size_t cnt;
+
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ table[slot * entry_size + 1 + cnt]
+ = pelem->ordering[collate->nrules + cnt];
+ }
+ else
+ ADD_FORWARD (pelem);
+ }
+
+ /* Now check for missing single byte entries. If one exist we fill
+ with the UNDEFINED entry. */
+ for (cnt = 0; cnt < 256; ++cnt)
+ /* The first weight is never 0 for existing entries. */
+ if (table[cnt * entry_size + 1] == 0)
+ {
+ /* We have to fill in the information from the UNDEFINED
+ entry. */
+ table[cnt * entry_size] = (u32_t) cnt;
+
+ if (collate->undefined.ordering_len == collate->nrules)
+ {
+ size_t inner;
+
+ for (inner = 0; inner < collate->nrules; ++inner)
+ if (collate->undefined.ordering[collate->nrules + inner]
+ == ELLIPSIS_CHAR)
+ table[cnt * entry_size + 1 + inner] = cnt;
+ else
+ table[cnt * entry_size + 1 + inner]
+ = collate->undefined.ordering[collate->nrules + inner];
+ }
+ else
+ {
+ if (undefined_offset != UINT_MAX)
+ {
+ table[cnt * entry_size + 1] = FORWARD_CHAR;
+ table[cnt * entry_size + 2] = undefined_offset;
+ }
+ else
+ {
+ const size_t slot = cnt;
+ const size_t level = 0;
+
+ ADD_FORWARD (&collate->undefined);
+ undefined_offset = table[cnt * entry_size + 2];
+ }
+ }
+ }
+
+ /* Now we are ready for inserting the whole rest. */
+ last = NULL;
+ while (iterate_table (&collate->result, &last, (const void **) &name,
+ &len, (void **) &pelem) >= 0)
+ if (pelem->name[0] > 0xff)
+ {
+ /* Find the position. */
+ size_t slot = ((size_t) pelem->name[0]) % table_best;
+ size_t level = 0;
+
+ while (table[(level * table_best + slot) * entry_size + 1] != 0)
+ ++level;
+ assert (level < level_best);
+
+ if (pelem->name[1] == L'\0' && pelem->next == NULL
+ && pelem->ordering_len == collate->nrules)
+ {
+ /* Again a simple entry. */
+ size_t inner;
+
+ for (inner = 0; inner < collate->nrules; ++inner)
+ table[(level * table_best + slot) * entry_size + 1 + inner]
+ = pelem->ordering[collate->nrules + inner];
+ }
+ else
+ ADD_FORWARD (pelem);
+ }
+
+ /* Add the UNDEFINED entry. */
+ {
+ /* Here we have to construct the non-simple table entry. */
+ size_t idx, cnt;
+
+ undefined_offset = obstack_object_size (&non_simple);
+
+ idx = collate->nrules;
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ {
+ size_t disp;
+
+ ADD_VALUE (collate->undefined.ordering[cnt]);
+ for (disp = 0; disp < collate->undefined.ordering[cnt]; ++disp)
+ ADD_VALUE (collate->undefined.ordering[idx++]);
+ }
+ }
+
+ /* Finish the extra block. */
+ extra_len = obstack_object_size (&non_simple);
+ extra = (u32_t *) obstack_finish (&non_simple);
+ assert ((extra_len % sizeof (u32_t)) == 0);
+
+ /* Now we have to build the two array for the other byte ordering. */
+ table2 = (u32_t *) alloca (table_best * level_best * entry_size
+ * sizeof (table[0]));
+ extra2 = (u32_t *) alloca (extra_len);
+
+ for (cnt = 0; cnt < table_best * level_best * entry_size; ++cnt)
+ table2[cnt] = SWAPU32 (table[cnt]);
+
+ for (cnt = 0; cnt < extra_len / sizeof (u32_t); ++cnt)
+ extra2[cnt] = SWAPU32 (extra2[cnt]);
+
+ /* Store table adresses and lengths. */
+#if __BYTE_ORDER == __BIG_ENDIAN
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EB)].iov_base = table;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EB)].iov_len
+ = table_best * level_best * entry_size * sizeof (table[0]);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EL)].iov_base = table2;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EL)].iov_len
+ = table_best * level_best * entry_size * sizeof (table[0]);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EB)].iov_base = extra;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EB)].iov_len = extra_len;
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EL)].iov_base = extra2;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EL)].iov_len = extra_len;
+#else
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EB)].iov_base = table2;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EB)].iov_len
+ = table_best * level_best * entry_size * sizeof (table[0]);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EL)].iov_base = table;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_TABLE_EL)].iov_len
+ = table_best * level_best * entry_size * sizeof (table[0]);
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EB)].iov_base = extra2;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EB)].iov_len = extra_len;
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EL)].iov_base = extra;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_EXTRA_EL)].iov_len = extra_len;
+#endif
+
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_UNDEFINED)].iov_base = &undefined_offset;
+ iov[2 + _NL_ITEM_INDEX (_NL_COLLATE_UNDEFINED)].iov_len = sizeof (u32_t);
+
+ /* Update idx array. */
+ idx[0] = iov[0].iov_len + iov[1].iov_len;
+ for (cnt = 1; cnt < nelems; ++cnt)
+ idx[cnt] = idx[cnt - 1] + iov[1 + cnt].iov_len;
+
+ write_locale_data (output_path, "LC_COLLATE", 2 + nelems, iov);
+}
+
+
+void
+collate_element_to (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ unsigned int value;
+ void *not_used;
+
+ if (collate->combine_token != NULL)
+ {
+ free ((void *) collate->combine_token);
+ collate->combine_token = NULL;
+ }
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+ if (value != ILLEGAL_CHAR_VALUE)
+ {
+ lr_error (lr, _("symbol for multicharacter collating element "
+ "`%.*s' duplicates symbolic name in charset"),
+ code->val.str.len, code->val.str.start);
+ return;
+ }
+
+ if (find_entry (&collate->elements, code->val.str.start, code->val.str.len,
+ &not_used) >= 0)
+ {
+ lr_error (lr, _("symbol for multicharacter collating element "
+ "`%.*s' duplicates other element definition"),
+ code->val.str.len, code->val.str.start);
+ return;
+ }
+
+ if (find_entry (&collate->elements, code->val.str.start, code->val.str.len,
+ &not_used) >= 0)
+ {
+ lr_error (lr, _("symbol for multicharacter collating element "
+ "`%.*s' duplicates symbol definition"),
+ code->val.str.len, code->val.str.start);
+ return;
+ }
+
+ collate->combine_token = code->val.str.start;
+ collate->combine_token_len = code->val.str.len;
+}
+
+
+void
+collate_element_from (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ element_t *elemp, *runp;
+
+ /* CODE is a string. */
+ elemp = (element_t *) obstack_alloc (&collate->element_mem,
+ sizeof (element_t));
+
+ /* We have to translate the string. It may contain <...> character
+ names. */
+ elemp->name = (wchar_t *) translate_string (code->val.str.start, charset);
+ elemp->this_weight = 0;
+ elemp->ordering = NULL;
+ elemp->ordering_len = 0;
+
+ free (code->val.str.start);
+
+ if (elemp->name == NULL)
+ {
+ /* At least one character in the string is not defined. We simply
+ do nothing. */
+ if (verbose)
+ lr_error (lr, _("\
+`from' string in collation element declaration contains unknown character"));
+ return;
+ }
+
+ if (elemp->name[0] == L'\0' || elemp->name[1] == L'\0')
+ {
+ lr_error (lr, _("illegal colltion element"));
+ return;
+ }
+
+ /* The entries in the linked lists of RESULT are sorting in
+ descending order. The order is important for the `strcoll' and
+ `wcscoll' functions. */
+ if (find_entry (&collate->result, elemp->name, sizeof (wchar_t),
+ (void *) &runp) >= 0)
+ {
+ /* We already have an entry with this key. Check whether it is
+ identical. */
+ element_t *prevp = NULL;
+ int cmpres;
+
+ do
+ {
+ cmpres = wcscmp (elemp->name, runp->name);
+ if (cmpres <= 0)
+ break;
+ prevp = runp;
+ }
+ while ((runp = runp->next) != NULL);
+
+ if (cmpres == 0)
+ lr_error (lr, _("duplicate collating element definition"));
+ else
+ {
+ elemp->next = runp;
+ if (prevp == NULL)
+ {
+ if (set_entry (&collate->result, elemp->name, sizeof (wchar_t),
+ elemp) < 0)
+ error (EXIT_FAILURE, 0,
+ _("\
+error while inserting collation element into hash table"));
+ }
+ else
+ prevp->next = elemp;
+ }
+ }
+ else
+ {
+ elemp->next = NULL;
+ if (insert_entry (&collate->result, elemp->name, sizeof (wchar_t), elemp)
+ < 0)
+ error (EXIT_FAILURE, errno, _("error while inserting to hash table"));
+ }
+
+ if (insert_entry (&collate->elements, collate->combine_token,
+ collate->combine_token_len, (void *) elemp) < 0)
+ lr_error (lr, _("cannot insert new collating symbol definition: %s"),
+ strerror (errno));
+}
+
+
+void
+collate_symbol (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ wchar_t value;
+ void *not_used;
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+ if (value != ILLEGAL_CHAR_VALUE)
+ {
+ lr_error (lr, _("symbol for multicharacter collating element "
+ "`%.*s' duplicates symbolic name in charset"),
+ code->val.str.len, code->val.str.start);
+ return;
+ }
+
+ if (find_entry (&collate->elements, code->val.str.start, code->val.str.len,
+ &not_used) >= 0)
+ {
+ lr_error (lr, _("symbol for multicharacter collating element "
+ "`%.*s' duplicates element definition"),
+ code->val.str.len, code->val.str.start);
+ return;
+ }
+
+ if (find_entry (&collate->symbols, code->val.str.start, code->val.str.len,
+ &not_used) >= 0)
+ {
+ lr_error (lr, _("symbol for multicharacter collating element "
+ "`%.*s' duplicates other symbol definition"),
+ code->val.str.len, code->val.str.start);
+ return;
+ }
+
+ if (insert_entry (&collate->symbols, code->val.str.start, code->val.str.len,
+ (void *) 0) < 0)
+ lr_error (lr, _("cannot insert new collating symbol definition: %s"),
+ strerror (errno));
+}
+
+
+void
+collate_new_order (struct linereader *lr, struct localedef_t *locale,
+ enum coll_sort_rule sort_rule)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+
+ if (collate->nrules >= collate->nrules_max)
+ {
+ collate->nrules_max *= 2;
+ collate->rules
+ = (enum coll_sort_rule *) xrealloc (collate->rules,
+ collate->nrules_max
+ * sizeof (enum coll_sort_rule));
+ }
+
+ collate->rules[collate->nrules++] = sort_rule;
+}
+
+
+void
+collate_build_arrays (struct linereader *lr, struct localedef_t *locale)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+
+ collate->rules
+ = (enum coll_sort_rule *) xrealloc (collate->rules,
+ collate->nrules
+ * sizeof (enum coll_sort_rule));
+
+ /* Allocate arrays for temporary weights. */
+ collate->weight_cnt = (int *) xmalloc (collate->nrules * sizeof (int));
+
+ /* Choose arbitrary start value for table size. */
+ collate->nweight_max = 5 * collate->nrules;
+ collate->weight = (int *) xmalloc (collate->nweight_max * sizeof (int));
+}
+
+
+int
+collate_order_elem (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ const wchar_t zero = L'\0';
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ int result = 0;
+ wchar_t value;
+ void *tmp;
+ int i;
+
+ switch (code->tok)
+ {
+ case tok_bsymbol:
+ /* We have a string to find in one of the three hashing tables. */
+ value = charset_find_value (charset, code->val.str.start,
+ code->val.str.len);
+ if (value != ILLEGAL_CHAR_VALUE)
+ {
+ element_t *lastp, *firstp;
+
+ collate->kind = character;
+
+ if (find_entry (&collate->result, &value, sizeof (wchar_t),
+ (void *) &firstp) < 0)
+ firstp = lastp = NULL;
+ else
+ {
+ /* The entry for the simple character is always found at
+ the end. */
+ lastp = firstp;
+ while (lastp->next != NULL)
+ lastp = lastp->next;
+
+ if (lastp->name[0] == value && lastp->name[1] == L'\0')
+ {
+ lr_error (lr, _("duplicate definition for character `%.*s'"),
+ code->val.str.len, code->val.str.start);
+ lr_ignore_rest (lr, 0);
+ result = -1;
+ break;
+ }
+ }
+
+ collate->current_element
+ = (element_t *) obstack_alloc (&collate->element_mem,
+ sizeof (element_t));
+
+ obstack_grow (&collate->element_mem, &value, sizeof (value));
+ obstack_grow (&collate->element_mem, &zero, sizeof (zero));
+
+ collate->current_element->name =
+ (const wchar_t *) obstack_finish (&collate->element_mem);
+
+ collate->current_element->this_weight = ++collate->order_cnt;
+
+ collate->current_element->next = NULL;
+
+ if (firstp == NULL)
+ {
+ if (insert_entry (&collate->result, &value, sizeof (wchar_t),
+ (void *) collate->current_element) < 0)
+ {
+ lr_error (lr, _("cannot insert collation element `%.*s'"),
+ code->val.str.len, code->val.str.start);
+ exit (4);
+ }
+ }
+ else
+ lastp->next = collate->current_element;
+ }
+ else if (find_entry (&collate->elements, code->val.str.start,
+ code->val.str.len, &tmp) >= 0)
+ {
+ collate->current_element = (element_t *) tmp;
+
+ if (collate->current_element->this_weight != 0)
+ {
+ lr_error (lr, _("\
+collation element `%.*s' appears more than once: ignore line"),
+ code->val.str.len, code->val.str.start);
+ lr_ignore_rest (lr, 0);
+ result = -1;
+ break;
+ }
+
+ collate->kind = element;
+ collate->current_element->this_weight = ++collate->order_cnt;
+ }
+ else if (find_entry (&collate->symbols, code->val.str.start,
+ code->val.str.len, &tmp) >= 0)
+ {
+ unsigned int order = ++collate->order_cnt;
+
+ if ((unsigned int) tmp != 0)
+ {
+ lr_error (lr, _("\
+collation symbol `.*s' appears more than once: ignore line"),
+ code->val.str.len, code->val.str.start);
+ lr_ignore_rest (lr, 0);
+ result = -1;
+ break;
+ }
+
+ collate->kind = symbol;
+
+ if (set_entry (&collate->symbols, code->val.str.start,
+ code->val.str.len, (void *) order) < 0)
+ {
+ lr_error (lr, _("cannot process order specification"));
+ exit (4);
+ }
+ }
+ else
+ {
+ if (verbose)
+ lr_error (lr, _("unknown symbol `%.*s': line ignored"),
+ code->val.str.len, code->val.str.start);
+ lr_ignore_rest (lr, 0);
+
+ result = -1;
+ }
+ break;
+
+ case tok_undefined:
+ collate->kind = undefined;
+ collate->current_element = &collate->undefined;
+ break;
+
+ case tok_ellipsis:
+ if (collate->was_ellipsis)
+ {
+ lr_error (lr, _("\
+two lines in a row containing `...' are not allowed"));
+ result = -1;
+ }
+ else if (collate->kind != character)
+ {
+ /* An ellipsis requires the previous line to be an
+ character definition. */
+ lr_error (lr, _("\
+line before ellipsis does not contain definition for character constant"));
+ lr_ignore_rest (lr, 0);
+ result = -1;
+ }
+ else
+ collate->kind = ellipsis;
+ break;
+
+ default:
+ assert (! "illegal token in `collate_order_elem'");
+ }
+
+ /* Now it's time to handle the ellipsis in the previous line. We do
+ this only when the last line contained an definition for an
+ character, the current line also defines an character, the
+ character code for the later is bigger than the former. */
+ if (collate->was_ellipsis)
+ {
+ if (collate->kind != character)
+ {
+ lr_error (lr, _("\
+line after ellipsis must contain character definition"));
+ lr_ignore_rest (lr, 0);
+ result = -1;
+ }
+ else if (collate->last_char > value)
+ {
+ lr_error (lr, _("end point of ellipsis range is bigger then start"));
+ lr_ignore_rest (lr, 0);
+ result = -1;
+ }
+ else
+ {
+ /* We can fill the arrays with the information we need. */
+ wchar_t name[2];
+ unsigned int *data;
+ size_t *ptr;
+ size_t cnt;
+
+ name[0] = collate->last_char + 1;
+ name[1] = L'\0';
+
+ data = (unsigned int *) alloca ((collate->nrules + collate->nweight)
+ * sizeof (unsigned int));
+ ptr = (size_t *) alloca (collate->nrules * sizeof (size_t));
+
+ if (data == NULL || ptr == NULL)
+ error (4, 0, _("memory exhausted"));
+
+ /* Prepare data. Because the characters covered by an
+ ellipsis all have equal values we prepare the data once
+ and only change the variable number (if there are any).
+ PTR[...] will point to the entries which will have to be
+ fixed during the output loop. */
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ {
+ data[cnt] = collate->weight_cnt[cnt];
+ ptr[cnt] = (cnt == 0
+ ? collate->nweight
+ : ptr[cnt - 1] + collate->weight_cnt[cnt - 1]);
+ }
+
+ for (cnt = 0; cnt < collate->nweight; ++cnt)
+ data[collate->nrules + cnt] = collate->weight[cnt];
+
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ if (data[ptr[cnt]] != ELLIPSIS_CHAR)
+ ptr[cnt] = 0;
+
+ while (name[0] <= value)
+ {
+ element_t *pelem;
+
+ pelem = (element_t *) obstack_alloc (&collate->element_mem,
+ sizeof (element_t));
+ if (pelem == NULL)
+ error (4, 0, _("memory exhausted"));
+
+ pelem->name
+ = (const wchar_t *) obstack_copy (&collate->element_mem,
+ name, 2 * sizeof (wchar_t));
+ pelem->this_weight = ++collate->order_cnt;
+
+ pelem->ordering_len = collate->nweight;
+ pelem->ordering
+ = (unsigned int *) obstack_copy (&collate->element_mem, data,
+ (collate->nrules
+ * pelem->ordering_len)
+ * sizeof (unsigned int));
+
+ /* `...' weights need to be adjusted. */
+ for (cnt = 0; cnt < collate->nrules; ++cnt)
+ if (ptr[cnt] != 0)
+ pelem->ordering[ptr[cnt]] = pelem->this_weight;
+
+ /* Insert new entry into result table. */
+ if (find_entry (&collate->result, name, sizeof (wchar_t),
+ (void *) &pelem->next) >= 0)
+ {
+ if (set_entry (&collate->result, name, sizeof (wchar_t),
+ (void *) pelem->next) < 0)
+ error (4, 0, _("cannot insert into result table"));
+ }
+ else
+ if (insert_entry (&collate->result, name, sizeof (wchar_t),
+ (void *) pelem->next) < 0)
+ error (4, 0, _("cannot insert into result table"));
+
+ /* Increment counter. */
+ ++name[0];
+ }
+ }
+ }
+
+ /* Reset counters for weights. */
+ collate->weight_idx = 0;
+ collate->nweight = 0;
+ for (i = 0; i < collate->nrules; ++i)
+ collate->weight_cnt[i] = 0;
+ collate->current_patch = NULL;
+
+ return result;
+}
+
+
+int
+collate_weight_bsymbol (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ unsigned int here_weight;
+ wchar_t value;
+ void *tmp;
+
+ assert (code->tok == tok_bsymbol);
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+ if (value != ILLEGAL_CHAR_VALUE)
+ {
+ element_t *runp;
+
+ if (find_entry (&collate->result, &value, sizeof (wchar_t),
+ (void *)&runp) < 0)
+ runp = NULL;
+
+ while (runp != NULL
+ && (runp->name[0] != value || runp->name[1] != L'\0'))
+ runp = runp->next;
+
+ here_weight = runp == NULL ? 0 : runp->this_weight;
+ }
+ else if (find_entry (&collate->elements, code->val.str.start,
+ code->val.str.len, &tmp) >= 0)
+ {
+ element_t *runp = (element_t *) tmp;
+
+ here_weight = runp->this_weight;
+ }
+ else if (find_entry (&collate->symbols, code->val.str.start,
+ code->val.str.len, &tmp) >= 0)
+ {
+ here_weight = (unsigned int) tmp;
+ }
+ else
+ {
+ if (verbose)
+ lr_error (lr, _("unknown symbol `%.*s': line ignored"),
+ code->val.str.len, code->val.str.start);
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+
+ /* When we currently work on a collation symbol we do not expect any
+ weight. */
+ if (collate->kind == symbol)
+ {
+ lr_error (lr, _("\
+specification of sorting weight for collation symbol does not make sense"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+
+ /* Add to the current collection of weights. */
+ if (collate->nweight >= collate->nweight_max)
+ {
+ collate->nweight_max *= 2;
+ collate->weight = (unsigned int *) xrealloc (collate->weight,
+ collate->nweight_max);
+ }
+
+ /* If the weight is currently not known, we remember to patch the
+ resulting tables. */
+ if (here_weight == 0)
+ {
+ patch_t *newp;
+
+ newp = (patch_t *) obstack_alloc (&collate->element_mem,
+ sizeof (patch_t));
+ newp->fname = lr->fname;
+ newp->lineno = lr->lineno;
+ newp->token = (const char *) obstack_copy0 (&collate->element_mem,
+ code->val.str.start,
+ code->val.str.len);
+ newp->where.idx = collate->nweight++;
+ newp->next = collate->current_patch;
+ collate->current_patch = newp;
+ }
+ else
+ collate->weight[collate->nweight++] = here_weight;
+ ++collate->weight_cnt[collate->weight_idx];
+
+ return 0;
+}
+
+
+int
+collate_next_weight (struct linereader *lr, struct localedef_t *locale)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+
+ if (collate->kind == symbol)
+ {
+ lr_error (lr, _("\
+specification of sorting weight for collation symbol does not make sense"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+
+ ++collate->weight_idx;
+ if (collate->weight_idx >= collate->nrules)
+ {
+ lr_error (lr, _("too many weights"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+collate_simple_weight (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ unsigned int value = 0;
+
+ /* There current tokens can be `IGNORE', `...', or a string. */
+ switch (code->tok)
+ {
+ case tok_ignore:
+ /* This token is allowed in all situations. */
+ value = IGNORE_CHAR;
+ break;
+
+ case tok_ellipsis:
+ /* The ellipsis is only allowed for the `...' or `UNDEFINED'
+ entry. */
+ if (collate->kind != ellipsis && collate->kind != undefined)
+ {
+ lr_error (lr, _("\
+`...' must only be used in `...' and `UNDEFINED' entries"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+ value = ELLIPSIS_CHAR;
+ break;
+
+ case tok_string:
+ /* This can become difficult. We have to get the weights which
+ correspind the the single wide chars in the string. But some
+ of the `chars' might not be real characters, but collation
+ elements or symbols. And so the string decoder might have
+ signaled errors. The string at this point is not translated.
+ I.e., all <...> sequences are still there. */
+ {
+ char *runp = code->val.str.start;
+ void *tmp;
+
+ while (*runp != '\0')
+ {
+ char *startp = (char *) runp;
+ char *putp = (char *) runp;
+ wchar_t wch;
+
+ /* Lookup weight for char and store it. */
+ if (*runp == '<')
+ {
+ while (*++runp != '\0' && *runp != '>')
+ {
+ if (*runp == lr->escape_char)
+ if (*++runp == '\0')
+ {
+ lr_error (lr, _("unterminated weight name"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+ *putp++ = *runp;
+ }
+ if (*runp == '>')
+ ++runp;
+
+ if (putp == startp)
+ {
+ lr_error (lr, _("empty weight name: line ignored"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+
+ wch = charset_find_value (charset, startp, putp - startp);
+ if (wch != ILLEGAL_CHAR_VALUE)
+ {
+ element_t *pelem;
+
+ if (find_entry (&collate->result, &wch, sizeof (wchar_t),
+ (void *)&pelem) < 0)
+ pelem = NULL;
+
+ while (pelem != NULL
+ && (pelem->name[0] != wch
+ || pelem->name[1] != L'\0'))
+ pelem = pelem->next;
+
+ value = pelem == NULL ? 0 : pelem->this_weight;
+ }
+ else if (find_entry (&collate->elements, startp, putp - startp,
+ &tmp) >= 0)
+ {
+ element_t *pelem = (element_t *) tmp;
+
+ value = pelem->this_weight;
+ }
+ else if (find_entry (&collate->symbols, startp, putp - startp,
+ &tmp) >= 0)
+ {
+ value = (unsigned int) tmp;
+ }
+ else
+ {
+ if (verbose)
+ lr_error (lr, _("unknown symbol `%.*s': line ignored"),
+ putp - startp, startp);
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+ }
+ else
+ {
+ element_t *wp;
+ wchar_t wch;
+
+ if (*runp == lr->escape_char)
+ {
+ static char digits[] = "0123456789abcdef";
+ char *dp;
+ int base;
+
+ ++runp;
+ if (tolower (*runp) == 'x')
+ {
+ ++runp;
+ base = 16;
+ }
+ else if (tolower (*runp) == 'd')
+ {
+ ++runp;
+ base = 10;
+ }
+ else
+ base = 8;
+
+ dp = strchr (digits, tolower (*runp));
+ if (dp == NULL || (dp - digits) >= base)
+ {
+ illegal_char:
+ lr_error (lr, _("\
+illegal character constant in string"));
+ lr_ignore_rest (lr, 0);
+ return -1;
+ }
+ wch = dp - digits;
+ ++runp;
+
+ dp = strchr (digits, tolower (*runp));
+ if (dp == NULL || (dp - digits) >= base)
+ goto illegal_char;
+ wch *= base;
+ wch += dp - digits;
+ ++runp;
+
+ if (base != 16)
+ {
+ dp = strchr (digits, tolower (*runp));
+ if (dp != NULL && (dp - digits < base))
+ {
+ wch *= base;
+ wch += dp - digits;
+ ++runp;
+ }
+ }
+ }
+ else
+ wch = (wchar_t) *runp++;
+
+ /* Lookup the weight for WCH. */
+ if (find_entry (&collate->result, &wch, sizeof (wch),
+ (void *)&wp) < 0)
+ wp = NULL;
+
+ while (wp != NULL
+ && (wp->name[0] != wch || wp->name[1] != L'\0'))
+ wp = wp->next;
+
+ value = wp == NULL ? 0 : wp->this_weight;
+
+ /* To get the correct name for the error message. */
+ putp = runp;
+
+ /**************************************************\
+ |* I know here is something wrong. Characters in *|
+ |* the string which are not in the <...> form *|
+ |* cannot be declared forward for now!!! *|
+ \**************************************************/
+ }
+
+ /* Store in weight array. */
+ if (collate->nweight >= collate->nweight_max)
+ {
+ collate->nweight_max *= 2;
+ collate->weight
+ = (unsigned int *) xrealloc (collate->weight,
+ collate->nweight_max);
+ }
+
+ if (value == 0)
+ {
+ patch_t *newp;
+
+ newp = (patch_t *) obstack_alloc (&collate->element_mem,
+ sizeof (patch_t));
+ newp->fname = lr->fname;
+ newp->lineno = lr->lineno;
+ newp->token
+ = (const char *) obstack_copy0 (&collate->element_mem,
+ startp, putp - startp);
+ newp->where.idx = collate->nweight++;
+ newp->next = collate->current_patch;
+ collate->current_patch = newp;
+ }
+ else
+ collate->weight[collate->nweight++] = value;
+ ++collate->weight_cnt[collate->weight_idx];
+ }
+ }
+ return 0;
+
+ default:
+ assert (! "should not happen");
+ }
+
+
+ if (collate->nweight >= collate->nweight_max)
+ {
+ collate->nweight_max *= 2;
+ collate->weight = (unsigned int *) xrealloc (collate->weight,
+ collate->nweight_max);
+ }
+
+ collate->weight[collate->nweight++] = value;
+ ++collate->weight_cnt[collate->weight_idx];
+
+ return 0;
+}
+
+
+void
+collate_end_weight (struct linereader *lr, struct localedef_t *locale)
+{
+ struct locale_collate_t *collate = locale->categories[LC_COLLATE].collate;
+ element_t *pelem = collate->current_element;
+
+ if (collate->kind == symbol)
+ {
+ /* We don't have to do anything. */
+ collate->was_ellipsis = 0;
+ return;
+ }
+
+ if (collate->kind == ellipsis)
+ {
+ /* Before the next line is processed the ellipsis is handled. */
+ collate->was_ellipsis = 1;
+ return;
+ }
+
+ assert (collate->kind == character || collate->kind == element
+ || collate->kind == undefined);
+
+ /* Fill in the missing weights. */
+ while (++collate->weight_idx < collate->nrules)
+ {
+ collate->weight[collate->nweight++] = pelem->this_weight;
+ ++collate->weight_cnt[collate->weight_idx];
+ }
+
+ /* Now we know how many ordering weights the current
+ character/element has. Allocate room in the element structure
+ and copy information. */
+ pelem->ordering_len = collate->nweight;
+
+ /* First we write an array with the number of values for each
+ weight. */
+ obstack_grow (&collate->element_mem, collate->weight_cnt,
+ collate->nrules * sizeof (unsigned int));
+
+ /* Now the weights itselves. */
+ obstack_grow (&collate->element_mem, collate->weight,
+ collate->nweight * sizeof (unsigned int));
+
+ /* Get result. */
+ pelem->ordering = obstack_finish (&collate->element_mem);
+
+ /* Now we handle the "patches". */
+ while (collate->current_patch != NULL)
+ {
+ patch_t *this_patch;
+
+ this_patch = collate->current_patch;
+
+ this_patch->where.pos = &pelem->ordering[collate->nrules
+ + this_patch->where.idx];
+
+ collate->current_patch = this_patch->next;
+ this_patch->next = collate->all_patches;
+ collate->all_patches = this_patch;
+ }
+
+ /* Set information for next round. */
+ collate->was_ellipsis = 0;
+ if (collate->kind != undefined)
+ collate->last_char = pelem->name[0];
+}
diff --git a/locale/programs/ld-ctype.c b/locale/programs/ld-ctype.c
new file mode 100644
index 0000000000..c1cc8e53b8
--- /dev/null
+++ b/locale/programs/ld-ctype.c
@@ -0,0 +1,1310 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <endian.h>
+#include <limits.h>
+#include <string.h>
+
+#include "locales.h"
+#include "localeinfo.h"
+#include "langinfo.h"
+#include "locfile-token.h"
+#include "stringtrans.h"
+
+/* Uncomment the following line in the production version. */
+/* define NDEBUG 1 */
+#include <assert.h>
+
+
+void *xmalloc (size_t __n);
+void *xcalloc (size_t __n, size_t __s);
+void *xrealloc (void *__ptr, size_t __n);
+
+
+/* The bit used for representing a special class. */
+#define BITPOS(class) ((class) - tok_upper)
+#define BIT(class) (1 << BITPOS (class))
+
+#define ELEM(ctype, collection, idx, value) \
+ *find_idx (ctype, &ctype->collection idx, &ctype->collection##_max idx, \
+ &ctype->collection##_act idx, value)
+
+#define SWAPU32(w) \
+ (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
+
+#define SWAPU16(w) \
+ ((((w) >> 8) & 0xff) | (((w) & 0xff) << 8))
+
+
+/* To be compatible with former implementations we for now restrict
+ the number of bits for character classes to 16. When compatibility
+ is not necessary anymore increase the number to 32. */
+#define char_class_t u16_t
+#define CHAR_CLASS_TRANS SWAPU16
+#define char_class32_t u32_t
+#define CHAR_CLASS32_TRANS SWAPU32
+
+
+/* The real definition of the struct for the LC_CTYPE locale. */
+struct locale_ctype_t
+{
+ unsigned int *charnames;
+ size_t charnames_max;
+ size_t charnames_act;
+
+ /* We will allow up to 8 * sizeof(u32_t) - 1 character classes. */
+#define MAX_NR_CHARCLASS (8 * sizeof (u32_t) - 1)
+ int nr_charclass;
+ const char *classnames[MAX_NR_CHARCLASS];
+ unsigned long int current_class_mask;
+ unsigned int last_class_char;
+ u32_t *class_collection;
+ size_t class_collection_max;
+ size_t class_collection_act;
+ unsigned long int class_done;
+
+ /* If the following number ever turns out to be too small simply
+ increase it. But I doubt it will. --drepper@gnu */
+#define MAX_NR_CHARMAP 16
+ const char *mapnames[MAX_NR_CHARMAP];
+ u32_t *map_collection[MAX_NR_CHARMAP];
+ unsigned int map_collection_max[MAX_NR_CHARMAP];
+ unsigned int map_collection_act[MAX_NR_CHARMAP];
+ size_t map_collection_nr;
+ size_t last_map_idx;
+ unsigned int from_map_char;
+ int toupper_done;
+ int tolower_done;
+
+ /* The arrays for the binary representation. */
+ u32_t plane_size;
+ u32_t plane_cnt;
+ char_class_t *ctype_b;
+ char_class32_t *ctype32_b;
+ u32_t *names_el;
+ u32_t *names_eb;
+ u32_t **map_eb;
+ u32_t **map_el;
+ u32_t *class_name_ptr;
+ u32_t *map_name_ptr;
+};
+
+
+/* Prototypes for local functions. */
+static void ctype_class_newP (struct linereader *lr,
+ struct locale_ctype_t *ctype, const char *name);
+static void ctype_map_newP (struct linereader *lr,
+ struct locale_ctype_t *ctype,
+ const char *name, struct charset_t *charset);
+static u32_t *find_idx (struct locale_ctype_t *ctype, u32_t **table,
+ size_t *max, size_t *act, unsigned int idx);
+static void set_class_defaults (struct locale_ctype_t *ctype,
+ struct charset_t *charset);
+static void allocate_arrays (struct locale_ctype_t *ctype);
+
+
+void
+ctype_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *charset)
+{
+ unsigned int cnt;
+ struct locale_ctype_t *ctype;
+
+ /* It is important that we always use UCS1 encoding for strings now. */
+ encoding_method = ENC_UCS1;
+
+ /* Allocate the needed room. */
+ locale->categories[LC_CTYPE].ctype = ctype =
+ (struct locale_ctype_t *) xmalloc (sizeof (struct locale_ctype_t));
+
+ /* We have no names seen yet. */
+ ctype->charnames_max = charset->mb_cur_max == 1 ? 256 : 512;
+ ctype->charnames =
+ (unsigned int *) xmalloc (ctype->charnames_max * sizeof (unsigned int));
+ for (cnt = 0; cnt < 256; ++cnt)
+ ctype->charnames[cnt] = cnt;
+ ctype->charnames_act = 256;
+
+ /* Fill character class information. */
+ ctype->nr_charclass = 0;
+ ctype->current_class_mask = 0;
+ ctype->last_class_char = ILLEGAL_CHAR_VALUE;
+ /* The order of the following instructions determines the bit
+ positions! */
+ ctype_class_newP (lr, ctype, "upper");
+ ctype_class_newP (lr, ctype, "lower");
+ ctype_class_newP (lr, ctype, "alpha");
+ ctype_class_newP (lr, ctype, "digit");
+ ctype_class_newP (lr, ctype, "xdigit");
+ ctype_class_newP (lr, ctype, "space");
+ ctype_class_newP (lr, ctype, "print");
+ ctype_class_newP (lr, ctype, "graph");
+ ctype_class_newP (lr, ctype, "blank");
+ ctype_class_newP (lr, ctype, "cntrl");
+ ctype_class_newP (lr, ctype, "punct");
+ ctype_class_newP (lr, ctype, "alnum");
+
+ ctype->class_collection_max = charset->mb_cur_max == 1 ? 256 : 512;
+ ctype->class_collection = (u32_t *) xmalloc (sizeof (unsigned long int)
+ * ctype->class_collection_max);
+ memset (ctype->class_collection, '\0',
+ sizeof (unsigned long int) * ctype->class_collection_max);
+ ctype->class_collection_act = 256;
+
+ /* Fill character map information. */
+ ctype->map_collection_nr = 0;
+ ctype->last_map_idx = MAX_NR_CHARMAP;
+ ctype->from_map_char = ILLEGAL_CHAR_VALUE;
+ ctype_map_newP (lr, ctype, "toupper", charset);
+ ctype_map_newP (lr, ctype, "tolower", charset);
+
+ /* Fill first 256 entries in `toupper' and `tolower' arrays. */
+ for (cnt = 0; cnt < 256; ++cnt)
+ {
+ ctype->map_collection[0][cnt] = cnt;
+ ctype->map_collection[1][cnt] = cnt;
+ }
+}
+
+
+void
+ctype_finish (struct localedef_t *locale, struct charset_t *charset)
+{
+ /* See POSIX.2, table 2-6 for the meaning of the following table. */
+#define NCLASS 12
+ 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 */
+ { "upper", "--MX-XDDXXX-" },
+ { "lower", "--MX-XDDXXX-" },
+ { "alpha", "---X-XDDXXX-" },
+ { "digit", "XXX--XDDXXX-" },
+ { "xdigit", "-----XDDXXX-" },
+ { "space", "XXXXX------X" },
+ { "print", "---------X--" },
+ { "graph", "---------X--" },
+ { "blank", "XXXXXM-----X" },
+ { "cntrl", "XXXXX-XX--XX" },
+ { "punct", "XXXXX-DD-X-X" },
+ { "alnum", "-----XDDXXX-" }
+ };
+ size_t cnt;
+ int cls1, cls2;
+ unsigned int space_value;
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+
+ /* Set default value for classes not specified. */
+ set_class_defaults (ctype, charset);
+
+ /* Check according to table. */
+ for (cnt = 0; cnt < ctype->class_collection_max; ++cnt)
+ {
+ unsigned long int tmp;
+
+ tmp = ctype->class_collection[cnt];
+ if (tmp == 0)
+ continue;
+
+ for (cls1 = 0; cls1 < NCLASS; ++cls1)
+ if ((tmp & (1 << cls1)) != 0)
+ for (cls2 = 0; cls2 < NCLASS; ++cls2)
+ if (valid_table[cls1].allow[cls2] != '-')
+ {
+ int eq = (tmp & (1 << cls2)) != 0;
+ switch (valid_table[cls1].allow[cls2])
+ {
+ case 'M':
+ if (!eq)
+ {
+ char buf[17];
+ char *cp = buf;
+ unsigned int value;
+
+ value = ctype->charnames[cnt];
+
+ if ((value & 0xff000000) != 0)
+ cp += sprintf (cp, "\\%o", (value >> 24) & 0xff);
+ if ((value & 0xffff0000) != 0)
+ cp += sprintf (cp, "\\%o", (value >> 16) & 0xff);
+ if ((value & 0xffffff00) != 0)
+ cp += sprintf (cp, "\\%o", (value >> 8) & 0xff);
+ sprintf (cp, "\\%o", value & 0xff);
+
+ error (0, 0, _("\
+character %s'%s' in class `%s' must be in class `%s'"), value > 256 ? "L" : "",
+ cp, valid_table[cls1].name,
+ valid_table[cls2].name);
+ }
+ break;
+
+ case 'X':
+ if (eq)
+ {
+ char buf[17];
+ char *cp = buf;
+ unsigned int value;
+
+ value = ctype->charnames[cnt];
+
+ if ((value & 0xff000000) != 0)
+ cp += sprintf (cp, "\\%o", value >> 24);
+ if ((value & 0xffff0000) != 0)
+ cp += sprintf (cp, "\\%o", (value >> 16) & 0xff);
+ if ((value & 0xffffff00) != 0)
+ cp += sprintf (cp, "\\%o", (value >> 8) & 0xff);
+ sprintf (cp, "\\%o", value & 0xff);
+
+ error (0, 0, _("\
+character %s'%s' in class `%s' must not be in class `%s'"),
+ value > 256 ? "L" : "", cp,
+ valid_table[cls1].name, valid_table[cls2].name);
+ }
+ break;
+
+ case 'D':
+ ctype->class_collection[cnt] |= 1 << cls2;
+ break;
+
+ default:
+ error (5, 0, _("internal error in %s, line %u"),
+ __FUNCTION__, __LINE__);
+ }
+ }
+ }
+
+ /* ... and now test <SP> as a special case. */
+ space_value = charset_find_value (charset, "SP", 2);
+ if (space_value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("character <SP> not defined in character map"));
+ else if ((cnt = BITPOS (tok_space),
+ (ELEM (ctype, class_collection, , space_value)
+ & BIT (tok_space)) == 0)
+ || (cnt = BITPOS (tok_blank),
+ (ELEM (ctype, class_collection, , space_value)
+ & BIT (tok_blank)) == 0))
+ error (0, 0, _("<SP> character not in class `%s'"),
+ valid_table[cnt].name);
+ else if ((cnt = BITPOS (tok_punct),
+ (ELEM (ctype, class_collection, , space_value)
+ & BIT (tok_punct)) != 0)
+ || (cnt = BITPOS (tok_graph),
+ (ELEM (ctype, class_collection, , space_value)
+ & BIT (tok_graph))
+ != 0))
+ error (0, 0, _("<SP> character must not be in class `%s'"),
+ valid_table[cnt].name);
+ else
+ ELEM (ctype, class_collection, , space_value) |= BIT (tok_print);
+}
+
+
+void
+ctype_output (struct localedef_t *locale, const char *output_path)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ const size_t nelems = (_NL_ITEM_INDEX (_NL_NUM_LC_CTYPE)
+ + 2 * (ctype->map_collection_nr - 2));
+ struct iovec iov[2 + nelems + (ctype->nr_charclass + 1)
+ + (ctype->map_collection_nr + 1)];
+ struct locale_file data;
+ u32_t idx[nelems];
+ size_t elem, cnt, offset;
+
+
+ if ((locale->binary & (1 << LC_CTYPE)) != 0)
+ {
+ iov[0].iov_base = ctype;
+ iov[0].iov_len = locale->len[LC_CTYPE];
+
+ write_locale_data (output_path, "LC_CTYPE", 1, iov);
+
+ return;
+ }
+
+
+ /* Now prepare the output: Find the sizes of the table we can use. */
+ allocate_arrays (ctype);
+
+ data.magic = LIMAGIC (LC_CTYPE);
+ data.n = nelems;
+ iov[0].iov_base = (void *) &data;
+ iov[0].iov_len = sizeof (data);
+
+ iov[1].iov_base = (void *) idx;
+ iov[1].iov_len = sizeof (idx);
+
+ idx[0] = iov[0].iov_len + iov[1].iov_len;
+ offset = 0;
+
+ for (elem = 0; elem < nelems; ++elem)
+ {
+ if (elem < _NL_ITEM_INDEX (_NL_NUM_LC_CTYPE))
+ switch (elem)
+ {
+#define CTYPE_DATA(name, base, len) \
+ case _NL_ITEM_INDEX (name): \
+ iov[2 + elem].iov_base = base; \
+ iov[2 + elem].iov_len = len; \
+ break
+
+ CTYPE_DATA (_NL_CTYPE_CLASS,
+ ctype->ctype_b,
+ (256 + 128) * sizeof (char_class_t));
+
+ CTYPE_DATA (_NL_CTYPE_TOUPPER_EB,
+ ctype->map_eb[0],
+ (ctype->plane_size * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+ CTYPE_DATA (_NL_CTYPE_TOLOWER_EB,
+ ctype->map_eb[1],
+ (ctype->plane_size * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+
+ CTYPE_DATA (_NL_CTYPE_TOUPPER_EL,
+ ctype->map_el[0],
+ (ctype->plane_size * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+ CTYPE_DATA (_NL_CTYPE_TOLOWER_EL,
+ ctype->map_el[1],
+ (ctype->plane_size * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+
+ CTYPE_DATA (_NL_CTYPE_CLASS32,
+ ctype->ctype32_b,
+ (ctype->plane_size * ctype->plane_cnt
+ * sizeof (char_class32_t)));
+
+ CTYPE_DATA (_NL_CTYPE_NAMES_EB,
+ ctype->names_eb,
+ ctype->plane_size * ctype->plane_cnt * sizeof (u32_t));
+ CTYPE_DATA (_NL_CTYPE_NAMES_EL,
+ ctype->names_el,
+ ctype->plane_size * ctype->plane_cnt * sizeof (u32_t));
+
+ CTYPE_DATA (_NL_CTYPE_HASH_SIZE,
+ &ctype->plane_size, sizeof (u32_t));
+ CTYPE_DATA (_NL_CTYPE_HASH_LAYERS,
+ &ctype->plane_cnt, sizeof (u32_t));
+
+ CTYPE_DATA (_NL_CTYPE_CLASS_NAMES,
+ ctype->class_name_ptr,
+ ctype->nr_charclass * sizeof (u32_t));
+ CTYPE_DATA (_NL_CTYPE_MAP_NAMES,
+ ctype->map_name_ptr,
+ ctype->map_collection_nr * sizeof (u32_t));
+
+ CTYPE_DATA (_NL_CTYPE_WIDTH,
+ NULL, 0); /* Not yet implemented. */
+
+ default:
+ assert (! "unknown CTYPE element");
+ }
+ else
+ {
+ /* Handle extra maps. */
+ size_t nr = (elem - _NL_ITEM_INDEX (_NL_NUM_LC_CTYPE)) >> 1;
+
+ if (((elem - _NL_ITEM_INDEX (_NL_NUM_LC_CTYPE)) & 1) == 0)
+ iov[2 + elem].iov_base = ctype->map_eb[nr];
+ else
+ iov[2 + elem].iov_base = ctype->map_el[nr];
+
+ iov[2 + elem].iov_len = ((ctype->plane_size * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+ }
+
+ if (elem + 1 < nelems)
+ idx[elem + 1] = idx[elem] + iov[2 + elem].iov_len;
+ }
+
+ offset = idx[elem - 1] + iov[2 + elem - 1].iov_len;
+
+ /* The class name array. */
+ for (cnt = 0; cnt < ctype->nr_charclass; ++cnt, ++elem)
+ {
+ iov[2 + elem].iov_base = (void *) ctype->classnames[cnt];
+ iov[2 + elem].iov_len = strlen (ctype->classnames[cnt]) + 1;
+
+ ctype->class_name_ptr[cnt] = offset;
+ offset += iov[2 + elem].iov_len;
+ }
+ iov[2 + elem].iov_base = (void *) "";
+ iov[2 + elem].iov_len = 1;
+ ++elem;
+
+ /* The map name array. */
+ for (cnt = 0; cnt < ctype->map_collection_nr; ++cnt, ++elem)
+ {
+ iov[2 + elem].iov_base = (void *) ctype->mapnames[cnt];
+ iov[2 + elem].iov_len = strlen (ctype->mapnames[cnt]) + 1;
+
+ ctype->map_name_ptr[cnt] = offset;
+ offset += iov[2 + elem].iov_len;
+ }
+ iov[2 + elem].iov_base = (void *) "";
+ iov[2 + elem].iov_len = 1;
+ ++elem;
+
+ assert (elem == nelems + ctype->nr_charclass + ctype->map_collection_nr + 2);
+
+ write_locale_data (output_path, "LC_CTYPE", 2 + elem, iov);
+}
+
+
+/* Character class handling. */
+void
+ctype_class_new (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, struct token *code,
+ struct charset_t *charset)
+{
+ ctype_class_newP (lr, locale->categories[LC_CTYPE].ctype,
+ code->val.str.start);
+}
+
+
+int
+ctype_is_charclass (struct linereader *lr, struct localedef_t *locale,
+ const char *name)
+{
+ int cnt;
+
+ for (cnt = 0; cnt < locale->categories[LC_CTYPE].ctype->nr_charclass; ++cnt)
+ if (strcmp (name, locale->categories[LC_CTYPE].ctype->classnames[cnt])
+ == 0)
+ return 1;
+
+ return 0;
+}
+
+
+void
+ctype_class_start (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, const char *str,
+ struct charset_t *charset)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ int cnt;
+
+ switch (tok)
+ {
+ case tok_upper:
+ str = "upper";
+ break;
+ case tok_lower:
+ str = "lower";
+ break;
+ case tok_alpha:
+ str = "alpha";
+ break;
+ case tok_digit:
+ str = "digit";
+ break;
+ case tok_xdigit:
+ str = "xdigit";
+ break;
+ case tok_space:
+ str = "space";
+ break;
+ case tok_print:
+ str = "print";
+ break;
+ case tok_graph:
+ str = "graph";
+ break;
+ case tok_blank:
+ str = "blank";
+ break;
+ case tok_cntrl:
+ str = "cntrl";
+ break;
+ case tok_punct:
+ str = "punct";
+ break;
+ case tok_alnum:
+ str = "alnum";
+ break;
+ case tok_ident:
+ break;
+ default:
+ assert (! "illegal token as class name: should not happen");
+ }
+
+ for (cnt = 0; cnt < ctype->nr_charclass; ++cnt)
+ if (strcmp (str, ctype->classnames[cnt]) == 0)
+ break;
+
+ if (cnt >= ctype->nr_charclass)
+ assert (! "unknown class in class definition: should not happen");
+
+ ctype->class_done |= BIT (tok);
+
+ ctype->current_class_mask = 1 << cnt;
+ ctype->last_class_char = ILLEGAL_CHAR_VALUE;
+}
+
+
+void
+ctype_class_from (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ unsigned int value;
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+
+ ctype->last_class_char = value;
+
+ if (value == ILLEGAL_CHAR_VALUE)
+ /* In the LC_CTYPE category it is no error when a character is
+ not found. This has to be ignored silently. */
+ return;
+
+ *find_idx (ctype, &ctype->class_collection, &ctype->class_collection_max,
+ &ctype->class_collection_act, value)
+ |= ctype->current_class_mask;
+}
+
+
+void
+ctype_class_to (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ unsigned int value, cnt;
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+
+ assert (value >= ctype->last_class_char);
+
+ for (cnt = ctype->last_class_char + 1; cnt <= value; ++cnt)
+ *find_idx (ctype, &ctype->class_collection, &ctype->class_collection_max,
+ &ctype->class_collection_act, cnt)
+ |= ctype->current_class_mask;
+
+ ctype->last_class_char = ILLEGAL_CHAR_VALUE;
+}
+
+
+void
+ctype_class_end (struct linereader *lr, struct localedef_t *locale)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+
+ /* We have no special actions to perform here. */
+ ctype->current_class_mask = 0;
+ ctype->last_class_char = ILLEGAL_CHAR_VALUE;
+}
+
+
+/* Character map handling. */
+void
+ctype_map_new (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, struct token *code,
+ struct charset_t *charset)
+{
+ ctype_map_newP (lr, locale->categories[LC_CTYPE].ctype,
+ code->val.str.start, charset);
+}
+
+
+int
+ctype_is_charmap (struct linereader *lr, struct localedef_t *locale,
+ const char *name)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ size_t cnt;
+
+ for (cnt = 0; cnt < ctype->map_collection_nr; ++cnt)
+ if (strcmp (name, ctype->mapnames[cnt]) == 0)
+ return 1;
+
+ return 0;
+}
+
+
+void
+ctype_map_start (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, const char *name, struct charset_t *charset)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ size_t cnt;
+
+ switch (tok)
+ {
+ case tok_toupper:
+ ctype->toupper_done = 1;
+ name = "toupper";
+ break;
+ case tok_tolower:
+ ctype->tolower_done = 1;
+ name = "tolower";
+ break;
+ case tok_ident:
+ break;
+ default:
+ assert (! "unknown token in category `LC_CTYPE' should not happen");
+ }
+
+ for (cnt = 0; cnt < ctype->map_collection_nr; ++cnt)
+ if (strcmp (name, ctype->mapnames[cnt]) == 0)
+ break;
+
+ if (cnt == ctype->map_collection_nr)
+ assert (! "unknown token in category `LC_CTYPE' should not happen");
+
+ ctype->last_map_idx = cnt;
+ ctype->from_map_char = ILLEGAL_CHAR_VALUE;
+}
+
+
+void
+ctype_map_from (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ unsigned int value;
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+
+ if (value == ILLEGAL_CHAR_VALUE)
+ /* In the LC_CTYPE category it is no error when a character is
+ not found. This has to be ignored silently. */
+ return;
+
+ assert (ctype->last_map_idx < ctype->map_collection_nr);
+
+ ctype->from_map_char = value;
+}
+
+
+void
+ctype_map_to (struct linereader *lr, struct localedef_t *locale,
+ struct token *code, struct charset_t *charset)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+ unsigned int value;
+
+ value = charset_find_value (charset, code->val.str.start, code->val.str.len);
+
+ if (ctype->from_map_char == ILLEGAL_CHAR_VALUE
+ || value == ILLEGAL_CHAR_VALUE)
+ {
+ /* In the LC_CTYPE category it is no error when a character is
+ not found. This has to be ignored silently. */
+ ctype->from_map_char = ILLEGAL_CHAR_VALUE;
+ return;
+ }
+
+ *find_idx (ctype, &ctype->map_collection[ctype->last_map_idx],
+ &ctype->map_collection_max[ctype->last_map_idx],
+ &ctype->map_collection_act[ctype->last_map_idx],
+ ctype->from_map_char) = value;
+
+ ctype->from_map_char = ILLEGAL_CHAR_VALUE;
+}
+
+
+void
+ctype_map_end (struct linereader *lr, struct localedef_t *locale)
+{
+ struct locale_ctype_t *ctype = locale->categories[LC_CTYPE].ctype;
+
+ ctype->last_map_idx = MAX_NR_CHARMAP;
+ ctype->from_map_char = ILLEGAL_CHAR_VALUE;
+}
+
+
+/* Local functions. */
+static void
+ctype_class_newP (struct linereader *lr, struct locale_ctype_t *ctype,
+ const char *name)
+{
+ int cnt;
+
+ for (cnt = 0; cnt < ctype->nr_charclass; ++cnt)
+ if (strcmp (ctype->classnames[cnt], name) == 0)
+ break;
+
+ if (cnt < ctype->nr_charclass)
+ {
+ lr_error (lr, _("character class `%s' already defined"));
+ return;
+ }
+
+ if (ctype->nr_charclass == MAX_NR_CHARCLASS)
+ /* Exit code 2 is prescribed in P1003.2b. */
+ error (2, 0, _("\
+implementation limit: no more than %d character classes allowed"),
+ MAX_NR_CHARCLASS);
+
+ ctype->classnames[ctype->nr_charclass++] = name;
+}
+
+
+static void
+ctype_map_newP (struct linereader *lr, struct locale_ctype_t *ctype,
+ const char *name, struct charset_t *charset)
+{
+ size_t max_chars = 0;
+ int cnt;
+
+ for (cnt = 0; cnt < ctype->map_collection_nr; ++cnt)
+ {
+ if (strcmp (ctype->mapnames[cnt], name) == 0)
+ break;
+
+ if (max_chars < ctype->map_collection_max[cnt])
+ max_chars = ctype->map_collection_max[cnt];
+ }
+
+ if (cnt < ctype->map_collection_nr)
+ {
+ lr_error (lr, _("character map `%s' already defined"));
+ return;
+ }
+
+ if (ctype->map_collection_nr == MAX_NR_CHARMAP)
+ /* Exit code 2 is prescribed in P1003.2b. */
+ error (2, 0, _("\
+implementation limit: no more than %d character maps allowed"),
+ MAX_NR_CHARMAP);
+
+ ctype->mapnames[cnt] = name;
+
+ if (max_chars == 0)
+ ctype->map_collection_max[cnt] = charset->mb_cur_max == 1 ? 256
+ : 512;
+ else
+ ctype->map_collection_max[cnt] = max_chars;
+
+ ctype->map_collection[cnt] =
+ (u32_t *) xmalloc (sizeof (u32_t) * ctype->map_collection_max[cnt]);
+ memset (ctype->map_collection[cnt], '\0',
+ sizeof (u32_t) * ctype->map_collection_max[cnt]);
+ ctype->map_collection_act[cnt] = 256;
+
+ ++ctype->map_collection_nr;
+}
+
+
+static u32_t *
+find_idx (struct locale_ctype_t *ctype, u32_t **table, size_t *max,
+ size_t *act, unsigned int idx)
+{
+ size_t cnt;
+
+ if (idx < 256)
+ return &(*table)[idx];
+
+ for (cnt = 256; cnt < ctype->charnames_act; ++cnt)
+ if (ctype->charnames[cnt] == idx)
+ break;
+
+ /* We have to distinguish two cases: the names is found or not. */
+ if (cnt == ctype->charnames_act)
+ {
+ /* Extend the name array. */
+ if (ctype->charnames_act == ctype->charnames_max)
+ {
+ ctype->charnames_max *= 2;
+ ctype->charnames = (unsigned int *)
+ xrealloc (ctype->charnames,
+ sizeof (unsigned int) * ctype->charnames_max);
+ }
+ ctype->charnames[ctype->charnames_act++] = idx;
+ }
+
+ if (cnt >= *act)
+ {
+ if (cnt >= *max)
+ {
+ size_t old_max = *max;
+ do
+ *max *= 2;
+ while (*max <= cnt);
+
+ *table =
+ (u32_t *) xrealloc (*table, *max * sizeof (unsigned long int));
+ memset (&(*table)[old_max], '\0', (*max - old_max) * sizeof (u32_t));
+ }
+
+ (*table)[cnt] = 0;
+ *act = cnt;
+ }
+
+ return &(*table)[cnt];
+}
+
+
+static void
+set_class_defaults (struct locale_ctype_t *ctype, struct charset_t *charset)
+{
+ /* 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[2];
+ int ch;
+ /* Define string. */
+ strcpy (tmp, "?");
+
+ for (ch = from; ch <= to; ++ch)
+ {
+ unsigned int value;
+ tmp[0] = ch;
+
+ value = charset_find_value (charset, tmp, 1);
+ if (value == ILLEGAL_CHAR_VALUE)
+ {
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ tmp);
+ continue;
+ }
+ else
+ ELEM (ctype, class_collection, , value) |= bit;
+ }
+ }
+
+ /* Set default values if keyword was not present. */
+ if ((ctype->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." [P1003.2, 2.5.2.1] */
+ set_default (BIT (tok_upper), 'A', 'Z');
+
+ if ((ctype->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." [P1003.2, 2.5.2.1] */
+ set_default (BIT (tok_lower), 'a', 'z');
+
+ if ((ctype->class_done & BIT (tok_alpha)) == 0)
+ {
+ /* Table 2-6 in P1003.2 says that characters in class `upper' or
+ class `lower' *must* be in class `alpha'. */
+ unsigned long int mask = BIT (tok_upper) | BIT (tok_lower);
+ size_t cnt;
+
+ for (cnt = 0; cnt < ctype->class_collection_act; ++cnt)
+ if ((ctype->class_collection[cnt] & mask) != 0)
+ ctype->class_collection[cnt] |= BIT (tok_alpha);
+ }
+
+ if ((ctype->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." [P1003.2, 2.5.2.1] */
+ set_default (BIT (tok_digit), '0', '9');
+
+ /* "Only characters specified for the `alpha' and `digit' keyword
+ shall be specified. Characters specified for the keyword `alpha'
+ and `digit' are automatically included in this class. */
+ {
+ unsigned long int mask = BIT (tok_alpha) | BIT (tok_digit);
+ size_t cnt;
+
+ for (cnt = 0; cnt < ctype->class_collection_act; ++cnt)
+ if ((ctype->class_collection[cnt] & mask) != 0)
+ ctype->class_collection[cnt] |= BIT (tok_alnum);
+ }
+
+ if ((ctype->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 implementation-defined character values." [P1003.2, 2.5.2.1] */
+ {
+ unsigned int value;
+
+ value = charset_find_value (charset, "space", 5);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<space>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_space);
+
+ value = charset_find_value (charset, "form-feed", 9);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<form-feed>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_space);
+
+ value = charset_find_value (charset, "newline", 7);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<newline>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_space);
+
+ value = charset_find_value (charset, "carriage-return", 15);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<carriage-return>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_space);
+
+ value = charset_find_value (charset, "tab", 3);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<tab>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_space);
+
+ value = charset_find_value (charset, "vertical-tab", 12);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<vertical-tab>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_space);
+ }
+
+ if ((ctype->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." [P1003.2, 2.5.2.1] */
+ {
+ set_default (BIT (tok_xdigit), '0', '9');
+ set_default (BIT (tok_xdigit), 'A', 'F');
+ set_default (BIT (tok_xdigit), 'a', 'f');
+ }
+
+ if ((ctype->class_done & BIT (tok_blank)) == 0)
+ /* "If this keyword [blank] is unspecified, the characters <space> and
+ <tab> shall belong to this character class." [P1003.2, 2.5.2.1] */
+ {
+ unsigned int value;
+
+ value = charset_find_value (charset, "space", 5);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<space>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_blank);
+
+ value = charset_find_value (charset, "tab", 3);
+ if (value == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<tab>");
+ else
+ ELEM (ctype, class_collection, , value) |= BIT (tok_blank);
+ }
+
+ if ((ctype->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." [P1003.2, 2.5.2.1] */
+ {
+ unsigned long int mask = BIT (tok_upper) | BIT (tok_lower) |
+ BIT (tok_alpha) | BIT (tok_digit) | BIT (tok_xdigit) | BIT (tok_punct);
+ size_t cnt;
+
+ for (cnt = 0; cnt < ctype->class_collection_act; ++cnt)
+ if ((ctype->class_collection[cnt] & mask) != 0)
+ ctype->class_collection[cnt] |= BIT (tok_graph);
+ }
+
+ if ((ctype->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."
+ [P1003.2, 2.5.2.1] */
+ {
+ unsigned long int mask = BIT (tok_upper) | BIT (tok_lower) |
+ BIT (tok_alpha) | BIT (tok_digit) | BIT (tok_xdigit) | BIT (tok_punct);
+ size_t cnt;
+ int space;
+
+ for (cnt = 0; cnt < ctype->class_collection_act; ++cnt)
+ if ((ctype->class_collection[cnt] & mask) != 0)
+ ctype->class_collection[cnt] |= BIT (tok_print);
+
+ space = charset_find_value (charset, "space", 5);
+ if (space == ILLEGAL_CHAR_VALUE)
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ "<space>");
+ else
+ ELEM (ctype, class_collection, , space) |= BIT (tok_print);
+ }
+
+ if (ctype->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." [P1003.2, 2.5.2.1] */
+ {
+ char tmp[4];
+ int ch;
+
+ strcpy (tmp, "<?>");
+
+ for (ch = 'a'; ch <= 'z'; ++ch)
+ {
+ unsigned int value_from, value_to;
+
+ tmp[1] = (char) ch;
+
+ value_from = charset_find_value (charset, &tmp[1], 1);
+ if (value_from == ILLEGAL_CHAR_VALUE)
+ {
+ error (0, 0, _("\
+character `%c' not defined while needed as default value"),
+ tmp);
+ continue;
+ }
+
+ /* This conversion is implementation defined. */
+ tmp[1] = (char) (ch + ('A' - 'a'));
+ value_to = charset_find_value (charset, &tmp[1], 1);
+ if (value_to == -1)
+ {
+ error (0, 0, _("\
+character `%s' not defined while needed as default value"),
+ tmp);
+ continue;
+ }
+
+ /* The index [0] is determined by the order of the
+ `ctype_map_newP' calls in `ctype_startup'. */
+ ELEM (ctype, map_collection, [0], value_from) = value_to;
+ }
+ }
+
+ if (ctype->tolower_done == 0)
+ /* "If this keyword [tolower] is not specified, the mapping shall be
+ the reverse mapping of the one specified to `toupper'." [P1003.2] */
+ {
+ size_t cnt;
+
+ for (cnt = 0; cnt < ctype->map_collection_act[0]; ++cnt)
+ if (ctype->map_collection[0][cnt] != 0)
+ ELEM (ctype, map_collection, [1],
+ ctype->map_collection[0][cnt])
+ = ctype->charnames[cnt];
+ }
+}
+
+
+static void
+allocate_arrays (struct locale_ctype_t *ctype)
+{
+ size_t idx;
+
+ /* First we have to decide how we organize the arrays. It is easy for
+ a one-byte character set. But multi-byte character set cannot be
+ stored flat because they might be sparsly used. So we determine an
+ optimal hashing function for the used characters.
+
+ We use a very trivial hashing function to store the sparse table.
+ CH % TABSIZE is used as an index. To solve multiple hits we have
+ N planes. This gurantees a fixed search time for a character [N
+ / 2]. In the following code we determine the minmum value for
+ TABSIZE * N, where TABSIZE >= 256. */
+ size_t min_total = UINT_MAX;
+ size_t act_size = 256;
+
+ fputs (_("\
+Computing table size for character classes might take a while..."),
+ stderr);
+
+ while (act_size < min_total)
+ {
+ size_t cnt[act_size];
+ size_t act_planes = 1;
+
+ memset (cnt, '\0', sizeof cnt);
+
+ for (idx = 0; idx < 256; ++idx)
+ cnt[idx] = 1;
+
+ for (idx = 0; idx < ctype->charnames_act; ++idx)
+ if (ctype->charnames[idx] >= 256)
+ {
+ size_t nr = ctype->charnames[idx] % act_size;
+
+ if (++cnt[nr] > act_planes)
+ {
+ act_planes = cnt[nr];
+ if (act_size * act_planes >= min_total)
+ break;
+ }
+ }
+
+ if (act_size * act_planes < min_total)
+ {
+ min_total = act_size * act_planes;
+ ctype->plane_size = act_size;
+ ctype->plane_cnt = act_planes;
+ }
+
+ ++act_size;
+ }
+
+ fprintf (stderr, _(" done\n"));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define NAMES_B1 ctype->names_el
+# define NAMES_B2 ctype->names_eb
+#else
+# define NAMES_B1 ctype->names_eb
+# define NAMES_B2 ctype->names_el
+#endif
+
+ ctype->names_eb = (u32_t *) xcalloc (ctype->plane_size * ctype->plane_cnt,
+ sizeof (u32_t));
+ ctype->names_el = (u32_t *) xcalloc (ctype->plane_size * ctype->plane_cnt,
+ sizeof (u32_t));
+
+ for (idx = 1; idx < 256; ++idx)
+ NAMES_B1[idx] = idx;
+
+ /* Trick: change the 0th entry's name to 1 to mark the cell occupied. */
+ NAMES_B1[0] = 1;
+
+ for (idx = 256; idx < ctype->charnames_act; ++idx)
+ {
+ size_t nr = (ctype->charnames[idx] % ctype->plane_size);
+ size_t depth = 0;
+
+ while (NAMES_B1[nr + depth * ctype->plane_size])
+ ++depth;
+ assert (depth < ctype->plane_cnt);
+
+ NAMES_B1[nr + depth * ctype->plane_size] = ctype->charnames[idx];
+
+ /* Now for faster access remember the index in the NAMES_B array. */
+ ctype->charnames[idx] = nr + depth * ctype->plane_size;
+ }
+ NAMES_B1[0] = 0;
+
+ for (idx = 0; idx < ctype->plane_size * ctype->plane_cnt; ++idx)
+ NAMES_B2[idx] = SWAPU32 (NAMES_B1[idx]);
+
+
+ /* 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 up to 255 below the entry for \0. */
+ ctype->ctype_b = (char_class_t *) xcalloc (256 + 128,
+ sizeof (char_class_t));
+ ctype->ctype32_b = (char_class32_t *) xcalloc (ctype->plane_size
+ * ctype->plane_cnt,
+ sizeof (char_class32_t));
+
+ /* Fill in the character class information. */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define TRANS(w) CHAR_CLASS_TRANS (w)
+# define TRANS32(w) CHAR_CLASS32_TRANS (w)
+#else
+# define TRANS(w) (w)
+# define TRANS32(w) (w)
+#endif
+
+ for (idx = 0; idx < ctype->class_collection_act; ++idx)
+ if (ctype->charnames[idx] < 256)
+ ctype->ctype_b[128 + ctype->charnames[idx]]
+ = TRANS (ctype->class_collection[idx]);
+
+ /* Mirror first 128 entries. */
+ for (idx = 0; idx < 128; ++idx)
+ ctype->ctype_b[idx] = ctype->ctype_b[256 + idx];
+
+ /* The 32 bit array contains all characters. */
+ for (idx = 0; idx < ctype->class_collection_act; ++idx)
+ ctype->ctype32_b[ctype->charnames[idx]]
+ = TRANS32 (ctype->class_collection[idx]);
+
+ /* Room for table of mappings. */
+ ctype->map_eb = (u32_t **) xmalloc (ctype->map_collection_nr
+ * sizeof (u32_t *));
+ ctype->map_el = (u32_t **) xmalloc (ctype->map_collection_nr
+ * sizeof (u32_t *));
+
+ /* Fill in all mappings. */
+ for (idx = 0; idx < ctype->map_collection_nr; ++idx)
+ {
+ unsigned int idx2;
+
+ /* Allocate table. */
+ ctype->map_eb[idx] = (u32_t *) xmalloc ((ctype->plane_size
+ * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+ ctype->map_el[idx] = (u32_t *) xmalloc ((ctype->plane_size
+ * ctype->plane_cnt + 128)
+ * sizeof (u32_t));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define MAP_B1 ctype->map_el
+# define MAP_B2 ctype->map_eb
+#else
+# define MAP_B1 ctype->map_eb
+# define MAP_B2 ctype->map_el
+#endif
+
+ /* Copy default value (identity mapping). */
+ memcpy (&MAP_B1[idx][128], NAMES_B1,
+ ctype->plane_size * ctype->plane_cnt * sizeof (u32_t));
+
+ /* Copy values from collection. */
+ for (idx2 = 0; idx2 < ctype->map_collection_act[idx]; ++idx2)
+ if (ctype->map_collection[idx][idx2] != 0)
+ MAP_B1[idx][128 + ctype->charnames[idx2]] =
+ ctype->map_collection[idx][idx2];
+
+ /* Mirror first 128 entries. */
+ for (idx2 = 0; idx2 < 128; ++idx2)
+ MAP_B1[idx][idx2] = MAP_B1[idx][256 + idx2];
+
+
+ /* And now the other byte order. */
+ for (idx2 = 0; idx2 < ctype->plane_size * ctype->plane_cnt + 128; ++idx2)
+ MAP_B2[idx][idx2] = SWAPU32 (MAP_B1[idx][idx2]);
+ }
+
+ /* Extra array for class and map names. */
+ ctype->class_name_ptr = (u32_t *) xmalloc (ctype->nr_charclass
+ * sizeof (u32_t));
+ ctype->map_name_ptr = (u32_t *) xmalloc (ctype->map_collection_nr
+ * sizeof (u32_t));
+}
diff --git a/locale/programs/ld-messages.c b/locale/programs/ld-messages.c
new file mode 100644
index 0000000000..ebd5054b02
--- /dev/null
+++ b/locale/programs/ld-messages.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <alloca.h>
+#include <langinfo.h>
+#include <string.h>
+#include <sys/uio.h>
+
+#ifdef HAVE_REGEX
+# include <regex.h>
+#else
+# include <rx.h>
+#endif
+
+/* Undefine following line in production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#include "locales.h"
+#include "stringtrans.h"
+#include "localeinfo.h"
+
+
+void *xmalloc (size_t __n);
+
+
+/* The real definition of the struct for the LC_MESSAGES locale. */
+struct locale_messages_t
+{
+ const char *yesexpr;
+ const char *noexpr;
+ const char *yesstr;
+ const char *nostr;
+};
+
+
+void
+messages_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *charset)
+{
+ struct locale_messages_t *messages;
+
+ /* It is important that we always use UCS1 encoding for strings now. */
+ encoding_method = ENC_UCS1;
+
+ locale->categories[LC_MESSAGES].messages = messages =
+ (struct locale_messages_t *) xmalloc (sizeof (struct locale_messages_t));
+
+ memset (messages, '\0', sizeof (struct locale_messages_t));
+}
+
+
+void
+messages_finish (struct localedef_t *locale)
+{
+ struct locale_messages_t *messages
+ = locale->categories[LC_MESSAGES].messages;
+
+ /* The fields YESSTR and NOSTR are optional. */
+ if (messages->yesexpr == NULL)
+ error (0, 0, _("field `%s' in category `%s' undefined"),
+ "yesexpr", "LC_MESSAGES");
+ else
+ {
+ int result;
+ regex_t re;
+
+ /* Test whether it are correct regular expressions. */
+ result = regcomp (&re, messages->yesexpr, REG_EXTENDED);
+ if (result != 0)
+ {
+ char errbuf[BUFSIZ];
+
+ (void) regerror (result, &re, errbuf, BUFSIZ);
+ error (0, 0, _("\
+no correct regular expression for field `%s' in category `%s': %s"),
+ "yesexpr", "LC_MESSAGES", errbuf);
+ }
+ }
+
+ if (messages->noexpr == NULL)
+ error (0, 0, _("field `%s' in category `%s' undefined"),
+ "noexpr", "LC_MESSAGES");
+ else
+ {
+ int result;
+ regex_t re;
+
+ /* Test whether it are correct regular expressions. */
+ result = regcomp (&re, messages->noexpr, REG_EXTENDED);
+ if (result != 0)
+ {
+ char errbuf[BUFSIZ];
+
+ (void) regerror (result, &re, errbuf, BUFSIZ);
+ error (0, 0, _("\
+no correct regular expression for field `%s' in category `%s': %s"),
+ "noexpr", "LC_MESSAGES", errbuf);
+ }
+ }
+}
+
+
+void
+messages_output (struct localedef_t *locale, const char *output_path)
+{
+ struct locale_messages_t *messages
+ = locale->categories[LC_MESSAGES].messages;
+ struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES)];
+ struct locale_file data;
+ u32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES)];
+ size_t cnt = 0;
+
+ if ((locale->binary & (1 << LC_MESSAGES)) != 0)
+ {
+ iov[0].iov_base = messages;
+ iov[0].iov_len = locale->len[LC_MESSAGES];
+
+ write_locale_data (output_path, "LC_MESSAGES", 1, iov);
+
+ return;
+ }
+
+ data.magic = LIMAGIC (LC_MESSAGES);
+ data.n = _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES);
+ iov[cnt].iov_base = (void *) &data;
+ iov[cnt].iov_len = sizeof (data);
+ ++cnt;
+
+ iov[cnt].iov_base = (void *) idx;
+ iov[cnt].iov_len = sizeof (idx);
+ ++cnt;
+
+ idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
+ iov[cnt].iov_base = (void *) (messages->yesexpr ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (messages->noexpr ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (messages->yesstr ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (messages->nostr ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+
+ assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES));
+
+ write_locale_data (output_path, "LC_MESSAGES",
+ 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MESSAGES), iov);
+}
+
+
+void
+messages_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, struct token *code,
+ struct charset_t *charset)
+{
+ struct locale_messages_t *messages
+ = locale->categories[LC_MESSAGES].messages;
+
+ switch (tok)
+ {
+ case tok_yesexpr:
+ if (code->val.str.start == NULL)
+ {
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),
+ "yesexpr", "LC_MESSAGES");
+ messages->yesexpr = "";
+ }
+ else
+ messages->yesexpr = code->val.str.start;
+ break;
+
+ case tok_noexpr:
+ if (code->val.str.start == NULL)
+ {
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),
+ "noexpr", "LC_MESSAGES");
+ messages->noexpr = "";
+ }
+ else
+ messages->noexpr = code->val.str.start;
+ break;
+
+ case tok_yesstr:
+ if (code->val.str.start == NULL)
+ {
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),
+ "yesstr", "LC_MESSAGES");
+ messages->yesstr = "";
+ }
+ else
+ messages->yesstr = code->val.str.start;
+ break;
+
+ case tok_nostr:
+ if (code->val.str.start == NULL)
+ {
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),
+ "nostr", "LC_MESSAGES");
+ messages->nostr = "";
+ }
+ else
+ messages->nostr = code->val.str.start;
+ break;
+
+ default:
+ assert (! "unknown token in category `LC_MESSAGES': should not happen");
+ }
+}
diff --git a/locale/programs/ld-monetary.c b/locale/programs/ld-monetary.c
new file mode 100644
index 0000000000..18e27866fb
--- /dev/null
+++ b/locale/programs/ld-monetary.c
@@ -0,0 +1,385 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <langinfo.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Undefine following line in production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#include "locales.h"
+#include "localeinfo.h"
+#include "stringtrans.h"
+
+void *xmalloc (size_t __n);
+void *xrealloc (void *__ptr, size_t __n);
+
+
+/* The real definition of the struct for the LC_NUMERIC locale. */
+struct locale_monetary_t
+{
+ const char *int_curr_symbol;
+ const char *currency_symbol;
+ const char *mon_decimal_point;
+ const char *mon_thousands_sep;
+ char *mon_grouping;
+ size_t mon_grouping_max;
+ size_t mon_grouping_act;
+ const char *positive_sign;
+ const char *negative_sign;
+ signed char int_frac_digits;
+ signed char frac_digits;
+ signed char p_cs_precedes;
+ signed char p_sep_by_space;
+ signed char n_cs_precedes;
+ signed char n_sep_by_space;
+ signed char p_sign_posn;
+ signed char n_sign_posn;
+};
+
+
+/* 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 NR_VALID_INT_CURR ((sizeof (valid_int_curr) \
+ / sizeof (valid_int_curr[0])))
+#undef DEFINE_INT_CURR
+
+
+/* Prototypes for local functions. */
+static int curr_strcmp(const char *s1, const char **s2);
+
+
+void
+monetary_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *charset)
+{
+ struct locale_monetary_t *monetary;
+
+ /* It is important that we always use UCS1 encoding for strings now. */
+ encoding_method = ENC_UCS1;
+
+ locale->categories[LC_MONETARY].monetary = monetary =
+ (struct locale_monetary_t *) xmalloc (sizeof (struct locale_monetary_t));
+
+ memset (monetary, '\0', sizeof (struct locale_monetary_t));
+
+ monetary->mon_grouping_max = 80;
+ monetary->mon_grouping =
+ (char *) xmalloc (monetary->mon_grouping_max);
+ monetary->mon_grouping_act = 0;
+
+ monetary->int_frac_digits = -2;
+ monetary->frac_digits = -2;
+ monetary->p_cs_precedes = -2;
+ monetary->p_sep_by_space = -2;
+ monetary->n_cs_precedes = -2;
+ monetary->n_sep_by_space = -2;
+ monetary->p_sign_posn = -2;
+ monetary->n_sign_posn = -2;
+}
+
+
+void
+monetary_finish (struct localedef_t *locale)
+{
+ struct locale_monetary_t *monetary
+ = locale->categories[LC_MONETARY].monetary;
+
+#define TEST_ELEM(cat) \
+ if (monetary->cat == NULL) \
+ error (0, 0, _("field `%s' in category `%s' not defined"), \
+ #cat, "LC_MONETARY")
+
+ TEST_ELEM (int_curr_symbol);
+ TEST_ELEM (currency_symbol);
+ TEST_ELEM (mon_decimal_point);
+ TEST_ELEM (mon_thousands_sep);
+ TEST_ELEM (positive_sign);
+ TEST_ELEM (negative_sign);
+
+ /* The international currency symbol must come from ISO 4217. */
+ if (monetary->int_curr_symbol != NULL)
+ {
+ if (strlen (monetary->int_curr_symbol) != 4)
+ error (0, 0, _("\
+value of field `int_curr_symbol' in category `LC_MONETARY' has wrong length"));
+ else if (bsearch (monetary->int_curr_symbol, valid_int_curr,
+ NR_VALID_INT_CURR, sizeof (const char *),
+ (comparison_fn_t) curr_strcmp) == NULL)
+ error (0, 0, _("\
+value of field `int_curr_symbol' in category `LC_MONETARY' does \
+not correspond to a valid name in ISO 4217"));
+ }
+
+ /* 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 (monetary->mon_decimal_point[0] == '\0')
+ {
+ error (0, 0, _("\
+value for field `%s' in category `%s' must not be the empty string"),
+ "mon_decimal_point", "LC_MONETARY");
+ }
+
+ if (monetary->mon_grouping_act == 0)
+ error (0, 0, _("field `%s' in category `%s' not defined"),
+ "mon_grouping", "LC_MONETARY");
+
+#undef TEST_ELEM
+#define TEST_ELEM(cat, min, max) \
+ if (monetary->cat == -2) \
+ error (0, 0, _("field `%s' in category `%s' not defined"), \
+ #cat, "LC_MONETARY"); \
+ else if (monetary->cat < min || monetary->cat > max) \
+ error (0, 0, _("\
+value for field `%s' in category `%s' must be in range %d...%d"), \
+ #cat, "LC_MONETARY", min, max)
+
+ TEST_ELEM (int_frac_digits, -128, 127); /* No range check. */
+ TEST_ELEM (frac_digits, -128, 127); /* No range check. */
+ TEST_ELEM (p_cs_precedes, -1, 1);
+ TEST_ELEM (p_sep_by_space, -1, 2);
+ TEST_ELEM (n_cs_precedes, -1, 1);
+ TEST_ELEM (n_sep_by_space, -1, 2);
+ TEST_ELEM (p_sign_posn, -1, 4);
+ TEST_ELEM (n_sign_posn, -1, 4);
+}
+
+
+void
+monetary_output (struct localedef_t *locale, const char *output_path)
+{
+ struct locale_monetary_t *monetary
+ = locale->categories[LC_MONETARY].monetary;
+ struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)];
+ struct locale_file data;
+ u32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_MONETARY)];
+ size_t cnt = 0;
+
+ if ((locale->binary & (1 << LC_MONETARY)) != 0)
+ {
+ iov[0].iov_base = monetary;
+ iov[0].iov_len = locale->len[LC_MONETARY];
+
+ write_locale_data (output_path, "LC_MONETARY", 1, iov);
+
+ return;
+ }
+
+ data.magic = LIMAGIC (LC_MONETARY);
+ data.n = _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY);
+ iov[cnt].iov_base = (void *) &data;
+ iov[cnt].iov_len = sizeof (data);
+ ++cnt;
+
+ iov[cnt].iov_base = (void *) idx;
+ iov[cnt].iov_len = sizeof (idx);
+ ++cnt;
+
+ idx[cnt - 2] = iov[0].iov_len + iov[1].iov_len;
+ iov[cnt].iov_base = (void *) (monetary->int_curr_symbol ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (monetary->currency_symbol ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (monetary->mon_decimal_point ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (monetary->mon_thousands_sep ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = alloca (monetary->mon_grouping_act + 1);
+ iov[cnt].iov_len = monetary->mon_grouping_act + 1;
+ memcpy (iov[cnt].iov_base, monetary->mon_grouping,
+ monetary->mon_grouping_act);
+ ((char *) iov[cnt].iov_base)[monetary->mon_grouping_act] = '\0';
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (monetary->positive_sign ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (monetary->negative_sign ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->int_frac_digits;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->frac_digits;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->p_cs_precedes;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->p_sep_by_space;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->n_cs_precedes;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->n_sep_by_space;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->p_sign_posn;
+ iov[cnt].iov_len = 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) &monetary->n_sign_posn;
+ iov[cnt].iov_len = 1;
+
+ assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY));
+
+ write_locale_data (output_path, "LC_MONETARY",
+ 2 + _NL_ITEM_INDEX (_NL_NUM_LC_MONETARY), iov);
+}
+
+
+void
+monetary_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, struct token *code,
+ struct charset_t *charset)
+{
+ struct locale_monetary_t *monetary
+ = locale->categories[LC_MONETARY].monetary;
+
+ switch (tok)
+ {
+#define STR_ELEM(cat) \
+ case tok_##cat: \
+ if (monetary->cat != NULL) \
+ lr_error (lr, _("\
+field `%s' in category `%s' declared more than once"), \
+ #cat, "LC_MONETARY"); \
+ else if (code->val.str.start == NULL) \
+ { \
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
+ #cat, "LC_MONETARY"); \
+ monetary->cat = ""; \
+ } \
+ else \
+ monetary->cat = code->val.str.start; \
+ break
+
+ STR_ELEM (int_curr_symbol);
+ STR_ELEM (currency_symbol);
+ STR_ELEM (mon_decimal_point);
+ STR_ELEM (mon_thousands_sep);
+ STR_ELEM (positive_sign);
+ STR_ELEM (negative_sign);
+
+#define INT_ELEM(cat) \
+ case tok_##cat: \
+ if (monetary->cat != -2) \
+ lr_error (lr, _("\
+field `%s' in category `%s' declared more than once"), \
+ #cat, "LC_MONETARY"); \
+ else \
+ monetary->cat = code->val.num; \
+ break
+
+ INT_ELEM (int_frac_digits);
+ INT_ELEM (frac_digits);
+ INT_ELEM (p_cs_precedes);
+ INT_ELEM (p_sep_by_space);
+ INT_ELEM (n_cs_precedes);
+ INT_ELEM (n_sep_by_space);
+ INT_ELEM (p_sign_posn);
+ INT_ELEM (n_sign_posn);
+
+ case tok_mon_grouping:
+ if (monetary->mon_grouping_act == monetary->mon_grouping_max)
+ {
+ monetary->mon_grouping_max *= 2;
+ monetary->mon_grouping =
+ (char *) xrealloc (monetary->mon_grouping,
+ monetary->mon_grouping_max);
+ }
+ if (monetary->mon_grouping[monetary->mon_grouping_act - 1]
+ == '\177')
+ lr_error (lr, _("\
+`-1' must be last entry in `%s' field in `%s' category"),
+ "mon_grouping", "LC_MONETARY");
+ else
+ {
+ if (code->tok == tok_minus1)
+ monetary->mon_grouping[monetary->mon_grouping_act++] = '\177';
+ else if (code->val.num == 0)
+ lr_error (lr, _("\
+values for field `%s' in category `%s' must not be zero"),
+ "mon_grouping", "LC_MONETARY");
+ else if (code->val.num > 126)
+ lr_error (lr, _("\
+values for field `%s' in category `%s' must be smaller than 127"),
+ "mon_grouping", "LC_MONETARY");
+ else
+ monetary->mon_grouping[monetary->mon_grouping_act++]
+ = code->val.num;
+ }
+ break;
+
+ default:
+ assert (! "unknown token in category `LC_MONETARY': should not happen");
+ }
+}
+
+
+static int
+curr_strcmp(const char *s1, const char **s2)
+{
+ return strcmp (s1, *s2);
+}
diff --git a/locale/programs/ld-numeric.c b/locale/programs/ld-numeric.c
new file mode 100644
index 0000000000..0b5fe2afe5
--- /dev/null
+++ b/locale/programs/ld-numeric.c
@@ -0,0 +1,208 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <langinfo.h>
+#include <string.h>
+
+/* Undefine following line in production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#include "locales.h"
+#include "localeinfo.h"
+#include "stringtrans.h"
+
+void *xmalloc (size_t __n);
+void *xrealloc (void *__ptr, size_t __n);
+
+
+/* The real definition of the struct for the LC_NUMERIC locale. */
+struct locale_numeric_t
+{
+ const char *decimal_point;
+ const char *thousands_sep;
+ char *grouping;
+ size_t grouping_max;
+ size_t grouping_act;
+};
+
+
+void
+numeric_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *charset)
+{
+ struct locale_numeric_t *numeric;
+
+ /* It is important that we always use UCS1 encoding for strings now. */
+ encoding_method = ENC_UCS1;
+
+ locale->categories[LC_NUMERIC].numeric = numeric =
+ (struct locale_numeric_t *) xmalloc (sizeof (struct locale_numeric_t));
+
+ memset (numeric, '\0', sizeof (struct locale_numeric_t));
+
+ numeric->grouping_max = 80;
+ numeric->grouping = (char *) xmalloc (numeric->grouping_max);
+ numeric->grouping_act = 0;
+}
+
+
+void
+numeric_finish (struct localedef_t *locale)
+{
+ struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
+
+#define TEST_ELEM(cat) \
+ if (numeric->cat == NULL) \
+ error (0, 0, _("field `%s' in category `%s' not defined"), \
+ #cat, "LC_NUMERIC")
+
+ TEST_ELEM (decimal_point);
+ TEST_ELEM (thousands_sep);
+
+ /* 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 (numeric->decimal_point[0] == '\0')
+ {
+ error (0, 0, _("\
+value for field `%s' in category `%s' must not be the empty string"),
+ "decimal_point", "LC_NUMERIC");
+ }
+
+ if (numeric->grouping_act == 0)
+ error (0, 0, _("field `%s' in category `%s' not defined"),
+ "grouping", "LC_NUMERIC");
+}
+
+
+void
+numeric_output (struct localedef_t *locale, const char *output_path)
+{
+ struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
+ struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC)];
+ struct locale_file data;
+ u32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC)];
+ size_t cnt = 0;
+
+ if ((locale->binary & (1 << LC_NUMERIC)) != 0)
+ {
+ iov[0].iov_base = numeric;
+ iov[0].iov_len = locale->len[LC_NUMERIC];
+
+ write_locale_data (output_path, "LC_NUMERIC", 1, iov);
+
+ return;
+ }
+
+ data.magic = LIMAGIC (LC_NUMERIC);
+ data.n = _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC);
+ iov[cnt].iov_base = (void *) &data;
+ iov[cnt].iov_len = sizeof (data);
+ ++cnt;
+
+ iov[cnt].iov_base = (void *) idx;
+ iov[cnt].iov_len = sizeof (idx);
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (numeric->decimal_point ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = (void *) (numeric->thousands_sep ?: "");
+ iov[cnt].iov_len = strlen (iov[cnt].iov_base) + 1;
+ ++cnt;
+
+ idx[cnt - 2] = idx[cnt - 3] + iov[cnt - 1].iov_len;
+ iov[cnt].iov_base = alloca (numeric->grouping_act + 1);
+ iov[cnt].iov_len = numeric->grouping_act + 1;
+ memcpy (iov[cnt].iov_base, numeric->grouping, numeric->grouping_act);
+ ((char *) iov[cnt].iov_base)[numeric->grouping_act] = '\0';
+
+ assert (cnt + 1 == 2 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC));
+
+ write_locale_data (output_path, "LC_NUMERIC",
+ 2 + _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC), iov);
+}
+
+
+void
+numeric_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, struct token *code,
+ struct charset_t *charset)
+{
+ struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
+
+ switch (tok)
+ {
+#define STR_ELEM(cat) \
+ case tok_##cat: \
+ if (numeric->cat != NULL) \
+ lr_error (lr, _("\
+field `%s' in category `%s' declared more than once"), \
+ #cat, "LC_NUMERIC"); \
+ else if (code->val.str.start == NULL) \
+ { \
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
+ #cat, "LC_NUMERIC"); \
+ numeric->cat = ""; \
+ } \
+ else \
+ numeric->cat = code->val.str.start; \
+ break
+
+ STR_ELEM (decimal_point);
+ STR_ELEM (thousands_sep);
+
+ case tok_grouping:
+ if (numeric->grouping_act == numeric->grouping_max)
+ {
+ numeric->grouping_max *= 2;
+ numeric->grouping = (char *) xrealloc (numeric->grouping,
+ numeric->grouping_max);
+ }
+ if (numeric->grouping_act > 0
+ && (numeric->grouping[numeric->grouping_act - 1] == '\177'))
+ {
+ lr_error (lr, _("\
+`-1' must be last entry in `%s' field in `%s' category"),
+ "grouping", "LC_NUMERIC");
+ --numeric->grouping_act;
+ }
+
+ if (code->tok == tok_minus1)
+ numeric->grouping[numeric->grouping_act++] = '\177';
+ else if (code->val.num > 126)
+ lr_error (lr, _("\
+values for field `%s' in category `%s' must be smaller than 127"),
+ "grouping", "LC_NUMERIC");
+ else
+ numeric->grouping[numeric->grouping_act++] = code->val.num;
+ break;
+
+ default:
+ assert (! "unknown token in category `LC_NUMERIC': should not happen");
+ }
+}
diff --git a/locale/programs/ld-time.c b/locale/programs/ld-time.c
new file mode 100644
index 0000000000..2587faccdc
--- /dev/null
+++ b/locale/programs/ld-time.c
@@ -0,0 +1,310 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <langinfo.h>
+#include <string.h>
+
+/* Undefine following line in production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#include "locales.h"
+#include "localeinfo.h"
+#include "stringtrans.h"
+
+
+void *xmalloc (size_t __n);
+
+
+/* The real definition of the struct for the LC_TIME locale. */
+struct locale_time_t
+{
+ const char *abday[7];
+ size_t cur_num_abday;
+ const char *day[7];
+ size_t cur_num_day;
+ const char *abmon[12];
+ size_t cur_num_abmon;
+ const char *mon[12];
+ size_t cur_num_mon;
+ const char *am_pm[2];
+ size_t cur_num_am_pm;
+ const char *d_t_fmt;
+ const char *d_fmt;
+ const char *t_fmt;
+ const char *t_fmt_ampm;
+ const char *era;
+ const char *era_year;
+ const char *era_d_t_fmt;
+ const char *era_t_fmt;
+ const char *era_d_fmt;
+ const char *alt_digits[100];
+ size_t cur_num_alt_digits;
+};
+
+
+void
+time_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *charset)
+{
+ struct locale_time_t *time;
+
+ /* It is important that we always use UCS1 encoding for strings now. */
+ encoding_method = ENC_UCS1;
+
+ locale->categories[LC_TIME].time = time =
+ (struct locale_time_t *) xmalloc (sizeof (struct locale_time_t));
+
+ memset (time, '\0', sizeof (struct locale_time_t));
+}
+
+
+void
+time_finish (struct localedef_t *locale)
+{
+ struct locale_time_t *time = locale->categories[LC_TIME].time;
+
+#define TESTARR_ELEM(cat, max) \
+ if (time->cur_num_##cat == 0) \
+ error (0, 0, _("field `%s' in category `%s' not defined"), \
+ #cat, "LC_TIME"); \
+ else if (time->cur_num_##cat != max) \
+ error (0, 0, _("field `%s' in category `%s' has not enough values"), \
+ #cat, "LC_TIME")
+
+ TESTARR_ELEM (abday, 7);
+ TESTARR_ELEM (day, 7);
+ TESTARR_ELEM (abmon, 12);
+ TESTARR_ELEM (mon, 12);
+ TESTARR_ELEM (am_pm, 2);
+
+#define TEST_ELEM(cat) \
+ if (time->cat == NULL) \
+ error (0, 0, _("field `%s' in category `%s' not defined"), \
+ #cat, "LC_TIME")
+
+ TEST_ELEM (d_t_fmt);
+ TEST_ELEM (d_fmt);
+ TEST_ELEM (t_fmt);
+ TEST_ELEM (t_fmt_ampm);
+}
+
+
+void
+time_output (struct localedef_t *locale, const char *output_path)
+{
+ struct locale_time_t *time = locale->categories[LC_TIME].time;
+ struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME)
+ + time->cur_num_alt_digits];
+ struct locale_file data;
+ u32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)];
+ size_t cnt, last_idx, num;
+
+ if ((locale->binary & (1 << LC_TIME)) != 0)
+ {
+ iov[0].iov_base = time;
+ iov[0].iov_len = locale->len[LC_TIME];
+
+ write_locale_data (output_path, "LC_TIME", 1, iov);
+
+ return;
+ }
+
+ data.magic = LIMAGIC (LC_TIME);
+ data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME);
+ iov[0].iov_base = (void *) &data;
+ iov[0].iov_len = sizeof (data);
+
+ iov[1].iov_base = (void *) idx;
+ iov[1].iov_len = sizeof (idx);
+
+ idx[0] = iov[0].iov_len + iov[1].iov_len;
+
+ /* The ab'days. */
+ for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt)
+ {
+ iov[2 + cnt].iov_base =
+ (void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ }
+
+ /* The days. */
+ for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt)
+ {
+ iov[2 + cnt].iov_base =
+ (void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ }
+
+ /* The ab'mons. */
+ for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt)
+ {
+ iov[2 + cnt].iov_base =
+ (void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ }
+
+ /* The mons. */
+ for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt)
+ {
+ iov[2 + cnt].iov_base =
+ (void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ }
+
+ /* AM/PM. */
+ for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt)
+ {
+ iov[2 + cnt].iov_base =
+ (void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ }
+
+ iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->era ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->era_year ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len;
+ last_idx = ++cnt;
+
+ idx[1 + last_idx] = idx[last_idx];
+ for (num = 0; num < time->cur_num_alt_digits; ++num, ++cnt)
+ {
+ iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + last_idx] += iov[2 + cnt].iov_len;
+ }
+ ++last_idx;
+
+ iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len;
+ ++cnt;
+
+ iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: "");
+ iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1;
+ ++cnt;
+
+ assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME) - 1
+ + time->cur_num_alt_digits));
+
+ write_locale_data (output_path, "LC_TIME", 2 + cnt, iov);
+}
+
+
+void
+time_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t tok, struct token *code,
+ struct charset_t *charset)
+{
+ struct locale_time_t *time = locale->categories[LC_TIME].time;
+
+ switch (tok)
+ {
+#define STRARR_ELEM(cat, max) \
+ case tok_##cat: \
+ if (time->cur_num_##cat >= max) \
+ lr_error (lr, _(" \
+too many values for field `%s' in category `LC_TIME'"), \
+ #cat, "LC_TIME"); \
+ else if (code->val.str.start == NULL) \
+ { \
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
+ #cat, "LC_TIME"); \
+ time->cat[time->cur_num_##cat++] = ""; \
+ } \
+ else \
+ time->cat[time->cur_num_##cat++] \
+ = code->val.str.start; \
+ break
+
+ STRARR_ELEM (abday, 7);
+ STRARR_ELEM (day, 7);
+ STRARR_ELEM (abmon, 12);
+ STRARR_ELEM (mon, 12);
+ STRARR_ELEM (am_pm, 2);
+ STRARR_ELEM (alt_digits, 100);
+
+#define STR_ELEM(cat) \
+ case tok_##cat: \
+ if (time->cat != NULL) \
+ lr_error (lr, _("\
+field `%s' in category `%s' declared more than once"), \
+ #cat, "LC_TIME"); \
+ else if (code->val.str.start == NULL) \
+ { \
+ lr_error (lr, _("unknown character in field `%s' of category `%s'"),\
+ #cat, "LC_TIME"); \
+ time->cat = ""; \
+ } \
+ else \
+ time->cat = code->val.str.start; \
+ break
+
+ STR_ELEM (d_t_fmt);
+ STR_ELEM (d_fmt);
+ STR_ELEM (t_fmt);
+ STR_ELEM (t_fmt_ampm);
+ STR_ELEM (era);
+ STR_ELEM (era_year);
+ STR_ELEM (era_d_t_fmt);
+ STR_ELEM (era_d_fmt);
+ STR_ELEM (era_t_fmt);
+
+ default:
+ assert (! "unknown token in category `LC_TIME': should not happen");
+ }
+}
diff --git a/locale/programs/linereader.c b/locale/programs/linereader.c
new file mode 100644
index 0000000000..e4a1305712
--- /dev/null
+++ b/locale/programs/linereader.c
@@ -0,0 +1,579 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "linereader.h"
+#include "charset.h"
+#include "stringtrans.h"
+
+
+void *xmalloc (size_t __n);
+void *xrealloc (void *__p, size_t __n);
+char *xstrdup (const char *__str);
+
+
+static struct token *get_toplvl_escape (struct linereader *lr);
+static struct token *get_symname (struct linereader *lr);
+static struct token *get_ident (struct linereader *lr);
+static struct token *get_string (struct linereader *lr,
+ const struct charset_t *charset);
+
+
+struct linereader *
+lr_open (const char *fname, kw_hash_fct_t hf)
+{
+ FILE *fp;
+ struct linereader *result;
+ int n;
+
+ if (fname == NULL || strcmp (fname, "-") == 0
+ || strcmp (fname, "/dev/stdin") == 0)
+ fp = stdin;
+ else
+ {
+ fp = fopen (fname, "r");
+ if (fp == NULL)
+ return NULL;
+ }
+
+ result = (struct linereader *) xmalloc (sizeof (*result));
+
+ result->fp = fp;
+ result->fname = xstrdup (fname);
+ result->buf = NULL;
+ result->bufsize = 0;
+ result->lineno = 1;
+ result->idx = 0;
+ result->comment_char = '#';
+ result->escape_char = '\\';
+ result->translate_strings = 1;
+
+ n = getdelim (&result->buf, &result->bufsize, '\n', result->fp);
+ if (n < 0)
+ {
+ int save = errno;
+ fclose (result->fp);
+ free (result);
+ errno = save;
+ return NULL;
+ }
+
+ if (n > 1 && result->buf[n - 2] == '\\' && result->buf[n - 1] == '\n')
+ n -= 2;
+
+ result->buf[n] = '\0';
+ result->bufact = n;
+ result->hash_fct = hf;
+
+ return result;
+}
+
+
+int
+lr_eof (struct linereader *lr)
+{
+ return lr->bufact = 0;
+}
+
+
+void
+lr_close (struct linereader *lr)
+{
+ fclose (lr->fp);
+ free (lr->buf);
+ free (lr);
+}
+
+
+int
+lr_next (struct linereader *lr)
+{
+ int n;
+
+ n = getdelim (&lr->buf, &lr->bufsize, '\n', lr->fp);
+ if (n < 0)
+ return -1;
+
+ ++lr->lineno;
+
+ if (n > 1 && lr->buf[n - 2] == lr->escape_char && lr->buf[n - 1] == '\n')
+ {
+ /* An escaped newline character is substituted with a single <SP>. */
+ --n;
+ lr->buf[n - 1] = ' ';
+ }
+
+ lr->buf[n] = '\0';
+ lr->bufact = n;
+ lr->idx = 0;
+
+ return 0;
+}
+
+
+/* Defined in error.c. */
+/* This variable is incremented each time `error' is called. */
+extern unsigned int error_message_count;
+
+/* The calling program should define program_name and set it to the
+ name of the executing program. */
+extern char *program_name;
+
+
+struct token *
+lr_token (struct linereader *lr, const struct charset_t *charset)
+{
+ int ch;
+
+ while (1)
+ {
+ do
+ {
+ ch = lr_getc (lr);
+
+ if (ch == '\n')
+ {
+ lr->token.tok = tok_eol;
+ return &lr->token;
+ }
+ }
+ while (isspace (ch));
+
+ if (ch == EOF)
+ {
+ lr->token.tok = tok_eof;
+ return &lr->token;
+ };
+
+ if (ch != lr->comment_char)
+ break;
+
+ /* Ignore rest of line. */
+ lr_ignore_rest (lr, 0);
+ lr->token.tok = tok_eol;
+ return &lr->token;
+ }
+
+ /* Match escape sequences. */
+ if (ch == lr->escape_char)
+ return get_toplvl_escape (lr);
+
+ /* Match ellipsis. */
+ if (ch == '.' && strncmp (&lr->buf[lr->idx], "..", 2) == 0)
+ {
+ lr_getc (lr);
+ lr_getc (lr);
+ lr->token.tok = tok_ellipsis;
+ return &lr->token;
+ }
+
+ switch (ch)
+ {
+ case '<':
+ return get_symname (lr);
+
+ case '0' ... '9':
+ lr->token.tok = tok_number;
+ lr->token.val.num = ch - '0';
+
+ while (isdigit (ch = lr_getc (lr)))
+ {
+ lr->token.val.num *= 10;
+ lr->token.val.num += ch - '0';
+ }
+ if (isalpha (ch))
+ lr_error (lr, _("garbage at end of digit"));
+ lr_ungetn (lr, 1);
+
+ return &lr->token;
+
+ case ';':
+ lr->token.tok = tok_semicolon;
+ return &lr->token;
+
+ case ',':
+ lr->token.tok = tok_comma;
+ return &lr->token;
+
+ case '(':
+ lr->token.tok = tok_open_brace;
+ return &lr->token;
+
+ case ')':
+ lr->token.tok = tok_close_brace;
+ return &lr->token;
+
+ case '"':
+ return get_string (lr, charset);
+
+ case '-':
+ ch = lr_getc (lr);
+ if (ch == '1')
+ {
+ lr->token.tok = tok_minus1;
+ return &lr->token;
+ }
+ lr_ungetn (lr, 2);
+ break;
+ }
+
+ return get_ident (lr);
+}
+
+
+static struct token *
+get_toplvl_escape (struct linereader *lr)
+{
+ /* This is supposed to be a numeric value. We return the
+ numerical value and the number of bytes. */
+ size_t start_idx = lr->idx - 1;
+ unsigned int value = 0;
+ int nbytes = 0;
+ int ch;
+
+ do
+ {
+ unsigned int byte = 0;
+ unsigned int base = 8;
+
+ ch = lr_getc (lr);
+
+ if (ch == 'd')
+ {
+ base = 10;
+ ch = lr_getc (lr);
+ }
+ else if (ch == 'x')
+ {
+ base = 16;
+ ch = lr_getc (lr);
+ }
+
+ if ((base == 16 && !isxdigit (ch))
+ || (base != 16 && (ch < '0' || ch >= '0' + base)))
+ {
+ esc_error:
+ lr->token.val.str.start = &lr->buf[start_idx];
+
+ while (ch != EOF || !isspace (ch))
+ ch = lr_getc (lr);
+ lr->token.val.str.len = lr->idx - start_idx;
+
+ lr->token.tok = tok_error;
+ return &lr->token;
+ }
+
+ if (isdigit (ch))
+ byte = ch - '0';
+ else
+ byte = tolower (ch) - 'a' + 10;
+
+ ch = lr_getc (lr);
+ if ((base == 16 && !isxdigit (ch))
+ || (base != 16 && (ch < '0' || ch >= '0' + base)))
+ goto esc_error;
+
+ byte *= base;
+ if (isdigit (ch))
+ byte += ch - '0';
+ else
+ byte += tolower (ch) - 'a' + 10;
+
+ ch = lr_getc (lr);
+ if (base != 16 && isdigit (ch))
+ {
+ byte *= base;
+ base += ch - '0';
+
+ ch = lr_getc (lr);
+ }
+
+ value *= 256;
+ value += byte;
+
+ ++nbytes;
+ }
+ while (ch == lr->escape_char && nbytes < 4);
+
+ if (!isspace (ch))
+ lr_error (lr, _("garbage at end of character code specification"));
+
+ lr_ungetn (lr, 1);
+
+ lr->token.tok = tok_charcode;
+ lr->token.val.charcode.val = value;
+ lr->token.val.charcode.nbytes = nbytes;
+
+ return &lr->token;
+}
+
+
+#define ADDC(ch) \
+ do \
+ { \
+ if (bufact == bufmax) \
+ { \
+ bufmax *= 2; \
+ buf = xrealloc (buf, bufmax); \
+ } \
+ buf[bufact++] = (ch); \
+ } \
+ while (0)
+
+
+static struct token *
+get_symname (struct linereader *lr)
+{
+ /* Symbol in brackets. We must distinguish three kinds:
+ 1. reserved words
+ 2. ISO 10646 position values
+ 3. all other. */
+ char *buf;
+ size_t bufact = 0;
+ size_t bufmax = 56;
+ const struct keyword_t *kw;
+ int ch;
+
+ buf = (char *) xmalloc (bufmax);
+
+ do
+ {
+ ch = lr_getc (lr);
+ if (ch == lr->escape_char)
+ {
+ int c2 = lr_getc (lr);
+ ADDC (c2);
+
+ if (c2 == '\n')
+ ch = '\n';
+ }
+ else
+ ADDC (ch);
+ }
+ while (ch != '>' && ch != '\n');
+
+ if (ch == '\n')
+ lr_error (lr, _("unterminated symbolic name"));
+
+ /* Test for ISO 10646 position value. */
+ if (buf[0] == 'U' && (bufact == 6 || bufact == 10))
+ {
+ char *cp = buf + 1;
+ while (cp < &buf[bufact - 1] && isxdigit (*cp))
+ ++cp;
+
+ if (cp == &buf[bufact - 1])
+ {
+ /* Yes, it is. */
+ lr->token.tok = bufact == 6 ? tok_ucs2 : tok_ucs4;
+ lr->token.val.charcode.val = strtoul (buf, NULL, 16);
+ lr->token.val.charcode.nbytes = lr->token.tok == tok_ucs2 ? 2 : 4;
+
+ return &lr->token;
+ }
+ }
+
+ /* It is a symbolic name. Test for reserved words. */
+ kw = lr->hash_fct (buf, bufact - 1);
+
+ if (kw != NULL && kw->symname_or_ident == 1)
+ {
+ lr->token.tok = kw->token;
+ free (buf);
+ }
+ else
+ {
+ lr->token.tok = tok_bsymbol;
+
+ buf[bufact] = '\0';
+ buf = xrealloc (buf, bufact + 1);
+
+ lr->token.val.str.start = buf;
+ lr->token.val.str.len = bufact - 1;
+ }
+
+ return &lr->token;
+}
+
+
+static struct token *
+get_ident (struct linereader *lr)
+{
+ char *buf;
+ size_t bufact;
+ size_t bufmax = 56;
+ const struct keyword_t *kw;
+ int ch;
+
+ buf = xmalloc (bufmax);
+ bufact = 0;
+
+ ADDC (lr->buf[lr->idx - 1]);
+
+ while (!isspace ((ch = lr_getc (lr))) && ch != '"' && ch != ';'
+ && ch != '<' && ch != ',')
+ /* XXX Handle escape sequences? */
+ ADDC (ch);
+
+ lr_ungetn (lr, 1);
+
+ kw = lr->hash_fct (buf, bufact);
+
+ if (kw != NULL && kw->symname_or_ident == 0)
+ {
+ lr->token.tok = kw->token;
+ free (buf);
+ }
+ else
+ {
+ lr->token.tok = tok_ident;
+
+ buf[bufact] = '\0';
+ buf = xrealloc (buf, bufact + 1);
+
+ lr->token.val.str.start = buf;
+ lr->token.val.str.len = bufact;
+ }
+
+ return &lr->token;
+}
+
+
+static struct token *
+get_string (struct linereader *lr, const struct charset_t *charset)
+{
+ int illegal_string = 0;
+ char *buf, *cp;
+ size_t bufact;
+ size_t bufmax = 56;
+ int ch;
+
+ buf = xmalloc (bufmax);
+ bufact = 0;
+
+ while ((ch = lr_getc (lr)) != '"' && ch != '\n' && ch != EOF)
+ if (ch != '<' || charset == NULL)
+ {
+ if (ch == lr->escape_char)
+ {
+ ch = lr_getc (lr);
+ if (ch == '\n' || ch == EOF)
+ break;
+ }
+ ADDC (ch);
+ }
+ else
+ {
+ /* We have to get the value of the symbol. */
+ unsigned int value;
+ size_t startidx = bufact;
+
+ if (!lr->translate_strings)
+ ADDC ('<');
+
+ while ((ch = lr_getc (lr)) != '>' && ch != '\n' && ch != EOF)
+ {
+ if (ch == lr->escape_char)
+ {
+ ch = lr_getc (lr);
+ if (ch == '\n' || ch == EOF)
+ break;
+ }
+ ADDC (ch);
+ }
+
+ if (ch == '\n' || ch == EOF)
+ lr_error (lr, _("unterminated string"));
+ else
+ if (!lr->translate_strings)
+ ADDC ('>');
+
+ if (lr->translate_strings)
+ {
+ value = charset_find_value (charset, &buf[startidx],
+ bufact - startidx);
+ if (value == ILLEGAL_CHAR_VALUE)
+ illegal_string = 1;
+ bufact = startidx;
+
+ if (bufmax - bufact < 8)
+ {
+ bufmax *= 2;
+ buf = (char *) xrealloc (buf, bufmax);
+ }
+
+ cp = &buf[bufact];
+ if (encode_char (value, &cp))
+ illegal_string = 1;
+
+ bufact = cp - buf;
+ }
+ }
+
+ /* Catch errors with trailing escape character. */
+ if (bufact > 0 && buf[bufact - 1] == lr->escape_char
+ && (bufact == 1 || buf[bufact - 2] != lr->escape_char))
+ {
+ lr_error (lr, _("illegal escape sequence at end of string"));
+ --bufact;
+ }
+ else if (ch == '\n' || ch == EOF)
+ lr_error (lr, _("unterminated string"));
+
+ /* Terminate string if necessary. */
+ if (lr->translate_strings)
+ {
+ cp = &buf[bufact];
+ if (encode_char (0, &cp))
+ illegal_string = 1;
+
+ bufact = cp - buf;
+ }
+ else
+ ADDC ('\0');
+
+ lr->token.tok = tok_string;
+
+ if (illegal_string)
+ {
+ free (buf);
+ lr->token.val.str.start = NULL;
+ lr->token.val.str.len = 0;
+ }
+ else
+ {
+ buf = xrealloc (buf, bufact + 1);
+
+ lr->token.val.str.start = buf;
+ lr->token.val.str.len = bufact;
+ }
+
+ return &lr->token;
+}
diff --git a/locale/programs/linereader.h b/locale/programs/linereader.h
new file mode 100644
index 0000000000..b78697e87d
--- /dev/null
+++ b/locale/programs/linereader.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifndef _LINEREADER_H
+#define _LINEREADER_H 1
+
+#include <ctype.h>
+#include <libintl.h>
+#include <stdio.h>
+
+#include "error.h"
+#include "locfile-token.h"
+
+
+typedef const struct keyword_t *(*kw_hash_fct_t) (const char *, int);
+struct charset_t;
+
+
+struct token
+{
+ enum token_t tok;
+ union
+ {
+ struct
+ {
+ char *start;
+ size_t len;
+ } str;
+ unsigned long int num;
+ struct
+ {
+ unsigned int val;
+ int nbytes;
+ } charcode;
+ } val;
+};
+
+
+struct linereader
+{
+ FILE *fp;
+ const char *fname;
+ char *buf;
+ size_t bufsize;
+ size_t bufact;
+ size_t lineno;
+
+ size_t idx;
+
+ char comment_char;
+ char escape_char;
+
+ struct token token;
+
+ int translate_strings;
+
+ kw_hash_fct_t hash_fct;
+};
+
+
+/* Functions defined in linereader.c. */
+struct linereader *lr_open (const char *fname, kw_hash_fct_t hf);
+int lr_eof (struct linereader *lr);
+void lr_close (struct linereader *lr);
+int lr_next (struct linereader *lr);
+struct token *lr_token (struct linereader *lr,
+ const struct charset_t *charset);
+
+
+#define lr_error(lr, fmt, args...) \
+ error_at_line (0, 0, lr->fname, lr->lineno, fmt, ## args)
+
+
+
+static inline int
+lr_getc (struct linereader *lr)
+{
+ if (lr->idx == lr->bufact)
+ {
+ if (lr->bufact != 0)
+ if (lr_next (lr) < 0)
+ return EOF;
+
+ if (lr->bufact == 0)
+ return EOF;
+ }
+
+ return lr->buf[lr->idx] == '\32' ? EOF : lr->buf[lr->idx++];
+}
+
+
+static inline int
+lr_ungetc (struct linereader *lr, int ch)
+{
+ if (lr->idx == 0)
+ return -1;
+
+ lr->buf[--lr->idx] = ch;
+ return 0;
+}
+
+
+static inline int
+lr_ungetn (struct linereader *lr, int n)
+{
+ if (lr->idx < n)
+ return -1;
+
+ lr->idx -= n;
+ return 0;
+}
+
+
+static inline void
+lr_ignore_rest (struct linereader *lr, int verbose)
+{
+ if (verbose)
+ {
+ while (isspace (lr->buf[lr->idx]) && lr->buf[lr->idx] != '\n'
+ && lr->buf[lr->idx] != lr->comment_char)
+ if (lr->buf[lr->idx] == '\0')
+ {
+ if (lr_next (lr) < 0)
+ return;
+ }
+ else
+ ++lr->idx;
+
+ if (lr->buf[lr->idx] != '\n' &&lr->buf[lr->idx] != lr->comment_char)
+ lr_error (lr, _("trailing garbage at end of line"));
+ }
+
+ /* Ignore continued line. */
+ while (lr->bufact > 0 && lr->buf[lr->bufact - 1] != '\n')
+ if (lr_next (lr) < 0)
+ break;
+
+ lr->idx = lr->bufact;
+}
+
+
+#endif /* linereader.h */
diff --git a/locale/programs/locale.c b/locale/programs/locale.c
new file mode 100644
index 0000000000..4e4ff83a37
--- /dev/null
+++ b/locale/programs/locale.c
@@ -0,0 +1,544 @@
+/* Copyright (C) 1995, 1996 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 <errno.h>
+
+/*#include "localedef.h"*/
+#include "localeinfo.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
+
+/* XXX Hack */
+struct cat_item
+{
+ int item_id;
+ const char *name;
+ enum { std, opt } status;
+ enum value_type value_type;
+ int min;
+ int max;
+};
+
+
+/* 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 "locale/aux/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 "locale/aux/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};");
+}
diff --git a/locale/programs/localedef.c b/locale/programs/localedef.c
new file mode 100644
index 0000000000..a98bac4301
--- /dev/null
+++ b/locale/programs/localedef.c
@@ -0,0 +1,461 @@
+/* Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "error.h"
+#include "charset.h"
+#include "locfile.h"
+
+/* Undefine the following line in the production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+
+/* List of locale definition files which are used in `copy' instructions. */
+struct copy_def_list_t
+{
+ struct copy_def_list_t *next;
+
+ const char *name;
+ int mask;
+
+ struct localedef_t *locale;
+
+ struct
+ {
+ void *data;
+ size_t len;
+ } binary[6];
+};
+
+
+/* List of copied locales. */
+struct copy_def_list_t *copy_list;
+
+/* If this is defined be POSIX conform. */
+int posix_conformance;
+
+/* Name of the running program. */
+const char *program_name;
+
+/* If not zero give a lot more messages. */
+int verbose;
+
+
+
+/* Long options. */
+static const struct option long_options[] =
+{
+ { "charmap", required_argument, NULL, 'f' },
+ { "code-set-name", required_argument, NULL, 'u' },
+ { "help", no_argument, NULL, 'h' },
+ { "force", no_argument, NULL, 'c' },
+ { "inputfile", required_argument, NULL, 'i' },
+ { "posix", no_argument, &posix_conformance, 1 },
+ { "verbose", no_argument, &verbose, 1},
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* Prototypes for global functions. */
+void *xmalloc (size_t __n);
+
+/* Prototypes for local functions. */
+static void usage (int status) __attribute__ ((noreturn));
+static void error_print (void);
+static const char *construct_output_path (const char *path);
+
+
+int
+main (int argc, char *argv[])
+{
+ int optchar;
+ int do_help = 0;
+ int do_version = 0;
+ int force_output = 0;
+ const char *charmap_file = NULL;
+ const char *input_file = NULL;
+ const char *ucs_csn = NULL;
+ const char *output_path;
+ int cannot_write_why;
+ struct charset_t *charset;
+ struct localedef_t *localedef;
+ struct copy_def_list_t *act_add_locdef;
+
+ /* Set initial values for global varaibles. */
+ copy_list = NULL;
+ posix_conformance = getenv ("POSIXLY_CORRECT") != NULL;
+ program_name = argv[0];
+ error_print_progname = error_print;
+ 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. */
+#if 0
+ /* In the final version for glibc we can use the variable. */
+ textdomain (_libc_intl_domainname);
+#else
+ textdomain ("SYS_libc");
+#endif
+
+ while ((optchar = getopt_long (argc, argv, "cf:hi:u:vV", long_options, NULL))
+ != EOF)
+ switch (optchar)
+ {
+ case '\0': /* Long option. */
+ break;
+
+ case 'c':
+ force_output = 1;
+ break;
+
+ case 'f':
+ charmap_file = optarg;
+ break;
+
+ case 'h':
+ do_help = 1;
+ break;
+
+ case 'i':
+ input_file = optarg;
+ break;
+
+ case 'u':
+ ucs_csn = optarg;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'V':
+ do_version = 1;
+ break;
+
+ default:
+ usage (4); /* A value >3 is forced by POSIX. */
+ break;
+ }
+
+ /* 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, "%s - GNU %s %s\n", program_name, PACKAGE, VERSION);
+ exit (0);
+ }
+
+ /* Help is requested. */
+ if (do_help)
+ /* Possible violation: POSIX.2 4.35.8 defines the return value 0 as
+ "No errors occured and the locale(s) were successfully created."
+ But giving a other value than 0 does not make sense here. It
+ is perhaps not that important because POSIX does not specify the
+ -h option for localedef. */
+ usage (0);
+
+ if (argc - optind != 1)
+ /* We need exactly one non-option parameter. */
+ usage (4);
+
+ /* The parameter describes the output path of the constructed files.
+ If the described files cannot be written return a NULL pointer. */
+ output_path = construct_output_path (argv[optind]);
+ cannot_write_why = errno;
+
+ /* Now that the parameters are processed we have to reset the local
+ ctype locale. (P1003.2 4.35.5.2) */
+ setlocale (LC_CTYPE, "POSIX");
+
+ /* Look whether the system really allows locale definitions. POSIX
+ defines error code 3 for this situation so I think it must be
+ a fatal error (see P1003.2 4.35.8). */
+ if (sysconf (_SC_2_LOCALEDEF) < 0)
+ error (3, 0, _("FATAL: system does not define `_POSIX2_LOCALEDEF'"));
+
+ /* Process charmap file. */
+ charset = charmap_read (charmap_file);
+
+ /* Now read the locale file. */
+ localedef = locfile_read (input_file, charset);
+ if (localedef->failed != 0)
+ error (4, errno, _("cannot open locale definition file `%s'"), input_file);
+
+ /* Perhaps we saw some `copy' instructions. Process the given list.
+ We use a very simple algorithm: we look up the list from the
+ beginning every time. */
+ do
+ {
+ int cat;
+
+ for (act_add_locdef = copy_list; act_add_locdef != NULL;
+ act_add_locdef = act_add_locdef->next)
+ {
+ for (cat = LC_COLLATE; cat <= LC_MESSAGES; ++cat)
+ if ((act_add_locdef->mask & (1 << cat)) != 0)
+ {
+ act_add_locdef->mask &= ~(1 << cat);
+ break;
+ }
+ if (cat <= LC_MESSAGES)
+ break;
+ }
+
+ if (act_add_locdef != NULL)
+ {
+ int avail = 0;
+
+ if (act_add_locdef->locale == NULL)
+ act_add_locdef->locale = locfile_read (act_add_locdef->name,
+ charset);
+
+ if (! act_add_locdef->locale->failed)
+ {
+ avail = act_add_locdef->locale->categories[cat].generic != NULL;
+ if (avail)
+ localedef->categories[cat].generic
+ = act_add_locdef->locale->categories[cat].generic;
+ }
+
+ if (! avail)
+ {
+ const char *locale_names[] = { "LC_COLLATE", "LC_CTYPE",
+ "LC_MONETARY", "LC_NUMERIC",
+ "LC_TIME", "LC_MESSAGES" };
+ char *fname;
+ int fd;
+ struct stat st;
+
+ asprintf (&fname, LOCALE_PATH "/%s/%s", act_add_locdef->name,
+ locale_names[cat]);
+ fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ {
+ free (fname);
+
+ asprintf (&fname, LOCALE_PATH "/%s/%s/SYS_%s",
+ act_add_locdef->name, locale_names[cat],
+ locale_names[cat]);
+
+ fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ error (5, 0, _("\
+locale file `%s', used in `copy' statement, not found"),
+ act_add_locdef->name);
+ }
+
+ if (fstat (fd, &st) < 0)
+ error (5, errno, _("\
+cannot `stat' locale file `%s'"),
+ fname);
+
+ localedef->len[cat] = st.st_size;
+ localedef->categories[cat].generic
+ = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (localedef->categories[cat].generic == (void *) -1)
+ {
+ size_t left = st.st_size;
+ void *read_ptr;
+
+ localedef->categories[cat].generic
+ = xmalloc (st.st_size);
+ read_ptr = localedef->categories[cat].generic;
+
+ do
+ {
+ long int n;
+ n = read (fd, read_ptr, left);
+ if (n == 1)
+ error (5, errno, _("cannot read locale file `%s'"),
+ fname);
+ read_ptr += n;
+ left -= n;
+ }
+ while (left > 0);
+ }
+
+ close (fd);
+ free (fname);
+
+ localedef->binary |= 1 << cat;
+ }
+ }
+ }
+ while (act_add_locdef != NULL);
+
+ /* Check the categories we processed in source form. */
+ check_all_categories (localedef, charset);
+
+ /* We are now able to write the data files. If warning were given we
+ do it only if it is explicitly requested (--force). */
+ if (error_message_count == 0 || force_output != 0)
+ {
+ if (cannot_write_why != 0)
+ error (4, cannot_write_why, _("cannot write output files to `%s'"),
+ output_path);
+ else
+ write_all_categories (localedef, output_path);
+ }
+ else
+ error (4, 0, _("no output file produced because warning were issued"));
+
+ /* This exit status is prescribed by POSIX.2 4.35.7. */
+ exit (error_message_count != 0);
+}
+
+
+void
+def_to_process (const char *name, int category)
+{
+ struct copy_def_list_t *new, **rp;
+
+ for (rp = &copy_list; *rp != NULL; rp = &(*rp)->next)
+ if (strcmp (name, (*rp)->name) == 0)
+ break;
+
+ if (*rp == NULL)
+ {
+ size_t cnt;
+
+ *rp = (struct copy_def_list_t *) xmalloc (sizeof (**rp));
+
+ (*rp)->next = NULL;
+ (*rp)->name = name;
+ (*rp)->mask = 0;
+ (*rp)->locale = NULL;
+
+ for (cnt = 0; cnt < 6; ++cnt)
+ {
+ (*rp)->binary[cnt].data = NULL;
+ (*rp)->binary[cnt].len = 0;
+ }
+ }
+ new = *rp;
+
+ if ((new->mask & category) != 0)
+ /* We already have the information. This cannot happen. */
+ error (5, 0, _("\
+category data requested more than once: should not happen"));
+
+ new->mask |= category;
+}
+
+
+/* Display usage information and exit. */
+static void
+usage (int status)
+{
+ if (status != 0)
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
+ else
+ printf (_("\
+Usage: %s [OPTION]... name\n\
+Mandatory arguments to long options are mandatory for short options too.\n\
+ -c, --force create output even if warning messages were issued\n\
+ -h, --help display this help and exit\n\
+ -f, --charmap=FILE symbolic character names defined in FILE\n\
+ -i, --inputfile=FILE source definitions are found in FILE\n\
+ -u, --code-set-name=NAME specify code set for mapping ISO 10646 elements\n\
+ -v, --verbose print more messages\n\
+ -V, --version output version information and exit\n\
+ --posix be strictly POSIX conform\n\
+\n\
+System's directory for character maps: %s\n\
+ locale files : %s\n"),
+ program_name, CHARMAP_PATH, LOCALE_PATH);
+
+ exit (status);
+}
+
+
+/* The address of this function will be assigned to the hook in the error
+ functions. */
+static void
+error_print ()
+{
+ /* We don't want the program name to be printed in messages. Emacs'
+ compile.el does not like this. */
+}
+
+
+/* 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 const char *
+construct_output_path (const char *path)
+{
+ char *result;
+
+ if (strchr (path, '/') == NULL)
+ {
+ /* This is a system path. */
+ int path_max_len = pathconf (LOCALE_PATH, _PC_PATH_MAX) + 1;
+ result = (char *) xmalloc (path_max_len);
+
+ snprintf (result, path_max_len, "%s/%s", LOCALE_PATH, path);
+ }
+ else
+ {
+ char *t;
+ /* This is a user path. */
+ result = xmalloc (strlen (path) + 2);
+ t = stpcpy (result, path);
+ *t = '\0';
+ }
+
+ errno = 0;
+
+ if (euidaccess (result, W_OK) == -1)
+ /* Perhaps the directory does not exist now. Try to create it. */
+ if (errno == ENOENT)
+ {
+ errno = 0;
+ mkdir (result, 0777);
+ }
+
+ strcat (result, "/");
+
+ return result;
+}
diff --git a/locale/programs/locales.h b/locale/programs/locales.h
new file mode 100644
index 0000000000..3c7676b765
--- /dev/null
+++ b/locale/programs/locales.h
@@ -0,0 +1,207 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifndef _LOCALES_H
+#define _LOCALES_H
+
+#include <ctype.h>
+
+/* Undefine following line in production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#include "linereader.h"
+#include "locfile-token.h"
+#include "charset.h"
+#include "locfile.h"
+#include "localeinfo.h"
+
+
+/* Header of the locale data files. */
+struct locale_file
+{
+ int magic;
+ int n;
+};
+
+
+/* Handle LC_CTYPE category. */
+
+static inline unsigned int
+charclass_to_bit (enum token_t tok)
+{
+ static unsigned int lastbit = _ISalnum;
+
+ switch (tok)
+ {
+#define CLASS(name) case tok_##name: return _IS##name
+ CLASS (upper);
+ CLASS (lower);
+ CLASS (alpha);
+ CLASS (digit);
+ CLASS (alnum);
+ CLASS (space);
+ CLASS (cntrl);
+ CLASS (punct);
+ CLASS (graph);
+ CLASS (print);
+ CLASS (xdigit);
+ CLASS (blank);
+#undef CLASS
+ case tok_string:
+ lastbit <<= 1;
+ if (lastbit == 0ul)
+ /* Exit status 2 means a limitation in the implementation is
+ exceeded. */
+ error (2, 0, _("too many character classes defined"));
+ return lastbit;
+ default:
+ assert (1 == 0);
+ }
+ return 0;
+}
+
+/* Remember name of newly created charclass. */
+void ctype_startup (struct linereader *lr, struct localedef_t *locale,
+ struct charset_t *__charset);
+void ctype_finish (struct localedef_t *__locale, struct charset_t *__charset);
+
+void ctype_output (struct localedef_t *locale, const char *output_path);
+
+int ctype_is_charclass (struct linereader *lr, struct localedef_t *locale,
+ const char *__name);
+void ctype_class_new (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, struct token *__code,
+ struct charset_t *__charset);
+void ctype_class_start (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, const char *__name,
+ struct charset_t *__charset);
+void ctype_class_from (struct linereader *lr, struct localedef_t *locale,
+ struct token *__code, struct charset_t *__charset);
+void ctype_class_to (struct linereader *lr, struct localedef_t *locale,
+ struct token *__code, struct charset_t *__charset);
+void ctype_class_end (struct linereader *lr, struct localedef_t *locale);
+
+int ctype_is_charmap (struct linereader *lr, struct localedef_t *locale,
+ const char *__name);
+void ctype_map_new (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, struct token *__code,
+ struct charset_t *__charset);
+void ctype_map_start (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, const char *__name,
+ struct charset_t *__charset);
+void ctype_map_from (struct linereader *lr, struct localedef_t *locale,
+ struct token *__code, struct charset_t *__charset);
+void ctype_map_to (struct linereader *lr, struct localedef_t *locale,
+ struct token *__code, struct charset_t *__charset);
+void ctype_map_end (struct linereader *lr, struct localedef_t *locale);
+
+
+/* Handle LC_COLLATE category. */
+
+void collate_startup (struct linereader *__lr, struct localedef_t *__locale,
+ struct charset_t *__charset);
+
+void collate_finish (struct localedef_t *__locale,
+ struct charset_t *__charset);
+
+void collate_output (struct localedef_t *locale, const char *output_path);
+
+void collate_element_to (struct linereader *__lr, struct localedef_t *__locale,
+ struct token *__code, struct charset_t *__charset);
+void collate_element_from (struct linereader *__lr,
+ struct localedef_t *__locale, struct token *__code,
+ struct charset_t *__charset);
+void collate_symbol (struct linereader *__lr, struct localedef_t *__locale,
+ struct token *__code, struct charset_t *__charset);
+void collate_new_order (struct linereader *__lr, struct localedef_t *__locale,
+ enum coll_sort_rule __sort_rule);
+void collate_build_arrays (struct linereader *__lr,
+ struct localedef_t *__locale);
+int collate_order_elem (struct linereader *__lr, struct localedef_t *__locale,
+ struct token *__code, struct charset_t *__charset);
+int collate_weight_bsymbol (struct linereader *__lr,
+ struct localedef_t *__locale,
+ struct token *__code, struct charset_t *__charset);
+int collate_next_weight (struct linereader *__lr,
+ struct localedef_t *__locale);
+int collate_simple_weight (struct linereader *__lr,
+ struct localedef_t *__locale,
+ struct token *__code, struct charset_t *__charset);
+void collate_end_weight (struct linereader *__lr,
+ struct localedef_t *__locale);
+
+
+/* Handle LC_MONETARY category. */
+
+void monetary_startup (struct linereader *__lr, struct localedef_t *__locale,
+ struct charset_t *__charset);
+
+void monetary_finish (struct localedef_t *__locale);
+
+void monetary_output (struct localedef_t *locale, const char *output_path);
+
+void monetary_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, struct token *__code,
+ struct charset_t *__charset);
+
+
+/* Handle LC_NUMERIC category. */
+
+void numeric_startup (struct linereader *__lr, struct localedef_t *__locale,
+ struct charset_t *__charset);
+
+void numeric_finish (struct localedef_t *__locale);
+
+void numeric_output (struct localedef_t *locale, const char *output_path);
+
+void numeric_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, struct token *__code,
+ struct charset_t *__charset);
+
+
+/* Handle LC_TIME category. */
+
+void time_startup (struct linereader *__lr, struct localedef_t *__locale,
+ struct charset_t *__charset);
+
+void time_finish (struct localedef_t *__locale);
+
+void time_output (struct localedef_t *locale, const char *output_path);
+
+void time_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, struct token *__code,
+ struct charset_t *__charset);
+
+
+/* Handle LC_MESSAGES category. */
+
+void messages_startup (struct linereader *__lr, struct localedef_t *__locale,
+ struct charset_t *__charset);
+
+void messages_finish (struct localedef_t *__locale);
+
+void messages_output (struct localedef_t *locale, const char *output_path);
+
+void messages_add (struct linereader *lr, struct localedef_t *locale,
+ enum token_t __tok, struct token *__code,
+ struct charset_t *__charset);
+
+
+#endif /* locales.h */
diff --git a/locale/programs/locfile-kw.gperf b/locale/programs/locfile-kw.gperf
new file mode 100644
index 0000000000..85e031c777
--- /dev/null
+++ b/locale/programs/locfile-kw.gperf
@@ -0,0 +1,99 @@
+%{
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+
+#include "locfile-token.h"
+%}
+struct keyword_t ;
+%%
+escape_char, tok_escape_char, 0
+comment_char, tok_comment_char, 0
+LC_CTYPE, tok_lc_ctype, 0
+END, tok_end, 0
+copy, tok_copy, 0
+upper, tok_upper, 0
+lower, tok_lower, 0
+alpha, tok_alpha, 0
+digit, tok_digit, 0
+alnum, tok_alnum, 0
+space, tok_space, 0
+cntrl, tok_cntrl, 0
+punct, tok_punct, 0
+graph, tok_graph, 0
+print, tok_print, 0
+xdigit, tok_xdigit, 0
+blank, tok_blank, 0
+charclass, tok_charclass, 0
+charmap, tok_charmap, 0
+toupper, tok_toupper, 0
+tolower, tok_tolower, 0
+LC_COLLATE, tok_lc_collate, 0
+collating-element, tok_collating_element, 0
+collating-symbol, tok_collating_symbol, 0
+order_start, tok_order_start, 0
+order_end, tok_order_end, 0
+from, tok_from, 0
+forward, tok_forward, 0
+backward, tok_backward, 0
+position, tok_position, 0
+UNDEFINED, tok_undefined, 0
+IGNORE, tok_ignore, 0
+LC_MONETARY, tok_lc_monetary, 0
+int_curr_symbol, tok_int_curr_symbol, 0
+currency_symbol, tok_currency_symbol, 0
+mon_decimal_point, tok_mon_decimal_point, 0
+mon_thousands_sep, tok_mon_thousands_sep, 0
+mon_grouping, tok_mon_grouping, 0
+positive_sign, tok_positive_sign, 0
+negative_sign, tok_negative_sign, 0
+int_frac_digits, tok_int_frac_digits, 0
+frac_digits, tok_frac_digits, 0
+p_cs_precedes, tok_p_cs_precedes, 0
+p_sep_by_space, tok_p_sep_by_space, 0
+n_cs_precedes, tok_n_cs_precedes, 0
+n_sep_by_space, tok_n_sep_by_space, 0
+p_sign_posn, tok_p_sign_posn, 0
+n_sign_posn, tok_n_sign_posn, 0
+LC_NUMERIC, tok_lc_numeric, 0
+decimal_point, tok_decimal_point, 0
+thousands_sep, tok_thousands_sep, 0
+grouping, tok_grouping, 0
+LC_TIME, tok_lc_time, 0
+abday, tok_abday, 0
+day, tok_day, 0
+abmon, tok_abmon, 0
+mon, tok_mon, 0
+d_t_fmt, tok_d_t_fmt, 0
+d_fmt, tok_d_fmt, 0
+t_fmt, tok_t_fmt, 0
+am_pm, tok_am_pm, 0
+t_fmt_ampm, tok_t_fmt_ampm, 0
+era, tok_era, 0
+era_year, tok_era_year, 0
+era_d_fmt, tok_era_d_fmt, 0
+era_d_t_fmt, tok_era_d_t_fmt, 0
+era_t_fmt, tok_era_t_fmt, 0
+alt_digits, tok_alt_digits, 0
+LC_MESSAGES, tok_lc_messages, 0
+yesexpr, tok_yesexpr, 0
+noexpr, tok_noexpr, 0
+yesstr, tok_yesstr, 0
+nostr, tok_nostr, 0
diff --git a/locale/programs/locfile-kw.h b/locale/programs/locfile-kw.h
new file mode 100644
index 0000000000..c892669893
--- /dev/null
+++ b/locale/programs/locfile-kw.h
@@ -0,0 +1,211 @@
+/* C code produced by gperf version 2.5 (GNU C++ version) */
+/* Command-line: gperf -acCgopt -k1,2,5,$ -N locfile_hash programs/locfile-kw.gperf */
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+
+#include "locfile-token.h"
+struct keyword_t ;
+
+#define TOTAL_KEYWORDS 73
+#define MIN_WORD_LENGTH 3
+#define MAX_WORD_LENGTH 17
+#define MIN_HASH_VALUE 3
+#define MAX_HASH_VALUE 185
+/* maximum key range = 183, duplicates = 0 */
+
+#ifdef __GNUC__
+inline
+#endif
+static unsigned int
+hash (register const char *str, register int len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 0, 0, 0,
+ 0, 0, 186, 0, 186, 186, 0, 186, 0, 35,
+ 186, 186, 0, 0, 0, 5, 186, 186, 186, 0,
+ 186, 186, 186, 186, 186, 15, 186, 0, 0, 5,
+ 15, 10, 55, 30, 15, 75, 186, 20, 5, 40,
+ 10, 0, 0, 186, 35, 30, 0, 70, 186, 10,
+ 20, 75, 186, 186, 186, 186, 186, 186,
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ case 5:
+ hval += asso_values[(int) str[4]];
+ case 4:
+ case 3:
+ case 2:
+ hval += asso_values[(int) str[1]];
+ case 1:
+ hval += asso_values[(int) str[0]];
+ break;
+ }
+ return hval + asso_values[(int) str[len - 1]];
+}
+
+#ifdef __GNUC__
+inline
+#endif
+const struct keyword_t *
+locfile_hash (register const char *str, register int len)
+{
+ static const struct keyword_t wordlist[] =
+ {
+ {"",}, {"",}, {"",},
+ {"END", tok_end, 0},
+ {"",}, {"",},
+ {"IGNORE", tok_ignore, 0},
+ {"LC_TIME", tok_lc_time, 0},
+ {"LC_CTYPE", tok_lc_ctype, 0},
+ {"",},
+ {"alpha", tok_alpha, 0},
+ {"LC_MESSAGES", tok_lc_messages, 0},
+ {"",}, {"",},
+ {"UNDEFINED", tok_undefined, 0},
+ {"LC_NUMERIC", tok_lc_numeric, 0},
+ {"",}, {"",},
+ {"position", tok_position, 0},
+ {"",},
+ {"t_fmt", tok_t_fmt, 0},
+ {"",},
+ {"collating-element", tok_collating_element, 0},
+ {"positive_sign", tok_positive_sign, 0},
+ {"",},
+ {"abmon", tok_abmon, 0},
+ {"collating-symbol", tok_collating_symbol, 0},
+ {"",}, {"",}, {"",},
+ {"cntrl", tok_cntrl, 0},
+ {"",}, {"",},
+ {"backward", tok_backward, 0},
+ {"",},
+ {"d_fmt", tok_d_fmt, 0},
+ {"",}, {"",}, {"",},
+ {"p_sep_by_space", tok_p_sep_by_space, 0},
+ {"print", tok_print, 0},
+ {"",},
+ {"toupper", tok_toupper, 0},
+ {"negative_sign", tok_negative_sign, 0},
+ {"",},
+ {"LC_COLLATE", tok_lc_collate, 0},
+ {"LC_MONETARY", tok_lc_monetary, 0},
+ {"",},
+ {"era", tok_era, 0},
+ {"n_sep_by_space", tok_n_sep_by_space, 0},
+ {"blank", tok_blank, 0},
+ {"noexpr", tok_noexpr, 0},
+ {"tolower", tok_tolower, 0},
+ {"mon", tok_mon, 0},
+ {"era_t_fmt", tok_era_t_fmt, 0},
+ {"space", tok_space, 0},
+ {"",},
+ {"mon_thousands_sep", tok_mon_thousands_sep, 0},
+ {"thousands_sep", tok_thousands_sep, 0},
+ {"",},
+ {"alt_digits", tok_alt_digits, 0},
+ {"",},
+ {"comment_char", tok_comment_char, 0},
+ {"",},
+ {"charclass", tok_charclass, 0},
+ {"t_fmt_ampm", tok_t_fmt_ampm, 0},
+ {"p_sign_posn", tok_p_sign_posn, 0},
+ {"charmap", tok_charmap, 0},
+ {"",},
+ {"era_d_fmt", tok_era_d_fmt, 0},
+ {"",},
+ {"era_d_t_fmt", tok_era_d_t_fmt, 0},
+ {"mon_decimal_point", tok_mon_decimal_point, 0},
+ {"p_cs_precedes", tok_p_cs_precedes, 0},
+ {"",},
+ {"punct", tok_punct, 0},
+ {"n_sign_posn", tok_n_sign_posn, 0},
+ {"forward", tok_forward, 0},
+ {"decimal_point", tok_decimal_point, 0},
+ {"",},
+ {"lower", tok_lower, 0},
+ {"order_start", tok_order_start, 0},
+ {"",},
+ {"n_cs_precedes", tok_n_cs_precedes, 0},
+ {"copy", tok_copy, 0},
+ {"nostr", tok_nostr, 0},
+ {"escape_char", tok_escape_char, 0},
+ {"",}, {"",}, {"",},
+ {"alnum", tok_alnum, 0},
+ {"",},
+ {"d_t_fmt", tok_d_t_fmt, 0},
+ {"day", tok_day, 0},
+ {"order_end", tok_order_end, 0},
+ {"digit", tok_digit, 0},
+ {"",}, {"",}, {"",}, {"",},
+ {"graph", tok_graph, 0},
+ {"",}, {"",},
+ {"grouping", tok_grouping, 0},
+ {"",},
+ {"currency_symbol", tok_currency_symbol, 0},
+ {"",}, {"",}, {"",}, {"",},
+ {"int_curr_symbol", tok_int_curr_symbol, 0},
+ {"",},
+ {"mon_grouping", tok_mon_grouping, 0},
+ {"",}, {"",}, {"",},
+ {"xdigit", tok_xdigit, 0},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"am_pm", tok_am_pm, 0},
+ {"yesstr", tok_yesstr, 0},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"from", tok_from, 0},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",},
+ {"upper", tok_upper, 0},
+ {"frac_digits", tok_frac_digits, 0},
+ {"yesexpr", tok_yesexpr, 0},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"abday", tok_abday, 0},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"era_year", tok_era_year, 0},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",}, {"",}, {"",},
+ {"int_frac_digits", tok_int_frac_digits, 0},
+ };
+
+ 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/programs/locfile-token.h b/locale/programs/locfile-token.h
new file mode 100644
index 0000000000..1c3cfdc9db
--- /dev/null
+++ b/locale/programs/locfile-token.h
@@ -0,0 +1,147 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifndef _TOKEN_H
+#define _TOKEN_H
+
+enum token_t
+{
+ tok_none = 0,
+
+ tok_eof,
+ tok_eol,
+ tok_bsymbol,
+ tok_ident,
+ tok_ellipsis,
+ tok_semicolon,
+ tok_comma,
+ tok_open_brace,
+ tok_close_brace,
+ tok_charcode,
+ tok_ucs2,
+ tok_ucs4,
+ tok_number,
+ tok_minus1,
+ tok_string,
+
+ tok_escape_char,
+ tok_comment_char,
+ tok_end,
+ tok_g0esc,
+ tok_g1esc,
+ tok_g2esc,
+ tok_g3esc,
+
+ tok_code_set_name,
+ tok_mb_cur_max,
+ tok_mb_cur_min,
+ tok_charmap,
+ tok_width,
+ tok_width_variable,
+ tok_width_default,
+
+ tok_lc_ctype,
+ tok_copy,
+ tok_upper,
+ tok_lower,
+ tok_alpha,
+ tok_digit,
+ tok_xdigit,
+ tok_space,
+ tok_print,
+ tok_graph,
+ tok_blank,
+ tok_cntrl,
+ tok_punct,
+ tok_alnum,
+ tok_charclass,
+ tok_toupper,
+ tok_tolower,
+ tok_lc_collate,
+ tok_collating_element,
+ tok_collating_symbol,
+ tok_order_start,
+ tok_order_end,
+ tok_from,
+ tok_forward,
+ tok_backward,
+ tok_position,
+ tok_undefined,
+ tok_ignore,
+ tok_lc_monetary,
+ tok_int_curr_symbol,
+ tok_currency_symbol,
+ tok_mon_decimal_point,
+ tok_mon_thousands_sep,
+ tok_mon_grouping,
+ tok_positive_sign,
+ tok_negative_sign,
+ tok_int_frac_digits,
+ tok_frac_digits,
+ tok_p_cs_precedes,
+ tok_p_sep_by_space,
+ tok_n_cs_precedes,
+ tok_n_sep_by_space,
+ tok_p_sign_posn,
+ tok_n_sign_posn,
+ tok_lc_numeric,
+ tok_decimal_point,
+ tok_thousands_sep,
+ tok_grouping,
+ tok_lc_time,
+ tok_abday,
+ tok_day,
+ tok_abmon,
+ tok_mon,
+ tok_d_t_fmt,
+ tok_d_fmt,
+ tok_t_fmt,
+ tok_am_pm,
+ tok_t_fmt_ampm,
+ tok_era,
+ tok_era_year,
+ tok_era_d_fmt,
+ tok_era_d_t_fmt,
+ tok_era_t_fmt,
+ tok_alt_digits,
+ tok_lc_messages,
+ tok_yesexpr,
+ tok_noexpr,
+ tok_yesstr,
+ tok_nostr,
+
+ tok_error
+};
+
+
+struct keyword_t
+{
+ const char *name;
+ enum token_t token;
+ int symname_or_ident;
+
+ /* Only for locdef file. */
+ int locale;
+ enum token_t base;
+ enum token_t group;
+ enum token_t list;
+};
+
+
+#endif /* token.h */
diff --git a/locale/programs/locfile.c b/locale/programs/locfile.c
new file mode 100644
index 0000000000..cb98a5d530
--- /dev/null
+++ b/locale/programs/locfile.c
@@ -0,0 +1,979 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <malloc.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include "locfile.h"
+#include "linereader.h"
+#include "localeinfo.h"
+#include "locales.h"
+
+
+/* Uncomment the following line in the production version. */
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+/* Define the lookup function. */
+#include "locfile-kw.h"
+
+
+/* Some useful macros. */
+#define MIN(a, b) (__extension__ ({ typeof (a) _a = (a); \
+ typeof (b) _b = (b); \
+ _a < _b ? _a : _b; }))
+
+
+void *xmalloc (size_t __n);
+char *xstrdup (const char *__str);
+
+struct localedef_t *
+locfile_read (const char *filename, struct charset_t *charset)
+{
+ struct linereader *ldfile;
+ struct localedef_t *result;
+ int state;
+ enum token_t expected_tok = tok_none;
+ const char *expected_str = NULL;
+ enum token_t ctype_tok_sym = tok_none;
+ const char *ctype_tok_str = NULL;
+ int copy_category = 0;
+ int cnt;
+
+ /* Allocate space for result. */
+ result = (struct localedef_t *) xmalloc (sizeof (struct localedef_t));
+ memset (result, '\0', sizeof (struct localedef_t));
+
+ ldfile = lr_open (filename, locfile_hash);
+ if (ldfile == NULL)
+ {
+ if (filename[0] != '/')
+ {
+ char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
+
+ stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
+ ldfile = lr_open (path, locfile_hash);
+ }
+
+ if (ldfile == NULL)
+ {
+ result->failed = 1;
+ return result;
+ }
+ }
+
+#define HANDLE_COPY(category, token, string) \
+ if (nowtok == tok_copy) \
+ { \
+ copy_category = category; \
+ expected_tok = token; \
+ expected_str = string; \
+ state = 8; \
+ continue; \
+ } \
+ ++state
+
+#define LOCALE_PROLOG(token, string) \
+ if (nowtok == tok_eol) \
+ /* Ignore empty lines. */ \
+ continue; \
+ if (nowtok == tok_end) \
+ { \
+ expected_tok = token; \
+ expected_str = string; \
+ state = 4; \
+ continue; \
+ } \
+ if (nowtok == tok_copy) \
+ goto only_copy;
+
+
+#define READ_STRING(fn, errlabel) \
+ do \
+ { \
+ arg = lr_token (ldfile, charset); \
+ if (arg->tok != tok_string) \
+ goto errlabel; \
+ fn (ldfile, result, nowtok, arg, charset); \
+ lr_ignore_rest (ldfile, 1); \
+ } \
+ while (0)
+
+#define READ_STRING_LIST(fn, errlabel) \
+ do \
+ { \
+ arg = lr_token (ldfile, charset); \
+ while (arg->tok == tok_string) \
+ { \
+ fn (ldfile, result, nowtok, arg, charset); \
+ arg = lr_token (ldfile, charset); \
+ if (arg->tok != tok_semicolon) \
+ break; \
+ arg = lr_token (ldfile, charset); \
+ } \
+ if (arg->tok != tok_eol) \
+ goto errlabel; \
+ } \
+ while (0)
+
+#define READ_NUMBER(fn, errlabel) \
+ do \
+ { \
+ arg = lr_token (ldfile, charset); \
+ if (arg->tok != tok_minus1 && arg->tok != tok_number) \
+ goto errlabel; \
+ fn (ldfile, result, nowtok, arg, charset); \
+ lr_ignore_rest (ldfile, 1); \
+ } \
+ while (0)
+
+#define READ_NUMBER_LIST(fn, errlabel) \
+ do \
+ { \
+ arg = lr_token (ldfile, charset); \
+ while (arg->tok == tok_minus1 || arg->tok == tok_number) \
+ { \
+ fn (ldfile, result, nowtok, arg, charset); \
+ arg = lr_token (ldfile, charset); \
+ if (arg->tok != tok_semicolon) \
+ break; \
+ arg = lr_token (ldfile, charset); \
+ } \
+ if (arg->tok != tok_eol) \
+ goto errlabel; \
+ } \
+ while (0)
+
+#define SYNTAX_ERROR(string) \
+ lr_error (ldfile, string); \
+ lr_ignore_rest (ldfile, 0);
+
+
+ /* Parse locale definition file and store result in RESULT. */
+ state = 1;
+ while (1)
+ {
+ /* What's on? */
+ struct token *now = lr_token (ldfile, charset);
+ enum token_t nowtok = now->tok;
+ struct token *arg;
+
+ if (nowtok == tok_eof)
+ break;
+
+ switch (state)
+ {
+ case 1:
+ /* The beginning. We expect the special declarations, EOL or
+ the start of any locale. */
+ if (nowtok == tok_eol)
+ /* Ignore empty lines. */
+ continue;
+
+ switch (nowtok)
+ {
+ case tok_escape_char:
+ case tok_comment_char:
+ /* We need an argument. */
+ arg = lr_token (ldfile, charset);
+
+ if (arg->tok != tok_ident)
+ {
+ SYNTAX_ERROR (_("bad argument"));
+ continue;
+ }
+
+ if (arg->val.str.len != 1)
+ {
+ lr_error (ldfile, _("\
+argument to `%s' must be a single character"),
+ nowtok == tok_escape_char ? "escape_char"
+ : "comment_char");
+
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+
+ if (nowtok == tok_escape_char)
+ ldfile->escape_char = *arg->val.str.start;
+ else
+ ldfile->comment_char = *arg->val.str.start;
+ break;
+
+ case tok_lc_ctype:
+ state = 2;
+ break;
+
+ case tok_lc_collate:
+ state = 10;
+ break;
+
+ case tok_lc_monetary:
+ state = 20;
+ break;
+
+ case tok_lc_numeric:
+ state = 30;
+ break;
+
+ case tok_lc_time:
+ state = 40;
+ break;
+
+ case tok_lc_messages:
+ state = 50;
+ break;
+
+ default:
+ SYNTAX_ERROR (_("\
+syntax error: not inside a locale definition section"));
+ continue;
+ }
+ lr_ignore_rest (ldfile, 1);
+ continue;
+
+ case 2:
+ HANDLE_COPY (LC_CTYPE, tok_lc_ctype, "LC_CYTPE");
+
+ ctype_startup (ldfile, result, charset);
+ /* FALLTHROUGH */
+
+ case 3:
+ /* Here we accept all the character classes, tolower/toupper,
+ and following ANSI C:1995 self-defined classes. */
+ LOCALE_PROLOG (tok_lc_ctype, "LC_CTYPE");
+
+ if (nowtok == tok_charclass)
+ {
+ READ_STRING_LIST (ctype_class_new, bad_new_charclass);
+ continue;
+ bad_new_charclass:
+ SYNTAX_ERROR (_("\
+syntax error in definition of new character class"));
+ continue;
+ }
+
+ if (nowtok == tok_charmap)
+ {
+ READ_STRING_LIST (ctype_map_new, bad_new_charmap);
+ continue;
+ bad_new_charmap:
+ SYNTAX_ERROR (_("\
+syntax error in definition of new character map"));
+ continue;
+ }
+
+ if (nowtok == tok_upper || nowtok == tok_lower
+ || nowtok == tok_alpha || nowtok == tok_digit
+ || nowtok == tok_alnum || nowtok == tok_space
+ || nowtok == tok_cntrl || nowtok == tok_punct
+ || nowtok == tok_graph || nowtok == tok_print
+ || nowtok == tok_xdigit || nowtok == tok_blank)
+ {
+ ctype_tok_sym = nowtok;
+ ctype_tok_str = NULL;
+ state = 5;
+ continue;
+ }
+
+ if (nowtok == tok_toupper|| nowtok == tok_tolower)
+ {
+ ctype_tok_sym = nowtok;
+ ctype_tok_str = NULL;
+ state = 6;
+ continue;
+ }
+
+ if (nowtok != tok_ident)
+ goto bad_charclass;
+
+ /* We possibly have a self-defined character class. */
+ if (ctype_is_charclass (ldfile, result, now->val.str.start))
+ {
+ ctype_tok_sym = nowtok;
+ ctype_tok_str = now->val.str.start;
+ state = 5;
+ continue;
+ }
+
+ /* ...or a self-defined character map. */
+ if (ctype_is_charmap (ldfile, result, now->val.str.start))
+ {
+ ctype_tok_sym = nowtok;
+ ctype_tok_str = now->val.str.start;
+ state = 6;
+ continue;
+ }
+
+ SYNTAX_ERROR (_("syntax error in definition of LC_CTYPE category"));
+ continue;
+
+ case 4:
+ /* Handle `END xxx'. */
+ if (nowtok != expected_tok)
+ lr_error (ldfile, _("\
+`%1$s' definition does not end with `END %1$s'"), expected_str);
+
+ lr_ignore_rest (ldfile, nowtok == expected_tok);
+ state = 1;
+ continue;
+
+ case 5:
+ /* Here we expect a semicolon separated list of bsymbols. The
+ bit to be set in the word is given in CHARCLASS_BIT. */
+ arg = now;
+
+ ctype_class_start (ldfile, result, ctype_tok_sym, ctype_tok_str,
+ charset);
+
+ while (arg->tok != tok_eol)
+ {
+ /* Any token other than a bsymbol is an error. */
+ if (arg->tok != tok_bsymbol)
+ {
+ bad_charclass:
+ SYNTAX_ERROR (_("\
+syntax error in character class definition"));
+ break;
+ }
+
+ /* Lookup value for token and write into array. */
+ ctype_class_from (ldfile, result, arg, charset);
+
+ arg = lr_token (ldfile, charset);
+ if (arg->tok == tok_semicolon)
+ arg = lr_token (ldfile, charset);
+ else if (arg->tok != tok_eol)
+ goto bad_charclass;
+
+ /* Look for ellipsis. */
+ if (arg->tok == tok_ellipsis)
+ {
+ arg = lr_token (ldfile, charset);
+ if (arg->tok != tok_semicolon)
+ goto bad_charclass;
+
+ arg = lr_token (ldfile, charset);
+ if (arg->tok != tok_bsymbol)
+ goto bad_charclass;
+
+ /* Write range starting at LAST to ARG->VAL. */
+ ctype_class_to (ldfile, result, arg, charset);
+
+ arg = lr_token (ldfile, charset);
+ if (arg->tok == tok_semicolon)
+ arg = lr_token (ldfile, charset);
+ else if (arg->tok != tok_eol)
+ goto bad_charclass;
+ }
+ }
+
+ /* Mark class as already seen. */
+ ctype_class_end (ldfile, result);
+ state = 3;
+
+ continue;
+
+ case 6:
+ /* Here we expect a list of character mappings. Note: the
+ first opening brace is already matched. */
+ ctype_map_start (ldfile, result, ctype_tok_sym, ctype_tok_str,
+ charset);
+
+ while (1)
+ {
+ /* Match ( bsymbol , bsymbol ) */
+ if (now->tok != tok_open_brace)
+ goto bad_charmap;
+
+ now = lr_token (ldfile, charset);
+ if (now->tok != tok_bsymbol)
+ {
+ bad_charmap:
+ SYNTAX_ERROR (_("\
+syntax error in character mapping definition"));
+ state = 3;
+ break;
+ }
+
+ /* Lookup arg and assign to FROM. */
+ ctype_map_from (ldfile, result, now, charset);
+
+ now = lr_token (ldfile, charset);
+ if (now->tok != tok_comma)
+ goto bad_charmap;
+
+ now = lr_token (ldfile, charset);
+ if (now->tok != tok_bsymbol)
+ goto bad_charmap;
+
+ /* Lookup arg and assign to TO. */
+ ctype_map_to (ldfile, result, now, charset);
+
+ now = lr_token (ldfile, charset);
+ if (now->tok != tok_close_brace)
+ goto bad_charmap;
+
+ now = lr_token (ldfile, charset);
+ if (now->tok == tok_eol)
+ {
+ state = 3;
+ break;
+ }
+ if (now->tok != tok_semicolon)
+ goto bad_charmap;
+
+ now = lr_token (ldfile, charset);
+ }
+
+ ctype_map_end (ldfile, result);
+ continue;
+
+ case 8:
+ {
+ /* We have seen `copy'. First match the argument. */
+ int warned = 0;
+
+ if (nowtok != tok_string)
+ lr_error (ldfile, _("expect string argument for `copy'"));
+ else
+ def_to_process (now->val.str.start, 1 << copy_category);
+
+ lr_ignore_rest (ldfile, nowtok == tok_string);
+
+ /* The rest of the line must be empty
+ and the next keyword must be `END xxx'. */
+
+ while (lr_token (ldfile, charset)->tok != tok_end)
+ {
+ if (warned == 0)
+ {
+ only_copy:
+ lr_error (ldfile, _("\
+no other keyword shall be specified when `copy' is used"));
+ warned = 1;
+ }
+
+ lr_ignore_rest (ldfile, 0);
+ }
+
+ state = 4;
+ }
+ continue;
+
+ case 10:
+ HANDLE_COPY (LC_COLLATE, tok_lc_collate, "LC_COLLATE");
+
+ collate_startup (ldfile, result, charset);
+ /* FALLTHROUGH */
+
+ case 11:
+ /* Process the LC_COLLATE section. We expect `END LC_COLLATE'
+ any of the collation specifications, or any bsymbol. */
+ LOCALE_PROLOG (tok_lc_collate, "LC_COLLATE");
+
+ if (nowtok == tok_order_start)
+ {
+ state = 12;
+ continue;
+ }
+
+ if (nowtok != tok_collating_element
+ && nowtok != tok_collating_symbol)
+ {
+ bad_collation:
+ lr_error (ldfile, _("\
+syntax error in collation definition"));
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+
+ /* Get argument. */
+ arg = lr_token (ldfile, charset);
+ if (arg->tok != tok_bsymbol)
+ {
+ lr_error (ldfile, _("\
+collation symbol expected after `%s'"),
+ nowtok == tok_collating_element
+ ? "collating-element" : "collating-symbol");
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+
+ if (nowtok == tok_collating_element)
+ {
+ /* Save to-value as new name. */
+ collate_element_to (ldfile, result, arg, charset);
+
+ arg = lr_token (ldfile, charset);
+ if (arg->tok != tok_from)
+ {
+ lr_error (ldfile, _("\
+`from' expected after first argument to `collating-element'"));
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+
+ arg = lr_token (ldfile, charset);
+ if (arg->tok != tok_string)
+ {
+ lr_error (ldfile, _("\
+from-value of `collating-element' must be a string"));
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+
+ /* Enter new collating element. */
+ collate_element_from (ldfile, result, arg, charset);
+ }
+ else
+ /* Enter new collating symbol into table. */
+ collate_symbol (ldfile, result, arg, charset);
+
+ lr_ignore_rest (ldfile, 1);
+ continue;
+
+ case 12:
+ /* We parse the rest of the line containing `order_start'.
+ In any case we continue with parsing the symbols. */
+ state = 13;
+
+ cnt = 0;
+ while (now->tok != tok_eol)
+ {
+ int collation_method = 0;
+
+ ++cnt;
+
+ do
+ {
+ if (now->tok == tok_forward)
+ collation_method |= sort_forward;
+ else if (now->tok == tok_backward)
+ collation_method |= sort_backward;
+ else if (now->tok == tok_position)
+ collation_method |= sort_position;
+ else
+ {
+ lr_error (ldfile, _("unknown collation directive"));
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+
+ now = lr_token (ldfile, charset);
+ }
+ while (now->tok == tok_comma
+ && (now == lr_token (ldfile, charset) != tok_none));
+
+ /* Check for consistency: forward and backwards are
+ mutually exclusive. */
+ if ((collation_method & sort_forward) != 0
+ && (collation_method & sort_backward) != 0)
+ {
+ lr_error (ldfile, _("\
+sorting order `forward' and `backward' are mutually exclusive"));
+ /* The recover clear the backward flag. */
+ collation_method &= ~sort_backward;
+ }
+
+ /* ??? I don't know whether this is correct but while
+ thinking about the `strcoll' functions I found that I
+ need a direction when performing position depended
+ collation. So I assume here that implicitly the
+ direction `forward' is given when `position' alone is
+ written. --drepper */
+ if (collation_method == sort_position)
+ collation_method |= sort_forward;
+
+ /* Enter info about next collation order. */
+ collate_new_order (ldfile, result, collation_method);
+
+ if (now->tok != tok_eol && now->tok != tok_semicolon)
+ {
+ lr_error (ldfile, _("\
+syntax error in `order_start' directive"));
+ lr_ignore_rest (ldfile, 0);
+ break;
+ }
+
+ if (now->tok == tok_semicolon)
+ now = lr_token (ldfile, charset);
+ }
+
+ /* If no argument to `order_start' is given, one `forward'
+ argument is implicitely assumed. */
+ if (cnt == 0)
+ collate_new_order (ldfile, result, sort_forward);
+
+
+ /* We now know about all sorting rules. */
+ collate_build_arrays (ldfile, result);
+
+ continue;
+
+ case 13:
+ /* We read one symbol a line until `order_end' is found. */
+ {
+ static int last_correct = 1;
+
+ if (nowtok == tok_order_end)
+ {
+ state = 14;
+ lr_ignore_rest (ldfile, 1);
+ continue;
+ }
+
+ /* Ignore empty lines. */
+ if (nowtok == tok_eol)
+ continue;
+
+ if (nowtok != tok_bsymbol && nowtok != tok_undefined
+ && nowtok != tok_ellipsis)
+ {
+ if (last_correct == 1)
+ {
+ lr_error (ldfile, _("\
+syntax error in collating order definition"));
+ last_correct = 0;
+ }
+ lr_ignore_rest (ldfile, 0);
+ continue;
+ }
+ else
+ {
+ last_correct = 1;
+
+ /* Remember current token. */
+ if (collate_order_elem (ldfile, result, now, charset) < 0)
+ continue;
+ }
+
+ /* Read optional arguments. */
+ arg = lr_token (ldfile, charset);
+ while (arg->tok != tok_eol)
+ {
+ if (arg->tok != tok_ignore && arg->tok != tok_ellipsis
+ && arg->tok != tok_bsymbol && arg->tok != tok_string)
+ break;
+
+ if (arg->tok == tok_ignore || arg->tok == tok_ellipsis
+ || arg->tok == tok_string)
+ {
+ /* Call handler for simple weights. */
+ if (collate_simple_weight (ldfile, result, arg, charset)
+ < 0)
+ goto illegal_weight;
+
+ arg = lr_token (ldfile, charset);
+ }
+ else
+ do
+ {
+ /* Collect char. */
+ int ok = collate_weight_bsymbol (ldfile, result, arg,
+ charset);
+ if (ok < 0)
+ goto illegal_weight;
+
+ arg = lr_token (ldfile, charset);
+ }
+ while (arg->tok == tok_bsymbol);
+
+ /* Are there more weights? */
+ if (arg->tok != tok_semicolon)
+ break;
+
+ /* Yes, prepare next weight. */
+ if (collate_next_weight (ldfile, result) < 0)
+ goto illegal_weight;
+
+ arg = lr_token (ldfile, charset);
+ }
+
+ if (arg->tok != tok_eol)
+ {
+ SYNTAX_ERROR (_("syntax error in order specification"));
+ }
+
+ collate_end_weight (ldfile, result);
+ illegal_weight:
+ }
+ continue;
+
+ case 14:
+ /* Following to the `order_end' keyword we don't expect
+ anything but the `END'. */
+ if (nowtok == tok_eol)
+ continue;
+
+ if (nowtok != tok_end)
+ goto bad_collation;
+
+ expected_tok = tok_lc_collate;
+ expected_str = "LC_COLLATE";
+ state = 4;
+
+ ldfile->translate_strings = 1;
+ continue;
+
+ case 20:
+ HANDLE_COPY (LC_MONETARY, tok_lc_monetary, "LC_MONETARY");
+
+ monetary_startup (ldfile, result, charset);
+ /* FALLTHROUGH */
+
+ case 21:
+ LOCALE_PROLOG (tok_lc_monetary, "LC_MONETARY");
+
+ switch (nowtok)
+ {
+ case tok_int_curr_symbol:
+ case tok_currency_symbol:
+ case tok_mon_decimal_point:
+ case tok_mon_thousands_sep:
+ case tok_positive_sign:
+ case tok_negative_sign:
+ READ_STRING (monetary_add, bad_monetary);
+ break;
+
+ case tok_int_frac_digits:
+ case tok_frac_digits:
+ case tok_p_cs_precedes:
+ case tok_p_sep_by_space:
+ case tok_n_cs_precedes:
+ case tok_n_sep_by_space:
+ case tok_p_sign_posn:
+ case tok_n_sign_posn:
+ READ_NUMBER (monetary_add, bad_monetary);
+ break;
+
+ case tok_mon_grouping:
+ /* We have a semicolon separated list of integers. */
+ READ_NUMBER_LIST (monetary_add, bad_monetary);
+ break;
+
+ default:
+ bad_monetary:
+ SYNTAX_ERROR (_("syntax error in monetary locale definition"));
+ }
+ continue;
+
+ case 30:
+ HANDLE_COPY (LC_NUMERIC, tok_lc_numeric, "LC_NUMERIC");
+
+ numeric_startup (ldfile, result, charset);
+ /* FALLTHROUGH */
+
+ case 31:
+ LOCALE_PROLOG (tok_lc_numeric, "LC_NUMERIC");
+
+ switch (nowtok)
+ {
+ case tok_decimal_point:
+ case tok_thousands_sep:
+ READ_STRING (numeric_add, bad_numeric);
+ break;
+
+ case tok_grouping:
+ /* We have a semicolon separated list of integers. */
+ READ_NUMBER_LIST (numeric_add, bad_numeric);
+ break;
+
+ default:
+ bad_numeric:
+ SYNTAX_ERROR (_("syntax error in numeric locale definition"));
+ }
+ continue;
+
+ case 40:
+ HANDLE_COPY (LC_TIME, tok_lc_time, "LC_TIME");
+
+ time_startup (ldfile, result, charset);
+ /* FALLTHROUGH */
+
+ case 41:
+ LOCALE_PROLOG (tok_lc_time, "LC_TIME");
+
+ switch (nowtok)
+ {
+ case tok_abday:
+ case tok_day:
+ case tok_abmon:
+ case tok_mon:
+ case tok_am_pm:
+ case tok_alt_digits:
+ READ_STRING_LIST (time_add, bad_time);
+ continue;
+
+ case tok_d_t_fmt:
+ case tok_d_fmt:
+ case tok_t_fmt:
+ case tok_t_fmt_ampm:
+ case tok_era:
+ case tok_era_year:
+ case tok_era_d_t_fmt:
+ case tok_era_d_fmt:
+ case tok_era_t_fmt:
+ READ_STRING (time_add, bad_time);
+ break;
+
+ default:
+ bad_time:
+ SYNTAX_ERROR (_("syntax error in time locale definition"));
+ }
+ continue;
+
+ case 50:
+ HANDLE_COPY (LC_MESSAGES, tok_lc_messages, "LC_MESSAGES");
+
+ messages_startup (ldfile, result, charset);
+ /* FALLTHROUGH */
+
+ case 51:
+ LOCALE_PROLOG (tok_lc_messages, "LC_MESSAGES");
+
+ switch (nowtok)
+ {
+ case tok_yesexpr:
+ case tok_noexpr:
+ case tok_yesstr:
+ case tok_nostr:
+ READ_STRING (messages_add, bad_message);
+ break;
+
+ default:
+ bad_message:
+ SYNTAX_ERROR (_("syntax error in message locale definition"));
+ }
+ continue;
+
+ default:
+ error (5, 0, _("%s: error in state machine"), __FILE__);
+ /* NOTREACHED */
+ }
+
+ break;
+ }
+
+ /* We read all of the file. */
+ lr_close (ldfile);
+
+ /* Let's see what information is available. */
+ for (cnt = LC_CTYPE; cnt <= LC_MESSAGES; ++cnt)
+ if (result->categories[cnt].generic != NULL)
+ result->avail |= 1 << cnt;
+
+ return result;
+}
+
+
+void
+check_all_categories (struct localedef_t *locale, struct charset_t *charset)
+{
+ /* Call the finishing functions for all locales. */
+ if ((locale->binary & (1 << LC_CTYPE)) == 0)
+ ctype_finish (locale, charset);
+ if ((locale->binary & (1 << LC_COLLATE)) == 0)
+ collate_finish (locale, charset);
+ if ((locale->binary & (1 << LC_MONETARY)) == 0)
+ monetary_finish (locale);
+ if ((locale->binary & (1 << LC_NUMERIC)) == 0)
+ numeric_finish (locale);
+ if ((locale->binary & (1 << LC_TIME)) == 0)
+ time_finish (locale);
+ if ((locale->binary & (1 << LC_MESSAGES)) == 0)
+ messages_finish (locale);
+}
+
+
+void
+write_all_categories (struct localedef_t *locale, const char *output_path)
+{
+ /* Call all functions to write locale data. */
+ ctype_output (locale, output_path);
+ collate_output (locale, output_path);
+ monetary_output (locale, output_path);
+ numeric_output (locale, output_path);
+ time_output (locale, output_path);
+ messages_output (locale, output_path);
+}
+
+
+void
+write_locale_data (const char *output_path, const char *category,
+ size_t n_elem, struct iovec *vec)
+{
+ size_t cnt, step;
+ int fd;
+ char *fname;
+
+ asprintf (&fname, "%s/%s", output_path, category);
+ fd = creat (fname, 0666);
+ if (fd == -1)
+ {
+ int save_err = errno;
+
+ if (errno == EISDIR)
+ {
+ free (fname);
+ asprintf (&fname, "%1$s/%2$s/SYS_%2$s", output_path, category);
+ fd = creat (fname, 0666);
+ if (fd == -1)
+ save_err = errno;
+ }
+
+ if (fd == -1)
+ {
+ error (0, save_err, _("cannot open output file for category `%s'"),
+ category);
+ return;
+ }
+ }
+ free (fname);
+
+ /* Write the data using writev. But we must take care for the
+ limitation of the implementation. */
+ for (cnt = 0; cnt < n_elem; cnt += step)
+ {
+ /* XXX Fixme: should be in libc header. */
+#ifndef MAX_IOVEC
+# define MAX_IOVEC 8
+#endif
+ step = MIN (MAX_IOVEC, n_elem - cnt);
+
+ if (writev (fd, &vec[cnt], step) < 0)
+ {
+ error (0, errno, _("failure while writing data for category `%s'"),
+ category);
+ break;
+ }
+ }
+
+ close (fd);
+}
diff --git a/locale/programs/locfile.h b/locale/programs/locfile.h
new file mode 100644
index 0000000000..e337e961ed
--- /dev/null
+++ b/locale/programs/locfile.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifndef _LOCFILE_H
+#define _LOCFILE_H
+
+#include <sys/uio.h>
+
+#include "charset.h"
+
+/* Opaque types for the different loales. */
+struct locale_ctype_t;
+struct locale_collate_t;
+struct locale_monetary_t;
+struct locale_numeric_t;
+struct locale_time_t;
+struct locale_messages_t;
+
+struct localedef_t
+{
+ int failed;
+
+ int avail;
+ int binary;
+
+ union
+ {
+ void *generic;
+ struct locale_ctype_t *ctype;
+ struct locale_collate_t *collate;
+ struct locale_monetary_t *monetary;
+ struct locale_numeric_t *numeric;
+ struct locale_time_t *time;
+ struct locale_messages_t *messages;
+ } categories[6];
+
+ size_t len[6];
+};
+
+
+/* Found in localedef.c. */
+void def_to_process (const char *name, int category);
+
+
+/* Found in locfile.c. */
+struct localedef_t *locfile_read (const char *filename,
+ struct charset_t *charset);
+
+void check_all_categories (struct localedef_t *locale,
+ struct charset_t *charset);
+
+void write_all_categories (struct localedef_t *locale,
+ const char *output_path);
+
+
+void write_locale_data (const char *output_path, const char *category,
+ size_t n_elem, struct iovec *vec);
+
+#endif /* locfile.h */
diff --git a/locale/programs/stringtrans.c b/locale/programs/stringtrans.c
new file mode 100644
index 0000000000..bff5aa41a2
--- /dev/null
+++ b/locale/programs/stringtrans.c
@@ -0,0 +1,146 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+COntributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "charset.h"
+#include "stringtrans.h"
+
+
+/* Global variable. */
+enum encoding_method encoding_method = ENC_UCS4;
+
+
+void *xmalloc (size_t __n);
+void *xrealloc (void *__p, size_t __n);
+
+
+#define ADDC(ch) \
+ do \
+ { \
+ if (bufact == bufmax) \
+ { \
+ bufmax *= 2; \
+ buf = xrealloc (buf, bufmax); \
+ } \
+ buf[bufact++] = (ch); \
+ } \
+ while (0)
+
+
+char *
+translate_string (char *str, struct charset_t *charset)
+{
+ char *buf;
+ size_t bufact = 0;
+ size_t bufmax = 56;
+
+ buf = (char *) xmalloc (bufmax);
+
+ while (str[0] != '\0')
+ {
+ char *tp;
+ unsigned int value;
+
+ if (str[0] != '<')
+ {
+ ADDC (*str++);
+ continue;
+ }
+
+ tp = &str[1];
+ while (tp[0] != '\0' && tp[0] != '>')
+ if (tp[0] == '\\')
+ if (tp[1] != '\0')
+ tp += 2;
+ else
+ ++tp;
+ else
+ ++tp;
+
+ if (tp[0] == '\0')
+ {
+ free (buf);
+ return NULL;
+ }
+
+ value = charset_find_value (charset, str + 1, tp - (str + 1));
+ if (value == ILLEGAL_CHAR_VALUE)
+ {
+ free (buf);
+ return NULL;
+ }
+ else
+ {
+ /* Encode string using current method. */
+ char *cp;
+
+ if (bufmax - bufact < 8)
+ {
+ bufmax *= 2;
+ buf = (char *) xrealloc (buf, bufmax);
+ }
+
+ cp = &buf[bufact];
+ if (encode_char (value, &cp) < 0)
+ {
+ free (buf);
+ return NULL;
+ }
+ bufact = cp - buf;
+ }
+
+ str = &tp[1];
+ }
+
+ ADDC ('\0');
+
+ return buf;;
+}
+
+
+int
+encode_char (unsigned int value, char **cpp)
+{
+ switch (encoding_method)
+ {
+ case ENC_UCS1:
+ if (value > 255)
+ return -11;
+ *(*cpp)++ = (char) value;
+ break;
+
+ case ENC_UCS4:
+ *(*cpp)++ = (char) (value >> 24);
+ *(*cpp)++ = (char) ((value >> 16) & 0xff);
+ *(*cpp)++ = (char) ((value >> 8) & 0xff);
+ *(*cpp)++ = (char) (value & 0xff);
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/locale/programs/stringtrans.h b/locale/programs/stringtrans.h
new file mode 100644
index 0000000000..3576ce445c
--- /dev/null
+++ b/locale/programs/stringtrans.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifndef _TRANSLATE_H
+#define _TRANSLATE_H 1
+
+enum encoding_method
+{
+ ENC_UCS1,
+ ENC_UCS4
+};
+
+
+extern enum encoding_method encoding_method;
+
+
+char *translate_string (char *__str, struct charset_t *__charset);
+
+int encode_char (unsigned int __value, char **__cpp);
+
+
+#endif /* translate.h */