-/* 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>.
+/* Copyright (C) 1996-2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 1996.
-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.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; 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.
+ This program 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 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. */
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
-# include <config.h>
+# include "config.h"
#endif
+#include <argp.h>
+#include <assert.h>
#include <ctype.h>
#include <endian.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
-#include <getopt.h>
+#include <iconv.h>
+#include <langinfo.h>
#include <locale.h>
#include <libintl.h>
#include <limits.h>
#include <nl_types.h>
#include <obstack.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <wchar.h>
#include "version.h"
struct set_list *all_sets;
struct set_list *current_set;
size_t total_messages;
- char quote_char;
+ wint_t 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[] =
+/* Name of output file. */
+static const char *output_name;
+
+/* Name of generated C header file. */
+static const char *header_name;
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+#define OPT_NEW 1
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { "header", 'H', N_("NAME"), 0,
+ N_("Create C header file NAME containing symbol definitions") },
+ { "new", OPT_NEW, NULL, 0,
+ N_("Do not use existing catalog, force new output file") },
+ { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") },
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Generate message catalog.\
+\vIf INPUT-FILE is -, input is read from standard input. If OUTPUT-FILE\n\
+is -, output is written to standard output.\n");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("\
+-o OUTPUT-FILE [INPUT-FILE]...\n[OUTPUT-FILE [INPUT-FILE]...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Function to print some extra text in the help message. */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
{
- { "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 }
+ options, parse_opt, args_doc, doc, NULL, more_help
};
+
/* Wrapper functions with error checking for standard functions. */
-extern void *xmalloc (size_t n);
+#include <programs/xmalloc.h>
/* 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 normalize_line (const char *fname, size_t line, iconv_t cd,
+ wchar_t *string, wchar_t quote_char,
+ wchar_t escape_char);
static void read_old (struct catalog *catalog, const char *file_name);
+static int open_conversion (const char *codesetp, iconv_t *cd_towcp,
+ iconv_t *cd_tombp, wchar_t *escape_charp);
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;
+ int remaining;
/* Set program name for messages. */
error_print_progname = error_print;
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, "gencat (GNU %s) %s\n", PACKAGE, VERSION);
- fprintf (stderr, _("\
-Copyright (C) %s Free Software Foundation, Inc.\n\
-This is free software; see the source for copying conditions. There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "1996");
- fprintf (stderr, _("Written by %s.\n"),
- "Ulrich Drepper");
-
- exit (EXIT_SUCCESS);
- }
-
- /* Help is requested. */
- if (do_help)
- usage (EXIT_SUCCESS);
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
/* Determine output file. */
if (output_name == NULL)
- output_name = optind < argc ? argv[optind++] : "-";
+ output_name = remaining < argc ? argv[remaining++] : "-";
/* Process all input files. */
setlocale (LC_CTYPE, "C");
- if (optind < argc)
+ if (remaining < argc)
do
- result = read_input_file (result, argv[optind]);
- while (++optind < argc);
+ result = read_input_file (result, argv[remaining]);
+ while (++remaining < argc);
else
result = read_input_file (NULL, "-");
if (result != NULL)
write_out (result, output_name, header_name);
- exit (EXIT_SUCCESS);
+ return error_message_count != 0;
}
-static void
-usage (int status)
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
{
- if (status != EXIT_SUCCESS)
- fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
- program_invocation_name);
- else
+ switch (key)
+ {
+ case 'H':
+ header_name = arg;
+ break;
+ case OPT_NEW:
+ force_new = 1;
+ break;
+ case 'o':
+ output_name = arg;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static char *
+more_help (int key, const char *text, void *input)
+{
+ char *tp = NULL;
+ switch (key)
{
- 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);
- fputs (gettext ("Report bugs to <bug-glibc@prep.ai.mit.edu>.\n"),
- stdout);
+ case ARGP_KEY_HELP_EXTRA:
+ /* We print some extra information. */
+ if (asprintf (&tp, gettext ("\
+For bug reporting instructions, please see:\n\
+%s.\n"), REPORT_BUGS_TO) < 0)
+ return NULL;
+ return tp;
+ default:
+ break;
}
+ return (char *) text;
+}
- exit (status);
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "gencat %s%s\n", PKGVERSION, VERSION);
+ fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2014");
+ fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
}
/* The address of this function will be assigned to the hook in the
error functions. */
static void
-error_print ()
+error_print (void)
{
/* We don't want the program name to be printed in messages. Emacs'
compile.el does not like this. */
char *buf;
size_t len;
size_t line_number;
+ wchar_t *wbuf;
+ size_t wbufsize;
+ iconv_t cd_towc = (iconv_t) -1;
+ iconv_t cd_tomb = (iconv_t) -1;
+ wchar_t escape_char = L'\\';
+ char *codeset = NULL;
if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
{
/* If we haven't seen anything yet, allocate result structure. */
if (current == NULL)
{
- current = (struct catalog *) xmalloc (sizeof (*current));
+ current = (struct catalog *) xcalloc (1, 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_alloc malloc
#define obstack_chunk_free free
obstack_init (¤t->mem_pool);
+
+ current->current_set = find_set (current, NL_SETD);
}
buf = NULL;
len = 0;
line_number = 0;
+
+ wbufsize = 1024;
+ wbuf = (wchar_t *) xmalloc (wbufsize);
+
while (!feof (fp))
{
int continued;
++line_number;
/* It the line continued? */
+ continued = 0;
if (buf[act_len - 1] == '\n')
{
--act_len;
- continued = buf[act_len - 1] == '\\';
- if (continued)
- --act_len;
+
+ /* There might be more than one backslash at the end of
+ the line. Only if there is an odd number of them is
+ the line continued. */
+ if (act_len > 0 && buf[act_len - 1] == '\\')
+ {
+ int temp_act_len = act_len;
+
+ do
+ {
+ --temp_act_len;
+ continued = !continued;
+ }
+ while (temp_act_len > 0 && buf[temp_act_len - 1] == '\\');
+
+ if (continued)
+ --act_len;
+ }
}
- else
- continued = 0;
/* Append to currently selected line. */
obstack_grow (¤t->mem_pool, buf, act_len);
used = 0;
if (this_line[0] == '$')
{
- if (isspace (this_line[1]))
- /* This is a comment line. Do nothing. */;
+ if (isblank (this_line[1]))
+ {
+ int cnt = 1;
+ while (isblank (this_line[cnt]))
+ ++cnt;
+ if (strncmp (&this_line[cnt], "codeset=", 8) != 0)
+ /* This is a comment line. Do nothing. */;
+ else if (codeset != NULL)
+ /* Ignore multiple codeset. */;
+ else
+ {
+ int start = cnt + 8;
+ cnt = start;
+ while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
+ ++cnt;
+ if (cnt != start)
+ {
+ int len = cnt - start;
+ codeset = xmalloc (len + 1);
+ *((char *) mempcpy (codeset, &this_line[start], len))
+ = '\0';
+ }
+ }
+ }
else if (strncmp (&this_line[1], "set", 3) == 0)
{
- int cnt = sizeof ("cnt");
+ int cnt = sizeof ("set");
int set_number;
const char *symbol = NULL;
while (isspace (this_line[cnt]))
}
else
{
- /* We have found seomthing which looks like a
+ /* We have found seomthing that looks like a
correct identifier. */
struct set_list *runp;
else if (strncmp (&this_line[1], "delset", 6) == 0)
{
int cnt = sizeof ("delset");
- size_t set_number;
while (isspace (this_line[cnt]))
++cnt;
++cnt;
if (cnt == start)
- {
- error_at_line (0, 0, fname, start_line,
- gettext ("illegal set number"));
- set_number = 0;
- }
+ error_at_line (0, 0, fname, start_line,
+ gettext ("illegal set number"));
else
{
const char *symbol;
/* 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)
{
}
else if (strncmp (&this_line[1], "quote", 5) == 0)
{
- int cnt = sizeof ("quote");
+ char buf[2];
+ char *bufptr;
+ size_t buflen;
+ char *wbufptr;
+ size_t wbuflen;
+ int cnt;
+
+ cnt = sizeof ("quote");
while (isspace (this_line[cnt]))
++cnt;
+
+ /* We need the conversion. */
+ if (cd_towc == (iconv_t) -1
+ && open_conversion (codeset, &cd_towc, &cd_tomb,
+ &escape_char) != 0)
+ /* Something is wrong. */
+ goto out;
+
/* Yes, the quote char can be '\0'; this means no quote
- char. */
- current->quote_char = this_line[cnt];
+ char. The function using the information works on
+ wide characters so we have to convert it here. */
+ buf[0] = this_line[cnt];
+ buf[1] = '\0';
+ bufptr = buf;
+ buflen = 2;
+
+ wbufptr = (char *) wbuf;
+ wbuflen = wbufsize;
+
+ /* Flush the state. */
+ iconv (cd_towc, NULL, NULL, NULL, NULL);
+
+ iconv (cd_towc, &bufptr, &buflen, &wbufptr, &wbuflen);
+ if (buflen != 0 || (wchar_t *) wbufptr != &wbuf[2])
+ error_at_line (0, 0, fname, start_line,
+ gettext ("invalid quote character"));
+ else
+ /* Use the converted wide character. */
+ current->quote_char = wbuf[0];
}
else
{
else if (isalnum (this_line[0]) || this_line[0] == '_')
{
const char *ident = this_line;
+ char *line = this_line;
int message_number;
do
- ++this_line;
- while (this_line[0] != '\0' && !isspace (this_line[0]));;
- this_line[0] = '\0'; /* Terminate the identifier. */
+ ++line;
+ while (line[0] != '\0' && !isspace (line[0]));
+ if (line[0] != '\0')
+ *line++ = '\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;
+ struct message_list *lastp;
message_number = atoi (ident);
/* Find location to insert the new message. */
runp = current->current_set->messages;
+ lastp = NULL;
while (runp != NULL)
if (runp->number == message_number)
break;
else
- runp = runp->next;
+ {
+ lastp = runp;
+ 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;
+ number in the message set. */
+ if (runp->symbol == NULL)
+ {
+ /* The existing message had its number specified
+ by the user. Fatal collision type uh, oh. */
+ 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;
+ }
+ else
+ {
+ /* Collision was with number auto-assigned to a
+ symbolic. Change existing symbolic number
+ and move to end the list (if not already there). */
+ runp->number = ++current->current_set->last_message;
+
+ if (runp->next != NULL)
+ {
+ struct message_list *endp;
+
+ if (lastp == NULL)
+ current->current_set->messages=runp->next;
+ else
+ lastp->next=runp->next;
+
+ endp = runp->next;
+ while (endp->next != NULL)
+ endp = endp->next;
+
+ endp->next = runp;
+ runp->next = NULL;
+ }
+ }
}
ident = NULL; /* We don't have a symbol. */
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. */
+ runp = current->current_set->messages;
while (runp != NULL)
if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
break;
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, 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;
if (message_number != 0)
{
+ char *inbuf;
+ size_t inlen;
+ char *outbuf;
+ size_t outlen;
struct message_list *newp;
+ size_t line_len = strlen (line) + 1;
+ size_t ident_len = 0;
+
+ /* We need the conversion. */
+ if (cd_towc == (iconv_t) -1
+ && open_conversion (codeset, &cd_towc, &cd_tomb,
+ &escape_char) != 0)
+ /* Something is wrong. */
+ goto out;
+
+ /* Convert to a wide character string. We have to
+ interpret escape sequences which will be impossible
+ without doing the conversion if the codeset of the
+ message is stateful. */
+ while (1)
+ {
+ inbuf = line;
+ inlen = line_len;
+ outbuf = (char *) wbuf;
+ outlen = wbufsize;
- used = 1; /* Yes, we use the line. */
+ /* Flush the state. */
+ iconv (cd_towc, NULL, NULL, NULL, NULL);
+
+ iconv (cd_towc, &inbuf, &inlen, &outbuf, &outlen);
+ if (inlen == 0)
+ {
+ /* The string is converted. */
+ assert (outlen < wbufsize);
+ assert (wbuf[(wbufsize - outlen) / sizeof (wchar_t) - 1]
+ == L'\0');
+ break;
+ }
+
+ if (outlen != 0)
+ {
+ /* Something is wrong with this string, we ignore it. */
+ error_at_line (0, 0, fname, start_line, gettext ("\
+invalid character: message ignored"));
+ goto ignore;
+ }
+
+ /* The output buffer is too small. */
+ wbufsize *= 2;
+ wbuf = (wchar_t *) xrealloc (wbuf, wbufsize);
+ }
/* Strip quote characters, change escape sequences into
correct characters etc. */
- normalize_line (fname, start_line, this_line,
- current->quote_char);
+ normalize_line (fname, start_line, cd_towc, wbuf,
+ current->quote_char, escape_char);
+
+ if (ident)
+ ident_len = line - this_line;
+
+ /* Now the string is free of escape sequences. Convert it
+ back into a multibyte character string. First free the
+ memory allocated for the original string. */
+ obstack_free (¤t->mem_pool, this_line);
+
+ used = 1; /* Yes, we use the line. */
+
+ /* Now fill in the new string. It should never happen that
+ the replaced string is longer than the original. */
+ inbuf = (char *) wbuf;
+ inlen = (wcslen (wbuf) + 1) * sizeof (wchar_t);
+
+ outlen = obstack_room (¤t->mem_pool);
+ obstack_blank (¤t->mem_pool, outlen);
+ this_line = (char *) obstack_base (¤t->mem_pool);
+ outbuf = this_line + ident_len;
+ outlen -= ident_len;
+
+ /* Flush the state. */
+ iconv (cd_tomb, NULL, NULL, NULL, NULL);
+
+ iconv (cd_tomb, &inbuf, &inlen, &outbuf, &outlen);
+ if (inlen != 0)
+ {
+ error_at_line (0, 0, fname, start_line,
+ gettext ("invalid line"));
+ goto ignore;
+ }
+ assert (outbuf[-1] == '\0');
+
+ /* Free the memory in the obstack we don't use. */
+ obstack_blank (¤t->mem_pool, -(int) outlen);
+ line = obstack_finish (¤t->mem_pool);
newp = (struct message_list *) xmalloc (sizeof (*newp));
newp->number = message_number;
- newp->message = this_line;
+ newp->message = line + ident_len;
/* Remember symbolic name; is NULL if no is given. */
- newp->symbol = ident;
+ newp->symbol = ident ? line : NULL;
/* Remember where we found the character. */
newp->fname = fname;
newp->line = start_line;
gettext ("malformed line ignored"));
}
+ ignore:
/* We can save the memory for the line if it was not used. */
if (!used)
obstack_free (¤t->mem_pool, this_line);
}
+ /* Close the conversion modules. */
+ iconv_close (cd_towc);
+ iconv_close (cd_tomb);
+ free (codeset);
+
+ out:
+ free (wbuf);
+
if (fp != stdin)
fclose (fp);
return current;
struct obstack string_pool;
const char *strings;
size_t strings_size;
- u_int32_t *array1, *array2;
+ uint32_t *array1, *array2;
size_t cnt;
int fd;
/* 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);
+ (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
+ memset (array1, '\0', best_size * best_depth * sizeof (uint32_t) * 3);
array2
- = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
+ = (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
obstack_init (&string_pool);
set_run = catalog->all_sets;
/* 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);
+ write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
+ write (fd, array2, best_size * best_depth * sizeof (uint32_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);
+ write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
+ write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
#else
# error Cannot handle __BYTE_ORDER byte order
#endif
#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:%Zu */\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:%Zu */\n",
- set_run->symbol, message_run->symbol,
- message_run->number, message_run->fname,
- message_run->line);
+ {
+ if (set_run->symbol == NULL)
+ fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu */\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:%Zu */\n",
+ set_run->symbol, message_run->symbol,
+ message_run->number, message_run->fname,
+ message_run->line);
+ }
message_run = message_run->next;
}
result = result->next;
/* Prepare new message set. */
- result = (struct set_list *) xmalloc (sizeof (*result));
+ result = (struct set_list *) xcalloc (1, sizeof (*result));
result->number = number;
- result->deleted = 0;
- result->messages = NULL;
result->next = current->all_sets;
current->all_sets = 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)
+normalize_line (const char *fname, size_t line, iconv_t cd, wchar_t *string,
+ wchar_t quote_char, wchar_t escape_char)
{
int is_quoted;
- char *rp = string;
- char *wp = string;
+ wchar_t *rp = string;
+ wchar_t *wp = string;
- if (quote_char != '\0' && *rp == quote_char)
+ if (quote_char != L'\0' && *rp == quote_char)
{
is_quoted = 1;
++rp;
else
is_quoted = 0;
- while (*rp != '\0')
+ while (*rp != L'\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 == '\\')
+ else if (*rp == escape_char)
{
++rp;
- if (quote_char != '\0' && *rp == quote_char)
+ if (quote_char != L'\0' && *rp == quote_char)
/* This is an extension to XPG. */
*wp++ = *rp++;
else
/* Recognize escape sequences. */
switch (*rp)
{
- case 'n':
- *wp++ = '\n';
+ case L'n':
+ *wp++ = L'\n';
++rp;
break;
- case 't':
- *wp++ = '\t';
+ case L't':
+ *wp++ = L'\t';
++rp;
break;
- case 'v':
- *wp++ = '\v';
+ case L'v':
+ *wp++ = L'\v';
++rp;
break;
- case 'b':
- *wp++ = '\b';
+ case L'b':
+ *wp++ = L'\b';
++rp;
break;
- case 'r':
- *wp++ = '\r';
+ case L'r':
+ *wp++ = L'\r';
++rp;
break;
- case 'f':
- *wp++ = '\f';
+ case L'f':
+ *wp++ = L'\f';
++rp;
break;
- case '\\':
- *wp++ = '\\';
- ++rp;
- break;
- case '0' ... '7':
+ case L'0' ... L'7':
{
- int number = *rp++ - '0';
- while (number <= (255 / 8) && *rp >= '0' && *rp <= '7')
+ int number;
+ char cbuf[2];
+ char *cbufptr;
+ size_t cbufin;
+ wchar_t wcbuf[2];
+ char *wcbufptr;
+ size_t wcbufin;
+
+ number = *rp++ - L'0';
+ while (number <= (255 / 8) && *rp >= L'0' && *rp <= L'7')
{
number *= 8;
- number += *rp++ - '0';
+ number += *rp++ - L'0';
}
- *wp++ = (char) number;
+
+ cbuf[0] = (char) number;
+ cbuf[1] = '\0';
+ cbufptr = cbuf;
+ cbufin = 2;
+
+ wcbufptr = (char *) wcbuf;
+ wcbufin = sizeof (wcbuf);
+
+ /* Flush the state. */
+ iconv (cd, NULL, NULL, NULL, NULL);
+
+ iconv (cd, &cbufptr, &cbufin, &wcbufptr, &wcbufin);
+ if (cbufptr != &cbuf[2] || (wchar_t *) wcbufptr != &wcbuf[2])
+ error_at_line (0, 0, fname, line,
+ gettext ("invalid escape sequence"));
+ else
+ *wp++ = wcbuf[0];
}
break;
default:
- /* Simply ignore the backslash character. */
+ if (*rp == escape_char)
+ {
+ *wp++ = escape_char;
+ ++rp;
+ }
+ else
+ /* Simply ignore the backslash character. */;
break;
}
}
/* 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"));
+ error_at_line (0, 0, fname, line, gettext ("unterminated message"));
/* Terminate string. */
- *wp = '\0';
+ *wp = L'\0';
return;
}
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"));
+ if (__open_catalog (file_name, NULL, NULL, &old_cat_obj) != 0)
+ {
+ 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. */
+ one is used. If the new one is empty it indicates that the
+ message should be deleted. */
for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
{
struct message_list *message, *last;
/* No message in this slot. */
continue;
- if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set)
+ if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (uint32_t) 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);
message = set->messages;
while (message != NULL)
{
- if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
+ if ((uint32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
break;
last = message;
message = message->next;
}
if (message == NULL
- || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
+ || (uint32_t) 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. */
++catalog->total_messages;
}
+ else if (*message->message == '\0')
+ {
+ /* The new empty message has overridden the old one thus
+ "deleting" it as required. Now remove the empty remains. */
+ if (last == NULL)
+ set->messages = message->next;
+ else
+ last->next = message->next;
+ }
+ }
+}
+
+
+static int
+open_conversion (const char *codeset, iconv_t *cd_towcp, iconv_t *cd_tombp,
+ wchar_t *escape_charp)
+{
+ char buf[2];
+ char *bufptr;
+ size_t bufsize;
+ wchar_t wbuf[2];
+ char *wbufptr;
+ size_t wbufsize;
+
+ /* If the input file does not specify the codeset use the locale's. */
+ if (codeset == NULL)
+ {
+ setlocale (LC_ALL, "");
+ codeset = nl_langinfo (CODESET);
+ setlocale (LC_ALL, "C");
+ }
+
+ /* Get the conversion modules. */
+ *cd_towcp = iconv_open ("WCHAR_T", codeset);
+ *cd_tombp = iconv_open (codeset, "WCHAR_T");
+ if (*cd_towcp == (iconv_t) -1 || *cd_tombp == (iconv_t) -1)
+ {
+ error (0, 0, gettext ("conversion modules not available"));
+ if (*cd_towcp != (iconv_t) -1)
+ iconv_close (*cd_towcp);
+
+ return 1;
}
+
+ /* One special case for historical reasons is the backslash
+ character. In some codesets the byte value 0x5c is not mapped to
+ U005c in Unicode. These charsets then don't have a backslash
+ character at all. Therefore we have to live with whatever the
+ codeset provides and recognize, instead of the U005c, the character
+ the byte value 0x5c is mapped to. */
+ buf[0] = '\\';
+ buf[1] = '\0';
+ bufptr = buf;
+ bufsize = 2;
+
+ wbufptr = (char *) wbuf;
+ wbufsize = sizeof (wbuf);
+
+ iconv (*cd_towcp, &bufptr, &bufsize, &wbufptr, &wbufsize);
+ if (bufsize != 0 || wbufsize != 0)
+ {
+ /* Something went wrong, we couldn't convert the byte 0x5c. Go
+ on with using U005c. */
+ error (0, 0, gettext ("cannot determine escape character"));
+ *escape_charp = L'\\';
+ }
+ else
+ *escape_charp = wbuf[0];
+
+ return 0;
}