/* 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 #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) + 2 * strlen (category[cat_no].name) + 5); t = stpcpy (path, output_path); strcpy (t, category[cat_no].name); fd = creat (path, 0666); if (fd == -1) { /* Check whether it failed because the named file is a directory. In that case we use the file .../LC_xxx/SYS_LC_xxx, as the loading functions of the C Library do. */ struct stat st; if (stat (path, &st) == 0 && S_ISDIR (st.st_mode)) { stpcpy (stpcpy (strchr (path, '\0'), "/SYS_"), 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, 10); 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 if (locale_count >= max_count) 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: */