Blame catgets/gencat.c

Packit 6c4009
/* Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@redhat.com>, 1996.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#ifdef HAVE_CONFIG_H
Packit 6c4009
# include "config.h"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <argp.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <endian.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <iconv.h>
Packit 6c4009
#include <langinfo.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <nl_types.h>
Packit 6c4009
#include <obstack.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <wchar.h>
Packit 6c4009
Packit 6c4009
#include "version.h"
Packit 6c4009
Packit 6c4009
#include "catgetsinfo.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define SWAPU32(w) \
Packit 6c4009
  (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
Packit 6c4009
Packit 6c4009
struct message_list
Packit 6c4009
{
Packit 6c4009
  int number;
Packit 6c4009
  const char *message;
Packit 6c4009
Packit 6c4009
  const char *fname;
Packit 6c4009
  size_t line;
Packit 6c4009
  const char *symbol;
Packit 6c4009
Packit 6c4009
  struct message_list *next;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct set_list
Packit 6c4009
{
Packit 6c4009
  int number;
Packit 6c4009
  int deleted;
Packit 6c4009
  struct message_list *messages;
Packit 6c4009
  int last_message;
Packit 6c4009
Packit 6c4009
  const char *fname;
Packit 6c4009
  size_t line;
Packit 6c4009
  const char *symbol;
Packit 6c4009
Packit 6c4009
  struct set_list *next;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct catalog
Packit 6c4009
{
Packit 6c4009
  struct set_list *all_sets;
Packit 6c4009
  struct set_list *current_set;
Packit 6c4009
  size_t total_messages;
Packit 6c4009
  wint_t quote_char;
Packit 6c4009
  int last_set;
Packit 6c4009
Packit 6c4009
  struct obstack mem_pool;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* If non-zero force creation of new file, not using existing one.  */
