[BZ #40]
[platform/upstream/glibc.git] / iconv / iconvconfig.c
1 /* Generate fastloading iconv module configuration files.
2    Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@redhat.com>, 2000.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <argp.h>
22 #include <assert.h>
23 #include <error.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <libintl.h>
27 #include <locale.h>
28 #include <mcheck.h>
29 #include <search.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <stdio_ext.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/cdefs.h>
37 #include <sys/uio.h>
38
39 #include "iconvconfig.h"
40
41 /* Get libc version number.  */
42 #include "../version.h"
43
44 #define PACKAGE _libc_intl_domainname
45
46
47 /* The hashing function we use.  */
48 #include "../intl/hash-string.h"
49
50
51 /* Types used.  */
52 struct module
53 {
54   char *fromname;
55   struct Strent *fromname_strent;
56   char *filename;
57   struct Strent *filename_strent;
58   const char *directory;
59   struct Strent *directory_strent;
60   struct module *next;
61   int cost;
62   struct Strent *toname_strent;
63   char toname[0];
64 };
65
66 struct alias
67 {
68   char *fromname;
69   struct Strent *froment;
70   struct module *module;
71   struct Strent *toent;
72   char toname[0];
73 };
74
75 struct name
76 {
77   const char *name;
78   struct Strent *strent;
79   int module_idx;
80   uint32_t hashval;
81 };
82
83 struct name_info
84 {
85   const char *canonical_name;
86   struct Strent *canonical_strent;
87
88   struct module *from_internal;
89   struct module *to_internal;
90
91   struct other_conv_list
92   {
93     int dest_idx;
94     struct other_conv
95     {
96       gidx_t module_idx;
97       struct module *module;
98       struct other_conv *next;
99     } other_conv;
100     struct other_conv_list *next;
101   } *other_conv_list;
102 };
103
104
105 /* Name and version of program.  */
106 static void print_version (FILE *stream, struct argp_state *state);
107 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
108
109 /* Short description of program.  */
110 static const char doc[] = N_("\
111 Create fastloading iconv module configuration file.");
112
113 /* Strings for arguments in help texts.  */
114 static const char args_doc[] = N_("[DIR...]");
115
116 /* Prototype for option handler.  */
117 static error_t parse_opt (int key, char *arg, struct argp_state *state);
118
119 /* Function to print some extra text in the help message.  */
120 static char *more_help (int key, const char *text, void *input);
121
122 /* Definitions of arguments for argp functions.  */
123 #define OPT_PREFIX 300
124 static const struct argp_option options[] =
125 {
126   { "prefix", OPT_PREFIX, "PATH", 0, N_("Prefix used for all file accesses") },
127   { NULL, 0, NULL, 0, NULL }
128 };
129
130 /* Data structure to communicate with argp functions.  */
131 static struct argp argp =
132 {
133
134   options, parse_opt, args_doc, doc, NULL, more_help
135 };
136
137
138 /* The function doing the actual work.  */
139 static int handle_dir (const char *dir);
140
141 /* Add all known builtin conversions and aliases.  */
142 static void add_builtins (void);
143
144 /* Create list of all aliases without circular aliases.  */
145 static void get_aliases (void);
146
147 /* Create list of all modules.  */
148 static void get_modules (void);
149
150 /* Get list of all the names and thereby indexing them.  */
151 static void generate_name_list (void);
152
153 /* Collect information about all the names.  */
154 static void generate_name_info (void);
155
156 /* Write the output file.  */
157 static int write_output (void);
158
159
160 /* Prefix to be used for all file accesses.  */
161 static const char *prefix = "";
162 /* Its length.  */
163 static size_t prefix_len;
164
165 /* Search tree of the modules we know.  */
166 static void *modules;
167
168 /* Search tree of the aliases we know.  */
169 static void *aliases;
170
171 /* Search tree for name to index mapping.  */
172 static void *names;
173
174 /* Number of names we know about.  */
175 static int nnames;
176
177 /* List of all aliases.  */
178 static struct alias **alias_list;
179 static size_t nalias_list;
180 static size_t nalias_list_max;
181
182 /* List of all modules.  */
183 static struct module **module_list;
184 static size_t nmodule_list;
185 static size_t nmodule_list_max;
186
187 /* Names and information about them.  */
188 static struct name_info *name_info;
189 static size_t nname_info;
190
191 /* Number of translations not from or to INTERNAL.  */
192 static size_t nextra_modules;
193
194
195 /* Names and aliases for the builtin transformations.  */
196 static struct
197 {
198   const char *from;
199   const char *to;
200 } builtin_alias[] =
201   {
202 #define BUILTIN_ALIAS(alias, real) \
203     { .from = alias, .to = real },
204 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
205                                MinF, MaxF, MinT, MaxT)
206 #include <gconv_builtin.h>
207   };
208 #undef BUILTIN_ALIAS
209 #undef BUILTIN_TRANSFORMATION
210 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
211
212 static struct
213 {
214   const char *from;
215   const char *to;
216   const char *module;
217   int cost;
218 } builtin_trans[] =
219   {
220 #define BUILTIN_ALIAS(alias, real)
221 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
222                                MinF, MaxF, MinT, MaxT) \
223     { .from = From, .to = To, .module = Name, .cost = Cost },
224 #include <gconv_builtin.h>
225   };
226 #undef BUILTIN_ALIAS
227 #undef BUILTIN_TRANSFORMATION
228 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
229
230
231 /* Filename extension for the modules.  */
232 #ifndef MODULE_EXT
233 # define MODULE_EXT ".so"
234 #endif
235 static const char gconv_module_ext[] = MODULE_EXT;
236
237
238 extern void *xmalloc (size_t n) __attribute_malloc__;
239 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
240 extern void *xrealloc (void *p, size_t n);
241
242
243 /* C string table handling.  */
244 struct Strtab;
245 struct Strent;
246
247 /* Create new C string table object in memory.  */
248 extern struct Strtab *strtabinit (void);
249
250 /* Free resources allocated for C string table ST.  */
251 extern void strtabfree (struct Strtab *st);
252
253 /* Add string STR (length LEN is != 0) to C string table ST.  */
254 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
255                                  size_t len);
256
257 /* Finalize string table ST and store size in *SIZE and return a pointer.  */
258 extern void *strtabfinalize (struct Strtab *st, size_t *size);
259
260 /* Get offset in string table for string associated with SE.  */
261 extern size_t strtaboffset (struct Strent *se);
262
263 /* String table we construct.  */
264 static struct Strtab *strtab;
265
266
267
268 int
269 main (int argc, char *argv[])
270 {
271   int remaining;
272   int status = 0;
273   char *path;
274   char *tp;
275
276   /* Enable memory use testing.  */
277   /* mcheck_pedantic (NULL); */
278   mtrace ();
279
280   /* Set locale via LC_ALL.  */
281   setlocale (LC_ALL, "");
282
283   /* Set the text message domain.  */
284   textdomain (_libc_intl_domainname);
285
286   /* Parse and process arguments.  */
287   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
288
289   /* Initialize the string table.  */
290   strtab = strtabinit ();
291
292   /* Handle all directories mentioned.  */
293   while (remaining < argc)
294     status |= handle_dir (argv[remaining++]);
295
296   /* In any case also handle the standard directory.  */
297   path = strdupa (GCONV_PATH);
298   tp = strtok (path, ":");
299   while (tp != NULL)
300     {
301       status |= handle_dir (tp);
302
303       tp = strtok (NULL, ":");
304     }
305
306   /* Add the builtin transformations and aliases without overwriting
307      anything.  */
308   add_builtins ();
309
310   /* Store aliases in an array.  */
311   get_aliases ();
312
313   /* Get list of all modules.  */
314   get_modules ();
315
316   /* Generate list of all the names we know to handle in some way.  */
317   generate_name_list ();
318
319   /* Now we know all the names we will handle, collect information
320      about them.  */
321   generate_name_info ();
322
323   /* Write the output file, but only if we haven't seen any error.  */
324   if (status == 0)
325     status = write_output ();
326   else
327     error (1, 0, _("no output file produced because warning were issued"));
328
329   return status;
330 }
331
332
333 /* Handle program arguments.  */
334 static error_t
335 parse_opt (int key, char *arg, struct argp_state *state)
336 {
337   switch (key)
338     {
339     case OPT_PREFIX:
340       prefix = arg;
341       prefix_len = strlen (prefix);
342       break;
343     default:
344       return ARGP_ERR_UNKNOWN;
345     }
346   return 0;
347 }
348
349
350 static char *
351 more_help (int key, const char *text, void *input)
352 {
353   switch (key)
354     {
355     case ARGP_KEY_HELP_EXTRA:
356       /* We print some extra information.  */
357       return strdup (gettext ("\
358 For bug reporting instructions, please see:\n\
359 <http://www.gnu.org/software/libc/bugs.html>.\n"));
360     default:
361       break;
362     }
363   return (char *) text;
364 }
365
366
367 /* Print the version information.  */
368 static void
369 print_version (FILE *stream, struct argp_state *state)
370 {
371   fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
372   fprintf (stream, gettext ("\
373 Copyright (C) %s Free Software Foundation, Inc.\n\
374 This is free software; see the source for copying conditions.  There is NO\n\
375 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
376 "), "2004");
377   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
378 }
379
380
381 static int
382 alias_compare (const void *p1, const void *p2)
383 {
384   const struct alias *a1 = (const struct alias *) p1;
385   const struct alias *a2 = (const struct alias *) p2;
386
387   return strcmp (a1->fromname, a2->fromname);
388 }
389
390
391 static void
392 new_alias (const char *fromname, size_t fromlen, const char *toname,
393            size_t tolen)
394 {
395   struct alias *newp;
396   void **inserted;
397
398   newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
399
400   newp->fromname = mempcpy (newp->toname, toname, tolen);
401   memcpy (newp->fromname, fromname, fromlen);
402   newp->module = NULL;
403
404   inserted = (void **) tsearch (newp, &aliases, alias_compare);
405   if (inserted == NULL)
406     error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
407   if (*inserted != newp)
408     /* Something went wrong, free this entry.  */
409     free (newp);
410   else
411     {
412       newp->froment = strtabadd (strtab, newp->fromname, fromlen);
413       newp->toent = strtabadd (strtab, newp->toname, tolen);
414     }
415 }
416
417
418 /* Add new alias.  */
419 static void
420 add_alias (char *rp)
421 {
422   /* We now expect two more string.  The strings are normalized
423      (converted to UPPER case) and strored in the alias database.  */
424   char *from;
425   char *to;
426   char *wp;
427
428   while (isspace (*rp))
429     ++rp;
430   from = wp = rp;
431   while (*rp != '\0' && !isspace (*rp))
432     *wp++ = toupper (*rp++);
433   if (*rp == '\0')
434     /* There is no `to' string on the line.  Ignore it.  */
435     return;
436   *wp++ = '\0';
437   to = ++rp;
438   while (isspace (*rp))
439     ++rp;
440   while (*rp != '\0' && !isspace (*rp))
441     *wp++ = toupper (*rp++);
442   if (to == wp)
443     /* No `to' string, ignore the line.  */
444     return;
445   *wp++ = '\0';
446
447   assert (strlen (from) + 1 == (size_t) (to - from));
448   assert (strlen (to) + 1 == (size_t) (wp - to));
449
450   new_alias (from, to - from, to, wp - to);
451 }
452
453
454 static void
455 append_alias (const void *nodep, VISIT value, int level)
456 {
457   if (value != leaf && value != postorder)
458     return;
459
460   if (nalias_list_max == nalias_list)
461     {
462       nalias_list_max += 50;
463       alias_list = (struct alias **) xrealloc (alias_list,
464                                                (nalias_list_max
465                                                 * sizeof (struct alias *)));
466     }
467
468   alias_list[nalias_list++] = *(struct alias **) nodep;
469 }
470
471
472 static void
473 get_aliases (void)
474 {
475   twalk (aliases, append_alias);
476 }
477
478
479 static int
480 module_compare (const void *p1, const void *p2)
481 {
482   const struct module *m1 = (const struct module *) p1;
483   const struct module *m2 = (const struct module *) p2;
484   int result;
485
486   result = strcmp (m1->fromname, m2->fromname);
487   if (result == 0)
488     result = strcmp (m1->toname, m2->toname);
489
490   return result;
491 }
492
493
494 /* Create new module record.  */
495 static void
496 new_module (const char *fromname, size_t fromlen, const char *toname,
497             size_t tolen, const char *directory,
498             const char *filename, size_t filelen, int cost, size_t need_ext)
499 {
500   struct module *new_module;
501   size_t dirlen = strlen (directory) + 1;
502   char *tmp;
503   void **inserted;
504
505   new_module = (struct module *) xmalloc (sizeof (struct module)
506                                           + fromlen + tolen + filelen
507                                           + need_ext);
508
509   new_module->fromname = mempcpy (new_module->toname, toname, tolen);
510
511   new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
512
513   new_module->cost = cost;
514   new_module->next = NULL;
515
516   tmp = mempcpy (new_module->filename, filename, filelen);
517   if (need_ext)
518     {
519       memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
520       filelen += need_ext;
521     }
522   new_module->directory = directory;
523
524   /* Now insert the new module data structure in our search tree.  */
525   inserted = (void **) tsearch (new_module, &modules, module_compare);
526   if (inserted == NULL)
527     error (EXIT_FAILURE, errno, "while inserting in search tree");
528   if (*inserted != new_module)
529     free (new_module);
530   else
531     {
532       new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
533                                                fromlen);
534       new_module->toname_strent = strtabadd (strtab, new_module->toname,
535                                              tolen);
536       new_module->filename_strent = strtabadd (strtab, new_module->filename,
537                                                filelen);
538       new_module->directory_strent = strtabadd (strtab, directory, dirlen);
539     }
540 }
541
542
543 /* Add new module.  */
544 static void
545 internal_function
546 add_module (char *rp, const char *directory)
547 {
548   /* We expect now
549      1. `from' name
550      2. `to' name
551      3. filename of the module
552      4. an optional cost value
553   */
554   char *from;
555   char *to;
556   char *module;
557   char *wp;
558   int need_ext;
559   int cost;
560
561   while (isspace (*rp))
562     ++rp;
563   from = rp;
564   while (*rp != '\0' && !isspace (*rp))
565     {
566       *rp = toupper (*rp);
567       ++rp;
568     }
569   if (*rp == '\0')
570     return;
571   *rp++ = '\0';
572   to = wp = rp;
573   while (isspace (*rp))
574     ++rp;
575   while (*rp != '\0' && !isspace (*rp))
576     *wp++ = toupper (*rp++);
577   if (*rp == '\0')
578     return;
579   *wp++ = '\0';
580   do
581     ++rp;
582   while (isspace (*rp));
583   module = wp;
584   while (*rp != '\0' && !isspace (*rp))
585     *wp++ = *rp++;
586   if (*rp == '\0')
587     {
588       /* There is no cost, use one by default.  */
589       *wp++ = '\0';
590       cost = 1;
591     }
592   else
593     {
594       /* There might be a cost value.  */
595       char *endp;
596
597       *wp++ = '\0';
598       cost = strtol (rp, &endp, 10);
599       if (rp == endp || cost < 1)
600         /* No useful information.  */
601         cost = 1;
602     }
603
604   if (module[0] == '\0')
605     /* No module name given.  */
606     return;
607
608   /* See whether we must add the ending.  */
609   need_ext = 0;
610   if ((size_t) (wp - module) < sizeof (gconv_module_ext)
611       || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
612                  sizeof (gconv_module_ext)) != 0)
613     /* We must add the module extension.  */
614     need_ext = sizeof (gconv_module_ext) - 1;
615
616   assert (strlen (from) + 1 == (size_t) (to - from));
617   assert (strlen (to) + 1 == (size_t) (module - to));
618   assert (strlen (module) + 1 == (size_t) (wp - module));
619
620   new_module (from, to - from, to, module - to, directory, module, wp - module,
621               cost, need_ext);
622 }
623
624
625 /* Read the config file and add the data for this directory to that.  */
626 static int
627 handle_dir (const char *dir)
628 {
629   char *infile;
630   char *cp;
631   FILE *fp;
632   char *line = NULL;
633   size_t linelen = 0;
634   size_t dirlen = strlen (dir);
635
636   if (dir[dirlen - 1] != '/')
637     {
638       char *newp = (char *) xmalloc (dirlen + 2);
639       dir = memcpy (newp, dir, dirlen);
640       newp[dirlen++] = '/';
641       newp[dirlen] = '\0';
642     }
643
644   cp = infile = (char *) alloca (prefix_len + dirlen + sizeof "gconv-modules");
645   if (dir[0] == '/')
646     cp = mempcpy (cp, prefix, prefix_len);
647   strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
648
649   fp = fopen (infile, "r");
650   if (fp == NULL)
651     {
652       error (0, errno, "cannot open `%s'", infile);
653       return 1;
654     }
655
656   /* No threads present.  */
657   __fsetlocking (fp, FSETLOCKING_BYCALLER);
658
659   while (!feof_unlocked (fp))
660     {
661       char *rp, *endp, *word;
662       ssize_t n = __getdelim (&line, &linelen, '\n', fp);
663
664       if (n < 0)
665         /* An error occurred.  */
666         break;
667
668       rp = line;
669       /* Terminate the line (excluding comments or newline) with a NUL
670          byte to simplify the following code.  */
671       endp = strchr (rp, '#');
672       if (endp != NULL)
673         *endp = '\0';
674       else
675         if (rp[n - 1] == '\n')
676           rp[n - 1] = '\0';
677
678       while (isspace (*rp))
679         ++rp;
680
681       /* If this is an empty line go on with the next one.  */
682       if (rp == endp)
683         continue;
684
685       word = rp;
686       while (*rp != '\0' && !isspace (*rp))
687         ++rp;
688
689       if (rp - word == sizeof ("alias") - 1
690           && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
691         add_alias (rp);
692       else if (rp - word == sizeof ("module") - 1
693                && memcmp (word, "module", sizeof ("module") - 1) == 0)
694         add_module (rp, dir);
695       /* else */
696         /* Otherwise ignore the line.  */
697     }
698
699   free (line);
700
701   fclose (fp);
702
703   return 0;
704 }
705
706
707 static void
708 append_module (const void *nodep, VISIT value, int level)
709 {
710   struct module *mo;
711
712   if (value != leaf && value != postorder)
713     return;
714
715   mo = *(struct module **) nodep;
716
717   if (nmodule_list > 0
718       && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
719     {
720       /* Same name.  */
721       mo->next = module_list[nmodule_list - 1];
722       module_list[nmodule_list - 1] = mo;
723
724       return;
725     }
726
727   if (nmodule_list_max == nmodule_list)
728     {
729       nmodule_list_max += 50;
730       module_list = (struct module **) xrealloc (module_list,
731                                                  (nmodule_list_max
732                                                   * sizeof (struct module *)));
733     }
734
735   module_list[nmodule_list++] = mo;
736 }
737
738
739 static void
740 get_modules (void)
741 {
742   twalk (modules, append_module);
743 }
744
745
746 static void
747 add_builtins (void)
748 {
749   size_t cnt;
750
751   /* Add all aliases.  */
752   for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
753     new_alias (builtin_alias[cnt].from,
754                strlen (builtin_alias[cnt].from) + 1,
755                builtin_alias[cnt].to,
756                strlen (builtin_alias[cnt].to) + 1);
757
758   /* add the builtin transformations.  */
759   for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
760     new_module (builtin_trans[cnt].from,
761                 strlen (builtin_trans[cnt].from) + 1,
762                 builtin_trans[cnt].to,
763                 strlen (builtin_trans[cnt].to) + 1,
764                 "", builtin_trans[cnt].module,
765                 strlen (builtin_trans[cnt].module) + 1,
766                 builtin_trans[cnt].cost, 0);
767 }
768
769
770 static int
771 name_compare (const void *p1, const void *p2)
772 {
773   const struct name *n1 = (const struct name *) p1;
774   const struct name *n2 = (const struct name *) p2;
775
776   return strcmp (n1->name, n2->name);
777 }
778
779
780 static struct name *
781 new_name (const char *str, struct Strent *strent)
782 {
783   struct name *newp = (struct name *) xmalloc (sizeof (struct name));
784
785   newp->name = str;
786   newp->strent = strent;
787   newp->module_idx = -1;
788   newp->hashval = __hash_string (str);
789
790   ++nnames;
791
792   return newp;
793 }
794
795
796 static void
797 generate_name_list (void)
798 {
799   size_t i;
800
801   /* A name we always need.  */
802   tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
803                                             sizeof ("INTERNAL"))),
804            &names, name_compare);
805
806   for (i = 0; i < nmodule_list; ++i)
807     {
808       struct module *runp;
809
810       if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
811         tsearch (new_name (module_list[i]->fromname,
812                            module_list[i]->fromname_strent),
813                  &names, name_compare);
814
815       for (runp = module_list[i]; runp != NULL; runp = runp->next)
816         if (strcmp (runp->toname, "INTERNAL") != 0)
817           tsearch (new_name (runp->toname, runp->toname_strent),
818                    &names, name_compare);
819     }
820 }
821
822
823 static int
824 name_to_module_idx (const char *name, int add)
825 {
826   struct name **res;
827   struct name fake_name = { .name = name };
828   int idx;
829
830   res = (struct name **) tfind (&fake_name, &names, name_compare);
831   if (res == NULL)
832     abort ();
833
834   idx = (*res)->module_idx;
835   if (idx == -1 && add)
836     /* No module index assigned yet.  */
837     idx = (*res)->module_idx = nname_info++;
838
839   return idx;
840 }
841
842
843 static void
844 generate_name_info (void)
845 {
846   size_t i;
847   int idx;
848
849   name_info = (struct name_info *) xcalloc (nmodule_list + 1,
850                                             sizeof (struct name_info));
851
852   /* First add a special entry for the INTERNAL name.  This must have
853      index zero.  */
854   idx = name_to_module_idx ("INTERNAL", 1);
855   name_info[0].canonical_name = "INTERNAL";
856   name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
857                                              sizeof ("INTERNAL"));
858   assert (nname_info == 1);
859
860   for (i = 0; i < nmodule_list; ++i)
861     {
862       struct module *runp;
863
864       for (runp = module_list[i]; runp != NULL; runp = runp->next)
865         if (strcmp (runp->fromname, "INTERNAL") == 0)
866           {
867             idx = name_to_module_idx (runp->toname, 1);
868             name_info[idx].from_internal = runp;
869             assert (name_info[idx].canonical_name == NULL
870                     || strcmp (name_info[idx].canonical_name,
871                                runp->toname) == 0);
872             name_info[idx].canonical_name = runp->toname;
873             name_info[idx].canonical_strent = runp->toname_strent;
874           }
875         else if (strcmp (runp->toname, "INTERNAL") == 0)
876           {
877             idx = name_to_module_idx (runp->fromname, 1);
878             name_info[idx].to_internal = runp;
879             assert (name_info[idx].canonical_name == NULL
880                     || strcmp (name_info[idx].canonical_name,
881                                runp->fromname) == 0);
882             name_info[idx].canonical_name = runp->fromname;
883             name_info[idx].canonical_strent = runp->fromname_strent;
884           }
885         else
886           {
887             /* This is a transformation not to or from the INTERNAL
888                encoding.  */
889             int from_idx = name_to_module_idx (runp->fromname, 1);
890             int to_idx = name_to_module_idx (runp->toname, 1);
891             struct other_conv_list *newp;
892
893             newp = (struct other_conv_list *)
894               xmalloc (sizeof (struct other_conv_list));
895             newp->other_conv.module_idx = to_idx;
896             newp->other_conv.module = runp;
897             newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
898             newp->dest_idx = to_idx;
899             newp->next = name_info[from_idx].other_conv_list;
900             name_info[from_idx].other_conv_list = newp;
901             assert (name_info[from_idx].canonical_name == NULL
902                     || strcmp (name_info[from_idx].canonical_name,
903                                runp->fromname) == 0);
904             name_info[from_idx].canonical_name = runp->fromname;
905             name_info[from_idx].canonical_strent = runp->fromname_strent;
906
907             ++nextra_modules;
908           }
909     }
910
911   /* Now add the module index information for all the aliases.  */
912   for (i = 0; i < nalias_list; ++i)
913     {
914       struct name fake_name = { .name = alias_list[i]->toname };
915       struct name **tonamep;
916
917       tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
918       if (tonamep != NULL)
919         {
920           struct name *newp = new_name (alias_list[i]->fromname,
921                                         alias_list[i]->froment);
922           newp->module_idx = (*tonamep)->module_idx;
923           tsearch (newp, &names, name_compare);
924         }
925     }
926 }
927
928
929 static int
930 is_prime (unsigned long int candidate)
931 {
932   /* No even number and none less than 10 will be passed here.  */
933   unsigned long int divn = 3;
934   unsigned long int sq = divn * divn;
935
936   while (sq < candidate && candidate % divn != 0)
937     {
938       ++divn;
939       sq += 4 * divn;
940       ++divn;
941     }
942
943   return candidate % divn != 0;
944 }
945
946
947 static uint32_t
948 next_prime (uint32_t seed)
949 {
950   /* Make it definitely odd.  */
951   seed |= 1;
952
953   while (!is_prime (seed))
954     seed += 2;
955
956   return seed;
957 }
958
959
960 /* Format of the output file.
961
962    Offset   Length       Description
963    0000     4            Magic header bytes
964    0004     4            Offset of string table (stoff)
965    0008     4            Offset of name hashing table (hoff)
966    000C     4            Hashing table size (hsize)
967    0010     4            Offset of module table (moff)
968    0014     4            Offset of other conversion module table (ooff)
969
970    stoff    ???          String table
971
972    hoff     8*hsize      Array of tuples
973                             string table offset
974                             module index
975
976    moff     ???          Array of tuples
977                             canonical name offset
978                             from-internal module dir name offset
979                             from-internal module name off
980                             to-internal module dir name offset
981                             to-internal module name offset
982                             offset into other conversion table
983
984    ooff     ???          One or more of
985                             number of steps/modules
986                             one or more of tuple
987                               canonical name offset for output
988                               module dir name offset
989                               module name offset
990                          (following last entry with step count 0)
991 */
992 static int
993 write_output (void)
994 {
995   int fd;
996   char *string_table;
997   size_t string_table_size;
998   struct gconvcache_header header;
999   struct hash_entry *hash_table;
1000   size_t hash_size;
1001   struct module_entry *module_table;
1002   char *extra_table;
1003   char *cur_extra_table;
1004   size_t n;
1005   int idx;
1006   struct iovec iov[6];
1007   static const gidx_t null_word;
1008   size_t total;
1009   char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1010                 + strlen (".XXXXXX")];
1011   char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1012
1013   /* Function to insert the names.  */
1014   static void name_insert (const void *nodep, VISIT value, int level)
1015     {
1016       struct name *name;
1017       unsigned int idx;
1018       unsigned int hval2;
1019
1020       if (value != leaf && value != postorder)
1021         return;
1022
1023       name = *(struct name **) nodep;
1024       idx = name->hashval % hash_size;
1025       hval2 = 1 + name->hashval % (hash_size - 2);
1026
1027       while (hash_table[idx].string_offset != 0)
1028         if ((idx += hval2) >= hash_size)
1029           idx -= hash_size;
1030
1031       hash_table[idx].string_offset = strtaboffset (name->strent);
1032
1033       assert (name->module_idx != -1);
1034       hash_table[idx].module_idx = name->module_idx;
1035     }
1036
1037   /* Open the output file.  */
1038   assert (GCONV_MODULES_CACHE[0] == '/');
1039   strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1040           ".XXXXXX");
1041   fd = mkstemp (tmpfname);
1042   if (fd == -1)
1043     return 1;
1044
1045   strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1046
1047   /* Create the string table.  */
1048   string_table = strtabfinalize (strtab, &string_table_size);
1049
1050   /* Create the hashing table.  We know how many strings we have.
1051      Creating a perfect hash table is not reasonable here.  Therefore
1052      we use open hashing and a table size which is the next prime 40%
1053      larger than the number of strings.  */
1054   hash_size = next_prime (nnames * 1.4);
1055   hash_table = (struct hash_entry *) xcalloc (hash_size,
1056                                               sizeof (struct hash_entry));
1057   /* Fill the hash table.  */
1058   twalk (names, name_insert);
1059
1060   /* Create the section for the module list.  */
1061   module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1062                                                   nname_info);
1063
1064   /* Allocate memory for the non-INTERNAL conversions.  The allocated
1065      memory can be more than is actually needed.  */
1066   extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1067                                   + sizeof (gidx_t)
1068                                   + sizeof (struct extra_entry_module),
1069                                   nextra_modules);
1070   cur_extra_table = extra_table;
1071
1072   /* Fill in the module information.  */
1073   for (n = 0; n < nname_info; ++n)
1074     {
1075       module_table[n].canonname_offset =
1076         strtaboffset (name_info[n].canonical_strent);
1077
1078       if (name_info[n].from_internal == NULL)
1079         {
1080           module_table[n].fromdir_offset = 0;
1081           module_table[n].fromname_offset = 0;
1082         }
1083       else
1084         {
1085           module_table[n].fromdir_offset =
1086             strtaboffset (name_info[n].from_internal->directory_strent);
1087           module_table[n].fromname_offset =
1088             strtaboffset (name_info[n].from_internal->filename_strent);
1089         }
1090
1091       if (name_info[n].to_internal == NULL)
1092         {
1093           module_table[n].todir_offset = 0;
1094           module_table[n].toname_offset = 0;
1095         }
1096       else
1097         {
1098           module_table[n].todir_offset =
1099             strtaboffset (name_info[n].to_internal->directory_strent);
1100           module_table[n].toname_offset =
1101             strtaboffset (name_info[n].to_internal->filename_strent);
1102         }
1103
1104       if (name_info[n].other_conv_list != NULL)
1105         {
1106           struct other_conv_list *other = name_info[n].other_conv_list;
1107
1108           /* Store the reference.  We add 1 to distinguish the entry
1109              at offset zero from the case where no extra modules are
1110              available.  The file reader has to account for the
1111              offset.  */
1112           module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1113
1114           do
1115             {
1116               struct other_conv *runp;
1117               struct extra_entry *extra;
1118
1119               /* Allocate new entry.  */
1120               extra = (struct extra_entry *) cur_extra_table;
1121               cur_extra_table += sizeof (struct extra_entry);
1122               extra->module_cnt = 0;
1123
1124               runp = &other->other_conv;
1125               do
1126                 {
1127                   cur_extra_table += sizeof (struct extra_entry_module);
1128                   extra->module[extra->module_cnt].outname_offset =
1129                     runp->next == NULL
1130                     ? other->dest_idx : runp->next->module_idx;
1131                   extra->module[extra->module_cnt].dir_offset =
1132                     strtaboffset (runp->module->directory_strent);
1133                   extra->module[extra->module_cnt].name_offset =
1134                     strtaboffset (runp->module->filename_strent);
1135                   ++extra->module_cnt;
1136
1137                   runp = runp->next;
1138                 }
1139               while (runp != NULL);
1140
1141               other = other->next;
1142             }
1143           while (other != NULL);
1144
1145           /* Final module_cnt is zero.  */
1146           *((gidx_t *) cur_extra_table) = 0;
1147           cur_extra_table += sizeof (gidx_t);
1148         }
1149     }
1150
1151   header.magic = GCONVCACHE_MAGIC;
1152
1153   iov[0].iov_base = &header;
1154   iov[0].iov_len = sizeof (struct gconvcache_header);
1155   total = iov[0].iov_len;
1156
1157   header.string_offset = total;
1158   iov[1].iov_base = string_table;
1159   iov[1].iov_len = string_table_size;
1160   total += iov[1].iov_len;
1161
1162   idx = 2;
1163   if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1164     {
1165       iov[2].iov_base = (void *) &null_word;
1166       iov[2].iov_len = (sizeof (gidx_t)
1167                         - (string_table_size & (sizeof (gidx_t) - 1)));
1168       total += iov[2].iov_len;
1169       ++idx;
1170     }
1171
1172   header.hash_offset = total;
1173   header.hash_size = hash_size;
1174   iov[idx].iov_base = hash_table;
1175   iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1176   total += iov[idx].iov_len;
1177   ++idx;
1178
1179   header.module_offset = total;
1180   iov[idx].iov_base = module_table;
1181   iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1182   total += iov[idx].iov_len;
1183   ++idx;
1184
1185   assert ((size_t) (cur_extra_table - extra_table)
1186           <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1187                + sizeof (struct extra_entry_module))
1188               * nextra_modules));
1189   header.otherconv_offset = total;
1190   iov[idx].iov_base = extra_table;
1191   iov[idx].iov_len = cur_extra_table - extra_table;
1192   total += iov[idx].iov_len;
1193   ++idx;
1194
1195   if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1196       /* The file was created with mode 0600.  Make it world-readable.  */
1197       || fchmod (fd, 0644) != 0
1198       /* Rename the file, possibly replacing an old one.  */
1199       || rename (tmpfname, finalname) != 0)
1200     {
1201       int save_errno = errno;
1202       close (fd);
1203       unlink (tmpfname);
1204       error (EXIT_FAILURE, save_errno,
1205              gettext ("cannot generate output file"));
1206     }
1207
1208   close (fd);
1209
1210   return 0;
1211 }