10c0d7e6f13682e1a23a004f18251d7b9e8ba6d6
[platform/upstream/linaro-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 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
359     default:
360       break;
361     }
362   return (char *) text;
363 }
364
365
366 /* Print the version information.  */
367 static void
368 print_version (FILE *stream, struct argp_state *state)
369 {
370   fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
371   fprintf (stream, gettext ("\
372 Copyright (C) %s Free Software Foundation, Inc.\n\
373 This is free software; see the source for copying conditions.  There is NO\n\
374 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
375 "), "2004");
376   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
377 }
378
379
380 static int
381 alias_compare (const void *p1, const void *p2)
382 {
383   const struct alias *a1 = (const struct alias *) p1;
384   const struct alias *a2 = (const struct alias *) p2;
385
386   return strcmp (a1->fromname, a2->fromname);
387 }
388
389
390 static void
391 new_alias (const char *fromname, size_t fromlen, const char *toname,
392            size_t tolen)
393 {
394   struct alias *newp;
395   void **inserted;
396
397   newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
398
399   newp->fromname = mempcpy (newp->toname, toname, tolen);
400   memcpy (newp->fromname, fromname, fromlen);
401   newp->module = NULL;
402
403   inserted = (void **) tsearch (newp, &aliases, alias_compare);
404   if (inserted == NULL)
405     error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
406   if (*inserted != newp)
407     /* Something went wrong, free this entry.  */
408     free (newp);
409   else
410     {
411       newp->froment = strtabadd (strtab, newp->fromname, fromlen);
412       newp->toent = strtabadd (strtab, newp->toname, tolen);
413     }
414 }
415
416
417 /* Add new alias.  */
418 static void
419 add_alias (char *rp)
420 {
421   /* We now expect two more string.  The strings are normalized
422      (converted to UPPER case) and strored in the alias database.  */
423   char *from;
424   char *to;
425   char *wp;
426
427   while (isspace (*rp))
428     ++rp;
429   from = wp = rp;
430   while (*rp != '\0' && !isspace (*rp))
431     *wp++ = toupper (*rp++);
432   if (*rp == '\0')
433     /* There is no `to' string on the line.  Ignore it.  */
434     return;
435   *wp++ = '\0';
436   to = ++rp;
437   while (isspace (*rp))
438     ++rp;
439   while (*rp != '\0' && !isspace (*rp))
440     *wp++ = toupper (*rp++);
441   if (to == wp)
442     /* No `to' string, ignore the line.  */
443     return;
444   *wp++ = '\0';
445
446   assert (strlen (from) + 1 == (size_t) (to - from));
447   assert (strlen (to) + 1 == (size_t) (wp - to));
448
449   new_alias (from, to - from, to, wp - to);
450 }
451
452
453 static void
454 append_alias (const void *nodep, VISIT value, int level)
455 {
456   if (value != leaf && value != postorder)
457     return;
458
459   if (nalias_list_max == nalias_list)
460     {
461       nalias_list_max += 50;
462       alias_list = (struct alias **) xrealloc (alias_list,
463                                                (nalias_list_max
464                                                 * sizeof (struct alias *)));
465     }
466
467   alias_list[nalias_list++] = *(struct alias **) nodep;
468 }
469
470
471 static void
472 get_aliases (void)
473 {
474   twalk (aliases, append_alias);
475 }
476
477
478 static int
479 module_compare (const void *p1, const void *p2)
480 {
481   const struct module *m1 = (const struct module *) p1;
482   const struct module *m2 = (const struct module *) p2;
483   int result;
484
485   result = strcmp (m1->fromname, m2->fromname);
486   if (result == 0)
487     result = strcmp (m1->toname, m2->toname);
488
489   return result;
490 }
491
492
493 /* Create new module record.  */
494 static void
495 new_module (const char *fromname, size_t fromlen, const char *toname,
496             size_t tolen, const char *directory,
497             const char *filename, size_t filelen, int cost, size_t need_ext)
498 {
499   struct module *new_module;
500   size_t dirlen = strlen (directory) + 1;
501   char *tmp;
502   void **inserted;
503
504   new_module = (struct module *) xmalloc (sizeof (struct module)
505                                           + fromlen + tolen + filelen
506                                           + need_ext);
507
508   new_module->fromname = mempcpy (new_module->toname, toname, tolen);
509
510   new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
511
512   new_module->cost = cost;
513   new_module->next = NULL;
514
515   tmp = mempcpy (new_module->filename, filename, filelen);
516   if (need_ext)
517     {
518       memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
519       filelen += need_ext;
520     }
521   new_module->directory = directory;
522
523   /* Now insert the new module data structure in our search tree.  */
524   inserted = (void **) tsearch (new_module, &modules, module_compare);
525   if (inserted == NULL)
526     error (EXIT_FAILURE, errno, "while inserting in search tree");
527   if (*inserted != new_module)
528     free (new_module);
529   else
530     {
531       new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
532                                                fromlen);
533       new_module->toname_strent = strtabadd (strtab, new_module->toname,
534                                              tolen);
535       new_module->filename_strent = strtabadd (strtab, new_module->filename,
536                                                filelen);
537       new_module->directory_strent = strtabadd (strtab, directory, dirlen);
538     }
539 }
540
541
542 /* Add new module.  */
543 static void
544 internal_function
545 add_module (char *rp, const char *directory)
546 {
547   /* We expect now
548      1. `from' name
549      2. `to' name
550      3. filename of the module
551      4. an optional cost value
552   */
553   char *from;
554   char *to;
555   char *module;
556   char *wp;
557   int need_ext;
558   int cost;
559
560   while (isspace (*rp))
561     ++rp;
562   from = rp;
563   while (*rp != '\0' && !isspace (*rp))
564     {
565       *rp = toupper (*rp);
566       ++rp;
567     }
568   if (*rp == '\0')
569     return;
570   *rp++ = '\0';
571   to = wp = rp;
572   while (isspace (*rp))
573     ++rp;
574   while (*rp != '\0' && !isspace (*rp))
575     *wp++ = toupper (*rp++);
576   if (*rp == '\0')
577     return;
578   *wp++ = '\0';
579   do
580     ++rp;
581   while (isspace (*rp));
582   module = wp;
583   while (*rp != '\0' && !isspace (*rp))
584     *wp++ = *rp++;
585   if (*rp == '\0')
586     {
587       /* There is no cost, use one by default.  */
588       *wp++ = '\0';
589       cost = 1;
590     }
591   else
592     {
593       /* There might be a cost value.  */
594       char *endp;
595
596       *wp++ = '\0';
597       cost = strtol (rp, &endp, 10);
598       if (rp == endp || cost < 1)
599         /* No useful information.  */
600         cost = 1;
601     }
602
603   if (module[0] == '\0')
604     /* No module name given.  */
605     return;
606
607   /* See whether we must add the ending.  */
608   need_ext = 0;
609   if ((size_t) (wp - module) < sizeof (gconv_module_ext)
610       || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
611                  sizeof (gconv_module_ext)) != 0)
612     /* We must add the module extension.  */
613     need_ext = sizeof (gconv_module_ext) - 1;
614
615   assert (strlen (from) + 1 == (size_t) (to - from));
616   assert (strlen (to) + 1 == (size_t) (module - to));
617   assert (strlen (module) + 1 == (size_t) (wp - module));
618
619   new_module (from, to - from, to, module - to, directory, module, wp - module,
620               cost, need_ext);
621 }
622
623
624 /* Read the config file and add the data for this directory to that.  */
625 static int
626 handle_dir (const char *dir)
627 {
628   char *infile;
629   char *cp;
630   FILE *fp;
631   char *line = NULL;
632   size_t linelen = 0;
633   size_t dirlen = strlen (dir);
634
635   if (dir[dirlen - 1] != '/')
636     {
637       char *newp = (char *) xmalloc (dirlen + 2);
638       dir = memcpy (newp, dir, dirlen);
639       newp[dirlen++] = '/';
640       newp[dirlen] = '\0';
641     }
642
643   cp = infile = (char *) alloca (prefix_len + dirlen + sizeof "gconv-modules");
644   if (dir[0] == '/')
645     cp = mempcpy (cp, prefix, prefix_len);
646   strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
647
648   fp = fopen (infile, "r");
649   if (fp == NULL)
650     {
651       error (0, errno, "cannot open `%s'", infile);
652       return 1;
653     }
654
655   /* No threads present.  */
656   __fsetlocking (fp, FSETLOCKING_BYCALLER);
657
658   while (!feof_unlocked (fp))
659     {
660       char *rp, *endp, *word;
661       ssize_t n = __getdelim (&line, &linelen, '\n', fp);
662
663       if (n < 0)
664         /* An error occurred.  */
665         break;
666
667       rp = line;
668       /* Terminate the line (excluding comments or newline) with a NUL
669          byte to simplify the following code.  */
670       endp = strchr (rp, '#');
671       if (endp != NULL)
672         *endp = '\0';
673       else
674         if (rp[n - 1] == '\n')
675           rp[n - 1] = '\0';
676
677       while (isspace (*rp))
678         ++rp;
679
680       /* If this is an empty line go on with the next one.  */
681       if (rp == endp)
682         continue;
683
684       word = rp;
685       while (*rp != '\0' && !isspace (*rp))
686         ++rp;
687
688       if (rp - word == sizeof ("alias") - 1
689           && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
690         add_alias (rp);
691       else if (rp - word == sizeof ("module") - 1
692                && memcmp (word, "module", sizeof ("module") - 1) == 0)
693         add_module (rp, dir);
694       /* else */
695         /* Otherwise ignore the line.  */
696     }
697
698   free (line);
699
700   fclose (fp);
701
702   return 0;
703 }
704
705
706 static void
707 append_module (const void *nodep, VISIT value, int level)
708 {
709   struct module *mo;
710
711   if (value != leaf && value != postorder)
712     return;
713
714   mo = *(struct module **) nodep;
715
716   if (nmodule_list > 0
717       && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
718     {
719       /* Same name.  */
720       mo->next = module_list[nmodule_list - 1];
721       module_list[nmodule_list - 1] = mo;
722
723       return;
724     }
725
726   if (nmodule_list_max == nmodule_list)
727     {
728       nmodule_list_max += 50;
729       module_list = (struct module **) xrealloc (module_list,
730                                                  (nmodule_list_max
731                                                   * sizeof (struct module *)));
732     }
733
734   module_list[nmodule_list++] = mo;
735 }
736
737
738 static void
739 get_modules (void)
740 {
741   twalk (modules, append_module);
742 }
743
744
745 static void
746 add_builtins (void)
747 {
748   size_t cnt;
749
750   /* Add all aliases.  */
751   for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
752     new_alias (builtin_alias[cnt].from,
753                strlen (builtin_alias[cnt].from) + 1,
754                builtin_alias[cnt].to,
755                strlen (builtin_alias[cnt].to) + 1);
756
757   /* add the builtin transformations.  */
758   for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
759     new_module (builtin_trans[cnt].from,
760                 strlen (builtin_trans[cnt].from) + 1,
761                 builtin_trans[cnt].to,
762                 strlen (builtin_trans[cnt].to) + 1,
763                 "", builtin_trans[cnt].module,
764                 strlen (builtin_trans[cnt].module) + 1,
765                 builtin_trans[cnt].cost, 0);
766 }
767
768
769 static int
770 name_compare (const void *p1, const void *p2)
771 {
772   const struct name *n1 = (const struct name *) p1;
773   const struct name *n2 = (const struct name *) p2;
774
775   return strcmp (n1->name, n2->name);
776 }
777
778
779 static struct name *
780 new_name (const char *str, struct Strent *strent)
781 {
782   struct name *newp = (struct name *) xmalloc (sizeof (struct name));
783
784   newp->name = str;
785   newp->strent = strent;
786   newp->module_idx = -1;
787   newp->hashval = __hash_string (str);
788
789   ++nnames;
790
791   return newp;
792 }
793
794
795 static void
796 generate_name_list (void)
797 {
798   size_t i;
799
800   /* A name we always need.  */
801   tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
802                                             sizeof ("INTERNAL"))),
803            &names, name_compare);
804
805   for (i = 0; i < nmodule_list; ++i)
806     {
807       struct module *runp;
808
809       if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
810         tsearch (new_name (module_list[i]->fromname,
811                            module_list[i]->fromname_strent),
812                  &names, name_compare);
813
814       for (runp = module_list[i]; runp != NULL; runp = runp->next)
815         if (strcmp (runp->toname, "INTERNAL") != 0)
816           tsearch (new_name (runp->toname, runp->toname_strent),
817                    &names, name_compare);
818     }
819 }
820
821
822 static int
823 name_to_module_idx (const char *name, int add)
824 {
825   struct name **res;
826   struct name fake_name = { .name = name };
827   int idx;
828
829   res = (struct name **) tfind (&fake_name, &names, name_compare);
830   if (res == NULL)
831     abort ();
832
833   idx = (*res)->module_idx;
834   if (idx == -1 && add)
835     /* No module index assigned yet.  */
836     idx = (*res)->module_idx = nname_info++;
837
838   return idx;
839 }
840
841
842 static void
843 generate_name_info (void)
844 {
845   size_t i;
846   int idx;
847
848   name_info = (struct name_info *) xcalloc (nmodule_list + 1,
849                                             sizeof (struct name_info));
850
851   /* First add a special entry for the INTERNAL name.  This must have
852      index zero.  */
853   idx = name_to_module_idx ("INTERNAL", 1);
854   name_info[0].canonical_name = "INTERNAL";
855   name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
856                                              sizeof ("INTERNAL"));
857   assert (nname_info == 1);
858
859   for (i = 0; i < nmodule_list; ++i)
860     {
861       struct module *runp;
862
863       for (runp = module_list[i]; runp != NULL; runp = runp->next)
864         if (strcmp (runp->fromname, "INTERNAL") == 0)
865           {
866             idx = name_to_module_idx (runp->toname, 1);
867             name_info[idx].from_internal = runp;
868             assert (name_info[idx].canonical_name == NULL
869                     || strcmp (name_info[idx].canonical_name,
870                                runp->toname) == 0);
871             name_info[idx].canonical_name = runp->toname;
872             name_info[idx].canonical_strent = runp->toname_strent;
873           }
874         else if (strcmp (runp->toname, "INTERNAL") == 0)
875           {
876             idx = name_to_module_idx (runp->fromname, 1);
877             name_info[idx].to_internal = runp;
878             assert (name_info[idx].canonical_name == NULL
879                     || strcmp (name_info[idx].canonical_name,
880                                runp->fromname) == 0);
881             name_info[idx].canonical_name = runp->fromname;
882             name_info[idx].canonical_strent = runp->fromname_strent;
883           }
884         else
885           {
886             /* This is a transformation not to or from the INTERNAL
887                encoding.  */
888             int from_idx = name_to_module_idx (runp->fromname, 1);
889             int to_idx = name_to_module_idx (runp->toname, 1);
890             struct other_conv_list *newp;
891
892             newp = (struct other_conv_list *)
893               xmalloc (sizeof (struct other_conv_list));
894             newp->other_conv.module_idx = to_idx;
895             newp->other_conv.module = runp;
896             newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
897             newp->dest_idx = to_idx;
898             newp->next = name_info[from_idx].other_conv_list;
899             name_info[from_idx].other_conv_list = newp;
900             assert (name_info[from_idx].canonical_name == NULL
901                     || strcmp (name_info[from_idx].canonical_name,
902                                runp->fromname) == 0);
903             name_info[from_idx].canonical_name = runp->fromname;
904             name_info[from_idx].canonical_strent = runp->fromname_strent;
905
906             ++nextra_modules;
907           }
908     }
909
910   /* Now add the module index information for all the aliases.  */
911   for (i = 0; i < nalias_list; ++i)
912     {
913       struct name fake_name = { .name = alias_list[i]->toname };
914       struct name **tonamep;
915
916       tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
917       if (tonamep != NULL)
918         {
919           struct name *newp = new_name (alias_list[i]->fromname,
920                                         alias_list[i]->froment);
921           newp->module_idx = (*tonamep)->module_idx;
922           tsearch (newp, &names, name_compare);
923         }
924     }
925 }
926
927
928 static int
929 is_prime (unsigned long int candidate)
930 {
931   /* No even number and none less than 10 will be passed here.  */
932   unsigned long int divn = 3;
933   unsigned long int sq = divn * divn;
934
935   while (sq < candidate && candidate % divn != 0)
936     {
937       ++divn;
938       sq += 4 * divn;
939       ++divn;
940     }
941
942   return candidate % divn != 0;
943 }
944
945
946 static uint32_t
947 next_prime (uint32_t seed)
948 {
949   /* Make it definitely odd.  */
950   seed |= 1;
951
952   while (!is_prime (seed))
953     seed += 2;
954
955   return seed;
956 }
957
958
959 /* Format of the output file.
960
961    Offset   Length       Description
962    0000     4            Magic header bytes
963    0004     4            Offset of string table (stoff)
964    0008     4            Offset of name hashing table (hoff)
965    000C     4            Hashing table size (hsize)
966    0010     4            Offset of module table (moff)
967    0014     4            Offset of other conversion module table (ooff)
968
969    stoff    ???          String table
970
971    hoff     8*hsize      Array of tuples
972                             string table offset
973                             module index
974
975    moff     ???          Array of tuples
976                             canonical name offset
977                             from-internal module dir name offset
978                             from-internal module name off
979                             to-internal module dir name offset
980                             to-internal module name offset
981                             offset into other conversion table
982
983    ooff     ???          One or more of
984                             number of steps/modules
985                             one or more of tuple
986                               canonical name offset for output
987                               module dir name offset
988                               module name offset
989                          (following last entry with step count 0)
990 */
991 static int
992 write_output (void)
993 {
994   int fd;
995   char *string_table;
996   size_t string_table_size;
997   struct gconvcache_header header;
998   struct hash_entry *hash_table;
999   size_t hash_size;
1000   struct module_entry *module_table;
1001   char *extra_table;
1002   char *cur_extra_table;
1003   size_t n;
1004   int idx;
1005   struct iovec iov[6];
1006   static const gidx_t null_word;
1007   size_t total;
1008   char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1009                 + strlen (".XXXXXX")];
1010   char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1011
1012   /* Function to insert the names.  */
1013   static void name_insert (const void *nodep, VISIT value, int level)
1014     {
1015       struct name *name;
1016       unsigned int idx;
1017       unsigned int hval2;
1018
1019       if (value != leaf && value != postorder)
1020         return;
1021
1022       name = *(struct name **) nodep;
1023       idx = name->hashval % hash_size;
1024       hval2 = 1 + name->hashval % (hash_size - 2);
1025
1026       while (hash_table[idx].string_offset != 0)
1027         if ((idx += hval2) >= hash_size)
1028           idx -= hash_size;
1029
1030       hash_table[idx].string_offset = strtaboffset (name->strent);
1031
1032       assert (name->module_idx != -1);
1033       hash_table[idx].module_idx = name->module_idx;
1034     }
1035
1036   /* Open the output file.  */
1037   assert (GCONV_MODULES_CACHE[0] == '/');
1038   strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1039           ".XXXXXX");
1040   fd = mkstemp (tmpfname);
1041   if (fd == -1)
1042     return 1;
1043
1044   strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1045
1046   /* Create the string table.  */
1047   string_table = strtabfinalize (strtab, &string_table_size);
1048
1049   /* Create the hashing table.  We know how many strings we have.
1050      Creating a perfect hash table is not reasonable here.  Therefore
1051      we use open hashing and a table size which is the next prime 40%
1052      larger than the number of strings.  */
1053   hash_size = next_prime (nnames * 1.4);
1054   hash_table = (struct hash_entry *) xcalloc (hash_size,
1055                                               sizeof (struct hash_entry));
1056   /* Fill the hash table.  */
1057   twalk (names, name_insert);
1058
1059   /* Create the section for the module list.  */
1060   module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1061                                                   nname_info);
1062
1063   /* Allocate memory for the non-INTERNAL conversions.  The allocated
1064      memory can be more than is actually needed.  */
1065   extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1066                                   + sizeof (gidx_t)
1067                                   + sizeof (struct extra_entry_module),
1068                                   nextra_modules);
1069   cur_extra_table = extra_table;
1070
1071   /* Fill in the module information.  */
1072   for (n = 0; n < nname_info; ++n)
1073     {
1074       module_table[n].canonname_offset =
1075         strtaboffset (name_info[n].canonical_strent);
1076
1077       if (name_info[n].from_internal == NULL)
1078         {
1079           module_table[n].fromdir_offset = 0;
1080           module_table[n].fromname_offset = 0;
1081         }
1082       else
1083         {
1084           module_table[n].fromdir_offset =
1085             strtaboffset (name_info[n].from_internal->directory_strent);
1086           module_table[n].fromname_offset =
1087             strtaboffset (name_info[n].from_internal->filename_strent);
1088         }
1089
1090       if (name_info[n].to_internal == NULL)
1091         {
1092           module_table[n].todir_offset = 0;
1093           module_table[n].toname_offset = 0;
1094         }
1095       else
1096         {
1097           module_table[n].todir_offset =
1098             strtaboffset (name_info[n].to_internal->directory_strent);
1099           module_table[n].toname_offset =
1100             strtaboffset (name_info[n].to_internal->filename_strent);
1101         }
1102
1103       if (name_info[n].other_conv_list != NULL)
1104         {
1105           struct other_conv_list *other = name_info[n].other_conv_list;
1106
1107           /* Store the reference.  We add 1 to distinguish the entry
1108              at offset zero from the case where no extra modules are
1109              available.  The file reader has to account for the
1110              offset.  */
1111           module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1112
1113           do
1114             {
1115               struct other_conv *runp;
1116               struct extra_entry *extra;
1117
1118               /* Allocate new entry.  */
1119               extra = (struct extra_entry *) cur_extra_table;
1120               cur_extra_table += sizeof (struct extra_entry);
1121               extra->module_cnt = 0;
1122
1123               runp = &other->other_conv;
1124               do
1125                 {
1126                   cur_extra_table += sizeof (struct extra_entry_module);
1127                   extra->module[extra->module_cnt].outname_offset =
1128                     runp->next == NULL
1129                     ? other->dest_idx : runp->next->module_idx;
1130                   extra->module[extra->module_cnt].dir_offset =
1131                     strtaboffset (runp->module->directory_strent);
1132                   extra->module[extra->module_cnt].name_offset =
1133                     strtaboffset (runp->module->filename_strent);
1134                   ++extra->module_cnt;
1135
1136                   runp = runp->next;
1137                 }
1138               while (runp != NULL);
1139
1140               other = other->next;
1141             }
1142           while (other != NULL);
1143
1144           /* Final module_cnt is zero.  */
1145           *((gidx_t *) cur_extra_table) = 0;
1146           cur_extra_table += sizeof (gidx_t);
1147         }
1148     }
1149
1150   header.magic = GCONVCACHE_MAGIC;
1151
1152   iov[0].iov_base = &header;
1153   iov[0].iov_len = sizeof (struct gconvcache_header);
1154   total = iov[0].iov_len;
1155
1156   header.string_offset = total;
1157   iov[1].iov_base = string_table;
1158   iov[1].iov_len = string_table_size;
1159   total += iov[1].iov_len;
1160
1161   idx = 2;
1162   if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1163     {
1164       iov[2].iov_base = (void *) &null_word;
1165       iov[2].iov_len = (sizeof (gidx_t)
1166                         - (string_table_size & (sizeof (gidx_t) - 1)));
1167       total += iov[2].iov_len;
1168       ++idx;
1169     }
1170
1171   header.hash_offset = total;
1172   header.hash_size = hash_size;
1173   iov[idx].iov_base = hash_table;
1174   iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1175   total += iov[idx].iov_len;
1176   ++idx;
1177
1178   header.module_offset = total;
1179   iov[idx].iov_base = module_table;
1180   iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1181   total += iov[idx].iov_len;
1182   ++idx;
1183
1184   assert ((size_t) (cur_extra_table - extra_table)
1185           <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1186                + sizeof (struct extra_entry_module))
1187               * nextra_modules));
1188   header.otherconv_offset = total;
1189   iov[idx].iov_base = extra_table;
1190   iov[idx].iov_len = cur_extra_table - extra_table;
1191   total += iov[idx].iov_len;
1192   ++idx;
1193
1194   if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1195       /* The file was created with mode 0600.  Make it world-readable.  */
1196       || fchmod (fd, 0644) != 0
1197       /* Rename the file, possibly replacing an old one.  */
1198       || rename (tmpfname, finalname) != 0)
1199     {
1200       int save_errno = errno;
1201       close (fd);
1202       unlink (tmpfname);
1203       error (EXIT_FAILURE, save_errno,
1204              gettext ("cannot generate output file"));
1205     }
1206
1207   close (fd);
1208
1209   return 0;
1210 }