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