(glibc.spec): Another typo fix.
[glibc.git] / iconv / iconvconfig.c
blobe95d073a22647d4c2d816f968163a8ca3801661f
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)
993 static struct hash_entry *hash_table;
994 static size_t hash_size;
996 /* Function to insert the names. */
997 static void name_insert (const void *nodep, VISIT value, int level)
999 struct name *name;
1000 unsigned int idx;
1001 unsigned int hval2;
1003 if (value != leaf && value != postorder)
1004 return;
1006 name = *(struct name **) nodep;
1007 idx = name->hashval % hash_size;
1008 hval2 = 1 + name->hashval % (hash_size - 2);
1010 while (hash_table[idx].string_offset != 0)
1011 if ((idx += hval2) >= hash_size)
1012 idx -= hash_size;
1014 hash_table[idx].string_offset = strtaboffset (name->strent);
1016 assert (name->module_idx != -1);
1017 hash_table[idx].module_idx = name->module_idx;
1020 static int
1021 write_output (void)
1023 int fd;
1024 char *string_table;
1025 size_t string_table_size;
1026 struct gconvcache_header header;
1027 struct module_entry *module_table;
1028 char *extra_table;
1029 char *cur_extra_table;
1030 size_t n;
1031 int idx;
1032 struct iovec iov[6];
1033 static const gidx_t null_word;
1034 size_t total;
1035 char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1036 + strlen (".XXXXXX")];
1037 char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1039 /* Open the output file. */
1040 assert (GCONV_MODULES_CACHE[0] == '/');
1041 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1042 ".XXXXXX");
1043 fd = mkstemp (tmpfname);
1044 if (fd == -1)
1045 return 1;
1047 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1049 /* Create the string table. */
1050 string_table = strtabfinalize (strtab, &string_table_size);
1052 /* Create the hashing table. We know how many strings we have.
1053 Creating a perfect hash table is not reasonable here. Therefore
1054 we use open hashing and a table size which is the next prime 40%
1055 larger than the number of strings. */
1056 hash_size = next_prime (nnames * 1.4);
1057 hash_table = (struct hash_entry *) xcalloc (hash_size,
1058 sizeof (struct hash_entry));
1059 /* Fill the hash table. */
1060 twalk (names, name_insert);
1062 /* Create the section for the module list. */
1063 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1064 nname_info);
1066 /* Allocate memory for the non-INTERNAL conversions. The allocated
1067 memory can be more than is actually needed. */
1068 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1069 + sizeof (gidx_t)
1070 + sizeof (struct extra_entry_module),
1071 nextra_modules);
1072 cur_extra_table = extra_table;
1074 /* Fill in the module information. */
1075 for (n = 0; n < nname_info; ++n)
1077 module_table[n].canonname_offset =
1078 strtaboffset (name_info[n].canonical_strent);
1080 if (name_info[n].from_internal == NULL)
1082 module_table[n].fromdir_offset = 0;
1083 module_table[n].fromname_offset = 0;
1085 else
1087 module_table[n].fromdir_offset =
1088 strtaboffset (name_info[n].from_internal->directory_strent);
1089 module_table[n].fromname_offset =
1090 strtaboffset (name_info[n].from_internal->filename_strent);
1093 if (name_info[n].to_internal == NULL)
1095 module_table[n].todir_offset = 0;
1096 module_table[n].toname_offset = 0;
1098 else
1100 module_table[n].todir_offset =
1101 strtaboffset (name_info[n].to_internal->directory_strent);
1102 module_table[n].toname_offset =
1103 strtaboffset (name_info[n].to_internal->filename_strent);
1106 if (name_info[n].other_conv_list != NULL)
1108 struct other_conv_list *other = name_info[n].other_conv_list;
1110 /* Store the reference. We add 1 to distinguish the entry
1111 at offset zero from the case where no extra modules are
1112 available. The file reader has to account for the
1113 offset. */
1114 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1118 struct other_conv *runp;
1119 struct extra_entry *extra;
1121 /* Allocate new entry. */
1122 extra = (struct extra_entry *) cur_extra_table;
1123 cur_extra_table += sizeof (struct extra_entry);
1124 extra->module_cnt = 0;
1126 runp = &other->other_conv;
1129 cur_extra_table += sizeof (struct extra_entry_module);
1130 extra->module[extra->module_cnt].outname_offset =
1131 runp->next == NULL
1132 ? other->dest_idx : runp->next->module_idx;
1133 extra->module[extra->module_cnt].dir_offset =
1134 strtaboffset (runp->module->directory_strent);
1135 extra->module[extra->module_cnt].name_offset =
1136 strtaboffset (runp->module->filename_strent);
1137 ++extra->module_cnt;
1139 runp = runp->next;
1141 while (runp != NULL);
1143 other = other->next;
1145 while (other != NULL);
1147 /* Final module_cnt is zero. */
1148 *((gidx_t *) cur_extra_table) = 0;
1149 cur_extra_table += sizeof (gidx_t);
1153 header.magic = GCONVCACHE_MAGIC;
1155 iov[0].iov_base = &header;
1156 iov[0].iov_len = sizeof (struct gconvcache_header);
1157 total = iov[0].iov_len;
1159 header.string_offset = total;
1160 iov[1].iov_base = string_table;
1161 iov[1].iov_len = string_table_size;
1162 total += iov[1].iov_len;
1164 idx = 2;
1165 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1167 iov[2].iov_base = (void *) &null_word;
1168 iov[2].iov_len = (sizeof (gidx_t)
1169 - (string_table_size & (sizeof (gidx_t) - 1)));
1170 total += iov[2].iov_len;
1171 ++idx;
1174 header.hash_offset = total;
1175 header.hash_size = hash_size;
1176 iov[idx].iov_base = hash_table;
1177 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1178 total += iov[idx].iov_len;
1179 ++idx;
1181 header.module_offset = total;
1182 iov[idx].iov_base = module_table;
1183 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1184 total += iov[idx].iov_len;
1185 ++idx;
1187 assert ((size_t) (cur_extra_table - extra_table)
1188 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1189 + sizeof (struct extra_entry_module))
1190 * nextra_modules));
1191 header.otherconv_offset = total;
1192 iov[idx].iov_base = extra_table;
1193 iov[idx].iov_len = cur_extra_table - extra_table;
1194 total += iov[idx].iov_len;
1195 ++idx;
1197 if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1198 /* The file was created with mode 0600. Make it world-readable. */
1199 || fchmod (fd, 0644) != 0
1200 /* Rename the file, possibly replacing an old one. */
1201 || rename (tmpfname, finalname) != 0)
1203 int save_errno = errno;
1204 close (fd);
1205 unlink (tmpfname);
1206 error (EXIT_FAILURE, save_errno,
1207 gettext ("cannot generate output file"));
1210 close (fd);
1212 return 0;