Packit 6c4009
static int force_new;
Packit 6c4009
Packit 6c4009
/* Name of output file.  */
Packit 6c4009
static const char *output_name;
Packit 6c4009
Packit 6c4009
/* Name of generated C header file.  */
Packit 6c4009
static const char *header_name;
Packit 6c4009
Packit 6c4009
/* Name and version of program.  */
Packit 6c4009
static void print_version (FILE *stream, struct argp_state *state);
Packit 6c4009
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
Packit 6c4009
Packit 6c4009
#define OPT_NEW 1
Packit 6c4009
Packit 6c4009
/* Definitions of arguments for argp functions.  */
Packit 6c4009
static const struct argp_option options[] =
Packit 6c4009
{
Packit 6c4009
  { "header", 'H', N_("NAME"), 0,
Packit 6c4009
    N_("Create C header file NAME containing symbol definitions") },
Packit 6c4009
  { "new", OPT_NEW, NULL, 0,
Packit 6c4009
    N_("Do not use existing catalog, force new output file") },
Packit 6c4009
  { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") },
Packit 6c4009
  { NULL, 0, NULL, 0, NULL }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Short description of program.  */
Packit 6c4009
static const char doc[] = N_("Generate message catalog.\
Packit 6c4009
\vIf INPUT-FILE is -, input is read from standard input.  If OUTPUT-FILE\n\
Packit 6c4009
is -, output is written to standard output.\n");
Packit 6c4009
Packit 6c4009
/* Strings for arguments in help texts.  */
Packit 6c4009
static const char args_doc[] = N_("\
Packit 6c4009
-o OUTPUT-FILE [INPUT-FILE]...\n[OUTPUT-FILE [INPUT-FILE]...]");
Packit 6c4009
Packit 6c4009
/* Prototype for option handler.  */
Packit 6c4009
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 6c4009
Packit 6c4009
/* Function to print some extra text in the help message.  */
Packit 6c4009
static char *more_help (int key, const char *text, void *input);
Packit 6c4009
Packit 6c4009
/* Data structure to communicate with argp functions.  */
Packit 6c4009
static struct argp argp =
Packit 6c4009
{
Packit 6c4009
  options, parse_opt, args_doc, doc, NULL, more_help
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Wrapper functions with error checking for standard functions.  */
Packit 6c4009
#include <programs/xmalloc.h>
Packit 6c4009
Packit 6c4009
/* Prototypes for local functions.  */
Packit 6c4009
static void error_print (void);
Packit 6c4009
static struct catalog *read_input_file (struct catalog *current,
Packit 6c4009
					const char *fname);
Packit 6c4009
static void write_out (struct catalog *result, const char *output_name,
Packit 6c4009
		       const char *header_name);
Packit 6c4009
static struct set_list *find_set (struct catalog *current, int number);
Packit 6c4009
static void normalize_line (const char *fname, size_t line, iconv_t cd,
Packit 6c4009
			    wchar_t *string, wchar_t quote_char,
Packit 6c4009
			    wchar_t escape_char);
Packit 6c4009
static void read_old (struct catalog *catalog, const char *file_name);
Packit 6c4009
static int open_conversion (const char *codesetp, iconv_t *cd_towcp,
Packit 6c4009
			    iconv_t *cd_tombp, wchar_t *escape_charp);
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (int argc, char *argv[])
Packit 6c4009
{
Packit 6c4009
  struct catalog *result;
Packit 6c4009
  int remaining;
Packit 6c4009
Packit 6c4009
  /* Set program name for messages.  */
Packit 6c4009
  error_print_progname = error_print;
Packit 6c4009
Packit 6c4009
  /* Set locale via LC_ALL.  */
Packit 6c4009
  setlocale (LC_ALL, "");
Packit 6c4009
Packit 6c4009
  /* Set the text message domain.  */
Packit 6c4009
  textdomain (PACKAGE);
Packit 6c4009
Packit 6c4009
  /* Initialize local variables.  */
Packit 6c4009
  result = NULL;
Packit 6c4009
Packit 6c4009
  /* Parse and process arguments.  */
Packit 6c4009
  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 6c4009
Packit 6c4009
  /* Determine output file.  */
Packit 6c4009
  if (output_name == NULL)
Packit 6c4009
    output_name = remaining < argc ? argv[remaining++] : "-";
Packit 6c4009
Packit 6c4009
  /* Process all input files.  */
Packit 6c4009
  setlocale (LC_CTYPE, "C");
Packit 6c4009
  if (remaining < argc)
Packit 6c4009
    do
Packit 6c4009
      result = read_input_file (result, argv[remaining]);
Packit 6c4009
    while (++remaining < argc);
Packit 6c4009
  else
Packit 6c4009
    result = read_input_file (NULL, "-");
Packit 6c4009
Packit 6c4009
  /* Write out the result.  */
Packit 6c4009
  if (result != NULL)
Packit 6c4009
    write_out (result, output_name, header_name);
Packit 6c4009
Packit 6c4009
  return error_message_count != 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Handle program arguments.  */
Packit 6c4009
static error_t
Packit 6c4009
parse_opt (int key, char *arg, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case 'H':
Packit 6c4009
      header_name = arg;
Packit 6c4009
      break;
Packit 6c4009
    case OPT_NEW:
Packit 6c4009
      force_new = 1;
Packit 6c4009
      break;
Packit 6c4009
    case 'o':
Packit 6c4009
      output_name = arg;
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      return ARGP_ERR_UNKNOWN;
Packit 6c4009
    }
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static char *
Packit 6c4009
more_help (int key, const char *text, void *input)
Packit 6c4009
{
Packit 6c4009
  char *tp = NULL;
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case ARGP_KEY_HELP_EXTRA:
Packit 6c4009
      /* We print some extra information.  */
Packit 6c4009
      if (asprintf (&tp, gettext ("\
Packit 6c4009
For bug reporting instructions, please see:\n\
Packit 6c4009
%s.\n"), REPORT_BUGS_TO) < 0)
Packit 6c4009
	return NULL;
Packit 6c4009
      return tp;
Packit 6c4009
    default:
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  return (char *) text;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Print the version information.  */
Packit 6c4009
static void
Packit 6c4009
print_version (FILE *stream, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  fprintf (stream, "gencat %s%s\n", PKGVERSION, VERSION);
Packit 6c4009
  fprintf (stream, gettext ("\
Packit 6c4009
Copyright (C) %s Free Software Foundation, Inc.\n\
Packit 6c4009
This is free software; see the source for copying conditions.  There is NO\n\
Packit 6c4009
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Packit 6c4009
"), "2018");
Packit 6c4009
  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The address of this function will be assigned to the hook in the
Packit 6c4009
   error functions.  */
Packit 6c4009
static void
Packit 6c4009
error_print (void)
Packit 6c4009
{
Packit 6c4009
  /* We don't want the program name to be printed in messages.  Emacs'
Packit 6c4009
     compile.el does not like this.  */
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static struct catalog *
Packit 6c4009
read_input_file (struct catalog *current, const char *fname)
Packit 6c4009
{
Packit 6c4009
  FILE *fp;
Packit 6c4009
  char *buf;
Packit 6c4009
  size_t len;
Packit 6c4009
  size_t line_number;
Packit 6c4009
  wchar_t *wbuf;
Packit 6c4009
  size_t wbufsize;
Packit 6c4009
  iconv_t cd_towc = (iconv_t) -1;
Packit 6c4009
  iconv_t cd_tomb = (iconv_t) -1;
Packit 6c4009
  wchar_t escape_char = L'\\';
Packit 6c4009
  char *codeset = NULL;
Packit 6c4009
Packit 6c4009
  if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
Packit 6c4009
    {
Packit 6c4009
      fp = stdin;
Packit 6c4009
      fname = gettext ("*standard input*");
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    fp = fopen (fname, "r");
Packit 6c4009
  if (fp == NULL)
Packit 6c4009
    {
Packit 6c4009
      error (0, errno, gettext ("cannot open input file `%s'"), fname);
Packit 6c4009
      return current;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we haven't seen anything yet, allocate result structure.  */
Packit 6c4009
  if (current == NULL)
Packit 6c4009
    {
Packit 6c4009
      current = (struct catalog *) xcalloc (1, sizeof (*current));
Packit 6c4009
Packit 6c4009
#define obstack_chunk_alloc malloc
Packit 6c4009
#define obstack_chunk_free free
Packit 6c4009
      obstack_init (&current->mem_pool);
Packit 6c4009
Packit 6c4009
      current->current_set = find_set (current, NL_SETD);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  buf = NULL;
Packit 6c4009
  len = 0;
Packit 6c4009
  line_number = 0;
Packit 6c4009
Packit 6c4009
  wbufsize = 1024;
Packit 6c4009
  wbuf = (wchar_t *) xmalloc (wbufsize);
Packit 6c4009
Packit 6c4009
  while (!feof (fp))
Packit 6c4009
    {
Packit 6c4009
      int continued;
Packit 6c4009
      int used;
Packit 6c4009
      size_t start_line = line_number + 1;
Packit 6c4009
      char *this_line;
Packit 6c4009
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  int act_len;
Packit 6c4009
Packit 6c4009
	  act_len = getline (&buf, &len, fp);
Packit 6c4009
	  if (act_len <= 0)
Packit 6c4009
	    break;
Packit 6c4009
	  ++line_number;
Packit 6c4009
Packit 6c4009
	  /* It the line continued?  */
Packit 6c4009
	  continued = 0;
Packit 6c4009
	  if (buf[act_len - 1] == '\n')
Packit 6c4009
	    {
Packit 6c4009
	      --act_len;
Packit 6c4009
Packit 6c4009
	      /* There might be more than one backslash at the end of
Packit 6c4009
		 the line.  Only if there is an odd number of them is
Packit 6c4009
		 the line continued.  */
Packit 6c4009
	      if (act_len > 0 && buf[act_len - 1] == '\\')
Packit 6c4009
		{
Packit 6c4009
		  int temp_act_len = act_len;
Packit 6c4009
Packit 6c4009
		  do
Packit 6c4009
		    {
Packit 6c4009
		      --temp_act_len;
Packit 6c4009
		      continued = !continued;
Packit 6c4009
		    }
Packit 6c4009
		  while (temp_act_len > 0 && buf[temp_act_len - 1] == '\\');
Packit 6c4009
Packit 6c4009
		  if (continued)
Packit 6c4009
		    --act_len;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Append to currently selected line.  */
Packit 6c4009
	  obstack_grow (&current->mem_pool, buf, act_len);
Packit 6c4009
	}
Packit 6c4009
      while (continued);
Packit 6c4009
Packit 6c4009
      obstack_1grow (&current->mem_pool, '\0');
Packit 6c4009
      this_line = (char *) obstack_finish (&current->mem_pool);
Packit 6c4009
Packit 6c4009
      used = 0;
Packit 6c4009
      if (this_line[0] == '$')
Packit 6c4009
	{
Packit 6c4009
	  if (isblank (this_line[1]))
Packit 6c4009
	    {
Packit 6c4009
	      int cnt = 1;
Packit 6c4009
	      while (isblank (this_line[cnt]))
Packit 6c4009
		++cnt;
Packit 6c4009
	      if (strncmp (&this_line[cnt], "codeset=", 8) != 0)
Packit 6c4009
		/* This is a comment line. Do nothing.  */;
Packit 6c4009
	      else if (codeset != NULL)
Packit 6c4009
		/* Ignore multiple codeset. */;
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  int start = cnt + 8;
Packit 6c4009
		  cnt = start;
Packit 6c4009
		  while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
Packit 6c4009
		    ++cnt;
Packit 6c4009
		  if (cnt != start)
Packit 6c4009
		    {
Packit 6c4009
		      int len = cnt - start;
Packit 6c4009
		      codeset = xmalloc (len + 1);
Packit 6c4009
		      *((char *) mempcpy (codeset, &this_line[start], len))
Packit 6c4009
			= '\0';
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else if (strncmp (&this_line[1], "set", 3) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      int cnt = sizeof ("set");
Packit 6c4009
	      int set_number;
Packit 6c4009
	      const char *symbol = NULL;
Packit 6c4009
	      while (isspace (this_line[cnt]))
Packit 6c4009
		++cnt;
Packit 6c4009
Packit 6c4009
	      if (isdigit (this_line[cnt]))
Packit 6c4009
		{
Packit 6c4009
		  set_number = atol (&this_line[cnt]);
Packit 6c4009
Packit 6c4009
		  /* If the given number for the character set is
Packit 6c4009
		     higher than any we used for symbolic set names
Packit 6c4009
		     avoid clashing by using only higher numbers for
Packit 6c4009
		     the following symbolic definitions.  */
Packit 6c4009
		  if (set_number > current->last_set)
Packit 6c4009
		    current->last_set = set_number;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  /* See whether it is a reasonable identifier.  */
Packit 6c4009
		  int start = cnt;
Packit 6c4009
		  while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
Packit 6c4009
		    ++cnt;
Packit 6c4009
Packit 6c4009
		  if (cnt == start)
Packit 6c4009
		    {
Packit 6c4009
		      /* No correct character found.  */
Packit 6c4009
		      error_at_line (0, 0, fname, start_line,
Packit 6c4009
				     gettext ("illegal set number"));
Packit 6c4009
		      set_number = 0;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      /* We have found seomthing that looks like a
Packit 6c4009
			 correct identifier.  */
Packit 6c4009
		      struct set_list *runp;
Packit 6c4009
Packit 6c4009
		      this_line[cnt] = '\0';
Packit 6c4009
		      used = 1;
Packit 6c4009
		      symbol = &this_line[start];
Packit 6c4009
Packit 6c4009
		      /* Test whether the identifier was already used.  */
Packit 6c4009
		      runp = current->all_sets;
Packit 6c4009
		      while (runp != 0)
Packit 6c4009
			if (runp->symbol != NULL
Packit 6c4009
			    && strcmp (runp->symbol, symbol) == 0)
Packit 6c4009
			  break;
Packit 6c4009
			else
Packit 6c4009
			  runp = runp->next;
Packit 6c4009
Packit 6c4009
		      if (runp != NULL)
Packit 6c4009
			{
Packit 6c4009
			  /* We cannot allow duplicate identifiers for
Packit 6c4009
			     message sets.  */
Packit 6c4009
			  error_at_line (0, 0, fname, start_line,
Packit 6c4009
					 gettext ("duplicate set definition"));
Packit 6c4009
			  error_at_line (0, 0, runp->fname, runp->line,
Packit 6c4009
					 gettext ("\
Packit 6c4009
this is the first definition"));
Packit 6c4009
			  set_number = 0;
Packit 6c4009
			}
Packit 6c4009
		      else
Packit 6c4009
			/* Allocate next free message set for identifier.  */
Packit 6c4009
			set_number = ++current->last_set;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      if (set_number != 0)
Packit 6c4009
		{
Packit 6c4009
		  /* We found a legal set number.  */
Packit 6c4009
		  current->current_set = find_set (current, set_number);
Packit 6c4009
		  if (symbol != NULL)
Packit 6c4009
		      used = 1;
Packit 6c4009
		  current->current_set->symbol = symbol;
Packit 6c4009
		  current->current_set->fname = fname;
Packit 6c4009
		  current->current_set->line = start_line;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else if (strncmp (&this_line[1], "delset", 6) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      int cnt = sizeof ("delset");
Packit 6c4009
	      while (isspace (this_line[cnt]))
Packit 6c4009
		++cnt;
Packit 6c4009
Packit 6c4009
	      if (isdigit (this_line[cnt]))
Packit 6c4009
		{
Packit 6c4009
		  size_t set_number = atol (&this_line[cnt]);
Packit 6c4009
		  struct set_list *set;
Packit 6c4009
Packit 6c4009
		  /* Mark the message set with the given number as
Packit 6c4009
		     deleted.  */
Packit 6c4009
		  set = find_set (current, set_number);
Packit 6c4009
		  set->deleted = 1;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  /* See whether it is a reasonable identifier.  */
Packit 6c4009
		  int start = cnt;
Packit 6c4009
		  while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
Packit 6c4009
		    ++cnt;
Packit 6c4009
Packit 6c4009
		  if (cnt == start)
Packit 6c4009
		    error_at_line (0, 0, fname, start_line,
Packit 6c4009
				   gettext ("illegal set number"));
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      const char *symbol;
Packit 6c4009
		      struct set_list *runp;
Packit 6c4009
Packit 6c4009
		      this_line[cnt] = '\0';
Packit 6c4009
		      used = 1;
Packit 6c4009
		      symbol = &this_line[start];
Packit 6c4009
Packit 6c4009
		      /* We have a symbolic set name.  This name must
Packit 6c4009
			 appear somewhere else in the catalogs read so
Packit 6c4009
			 far.  */
Packit 6c4009
		      for (runp = current->all_sets; runp != NULL;
Packit 6c4009
			   runp = runp->next)
Packit 6c4009
			{
Packit 6c4009
			  if (strcmp (runp->symbol, symbol) == 0)
Packit 6c4009
			    {
Packit 6c4009
			      runp->deleted = 1;
Packit 6c4009
			      break;
Packit 6c4009
			    }
Packit 6c4009
			}
Packit 6c4009
		      if (runp == NULL)
Packit 6c4009
			/* Name does not exist before.  */
Packit 6c4009
			error_at_line (0, 0, fname, start_line,
Packit 6c4009
				       gettext ("unknown set `%s'"), symbol);
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else if (strncmp (&this_line[1], "quote", 5) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      char buf[2];
Packit 6c4009
	      char *bufptr;
Packit 6c4009
	      size_t buflen;
Packit 6c4009
	      char *wbufptr;
Packit 6c4009
	      size_t wbuflen;
Packit 6c4009
	      int cnt;
Packit 6c4009
Packit 6c4009
	      cnt = sizeof ("quote");
Packit 6c4009
	      while (isspace (this_line[cnt]))
Packit 6c4009
		++cnt;
Packit 6c4009
Packit 6c4009
	      /* We need the conversion.  */
Packit 6c4009
	      if (cd_towc == (iconv_t) -1
Packit 6c4009
		  && open_conversion (codeset, &cd_towc, &cd_tomb,
Packit 6c4009
				      &escape_char) != 0)
Packit 6c4009
		/* Something is wrong.  */
Packit 6c4009
		goto out;
Packit 6c4009
Packit 6c4009
	      /* Yes, the quote char can be '\0'; this means no quote
Packit 6c4009
		 char.  The function using the information works on
Packit 6c4009
		 wide characters so we have to convert it here.  */
Packit 6c4009
	      buf[0] = this_line[cnt];
Packit 6c4009
	      buf[1] = '\0';
Packit 6c4009
	      bufptr = buf;
Packit 6c4009
	      buflen = 2;
Packit 6c4009
Packit 6c4009
	      wbufptr = (char *) wbuf;
Packit 6c4009
	      wbuflen = wbufsize;
Packit 6c4009
Packit 6c4009
	      /* Flush the state.  */
Packit 6c4009
	      iconv (cd_towc, NULL, NULL, NULL, NULL);
Packit 6c4009
Packit 6c4009
	      iconv (cd_towc, &bufptr, &buflen, &wbufptr, &wbuflen);
Packit 6c4009
	      if (buflen != 0 || (wchar_t *) wbufptr != &wbuf[2])
Packit 6c4009
		error_at_line (0, 0, fname, start_line,
Packit 6c4009
			       gettext ("invalid quote character"));
Packit 6c4009
	      else
Packit 6c4009
		/* Use the converted wide character.  */
Packit 6c4009
		current->quote_char = wbuf[0];
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      int cnt;
Packit 6c4009
	      cnt = 2;
Packit 6c4009
	      while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
Packit 6c4009
		++cnt;
Packit 6c4009
	      this_line[cnt] = '\0';
Packit 6c4009
	      error_at_line (0, 0, fname, start_line,
Packit 6c4009
			     gettext ("unknown directive `%s': line ignored"),
Packit 6c4009
			     &this_line[1]);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else if (isalnum (this_line[0]) || this_line[0] == '_')
Packit 6c4009
	{
Packit 6c4009
	  const char *ident = this_line;
Packit 6c4009
	  char *line = this_line;
Packit 6c4009
	  int message_number;
Packit 6c4009
Packit 6c4009
	  do
Packit 6c4009
	    ++line;
Packit 6c4009
	  while (line[0] != '\0' && !isspace (line[0]));
Packit 6c4009
	  if (line[0] != '\0')
Packit 6c4009
	    *line++ = '\0';	/* Terminate the identifier.  */
Packit 6c4009
Packit 6c4009
	  /* Now we found the beginning of the message itself.  */
Packit 6c4009
Packit 6c4009
	  if (isdigit (ident[0]))
Packit 6c4009
	    {
Packit 6c4009
	      struct message_list *runp;
Packit 6c4009
	      struct message_list *lastp;
Packit 6c4009
Packit 6c4009
	      message_number = atoi (ident);
Packit 6c4009
Packit 6c4009
	      /* Find location to insert the new message.  */
Packit 6c4009
	      runp = current->current_set->messages;
Packit 6c4009
	      lastp = NULL;
Packit 6c4009
	      while (runp != NULL)
Packit 6c4009
		if (runp->number == message_number)
Packit 6c4009
		  break;
Packit 6c4009
		else
Packit 6c4009
		  {
Packit 6c4009
		    lastp = runp;
Packit 6c4009
		    runp = runp->next;
Packit 6c4009
		  }
Packit 6c4009
	      if (runp != NULL)
Packit 6c4009
		{
Packit 6c4009
		  /* Oh, oh.  There is already a message with this
Packit 6c4009
		     number in the message set.  */
Packit 6c4009
		  if (runp->symbol == NULL)
Packit 6c4009
		    {
Packit 6c4009
		      /* The existing message had its number specified
Packit 6c4009
			 by the user.  Fatal collision type uh, oh.  */
Packit 6c4009
		      error_at_line (0, 0, fname, start_line,
Packit 6c4009
				     gettext ("duplicated message number"));
Packit 6c4009
		      error_at_line (0, 0, runp->fname, runp->line,
Packit 6c4009
				     gettext ("this is the first definition"));
Packit 6c4009
		      message_number = 0;
Packit 6c4009
		    }
Packit 6c4009
		  else
Packit 6c4009
		    {
Packit 6c4009
		      /* Collision was with number auto-assigned to a
Packit 6c4009
			 symbolic.  Change existing symbolic number
Packit 6c4009
			 and move to end the list (if not already there).  */
Packit 6c4009
		      runp->number = ++current->current_set->last_message;
Packit 6c4009
Packit 6c4009
		      if (runp->next != NULL)
Packit 6c4009
			{
Packit 6c4009
			  struct message_list *endp;
Packit 6c4009
Packit 6c4009
			  if (lastp == NULL)
Packit 6c4009
			    current->current_set->messages=runp->next;
Packit 6c4009
			  else
Packit 6c4009
			    lastp->next=runp->next;
Packit 6c4009
Packit 6c4009
			  endp = runp->next;
Packit 6c4009
			  while (endp->next != NULL)
Packit 6c4009
			    endp = endp->next;
Packit 6c4009
Packit 6c4009
			  endp->next = runp;
Packit 6c4009
			  runp->next = NULL;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
	      ident = NULL;	/* We don't have a symbol.  */
Packit 6c4009
Packit 6c4009
	      if (message_number != 0
Packit 6c4009
		  && message_number > current->current_set->last_message)
Packit 6c4009
		current->current_set->last_message = message_number;
Packit 6c4009
	    }
Packit 6c4009
	  else if (ident[0] != '\0')
Packit 6c4009
	    {
Packit 6c4009
	      struct message_list *runp;
Packit 6c4009
Packit 6c4009
	      /* Test whether the symbolic name was not used for
Packit 6c4009
		 another message in this message set.  */
Packit 6c4009
	      runp = current->current_set->messages;
Packit 6c4009
	      while (runp != NULL)
Packit 6c4009
		if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
Packit 6c4009
		  break;
Packit 6c4009
		else
Packit 6c4009
		  runp = runp->next;
Packit 6c4009
	      if (runp != NULL)
Packit 6c4009
		{
Packit 6c4009
		  /* The name is already used.  */
Packit 6c4009
		  error_at_line (0, 0, fname, start_line, gettext ("\
Packit 6c4009
duplicated message identifier"));
Packit 6c4009
		  error_at_line (0, 0, runp->fname, runp->line,
Packit 6c4009
				 gettext ("this is the first definition"));
Packit 6c4009
		  message_number = 0;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		/* Give the message the next unused number.  */
Packit 6c4009
		message_number = ++current->current_set->last_message;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    message_number = 0;
Packit 6c4009
Packit 6c4009
	  if (message_number != 0)
Packit 6c4009
	    {
Packit 6c4009
	      char *inbuf;
Packit 6c4009
	      size_t inlen;
Packit 6c4009
	      char *outbuf;
Packit 6c4009
	      size_t outlen;
Packit 6c4009
	      struct message_list *newp;
Packit 6c4009
	      size_t line_len = strlen (line) + 1;
Packit 6c4009
	      size_t ident_len = 0;
Packit 6c4009
Packit 6c4009
	      /* We need the conversion.  */
Packit 6c4009
	      if (cd_towc == (iconv_t) -1
Packit 6c4009
		  && open_conversion (codeset, &cd_towc, &cd_tomb,
Packit 6c4009
				      &escape_char) != 0)
Packit 6c4009
		/* Something is wrong.  */
Packit 6c4009
		goto out;
Packit 6c4009
Packit 6c4009
	      /* Convert to a wide character string.  We have to
Packit 6c4009
		 interpret escape sequences which will be impossible
Packit 6c4009
		 without doing the conversion if the codeset of the
Packit 6c4009
		 message is stateful.  */
Packit 6c4009
	      while (1)
Packit 6c4009
		{
Packit 6c4009
		  inbuf = line;
Packit 6c4009
		  inlen = line_len;
Packit 6c4009
		  outbuf = (char *) wbuf;
Packit 6c4009
		  outlen = wbufsize;
Packit 6c4009
Packit 6c4009
		  /* Flush the state.  */
Packit 6c4009
		  iconv (cd_towc, NULL, NULL, NULL, NULL);
Packit 6c4009
Packit 6c4009
		  iconv (cd_towc, &inbuf, &inlen, &outbuf, &outlen);
Packit 6c4009
		  if (inlen == 0)
Packit 6c4009
		    {
Packit 6c4009
		      /* The string is converted.  */
Packit 6c4009
		      assert (outlen < wbufsize);
Packit 6c4009
		      assert (wbuf[(wbufsize - outlen) / sizeof (wchar_t) - 1]
Packit 6c4009
			      == L'\0');
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  if (outlen != 0)
Packit 6c4009
		    {
Packit 6c4009
		      /* Something is wrong with this string, we ignore it.  */
Packit 6c4009
		      error_at_line (0, 0, fname, start_line, gettext ("\
Packit 6c4009
invalid character: message ignored"));
Packit 6c4009
		      goto ignore;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  /* The output buffer is too small.  */
Packit 6c4009
		  wbufsize *= 2;
Packit 6c4009
		  wbuf = (wchar_t *) xrealloc (wbuf, wbufsize);
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* Strip quote characters, change escape sequences into
Packit 6c4009
		 correct characters etc.  */
Packit 6c4009
	      normalize_line (fname, start_line, cd_towc, wbuf,
Packit 6c4009
			      current->quote_char, escape_char);
Packit 6c4009
Packit 6c4009
	      if (ident)
Packit 6c4009
		ident_len = line - this_line;
Packit 6c4009
Packit 6c4009
	      /* Now the string is free of escape sequences.  Convert it
Packit 6c4009
		 back into a multibyte character string.  First free the
Packit 6c4009
		 memory allocated for the original string.  */
Packit 6c4009
	      obstack_free (&current->mem_pool, this_line);
Packit 6c4009
Packit 6c4009
	      used = 1;	/* Yes, we use the line.  */
Packit 6c4009
Packit 6c4009
	      /* Now fill in the new string.  It should never happen that
Packit 6c4009
		 the replaced string is longer than the original.  */
Packit 6c4009
	      inbuf = (char *) wbuf;
Packit 6c4009
	      inlen = (wcslen (wbuf) + 1) * sizeof (wchar_t);
Packit 6c4009
Packit 6c4009
	      outlen = obstack_room (&current->mem_pool);
Packit 6c4009
	      obstack_blank (&current->mem_pool, outlen);
Packit 6c4009
	      this_line = (char *) obstack_base (&current->mem_pool);
Packit 6c4009
	      outbuf = this_line + ident_len;
Packit 6c4009
	      outlen -= ident_len;
Packit 6c4009
Packit 6c4009
	      /* Flush the state.  */
Packit 6c4009
	      iconv (cd_tomb, NULL, NULL, NULL, NULL);
Packit 6c4009
Packit 6c4009
	      iconv (cd_tomb, &inbuf, &inlen, &outbuf, &outlen);
Packit 6c4009
	      if (inlen != 0)
Packit 6c4009
		{
Packit 6c4009
		  error_at_line (0, 0, fname, start_line,
Packit 6c4009
				 gettext ("invalid line"));
Packit 6c4009
		  goto ignore;
Packit 6c4009
		}
Packit 6c4009
	      assert (outbuf[-1] == '\0');
Packit 6c4009
Packit 6c4009
	      /* Free the memory in the obstack we don't use.  */
Packit 6c4009
	      obstack_blank (&current->mem_pool, -(int) outlen);
Packit 6c4009
	      line = obstack_finish (&current->mem_pool);
Packit 6c4009
Packit 6c4009
	      newp = (struct message_list *) xmalloc (sizeof (*newp));
Packit 6c4009
	      newp->number = message_number;
Packit 6c4009
	      newp->message = line + ident_len;
Packit 6c4009
	      /* Remember symbolic name; is NULL if no is given.  */
Packit 6c4009
	      newp->symbol = ident ? line : NULL;
Packit 6c4009
	      /* Remember where we found the character.  */
Packit 6c4009
	      newp->fname = fname;
Packit 6c4009
	      newp->line = start_line;
Packit 6c4009
Packit 6c4009
	      /* Find place to insert to message.  We keep them in a
Packit 6c4009
		 sorted single linked list.  */
Packit 6c4009
	      if (current->current_set->messages == NULL
Packit 6c4009
		  || current->current_set->messages->number > message_number)
Packit 6c4009
		{
Packit 6c4009
		  newp->next = current->current_set->messages;
Packit 6c4009
		  current->current_set->messages = newp;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  struct message_list *runp;
Packit 6c4009
		  runp = current->current_set->messages;
Packit 6c4009
		  while (runp->next != NULL)
Packit 6c4009
		    if (runp->next->number > message_number)
Packit 6c4009
		      break;
Packit 6c4009
		    else
Packit 6c4009
		      runp = runp->next;
Packit 6c4009
		  newp->next = runp->next;
Packit 6c4009
		  runp->next = newp;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  ++current->total_messages;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  size_t cnt;
Packit 6c4009
Packit 6c4009
	  cnt = 0;
Packit 6c4009
	  /* See whether we have any non-white space character in this
Packit 6c4009
	     line.  */
Packit 6c4009
	  while (this_line[cnt] != '\0' && isspace (this_line[cnt]))
Packit 6c4009
	    ++cnt;
Packit 6c4009
Packit 6c4009
	  if (this_line[cnt] != '\0')
Packit 6c4009
	    /* Yes, some unknown characters found.  */
Packit 6c4009
	    error_at_line (0, 0, fname, start_line,
Packit 6c4009
			   gettext ("malformed line ignored"));
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
    ignore:
Packit 6c4009
      /* We can save the memory for the line if it was not used.  */
Packit 6c4009
      if (!used)
Packit 6c4009
	obstack_free (&current->mem_pool, this_line);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Close the conversion modules.  */
Packit 6c4009
  iconv_close (cd_towc);
Packit 6c4009
  iconv_close (cd_tomb);
Packit 6c4009
  free (codeset);
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  free (wbuf);
Packit 6c4009
Packit 6c4009
  if (fp != stdin)
Packit 6c4009
    fclose (fp);
Packit 6c4009
  return current;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
write_out (struct catalog *catalog, const char *output_name,
Packit 6c4009
	   const char *header_name)
Packit 6c4009
{
Packit 6c4009
  /* Computing the "optimal" size.  */
Packit 6c4009
  struct set_list *set_run;
Packit 6c4009
  size_t best_total, best_size, best_depth;
Packit 6c4009
  size_t act_size, act_depth;
Packit 6c4009
  struct catalog_obj obj;
Packit 6c4009
  struct obstack string_pool;
Packit 6c4009
  const char *strings;
Packit 6c4009
  size_t strings_size;
Packit 6c4009
  uint32_t *array1, *array2;
Packit 6c4009
  size_t cnt;
Packit 6c4009
  int fd;
Packit 6c4009
Packit 6c4009
  /* If not otherwise told try to read file with existing
Packit 6c4009
     translations.  */
Packit 6c4009
  if (!force_new)
Packit 6c4009
    read_old (catalog, output_name);
Packit 6c4009
Packit 6c4009
  /* Initialize best_size with a very high value.  */
Packit 6c4009
  best_total = best_size = best_depth = UINT_MAX;
Packit 6c4009
Packit 6c4009
  /* We need some start size for testing.  Let's start with
Packit 6c4009
     TOTAL_MESSAGES / 5, which theoretically provides a mean depth of
Packit 6c4009
     5.  */
Packit 6c4009
  act_size = 1 + catalog->total_messages / 5;
Packit 6c4009
Packit 6c4009
  /* We determine the size of a hash table here.  Because the message
Packit 6c4009
     numbers can be chosen arbitrary by the programmer we cannot use
Packit 6c4009
     the simple method of accessing the array using the message
Packit 6c4009
     number.  The algorithm is based on the trivial hash function
Packit 6c4009
     NUMBER % TABLE_SIZE, where collisions are stored in a second
Packit 6c4009
     dimension up to TABLE_DEPTH.  We here compute TABLE_SIZE so that
Packit 6c4009
     the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal.  */
Packit 6c4009
  while (act_size <= best_total)
Packit 6c4009
    {
Packit 6c4009
      size_t deep[act_size];
Packit 6c4009
Packit 6c4009
      act_depth = 1;
Packit 6c4009
      memset (deep, '\0', act_size * sizeof (size_t));
Packit 6c4009
      set_run = catalog->all_sets;
Packit 6c4009
      while (set_run != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct message_list *message_run;
Packit 6c4009
Packit 6c4009
	  message_run = set_run->messages;
Packit 6c4009
	  while (message_run != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      size_t idx = (message_run->number * set_run->number) % act_size;
Packit 6c4009
Packit 6c4009
	      ++deep[idx];
Packit 6c4009
	      if (deep[idx] > act_depth)
Packit 6c4009
		{
Packit 6c4009
		  act_depth = deep[idx];
Packit 6c4009
		  if (act_depth * act_size > best_total)
Packit 6c4009
		    break;
Packit 6c4009
		}
Packit 6c4009
	      message_run = message_run->next;
Packit 6c4009
	    }
Packit 6c4009
	  set_run = set_run->next;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (act_depth * act_size <= best_total)
Packit 6c4009
	{
Packit 6c4009
	  /* We have found a better solution.  */
Packit 6c4009
	  best_total = act_depth * act_size;
Packit 6c4009
	  best_size = act_size;
Packit 6c4009
	  best_depth = act_depth;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      ++act_size;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* let's be prepared for an empty message file.  */
Packit 6c4009
  if (best_size == UINT_MAX)
Packit 6c4009
    {
Packit 6c4009
      best_size = 1;
Packit 6c4009
      best_depth = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* OK, now we have the size we will use.  Fill in the header, build
Packit 6c4009
     the table and the second one with swapped byte order.  */
Packit 6c4009
  obj.magic = CATGETS_MAGIC;
Packit 6c4009
  obj.plane_size = best_size;
Packit 6c4009
  obj.plane_depth = best_depth;
Packit 6c4009
Packit 6c4009
  /* Allocate room for all needed arrays.  */
Packit 6c4009
  array1 =
Packit 6c4009
    (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
  memset (array1, '\0', best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
  array2
Packit 6c4009
    = (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
  obstack_init (&string_pool);
Packit 6c4009
Packit 6c4009
  set_run = catalog->all_sets;
Packit 6c4009
  while (set_run != NULL)
Packit 6c4009
    {
Packit 6c4009
      struct message_list *message_run;
Packit 6c4009
Packit 6c4009
      message_run = set_run->messages;
Packit 6c4009
      while (message_run != NULL)
Packit 6c4009
	{
Packit 6c4009
	  size_t idx = (((message_run->number * set_run->number) % best_size)
Packit 6c4009
			* 3);
Packit 6c4009
	  /* Determine collision depth.  */
Packit 6c4009
	  while (array1[idx] != 0)
Packit 6c4009
	    idx += best_size * 3;
Packit 6c4009
Packit 6c4009
	  /* Store set number, message number and pointer into string
Packit 6c4009
	     space, relative to the first string.  */
Packit 6c4009
	  array1[idx + 0] = set_run->number;
Packit 6c4009
	  array1[idx + 1] = message_run->number;
Packit 6c4009
	  array1[idx + 2] = obstack_object_size (&string_pool);
Packit 6c4009
Packit 6c4009
	  /* Add current string to the continuous space containing all
Packit 6c4009
	     strings.  */
Packit 6c4009
	  obstack_grow0 (&string_pool, message_run->message,
Packit 6c4009
			 strlen (message_run->message));
Packit 6c4009
Packit 6c4009
	  message_run = message_run->next;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      set_run = set_run->next;
Packit 6c4009
    }
Packit 6c4009
  strings_size = obstack_object_size (&string_pool);
Packit 6c4009
  strings = obstack_finish (&string_pool);
Packit 6c4009
Packit 6c4009
  /* Compute ARRAY2 by changing the byte order.  */
Packit 6c4009
  for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt)
Packit 6c4009
    array2[cnt] = SWAPU32 (array1[cnt]);
Packit 6c4009
Packit 6c4009
  /* Now we can write out the whole data.  */
Packit 6c4009
  if (strcmp (output_name, "-") == 0
Packit 6c4009
      || strcmp (output_name, "/dev/stdout") == 0)
Packit 6c4009
    fd = STDOUT_FILENO;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      fd = creat (output_name, 0666);
Packit 6c4009
      if (fd < 0)
Packit 6c4009
	error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"),
Packit 6c4009
	       output_name);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Write out header.  */
Packit 6c4009
  write (fd, &obj, sizeof (obj));
Packit 6c4009
Packit 6c4009
  /* We always write out the little endian version of the index
Packit 6c4009
     arrays.  */
Packit 6c4009
#if __BYTE_ORDER == __LITTLE_ENDIAN
Packit 6c4009
  write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
  write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
#elif __BYTE_ORDER == __BIG_ENDIAN
Packit 6c4009
  write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
  write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
Packit 6c4009
#else
Packit 6c4009
# error Cannot handle __BYTE_ORDER byte order
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Finally write the strings.  */
Packit 6c4009
  write (fd, strings, strings_size);
Packit 6c4009
Packit 6c4009
  if (fd != STDOUT_FILENO)
Packit 6c4009
    close (fd);
Packit 6c4009
Packit 6c4009
  /* If requested now write out the header file.  */
Packit 6c4009
  if (header_name != NULL)
Packit 6c4009
    {
Packit 6c4009
      int first = 1;
Packit 6c4009
      FILE *fp;
Packit 6c4009
Packit 6c4009
      /* Open output file.  "-" or "/dev/stdout" means write to
Packit 6c4009
	 standard output.  */
Packit 6c4009
      if (strcmp (header_name, "-") == 0
Packit 6c4009
	  || strcmp (header_name, "/dev/stdout") == 0)
Packit 6c4009
	fp = stdout;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  fp = fopen (header_name, "w");
Packit 6c4009
	  if (fp == NULL)
Packit 6c4009
	    error (EXIT_FAILURE, errno,
Packit 6c4009
		   gettext ("cannot open output file `%s'"), header_name);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Iterate over all sets and all messages.  */
Packit 6c4009
      set_run = catalog->all_sets;
Packit 6c4009
      while (set_run != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct message_list *message_run;
Packit 6c4009
Packit 6c4009
	  /* If the current message set has a symbolic name write this
Packit 6c4009
	     out first.  */
Packit 6c4009
	  if (set_run->symbol != NULL)
Packit 6c4009
	    fprintf (fp, "%s#define %sSet %#x\t/* %s:%Zu */\n",
Packit 6c4009
		     first ? "" : "\n", set_run->symbol, set_run->number - 1,
Packit 6c4009
		     set_run->fname, set_run->line);
Packit 6c4009
	  first = 0;
Packit 6c4009
Packit 6c4009
	  message_run = set_run->messages;
Packit 6c4009
	  while (message_run != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* If the current message has a symbolic name write
Packit 6c4009
		 #define out.  But we have to take care for the set
Packit 6c4009
		 not having a symbolic name.  */
Packit 6c4009
	      if (message_run->symbol != NULL)
Packit 6c4009
		{
Packit 6c4009
		  if (set_run->symbol == NULL)
Packit 6c4009
		    fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu */\n",
Packit 6c4009
			     set_run->number, message_run->symbol,
Packit 6c4009
			     message_run->number, message_run->fname,
Packit 6c4009
			     message_run->line);
Packit 6c4009
		  else
Packit 6c4009
		    fprintf (fp, "#define %s%s %#x\t/* %s:%Zu */\n",
Packit 6c4009
			     set_run->symbol, message_run->symbol,
Packit 6c4009
			     message_run->number, message_run->fname,
Packit 6c4009
			     message_run->line);
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      message_run = message_run->next;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  set_run = set_run->next;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (fp != stdout)
Packit 6c4009
	fclose (fp);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static struct set_list *
Packit 6c4009
find_set (struct catalog *current, int number)
Packit 6c4009
{
Packit 6c4009
  struct set_list *result = current->all_sets;
Packit 6c4009
Packit 6c4009
  /* We must avoid set number 0 because a set of this number signals
Packit 6c4009
     in the tables that the entry is not occupied.  */
Packit 6c4009
  ++number;
Packit 6c4009
Packit 6c4009
  while (result != NULL)
Packit 6c4009
    if (result->number == number)
Packit 6c4009
      return result;
Packit 6c4009
    else
Packit 6c4009
      result = result->next;
Packit 6c4009
Packit 6c4009
  /* Prepare new message set.  */
Packit 6c4009
  result = (struct set_list *) xcalloc (1, sizeof (*result));
Packit 6c4009
  result->number = number;
Packit 6c4009
  result->next = current->all_sets;
Packit 6c4009
  current->all_sets = result;
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Normalize given string *in*place* by processing escape sequences
Packit 6c4009
   and quote characters.  */
Packit 6c4009
static void
Packit 6c4009
normalize_line (const char *fname, size_t line, iconv_t cd, wchar_t *string,
Packit 6c4009
		wchar_t quote_char, wchar_t escape_char)
Packit 6c4009
{
Packit 6c4009
  int is_quoted;
Packit 6c4009
  wchar_t *rp = string;
Packit 6c4009
  wchar_t *wp = string;
Packit 6c4009
Packit 6c4009
  if (quote_char != L'\0' && *rp == quote_char)
Packit 6c4009
    {
Packit 6c4009
      is_quoted = 1;
Packit 6c4009
      ++rp;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    is_quoted = 0;
Packit 6c4009
Packit 6c4009
  while (*rp != L'\0')
Packit 6c4009
    if (*rp == quote_char)
Packit 6c4009
      /* We simply end the string when we find the first time an
Packit 6c4009
	 not-escaped quote character.  */
Packit 6c4009
	break;
Packit 6c4009
    else if (*rp == escape_char)
Packit 6c4009
      {
Packit 6c4009
	++rp;
Packit 6c4009
	if (quote_char != L'\0' && *rp == quote_char)
Packit 6c4009
	  /* This is an extension to XPG.  */
Packit 6c4009
	  *wp++ = *rp++;
Packit 6c4009
	else
Packit 6c4009
	  /* Recognize escape sequences.  */
Packit 6c4009
	  switch (*rp)
Packit 6c4009
	    {
Packit 6c4009
	    case L'n':
Packit 6c4009
	      *wp++ = L'\n';
Packit 6c4009
	      ++rp;
Packit 6c4009
	      break;
Packit 6c4009
	    case L't':
Packit 6c4009
	      *wp++ = L'\t';
Packit 6c4009
	      ++rp;
Packit 6c4009
	      break;
Packit 6c4009
	    case L'v':
Packit 6c4009
	      *wp++ = L'\v';
Packit 6c4009
	      ++rp;
Packit 6c4009
	      break;
Packit 6c4009
	    case L'b':
Packit 6c4009
	      *wp++ = L'\b';
Packit 6c4009
	      ++rp;
Packit 6c4009
	      break;
Packit 6c4009
	    case L'r':
Packit 6c4009
	      *wp++ = L'\r';
Packit 6c4009
	      ++rp;
Packit 6c4009
	      break;
Packit 6c4009
	    case L'f':
Packit 6c4009
	      *wp++ = L'\f';
Packit 6c4009
	      ++rp;
Packit 6c4009
	      break;
Packit 6c4009
	    case L'0' ... L'7':
Packit 6c4009
	      {
Packit 6c4009
		int number;
Packit 6c4009
		char cbuf[2];
Packit 6c4009
		char *cbufptr;
Packit 6c4009
		size_t cbufin;
Packit 6c4009
		wchar_t wcbuf[2];
Packit 6c4009
		char *wcbufptr;
Packit 6c4009
		size_t wcbufin;
Packit 6c4009
Packit 6c4009
		number = *rp++ - L'0';
Packit 6c4009
		while (number <= (255 / 8) && *rp >= L'0' && *rp <= L'7')
Packit 6c4009
		  {
Packit 6c4009
		    number *= 8;
Packit 6c4009
		    number += *rp++ - L'0';
Packit 6c4009
		  }
Packit 6c4009
Packit 6c4009
		cbuf[0] = (char) number;
Packit 6c4009
		cbuf[1] = '\0';
Packit 6c4009
		cbufptr = cbuf;
Packit 6c4009
		cbufin = 2;
Packit 6c4009
Packit 6c4009
		wcbufptr = (char *) wcbuf;
Packit 6c4009
		wcbufin = sizeof (wcbuf);
Packit 6c4009
Packit 6c4009
		/* Flush the state.  */
Packit 6c4009
		iconv (cd, NULL, NULL, NULL, NULL);
Packit 6c4009
Packit 6c4009
		iconv (cd, &cbufptr, &cbufin, &wcbufptr, &wcbufin);
Packit 6c4009
		if (cbufptr != &cbuf[2] || (wchar_t *) wcbufptr != &wcbuf[2])
Packit 6c4009
		  error_at_line (0, 0, fname, line,
Packit 6c4009
				 gettext ("invalid escape sequence"));
Packit 6c4009
		else
Packit 6c4009
		  *wp++ = wcbuf[0];
Packit 6c4009
	      }
Packit 6c4009
	      break;
Packit 6c4009
	    default:
Packit 6c4009
	      if (*rp == escape_char)
Packit 6c4009
		{
Packit 6c4009
		  *wp++ = escape_char;
Packit 6c4009
		  ++rp;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		/* Simply ignore the backslash character.  */;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
      }
Packit 6c4009
    else
Packit 6c4009
      *wp++ = *rp++;
Packit 6c4009
Packit 6c4009
  /* If we saw a quote character at the beginning we expect another
Packit 6c4009
     one at the end.  */
Packit 6c4009
  if (is_quoted && *rp != quote_char)
Packit 6c4009
    error_at_line (0, 0, fname, line, gettext ("unterminated message"));
Packit 6c4009
Packit 6c4009
  /* Terminate string.  */
Packit 6c4009
  *wp = L'\0';
Packit 6c4009
  return;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
read_old (struct catalog *catalog, const char *file_name)
Packit 6c4009
{
Packit 6c4009
  struct catalog_info old_cat_obj;
Packit 6c4009
  struct set_list *set = NULL;
Packit 6c4009
  int last_set = -1;
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit 6c4009
  /* Try to open catalog, but don't look through the NLSPATH.  */
Packit 6c4009
  if (__open_catalog (file_name, NULL, NULL, &old_cat_obj) != 0)
Packit 6c4009
    {
Packit 6c4009
      if (errno == ENOENT)
Packit 6c4009
	/* No problem, the catalog simply does not exist.  */
Packit 6c4009
	return;
Packit 6c4009
      else
Packit 6c4009
	error (EXIT_FAILURE, errno,
Packit 6c4009
	       gettext ("while opening old catalog file"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* OK, we have the catalog loaded.  Now read all messages and merge
Packit 6c4009
     them.  When set and message number clash for any message the new
Packit 6c4009
     one is used.  If the new one is empty it indicates that the
Packit 6c4009
     message should be deleted.  */
Packit 6c4009
  for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
Packit 6c4009
    {
Packit 6c4009
      struct message_list *message, *last;
Packit 6c4009
Packit 6c4009
      if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0)
Packit 6c4009
	/* No message in this slot.  */
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (uint32_t) last_set)
Packit 6c4009
	{
Packit 6c4009
	  last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1;
Packit 6c4009
	  set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      last = NULL;
Packit 6c4009
      message = set->messages;
Packit 6c4009
      while (message != NULL)
Packit 6c4009
	{
Packit 6c4009
	  if ((uint32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
Packit 6c4009
	    break;
Packit 6c4009
	  last = message;
Packit 6c4009
	  message = message->next;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (message == NULL
Packit 6c4009
	  || (uint32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
Packit 6c4009
	{
Packit 6c4009
	  /* We have found a message which is not yet in the catalog.
Packit 6c4009
	     Insert it at the right position.  */
Packit 6c4009
	  struct message_list *newp;
Packit 6c4009
Packit 6c4009
	  newp = (struct message_list *) xmalloc (sizeof(*newp));
Packit 6c4009
	  newp->number = old_cat_obj.name_ptr[cnt * 3 + 1];
Packit 6c4009
	  newp->message =
Packit 6c4009
	    &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]];
Packit 6c4009
	  newp->fname = NULL;
Packit 6c4009
	  newp->line = 0;
Packit 6c4009
	  newp->symbol = NULL;
Packit 6c4009
	  newp->next = message;
Packit 6c4009
Packit 6c4009
	  if (last == NULL)
Packit 6c4009
	    set->messages = newp;
Packit 6c4009
	  else
Packit 6c4009
	    last->next = newp;
Packit 6c4009
Packit 6c4009
	  ++catalog->total_messages;
Packit 6c4009
	}
Packit 6c4009
      else if (*message->message == '\0')
Packit 6c4009
	{
Packit 6c4009
	  /* The new empty message has overridden the old one thus
Packit 6c4009
	     "deleting" it as required.  Now remove the empty remains. */
Packit 6c4009
	  if (last == NULL)
Packit 6c4009
	    set->messages = message->next;
Packit 6c4009
	  else
Packit 6c4009
	    last->next = message->next;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
open_conversion (const char *codeset, iconv_t *cd_towcp, iconv_t *cd_tombp,
Packit 6c4009
		 wchar_t *escape_charp)
Packit 6c4009
{
Packit 6c4009
  char buf[2];
Packit 6c4009
  char *bufptr;
Packit 6c4009
  size_t bufsize;
Packit 6c4009
  wchar_t wbuf[2];
Packit 6c4009
  char *wbufptr;
Packit 6c4009
  size_t wbufsize;
Packit 6c4009
Packit 6c4009
  /* If the input file does not specify the codeset use the locale's.  */
Packit 6c4009
  if (codeset == NULL)
Packit 6c4009
    {
Packit 6c4009
      setlocale (LC_ALL, "");
Packit 6c4009
      codeset = nl_langinfo (CODESET);
Packit 6c4009
      setlocale (LC_ALL, "C");
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Get the conversion modules.  */
Packit 6c4009
  *cd_towcp = iconv_open ("WCHAR_T", codeset);
Packit 6c4009
  *cd_tombp = iconv_open (codeset, "WCHAR_T");
Packit 6c4009
  if (*cd_towcp == (iconv_t) -1 || *cd_tombp == (iconv_t) -1)
Packit 6c4009
    {
Packit 6c4009
      error (0, 0, gettext ("conversion modules not available"));
Packit 6c4009
      if (*cd_towcp != (iconv_t) -1)
Packit 6c4009
	iconv_close (*cd_towcp);
Packit 6c4009
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* One special case for historical reasons is the backslash
Packit 6c4009
     character.  In some codesets the byte value 0x5c is not mapped to
Packit 6c4009
     U005c in Unicode.  These charsets then don't have a backslash
Packit 6c4009
     character at all.  Therefore we have to live with whatever the
Packit 6c4009
     codeset provides and recognize, instead of the U005c, the character
Packit 6c4009
     the byte value 0x5c is mapped to.  */
Packit 6c4009
  buf[0] = '\\';
Packit 6c4009
  buf[1] = '\0';
Packit 6c4009
  bufptr = buf;
Packit 6c4009
  bufsize = 2;
Packit 6c4009
Packit 6c4009
  wbufptr = (char *) wbuf;
Packit 6c4009
  wbufsize = sizeof (wbuf);
Packit 6c4009
Packit 6c4009
  iconv (*cd_towcp, &bufptr, &bufsize, &wbufptr, &wbufsize);
Packit 6c4009
  if (bufsize != 0 || wbufsize != 0)
Packit 6c4009
    {
Packit 6c4009
      /* Something went wrong, we couldn't convert the byte 0x5c.  Go
Packit 6c4009
	 on with using U005c.  */
Packit 6c4009
      error (0, 0, gettext ("cannot determine escape character"));
Packit 6c4009
      *escape_charp = L'\\';
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    *escape_charp = wbuf[0];
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}