Use macros for x86 PIC thunk names.
[glibc.git] / iconv / iconvconfig.c
blob9738ed890d96374d46439267ffbc647f4d5ed6af
1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000-2011, 2012 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2000.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
11 This program 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
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #include <argp.h>
21 #include <assert.h>
22 #include <error.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <libintl.h>
26 #include <locale.h>
27 #include <mcheck.h>
28 #include <search.h>
29 #include <stdint.h>
30 #include <stdbool.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 #define OPT_NOSTDLIB 301
125 static const struct argp_option options[] =
127 { "prefix", OPT_PREFIX, "PATH", 0, N_("Prefix used for all file accesses") },
128 { "output", 'o', "FILE", 0, N_("\
129 Put output in FILE instead of installed location\
130 (--prefix does not apply to FILE)") },
131 { "nostdlib", OPT_NOSTDLIB, NULL, 0,
132 N_("Do not search standard directories, only those on the command line") },
133 { NULL, 0, NULL, 0, NULL }
136 /* Data structure to communicate with argp functions. */
137 static struct argp argp =
139 options, parse_opt, args_doc, doc, NULL, more_help
143 /* The function doing the actual work. */
144 static int handle_dir (const char *dir);
146 /* Add all known builtin conversions and aliases. */
147 static void add_builtins (void);
149 /* Create list of all aliases without circular aliases. */
150 static void get_aliases (void);
152 /* Create list of all modules. */
153 static void get_modules (void);
155 /* Get list of all the names and thereby indexing them. */
156 static void generate_name_list (void);
158 /* Collect information about all the names. */
159 static void generate_name_info (void);
161 /* Write the output file. */
162 static int write_output (void);
165 /* Prefix to be used for all file accesses. */
166 static const char *prefix = "";
167 /* Its length. */
168 static size_t prefix_len;
170 /* Directory to place output file in. */
171 static const char *output_file;
172 /* Its length. */
173 static size_t output_file_len;
175 /* If true, omit the GCONV_PATH directories and require some arguments. */
176 static bool nostdlib;
178 /* Search tree of the modules we know. */
179 static void *modules;
181 /* Search tree of the aliases we know. */
182 static void *aliases;
184 /* Search tree for name to index mapping. */
185 static void *names;
187 /* Number of names we know about. */
188 static int nnames;
190 /* List of all aliases. */
191 static struct alias **alias_list;
192 static size_t nalias_list;
193 static size_t nalias_list_max;
195 /* List of all modules. */
196 static struct module **module_list;
197 static size_t nmodule_list;
198 static size_t nmodule_list_max;
200 /* Names and information about them. */
201 static struct name_info *name_info;
202 static size_t nname_info;
204 /* Number of translations not from or to INTERNAL. */
205 static size_t nextra_modules;
208 /* Names and aliases for the builtin transformations. */
209 static struct
211 const char *from;
212 const char *to;
213 } builtin_alias[] =
215 #define BUILTIN_ALIAS(alias, real) \
216 { .from = alias, .to = real },
217 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
218 MinF, MaxF, MinT, MaxT)
219 #include <gconv_builtin.h>
221 #undef BUILTIN_ALIAS
222 #undef BUILTIN_TRANSFORMATION
223 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
225 static struct
227 const char *from;
228 const char *to;
229 const char *module;
230 int cost;
231 } builtin_trans[] =
233 #define BUILTIN_ALIAS(alias, real)
234 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
235 MinF, MaxF, MinT, MaxT) \
236 { .from = From, .to = To, .module = Name, .cost = Cost },
237 #include <gconv_builtin.h>
239 #undef BUILTIN_ALIAS
240 #undef BUILTIN_TRANSFORMATION
241 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
244 /* Filename extension for the modules. */
245 #ifndef MODULE_EXT
246 # define MODULE_EXT ".so"
247 #endif
248 static const char gconv_module_ext[] = MODULE_EXT;
251 extern void *xmalloc (size_t n)
252 __attribute_malloc__ __attribute_alloc_size (1);
253 extern void *xcalloc (size_t n, size_t s)
254 __attribute_malloc__ __attribute_alloc_size (1, 2);
255 extern void *xrealloc (void *o, size_t n)
256 __attribute_malloc__ __attribute_alloc_size (2);
259 /* C string table handling. */
260 struct Strtab;
261 struct Strent;
263 /* Create new C string table object in memory. */
264 extern struct Strtab *strtabinit (void);
266 /* Free resources allocated for C string table ST. */
267 extern void strtabfree (struct Strtab *st);
269 /* Add string STR (length LEN is != 0) to C string table ST. */
270 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
271 size_t len);
273 /* Finalize string table ST and store size in *SIZE and return a pointer. */
274 extern void *strtabfinalize (struct Strtab *st, size_t *size);
276 /* Get offset in string table for string associated with SE. */
277 extern size_t strtaboffset (struct Strent *se);
279 /* String table we construct. */
280 static struct Strtab *strtab;
285 main (int argc, char *argv[])
287 int remaining;
288 int status = 0;
290 /* Enable memory use testing. */
291 /* mcheck_pedantic (NULL); */
292 mtrace ();
294 /* Set locale via LC_ALL. */
295 setlocale (LC_ALL, "");
297 /* Set the text message domain. */
298 textdomain (_libc_intl_domainname);
300 /* Parse and process arguments. */
301 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
303 if (nostdlib && remaining == argc)
304 error (2, 0, _("Directory arguments required when using --nostdlib"));
306 /* Initialize the string table. */
307 strtab = strtabinit ();
309 /* Handle all directories mentioned. */
310 while (remaining < argc)
311 status |= handle_dir (argv[remaining++]);
313 if (! nostdlib)
315 /* In any case also handle the standard directory. */
316 char *path = strdupa (GCONV_PATH), *tp = strsep (&path, ":");
317 while (tp != NULL)
319 status |= handle_dir (tp);
321 tp = strsep (&path, ":");
325 /* Add the builtin transformations and aliases without overwriting
326 anything. */
327 add_builtins ();
329 /* Store aliases in an array. */
330 get_aliases ();
332 /* Get list of all modules. */
333 get_modules ();
335 /* Generate list of all the names we know to handle in some way. */
336 generate_name_list ();
338 /* Now we know all the names we will handle, collect information
339 about them. */
340 generate_name_info ();
342 /* Write the output file, but only if we haven't seen any error. */
343 if (status == 0)
344 status = write_output ();
345 else
346 error (1, 0, _("no output file produced because warnings were issued"));
348 return status;
352 /* Handle program arguments. */
353 static error_t
354 parse_opt (int key, char *arg, struct argp_state *state)
356 switch (key)
358 case OPT_PREFIX:
359 prefix = arg;
360 prefix_len = strlen (prefix);
361 break;
362 case 'o':
363 output_file = arg;
364 output_file_len = strlen (output_file);
365 break;
366 case OPT_NOSTDLIB:
367 nostdlib = true;
368 break;
369 default:
370 return ARGP_ERR_UNKNOWN;
372 return 0;
376 static char *
377 more_help (int key, const char *text, void *input)
379 switch (key)
381 case ARGP_KEY_HELP_EXTRA:
382 /* We print some extra information. */
383 return strdup (gettext ("\
384 For bug reporting instructions, please see:\n\
385 <http://www.gnu.org/software/libc/bugs.html>.\n"));
386 default:
387 break;
389 return (char *) text;
393 /* Print the version information. */
394 static void
395 print_version (FILE *stream, struct argp_state *state)
397 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
398 fprintf (stream, gettext ("\
399 Copyright (C) %s Free Software Foundation, Inc.\n\
400 This is free software; see the source for copying conditions. There is NO\n\
401 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
402 "), "2012");
403 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
407 static int
408 alias_compare (const void *p1, const void *p2)
410 const struct alias *a1 = (const struct alias *) p1;
411 const struct alias *a2 = (const struct alias *) p2;
413 return strcmp (a1->fromname, a2->fromname);
417 static void
418 new_alias (const char *fromname, size_t fromlen, const char *toname,
419 size_t tolen)
421 struct alias *newp;
422 void **inserted;
424 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
426 newp->fromname = mempcpy (newp->toname, toname, tolen);
427 memcpy (newp->fromname, fromname, fromlen);
428 newp->module = NULL;
430 inserted = (void **) tsearch (newp, &aliases, alias_compare);
431 if (inserted == NULL)
432 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
433 if (*inserted != newp)
434 /* Something went wrong, free this entry. */
435 free (newp);
436 else
438 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
439 newp->toent = strtabadd (strtab, newp->toname, tolen);
444 /* Add new alias. */
445 static void
446 add_alias (char *rp)
448 /* We now expect two more string. The strings are normalized
449 (converted to UPPER case) and strored in the alias database. */
450 char *from;
451 char *to;
452 char *wp;
454 while (isspace (*rp))
455 ++rp;
456 from = wp = rp;
457 while (*rp != '\0' && !isspace (*rp))
458 *wp++ = toupper (*rp++);
459 if (*rp == '\0')
460 /* There is no `to' string on the line. Ignore it. */
461 return;
462 *wp++ = '\0';
463 to = ++rp;
464 while (isspace (*rp))
465 ++rp;
466 while (*rp != '\0' && !isspace (*rp))
467 *wp++ = toupper (*rp++);
468 if (to == wp)
469 /* No `to' string, ignore the line. */
470 return;
471 *wp++ = '\0';
473 assert (strlen (from) + 1 == (size_t) (to - from));
474 assert (strlen (to) + 1 == (size_t) (wp - to));
476 new_alias (from, to - from, to, wp - to);
480 static void
481 append_alias (const void *nodep, VISIT value, int level)
483 if (value != leaf && value != postorder)
484 return;
486 if (nalias_list_max == nalias_list)
488 nalias_list_max += 50;
489 alias_list = (struct alias **) xrealloc (alias_list,
490 (nalias_list_max
491 * sizeof (struct alias *)));
494 alias_list[nalias_list++] = *(struct alias **) nodep;
498 static void
499 get_aliases (void)
501 twalk (aliases, append_alias);
505 static int
506 module_compare (const void *p1, const void *p2)
508 const struct module *m1 = (const struct module *) p1;
509 const struct module *m2 = (const struct module *) p2;
510 int result;
512 result = strcmp (m1->fromname, m2->fromname);
513 if (result == 0)
514 result = strcmp (m1->toname, m2->toname);
516 return result;
520 /* Create new module record. */
521 static void
522 new_module (const char *fromname, size_t fromlen, const char *toname,
523 size_t tolen, const char *directory,
524 const char *filename, size_t filelen, int cost, size_t need_ext)
526 struct module *new_module;
527 size_t dirlen = strlen (directory) + 1;
528 char *tmp;
529 void **inserted;
531 new_module = (struct module *) xmalloc (sizeof (struct module)
532 + fromlen + tolen + filelen
533 + need_ext);
535 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
537 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
539 new_module->cost = cost;
540 new_module->next = NULL;
542 tmp = mempcpy (new_module->filename, filename, filelen);
543 if (need_ext)
545 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
546 filelen += need_ext;
548 new_module->directory = directory;
550 /* Now insert the new module data structure in our search tree. */
551 inserted = (void **) tsearch (new_module, &modules, module_compare);
552 if (inserted == NULL)
553 error (EXIT_FAILURE, errno, "while inserting in search tree");
554 if (*inserted != new_module)
555 free (new_module);
556 else
558 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
559 fromlen);
560 new_module->toname_strent = strtabadd (strtab, new_module->toname,
561 tolen);
562 new_module->filename_strent = strtabadd (strtab, new_module->filename,
563 filelen);
564 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
569 /* Add new module. */
570 static void
571 internal_function
572 add_module (char *rp, const char *directory)
574 /* We expect now
575 1. `from' name
576 2. `to' name
577 3. filename of the module
578 4. an optional cost value
580 char *from;
581 char *to;
582 char *module;
583 char *wp;
584 int need_ext;
585 int cost;
587 while (isspace (*rp))
588 ++rp;
589 from = rp;
590 while (*rp != '\0' && !isspace (*rp))
592 *rp = toupper (*rp);
593 ++rp;
595 if (*rp == '\0')
596 return;
597 *rp++ = '\0';
598 to = wp = rp;
599 while (isspace (*rp))
600 ++rp;
601 while (*rp != '\0' && !isspace (*rp))
602 *wp++ = toupper (*rp++);
603 if (*rp == '\0')
604 return;
605 *wp++ = '\0';
607 ++rp;
608 while (isspace (*rp));
609 module = wp;
610 while (*rp != '\0' && !isspace (*rp))
611 *wp++ = *rp++;
612 if (*rp == '\0')
614 /* There is no cost, use one by default. */
615 *wp++ = '\0';
616 cost = 1;
618 else
620 /* There might be a cost value. */
621 char *endp;
623 *wp++ = '\0';
624 cost = strtol (rp, &endp, 10);
625 if (rp == endp || cost < 1)
626 /* No useful information. */
627 cost = 1;
630 if (module[0] == '\0')
631 /* No module name given. */
632 return;
634 /* See whether we must add the ending. */
635 need_ext = 0;
636 if ((size_t) (wp - module) < sizeof (gconv_module_ext)
637 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
638 sizeof (gconv_module_ext)) != 0)
639 /* We must add the module extension. */
640 need_ext = sizeof (gconv_module_ext) - 1;
642 assert (strlen (from) + 1 == (size_t) (to - from));
643 assert (strlen (to) + 1 == (size_t) (module - to));
644 assert (strlen (module) + 1 == (size_t) (wp - module));
646 new_module (from, to - from, to, module - to, directory, module, wp - module,
647 cost, need_ext);
651 /* Read the config file and add the data for this directory to that. */
652 static int
653 handle_dir (const char *dir)
655 char *cp;
656 FILE *fp;
657 char *line = NULL;
658 size_t linelen = 0;
659 size_t dirlen = strlen (dir);
661 if (dir[dirlen - 1] != '/')
663 char *newp = (char *) xmalloc (dirlen + 2);
664 dir = memcpy (newp, dir, dirlen);
665 newp[dirlen++] = '/';
666 newp[dirlen] = '\0';
669 char infile[prefix_len + dirlen + sizeof "gconv-modules"];
670 cp = infile;
671 if (dir[0] == '/')
672 cp = mempcpy (cp, prefix, prefix_len);
673 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
675 fp = fopen (infile, "r");
676 if (fp == NULL)
678 error (0, errno, "cannot open `%s'", infile);
679 return 1;
682 /* No threads present. */
683 __fsetlocking (fp, FSETLOCKING_BYCALLER);
685 while (!feof_unlocked (fp))
687 char *rp, *endp, *word;
688 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
690 if (n < 0)
691 /* An error occurred. */
692 break;
694 rp = line;
695 /* Terminate the line (excluding comments or newline) with a NUL
696 byte to simplify the following code. */
697 endp = strchr (rp, '#');
698 if (endp != NULL)
699 *endp = '\0';
700 else
701 if (rp[n - 1] == '\n')
702 rp[n - 1] = '\0';
704 while (isspace (*rp))
705 ++rp;
707 /* If this is an empty line go on with the next one. */
708 if (rp == endp)
709 continue;
711 word = rp;
712 while (*rp != '\0' && !isspace (*rp))
713 ++rp;
715 if (rp - word == sizeof ("alias") - 1
716 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
717 add_alias (rp);
718 else if (rp - word == sizeof ("module") - 1
719 && memcmp (word, "module", sizeof ("module") - 1) == 0)
720 add_module (rp, dir);
721 /* else */
722 /* Otherwise ignore the line. */
725 free (line);
727 fclose (fp);
729 return 0;
733 static void
734 append_module (const void *nodep, VISIT value, int level)
736 struct module *mo;
738 if (value != leaf && value != postorder)
739 return;
741 mo = *(struct module **) nodep;
743 if (nmodule_list > 0
744 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
746 /* Same name. */
747 mo->next = module_list[nmodule_list - 1];
748 module_list[nmodule_list - 1] = mo;
750 return;
753 if (nmodule_list_max == nmodule_list)
755 nmodule_list_max += 50;
756 module_list = (struct module **) xrealloc (module_list,
757 (nmodule_list_max
758 * sizeof (struct module *)));
761 module_list[nmodule_list++] = mo;
765 static void
766 get_modules (void)
768 twalk (modules, append_module);
772 static void
773 add_builtins (void)
775 size_t cnt;
777 /* Add all aliases. */
778 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
779 new_alias (builtin_alias[cnt].from,
780 strlen (builtin_alias[cnt].from) + 1,
781 builtin_alias[cnt].to,
782 strlen (builtin_alias[cnt].to) + 1);
784 /* add the builtin transformations. */
785 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
786 new_module (builtin_trans[cnt].from,
787 strlen (builtin_trans[cnt].from) + 1,
788 builtin_trans[cnt].to,
789 strlen (builtin_trans[cnt].to) + 1,
790 "", builtin_trans[cnt].module,
791 strlen (builtin_trans[cnt].module) + 1,
792 builtin_trans[cnt].cost, 0);
796 static int
797 name_compare (const void *p1, const void *p2)
799 const struct name *n1 = (const struct name *) p1;
800 const struct name *n2 = (const struct name *) p2;
802 return strcmp (n1->name, n2->name);
806 static struct name *
807 new_name (const char *str, struct Strent *strent)
809 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
811 newp->name = str;
812 newp->strent = strent;
813 newp->module_idx = -1;
814 newp->hashval = __hash_string (str);
816 ++nnames;
818 return newp;
822 static void
823 generate_name_list (void)
825 size_t i;
827 /* A name we always need. */
828 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
829 sizeof ("INTERNAL"))),
830 &names, name_compare);
832 for (i = 0; i < nmodule_list; ++i)
834 struct module *runp;
836 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
837 tsearch (new_name (module_list[i]->fromname,
838 module_list[i]->fromname_strent),
839 &names, name_compare);
841 for (runp = module_list[i]; runp != NULL; runp = runp->next)
842 if (strcmp (runp->toname, "INTERNAL") != 0)
843 tsearch (new_name (runp->toname, runp->toname_strent),
844 &names, name_compare);
849 static int
850 name_to_module_idx (const char *name, int add)
852 struct name **res;
853 struct name fake_name = { .name = name };
854 int idx;
856 res = (struct name **) tfind (&fake_name, &names, name_compare);
857 if (res == NULL)
858 abort ();
860 idx = (*res)->module_idx;
861 if (idx == -1 && add)
862 /* No module index assigned yet. */
863 idx = (*res)->module_idx = nname_info++;
865 return idx;
869 static void
870 generate_name_info (void)
872 size_t i;
873 int idx;
875 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
876 sizeof (struct name_info));
878 /* First add a special entry for the INTERNAL name. This must have
879 index zero. */
880 idx = name_to_module_idx ("INTERNAL", 1);
881 name_info[0].canonical_name = "INTERNAL";
882 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
883 sizeof ("INTERNAL"));
884 assert (nname_info == 1);
886 for (i = 0; i < nmodule_list; ++i)
888 struct module *runp;
890 for (runp = module_list[i]; runp != NULL; runp = runp->next)
891 if (strcmp (runp->fromname, "INTERNAL") == 0)
893 idx = name_to_module_idx (runp->toname, 1);
894 name_info[idx].from_internal = runp;
895 assert (name_info[idx].canonical_name == NULL
896 || strcmp (name_info[idx].canonical_name,
897 runp->toname) == 0);
898 name_info[idx].canonical_name = runp->toname;
899 name_info[idx].canonical_strent = runp->toname_strent;
901 else if (strcmp (runp->toname, "INTERNAL") == 0)
903 idx = name_to_module_idx (runp->fromname, 1);
904 name_info[idx].to_internal = runp;
905 assert (name_info[idx].canonical_name == NULL
906 || strcmp (name_info[idx].canonical_name,
907 runp->fromname) == 0);
908 name_info[idx].canonical_name = runp->fromname;
909 name_info[idx].canonical_strent = runp->fromname_strent;
911 else
913 /* This is a transformation not to or from the INTERNAL
914 encoding. */
915 int from_idx = name_to_module_idx (runp->fromname, 1);
916 int to_idx = name_to_module_idx (runp->toname, 1);
917 struct other_conv_list *newp;
919 newp = (struct other_conv_list *)
920 xmalloc (sizeof (struct other_conv_list));
921 newp->other_conv.module_idx = to_idx;
922 newp->other_conv.module = runp;
923 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
924 newp->dest_idx = to_idx;
925 newp->next = name_info[from_idx].other_conv_list;
926 name_info[from_idx].other_conv_list = newp;
927 assert (name_info[from_idx].canonical_name == NULL
928 || strcmp (name_info[from_idx].canonical_name,
929 runp->fromname) == 0);
930 name_info[from_idx].canonical_name = runp->fromname;
931 name_info[from_idx].canonical_strent = runp->fromname_strent;
933 ++nextra_modules;
937 /* Now add the module index information for all the aliases. */
938 for (i = 0; i < nalias_list; ++i)
940 struct name fake_name = { .name = alias_list[i]->toname };
941 struct name **tonamep;
943 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
944 if (tonamep != NULL)
946 struct name *newp = new_name (alias_list[i]->fromname,
947 alias_list[i]->froment);
948 newp->module_idx = (*tonamep)->module_idx;
949 tsearch (newp, &names, name_compare);
955 static int
956 is_prime (unsigned long int candidate)
958 /* No even number and none less than 10 will be passed here. */
959 unsigned long int divn = 3;
960 unsigned long int sq = divn * divn;
962 while (sq < candidate && candidate % divn != 0)
964 ++divn;
965 sq += 4 * divn;
966 ++divn;
969 return candidate % divn != 0;
973 static uint32_t
974 next_prime (uint32_t seed)
976 /* Make it definitely odd. */
977 seed |= 1;
979 while (!is_prime (seed))
980 seed += 2;
982 return seed;
986 /* Format of the output file.
988 Offset Length Description
989 0000 4 Magic header bytes
990 0004 2 Offset of string table (stoff)
991 0006 2 Offset of name hashing table (hoff)
992 0008 2 Hashing table size (hsize)
993 000A 2 Offset of module table (moff)
994 000C 2 Offset of other conversion module table (ooff)
996 stoff ??? String table
998 hoff 8*hsize Array of tuples
999 string table offset
1000 module index
1002 moff ??? Array of tuples
1003 canonical name offset
1004 from-internal module dir name offset
1005 from-internal module name off
1006 to-internal module dir name offset
1007 to-internal module name offset
1008 offset into other conversion table
1010 ooff ??? One or more of
1011 number of steps/modules
1012 one or more of tuple
1013 canonical name offset for output
1014 module dir name offset
1015 module name offset
1016 (following last entry with step count 0)
1019 static struct hash_entry *hash_table;
1020 static size_t hash_size;
1022 /* Function to insert the names. */
1023 static void name_insert (const void *nodep, VISIT value, int level)
1025 struct name *name;
1026 unsigned int idx;
1027 unsigned int hval2;
1029 if (value != leaf && value != postorder)
1030 return;
1032 name = *(struct name **) nodep;
1033 idx = name->hashval % hash_size;
1034 hval2 = 1 + name->hashval % (hash_size - 2);
1036 while (hash_table[idx].string_offset != 0)
1037 if ((idx += hval2) >= hash_size)
1038 idx -= hash_size;
1040 hash_table[idx].string_offset = strtaboffset (name->strent);
1042 assert (name->module_idx != -1);
1043 hash_table[idx].module_idx = name->module_idx;
1046 static int
1047 write_output (void)
1049 int fd;
1050 char *string_table;
1051 size_t string_table_size;
1052 struct gconvcache_header header;
1053 struct module_entry *module_table;
1054 char *extra_table;
1055 char *cur_extra_table;
1056 size_t n;
1057 int idx;
1058 struct iovec iov[6];
1059 static const gidx_t null_word;
1060 size_t total;
1061 char finalname[prefix_len + sizeof GCONV_MODULES_CACHE];
1062 char tmpfname[(output_file == NULL ? sizeof finalname : output_file_len + 1)
1063 + strlen (".XXXXXX")];
1065 /* Open the output file. */
1066 if (output_file == NULL)
1068 assert (GCONV_MODULES_CACHE[0] == '/');
1069 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len),
1070 GCONV_MODULES_CACHE),
1071 ".XXXXXX");
1072 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1074 else
1075 strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX");
1076 fd = mkstemp (tmpfname);
1077 if (fd == -1)
1078 return 1;
1080 /* Create the string table. */
1081 string_table = strtabfinalize (strtab, &string_table_size);
1083 /* Create the hashing table. We know how many strings we have.
1084 Creating a perfect hash table is not reasonable here. Therefore
1085 we use open hashing and a table size which is the next prime 40%
1086 larger than the number of strings. */
1087 hash_size = next_prime (nnames * 1.4);
1088 hash_table = (struct hash_entry *) xcalloc (hash_size,
1089 sizeof (struct hash_entry));
1090 /* Fill the hash table. */
1091 twalk (names, name_insert);
1093 /* Create the section for the module list. */
1094 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1095 nname_info);
1097 /* Allocate memory for the non-INTERNAL conversions. The allocated
1098 memory can be more than is actually needed. */
1099 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1100 + sizeof (gidx_t)
1101 + sizeof (struct extra_entry_module),
1102 nextra_modules);
1103 cur_extra_table = extra_table;
1105 /* Fill in the module information. */
1106 for (n = 0; n < nname_info; ++n)
1108 module_table[n].canonname_offset =
1109 strtaboffset (name_info[n].canonical_strent);
1111 if (name_info[n].from_internal == NULL)
1113 module_table[n].fromdir_offset = 0;
1114 module_table[n].fromname_offset = 0;
1116 else
1118 module_table[n].fromdir_offset =
1119 strtaboffset (name_info[n].from_internal->directory_strent);
1120 module_table[n].fromname_offset =
1121 strtaboffset (name_info[n].from_internal->filename_strent);
1124 if (name_info[n].to_internal == NULL)
1126 module_table[n].todir_offset = 0;
1127 module_table[n].toname_offset = 0;
1129 else
1131 module_table[n].todir_offset =
1132 strtaboffset (name_info[n].to_internal->directory_strent);
1133 module_table[n].toname_offset =
1134 strtaboffset (name_info[n].to_internal->filename_strent);
1137 if (name_info[n].other_conv_list != NULL)
1139 struct other_conv_list *other = name_info[n].other_conv_list;
1141 /* Store the reference. We add 1 to distinguish the entry
1142 at offset zero from the case where no extra modules are
1143 available. The file reader has to account for the
1144 offset. */
1145 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1149 struct other_conv *runp;
1150 struct extra_entry *extra;
1152 /* Allocate new entry. */
1153 extra = (struct extra_entry *) cur_extra_table;
1154 cur_extra_table += sizeof (struct extra_entry);
1155 extra->module_cnt = 0;
1157 runp = &other->other_conv;
1160 cur_extra_table += sizeof (struct extra_entry_module);
1161 extra->module[extra->module_cnt].outname_offset =
1162 runp->next == NULL
1163 ? other->dest_idx : runp->next->module_idx;
1164 extra->module[extra->module_cnt].dir_offset =
1165 strtaboffset (runp->module->directory_strent);
1166 extra->module[extra->module_cnt].name_offset =
1167 strtaboffset (runp->module->filename_strent);
1168 ++extra->module_cnt;
1170 runp = runp->next;
1172 while (runp != NULL);
1174 other = other->next;
1176 while (other != NULL);
1178 /* Final module_cnt is zero. */
1179 *((gidx_t *) cur_extra_table) = 0;
1180 cur_extra_table += sizeof (gidx_t);
1184 /* Clear padding. */
1185 memset (&header, 0, sizeof (struct gconvcache_header));
1187 header.magic = GCONVCACHE_MAGIC;
1189 iov[0].iov_base = &header;
1190 iov[0].iov_len = sizeof (struct gconvcache_header);
1191 total = iov[0].iov_len;
1193 header.string_offset = total;
1194 iov[1].iov_base = string_table;
1195 iov[1].iov_len = string_table_size;
1196 total += iov[1].iov_len;
1198 idx = 2;
1199 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1201 iov[2].iov_base = (void *) &null_word;
1202 iov[2].iov_len = (sizeof (gidx_t)
1203 - (string_table_size & (sizeof (gidx_t) - 1)));
1204 total += iov[2].iov_len;
1205 ++idx;
1208 header.hash_offset = total;
1209 header.hash_size = hash_size;
1210 iov[idx].iov_base = hash_table;
1211 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1212 total += iov[idx].iov_len;
1213 ++idx;
1215 header.module_offset = total;
1216 iov[idx].iov_base = module_table;
1217 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1218 total += iov[idx].iov_len;
1219 ++idx;
1221 assert ((size_t) (cur_extra_table - extra_table)
1222 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1223 + sizeof (struct extra_entry_module))
1224 * nextra_modules));
1225 header.otherconv_offset = total;
1226 iov[idx].iov_base = extra_table;
1227 iov[idx].iov_len = cur_extra_table - extra_table;
1228 total += iov[idx].iov_len;
1229 ++idx;
1231 if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1232 /* The file was created with mode 0600. Make it world-readable. */
1233 || fchmod (fd, 0644) != 0
1234 /* Rename the file, possibly replacing an old one. */
1235 || rename (tmpfname, output_file ?: finalname) != 0)
1237 int save_errno = errno;
1238 close (fd);
1239 unlink (tmpfname);
1240 error (EXIT_FAILURE, save_errno,
1241 gettext ("cannot generate output file"));
1244 close (fd);
1246 return 0;