diff options
author | Roland McGrath <roland@gnu.org> | 1996-04-20 00:05:25 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 1996-04-20 00:05:25 +0000 |
commit | a641835acd28fabf507c5a99d7805c694c2ec7e3 (patch) | |
tree | 5829796a450826b540dc763b8891b110bafd35b1 /catgets/gencat.c | |
parent | f24f4dc82d76bc2bed2d7e28877c5e9e469d4931 (diff) | |
download | glibc-a641835acd28fabf507c5a99d7805c694c2ec7e3.tar glibc-a641835acd28fabf507c5a99d7805c694c2ec7e3.tar.gz glibc-a641835acd28fabf507c5a99d7805c694c2ec7e3.tar.bz2 glibc-a641835acd28fabf507c5a99d7805c694c2ec7e3.zip |
Fri Apr 19 00:49:44 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>cvs/libc-960420
* stdlib/rpmatch.c (rpmatch: try): Take new arg NOMATCH, return value
for nonmatching nonerror (instead of !MATCH).
(rpmatch): Use it, so we return -1 when NOEXPR doesn't match either.
* resolv/getnetnamadr.c (getnetbyaddr): Use u_int32_t instead of
unsigned long for variable NET2.
* time/etcetera, time/europe, time/solar89: Updated from ADO's 96e.
Tue Apr 9 14:37:31 1996 Ulrich Drepper <drepper@cygnus.com>
* catgets/Makefile, catgets/catgets.c, catgets/catgetsinfo.h,
catgets/config.h, catgets/gencat.c, catgets/nl_types.h,
catgets/open_catalog.c: New files. Implementation of XPG4
compliant catgets() function and needed tools.
* Makefile (subdirs): Add catgets.
Thu Apr 18 23:36:11 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
* math/Makefile (CPPFLAGS): Append -D__NO_MATH_INLINES.
Wed Apr 10 20:48:43 1996 Ulrich Drepper <drepper@cygnus.com>
* stdio-common/vfprintf.c: Correct some typos.
* sysdeps/libm-ieee754/w_gammaf.c, sysdeps/libm-ieee754/w_lgamma.c,
sysdeps/libm-ieee754/w_lgammaf.c: Reference signgam instead of
__signgam.
Thu Apr 18 21:07:27 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
* Makerules (no-whole-archive): New variable.
(build-shlib): Use it.
* elf/Makefile (libdl.so): Use it.
* configure.in (libc_cv_ld_no_whole_archive): New check for
--no-whole-archive.
* config.make.in (have-no-whole-archive): New variable.
* stdio-common/printf_fp.c: Increase fudge factor for BIGNUM_SIZE calc
from 3 to 4.
* Make-dist: Include version.mk.
(version, release): Variables removed.
* Makeconfig (version.mk): New target.
Fri Apr 19 01:42:18 1996 Ulrich Drepper <drepper@cygnus.com>
* locale/Makefile (headers): Add langinfo.h.
(CPPFLAGS): Remove -Iliblib.
Diffstat (limited to 'catgets/gencat.c')
-rw-r--r-- | catgets/gencat.c | 1030 |
1 files changed, 1030 insertions, 0 deletions
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; + } + } +} |