Support cancellation in librt.
[glibc.git] / iconv / iconvconfig.c
blobcd243f6807349ae4f298ec59d0e72b65f229f12d
1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001, 2002, 2003 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 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
359 default:
360 break;
362 return (char *) text;
366 /* Print the version information. */
367 static void
368 print_version (FILE *stream, struct argp_state *state)
370 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
371 fprintf (stream, gettext ("\
372 Copyright (C) %s Free Software Foundation, Inc.\n\
373 This is free software; see the source for copying conditions. There is NO\n\
374 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
375 "), "2003");
376 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
380 static int
381 alias_compare (const void *p1, const void *p2)
383 const struct alias *a1 = (const struct alias *) p1;
384 const struct alias *a2 = (const struct alias *) p2;
386 return strcmp (a1->fromname, a2->fromname);
390 static void
391 new_alias (const char *fromname, size_t fromlen, const char *toname,
392 size_t tolen)
394 struct alias *newp;
395 void **inserted;
397 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
399 newp->fromname = mempcpy (newp->toname, toname, tolen);
400 memcpy (newp->fromname, fromname, fromlen);
401 newp->module = NULL;
403 inserted = (void **) tsearch (newp, &aliases, alias_compare);
404 if (inserted == NULL)
405 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
406 if (*inserted != newp)
407 /* Something went wrong, free this entry. */
408 free (newp);
409 else
411 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
412 newp->toent = strtabadd (strtab, newp->toname, tolen);
417 /* Add new alias. */
418 static void
419 add_alias (char *rp)
421 /* We now expect two more string. The strings are normalized
422 (converted to UPPER case) and strored in the alias database. */
423 char *from;
424 char *to;
425 char *wp;
427 while (isspace (*rp))
428 ++rp;
429 from = wp = rp;
430 while (*rp != '\0' && !isspace (*rp))
431 *wp++ = toupper (*rp++);
432 if (*rp == '\0')
433 /* There is no `to' string on the line. Ignore it. */
434 return;
435 *wp++ = '\0';
436 to = ++rp;
437 while (isspace (*rp))
438 ++rp;
439 while (*rp != '\0' && !isspace (*rp))
440 *wp++ = toupper (*rp++);
441 if (to == wp)
442 /* No `to' string, ignore the line. */
443 return;
444 *wp++ = '\0';
446 assert (strlen (from) + 1 == (size_t) (to - from));
447 assert (strlen (to) + 1 == (size_t) (wp - to));
449 new_alias (from, to - from, to, wp - to);
453 static void
454 append_alias (const void *nodep, VISIT value, int level)
456 if (value != leaf && value != postorder)
457 return;
459 if (nalias_list_max == nalias_list)
461 nalias_list_max += 50;
462 alias_list = (struct alias **) xrealloc (alias_list,
463 (nalias_list_max
464 * sizeof (struct alias *)));
467 alias_list[nalias_list++] = *(struct alias **) nodep;
471 static void
472 get_aliases (void)
474 twalk (aliases, append_alias);
478 static int
479 module_compare (const void *p1, const void *p2)
481 const struct module *m1 = (const struct module *) p1;
482 const struct module *m2 = (const struct module *) p2;
483 int result;
485 result = strcmp (m1->fromname, m2->fromname);
486 if (result == 0)
487 result = strcmp (m1->toname, m2->toname);
489 return result;
493 /* Create new module record. */
494 static void
495 new_module (const char *fromname, size_t fromlen, const char *toname,
496 size_t tolen, const char *directory,
497 const char *filename, size_t filelen, int cost, size_t need_ext)
499 struct module *new_module;
500 size_t dirlen = strlen (directory) + 1;
501 char *tmp;
502 void **inserted;
504 new_module = (struct module *) xmalloc (sizeof (struct module)
505 + fromlen + tolen + filelen
506 + need_ext);
508 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
510 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
512 new_module->cost = cost;
513 new_module->next = NULL;
515 tmp = mempcpy (new_module->filename, filename, filelen);
516 if (need_ext)
518 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
519 filelen += need_ext;
521 new_module->directory = directory;
523 /* Now insert the new module data structure in our search tree. */
524 inserted = (void **) tsearch (new_module, &modules, module_compare);
525 if (inserted == NULL)
526 error (EXIT_FAILURE, errno, "while inserting in search tree");
527 if (*inserted != new_module)
528 free (new_module);
529 else
531 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
532 fromlen);
533 new_module->toname_strent = strtabadd (strtab, new_module->toname,
534 tolen);
535 new_module->filename_strent = strtabadd (strtab, new_module->filename,
536 filelen);
537 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
542 /* Add new module. */
543 static void
544 internal_function
545 add_module (char *rp, const char *directory)
547 /* We expect now
548 1. `from' name
549 2. `to' name
550 3. filename of the module
551 4. an optional cost value
553 char *from;
554 char *to;
555 char *module;
556 char *wp;
557 int need_ext;
558 int cost;
560 while (isspace (*rp))
561 ++rp;
562 from = rp;
563 while (*rp != '\0' && !isspace (*rp))
565 *rp = toupper (*rp);
566 ++rp;
568 if (*rp == '\0')
569 return;
570 *rp++ = '\0';
571 to = wp = rp;
572 while (isspace (*rp))
573 ++rp;
574 while (*rp != '\0' && !isspace (*rp))
575 *wp++ = toupper (*rp++);
576 if (*rp == '\0')
577 return;
578 *wp++ = '\0';
580 ++rp;
581 while (isspace (*rp));
582 module = wp;
583 while (*rp != '\0' && !isspace (*rp))
584 *wp++ = *rp++;
585 if (*rp == '\0')
587 /* There is no cost, use one by default. */
588 *wp++ = '\0';
589 cost = 1;
591 else
593 /* There might be a cost value. */
594 char *endp;
596 *wp++ = '\0';
597 cost = strtol (rp, &endp, 10);
598 if (rp == endp || cost < 1)
599 /* No useful information. */
600 cost = 1;
603 if (module[0] == '\0')
604 /* No module name given. */
605 return;
607 /* See whether we must add the ending. */
608 need_ext = 0;
609 if ((size_t) (wp - module) < sizeof (gconv_module_ext)
610 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
611 sizeof (gconv_module_ext)) != 0)
612 /* We must add the module extension. */
613 need_ext = sizeof (gconv_module_ext) - 1;
615 assert (strlen (from) + 1 == (size_t) (to - from));
616 assert (strlen (to) + 1 == (size_t) (module - to));
617 assert (strlen (module) + 1 == (size_t) (wp - module));
619 new_module (from, to - from, to, module - to, directory, module, wp - module,
620 cost, need_ext);
624 /* Read the config file and add the data for this directory to that. */
625 static int
626 handle_dir (const char *dir)
628 char *infile;
629 char *cp;
630 FILE *fp;
631 char *line = NULL;
632 size_t linelen = 0;
633 size_t dirlen = strlen (dir);
635 if (dir[dirlen - 1] != '/')
637 char *newp = (char *) xmalloc (dirlen + 2);
638 dir = memcpy (newp, dir, dirlen);
639 newp[dirlen++] = '/';
640 newp[dirlen] = '\0';
643 cp = infile = (char *) alloca (prefix_len + dirlen + sizeof "gconv-modules");
644 if (dir[0] == '/')
645 cp = mempcpy (cp, prefix, prefix_len);
646 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
648 fp = fopen (infile, "r");
649 if (fp == NULL)
651 error (0, errno, "cannot open `%s'", infile);
652 return 1;
655 /* No threads present. */
656 __fsetlocking (fp, FSETLOCKING_BYCALLER);
658 while (!feof_unlocked (fp))
660 char *rp, *endp, *word;
661 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
663 if (n < 0)
664 /* An error occurred. */
665 break;
667 rp = line;
668 /* Terminate the line (excluding comments or newline) with a NUL
669 byte to simplify the following code. */
670 endp = strchr (rp, '#');
671 if (endp != NULL)
672 *endp = '\0';
673 else
674 if (rp[n - 1] == '\n')
675 rp[n - 1] = '\0';
677 while (isspace (*rp))
678 ++rp;
680 /* If this is an empty line go on with the next one. */
681 if (rp == endp)
682 continue;
684 word = rp;
685 while (*rp != '\0' && !isspace (*rp))
686 ++rp;
688 if (rp - word == sizeof ("alias") - 1
689 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
690 add_alias (rp);
691 else if (rp - word == sizeof ("module") - 1
692 && memcmp (word, "module", sizeof ("module") - 1) == 0)
693 add_module (rp, dir);
694 /* else */
695 /* Otherwise ignore the line. */
698 free (line);
700 fclose (fp);
702 return 0;
706 static void
707 append_module (const void *nodep, VISIT value, int level)
709 struct module *mo;
711 if (value != leaf && value != postorder)
712 return;
714 mo = *(struct module **) nodep;
716 if (nmodule_list > 0
717 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
719 /* Same name. */
720 mo->next = module_list[nmodule_list - 1];
721 module_list[nmodule_list - 1] = mo;
723 return;
726 if (nmodule_list_max == nmodule_list)
728 nmodule_list_max += 50;
729 module_list = (struct module **) xrealloc (module_list,
730 (nmodule_list_max
731 * sizeof (struct module *)));
734 module_list[nmodule_list++] = mo;
738 static void
739 get_modules (void)
741 twalk (modules, append_module);
745 static void
746 add_builtins (void)
748 size_t cnt;
750 /* Add all aliases. */
751 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
752 new_alias (builtin_alias[cnt].from,
753 strlen (builtin_alias[cnt].from) + 1,
754 builtin_alias[cnt].to,
755 strlen (builtin_alias[cnt].to) + 1);
757 /* add the builtin transformations. */
758 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
759 new_module (builtin_trans[cnt].from,
760 strlen (builtin_trans[cnt].from) + 1,
761 builtin_trans[cnt].to,
762 strlen (builtin_trans[cnt].to) + 1,
763 "", builtin_trans[cnt].module,
764 strlen (builtin_trans[cnt].module) + 1,
765 builtin_trans[cnt].cost, 0);
769 static int
770 name_compare (const void *p1, const void *p2)
772 const struct name *n1 = (const struct name *) p1;
773 const struct name *n2 = (const struct name *) p2;
775 return strcmp (n1->name, n2->name);
779 static struct name *
780 new_name (const char *str, struct Strent *strent)
782 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
784 newp->name = str;
785 newp->strent = strent;
786 newp->module_idx = -1;
787 newp->hashval = __hash_string (str);
789 ++nnames;
791 return newp;
795 static void
796 generate_name_list (void)
798 size_t i;
800 /* A name we always need. */
801 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
802 sizeof ("INTERNAL"))),
803 &names, name_compare);
805 for (i = 0; i < nmodule_list; ++i)
807 struct module *runp;
809 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
810 tsearch (new_name (module_list[i]->fromname,
811 module_list[i]->fromname_strent),
812 &names, name_compare);
814 for (runp = module_list[i]; runp != NULL; runp = runp->next)
815 if (strcmp (runp->toname, "INTERNAL") != 0)
816 tsearch (new_name (runp->toname, runp->toname_strent),
817 &names, name_compare);
822 static int
823 name_to_module_idx (const char *name, int add)
825 struct name **res;
826 struct name fake_name = { .name = name };
827 int idx;
829 res = (struct name **) tfind (&fake_name, &names, name_compare);
830 if (res == NULL)
831 abort ();
833 idx = (*res)->module_idx;
834 if (idx == -1 && add)
835 /* No module index assigned yet. */
836 idx = (*res)->module_idx = nname_info++;
838 return idx;
842 static void
843 generate_name_info (void)
845 size_t i;
846 int idx;
848 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
849 sizeof (struct name_info));
851 /* First add a special entry for the INTERNAL name. This must have
852 index zero. */
853 idx = name_to_module_idx ("INTERNAL", 1);
854 name_info[0].canonical_name = "INTERNAL";
855 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
856 sizeof ("INTERNAL"));
857 assert (nname_info == 1);
859 for (i = 0; i < nmodule_list; ++i)
861 struct module *runp;
863 for (runp = module_list[i]; runp != NULL; runp = runp->next)
864 if (strcmp (runp->fromname, "INTERNAL") == 0)
866 idx = name_to_module_idx (runp->toname, 1);
867 name_info[idx].from_internal = runp;
868 assert (name_info[idx].canonical_name == NULL
869 || strcmp (name_info[idx].canonical_name,
870 runp->toname) == 0);
871 name_info[idx].canonical_name = runp->toname;
872 name_info[idx].canonical_strent = runp->toname_strent;
874 else if (strcmp (runp->toname, "INTERNAL") == 0)
876 idx = name_to_module_idx (runp->fromname, 1);
877 name_info[idx].to_internal = runp;
878 assert (name_info[idx].canonical_name == NULL
879 || strcmp (name_info[idx].canonical_name,
880 runp->fromname) == 0);
881 name_info[idx].canonical_name = runp->fromname;
882 name_info[idx].canonical_strent = runp->fromname_strent;
884 else
886 /* This is a transformation not to or from the INTERNAL
887 encoding. */
888 int from_idx = name_to_module_idx (runp->fromname, 1);
889 int to_idx = name_to_module_idx (runp->toname, 1);
890 struct other_conv_list *newp;
892 newp = (struct other_conv_list *)
893 xmalloc (sizeof (struct other_conv_list));
894 newp->other_conv.module_idx = to_idx;
895 newp->other_conv.module = runp;
896 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
897 newp->dest_idx = to_idx;
898 newp->next = name_info[from_idx].other_conv_list;
899 name_info[from_idx].other_conv_list = newp;
900 assert (name_info[from_idx].canonical_name == NULL
901 || strcmp (name_info[from_idx].canonical_name,
902 runp->fromname) == 0);
903 name_info[from_idx].canonical_name = runp->fromname;
904 name_info[from_idx].canonical_strent = runp->fromname_strent;
906 ++nextra_modules;
910 /* Now add the module index information for all the aliases. */
911 for (i = 0; i < nalias_list; ++i)
913 struct name fake_name = { .name = alias_list[i]->toname };
914 struct name **tonamep;
916 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
917 if (tonamep != NULL)
919 struct name *newp = new_name (alias_list[i]->fromname,
920 alias_list[i]->froment);
921 newp->module_idx = (*tonamep)->module_idx;
922 tsearch (newp, &names, name_compare);
928 static int
929 is_prime (unsigned long int candidate)
931 /* No even number and none less than 10 will be passed here. */
932 unsigned long int divn = 3;
933 unsigned long int sq = divn * divn;
935 while (sq < candidate && candidate % divn != 0)
937 ++divn;
938 sq += 4 * divn;
939 ++divn;
942 return candidate % divn != 0;
946 static uint32_t
947 next_prime (uint32_t seed)
949 /* Make it definitely odd. */
950 seed |= 1;
952 while (!is_prime (seed))
953 seed += 2;
955 return seed;
959 /* Format of the output file.
961 Offset Length Description
962 0000 4 Magic header bytes
963 0004 4 Offset of string table (stoff)
964 0008 4 Offset of name hashing table (hoff)
965 000C 4 Hashing table size (hsize)
966 0010 4 Offset of module table (moff)
967 0014 4 Offset of other conversion module table (ooff)
969 stoff ??? String table
971 hoff 8*hsize Array of tuples
972 string table offset
973 module index
975 moff ??? Array of tuples
976 canonical name offset
977 from-internal module dir name offset
978 from-internal module name off
979 to-internal module dir name offset
980 to-internal module name offset
981 offset into other conversion table
983 ooff ??? One or more of
984 number of steps/modules
985 one or more of tuple
986 canonical name offset for output
987 module dir name offset
988 module name offset
989 (following last entry with step count 0)
991 static int
992 write_output (void)
994 int fd;
995 char *string_table;
996 size_t string_table_size;
997 struct gconvcache_header header;
998 struct hash_entry *hash_table;
999 size_t hash_size;
1000 struct module_entry *module_table;
1001 char *extra_table;
1002 char *cur_extra_table;
1003 size_t n;
1004 int idx;
1005 struct iovec iov[6];
1006 static const gidx_t null_word;
1007 size_t total;
1008 char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1009 + strlen (".XXXXXX")];
1010 char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1012 /* Function to insert the names. */
1013 static void name_insert (const void *nodep, VISIT value, int level)
1015 struct name *name;
1016 unsigned int idx;
1017 unsigned int hval2;
1019 if (value != leaf && value != postorder)
1020 return;
1022 name = *(struct name **) nodep;
1023 idx = name->hashval % hash_size;
1024 hval2 = 1 + name->hashval % (hash_size - 2);
1026 while (hash_table[idx].string_offset != 0)
1027 if ((idx += hval2) >= hash_size)
1028 idx -= hash_size;
1030 hash_table[idx].string_offset = strtaboffset (name->strent);
1032 assert (name->module_idx != -1);
1033 hash_table[idx].module_idx = name->module_idx;
1036 /* Open the output file. */
1037 assert (GCONV_MODULES_CACHE[0] == '/');
1038 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1039 ".XXXXXX");
1040 fd = mkstemp (tmpfname);
1041 if (fd == -1)
1042 return 1;
1044 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1046 /* Create the string table. */
1047 string_table = strtabfinalize (strtab, &string_table_size);
1049 /* Create the hashing table. We know how many strings we have.
1050 Creating a perfect hash table is not reasonable here. Therefore
1051 we use open hashing and a table size which is the next prime 40%
1052 larger than the number of strings. */
1053 hash_size = next_prime (nnames * 1.4);
1054 hash_table = (struct hash_entry *) xcalloc (hash_size,
1055 sizeof (struct hash_entry));
1056 /* Fill the hash table. */
1057 twalk (names, name_insert);
1059 /* Create the section for the module list. */
1060 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1061 nname_info);
1063 /* Allocate memory for the non-INTERNAL conversions. The allocated
1064 memory can be more than is actually needed. */
1065 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1066 + sizeof (gidx_t)
1067 + sizeof (struct extra_entry_module),
1068 nextra_modules);
1069 cur_extra_table = extra_table;
1071 /* Fill in the module information. */
1072 for (n = 0; n < nname_info; ++n)
1074 module_table[n].canonname_offset =
1075 strtaboffset (name_info[n].canonical_strent);
1077 if (name_info[n].from_internal == NULL)
1079 module_table[n].fromdir_offset = 0;
1080 module_table[n].fromname_offset = 0;
1082 else
1084 module_table[n].fromdir_offset =
1085 strtaboffset (name_info[n].from_internal->directory_strent);
1086 module_table[n].fromname_offset =
1087 strtaboffset (name_info[n].from_internal->filename_strent);
1090 if (name_info[n].to_internal == NULL)
1092 module_table[n].todir_offset = 0;
1093 module_table[n].toname_offset = 0;
1095 else
1097 module_table[n].todir_offset =
1098 strtaboffset (name_info[n].to_internal->directory_strent);
1099 module_table[n].toname_offset =
1100 strtaboffset (name_info[n].to_internal->filename_strent);
1103 if (name_info[n].other_conv_list != NULL)
1105 struct other_conv_list *other = name_info[n].other_conv_list;
1107 /* Store the reference. We add 1 to distinguish the entry
1108 at offset zero from the case where no extra modules are
1109 available. The file reader has to account for the
1110 offset. */
1111 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1115 struct other_conv *runp;
1116 struct extra_entry *extra;
1118 /* Allocate new entry. */
1119 extra = (struct extra_entry *) cur_extra_table;
1120 cur_extra_table += sizeof (struct extra_entry);
1121 extra->module_cnt = 0;
1123 runp = &other->other_conv;
1126 cur_extra_table += sizeof (struct extra_entry_module);
1127 extra->module[extra->module_cnt].outname_offset =
1128 runp->next == NULL
1129 ? other->dest_idx : runp->next->module_idx;
1130 extra->module[extra->module_cnt].dir_offset =
1131 strtaboffset (runp->module->directory_strent);
1132 extra->module[extra->module_cnt].name_offset =
1133 strtaboffset (runp->module->filename_strent);
1134 ++extra->module_cnt;
1136 runp = runp->next;
1138 while (runp != NULL);
1140 other = other->next;
1142 while (other != NULL);
1144 /* Final module_cnt is zero. */
1145 *((gidx_t *) cur_extra_table) = 0;
1146 cur_extra_table += sizeof (gidx_t);
1150 header.magic = GCONVCACHE_MAGIC;
1152 iov[0].iov_base = &header;
1153 iov[0].iov_len = sizeof (struct gconvcache_header);
1154 total = iov[0].iov_len;
1156 header.string_offset = total;
1157 iov[1].iov_base = string_table;
1158 iov[1].iov_len = string_table_size;
1159 total += iov[1].iov_len;
1161 idx = 2;
1162 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1164 iov[2].iov_base = (void *) &null_word;
1165 iov[2].iov_len = (sizeof (gidx_t)
1166 - (string_table_size & (sizeof (gidx_t) - 1)));
1167 total += iov[2].iov_len;
1168 ++idx;
1171 header.hash_offset = total;
1172 header.hash_size = hash_size;
1173 iov[idx].iov_base = hash_table;
1174 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1175 total += iov[idx].iov_len;
1176 ++idx;
1178 header.module_offset = total;
1179 iov[idx].iov_base = module_table;
1180 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1181 total += iov[idx].iov_len;
1182 ++idx;
1184 assert ((size_t) (cur_extra_table - extra_table)
1185 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1186 + sizeof (struct extra_entry_module))
1187 * nextra_modules));
1188 header.otherconv_offset = total;
1189 iov[idx].iov_base = extra_table;
1190 iov[idx].iov_len = cur_extra_table - extra_table;
1191 total += iov[idx].iov_len;
1192 ++idx;
1194 if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1195 /* The file was created with mode 0600. Make it world-readable. */
1196 || fchmod (fd, 0644) != 0
1197 /* Rename the file, possibly replacing an old one. */
1198 || rename (tmpfname, finalname) != 0)
1200 int save_errno = errno;
1201 close (fd);
1202 unlink (tmpfname);
1203 error (EXIT_FAILURE, save_errno,
1204 gettext ("cannot generate output file"));
1207 close (fd);
1209 return 0;