OSDN Git Service

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