No need to use __THROW in this header.
[glibc.git] / iconv / iconvconfig.c
blobda0d54f43b9ce4edb44f34105c97e2fb9d942a28
1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2000.
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.
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.
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. */
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>
39 #include "iconvconfig.h"
41 /* Get libc version number. */
42 #include "../version.h"
44 #define PACKAGE _libc_intl_domainname
47 /* The hashing function we use. */
48 #include "../intl/hash-string.h"
51 /* Types used. */
52 struct module
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];
66 struct alias
68 char *fromname;
69 struct Strent *froment;
70 struct module *module;
71 struct Strent *toent;
72 char toname[0];
75 struct name
77 const char *name;
78 struct Strent *strent;
79 int module_idx;
80 uint32_t hashval;
83 struct name_info
85 const char *canonical_name;
86 struct Strent *canonical_strent;
88 struct module *from_internal;
89 struct module *to_internal;
91 struct other_conv_list
93 int dest_idx;
94 struct other_conv
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;
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;
109 /* Short description of program. */
110 static const char doc[] = N_("\
111 Create fastloading iconv module configuration file.");
113 /* Strings for arguments in help texts. */
114 static const char args_doc[] = N_("[DIR...]");
116 /* Prototype for option handler. */
117 static error_t parse_opt (int key, char *arg, struct argp_state *state);
119 /* Function to print some extra text in the help message. */
120 static char *more_help (int key, const char *text, void *input);
122 /* Definitions of arguments for argp functions. */
123 #define OPT_PREFIX 300
124 static const struct argp_option options[] =
126 { "prefix", OPT_PREFIX, "PATH", 0, N_("Prefix used for all file accesses") },
127 { NULL, 0, NULL, 0, NULL }
130 /* Data structure to communicate with argp functions. */
131 static struct argp argp =
134 options, parse_opt, args_doc, doc, NULL, more_help
138 /* The function doing the actual work. */
139 static int handle_dir (const char *dir);
141 /* Add all known builtin conversions and aliases. */
142 static void add_builtins (void);
144 /* Create list of all aliases without circular aliases. */
145 static void get_aliases (void);
147 /* Create list of all modules. */
148 static void get_modules (void);
150 /* Get list of all the names and thereby indexing them. */
151 static void generate_name_list (void);
153 /* Collect information about all the names. */
154 static void generate_name_info (void);
156 /* Write the output file. */
157 static int write_output (void);
160 /* Prefix to be used for all file accesses. */
161 static const char *prefix = "";
162 /* Its length. */
163 static size_t prefix_len;
165 /* Search tree of the modules we know. */
166 static void *modules;
168 /* Search tree of the aliases we know. */
169 static void *aliases;
171 /* Search tree for name to index mapping. */
172 static void *names;
174 /* Number of names we know about. */
175 static int nnames;
177 /* List of all aliases. */
178 static struct alias **alias_list;
179 static size_t nalias_list;
180 static size_t nalias_list_max;
182 /* List of all modules. */
183 static struct module **module_list;
184 static size_t nmodule_list;
185 static size_t nmodule_list_max;
187 /* Names and information about them. */
188 static struct name_info *name_info;
189 static size_t nname_info;
191 /* Number of translations not from or to INTERNAL. */
192 static size_t nextra_modules;
195 /* Names and aliases for the builtin transformations. */
196 static struct
198 const char *from;
199 const char *to;
200 } builtin_alias[] =
202 #define BUILTIN_ALIAS(alias, real) \
203 { .from = alias, .to = real },
204 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
205 MinF, MaxF, MinT, MaxT)
206 #include <gconv_builtin.h>
208 #undef BUILTIN_ALIAS
209 #undef BUILTIN_TRANSFORMATION
210 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
212 static struct
214 const char *from;
215 const char *to;
216 const char *module;
217 int cost;
218 } builtin_trans[] =
220 #define BUILTIN_ALIAS(alias, real)
221 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
222 MinF, MaxF, MinT, MaxT) \
223 { .from = From, .to = To, .module = Name, .cost = Cost },
224 #include <gconv_builtin.h>
226 #undef BUILTIN_ALIAS
227 #undef BUILTIN_TRANSFORMATION
228 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
231 /* Filename extension for the modules. */
232 #ifndef MODULE_EXT
233 # define MODULE_EXT ".so"
234 #endif
235 static const char gconv_module_ext[] = MODULE_EXT;
238 extern void *xmalloc (size_t n) __attribute_malloc__;
239 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
240 extern void *xrealloc (void *p, size_t n);
243 /* C string table handling. */
244 struct Strtab;
245 struct Strent;
247 /* Create new C string table object in memory. */
248 extern struct Strtab *strtabinit (void);
250 /* Free resources allocated for C string table ST. */
251 extern void strtabfree (struct Strtab *st);
253 /* Add string STR (length LEN is != 0) to C string table ST. */
254 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
255 size_t len);
257 /* Finalize string table ST and store size in *SIZE and return a pointer. */
258 extern void *strtabfinalize (struct Strtab *st, size_t *size);
260 /* Get offset in string table for string associated with SE. */
261 extern size_t strtaboffset (struct Strent *se);
263 /* String table we construct. */
264 static struct Strtab *strtab;
269 main (int argc, char *argv[])
271 int remaining;
272 int status = 0;
273 char *path;
274 char *tp;
276 /* Enable memory use testing. */
277 /* mcheck_pedantic (NULL); */
278 mtrace ();
280 /* Set locale via LC_ALL. */
281 setlocale (LC_ALL, "");
283 /* Set the text message domain. */
284 textdomain (_libc_intl_domainname);
286 /* Parse and process arguments. */
287 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
289 /* Initialize the string table. */
290 strtab = strtabinit ();
292 /* Handle all directories mentioned. */
293 while (remaining < argc)
294 status |= handle_dir (argv[remaining++]);
296 /* In any case also handle the standard directory. */
297 path = strdupa (GCONV_PATH);
298 tp = strtok (path, ":");
299 while (tp != NULL)
301 status |= handle_dir (tp);
303 tp = strtok (NULL, ":");
306 /* Add the builtin transformations and aliases without overwriting
307 anything. */
308 add_builtins ();
310 /* Store aliases in an array. */
311 get_aliases ();
313 /* Get list of all modules. */
314 get_modules ();
316 /* Generate list of all the names we know to handle in some way. */
317 generate_name_list ();
319 /* Now we know all the names we will handle, collect information
320 about them. */
321 generate_name_info ();
323 /* Write the output file, but only if we haven't seen any error. */
324 if (status == 0)
325 status = write_output ();
326 else
327 error (1, 0, _("no output file produced because warning were issued"));
329 return status;
333 /* Handle program arguments. */
334 static error_t
335 parse_opt (int key, char *arg, struct argp_state *state)
337 switch (key)
339 case OPT_PREFIX:
340 prefix = arg;
341 prefix_len = strlen (prefix);
342 break;
343 default:
344 return ARGP_ERR_UNKNOWN;
346 return 0;
350 static char *
351 more_help (int key, const char *text, void *input)
353 switch (key)
355 case ARGP_KEY_HELP_EXTRA:
356 /* We print some extra information. */
357 return strdup (gettext ("\
358 For bug reporting instructions, please see:\n\
359 <http://www.gnu.org/software/libc/bugs.html>.\n"));
360 default:
361 break;
363 return (char *) text;
367 /* Print the version information. */
368 static void
369 print_version (FILE *stream, struct argp_state *state)
371 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
372 fprintf (stream, gettext ("\
373 Copyright (C) %s Free Software Foundation, Inc.\n\
374 This is free software; see the source for copying conditions. There is NO\n\
375 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
376 "), "2004");
377 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
381 static int
382 alias_compare (const void *p1, const void *p2)
384 const struct alias *a1 = (const struct alias *) p1;
385 const struct alias *a2 = (const struct alias *) p2;
387 return strcmp (a1->fromname, a2->fromname);
391 static void
392 new_alias (const char *fromname, size_t fromlen, const char *toname,
393 size_t tolen)
395 struct alias *newp;
396 void **inserted;
398 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
400 newp->fromname = mempcpy (newp->toname, toname, tolen);
401 memcpy (newp->fromname, fromname, fromlen);
402 newp->module = NULL;
404 inserted = (void **) tsearch (newp, &aliases, alias_compare);
405 if (inserted == NULL)
406 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
407 if (*inserted != newp)
408 /* Something went wrong, free this entry. */
409 free (newp);
410 else
412 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
413 newp->toent = strtabadd (strtab, newp->toname, tolen);
418 /* Add new alias. */
419 static void
420 add_alias (char *rp)
422 /* We now expect two more string. The strings are normalized
423 (converted to UPPER case) and strored in the alias database. */
424 char *from;
425 char *to;
426 char *wp;
428 while (isspace (*rp))
429 ++rp;
430 from = wp = rp;
431 while (*rp != '\0' && !isspace (*rp))
432 *wp++ = toupper (*rp++);
433 if (*rp == '\0')
434 /* There is no `to' string on the line. Ignore it. */
435 return;
436 *wp++ = '\0';
437 to = ++rp;
438 while (isspace (*rp))
439 ++rp;
440 while (*rp != '\0' && !isspace (*rp))
441 *wp++ = toupper (*rp++);
442 if (to == wp)
443 /* No `to' string, ignore the line. */
444 return;
445 *wp++ = '\0';
447 assert (strlen (from) + 1 == (size_t) (to - from));
448 assert (strlen (to) + 1 == (size_t) (wp - to));
450 new_alias (from, to - from, to, wp - to);
454 static void
455 append_alias (const void *nodep, VISIT value, int level)
457 if (value != leaf && value != postorder)
458 return;
460 if (nalias_list_max == nalias_list)
462 nalias_list_max += 50;
463 alias_list = (struct alias **) xrealloc (alias_list,
464 (nalias_list_max
465 * sizeof (struct alias *)));
468 alias_list[nalias_list++] = *(struct alias **) nodep;
472 static void
473 get_aliases (void)
475 twalk (aliases, append_alias);
479 static int
480 module_compare (const void *p1, const void *p2)
482 const struct module *m1 = (const struct module *) p1;
483 const struct module *m2 = (const struct module *) p2;
484 int result;
486 result = strcmp (m1->fromname, m2->fromname);
487 if (result == 0)
488 result = strcmp (m1->toname, m2->toname);
490 return result;
494 /* Create new module record. */
495 static void
496 new_module (const char *fromname, size_t fromlen, const char *toname,
497 size_t tolen, const char *directory,
498 const char *filename, size_t filelen, int cost, size_t need_ext)
500 struct module *new_module;
501 size_t dirlen = strlen (directory) + 1;
502 char *tmp;
503 void **inserted;
505 new_module = (struct module *) xmalloc (sizeof (struct module)
506 + fromlen + tolen + filelen
507 + need_ext);
509 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
511 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
513 new_module->cost = cost;
514 new_module->next = NULL;
516 tmp = mempcpy (new_module->filename, filename, filelen);
517 if (need_ext)
519 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
520 filelen += need_ext;
522 new_module->directory = directory;
524 /* Now insert the new module data structure in our search tree. */
525 inserted = (void **) tsearch (new_module, &modules, module_compare);
526 if (inserted == NULL)
527 error (EXIT_FAILURE, errno, "while inserting in search tree");
528 if (*inserted != new_module)
529 free (new_module);
530 else
532 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
533 fromlen);
534 new_module->toname_strent = strtabadd (strtab, new_module->toname,
535 tolen);
536 new_module->filename_strent = strtabadd (strtab, new_module->filename,
537 filelen);
538 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
543 /* Add new module. */
544 static void
545 internal_function
546 add_module (char *rp, const char *directory)
548 /* We expect now
549 1. `from' name
550 2. `to' name
551 3. filename of the module
552 4. an optional cost value
554 char *from;
555 char *to;
556 char *module;
557 char *wp;
558 int need_ext;
559 int cost;
561 while (isspace (*rp))
562 ++rp;
563 from = rp;
564 while (*rp != '\0' && !isspace (*rp))
566 *rp = toupper (*rp);
567 ++rp;
569 if (*rp == '\0')
570 return;
571 *rp++ = '\0';
572 to = wp = rp;
573 while (isspace (*rp))
574 ++rp;
575 while (*rp != '\0' && !isspace (*rp))
576 *wp++ = toupper (*rp++);
577 if (*rp == '\0')
578 return;
579 *wp++ = '\0';
581 ++rp;
582 while (isspace (*rp));
583 module = wp;
584 while (*rp != '\0' && !isspace (*rp))
585 *wp++ = *rp++;
586 if (*rp == '\0')
588 /* There is no cost, use one by default. */
589 *wp++ = '\0';
590 cost = 1;
592 else
594 /* There might be a cost value. */
595 char *endp;
597 *wp++ = '\0';
598 cost = strtol (rp, &endp, 10);
599 if (rp == endp || cost < 1)
600 /* No useful information. */
601 cost = 1;
604 if (module[0] == '\0')
605 /* No module name given. */
606 return;
608 /* See whether we must add the ending. */
609 need_ext = 0;
610 if ((size_t) (wp - module) < sizeof (gconv_module_ext)
611 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
612 sizeof (gconv_module_ext)) != 0)
613 /* We must add the module extension. */
614 need_ext = sizeof (gconv_module_ext) - 1;
616 assert (strlen (from) + 1 == (size_t) (to - from));
617 assert (strlen (to) + 1 == (size_t) (module - to));
618 assert (strlen (module) + 1 == (size_t) (wp - module));
620 new_module (from, to - from, to, module - to, directory, module, wp - module,
621 cost, need_ext);
625 /* Read the config file and add the data for this directory to that. */
626 static int
627 handle_dir (const char *dir)
629 char *infile;
630 char *cp;
631 FILE *fp;
632 char *line = NULL;
633 size_t linelen = 0;
634 size_t dirlen = strlen (dir);
636 if (dir[dirlen - 1] != '/')
638 char *newp = (char *) xmalloc (dirlen + 2);
639 dir = memcpy (newp, dir, dirlen);
640 newp[dirlen++] = '/';
641 newp[dirlen] = '\0';
644 cp = infile = (char *) alloca (prefix_len + dirlen + sizeof "gconv-modules");
645 if (dir[0] == '/')
646 cp = mempcpy (cp, prefix, prefix_len);
647 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
649 fp = fopen (infile, "r");
650 if (fp == NULL)
652 error (0, errno, "cannot open `%s'", infile);
653 return 1;
656 /* No threads present. */
657 __fsetlocking (fp, FSETLOCKING_BYCALLER);
659 while (!feof_unlocked (fp))
661 char *rp, *endp, *word;
662 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
664 if (n < 0)
665 /* An error occurred. */
666 break;
668 rp = line;
669 /* Terminate the line (excluding comments or newline) with a NUL
670 byte to simplify the following code. */
671 endp = strchr (rp, '#');
672 if (endp != NULL)
673 *endp = '\0';
674 else
675 if (rp[n - 1] == '\n')
676 rp[n - 1] = '\0';
678 while (isspace (*rp))
679 ++rp;
681 /* If this is an empty line go on with the next one. */
682 if (rp == endp)
683 continue;
685 word = rp;
686 while (*rp != '\0' && !isspace (*rp))
687 ++rp;
689 if (rp - word == sizeof ("alias") - 1
690 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
691 add_alias (rp);
692 else if (rp - word == sizeof ("module") - 1
693 && memcmp (word, "module", sizeof ("module") - 1) == 0)
694 add_module (rp, dir);
695 /* else */
696 /* Otherwise ignore the line. */
699 free (line);
701 fclose (fp);
703 return 0;
707 static void
708 append_module (const void *nodep, VISIT value, int level)
710 struct module *mo;
712 if (value != leaf && value != postorder)
713 return;
715 mo = *(struct module **) nodep;
717 if (nmodule_list > 0
718 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
720 /* Same name. */
721 mo->next = module_list[nmodule_list - 1];
722 module_list[nmodule_list - 1] = mo;
724 return;
727 if (nmodule_list_max == nmodule_list)
729 nmodule_list_max += 50;
730 module_list = (struct module **) xrealloc (module_list,
731 (nmodule_list_max
732 * sizeof (struct module *)));
735 module_list[nmodule_list++] = mo;
739 static void
740 get_modules (void)
742 twalk (modules, append_module);
746 static void
747 add_builtins (void)
749 size_t cnt;
751 /* Add all aliases. */
752 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
753 new_alias (builtin_alias[cnt].from,
754 strlen (builtin_alias[cnt].from) + 1,
755 builtin_alias[cnt].to,
756 strlen (builtin_alias[cnt].to) + 1);
758 /* add the builtin transformations. */
759 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
760 new_module (builtin_trans[cnt].from,
761 strlen (builtin_trans[cnt].from) + 1,
762 builtin_trans[cnt].to,
763 strlen (builtin_trans[cnt].to) + 1,
764 "", builtin_trans[cnt].module,
765 strlen (builtin_trans[cnt].module) + 1,
766 builtin_trans[cnt].cost, 0);
770 static int
771 name_compare (const void *p1, const void *p2)
773 const struct name *n1 = (const struct name *) p1;
774 const struct name *n2 = (const struct name *) p2;
776 return strcmp (n1->name, n2->name);
780 static struct name *
781 new_name (const char *str, struct Strent *strent)
783 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
785 newp->name = str;
786 newp->strent = strent;
787 newp->module_idx = -1;
788 newp->hashval = __hash_string (str);
790 ++nnames;
792 return newp;
796 static void
797 generate_name_list (void)
799 size_t i;
801 /* A name we always need. */
802 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
803 sizeof ("INTERNAL"))),
804 &names, name_compare);
806 for (i = 0; i < nmodule_list; ++i)
808 struct module *runp;
810 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
811 tsearch (new_name (module_list[i]->fromname,
812 module_list[i]->fromname_strent),
813 &names, name_compare);
815 for (runp = module_list[i]; runp != NULL; runp = runp->next)
816 if (strcmp (runp->toname, "INTERNAL") != 0)
817 tsearch (new_name (runp->toname, runp->toname_strent),
818 &names, name_compare);
823 static int
824 name_to_module_idx (const char *name, int add)
826 struct name **res;
827 struct name fake_name = { .name = name };
828 int idx;
830 res = (struct name **) tfind (&fake_name, &names, name_compare);
831 if (res == NULL)
832 abort ();
834 idx = (*res)->module_idx;
835 if (idx == -1 && add)
836 /* No module index assigned yet. */
837 idx = (*res)->module_idx = nname_info++;
839 return idx;
843 static void
844 generate_name_info (void)
846 size_t i;
847 int idx;
849 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
850 sizeof (struct name_info));
852 /* First add a special entry for the INTERNAL name. This must have
853 index zero. */
854 idx = name_to_module_idx ("INTERNAL", 1);
855 name_info[0].canonical_name = "INTERNAL";
856 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
857 sizeof ("INTERNAL"));
858 assert (nname_info == 1);
860 for (i = 0; i < nmodule_list; ++i)
862 struct module *runp;
864 for (runp = module_list[i]; runp != NULL; runp = runp->next)
865 if (strcmp (runp->fromname, "INTERNAL") == 0)
867 idx = name_to_module_idx (runp->toname, 1);
868 name_info[idx].from_internal = runp;
869 assert (name_info[idx].canonical_name == NULL
870 || strcmp (name_info[idx].canonical_name,
871 runp->toname) == 0);
872 name_info[idx].canonical_name = runp->toname;
873 name_info[idx].canonical_strent = runp->toname_strent;
875 else if (strcmp (runp->toname, "INTERNAL") == 0)
877 idx = name_to_module_idx (runp->fromname, 1);
878 name_info[idx].to_internal = runp;
879 assert (name_info[idx].canonical_name == NULL
880 || strcmp (name_info[idx].canonical_name,
881 runp->fromname) == 0);
882 name_info[idx].canonical_name = runp->fromname;
883 name_info[idx].canonical_strent = runp->fromname_strent;
885 else
887 /* This is a transformation not to or from the INTERNAL
888 encoding. */
889 int from_idx = name_to_module_idx (runp->fromname, 1);
890 int to_idx = name_to_module_idx (runp->toname, 1);
891 struct other_conv_list *newp;
893 newp = (struct other_conv_list *)
894 xmalloc (sizeof (struct other_conv_list));
895 newp->other_conv.module_idx = to_idx;
896 newp->other_conv.module = runp;
897 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
898 newp->dest_idx = to_idx;
899 newp->next = name_info[from_idx].other_conv_list;
900 name_info[from_idx].other_conv_list = newp;
901 assert (name_info[from_idx].canonical_name == NULL
902 || strcmp (name_info[from_idx].canonical_name,
903 runp->fromname) == 0);
904 name_info[from_idx].canonical_name = runp->fromname;
905 name_info[from_idx].canonical_strent = runp->fromname_strent;
907 ++nextra_modules;
911 /* Now add the module index information for all the aliases. */
912 for (i = 0; i < nalias_list; ++i)
914 struct name fake_name = { .name = alias_list[i]->toname };
915 struct name **tonamep;
917 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
918 if (tonamep != NULL)
920 struct name *newp = new_name (alias_list[i]->fromname,
921 alias_list[i]->froment);
922 newp->module_idx = (*tonamep)->module_idx;
923 tsearch (newp, &names, name_compare);
929 static int
930 is_prime (unsigned long int candidate)
932 /* No even number and none less than 10 will be passed here. */
933 unsigned long int divn = 3;
934 unsigned long int sq = divn * divn;
936 while (sq < candidate && candidate % divn != 0)
938 ++divn;
939 sq += 4 * divn;
940 ++divn;
943 return candidate % divn != 0;
947 static uint32_t
948 next_prime (uint32_t seed)
950 /* Make it definitely odd. */
951 seed |= 1;
953 while (!is_prime (seed))
954 seed += 2;
956 return seed;
960 /* Format of the output file.
962 Offset Length Description
963 0000 4 Magic header bytes
964 0004 4 Offset of string table (stoff)
965 0008 4 Offset of name hashing table (hoff)
966 000C 4 Hashing table size (hsize)
967 0010 4 Offset of module table (moff)
968 0014 4 Offset of other conversion module table (ooff)
970 stoff ??? String table
972 hoff 8*hsize Array of tuples
973 string table offset
974 module index
976 moff ??? Array of tuples
977 canonical name offset
978 from-internal module dir name offset
979 from-internal module name off
980 to-internal module dir name offset
981 to-internal module name offset
982 offset into other conversion table
984 ooff ??? One or more of
985 number of steps/modules
986 one or more of tuple
987 canonical name offset for output
988 module dir name offset
989 module name offset
990 (following last entry with step count 0)
992 static int
993 write_output (void)
995 int fd;
996 char *string_table;
997 size_t string_table_size;
998 struct gconvcache_header header;
999 struct hash_entry *hash_table;
1000 size_t hash_size;
1001 struct module_entry *module_table;
1002 char *extra_table;
1003 char *cur_extra_table;
1004 size_t n;
1005 int idx;
1006 struct iovec iov[6];
1007 static const gidx_t null_word;
1008 size_t total;
1009 char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1010 + strlen (".XXXXXX")];
1011 char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1013 /* Function to insert the names. */
1014 static void name_insert (const void *nodep, VISIT value, int level)
1016 struct name *name;
1017 unsigned int idx;
1018 unsigned int hval2;
1020 if (value != leaf && value != postorder)
1021 return;
1023 name = *(struct name **) nodep;
1024 idx = name->hashval % hash_size;
1025 hval2 = 1 + name->hashval % (hash_size - 2);
1027 while (hash_table[idx].string_offset != 0)
1028 if ((idx += hval2) >= hash_size)
1029 idx -= hash_size;
1031 hash_table[idx].string_offset = strtaboffset (name->strent);
1033 assert (name->module_idx != -1);
1034 hash_table[idx].module_idx = name->module_idx;
1037 /* Open the output file. */
1038 assert (GCONV_MODULES_CACHE[0] == '/');
1039 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1040 ".XXXXXX");
1041 fd = mkstemp (tmpfname);
1042 if (fd == -1)
1043 return 1;
1045 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1047 /* Create the string table. */
1048 string_table = strtabfinalize (strtab, &string_table_size);
1050 /* Create the hashing table. We know how many strings we have.
1051 Creating a perfect hash table is not reasonable here. Therefore
1052 we use open hashing and a table size which is the next prime 40%
1053 larger than the number of strings. */
1054 hash_size = next_prime (nnames * 1.4);
1055 hash_table = (struct hash_entry *) xcalloc (hash_size,
1056 sizeof (struct hash_entry));
1057 /* Fill the hash table. */
1058 twalk (names, name_insert);
1060 /* Create the section for the module list. */
1061 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1062 nname_info);
1064 /* Allocate memory for the non-INTERNAL conversions. The allocated
1065 memory can be more than is actually needed. */
1066 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1067 + sizeof (gidx_t)
1068 + sizeof (struct extra_entry_module),
1069 nextra_modules);
1070 cur_extra_table = extra_table;
1072 /* Fill in the module information. */
1073 for (n = 0; n < nname_info; ++n)
1075 module_table[n].canonname_offset =
1076 strtaboffset (name_info[n].canonical_strent);
1078 if (name_info[n].from_internal == NULL)
1080 module_table[n].fromdir_offset = 0;
1081 module_table[n].fromname_offset = 0;
1083 else
1085 module_table[n].fromdir_offset =
1086 strtaboffset (name_info[n].from_internal->directory_strent);
1087 module_table[n].fromname_offset =
1088 strtaboffset (name_info[n].from_internal->filename_strent);
1091 if (name_info[n].to_internal == NULL)
1093 module_table[n].todir_offset = 0;
1094 module_table[n].toname_offset = 0;
1096 else
1098 module_table[n].todir_offset =
1099 strtaboffset (name_info[n].to_internal->directory_strent);
1100 module_table[n].toname_offset =
1101 strtaboffset (name_info[n].to_internal->filename_strent);
1104 if (name_info[n].other_conv_list != NULL)
1106 struct other_conv_list *other = name_info[n].other_conv_list;
1108 /* Store the reference. We add 1 to distinguish the entry
1109 at offset zero from the case where no extra modules are
1110 available. The file reader has to account for the
1111 offset. */
1112 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1116 struct other_conv *runp;
1117 struct extra_entry *extra;
1119 /* Allocate new entry. */
1120 extra = (struct extra_entry *) cur_extra_table;
1121 cur_extra_table += sizeof (struct extra_entry);
1122 extra->module_cnt = 0;
1124 runp = &other->other_conv;
1127 cur_extra_table += sizeof (struct extra_entry_module);
1128 extra->module[extra->module_cnt].outname_offset =
1129 runp->next == NULL
1130 ? other->dest_idx : runp->next->module_idx;
1131 extra->module[extra->module_cnt].dir_offset =
1132 strtaboffset (runp->module->directory_strent);
1133 extra->module[extra->module_cnt].name_offset =
1134 strtaboffset (runp->module->filename_strent);
1135 ++extra->module_cnt;
1137 runp = runp->next;
1139 while (runp != NULL);
1141 other = other->next;
1143 while (other != NULL);
1145 /* Final module_cnt is zero. */
1146 *((gidx_t *) cur_extra_table) = 0;
1147 cur_extra_table += sizeof (gidx_t);
1151 header.magic = GCONVCACHE_MAGIC;
1153 iov[0].iov_base = &header;
1154 iov[0].iov_len = sizeof (struct gconvcache_header);
1155 total = iov[0].iov_len;
1157 header.string_offset = total;
1158 iov[1].iov_base = string_table;
1159 iov[1].iov_len = string_table_size;
1160 total += iov[1].iov_len;
1162 idx = 2;
1163 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1165 iov[2].iov_base = (void *) &null_word;
1166 iov[2].iov_len = (sizeof (gidx_t)
1167 - (string_table_size & (sizeof (gidx_t) - 1)));
1168 total += iov[2].iov_len;
1169 ++idx;
1172 header.hash_offset = total;
1173 header.hash_size = hash_size;
1174 iov[idx].iov_base = hash_table;
1175 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1176 total += iov[idx].iov_len;
1177 ++idx;
1179 header.module_offset = total;
1180 iov[idx].iov_base = module_table;
1181 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1182 total += iov[idx].iov_len;
1183 ++idx;
1185 assert ((size_t) (cur_extra_table - extra_table)
1186 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1187 + sizeof (struct extra_entry_module))
1188 * nextra_modules));
1189 header.otherconv_offset = total;
1190 iov[idx].iov_base = extra_table;
1191 iov[idx].iov_len = cur_extra_table - extra_table;
1192 total += iov[idx].iov_len;
1193 ++idx;
1195 if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1196 /* The file was created with mode 0600. Make it world-readable. */
1197 || fchmod (fd, 0644) != 0
1198 /* Rename the file, possibly replacing an old one. */
1199 || rename (tmpfname, finalname) != 0)
1201 int save_errno = errno;
1202 close (fd);
1203 unlink (tmpfname);
1204 error (EXIT_FAILURE, save_errno,
1205 gettext ("cannot generate output file"));
1208 close (fd);
1210 return 0;