Blame iconv/iconvconfig.c

Packit 6c4009
/* Generate fastloading iconv module configuration files.
Packit 6c4009
   Copyright (C) 2000-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>, 2000.
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
#include <argp.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <mcheck.h>
Packit 6c4009
#include <search.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdio_ext.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/cdefs.h>
Packit 6c4009
#include <sys/uio.h>
Packit 6c4009
Packit 6c4009
#include "iconvconfig.h"
Packit 6c4009
Packit 6c4009
/* Get libc version number.  */
Packit 6c4009
#include "../version.h"
Packit 6c4009
Packit 6c4009
#define PACKAGE _libc_intl_domainname
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The hashing function we use.  */
Packit 6c4009
#include "../intl/hash-string.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Types used.  */
Packit 6c4009
struct module
Packit 6c4009
{
Packit 6c4009
  char *fromname;
Packit 6c4009
  struct Strent *fromname_strent;
Packit 6c4009
  char *filename;
Packit 6c4009
  struct Strent *filename_strent;
Packit 6c4009
  const char *directory;
Packit 6c4009
  struct Strent *directory_strent;
Packit 6c4009
  struct module *next;
Packit 6c4009
  int cost;
Packit 6c4009
  struct Strent *toname_strent;
Packit 6c4009
  char toname[0];
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct alias
Packit 6c4009
{
Packit 6c4009
  char *fromname;
Packit 6c4009
  struct Strent *froment;
Packit 6c4009
  struct module *module;
Packit 6c4009
  struct Strent *toent;
Packit 6c4009
  char toname[0];
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct name
Packit 6c4009
{
Packit 6c4009
  const char *name;
Packit 6c4009
  struct Strent *strent;
Packit 6c4009
  int module_idx;
Packit 6c4009
  uint32_t hashval;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct name_info
Packit 6c4009
{
Packit 6c4009
  const char *canonical_name;
Packit 6c4009
  struct Strent *canonical_strent;
Packit 6c4009
Packit 6c4009
  struct module *from_internal;
Packit 6c4009
  struct module *to_internal;
Packit 6c4009
Packit 6c4009
  struct other_conv_list
Packit 6c4009
  {
Packit 6c4009
    int dest_idx;
Packit 6c4009
    struct other_conv
Packit 6c4009
    {
Packit 6c4009
      gidx_t module_idx;
Packit 6c4009
      struct module *module;
Packit 6c4009
      struct other_conv *next;
Packit 6c4009
    } other_conv;
Packit 6c4009
    struct other_conv_list *next;
Packit 6c4009
  } *other_conv_list;
Packit 6c4009
};
Packit 6c4009
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
/* Short description of program.  */
Packit 6c4009
static const char doc[] = N_("\
Packit 6c4009
Create fastloading iconv module configuration file.");
Packit 6c4009
Packit 6c4009
/* Strings for arguments in help texts.  */
Packit 6c4009
static const char args_doc[] = N_("[DIR...]");
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
/* Definitions of arguments for argp functions.  */
Packit 6c4009
#define OPT_PREFIX 300
Packit 6c4009
#define OPT_NOSTDLIB 301
Packit 6c4009
static const struct argp_option options[] =
Packit 6c4009
{
Packit 6c4009
  { "prefix", OPT_PREFIX, N_("PATH"), 0,
Packit 6c4009
    N_("Prefix used for all file accesses") },
Packit 6c4009
  { "output", 'o', N_("FILE"), 0, N_("\
Packit 6c4009
Put output in FILE instead of installed location\
Packit 6c4009
 (--prefix does not apply to FILE)") },
Packit 6c4009
  { "nostdlib", OPT_NOSTDLIB, NULL, 0,
Packit 6c4009
    N_("Do not search standard directories, only those on the command line") },
Packit 6c4009
  { NULL, 0, NULL, 0, NULL }
Packit 6c4009
};
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
/* The function doing the actual work.  */
Packit 6c4009
static int handle_dir (const char *dir);
Packit 6c4009
Packit 6c4009
/* Add all known builtin conversions and aliases.  */
Packit 6c4009
static void add_builtins (void);
Packit 6c4009
Packit 6c4009
/* Create list of all aliases without circular aliases.  */
Packit 6c4009
static void get_aliases (void);
Packit 6c4009
Packit 6c4009
/* Create list of all modules.  */
Packit 6c4009
static void get_modules (void);
Packit 6c4009
Packit 6c4009
/* Get list of all the names and thereby indexing them.  */
Packit 6c4009
static void generate_name_list (void);
Packit 6c4009
Packit 6c4009
/* Collect information about all the names.  */
Packit 6c4009
static void generate_name_info (void);
Packit 6c4009
Packit 6c4009
/* Write the output file.  */
Packit 6c4009
static int write_output (void);
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Prefix to be used for all file accesses.  */
Packit 6c4009
static const char *prefix = "";
Packit 6c4009
/* Its length.  */
Packit 6c4009
static size_t prefix_len;
Packit 6c4009
Packit 6c4009
/* Directory to place output file in.  */
Packit 6c4009
static const char *output_file;
Packit 6c4009
/* Its length.  */
Packit 6c4009
static size_t output_file_len;
Packit 6c4009
Packit 6c4009
/* If true, omit the GCONV_PATH directories and require some arguments.  */
Packit 6c4009
static bool nostdlib;
Packit 6c4009
Packit 6c4009
/* Search tree of the modules we know.  */
Packit 6c4009
static void *modules;
Packit 6c4009
Packit 6c4009
/* Search tree of the aliases we know.  */
Packit 6c4009
static void *aliases;
Packit 6c4009
Packit 6c4009
/* Search tree for name to index mapping.  */
Packit 6c4009
static void *names;
Packit 6c4009
Packit 6c4009
/* Number of names we know about.  */
Packit 6c4009
static int nnames;
Packit 6c4009
Packit 6c4009
/* List of all aliases.  */
Packit 6c4009
static struct alias **alias_list;
Packit 6c4009
static size_t nalias_list;
Packit 6c4009
static size_t nalias_list_max;
Packit 6c4009
Packit 6c4009
/* List of all modules.  */
Packit 6c4009
static struct module **module_list;
Packit 6c4009
static size_t nmodule_list;
Packit 6c4009
static size_t nmodule_list_max;
Packit 6c4009
Packit 6c4009
/* Names and information about them.  */
Packit 6c4009
static struct name_info *name_info;
Packit 6c4009
static size_t nname_info;
Packit 6c4009
Packit 6c4009
/* Number of translations not from or to INTERNAL.  */
Packit 6c4009
static size_t nextra_modules;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Names and aliases for the builtin transformations.  */
Packit 6c4009
static struct
Packit 6c4009
{
Packit 6c4009
  const char *from;
Packit 6c4009
  const char *to;
Packit 6c4009
} builtin_alias[] =
Packit 6c4009
  {
Packit 6c4009
#define BUILTIN_ALIAS(alias, real) \
Packit 6c4009
    { .from = alias, .to = real },
Packit 6c4009
#define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
Packit 6c4009
			       MinF, MaxF, MinT, MaxT)
Packit 6c4009
#include <gconv_builtin.h>
Packit 6c4009
  };
Packit 6c4009
#undef BUILTIN_ALIAS
Packit 6c4009
#undef BUILTIN_TRANSFORMATION
Packit 6c4009
#define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
Packit 6c4009
Packit 6c4009
static struct
Packit 6c4009
{
Packit 6c4009
  const char *from;
Packit 6c4009
  const char *to;
Packit 6c4009
  const char *module;
Packit 6c4009
  int cost;
Packit 6c4009
} builtin_trans[] =
Packit 6c4009
  {
Packit 6c4009
#define BUILTIN_ALIAS(alias, real)
Packit 6c4009
#define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
Packit 6c4009
			       MinF, MaxF, MinT, MaxT) \
Packit 6c4009
    { .from = From, .to = To, .module = Name, .cost = Cost },
Packit 6c4009
#include <gconv_builtin.h>
Packit 6c4009
  };
Packit 6c4009
#undef BUILTIN_ALIAS
Packit 6c4009
#undef BUILTIN_TRANSFORMATION
Packit 6c4009
#define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Filename extension for the modules.  */
Packit 6c4009
#ifndef MODULE_EXT
Packit 6c4009
# define MODULE_EXT ".so"
Packit 6c4009
#endif
Packit 6c4009
static const char gconv_module_ext[] = MODULE_EXT;
Packit 6c4009
Packit 6c4009
Packit 6c4009
#include <programs/xmalloc.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* C string table handling.  */
Packit 6c4009
struct Strtab;
Packit 6c4009
struct Strent;
Packit 6c4009
Packit 6c4009
/* Create new C string table object in memory.  */
Packit 6c4009
extern struct Strtab *strtabinit (void);
Packit 6c4009
Packit 6c4009
/* Free resources allocated for C string table ST.  */
Packit 6c4009
extern void strtabfree (struct Strtab *st);
Packit 6c4009
Packit 6c4009
/* Add string STR (length LEN is != 0) to C string table ST.  */
Packit 6c4009
extern struct Strent *strtabadd (struct Strtab *st, const char *str,
Packit 6c4009
				 size_t len);
Packit 6c4009
Packit 6c4009
/* Finalize string table ST and store size in *SIZE and return a pointer.  */
Packit 6c4009
extern void *strtabfinalize (struct Strtab *st, size_t *size);
Packit 6c4009
Packit 6c4009
/* Get offset in string table for string associated with SE.  */
Packit 6c4009
extern size_t strtaboffset (struct Strent *se);
Packit 6c4009
Packit 6c4009
/* String table we construct.  */
Packit 6c4009
static struct Strtab *strtab;
Packit 6c4009
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (int argc, char *argv[])
Packit 6c4009
{
Packit 6c4009
  int remaining;
Packit 6c4009
  int status = 0;
Packit 6c4009
Packit 6c4009
  /* Enable memory use testing.  */
Packit 6c4009
  /* mcheck_pedantic (NULL); */
Packit 6c4009
  mtrace ();
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 (_libc_intl_domainname);
Packit 6c4009
Packit 6c4009
  /* Parse and process arguments.  */
Packit 6c4009
  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 6c4009
Packit 6c4009
  if (nostdlib && remaining == argc)
Packit 6c4009
    error (2, 0, _("Directory arguments required when using --nostdlib"));
Packit 6c4009
Packit 6c4009
  /* Initialize the string table.  */
Packit 6c4009
  strtab = strtabinit ();
Packit 6c4009
Packit 6c4009
  /* Handle all directories mentioned.  */
Packit 6c4009
  while (remaining < argc)
Packit 6c4009
    status |= handle_dir (argv[remaining++]);
Packit 6c4009
Packit 6c4009
  if (! nostdlib)
Packit 6c4009
    {
Packit 6c4009
      /* In any case also handle the standard directory.  */
Packit 6c4009
      char *path = strdupa (GCONV_PATH), *tp = strsep (&path, ":");
Packit 6c4009
      while (tp != NULL)
Packit 6c4009
	{
Packit 6c4009
	  status |= handle_dir (tp);
Packit 6c4009
Packit 6c4009
	  tp = strsep (&path, ":");
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Add the builtin transformations and aliases without overwriting
Packit 6c4009
     anything.  */
Packit 6c4009
  add_builtins ();
Packit 6c4009
Packit 6c4009
  /* Store aliases in an array.  */
Packit 6c4009
  get_aliases ();
Packit 6c4009
Packit 6c4009
  /* Get list of all modules.  */
Packit 6c4009
  get_modules ();
Packit 6c4009
Packit 6c4009
  /* Generate list of all the names we know to handle in some way.  */
Packit 6c4009
  generate_name_list ();
Packit 6c4009
Packit 6c4009
  /* Now we know all the names we will handle, collect information
Packit 6c4009
     about them.  */
Packit 6c4009
  generate_name_info ();
Packit 6c4009
Packit 6c4009
  /* Write the output file, but only if we haven't seen any error.  */
Packit 6c4009
  if (status == 0)
Packit 6c4009
    status = write_output ();
Packit 6c4009
  else
Packit 6c4009
    error (1, 0, _("no output file produced because warnings were issued"));
Packit 6c4009
Packit 6c4009
  return status;
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 OPT_PREFIX:
Packit 6c4009
      prefix = arg;
Packit 6c4009
      prefix_len = strlen (prefix);
Packit 6c4009
      break;
Packit 6c4009
    case 'o':
Packit 6c4009
      output_file = arg;
Packit 6c4009
      output_file_len = strlen (output_file);
Packit 6c4009
      break;
Packit 6c4009
    case OPT_NOSTDLIB:
Packit 6c4009
      nostdlib = true;
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
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, "iconvconfig %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
static int
Packit 6c4009
alias_compare (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct alias *a1 = (const struct alias *) p1;
Packit 6c4009
  const struct alias *a2 = (const struct alias *) p2;
Packit 6c4009
Packit 6c4009
  return strcmp (a1->fromname, a2->fromname);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
new_alias (const char *fromname, size_t fromlen, const char *toname,
Packit 6c4009
	   size_t tolen)
Packit 6c4009
{
Packit 6c4009
  struct alias *newp;
Packit 6c4009
  void **inserted;
Packit 6c4009
Packit 6c4009
  newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
Packit 6c4009
Packit 6c4009
  newp->fromname = mempcpy (newp->toname, toname, tolen);
Packit 6c4009
  memcpy (newp->fromname, fromname, fromlen);
Packit 6c4009
  newp->module = NULL;
Packit 6c4009
Packit 6c4009
  inserted = (void **) tsearch (newp, &aliases, alias_compare);
Packit 6c4009
  if (inserted == NULL)
Packit 6c4009
    error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
Packit 6c4009
  if (*inserted != newp)
Packit 6c4009
    /* Something went wrong, free this entry.  */
Packit 6c4009
    free (newp);
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      newp->froment = strtabadd (strtab, newp->fromname, fromlen);
Packit 6c4009
      newp->toent = strtabadd (strtab, newp->toname, tolen);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Add new alias.  */
Packit 6c4009
static void
Packit 6c4009
add_alias (char *rp)
Packit 6c4009
{
Packit 6c4009
  /* We now expect two more string.  The strings are normalized
Packit 6c4009
     (converted to UPPER case) and strored in the alias database.  */
Packit 6c4009
  char *from;
Packit 6c4009
  char *to;
Packit 6c4009
  char *wp;
Packit 6c4009
Packit 6c4009
  while (isspace (*rp))
Packit 6c4009
    ++rp;
Packit 6c4009
  from = wp = rp;
Packit 6c4009
  while (*rp != '\0' && !isspace (*rp))
Packit 6c4009
    *wp++ = toupper (*rp++);
Packit 6c4009
  if (*rp == '\0')
Packit 6c4009
    /* There is no `to' string on the line.  Ignore it.  */
Packit 6c4009
    return;
Packit 6c4009
  *wp++ = '\0';
Packit 6c4009
  to = ++rp;
Packit 6c4009
  while (isspace (*rp))
Packit 6c4009
    ++rp;
Packit 6c4009
  while (*rp != '\0' && !isspace (*rp))
Packit 6c4009
    *wp++ = toupper (*rp++);
Packit 6c4009
  if (to == wp)
Packit 6c4009
    /* No `to' string, ignore the line.  */
Packit 6c4009
    return;
Packit 6c4009
  *wp++ = '\0';
Packit 6c4009
Packit 6c4009
  assert (strlen (from) + 1 == (size_t) (to - from));
Packit 6c4009
  assert (strlen (to) + 1 == (size_t) (wp - to));
Packit 6c4009
Packit 6c4009
  new_alias (from, to - from, to, wp - to);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
append_alias (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  if (value != leaf && value != postorder)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  if (nalias_list_max == nalias_list)
Packit 6c4009
    {
Packit 6c4009
      nalias_list_max += 50;
Packit 6c4009
      alias_list = (struct alias **) xrealloc (alias_list,
Packit 6c4009
					       (nalias_list_max
Packit 6c4009
						* sizeof (struct alias *)));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  alias_list[nalias_list++] = *(struct alias **) nodep;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
get_aliases (void)
Packit 6c4009
{
Packit 6c4009
  twalk (aliases, append_alias);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
module_compare (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct module *m1 = (const struct module *) p1;
Packit 6c4009
  const struct module *m2 = (const struct module *) p2;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  result = strcmp (m1->fromname, m2->fromname);
Packit 6c4009
  if (result == 0)
Packit 6c4009
    result = strcmp (m1->toname, m2->toname);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Create new module record.  */
Packit 6c4009
static void
Packit 6c4009
new_module (const char *fromname, size_t fromlen, const char *toname,
Packit 6c4009
	    size_t tolen, const char *directory,
Packit 6c4009
	    const char *filename, size_t filelen, int cost, size_t need_ext)
Packit 6c4009
{
Packit 6c4009
  struct module *new_module;
Packit 6c4009
  size_t dirlen = strlen (directory) + 1;
Packit 6c4009
  char *tmp;
Packit 6c4009
  void **inserted;
Packit 6c4009
Packit 6c4009
  new_module = (struct module *) xmalloc (sizeof (struct module)
Packit 6c4009
					  + fromlen + tolen + filelen
Packit 6c4009
					  + need_ext);
Packit 6c4009
Packit 6c4009
  new_module->fromname = mempcpy (new_module->toname, toname, tolen);
Packit 6c4009
Packit 6c4009
  new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
Packit 6c4009
Packit 6c4009
  new_module->cost = cost;
Packit 6c4009
  new_module->next = NULL;
Packit 6c4009
Packit 6c4009
  tmp = mempcpy (new_module->filename, filename, filelen);
Packit 6c4009
  if (need_ext)
Packit 6c4009
    {
Packit 6c4009
      memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
Packit 6c4009
      filelen += need_ext;
Packit 6c4009
    }
Packit 6c4009
  new_module->directory = directory;
Packit 6c4009
Packit 6c4009
  /* Now insert the new module data structure in our search tree.  */
Packit 6c4009
  inserted = (void **) tsearch (new_module, &modules, module_compare);
Packit 6c4009
  if (inserted == NULL)
Packit 6c4009
    error (EXIT_FAILURE, errno, "while inserting in search tree");
Packit 6c4009
  if (*inserted != new_module)
Packit 6c4009
    free (new_module);
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
Packit 6c4009
					       fromlen);
Packit 6c4009
      new_module->toname_strent = strtabadd (strtab, new_module->toname,
Packit 6c4009
					     tolen);
Packit 6c4009
      new_module->filename_strent = strtabadd (strtab, new_module->filename,
Packit 6c4009
					       filelen);
Packit 6c4009
      new_module->directory_strent = strtabadd (strtab, directory, dirlen);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Add new module.  */
Packit 6c4009
static void
Packit 6c4009
add_module (char *rp, const char *directory)
Packit 6c4009
{
Packit 6c4009
  /* We expect now
Packit 6c4009
     1. `from' name
Packit 6c4009
     2. `to' name
Packit 6c4009
     3. filename of the module
Packit 6c4009
     4. an optional cost value
Packit 6c4009
  */
Packit 6c4009
  char *from;
Packit 6c4009
  char *to;
Packit 6c4009
  char *module;
Packit 6c4009
  char *wp;
Packit 6c4009
  int need_ext;
Packit 6c4009
  int cost;
Packit 6c4009
Packit 6c4009
  while (isspace (*rp))
Packit 6c4009
    ++rp;
Packit 6c4009
  from = rp;
Packit 6c4009
  while (*rp != '\0' && !isspace (*rp))
Packit 6c4009
    {
Packit 6c4009
      *rp = toupper (*rp);
Packit 6c4009
      ++rp;
Packit 6c4009
    }
Packit 6c4009
  if (*rp == '\0')
Packit 6c4009
    return;
Packit 6c4009
  *rp++ = '\0';
Packit 6c4009
  to = wp = rp;
Packit 6c4009
  while (isspace (*rp))
Packit 6c4009
    ++rp;
Packit 6c4009
  while (*rp != '\0' && !isspace (*rp))
Packit 6c4009
    *wp++ = toupper (*rp++);
Packit 6c4009
  if (*rp == '\0')
Packit 6c4009
    return;
Packit 6c4009
  *wp++ = '\0';
Packit 6c4009
  do
Packit 6c4009
    ++rp;
Packit 6c4009
  while (isspace (*rp));
Packit 6c4009
  module = wp;
Packit 6c4009
  while (*rp != '\0' && !isspace (*rp))
Packit 6c4009
    *wp++ = *rp++;
Packit 6c4009
  if (*rp == '\0')
Packit 6c4009
    {
Packit 6c4009
      /* There is no cost, use one by default.  */
Packit 6c4009
      *wp++ = '\0';
Packit 6c4009
      cost = 1;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* There might be a cost value.  */
Packit 6c4009
      char *endp;
Packit 6c4009
Packit 6c4009
      *wp++ = '\0';
Packit 6c4009
      cost = strtol (rp, &endp, 10);
Packit 6c4009
      if (rp == endp || cost < 1)
Packit 6c4009
	/* No useful information.  */
Packit 6c4009
	cost = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (module[0] == '\0')
Packit 6c4009
    /* No module name given.  */
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  /* See whether we must add the ending.  */
Packit 6c4009
  need_ext = 0;
Packit 6c4009
  if ((size_t) (wp - module) < sizeof (gconv_module_ext)
Packit 6c4009
      || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
Packit 6c4009
		 sizeof (gconv_module_ext)) != 0)
Packit 6c4009
    /* We must add the module extension.  */
Packit 6c4009
    need_ext = sizeof (gconv_module_ext) - 1;
Packit 6c4009
Packit 6c4009
  assert (strlen (from) + 1 == (size_t) (to - from));
Packit 6c4009
  assert (strlen (to) + 1 == (size_t) (module - to));
Packit 6c4009
  assert (strlen (module) + 1 == (size_t) (wp - module));
Packit 6c4009
Packit 6c4009
  new_module (from, to - from, to, module - to, directory, module, wp - module,
Packit 6c4009
	      cost, need_ext);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Read the config file and add the data for this directory to that.  */
Packit 6c4009
static int
Packit 6c4009
handle_dir (const char *dir)
Packit 6c4009
{
Packit 6c4009
  char *cp;
Packit 6c4009
  FILE *fp;
Packit 6c4009
  char *line = NULL;
Packit 6c4009
  size_t linelen = 0;
Packit 6c4009
  size_t dirlen = strlen (dir);
Packit 6c4009
Packit 6c4009
  if (dir[dirlen - 1] != '/')
Packit 6c4009
    {
Packit 6c4009
      char *newp = (char *) xmalloc (dirlen + 2);
Packit 6c4009
      dir = memcpy (newp, dir, dirlen);
Packit 6c4009
      newp[dirlen++] = '/';
Packit 6c4009
      newp[dirlen] = '\0';
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  char infile[prefix_len + dirlen + sizeof "gconv-modules"];
Packit 6c4009
  cp = infile;
Packit 6c4009
  if (dir[0] == '/')
Packit 6c4009
    cp = mempcpy (cp, prefix, prefix_len);
Packit 6c4009
  strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
Packit 6c4009
Packit 6c4009
  fp = fopen (infile, "r");
Packit 6c4009
  if (fp == NULL)
Packit 6c4009
    {
Packit 6c4009
      error (0, errno, "cannot open `%s'", infile);
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* No threads present.  */
Packit 6c4009
  __fsetlocking (fp, FSETLOCKING_BYCALLER);
Packit 6c4009
Packit 6c4009
  while (!feof_unlocked (fp))
Packit 6c4009
    {
Packit 6c4009
      char *rp, *endp, *word;
Packit 6c4009
      ssize_t n = __getdelim (&line, &linelen, '\n', fp);
Packit 6c4009
Packit 6c4009
      if (n < 0)
Packit 6c4009
	/* An error occurred.  */
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      rp = line;
Packit 6c4009
      /* Terminate the line (excluding comments or newline) with a NUL
Packit 6c4009
	 byte to simplify the following code.  */
Packit 6c4009
      endp = strchr (rp, '#');
Packit 6c4009
      if (endp != NULL)
Packit 6c4009
	*endp = '\0';
Packit 6c4009
      else
Packit 6c4009
	if (rp[n - 1] == '\n')
Packit 6c4009
	  rp[n - 1] = '\0';
Packit 6c4009
Packit 6c4009
      while (isspace (*rp))
Packit 6c4009
	++rp;
Packit 6c4009
Packit 6c4009
      /* If this is an empty line go on with the next one.  */
Packit 6c4009
      if (rp == endp)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      word = rp;
Packit 6c4009
      while (*rp != '\0' && !isspace (*rp))
Packit 6c4009
	++rp;
Packit 6c4009
Packit 6c4009
      if (rp - word == sizeof ("alias") - 1
Packit 6c4009
	  && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
Packit 6c4009
	add_alias (rp);
Packit 6c4009
      else if (rp - word == sizeof ("module") - 1
Packit 6c4009
	       && memcmp (word, "module", sizeof ("module") - 1) == 0)
Packit 6c4009
	add_module (rp, dir);
Packit 6c4009
      /* else */
Packit 6c4009
	/* Otherwise ignore the line.  */
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  free (line);
Packit 6c4009
Packit 6c4009
  fclose (fp);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
append_module (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  struct module *mo;
Packit 6c4009
Packit 6c4009
  if (value != leaf && value != postorder)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  mo = *(struct module **) nodep;
Packit 6c4009
Packit 6c4009
  if (nmodule_list > 0
Packit 6c4009
      && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Same name.  */
Packit 6c4009
      mo->next = module_list[nmodule_list - 1];
Packit 6c4009
      module_list[nmodule_list - 1] = mo;
Packit 6c4009
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (nmodule_list_max == nmodule_list)
Packit 6c4009
    {
Packit 6c4009
      nmodule_list_max += 50;
Packit 6c4009
      module_list = (struct module **) xrealloc (module_list,
Packit 6c4009
						 (nmodule_list_max
Packit 6c4009
						  * sizeof (struct module *)));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  module_list[nmodule_list++] = mo;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
get_modules (void)
Packit 6c4009
{
Packit 6c4009
  twalk (modules, append_module);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
add_builtins (void)
Packit 6c4009
{
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit 6c4009
  /* Add all aliases.  */
Packit 6c4009
  for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
Packit 6c4009
    new_alias (builtin_alias[cnt].from,
Packit 6c4009
	       strlen (builtin_alias[cnt].from) + 1,
Packit 6c4009
	       builtin_alias[cnt].to,
Packit 6c4009
	       strlen (builtin_alias[cnt].to) + 1);
Packit 6c4009
Packit 6c4009
  /* add the builtin transformations.  */
Packit 6c4009
  for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
Packit 6c4009
    new_module (builtin_trans[cnt].from,
Packit 6c4009
		strlen (builtin_trans[cnt].from) + 1,
Packit 6c4009
		builtin_trans[cnt].to,
Packit 6c4009
		strlen (builtin_trans[cnt].to) + 1,
Packit 6c4009
		"", builtin_trans[cnt].module,
Packit 6c4009
		strlen (builtin_trans[cnt].module) + 1,
Packit 6c4009
		builtin_trans[cnt].cost, 0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
name_compare (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct name *n1 = (const struct name *) p1;
Packit 6c4009
  const struct name *n2 = (const struct name *) p2;
Packit 6c4009
Packit 6c4009
  return strcmp (n1->name, n2->name);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static struct name *
Packit 6c4009
new_name (const char *str, struct Strent *strent)
Packit 6c4009
{
Packit 6c4009
  struct name *newp = (struct name *) xmalloc (sizeof (struct name));
Packit 6c4009
Packit 6c4009
  newp->name = str;
Packit 6c4009
  newp->strent = strent;
Packit 6c4009
  newp->module_idx = -1;
Packit 6c4009
  newp->hashval = __hash_string (str);
Packit 6c4009
Packit 6c4009
  ++nnames;
Packit 6c4009
Packit 6c4009
  return newp;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
generate_name_list (void)
Packit 6c4009
{
Packit 6c4009
  size_t i;
Packit 6c4009
Packit 6c4009
  /* A name we always need.  */
Packit 6c4009
  tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
Packit 6c4009
					    sizeof ("INTERNAL"))),
Packit 6c4009
	   &names, name_compare);
Packit 6c4009
Packit 6c4009
  for (i = 0; i < nmodule_list; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct module *runp;
Packit 6c4009
Packit 6c4009
      if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
Packit 6c4009
	tsearch (new_name (module_list[i]->fromname,
Packit 6c4009
			   module_list[i]->fromname_strent),
Packit 6c4009
		 &names, name_compare);
Packit 6c4009
Packit 6c4009
      for (runp = module_list[i]; runp != NULL; runp = runp->next)
Packit 6c4009
	if (strcmp (runp->toname, "INTERNAL") != 0)
Packit 6c4009
	  tsearch (new_name (runp->toname, runp->toname_strent),
Packit 6c4009
		   &names, name_compare);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
name_to_module_idx (const char *name, int add)
Packit 6c4009
{
Packit 6c4009
  struct name **res;
Packit 6c4009
  struct name fake_name = { .name = name };
Packit 6c4009
  int idx;
Packit 6c4009
Packit 6c4009
  res = (struct name **) tfind (&fake_name, &names, name_compare);
Packit 6c4009
  if (res == NULL)
Packit 6c4009
    abort ();
Packit 6c4009
Packit 6c4009
  idx = (*res)->module_idx;
Packit 6c4009
  if (idx == -1 && add)
Packit 6c4009
    /* No module index assigned yet.  */
Packit 6c4009
    idx = (*res)->module_idx = nname_info++;
Packit 6c4009
Packit 6c4009
  return idx;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
generate_name_info (void)
Packit 6c4009
{
Packit 6c4009
  size_t i;
Packit 6c4009
  int idx;
Packit 6c4009
Packit 6c4009
  name_info = (struct name_info *) xcalloc (nmodule_list + 1,
Packit 6c4009
					    sizeof (struct name_info));
Packit 6c4009
Packit 6c4009
  /* First add a special entry for the INTERNAL name.  This must have
Packit 6c4009
     index zero.  */
Packit 6c4009
  idx = name_to_module_idx ("INTERNAL", 1);
Packit 6c4009
  name_info[0].canonical_name = "INTERNAL";
Packit 6c4009
  name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
Packit 6c4009
					     sizeof ("INTERNAL"));
Packit 6c4009
  assert (nname_info == 1);
Packit 6c4009
Packit 6c4009
  for (i = 0; i < nmodule_list; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct module *runp;
Packit 6c4009
Packit 6c4009
      for (runp = module_list[i]; runp != NULL; runp = runp->next)
Packit 6c4009
	if (strcmp (runp->fromname, "INTERNAL") == 0)
Packit 6c4009
	  {
Packit 6c4009
	    idx = name_to_module_idx (runp->toname, 1);
Packit 6c4009
	    name_info[idx].from_internal = runp;
Packit 6c4009
	    assert (name_info[idx].canonical_name == NULL
Packit 6c4009
		    || strcmp (name_info[idx].canonical_name,
Packit 6c4009
			       runp->toname) == 0);
Packit 6c4009
	    name_info[idx].canonical_name = runp->toname;
Packit 6c4009
	    name_info[idx].canonical_strent = runp->toname_strent;
Packit 6c4009
	  }
Packit 6c4009
	else if (strcmp (runp->toname, "INTERNAL") == 0)
Packit 6c4009
	  {
Packit 6c4009
	    idx = name_to_module_idx (runp->fromname, 1);
Packit 6c4009
	    name_info[idx].to_internal = runp;
Packit 6c4009
	    assert (name_info[idx].canonical_name == NULL
Packit 6c4009
		    || strcmp (name_info[idx].canonical_name,
Packit 6c4009
			       runp->fromname) == 0);
Packit 6c4009
	    name_info[idx].canonical_name = runp->fromname;
Packit 6c4009
	    name_info[idx].canonical_strent = runp->fromname_strent;
Packit 6c4009
	  }
Packit 6c4009
	else
Packit 6c4009
	  {
Packit 6c4009
	    /* This is a transformation not to or from the INTERNAL
Packit 6c4009
	       encoding.  */
Packit 6c4009
	    int from_idx = name_to_module_idx (runp->fromname, 1);
Packit 6c4009
	    int to_idx = name_to_module_idx (runp->toname, 1);
Packit 6c4009
	    struct other_conv_list *newp;
Packit 6c4009
Packit 6c4009
	    newp = (struct other_conv_list *)
Packit 6c4009
	      xmalloc (sizeof (struct other_conv_list));
Packit 6c4009
	    newp->other_conv.module_idx = to_idx;
Packit 6c4009
	    newp->other_conv.module = runp;
Packit 6c4009
	    newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
Packit 6c4009
	    newp->dest_idx = to_idx;
Packit 6c4009
	    newp->next = name_info[from_idx].other_conv_list;
Packit 6c4009
	    name_info[from_idx].other_conv_list = newp;
Packit 6c4009
	    assert (name_info[from_idx].canonical_name == NULL
Packit 6c4009
		    || strcmp (name_info[from_idx].canonical_name,
Packit 6c4009
			       runp->fromname) == 0);
Packit 6c4009
	    name_info[from_idx].canonical_name = runp->fromname;
Packit 6c4009
	    name_info[from_idx].canonical_strent = runp->fromname_strent;
Packit 6c4009
Packit 6c4009
	    ++nextra_modules;
Packit 6c4009
	  }
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Now add the module index information for all the aliases.  */
Packit 6c4009
  for (i = 0; i < nalias_list; ++i)
Packit 6c4009
    {
Packit 6c4009
      struct name fake_name = { .name = alias_list[i]->toname };
Packit 6c4009
      struct name **tonamep;
Packit 6c4009
Packit 6c4009
      tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
Packit 6c4009
      if (tonamep != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct name *newp = new_name (alias_list[i]->fromname,
Packit 6c4009
					alias_list[i]->froment);
Packit 6c4009
	  newp->module_idx = (*tonamep)->module_idx;
Packit 6c4009
	  tsearch (newp, &names, name_compare);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
is_prime (unsigned long int candidate)
Packit 6c4009
{
Packit 6c4009
  /* No even number and none less than 10 will be passed here.  */
Packit 6c4009
  unsigned long int divn = 3;
Packit 6c4009
  unsigned long int sq = divn * divn;
Packit 6c4009
Packit 6c4009
  while (sq < candidate && candidate % divn != 0)
Packit 6c4009
    {
Packit 6c4009
      ++divn;
Packit 6c4009
      sq += 4 * divn;
Packit 6c4009
      ++divn;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return candidate % divn != 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static uint32_t
Packit 6c4009
next_prime (uint32_t seed)
Packit 6c4009
{
Packit 6c4009
  /* Make it definitely odd.  */
Packit 6c4009
  seed |= 1;
Packit 6c4009
Packit 6c4009
  while (!is_prime (seed))
Packit 6c4009
    seed += 2;
Packit 6c4009
Packit 6c4009
  return seed;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Format of the output file.
Packit 6c4009
Packit 6c4009
   Offset   Length       Description
Packit 6c4009
   0000     4            Magic header bytes
Packit 6c4009
   0004     2            Offset of string table (stoff)
Packit 6c4009
   0006     2            Offset of name hashing table (hoff)
Packit 6c4009
   0008     2            Hashing table size (hsize)
Packit 6c4009
   000A     2            Offset of module table (moff)
Packit 6c4009
   000C     2            Offset of other conversion module table (ooff)
Packit 6c4009
Packit 6c4009
   stoff    ???          String table
Packit 6c4009
Packit 6c4009
   hoff     8*hsize      Array of tuples
Packit 6c4009
			    string table offset
Packit 6c4009
			    module index
Packit 6c4009
Packit 6c4009
   moff     ???          Array of tuples
Packit 6c4009
			    canonical name offset
Packit 6c4009
			    from-internal module dir name offset
Packit 6c4009
			    from-internal module name off
Packit 6c4009
			    to-internal module dir name offset
Packit 6c4009
			    to-internal module name offset
Packit 6c4009
			    offset into other conversion table
Packit 6c4009
Packit 6c4009
   ooff     ???          One or more of
Packit 6c4009
			    number of steps/modules
Packit 6c4009
			    one or more of tuple
Packit 6c4009
			      canonical name offset for output
Packit 6c4009
			      module dir name offset
Packit 6c4009
			      module name offset
Packit 6c4009
			 (following last entry with step count 0)
Packit 6c4009
*/
Packit 6c4009
Packit 6c4009
static struct hash_entry *hash_table;
Packit 6c4009
static size_t hash_size;
Packit 6c4009
Packit 6c4009
/* Function to insert the names.  */
Packit 6c4009
static void name_insert (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  struct name *name;
Packit 6c4009
  unsigned int idx;
Packit 6c4009
  unsigned int hval2;
Packit 6c4009
Packit 6c4009
  if (value != leaf && value != postorder)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  name = *(struct name **) nodep;
Packit 6c4009
  idx = name->hashval % hash_size;
Packit 6c4009
  hval2 = 1 + name->hashval % (hash_size - 2);
Packit 6c4009
Packit 6c4009
  while (hash_table[idx].string_offset != 0)
Packit 6c4009
    if ((idx += hval2) >= hash_size)
Packit 6c4009
      idx -= hash_size;
Packit 6c4009
Packit 6c4009
  hash_table[idx].string_offset = strtaboffset (name->strent);
Packit 6c4009
Packit 6c4009
  assert (name->module_idx != -1);
Packit 6c4009
  hash_table[idx].module_idx = name->module_idx;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
write_output (void)
Packit 6c4009
{
Packit 6c4009
  int fd;
Packit 6c4009
  char *string_table;
Packit 6c4009
  size_t string_table_size;
Packit 6c4009
  struct gconvcache_header header;
Packit 6c4009
  struct module_entry *module_table;
Packit 6c4009
  char *extra_table;
Packit 6c4009
  char *cur_extra_table;
Packit 6c4009
  size_t n;
Packit 6c4009
  int idx;
Packit 6c4009
  struct iovec iov[6];
Packit 6c4009
  static const gidx_t null_word;
Packit 6c4009
  size_t total;
Packit 6c4009
  char finalname[prefix_len + sizeof GCONV_MODULES_CACHE];
Packit 6c4009
  char tmpfname[(output_file == NULL ? sizeof finalname : output_file_len + 1)
Packit 6c4009
		+ strlen (".XXXXXX")];
Packit 6c4009
Packit 6c4009
  /* Open the output file.  */
Packit 6c4009
  if (output_file == NULL)
Packit 6c4009
    {
Packit 6c4009
      assert (GCONV_MODULES_CACHE[0] == '/');
Packit 6c4009
      strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len),
Packit 6c4009
		      GCONV_MODULES_CACHE),
Packit 6c4009
	      ".XXXXXX");
Packit 6c4009
      strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX");
Packit 6c4009
  fd = mkstemp (tmpfname);
Packit 6c4009
  if (fd == -1)
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  /* Create the string table.  */
Packit 6c4009
  string_table = strtabfinalize (strtab, &string_table_size);
Packit 6c4009
Packit 6c4009
  /* Create the hashing table.  We know how many strings we have.
Packit 6c4009
     Creating a perfect hash table is not reasonable here.  Therefore
Packit Service 586b9b
     we use open hashing and a table size which is the next prime 50%
Packit 6c4009
     larger than the number of strings.  */
Packit Service bc1aee
  hash_size = next_prime (nnames + (nnames >> 1));
Packit 6c4009
  hash_table = (struct hash_entry *) xcalloc (hash_size,
Packit 6c4009
					      sizeof (struct hash_entry));
Packit 6c4009
  /* Fill the hash table.  */
Packit 6c4009
  twalk (names, name_insert);
Packit 6c4009
Packit 6c4009
  /* Create the section for the module list.  */
Packit 6c4009
  module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
Packit 6c4009
						  nname_info);
Packit 6c4009
Packit 6c4009
  /* Allocate memory for the non-INTERNAL conversions.  The allocated
Packit 6c4009
     memory can be more than is actually needed.  */
Packit 6c4009
  extra_table = (char *) xcalloc (sizeof (struct extra_entry)
Packit 6c4009
				  + sizeof (gidx_t)
Packit 6c4009
				  + sizeof (struct extra_entry_module),
Packit 6c4009
				  nextra_modules);
Packit 6c4009
  cur_extra_table = extra_table;
Packit 6c4009
Packit 6c4009
  /* Fill in the module information.  */
Packit 6c4009
  for (n = 0; n < nname_info; ++n)
Packit 6c4009
    {
Packit 6c4009
      module_table[n].canonname_offset =
Packit 6c4009
	strtaboffset (name_info[n].canonical_strent);
Packit 6c4009
Packit 6c4009
      if (name_info[n].from_internal == NULL)
Packit 6c4009
	{
Packit 6c4009
	  module_table[n].fromdir_offset = 0;
Packit 6c4009
	  module_table[n].fromname_offset = 0;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  module_table[n].fromdir_offset =
Packit 6c4009
	    strtaboffset (name_info[n].from_internal->directory_strent);
Packit 6c4009
	  module_table[n].fromname_offset =
Packit 6c4009
	    strtaboffset (name_info[n].from_internal->filename_strent);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (name_info[n].to_internal == NULL)
Packit 6c4009
	{
Packit 6c4009
	  module_table[n].todir_offset = 0;
Packit 6c4009
	  module_table[n].toname_offset = 0;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  module_table[n].todir_offset =
Packit 6c4009
	    strtaboffset (name_info[n].to_internal->directory_strent);
Packit 6c4009
	  module_table[n].toname_offset =
Packit 6c4009
	    strtaboffset (name_info[n].to_internal->filename_strent);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (name_info[n].other_conv_list != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct other_conv_list *other = name_info[n].other_conv_list;
Packit 6c4009
Packit 6c4009
	  /* Store the reference.  We add 1 to distinguish the entry
Packit 6c4009
	     at offset zero from the case where no extra modules are
Packit 6c4009
	     available.  The file reader has to account for the
Packit 6c4009
	     offset.  */
Packit 6c4009
	  module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
Packit 6c4009
Packit 6c4009
	  do
Packit 6c4009
	    {
Packit 6c4009
	      struct other_conv *runp;
Packit 6c4009
	      struct extra_entry *extra;
Packit 6c4009
Packit 6c4009
	      /* Allocate new entry.  */
Packit 6c4009
	      extra = (struct extra_entry *) cur_extra_table;
Packit 6c4009
	      cur_extra_table += sizeof (struct extra_entry);
Packit 6c4009
	      extra->module_cnt = 0;
Packit 6c4009
Packit 6c4009
	      runp = &other->other_conv;
Packit 6c4009
	      do
Packit 6c4009
		{
Packit 6c4009
		  cur_extra_table += sizeof (struct extra_entry_module);
Packit 6c4009
		  extra->module[extra->module_cnt].outname_offset =
Packit 6c4009
		    runp->next == NULL
Packit 6c4009
		    ? other->dest_idx : runp->next->module_idx;
Packit 6c4009
		  extra->module[extra->module_cnt].dir_offset =
Packit 6c4009
		    strtaboffset (runp->module->directory_strent);
Packit 6c4009
		  extra->module[extra->module_cnt].name_offset =
Packit 6c4009
		    strtaboffset (runp->module->filename_strent);
Packit 6c4009
		  ++extra->module_cnt;
Packit 6c4009
Packit 6c4009
		  runp = runp->next;
Packit 6c4009
		}
Packit 6c4009
	      while (runp != NULL);
Packit 6c4009
Packit 6c4009
	      other = other->next;
Packit 6c4009
	    }
Packit 6c4009
	  while (other != NULL);
Packit 6c4009
Packit 6c4009
	  /* Final module_cnt is zero.  */
Packit 6c4009
	  *((gidx_t *) cur_extra_table) = 0;
Packit 6c4009
	  cur_extra_table += sizeof (gidx_t);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Clear padding.  */
Packit 6c4009
  memset (&header, 0, sizeof (struct gconvcache_header));
Packit 6c4009
Packit 6c4009
  header.magic = GCONVCACHE_MAGIC;
Packit 6c4009
Packit 6c4009
  iov[0].iov_base = &header;
Packit 6c4009
  iov[0].iov_len = sizeof (struct gconvcache_header);
Packit 6c4009
  total = iov[0].iov_len;
Packit 6c4009
Packit 6c4009
  header.string_offset = total;
Packit 6c4009
  iov[1].iov_base = string_table;
Packit 6c4009
  iov[1].iov_len = string_table_size;
Packit 6c4009
  total += iov[1].iov_len;
Packit 6c4009
Packit 6c4009
  idx = 2;
Packit 6c4009
  if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
Packit 6c4009
    {
Packit 6c4009
      iov[2].iov_base = (void *) &null_word;
Packit 6c4009
      iov[2].iov_len = (sizeof (gidx_t)
Packit 6c4009
			- (string_table_size & (sizeof (gidx_t) - 1)));
Packit 6c4009
      total += iov[2].iov_len;
Packit 6c4009
      ++idx;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  header.hash_offset = total;
Packit 6c4009
  header.hash_size = hash_size;
Packit 6c4009
  iov[idx].iov_base = hash_table;
Packit 6c4009
  iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
Packit 6c4009
  total += iov[idx].iov_len;
Packit 6c4009
  ++idx;
Packit 6c4009
Packit 6c4009
  header.module_offset = total;
Packit 6c4009
  iov[idx].iov_base = module_table;
Packit 6c4009
  iov[idx].iov_len = nname_info * sizeof (struct module_entry);
Packit 6c4009
  total += iov[idx].iov_len;
Packit 6c4009
  ++idx;
Packit 6c4009
Packit 6c4009
  assert ((size_t) (cur_extra_table - extra_table)
Packit 6c4009
	  <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
Packit 6c4009
	       + sizeof (struct extra_entry_module))
Packit 6c4009
	      * nextra_modules));
Packit 6c4009
  header.otherconv_offset = total;
Packit 6c4009
  iov[idx].iov_base = extra_table;
Packit 6c4009
  iov[idx].iov_len = cur_extra_table - extra_table;
Packit 6c4009
  total += iov[idx].iov_len;
Packit 6c4009
  ++idx;
Packit 6c4009
Packit 6c4009
  if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
Packit 6c4009
      /* The file was created with mode 0600.  Make it world-readable.  */
Packit 6c4009
      || fchmod (fd, 0644) != 0
Packit 6c4009
      /* Rename the file, possibly replacing an old one.  */
Packit 6c4009
      || rename (tmpfname, output_file ?: finalname) != 0)
Packit 6c4009
    {
Packit 6c4009
      int save_errno = errno;
Packit 6c4009
      close (fd);
Packit 6c4009
      unlink (tmpfname);
Packit 6c4009
      error (EXIT_FAILURE, save_errno,
Packit 6c4009
	     gettext ("cannot generate output file"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  close (fd);
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}