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