Back to index

glibc  2.9
iconvconfig.c
Go to the documentation of this file.
00001 /* Generate fastloading iconv module configuration files.
00002    Copyright (C) 2000-2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@redhat.com>, 2000.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published
00008    by the Free Software Foundation; version 2 of the License, or
00009    (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #include <argp.h>
00021 #include <assert.h>
00022 #include <error.h>
00023 #include <errno.h>
00024 #include <fcntl.h>
00025 #include <libintl.h>
00026 #include <locale.h>
00027 #include <mcheck.h>
00028 #include <search.h>
00029 #include <stdint.h>
00030 #include <stdbool.h>
00031 #include <stdio.h>
00032 #include <stdio_ext.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <unistd.h>
00036 #include <sys/cdefs.h>
00037 #include <sys/uio.h>
00038 
00039 #include "iconvconfig.h"
00040 
00041 /* Get libc version number.  */
00042 #include "../version.h"
00043 
00044 #define PACKAGE _libc_intl_domainname
00045 
00046 
00047 /* The hashing function we use.  */
00048 #include "../intl/hash-string.h"
00049 
00050 
00051 /* Types used.  */
00052 struct module
00053 {
00054   char *fromname;
00055   struct Strent *fromname_strent;
00056   char *filename;
00057   struct Strent *filename_strent;
00058   const char *directory;
00059   struct Strent *directory_strent;
00060   struct module *next;
00061   int cost;
00062   struct Strent *toname_strent;
00063   char toname[0];
00064 };
00065 
00066 struct alias
00067 {
00068   char *fromname;
00069   struct Strent *froment;
00070   struct module *module;
00071   struct Strent *toent;
00072   char toname[0];
00073 };
00074 
00075 struct name
00076 {
00077   const char *name;
00078   struct Strent *strent;
00079   int module_idx;
00080   uint32_t hashval;
00081 };
00082 
00083 struct name_info
00084 {
00085   const char *canonical_name;
00086   struct Strent *canonical_strent;
00087 
00088   struct module *from_internal;
00089   struct module *to_internal;
00090 
00091   struct other_conv_list
00092   {
00093     int dest_idx;
00094     struct other_conv
00095     {
00096       gidx_t module_idx;
00097       struct module *module;
00098       struct other_conv *next;
00099     } other_conv;
00100     struct other_conv_list *next;
00101   } *other_conv_list;
00102 };
00103 
00104 
00105 /* Name and version of program.  */
00106 static void print_version (FILE *stream, struct argp_state *state);
00107 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
00108 
00109 /* Short description of program.  */
00110 static const char doc[] = N_("\
00111 Create fastloading iconv module configuration file.");
00112 
00113 /* Strings for arguments in help texts.  */
00114 static const char args_doc[] = N_("[DIR...]");
00115 
00116 /* Prototype for option handler.  */
00117 static error_t parse_opt (int key, char *arg, struct argp_state *state);
00118 
00119 /* Function to print some extra text in the help message.  */
00120 static char *more_help (int key, const char *text, void *input);
00121 
00122 /* Definitions of arguments for argp functions.  */
00123 #define OPT_PREFIX 300
00124 #define OPT_NOSTDLIB 301
00125 static const struct argp_option options[] =
00126 {
00127   { "prefix", OPT_PREFIX, "PATH", 0, N_("Prefix used for all file accesses") },
00128   { "output", 'o', "FILE", 0, N_("\
00129 Put output in FILE instead of installed location\
00130  (--prefix does not apply to FILE)") },
00131   { "nostdlib", OPT_NOSTDLIB, NULL, 0,
00132     N_("Do not search standard directories, only those on the command line") },
00133   { NULL, 0, NULL, 0, NULL }
00134 };
00135 
00136 /* Data structure to communicate with argp functions.  */
00137 static struct argp argp =
00138 {
00139   options, parse_opt, args_doc, doc, NULL, more_help
00140 };
00141 
00142 
00143 /* The function doing the actual work.  */
00144 static int handle_dir (const char *dir);
00145 
00146 /* Add all known builtin conversions and aliases.  */
00147 static void add_builtins (void);
00148 
00149 /* Create list of all aliases without circular aliases.  */
00150 static void get_aliases (void);
00151 
00152 /* Create list of all modules.  */
00153 static void get_modules (void);
00154 
00155 /* Get list of all the names and thereby indexing them.  */
00156 static void generate_name_list (void);
00157 
00158 /* Collect information about all the names.  */
00159 static void generate_name_info (void);
00160 
00161 /* Write the output file.  */
00162 static int write_output (void);
00163 
00164 
00165 /* Prefix to be used for all file accesses.  */
00166 static const char *prefix = "";
00167 /* Its length.  */
00168 static size_t prefix_len;
00169 
00170 /* Directory to place output file in.  */
00171 static const char *output_file;
00172 /* Its length.  */
00173 static size_t output_file_len;
00174 
00175 /* If true, omit the GCONV_PATH directories and require some arguments.  */
00176 static bool nostdlib;
00177 
00178 /* Search tree of the modules we know.  */
00179 static void *modules;
00180 
00181 /* Search tree of the aliases we know.  */
00182 static void *aliases;
00183 
00184 /* Search tree for name to index mapping.  */
00185 static void *names;
00186 
00187 /* Number of names we know about.  */
00188 static int nnames;
00189 
00190 /* List of all aliases.  */
00191 static struct alias **alias_list;
00192 static size_t nalias_list;
00193 static size_t nalias_list_max;
00194 
00195 /* List of all modules.  */
00196 static struct module **module_list;
00197 static size_t nmodule_list;
00198 static size_t nmodule_list_max;
00199 
00200 /* Names and information about them.  */
00201 static struct name_info *name_info;
00202 static size_t nname_info;
00203 
00204 /* Number of translations not from or to INTERNAL.  */
00205 static size_t nextra_modules;
00206 
00207 
00208 /* Names and aliases for the builtin transformations.  */
00209 static struct
00210 {
00211   const char *from;
00212   const char *to;
00213 } builtin_alias[] =
00214   {
00215 #define BUILTIN_ALIAS(alias, real) \
00216     { .from = alias, .to = real },
00217 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
00218                             MinF, MaxF, MinT, MaxT)
00219 #include <gconv_builtin.h>
00220   };
00221 #undef BUILTIN_ALIAS
00222 #undef BUILTIN_TRANSFORMATION
00223 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
00224 
00225 static struct
00226 {
00227   const char *from;
00228   const char *to;
00229   const char *module;
00230   int cost;
00231 } builtin_trans[] =
00232   {
00233 #define BUILTIN_ALIAS(alias, real)
00234 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
00235                             MinF, MaxF, MinT, MaxT) \
00236     { .from = From, .to = To, .module = Name, .cost = Cost },
00237 #include <gconv_builtin.h>
00238   };
00239 #undef BUILTIN_ALIAS
00240 #undef BUILTIN_TRANSFORMATION
00241 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
00242 
00243 
00244 /* Filename extension for the modules.  */
00245 #ifndef MODULE_EXT
00246 # define MODULE_EXT ".so"
00247 #endif
00248 static const char gconv_module_ext[] = MODULE_EXT;
00249 
00250 
00251 extern void *xmalloc (size_t n) __attribute_malloc__;
00252 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
00253 extern void *xrealloc (void *p, size_t n);
00254 
00255 
00256 /* C string table handling.  */
00257 struct Strtab;
00258 struct Strent;
00259 
00260 /* Create new C string table object in memory.  */
00261 extern struct Strtab *strtabinit (void);
00262 
00263 /* Free resources allocated for C string table ST.  */
00264 extern void strtabfree (struct Strtab *st);
00265 
00266 /* Add string STR (length LEN is != 0) to C string table ST.  */
00267 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
00268                              size_t len);
00269 
00270 /* Finalize string table ST and store size in *SIZE and return a pointer.  */
00271 extern void *strtabfinalize (struct Strtab *st, size_t *size);
00272 
00273 /* Get offset in string table for string associated with SE.  */
00274 extern size_t strtaboffset (struct Strent *se);
00275 
00276 /* String table we construct.  */
00277 static struct Strtab *strtab;
00278 
00279 
00280 
00281 int
00282 main (int argc, char *argv[])
00283 {
00284   int remaining;
00285   int status = 0;
00286 
00287   /* Enable memory use testing.  */
00288   /* mcheck_pedantic (NULL); */
00289   mtrace ();
00290 
00291   /* Set locale via LC_ALL.  */
00292   setlocale (LC_ALL, "");
00293 
00294   /* Set the text message domain.  */
00295   textdomain (_libc_intl_domainname);
00296 
00297   /* Parse and process arguments.  */
00298   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
00299 
00300   if (nostdlib && remaining == argc)
00301     error (2, 0, _("Directory arguments required when using --nostdlib"));
00302 
00303   /* Initialize the string table.  */
00304   strtab = strtabinit ();
00305 
00306   /* Handle all directories mentioned.  */
00307   while (remaining < argc)
00308     status |= handle_dir (argv[remaining++]);
00309 
00310   if (! nostdlib)
00311     {
00312       /* In any case also handle the standard directory.  */
00313       char *path = strdupa (GCONV_PATH), *tp = strsep (&path, ":");
00314       while (tp != NULL)
00315        {
00316          status |= handle_dir (tp);
00317 
00318          tp = strsep (&path, ":");
00319        }
00320     }
00321 
00322   /* Add the builtin transformations and aliases without overwriting
00323      anything.  */
00324   add_builtins ();
00325 
00326   /* Store aliases in an array.  */
00327   get_aliases ();
00328 
00329   /* Get list of all modules.  */
00330   get_modules ();
00331 
00332   /* Generate list of all the names we know to handle in some way.  */
00333   generate_name_list ();
00334 
00335   /* Now we know all the names we will handle, collect information
00336      about them.  */
00337   generate_name_info ();
00338 
00339   /* Write the output file, but only if we haven't seen any error.  */
00340   if (status == 0)
00341     status = write_output ();
00342   else
00343     error (1, 0, _("no output file produced because warnings were issued"));
00344 
00345   return status;
00346 }
00347 
00348 
00349 /* Handle program arguments.  */
00350 static error_t
00351 parse_opt (int key, char *arg, struct argp_state *state)
00352 {
00353   switch (key)
00354     {
00355     case OPT_PREFIX:
00356       prefix = arg;
00357       prefix_len = strlen (prefix);
00358       break;
00359     case 'o':
00360       output_file = arg;
00361       output_file_len = strlen (output_file);
00362       break;
00363     case OPT_NOSTDLIB:
00364       nostdlib = true;
00365       break;
00366     default:
00367       return ARGP_ERR_UNKNOWN;
00368     }
00369   return 0;
00370 }
00371 
00372 
00373 static char *
00374 more_help (int key, const char *text, void *input)
00375 {
00376   switch (key)
00377     {
00378     case ARGP_KEY_HELP_EXTRA:
00379       /* We print some extra information.  */
00380       return strdup (gettext ("\
00381 For bug reporting instructions, please see:\n\
00382 <http://www.gnu.org/software/libc/bugs.html>.\n"));
00383     default:
00384       break;
00385     }
00386   return (char *) text;
00387 }
00388 
00389 
00390 /* Print the version information.  */
00391 static void
00392 print_version (FILE *stream, struct argp_state *state)
00393 {
00394   fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
00395   fprintf (stream, gettext ("\
00396 Copyright (C) %s Free Software Foundation, Inc.\n\
00397 This is free software; see the source for copying conditions.  There is NO\n\
00398 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
00399 "), "2008");
00400   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
00401 }
00402 
00403 
00404 static int
00405 alias_compare (const void *p1, const void *p2)
00406 {
00407   const struct alias *a1 = (const struct alias *) p1;
00408   const struct alias *a2 = (const struct alias *) p2;
00409 
00410   return strcmp (a1->fromname, a2->fromname);
00411 }
00412 
00413 
00414 static void
00415 new_alias (const char *fromname, size_t fromlen, const char *toname,
00416           size_t tolen)
00417 {
00418   struct alias *newp;
00419   void **inserted;
00420 
00421   newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
00422 
00423   newp->fromname = mempcpy (newp->toname, toname, tolen);
00424   memcpy (newp->fromname, fromname, fromlen);
00425   newp->module = NULL;
00426 
00427   inserted = (void **) tsearch (newp, &aliases, alias_compare);
00428   if (inserted == NULL)
00429     error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
00430   if (*inserted != newp)
00431     /* Something went wrong, free this entry.  */
00432     free (newp);
00433   else
00434     {
00435       newp->froment = strtabadd (strtab, newp->fromname, fromlen);
00436       newp->toent = strtabadd (strtab, newp->toname, tolen);
00437     }
00438 }
00439 
00440 
00441 /* Add new alias.  */
00442 static void
00443 add_alias (char *rp)
00444 {
00445   /* We now expect two more string.  The strings are normalized
00446      (converted to UPPER case) and strored in the alias database.  */
00447   char *from;
00448   char *to;
00449   char *wp;
00450 
00451   while (isspace (*rp))
00452     ++rp;
00453   from = wp = rp;
00454   while (*rp != '\0' && !isspace (*rp))
00455     *wp++ = toupper (*rp++);
00456   if (*rp == '\0')
00457     /* There is no `to' string on the line.  Ignore it.  */
00458     return;
00459   *wp++ = '\0';
00460   to = ++rp;
00461   while (isspace (*rp))
00462     ++rp;
00463   while (*rp != '\0' && !isspace (*rp))
00464     *wp++ = toupper (*rp++);
00465   if (to == wp)
00466     /* No `to' string, ignore the line.  */
00467     return;
00468   *wp++ = '\0';
00469 
00470   assert (strlen (from) + 1 == (size_t) (to - from));
00471   assert (strlen (to) + 1 == (size_t) (wp - to));
00472 
00473   new_alias (from, to - from, to, wp - to);
00474 }
00475 
00476 
00477 static void
00478 append_alias (const void *nodep, VISIT value, int level)
00479 {
00480   if (value != leaf && value != postorder)
00481     return;
00482 
00483   if (nalias_list_max == nalias_list)
00484     {
00485       nalias_list_max += 50;
00486       alias_list = (struct alias **) xrealloc (alias_list,
00487                                           (nalias_list_max
00488                                           * sizeof (struct alias *)));
00489     }
00490 
00491   alias_list[nalias_list++] = *(struct alias **) nodep;
00492 }
00493 
00494 
00495 static void
00496 get_aliases (void)
00497 {
00498   twalk (aliases, append_alias);
00499 }
00500 
00501 
00502 static int
00503 module_compare (const void *p1, const void *p2)
00504 {
00505   const struct module *m1 = (const struct module *) p1;
00506   const struct module *m2 = (const struct module *) p2;
00507   int result;
00508 
00509   result = strcmp (m1->fromname, m2->fromname);
00510   if (result == 0)
00511     result = strcmp (m1->toname, m2->toname);
00512 
00513   return result;
00514 }
00515 
00516 
00517 /* Create new module record.  */
00518 static void
00519 new_module (const char *fromname, size_t fromlen, const char *toname,
00520            size_t tolen, const char *directory,
00521            const char *filename, size_t filelen, int cost, size_t need_ext)
00522 {
00523   struct module *new_module;
00524   size_t dirlen = strlen (directory) + 1;
00525   char *tmp;
00526   void **inserted;
00527 
00528   new_module = (struct module *) xmalloc (sizeof (struct module)
00529                                      + fromlen + tolen + filelen
00530                                      + need_ext);
00531 
00532   new_module->fromname = mempcpy (new_module->toname, toname, tolen);
00533 
00534   new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
00535 
00536   new_module->cost = cost;
00537   new_module->next = NULL;
00538 
00539   tmp = mempcpy (new_module->filename, filename, filelen);
00540   if (need_ext)
00541     {
00542       memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
00543       filelen += need_ext;
00544     }
00545   new_module->directory = directory;
00546 
00547   /* Now insert the new module data structure in our search tree.  */
00548   inserted = (void **) tsearch (new_module, &modules, module_compare);
00549   if (inserted == NULL)
00550     error (EXIT_FAILURE, errno, "while inserting in search tree");
00551   if (*inserted != new_module)
00552     free (new_module);
00553   else
00554     {
00555       new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
00556                                           fromlen);
00557       new_module->toname_strent = strtabadd (strtab, new_module->toname,
00558                                         tolen);
00559       new_module->filename_strent = strtabadd (strtab, new_module->filename,
00560                                           filelen);
00561       new_module->directory_strent = strtabadd (strtab, directory, dirlen);
00562     }
00563 }
00564 
00565 
00566 /* Add new module.  */
00567 static void
00568 internal_function
00569 add_module (char *rp, const char *directory)
00570 {
00571   /* We expect now
00572      1. `from' name
00573      2. `to' name
00574      3. filename of the module
00575      4. an optional cost value
00576   */
00577   char *from;
00578   char *to;
00579   char *module;
00580   char *wp;
00581   int need_ext;
00582   int cost;
00583 
00584   while (isspace (*rp))
00585     ++rp;
00586   from = rp;
00587   while (*rp != '\0' && !isspace (*rp))
00588     {
00589       *rp = toupper (*rp);
00590       ++rp;
00591     }
00592   if (*rp == '\0')
00593     return;
00594   *rp++ = '\0';
00595   to = wp = rp;
00596   while (isspace (*rp))
00597     ++rp;
00598   while (*rp != '\0' && !isspace (*rp))
00599     *wp++ = toupper (*rp++);
00600   if (*rp == '\0')
00601     return;
00602   *wp++ = '\0';
00603   do
00604     ++rp;
00605   while (isspace (*rp));
00606   module = wp;
00607   while (*rp != '\0' && !isspace (*rp))
00608     *wp++ = *rp++;
00609   if (*rp == '\0')
00610     {
00611       /* There is no cost, use one by default.  */
00612       *wp++ = '\0';
00613       cost = 1;
00614     }
00615   else
00616     {
00617       /* There might be a cost value.  */
00618       char *endp;
00619 
00620       *wp++ = '\0';
00621       cost = strtol (rp, &endp, 10);
00622       if (rp == endp || cost < 1)
00623        /* No useful information.  */
00624        cost = 1;
00625     }
00626 
00627   if (module[0] == '\0')
00628     /* No module name given.  */
00629     return;
00630 
00631   /* See whether we must add the ending.  */
00632   need_ext = 0;
00633   if ((size_t) (wp - module) < sizeof (gconv_module_ext)
00634       || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
00635                sizeof (gconv_module_ext)) != 0)
00636     /* We must add the module extension.  */
00637     need_ext = sizeof (gconv_module_ext) - 1;
00638 
00639   assert (strlen (from) + 1 == (size_t) (to - from));
00640   assert (strlen (to) + 1 == (size_t) (module - to));
00641   assert (strlen (module) + 1 == (size_t) (wp - module));
00642 
00643   new_module (from, to - from, to, module - to, directory, module, wp - module,
00644              cost, need_ext);
00645 }
00646 
00647 
00648 /* Read the config file and add the data for this directory to that.  */
00649 static int
00650 handle_dir (const char *dir)
00651 {
00652   char *cp;
00653   FILE *fp;
00654   char *line = NULL;
00655   size_t linelen = 0;
00656   size_t dirlen = strlen (dir);
00657 
00658   if (dir[dirlen - 1] != '/')
00659     {
00660       char *newp = (char *) xmalloc (dirlen + 2);
00661       dir = memcpy (newp, dir, dirlen);
00662       newp[dirlen++] = '/';
00663       newp[dirlen] = '\0';
00664     }
00665 
00666   char infile[prefix_len + dirlen + sizeof "gconv-modules"];
00667   cp = infile;
00668   if (dir[0] == '/')
00669     cp = mempcpy (cp, prefix, prefix_len);
00670   strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
00671 
00672   fp = fopen (infile, "r");
00673   if (fp == NULL)
00674     {
00675       error (0, errno, "cannot open `%s'", infile);
00676       return 1;
00677     }
00678 
00679   /* No threads present.  */
00680   __fsetlocking (fp, FSETLOCKING_BYCALLER);
00681 
00682   while (!feof_unlocked (fp))
00683     {
00684       char *rp, *endp, *word;
00685       ssize_t n = __getdelim (&line, &linelen, '\n', fp);
00686 
00687       if (n < 0)
00688        /* An error occurred.  */
00689        break;
00690 
00691       rp = line;
00692       /* Terminate the line (excluding comments or newline) with a NUL
00693         byte to simplify the following code.  */
00694       endp = strchr (rp, '#');
00695       if (endp != NULL)
00696        *endp = '\0';
00697       else
00698        if (rp[n - 1] == '\n')
00699          rp[n - 1] = '\0';
00700 
00701       while (isspace (*rp))
00702        ++rp;
00703 
00704       /* If this is an empty line go on with the next one.  */
00705       if (rp == endp)
00706        continue;
00707 
00708       word = rp;
00709       while (*rp != '\0' && !isspace (*rp))
00710        ++rp;
00711 
00712       if (rp - word == sizeof ("alias") - 1
00713          && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
00714        add_alias (rp);
00715       else if (rp - word == sizeof ("module") - 1
00716               && memcmp (word, "module", sizeof ("module") - 1) == 0)
00717        add_module (rp, dir);
00718       /* else */
00719        /* Otherwise ignore the line.  */
00720     }
00721 
00722   free (line);
00723 
00724   fclose (fp);
00725 
00726   return 0;
00727 }
00728 
00729 
00730 static void
00731 append_module (const void *nodep, VISIT value, int level)
00732 {
00733   struct module *mo;
00734 
00735   if (value != leaf && value != postorder)
00736     return;
00737 
00738   mo = *(struct module **) nodep;
00739 
00740   if (nmodule_list > 0
00741       && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
00742     {
00743       /* Same name.  */
00744       mo->next = module_list[nmodule_list - 1];
00745       module_list[nmodule_list - 1] = mo;
00746 
00747       return;
00748     }
00749 
00750   if (nmodule_list_max == nmodule_list)
00751     {
00752       nmodule_list_max += 50;
00753       module_list = (struct module **) xrealloc (module_list,
00754                                            (nmodule_list_max
00755                                             * sizeof (struct module *)));
00756     }
00757 
00758   module_list[nmodule_list++] = mo;
00759 }
00760 
00761 
00762 static void
00763 get_modules (void)
00764 {
00765   twalk (modules, append_module);
00766 }
00767 
00768 
00769 static void
00770 add_builtins (void)
00771 {
00772   size_t cnt;
00773 
00774   /* Add all aliases.  */
00775   for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
00776     new_alias (builtin_alias[cnt].from,
00777               strlen (builtin_alias[cnt].from) + 1,
00778               builtin_alias[cnt].to,
00779               strlen (builtin_alias[cnt].to) + 1);
00780 
00781   /* add the builtin transformations.  */
00782   for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
00783     new_module (builtin_trans[cnt].from,
00784               strlen (builtin_trans[cnt].from) + 1,
00785               builtin_trans[cnt].to,
00786               strlen (builtin_trans[cnt].to) + 1,
00787               "", builtin_trans[cnt].module,
00788               strlen (builtin_trans[cnt].module) + 1,
00789               builtin_trans[cnt].cost, 0);
00790 }
00791 
00792 
00793 static int
00794 name_compare (const void *p1, const void *p2)
00795 {
00796   const struct name *n1 = (const struct name *) p1;
00797   const struct name *n2 = (const struct name *) p2;
00798 
00799   return strcmp (n1->name, n2->name);
00800 }
00801 
00802 
00803 static struct name *
00804 new_name (const char *str, struct Strent *strent)
00805 {
00806   struct name *newp = (struct name *) xmalloc (sizeof (struct name));
00807 
00808   newp->name = str;
00809   newp->strent = strent;
00810   newp->module_idx = -1;
00811   newp->hashval = __hash_string (str);
00812 
00813   ++nnames;
00814 
00815   return newp;
00816 }
00817 
00818 
00819 static void
00820 generate_name_list (void)
00821 {
00822   size_t i;
00823 
00824   /* A name we always need.  */
00825   tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
00826                                        sizeof ("INTERNAL"))),
00827           &names, name_compare);
00828 
00829   for (i = 0; i < nmodule_list; ++i)
00830     {
00831       struct module *runp;
00832 
00833       if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
00834        tsearch (new_name (module_list[i]->fromname,
00835                         module_list[i]->fromname_strent),
00836                &names, name_compare);
00837 
00838       for (runp = module_list[i]; runp != NULL; runp = runp->next)
00839        if (strcmp (runp->toname, "INTERNAL") != 0)
00840          tsearch (new_name (runp->toname, runp->toname_strent),
00841                  &names, name_compare);
00842     }
00843 }
00844 
00845 
00846 static int
00847 name_to_module_idx (const char *name, int add)
00848 {
00849   struct name **res;
00850   struct name fake_name = { .name = name };
00851   int idx;
00852 
00853   res = (struct name **) tfind (&fake_name, &names, name_compare);
00854   if (res == NULL)
00855     abort ();
00856 
00857   idx = (*res)->module_idx;
00858   if (idx == -1 && add)
00859     /* No module index assigned yet.  */
00860     idx = (*res)->module_idx = nname_info++;
00861 
00862   return idx;
00863 }
00864 
00865 
00866 static void
00867 generate_name_info (void)
00868 {
00869   size_t i;
00870   int idx;
00871 
00872   name_info = (struct name_info *) xcalloc (nmodule_list + 1,
00873                                        sizeof (struct name_info));
00874 
00875   /* First add a special entry for the INTERNAL name.  This must have
00876      index zero.  */
00877   idx = name_to_module_idx ("INTERNAL", 1);
00878   name_info[0].canonical_name = "INTERNAL";
00879   name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
00880                                         sizeof ("INTERNAL"));
00881   assert (nname_info == 1);
00882 
00883   for (i = 0; i < nmodule_list; ++i)
00884     {
00885       struct module *runp;
00886 
00887       for (runp = module_list[i]; runp != NULL; runp = runp->next)
00888        if (strcmp (runp->fromname, "INTERNAL") == 0)
00889          {
00890            idx = name_to_module_idx (runp->toname, 1);
00891            name_info[idx].from_internal = runp;
00892            assert (name_info[idx].canonical_name == NULL
00893                   || strcmp (name_info[idx].canonical_name,
00894                             runp->toname) == 0);
00895            name_info[idx].canonical_name = runp->toname;
00896            name_info[idx].canonical_strent = runp->toname_strent;
00897          }
00898        else if (strcmp (runp->toname, "INTERNAL") == 0)
00899          {
00900            idx = name_to_module_idx (runp->fromname, 1);
00901            name_info[idx].to_internal = runp;
00902            assert (name_info[idx].canonical_name == NULL
00903                   || strcmp (name_info[idx].canonical_name,
00904                             runp->fromname) == 0);
00905            name_info[idx].canonical_name = runp->fromname;
00906            name_info[idx].canonical_strent = runp->fromname_strent;
00907          }
00908        else
00909          {
00910            /* This is a transformation not to or from the INTERNAL
00911               encoding.  */
00912            int from_idx = name_to_module_idx (runp->fromname, 1);
00913            int to_idx = name_to_module_idx (runp->toname, 1);
00914            struct other_conv_list *newp;
00915 
00916            newp = (struct other_conv_list *)
00917              xmalloc (sizeof (struct other_conv_list));
00918            newp->other_conv.module_idx = to_idx;
00919            newp->other_conv.module = runp;
00920            newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
00921            newp->dest_idx = to_idx;
00922            newp->next = name_info[from_idx].other_conv_list;
00923            name_info[from_idx].other_conv_list = newp;
00924            assert (name_info[from_idx].canonical_name == NULL
00925                   || strcmp (name_info[from_idx].canonical_name,
00926                             runp->fromname) == 0);
00927            name_info[from_idx].canonical_name = runp->fromname;
00928            name_info[from_idx].canonical_strent = runp->fromname_strent;
00929 
00930            ++nextra_modules;
00931          }
00932     }
00933 
00934   /* Now add the module index information for all the aliases.  */
00935   for (i = 0; i < nalias_list; ++i)
00936     {
00937       struct name fake_name = { .name = alias_list[i]->toname };
00938       struct name **tonamep;
00939 
00940       tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
00941       if (tonamep != NULL)
00942        {
00943          struct name *newp = new_name (alias_list[i]->fromname,
00944                                    alias_list[i]->froment);
00945          newp->module_idx = (*tonamep)->module_idx;
00946          tsearch (newp, &names, name_compare);
00947        }
00948     }
00949 }
00950 
00951 
00952 static int
00953 is_prime (unsigned long int candidate)
00954 {
00955   /* No even number and none less than 10 will be passed here.  */
00956   unsigned long int divn = 3;
00957   unsigned long int sq = divn * divn;
00958 
00959   while (sq < candidate && candidate % divn != 0)
00960     {
00961       ++divn;
00962       sq += 4 * divn;
00963       ++divn;
00964     }
00965 
00966   return candidate % divn != 0;
00967 }
00968 
00969 
00970 static uint32_t
00971 next_prime (uint32_t seed)
00972 {
00973   /* Make it definitely odd.  */
00974   seed |= 1;
00975 
00976   while (!is_prime (seed))
00977     seed += 2;
00978 
00979   return seed;
00980 }
00981 
00982 
00983 /* Format of the output file.
00984 
00985    Offset   Length       Description
00986    0000     4            Magic header bytes
00987    0004     2            Offset of string table (stoff)
00988    0006     2            Offset of name hashing table (hoff)
00989    0008     2            Hashing table size (hsize)
00990    000A     2            Offset of module table (moff)
00991    000C     2            Offset of other conversion module table (ooff)
00992 
00993    stoff    ???          String table
00994 
00995    hoff     8*hsize      Array of tuples
00996                             string table offset
00997                             module index
00998 
00999    moff     ???          Array of tuples
01000                             canonical name offset
01001                             from-internal module dir name offset
01002                             from-internal module name off
01003                             to-internal module dir name offset
01004                             to-internal module name offset
01005                             offset into other conversion table
01006 
01007    ooff     ???          One or more of
01008                             number of steps/modules
01009                             one or more of tuple
01010                               canonical name offset for output
01011                               module dir name offset
01012                               module name offset
01013                          (following last entry with step count 0)
01014 */
01015 
01016 static struct hash_entry *hash_table;
01017 static size_t hash_size;
01018 
01019 /* Function to insert the names.  */
01020 static void name_insert (const void *nodep, VISIT value, int level)
01021 {
01022   struct name *name;
01023   unsigned int idx;
01024   unsigned int hval2;
01025 
01026   if (value != leaf && value != postorder)
01027     return;
01028 
01029   name = *(struct name **) nodep;
01030   idx = name->hashval % hash_size;
01031   hval2 = 1 + name->hashval % (hash_size - 2);
01032 
01033   while (hash_table[idx].string_offset != 0)
01034     if ((idx += hval2) >= hash_size)
01035       idx -= hash_size;
01036 
01037   hash_table[idx].string_offset = strtaboffset (name->strent);
01038 
01039   assert (name->module_idx != -1);
01040   hash_table[idx].module_idx = name->module_idx;
01041 }
01042 
01043 static int
01044 write_output (void)
01045 {
01046   int fd;
01047   char *string_table;
01048   size_t string_table_size;
01049   struct gconvcache_header header;
01050   struct module_entry *module_table;
01051   char *extra_table;
01052   char *cur_extra_table;
01053   size_t n;
01054   int idx;
01055   struct iovec iov[6];
01056   static const gidx_t null_word;
01057   size_t total;
01058   char finalname[prefix_len + sizeof GCONV_MODULES_CACHE];
01059   char tmpfname[(output_file == NULL ? sizeof finalname : output_file_len + 1)
01060               + strlen (".XXXXXX")];
01061 
01062   /* Open the output file.  */
01063   if (output_file == NULL)
01064     {
01065       assert (GCONV_MODULES_CACHE[0] == '/');
01066       strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len),
01067                     GCONV_MODULES_CACHE),
01068              ".XXXXXX");
01069       strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
01070     }
01071   else
01072     strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX");
01073   fd = mkstemp (tmpfname);
01074   if (fd == -1)
01075     return 1;
01076 
01077   /* Create the string table.  */
01078   string_table = strtabfinalize (strtab, &string_table_size);
01079 
01080   /* Create the hashing table.  We know how many strings we have.
01081      Creating a perfect hash table is not reasonable here.  Therefore
01082      we use open hashing and a table size which is the next prime 40%
01083      larger than the number of strings.  */
01084   hash_size = next_prime (nnames * 1.4);
01085   hash_table = (struct hash_entry *) xcalloc (hash_size,
01086                                          sizeof (struct hash_entry));
01087   /* Fill the hash table.  */
01088   twalk (names, name_insert);
01089 
01090   /* Create the section for the module list.  */
01091   module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
01092                                             nname_info);
01093 
01094   /* Allocate memory for the non-INTERNAL conversions.  The allocated
01095      memory can be more than is actually needed.  */
01096   extra_table = (char *) xcalloc (sizeof (struct extra_entry)
01097                               + sizeof (gidx_t)
01098                               + sizeof (struct extra_entry_module),
01099                               nextra_modules);
01100   cur_extra_table = extra_table;
01101 
01102   /* Fill in the module information.  */
01103   for (n = 0; n < nname_info; ++n)
01104     {
01105       module_table[n].canonname_offset =
01106        strtaboffset (name_info[n].canonical_strent);
01107 
01108       if (name_info[n].from_internal == NULL)
01109        {
01110          module_table[n].fromdir_offset = 0;
01111          module_table[n].fromname_offset = 0;
01112        }
01113       else
01114        {
01115          module_table[n].fromdir_offset =
01116            strtaboffset (name_info[n].from_internal->directory_strent);
01117          module_table[n].fromname_offset =
01118            strtaboffset (name_info[n].from_internal->filename_strent);
01119        }
01120 
01121       if (name_info[n].to_internal == NULL)
01122        {
01123          module_table[n].todir_offset = 0;
01124          module_table[n].toname_offset = 0;
01125        }
01126       else
01127        {
01128          module_table[n].todir_offset =
01129            strtaboffset (name_info[n].to_internal->directory_strent);
01130          module_table[n].toname_offset =
01131            strtaboffset (name_info[n].to_internal->filename_strent);
01132        }
01133 
01134       if (name_info[n].other_conv_list != NULL)
01135        {
01136          struct other_conv_list *other = name_info[n].other_conv_list;
01137 
01138          /* Store the reference.  We add 1 to distinguish the entry
01139             at offset zero from the case where no extra modules are
01140             available.  The file reader has to account for the
01141             offset.  */
01142          module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
01143 
01144          do
01145            {
01146              struct other_conv *runp;
01147              struct extra_entry *extra;
01148 
01149              /* Allocate new entry.  */
01150              extra = (struct extra_entry *) cur_extra_table;
01151              cur_extra_table += sizeof (struct extra_entry);
01152              extra->module_cnt = 0;
01153 
01154              runp = &other->other_conv;
01155              do
01156               {
01157                 cur_extra_table += sizeof (struct extra_entry_module);
01158                 extra->module[extra->module_cnt].outname_offset =
01159                   runp->next == NULL
01160                   ? other->dest_idx : runp->next->module_idx;
01161                 extra->module[extra->module_cnt].dir_offset =
01162                   strtaboffset (runp->module->directory_strent);
01163                 extra->module[extra->module_cnt].name_offset =
01164                   strtaboffset (runp->module->filename_strent);
01165                 ++extra->module_cnt;
01166 
01167                 runp = runp->next;
01168               }
01169              while (runp != NULL);
01170 
01171              other = other->next;
01172            }
01173          while (other != NULL);
01174 
01175          /* Final module_cnt is zero.  */
01176          *((gidx_t *) cur_extra_table) = 0;
01177          cur_extra_table += sizeof (gidx_t);
01178        }
01179     }
01180 
01181   /* Clear padding.  */
01182   memset (&header, 0, sizeof (struct gconvcache_header));
01183 
01184   header.magic = GCONVCACHE_MAGIC;
01185 
01186   iov[0].iov_base = &header;
01187   iov[0].iov_len = sizeof (struct gconvcache_header);
01188   total = iov[0].iov_len;
01189 
01190   header.string_offset = total;
01191   iov[1].iov_base = string_table;
01192   iov[1].iov_len = string_table_size;
01193   total += iov[1].iov_len;
01194 
01195   idx = 2;
01196   if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
01197     {
01198       iov[2].iov_base = (void *) &null_word;
01199       iov[2].iov_len = (sizeof (gidx_t)
01200                      - (string_table_size & (sizeof (gidx_t) - 1)));
01201       total += iov[2].iov_len;
01202       ++idx;
01203     }
01204 
01205   header.hash_offset = total;
01206   header.hash_size = hash_size;
01207   iov[idx].iov_base = hash_table;
01208   iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
01209   total += iov[idx].iov_len;
01210   ++idx;
01211 
01212   header.module_offset = total;
01213   iov[idx].iov_base = module_table;
01214   iov[idx].iov_len = nname_info * sizeof (struct module_entry);
01215   total += iov[idx].iov_len;
01216   ++idx;
01217 
01218   assert ((size_t) (cur_extra_table - extra_table)
01219          <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
01220               + sizeof (struct extra_entry_module))
01221              * nextra_modules));
01222   header.otherconv_offset = total;
01223   iov[idx].iov_base = extra_table;
01224   iov[idx].iov_len = cur_extra_table - extra_table;
01225   total += iov[idx].iov_len;
01226   ++idx;
01227 
01228   if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
01229       /* The file was created with mode 0600.  Make it world-readable.  */
01230       || fchmod (fd, 0644) != 0
01231       /* Rename the file, possibly replacing an old one.  */
01232       || rename (tmpfname, output_file ?: finalname) != 0)
01233     {
01234       int save_errno = errno;
01235       close (fd);
01236       unlink (tmpfname);
01237       error (EXIT_FAILURE, save_errno,
01238             gettext ("cannot generate output file"));
01239     }
01240 
01241   close (fd);
01242 
01243   return 0;
01244 }