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