diff options
Diffstat (limited to 'catgets')
-rw-r--r-- | catgets/Makefile | 41 | ||||
-rw-r--r-- | catgets/catgets.c | 163 | ||||
-rw-r--r-- | catgets/catgetsinfo.h | 59 | ||||
-rw-r--r-- | catgets/config.h | 14 | ||||
-rw-r--r-- | catgets/gencat.c | 1030 | ||||
-rw-r--r-- | catgets/nl_types.h | 48 | ||||
-rw-r--r-- | catgets/open_catalog.c | 263 |
7 files changed, 1618 insertions, 0 deletions
diff --git a/catgets/Makefile b/catgets/Makefile new file mode 100644 index 0000000000..4646dd5a3f --- /dev/null +++ b/catgets/Makefile @@ -0,0 +1,41 @@ +# Copyright (C) 1996 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., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# +# Makefile for catgets. +# +subdir := catgets + +headers = nl_types.h +distribute = catgetsinfo.h config.h +routines = catgets open_catalog +others = gencat +install-bin = gencat + +gencat-modules = xmalloc + +# To find xmalloc.c +vpath %.c ../locale/programs + + +include ../Rules + +$(objpfx)gencat: $(gencat-modules:%=$(objpfx)%.o) + +CPPFLAGS := -DNLSPATH='"$(localedir)/%L/%N:$(localedir)/%L/LC_MESSAGES/%N:"' \ + -DHAVE_CONFIG_H $(CPPFLAGS) diff --git a/catgets/catgets.c b/catgets/catgets.c new file mode 100644 index 0000000000..ca0fdd693c --- /dev/null +++ b/catgets/catgets.c @@ -0,0 +1,163 @@ +/* Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <alloca.h> +#include <nl_types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> + +#include "catgetsinfo.h" + + +/* Open the catalog and return a descriptor for the catalog. */ +nl_catd +catopen (const char *cat_name, int flag) +{ + __nl_catd result; + const char *env_var; + + result = (__nl_catd) malloc (sizeof (__nl_catd)); + if (result == NULL) + /* We cannot get enough memory. */ + return (nl_catd) -1; + + result->status = closed; + + result->cat_name = strdup (cat_name); + if (result->cat_name == NULL) + { + free (result); + return (nl_catd) -1; + } + + if (strchr (cat_name, '/') == NULL) + { + if (flag == NL_CAT_LOCALE) + { + env_var = getenv ("LC_ALL"); + if (env_var == NULL) + { + env_var = getenv ("LC_MESSAGES"); + if (env_var == NULL) + { + env_var = getenv ("LANG"); + if (env_var == NULL) + env_var = "C"; + } + } + } + else + { + env_var = getenv ("LANG"); + if (env_var == NULL) + env_var = "C"; + } + + result->env_var = strdup (env_var); + if (result->env_var == NULL) + { + free ((void *) result->cat_name); + free ((void *) result); + return (nl_catd) -1; + } + + if (getenv ("NLSPATH") != NULL) + result->nlspath = strdup (getenv ("NLSPATH")); + else + result->nlspath = strdup (NLSPATH); + + if (result->nlspath == NULL) + { + free ((void *) result->cat_name); + free ((void *) result->env_var); + free ((void *) result); + return (nl_catd) -1; + } + } + else + { + result->env_var = NULL; + result->nlspath = NULL; + } + + return (nl_catd) result; +} + + +/* Return message from message catalog. */ +char * +catgets (nl_catd catalog_desc, int set, int message, const char *string) +{ + __nl_catd catalog; + size_t idx; + size_t cnt; + + /* Be generous if catalog which failed to be open is used. */ + if (catalog_desc == (nl_catd) -1 || ++set <= 0 || message < 0) + return (char *) string; + + catalog = (__nl_catd) catalog_desc; + + if (catalog->status == closed) + __open_catalog (catalog, 1); + + if (catalog->status == nonexisting) + return (char *) string; + + idx = ((set * message) % catalog->plane_size) * 3; + cnt = 0; + do + { + if (catalog->name_ptr[idx + 0] == (u_int32_t) set + && catalog->name_ptr[idx + 1] == (u_int32_t) message) + return (char *) &catalog->strings[catalog->name_ptr[idx + 2]]; + + idx += catalog->plane_size * 3; + } + while (++cnt < catalog->plane_depth); + + return (char *) string; +} + + +/* Return resources used for loaded message catalog. */ +int +catclose (nl_catd catalog_desc) +{ + __nl_catd catalog; + + catalog = (__nl_catd) catalog_desc; + + if (catalog->status == mmaped) + munmap ((void *) catalog->file_ptr, catalog->file_size); + else if (catalog->status == malloced) + free ((void *) catalog->file_ptr); + else if (catalog->status != closed && catalog->status != nonexisting) + return -1; + + if (catalog->nlspath) + free ((void *) catalog->nlspath); + if (catalog->env_var) + free ((void *) catalog->env_var); + free ((void *) catalog); + + return 0; +} diff --git a/catgets/catgetsinfo.h b/catgets/catgetsinfo.h new file mode 100644 index 0000000000..7e0a26f798 --- /dev/null +++ b/catgets/catgetsinfo.h @@ -0,0 +1,59 @@ +/* Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <sys/types.h> + + +struct catalog_obj +{ + u_int32_t magic; + u_int32_t plane_size; + u_int32_t plane_depth; + /* This is in fact two arrays in one: always a pair of name and + pointer into the data area. */ + u_int32_t name_ptr[0]; +}; + + +/* This structure will be filled after loading the catalog. */ +typedef struct catalog_info +{ + enum { closed, nonexisting, mmaped, malloced } status; + + const char *cat_name; + const char *env_var; + const char *nlspath; + + size_t plane_size; + size_t plane_depth; + u_int32_t *name_ptr; + const char *strings; + + struct catalog_obj *file_ptr; + size_t file_size; +} *__nl_catd; + + + +/* The magic number to signal we really have a catalog file. */ +#define CATGETS_MAGIC 0x960408de + + +/* Prototypes for helper functions. */ +void __open_catalog (__nl_catd __catalog, int __with_path); diff --git a/catgets/config.h b/catgets/config.h new file mode 100644 index 0000000000..ce7887b351 --- /dev/null +++ b/catgets/config.h @@ -0,0 +1,14 @@ +#ifndef _CG_CONFIG_H +#define _CG_CONFIG_H + +/* Use the internal textdomain used for libc messages. */ +#define PACKAGE _libc_intl_domainname +#ifndef VERSION +/* Get libc version number. */ +#include "../version.h" +#endif + + +#include_next <config.h> + +#endif diff --git a/catgets/gencat.c b/catgets/gencat.c new file mode 100644 index 0000000000..4b6eb43255 --- /dev/null +++ b/catgets/gencat.c @@ -0,0 +1,1030 @@ +/* Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <getopt.h> +#include <libintl.h> +#include <limits.h> +#include <nl_types.h> +#include <obstack.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "version.h" + +#include "catgetsinfo.h" + + +#define SWAPU32(w) \ + (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24)) + +struct message_list +{ + int number; + const char *message; + + const char *fname; + size_t line; + const char *symbol; + + struct message_list *next; +}; + + +struct set_list +{ + int number; + int deleted; + struct message_list *messages; + int last_message; + + const char *fname; + size_t line; + const char *symbol; + + struct set_list *next; +}; + + +struct catalog +{ + struct set_list *all_sets; + struct set_list *current_set; + size_t total_messages; + char quote_char; + int last_set; + + struct obstack mem_pool; +}; + + +/* If non-zero force creation of new file, not using existing one. */ +static int force_new; + +/* Long options. */ +static const struct option long_options[] = +{ + { "header", required_argument, NULL, 'H' }, + { "help", no_argument, NULL, 'h' }, + { "new", no_argument, &force_new, 1 }, + { "output", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } +}; + +/* Wrapper functions with error checking for standard functions. */ +extern void *xmalloc (size_t n); + +/* Prototypes for local functions. */ +static void usage (int status) __attribute__ ((noreturn)); +static void error_print (void); +static struct catalog *read_input_file (struct catalog *current, + const char *fname); +static void write_out (struct catalog *result, const char *output_name, + const char *header_name); +static struct set_list *find_set (struct catalog *current, int number); +static void normalize_line (const char *fname, size_t line, char *string, + char quote_char); +static void read_old (struct catalog *catalog, const char *file_name); + + +int +main (int argc, char *argv[]) +{ + struct catalog *result; + const char *output_name; + const char *header_name; + int do_help; + int do_version; + int opt; + + /* Set program name for messages. */ + error_print_progname = error_print; + + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); + + /* Set the text message domain. */ + textdomain (PACKAGE); + + /* Initialize local variables. */ + do_help = 0; + do_version = 0; + output_name = NULL; + header_name = NULL; + result = NULL; + + while ((opt = getopt_long (argc, argv, "hH:o:V", long_options, NULL)) != EOF) + switch (opt) + { + case '\0': /* Long option. */ + break; + case 'h': + do_help = 1; + break; + case 'H': + header_name = optarg; + break; + case 'o': + output_name = optarg; + break; + case 'V': + do_version = 1; + break; + default: + usage (EXIT_FAILURE); + } + + /* Version information is requested. */ + if (do_version) + { + fprintf (stderr, "%s - GNU %s %s\n", program_invocation_name, + PACKAGE, VERSION); + exit (EXIT_SUCCESS); + } + + /* Help is requested. */ + if (do_help) + usage (EXIT_SUCCESS); + + /* Determine output file. */ + if (output_name == NULL) + output_name = optind < argc ? argv[optind++] : "-"; + + /* Process all input files. */ + setlocale (LC_CTYPE, "C"); + if (optind < argc) + do + result = read_input_file (result, argv[optind]); + while (++optind < argc); + else + result = read_input_file (NULL, "-"); + + /* Write out the result. */ + if (result != NULL) + write_out (result, output_name, header_name); + + exit (EXIT_SUCCESS); +} + + +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]... -o OUTPUT-FILE [INPUT-FILE]...\n\ + %s [OPTION]... [OUTPUT-FILE [INPUT-FILE]...]\n\ +Mandatory arguments to long options are mandatory for short options too.\n\ + -H, --header create C header file containing symbol definitions\n\ + -h, --help display this help and exit\n\ + --new do not use existing catalog, force new output file\n\ + -o, --output=NAME write output to file NAME\n\ + -V, --version output version information and exit\n\ +If INPUT-FILE is -, input is read from standard input. If OUTPUT-FILE\n\ +is -, output is written to standard output.\n"), + program_invocation_name, program_invocation_name); + + exit (status); +} + + +/* The address of this function will be assigned to the hook in the + error functions. */ +static void +error_print () +{ + /* We don't want the program name to be printed in messages. Emacs' + compile.el does not like this. */ +} + + +static struct catalog * +read_input_file (struct catalog *current, const char *fname) +{ + FILE *fp; + char *buf; + size_t len; + size_t line_number; + + if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0) + { + fp = stdin; + fname = gettext ("*standard input*"); + } + else + fp = fopen (fname, "r"); + if (fp == NULL) + { + error (0, errno, gettext ("cannot open input file `%s'"), fname); + return current; + } + + /* If we haven't seen anything yet, allocate result structure. */ + if (current == NULL) + { + current = (struct catalog *) xmalloc (sizeof (*current)); + + current->all_sets = NULL; + current->total_messages = 0; + current->last_set = 0; + current->current_set = find_set (current, NL_SETD); + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + obstack_init (¤t->mem_pool); + } + + buf = NULL; + len = 0; + line_number = 0; + while (!feof (fp)) + { + int continued; + int used; + size_t start_line = line_number + 1; + char *this_line; + + do + { + int act_len; + + act_len = getline (&buf, &len, fp); + if (act_len <= 0) + break; + ++line_number; + + /* It the line continued? */ + if (buf[act_len - 1] == '\n') + { + --act_len; + continued = buf[act_len - 1] == '\\'; + if (continued) + --act_len; + } + else + continued = 0; + + /* Append to currently selected line. */ + obstack_grow (¤t->mem_pool, buf, act_len); + } + while (continued); + + obstack_1grow (¤t->mem_pool, '\0'); + this_line = (char *) obstack_finish (¤t->mem_pool); + + used = 0; + if (this_line[0] == '$') + { + if (isspace (this_line[1])) + /* This is a comment line. Do nothing. */; + else if (strncmp (&this_line[1], "set", 3) == 0) + { + int cnt = sizeof ("cnt"); + size_t set_number; + const char *symbol = NULL; + while (isspace (this_line[cnt])) + ++cnt; + + if (isdigit (this_line[cnt])) + { + set_number = atol (&this_line[cnt]); + + /* If the given number for the character set is + higher than any we used for symbolic set names + avoid clashing by using only higher numbers for + the following symbolic definitions. */ + if (set_number > current->last_set) + current->last_set = set_number; + } + else + { + /* See whether it is a reasonable identifier. */ + int start = cnt; + while (isalnum (this_line[cnt]) || this_line[cnt] == '_') + ++cnt; + + if (cnt == start) + { + /* No correct character found. */ + error_at_line (0, 0, fname, start_line, + gettext ("illegal set number")); + set_number = 0; + } + else + { + /* We have found seomthing which looks like a + correct identifier. */ + struct set_list *runp; + + this_line[cnt] = '\0'; + used = 1; + symbol = &this_line[start]; + + /* Test whether the identifier was already used. */ + runp = current->all_sets; + while (runp != 0) + if (runp->symbol != NULL + && strcmp (runp->symbol, symbol) == 0) + break; + else + runp = runp->next; + + if (runp != NULL) + { + /* We cannot allow duplicate identifiers for + message sets. */ + error_at_line (0, 0, fname, start_line, + gettext ("duplicate set definition")); + error_at_line (0, 0, runp->fname, runp->line, + gettext ("\ +this is the first definition")); + set_number = 0; + } + else + /* Allocate next free message set for identifier. */ + set_number = ++current->last_set; + } + } + + if (set_number != 0) + { + /* We found a legal set number. */ + current->current_set = find_set (current, set_number); + if (symbol != NULL) + used = 1; + current->current_set->symbol = symbol; + current->current_set->fname = fname; + current->current_set->line = start_line; + } + } + else if (strncmp (&this_line[1], "delset", 6) == 0) + { + int cnt = sizeof ("delset"); + size_t set_number; + while (isspace (this_line[cnt])) + ++cnt; + + if (isdigit (this_line[cnt])) + { + size_t set_number = atol (&this_line[cnt]); + struct set_list *set; + + /* Mark the message set with the given number as + deleted. */ + set = find_set (current, set_number); + set->deleted = 1; + } + else + { + /* See whether it is a reasonable identifier. */ + int start = cnt; + while (isalnum (this_line[cnt]) || this_line[cnt] == '_') + ++cnt; + + if (cnt == start) + { + error_at_line (0, 0, fname, start_line, + gettext ("illegal set number")); + set_number = 0; + } + else + { + const char *symbol; + struct set_list *runp; + + this_line[cnt] = '\0'; + used = 1; + symbol = &this_line[start]; + + /* We have a symbolic set name. This name must + appear somewhere else in the catalogs read so + far. */ + set_number = 0; + for (runp = current->all_sets; runp != NULL; + runp = runp->next) + { + if (strcmp (runp->symbol, symbol) == 0) + { + runp->deleted = 1; + break; + } + } + if (runp == NULL) + /* Name does not exist before. */ + error_at_line (0, 0, fname, start_line, + gettext ("unknown set `%s'"), symbol); + } + } + } + else if (strncmp (&this_line[1], "quote", 5) == 0) + { + int cnt = sizeof ("quote"); + while (isspace (this_line[cnt])) + ++cnt; + /* Yes, the quote char can be '\0'; this means no quote + char. */ + current->quote_char = this_line[cnt]; + } + else + { + int cnt; + cnt = 2; + while (this_line[cnt] != '\0' && !isspace (this_line[cnt])) + ++cnt; + this_line[cnt] = '\0'; + error_at_line (0, 0, fname, start_line, + gettext ("unknown directive `%s': line ignored"), + &this_line[1]); + } + } + else if (isalnum (this_line[0]) || this_line[0] == '_') + { + const char *ident = this_line; + int message_number; + + do + ++this_line; + while (this_line[0] != '\0' && !isspace (this_line[0]));; + this_line[0] = '\0'; /* Terminate the identifier. */ + + do + ++this_line; + while (isspace (this_line[0])); + /* Now we found the beginning of the message itself. */ + + if (isdigit (ident[0])) + { + struct message_list *runp; + + message_number = atoi (ident); + + /* Find location to insert the new message. */ + runp = current->current_set->messages; + while (runp != NULL) + if (runp->number == message_number) + break; + else + runp = runp->next; + if (runp != NULL) + { + /* Oh, oh. There is already a message with this + number is the message set. */ + error_at_line (0, 0, fname, start_line, + gettext ("duplicated message number")); + error_at_line (0, 0, runp->fname, runp->line, + gettext ("this is the first definition")); + message_number = 0; + } + ident = NULL; /* We don't have a symbol. */ + + if (message_number != 0 + && message_number > current->current_set->last_message) + current->current_set->last_message = message_number; + } + else if (ident[0] != '\0') + { + struct message_list *runp; + runp = current->current_set->messages; + + /* Test whether the symbolic name was not used for + another message in this message set. */ + while (runp != NULL) + if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0) + break; + else + runp = runp->next; + if (runp != NULL) + { + /* The name is already used. */ + error_at_line (0, 0, fname, start_line, + gettext ("duplicated message identifier")); + error_at_line (0, 0, runp->fname, runp->line, + gettext ("this is the first definition")); + message_number = 0; + } + else + /* Give the message the next unused number. */ + message_number = ++current->current_set->last_message; + } + else + message_number = 0; + + if (message_number != 0) + { + struct message_list *newp; + + used = 1; /* Yes, we use the line. */ + + /* Strip quote characters, change escape sequences into + correct characters etc. */ + normalize_line (fname, start_line, this_line, + current->quote_char); + + newp = (struct message_list *) xmalloc (sizeof (*newp)); + newp->number = message_number; + newp->message = this_line; + /* Remember symbolic name; is NULL if no is given. */ + newp->symbol = ident; + /* Remember where we found the character. */ + newp->fname = fname; + newp->line = start_line; + + /* Find place to insert to message. We keep them in a + sorted single linked list. */ + if (current->current_set->messages == NULL + || current->current_set->messages->number > message_number) + { + newp->next = current->current_set->messages; + current->current_set->messages = newp; + } + else + { + struct message_list *runp; + runp = current->current_set->messages; + while (runp->next != NULL) + if (runp->next->number > message_number) + break; + else + runp = runp->next; + newp->next = runp->next; + runp->next = newp; + } + } + ++current->total_messages; + } + else + { + size_t cnt; + + cnt = 0; + /* See whether we have any non-white space character in this + line. */ + while (this_line[cnt] != '\0' && isspace (this_line[cnt])) + ++cnt; + + if (this_line[cnt] != '\0') + /* Yes, some unknown characters found. */ + error_at_line (0, 0, fname, start_line, + gettext ("malformed line ignored")); + } + + /* We can save the memory for the line if it was not used. */ + if (!used) + obstack_free (¤t->mem_pool, this_line); + } + + if (fp != stdin) + fclose (fp); + return current; +} + + +static void +write_out (struct catalog *catalog, const char *output_name, + const char *header_name) +{ + /* Computing the "optimal" size. */ + struct set_list *set_run; + size_t best_total, best_size, best_depth; + size_t act_size, act_depth; + struct catalog_obj obj; + struct obstack string_pool; + const char *strings; + size_t strings_size; + u_int32_t *array1, *array2; + size_t cnt; + int fd; + + /* If not otherwise told try to read file with existing + translations. */ + if (!force_new) + read_old (catalog, output_name); + + /* Initialize best_size with a very high value. */ + best_total = best_size = best_depth = UINT_MAX; + + /* We need some start size for testing. Let's start with + TOTAL_MESSAGES / 5, which theoretically provides a mean depth of + 5. */ + act_size = 1 + catalog->total_messages / 5; + + /* We determine the size of a hash table here. Because the message + numbers can be chosen arbitrary by the programmer we cannot use + the simple method of accessing the array using the message + number. The algorithm is based on the trivial hash function + NUMBER % TABLE_SIZE, where collisions are stored in a second + dimension up to TABLE_DEPTH. We here compute TABLE_SIZE so that + the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal. */ + while (act_size <= best_total) + { + size_t deep[act_size]; + + act_depth = 1; + memset (deep, '\0', act_size * sizeof (size_t)); + set_run = catalog->all_sets; + while (set_run != NULL) + { + struct message_list *message_run; + + message_run = set_run->messages; + while (message_run != NULL) + { + size_t idx = (message_run->number * set_run->number) % act_size; + + ++deep[idx]; + if (deep[idx] > act_depth) + { + act_depth = deep[idx]; + if (act_depth * act_size > best_total) + break; + } + message_run = message_run->next; + } + + if (act_depth * act_size <= best_total) + { + /* We have found a better solution. */ + best_total = act_depth * act_size; + best_size = act_size; + best_depth = act_depth; + } + set_run = set_run->next; + } + + ++act_size; + } + + /* let's be prepared for an empty message file. */ + if (best_size == UINT_MAX) + { + best_size = 1; + best_depth = 1; + } + + /* OK, now we have the size we will use. Fill in the header, build + the table and the second one with swapped byte order. */ + obj.magic = CATGETS_MAGIC; + obj.plane_size = best_size; + obj.plane_depth = best_depth; + + /* Allocate room for all needed arrays. */ + array1 = + (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3); + memset (array1, '\0', best_size * best_depth * sizeof (u_int32_t) * 3); + array2 + = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3); + obstack_init (&string_pool); + + set_run = catalog->all_sets; + while (set_run != NULL) + { + struct message_list *message_run; + + message_run = set_run->messages; + while (message_run != NULL) + { + size_t idx = (((message_run->number * set_run->number) % best_size) + * 3); + /* Determine collision depth. */ + while (array1[idx] != 0) + idx += best_size * 3; + + /* Store set number, message number and pointer into string + space, relative to the first string. */ + array1[idx + 0] = set_run->number; + array1[idx + 1] = message_run->number; + array1[idx + 2] = obstack_object_size (&string_pool); + + /* Add current string to the continuous space containing all + strings. */ + obstack_grow0 (&string_pool, message_run->message, + strlen (message_run->message)); + + message_run = message_run->next; + } + + set_run = set_run->next; + } + strings_size = obstack_object_size (&string_pool); + strings = obstack_finish (&string_pool); + + /* Compute ARRAY2 by changing the byte order. */ + for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt) + array2[cnt] = SWAPU32 (array1[cnt]); + + /* Now we can write out the whole data. */ + if (strcmp (output_name, "-") == 0 + || strcmp (output_name, "/dev/stdout") == 0) + fd = STDOUT_FILENO; + else + { + fd = creat (output_name, 0666); + if (fd < 0) + error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"), + output_name); + } + + /* Write out header. */ + write (fd, &obj, sizeof (obj)); + + /* We always write out the little endian version of the index + arrays. */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3); + write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3); +#elif __BYTE_ORDER == __BIG_ENDIAN + write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3); + write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3); +#else +# error Cannot handle __BYTE_ORDER byte order +#endif + + /* Finally write the strings. */ + write (fd, strings, strings_size); + + if (fd != STDOUT_FILENO) + close (fd); + + /* If requested now write out the header file. */ + if (header_name != NULL) + { + int first = 1; + FILE *fp; + + /* Open output file. "-" or "/dev/stdout" means write to + standard output. */ + if (strcmp (header_name, "-") == 0 + || strcmp (header_name, "/dev/stdout") == 0) + fp = stdout; + else + { + fp = fopen (header_name, "w"); + if (fp == NULL) + error (EXIT_FAILURE, errno, + gettext ("cannot open output file `%s'"), header_name); + } + + /* Iterate over all sets and all messages. */ + set_run = catalog->all_sets; + while (set_run != NULL) + { + struct message_list *message_run; + + /* If the current message set has a symbolic name write this + out first. */ + if (set_run->symbol != NULL) + fprintf (fp, "%s#define %sSet %#x\t/* %s:%u */\n", + first ? "" : "\n", set_run->symbol, set_run->number - 1, + set_run->fname, set_run->line); + first = 0; + + message_run = set_run->messages; + while (message_run != NULL) + { + /* If the current message has a symbolic name write + #define out. But we have to take care for the set + not having a symbolic name. */ + if (message_run->symbol != NULL) + if (set_run->symbol == NULL) + fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%u */\n", + set_run->number, message_run->symbol, + message_run->number, message_run->fname, + message_run->line); + else + fprintf (fp, "#define %s%s %#x\t/* %s:%u */\n", + set_run->symbol, message_run->symbol, + message_run->number, message_run->fname, + message_run->line); + + message_run = message_run->next; + } + + set_run = set_run->next; + } + + if (fp != stdout) + fclose (fp); + } +} + + +static struct set_list * +find_set (struct catalog *current, int number) +{ + struct set_list *result = current->all_sets; + + /* We must avoid set number 0 because a set of this number signals + in the tables that the entry is not occupied. */ + ++number; + + while (result != NULL) + if (result->number == number) + return result; + else + result = result->next; + + /* Prepare new message set. */ + result = (struct set_list *) xmalloc (sizeof (*result)); + result->number = number; + result->deleted = 0; + result->messages = NULL; + result->next = current->all_sets; + current->all_sets = result; + + return result; +} + + +/* Normalize given string *in*place* by processing escape sequences + and quote characters. */ +static void +normalize_line (const char *fname, size_t line, char *string, char quote_char) +{ + int is_quoted; + char *rp = string; + char *wp = string; + + if (quote_char != '\0' && *rp == quote_char) + { + is_quoted = 1; + ++rp; + } + else + is_quoted = 0; + + while (*rp != '\0') + if (*rp == quote_char) + /* We simply end the string when we find the first time an + not-escaped quote character. */ + break; + else if (*rp == '\\') + { + ++rp; + if (quote_char != '\0' && *rp == quote_char) + /* This is an extension to XPG. */ + *wp++ = *rp++; + else + /* Recognize escape sequences. */ + switch (*rp) + { + case 'n': + *wp++ = '\n'; + ++rp; + break; + case 't': + *wp++ = '\t'; + ++rp; + break; + case 'v': + *wp++ = '\v'; + ++rp; + break; + case 'b': + *wp++ = '\b'; + ++rp; + break; + case 'r': + *wp++ = '\r'; + ++rp; + break; + case 'f': + *wp++ = '\f'; + ++rp; + break; + case '\\': + *wp++ = '\\'; + ++rp; + break; + case '0' ... '7': + { + int number = *rp++ - '0'; + while (number <= (255 / 8) && *rp >= '0' && *rp <= '7') + { + number *= 8; + number += *rp++ - '0'; + } + *wp++ = (char) number; + } + break; + default: + /* Simply ignore the backslash character. */ + break; + } + } + else + *wp++ = *rp++; + + /* If we saw a quote character at the beginning we expect another + one at the end. */ + if (is_quoted && *rp != quote_char) + error (0, 0, fname, line, gettext ("unterminated message")); + + /* Terminate string. */ + *wp = '\0'; + return; +} + + +static void +read_old (struct catalog *catalog, const char *file_name) +{ + struct catalog_info old_cat_obj; + struct set_list *set = NULL; + int last_set = -1; + size_t cnt; + + old_cat_obj.status = closed; + old_cat_obj.cat_name = file_name; + + /* Try to open catalog, but don't look through the NLSPATH. */ + __open_catalog (&old_cat_obj, 0); + + if (old_cat_obj.status != mmaped && old_cat_obj.status != malloced) + if (errno == ENOENT) + /* No problem, the catalog simply does not exist. */ + return; + else + error (EXIT_FAILURE, errno, gettext ("while opening old catalog file")); + + /* OK, we have the catalog loaded. Now read all messages and merge + them. When set and message number clash for any message the new + one is used. */ + for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt) + { + struct message_list *message, *last; + + if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0) + /* No message in this slot. */ + continue; + + if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != last_set) + { + last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1; + set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1); + } + + last = NULL; + message = set->messages; + while (message != NULL) + { + if (message->number >= old_cat_obj.name_ptr[cnt * 3 + 1]) + break; + last = message; + message = message->next; + } + + if (message == NULL + || message->number > old_cat_obj.name_ptr[cnt * 3 + 1]) + { + /* We have found a message which is not yet in the catalog. + Insert it at the right position. */ + struct message_list *newp; + + newp = (struct message_list *) xmalloc (sizeof(*newp)); + newp->number = old_cat_obj.name_ptr[cnt * 3 + 1]; + newp->message = + &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]]; + newp->fname = NULL; + newp->line = 0; + newp->symbol = NULL; + newp->next = message; + + if (last == NULL) + set->messages = newp; + else + last->next = newp; + + ++catalog->total_messages; + } + } +} diff --git a/catgets/nl_types.h b/catgets/nl_types.h new file mode 100644 index 0000000000..9f78d67468 --- /dev/null +++ b/catgets/nl_types.h @@ -0,0 +1,48 @@ +/* Copyright (C) 1996 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., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef _NL_TYPES_H +#define _NL_TYPES_H 1 +#include <features.h> + +/* The default message set used by the gencat program. */ +#define NL_SETD 1 + +/* Value for FLAG parameter of `catgets' to say we want XPG4 compliance. */ +#define NL_CAT_LOCALE 1 + +/* Message catalog descriptor type. */ +typedef void *nl_catd; + + +__BEGIN_DECLS + +/* Open message catalog for later use, returning descriptor. */ +extern nl_catd catopen __P ((__const char *__cat_name, int __flag)); + +/* Return translation with NUMBER in SET of CATALOG; if not found + return STRING. */ +extern char *catgets __P ((nl_catd __catalog, int __set, int __number, + __const char *__string)); + +/* Close message CATALOG. */ +extern int catclose __P ((nl_catd __catalog)); + +__END_DECLS + +#endif /* nl_types.h */ diff --git a/catgets/open_catalog.c b/catgets/open_catalog.c new file mode 100644 index 0000000000..9a8c7e643e --- /dev/null +++ b/catgets/open_catalog.c @@ -0,0 +1,263 @@ +/* Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <endian.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include "catgetsinfo.h" + + +#define SWAPU32(w) \ + (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24)) + + +void +__open_catalog (__nl_catd catalog, int with_path) +{ + int fd; + struct stat st; + int swapping; + + if (strchr (catalog->cat_name, '/') != NULL || !with_path) + fd = open (catalog->cat_name, O_RDONLY); + else + { + const char *run_nlspath = catalog->nlspath; +#define ENOUGH(n) \ + if (bufact + (n) >=bufmax) \ + { \ + char *old_buf = buf; \ + bufmax += 256 + (n); \ + buf = (char *) alloca (bufmax); \ + memcpy (buf, old_buf, bufact); \ + } + + /* The RUN_NLSPATH variable contains a colon separated list of + descriptions where we expect to find catalogs. We have to + recognize certain % substitutions and stop when we found the + first existing file. */ + char *buf; + size_t bufact; + size_t bufmax; + + buf = NULL; + bufmax = 0; + + fd = -1; + while (*run_nlspath != '\0') + { + bufact = 0; + while (*run_nlspath != ':' && *run_nlspath != '\0') + if (*run_nlspath == '%') + { + const char *tmp; + + ++run_nlspath; /* We have seen the `%'. */ + switch (*run_nlspath++) + { + case 'N': + /* Use the catalog name. */ + ENOUGH (strlen (catalog->cat_name)); + bufact = stpcpy (&buf[bufact], catalog->cat_name) - buf; + break; + case 'L': + /* Use the current locale category value. */ + ENOUGH (strlen (catalog->env_var)); + bufact = stpcpy (&buf[bufact], catalog->env_var) - buf; + break; + case 'l': + /* Use language element of locale category value. */ + tmp = catalog->env_var; + do + { + ENOUGH (1); + buf[bufact++] = *tmp++; + } + while (*tmp != '\0' && *tmp != '_' && *tmp != '.'); + break; + case 't': + /* Use territory element of locale category value. */ + tmp = catalog->env_var; + do + ++tmp; + while (*tmp != '\0' && *tmp != '_' && *tmp != '.'); + if (*tmp == '_') + { + ++tmp; + do + { + ENOUGH (1); + buf[bufact++] = *tmp; + } + while (*tmp != '\0' && *tmp != '.'); + } + break; + case 'c': + /* Use code set element of locale category value. */ + tmp = catalog->env_var; + do + ++tmp; + while (*tmp != '\0' && *tmp != '.'); + if (*tmp == '.') + { + ++tmp; + do + { + ENOUGH (1); + buf[bufact++] = *tmp; + } + while (*tmp != '\0'); + } + break; + case '%': + ENOUGH (1); + buf[bufact++] = '%'; + break; + default: + /* Unknown variable: ignore this path element. */ + bufact = 0; + while (*run_nlspath != '\0' && *run_nlspath != ':') + ++run_nlspath; + break; + } + } + else + { + ENOUGH (1); + buf[bufact++] = *run_nlspath++; + } + ENOUGH (1); + buf[bufact] = '\0'; + + if (bufact != 0) + { + fd = open (buf, O_RDONLY); + if (fd >= 0) + break; + } + + ++run_nlspath; + } + } + + if (fd < 0 || fstat (fd, &st) < 0) + { + catalog->status = nonexisting; + return; + } + +#ifndef MAP_COPY + /* Linux seems to lack read-only copy-on-write. */ +#define MAP_COPY MAP_PRIVATE +#endif +#ifndef MAP_FILE + /* Some systems do not have this flag; it is superfluous. */ +#define MAP_FILE 0 +#endif +#ifndef MAP_INHERIT + /* Some systems might lack this; they lose. */ +#define MAP_INHERIT 0 +#endif + catalog->file_size = st.st_size; + catalog->file_ptr = + (struct catalog_obj *) mmap (NULL, st.st_size, PROT_READ, + MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0); + if (catalog->file_ptr != (struct catalog_obj *) -1) + /* Tell the world we managed to mmap the file. */ + catalog->status = mmaped; + else + { + /* mmap failed perhaps because the system call is not + implemented. Try to load the file. */ + size_t todo; + catalog->file_ptr = malloc (st.st_size); + if (catalog->file_ptr == NULL) + { + catalog->status = nonexisting; + return; + } + todo = st.st_size; + /* Save read, handle partial reads. */ + do + { + size_t now = read (fd, (((char *) &catalog->file_ptr) + + (st.st_size - todo)), todo); + if (now == 0) + { + free ((void *) catalog->file_ptr); + catalog->status = nonexisting; + return; + } + todo -= now; + } + while (todo > 0); + catalog->status = malloced; + } + + /* We don't need the file anymore. */ + close (fd); + + /* Determine whether the file is a catalog file and if yes whether + it is written using the correct byte order. Else we have to swap + the values. */ + if (catalog->file_ptr->magic == CATGETS_MAGIC) + swapping = 0; + else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC)) + swapping = 1; + else + { + /* Illegal file. Free he resources and mark catalog as not + usable. */ + if (catalog->status == mmaped) + munmap ((void *) catalog->file_ptr, catalog->file_size); + else + free (catalog->file_ptr); + catalog->status = nonexisting; + return; + } + +#define SWAP(x) (swapping ? SWAPU32 (x) : (x)) + + /* Get dimensions of the used hashing table. */ + catalog->plane_size = SWAP (catalog->file_ptr->plane_size); + catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth); + + /* The file contains two versions of the pointer tables. Pick the + right one for the local byte order. */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + catalog->name_ptr = &catalog->file_ptr->name_ptr[0]; +#elif __BYTE_ORDER == __BIG_ENDIAN + catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size + * catalog->plane_depth + * 3]; +#else +# error Cannot handle __BYTE_ORDER byte order +#endif + + /* The rest of the file contains all the strings. They are + addressed relative to the position of the first string. */ + catalog->strings = + (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size + * catalog->plane_depth * 3 * 2]; +} |