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