Remove *xattr syscalls.
[glibc.git] / iconv / iconvconfig.c
blob27b8b02dacc9aa6aa2025c129bc974d3756c0589
1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001, 2002 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, MinF, MaxF, \
205 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, MinF, MaxF, \
222 MinT, MaxT) \
223 { .from = From, .to = To, .module = Name, .cost = Cost },
224 #include <gconv_builtin.h>
226 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
229 /* Filename extension for the modules. */
230 #ifndef MODULE_EXT
231 # define MODULE_EXT ".so"
232 #endif
233 static const char gconv_module_ext[] = MODULE_EXT;
236 extern void *xmalloc (size_t n) __attribute_malloc__;
237 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
238 extern void *xrealloc (void *p, size_t n);
241 /* C string table handling. */
242 struct Strtab;
243 struct Strent;
245 /* Create new C string table object in memory. */
246 extern struct Strtab *strtabinit (void);
248 /* Free resources allocated for C string table ST. */
249 extern void strtabfree (struct Strtab *st);
251 /* Add string STR (length LEN is != 0) to C string table ST. */
252 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
253 size_t len);
255 /* Finalize string table ST and store size in *SIZE and return a pointer. */
256 extern void *strtabfinalize (struct Strtab *st, size_t *size);
258 /* Get offset in string table for string associated with SE. */
259 extern size_t strtaboffset (struct Strent *se);
261 /* String table we construct. */
262 static struct Strtab *strtab;
267 main (int argc, char *argv[])
269 int remaining;
270 int status = 0;
271 char *path;
272 char *tp;
274 /* Enable memory use testing. */
275 /* mcheck_pedantic (NULL); */
276 mtrace ();
278 /* Set locale via LC_ALL. */
279 setlocale (LC_ALL, "");
281 /* Set the text message domain. */
282 textdomain (_libc_intl_domainname);
284 /* Parse and process arguments. */
285 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
287 /* Initialize the string table. */
288 strtab = strtabinit ();
290 /* Handle all directories mentioned. */
291 while (remaining < argc)
292 status |= handle_dir (argv[remaining++]);
294 /* In any case also handle the standard directory. */
295 path = strdupa (GCONV_PATH);
296 tp = strtok (path, ":");
297 while (tp != NULL)
299 status |= handle_dir (tp);
301 tp = strtok (NULL, ":");
304 /* Add the builtin transformations and aliases without overwriting
305 anything. */
306 add_builtins ();
308 /* Store aliases in an array. */
309 get_aliases ();
311 /* Get list of all modules. */
312 get_modules ();
314 /* Generate list of all the names we know to handle in some way. */
315 generate_name_list ();
317 /* Now we know all the names we will handle, collect information
318 about them. */
319 generate_name_info ();
321 /* Write the output file, but only if we haven't seen any error. */
322 if (status == 0)
323 status = write_output ();
324 else
325 error (1, 0, _("no output file produced because warning were issued"));
327 return status;
331 /* Handle program arguments. */
332 static error_t
333 parse_opt (int key, char *arg, struct argp_state *state)
335 switch (key)
337 case OPT_PREFIX:
338 prefix = arg;
339 prefix_len = strlen (prefix);
340 break;
341 default:
342 return ARGP_ERR_UNKNOWN;
344 return 0;
348 static char *
349 more_help (int key, const char *text, void *input)
351 switch (key)
353 case ARGP_KEY_HELP_EXTRA:
354 /* We print some extra information. */
355 return strdup (gettext ("\
356 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
357 default:
358 break;
360 return (char *) text;
364 /* Print the version information. */
365 static void
366 print_version (FILE *stream, struct argp_state *state)
368 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
369 fprintf (stream, gettext ("\
370 Copyright (C) %s Free Software Foundation, Inc.\n\
371 This is free software; see the source for copying conditions. There is NO\n\
372 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
373 "), "2002");
374 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
378 static int
379 alias_compare (const void *p1, const void *p2)
381 const struct alias *a1 = (const struct alias *) p1;
382 const struct alias *a2 = (const struct alias *) p2;
384 return strcmp (a1->fromname, a2->fromname);
388 static void
389 new_alias (const char *fromname, size_t fromlen, const char *toname,
390 size_t tolen)
392 struct alias *newp;
393 void **inserted;
395 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
397 newp->fromname = mempcpy (newp->toname, toname, tolen);
398 memcpy (newp->fromname, fromname, fromlen);
399 newp->module = NULL;
401 inserted = (void **) tsearch (newp, &aliases, alias_compare);
402 if (inserted == NULL)
403 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
404 if (*inserted != newp)
405 /* Something went wrong, free this entry. */
406 free (newp);
407 else
409 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
410 newp->toent = strtabadd (strtab, newp->toname, tolen);
415 /* Add new alias. */
416 static void
417 add_alias (char *rp)
419 /* We now expect two more string. The strings are normalized
420 (converted to UPPER case) and strored in the alias database. */
421 char *from;
422 char *to;
423 char *wp;
425 while (isspace (*rp))
426 ++rp;
427 from = wp = rp;
428 while (*rp != '\0' && !isspace (*rp))
429 *wp++ = toupper (*rp++);
430 if (*rp == '\0')
431 /* There is no `to' string on the line. Ignore it. */
432 return;
433 *wp++ = '\0';
434 to = ++rp;
435 while (isspace (*rp))
436 ++rp;
437 while (*rp != '\0' && !isspace (*rp))
438 *wp++ = toupper (*rp++);
439 if (to == wp)
440 /* No `to' string, ignore the line. */
441 return;
442 *wp++ = '\0';
444 assert (strlen (from) + 1 == to - from);
445 assert (strlen (to) + 1 == wp - to);
447 new_alias (from, to - from, to, wp - to);
451 static void
452 append_alias (const void *nodep, VISIT value, int level)
454 if (value != leaf && value != postorder)
455 return;
457 if (nalias_list_max == nalias_list)
459 nalias_list_max += 50;
460 alias_list = (struct alias **) xrealloc (alias_list,
461 (nalias_list_max
462 * sizeof (struct alias *)));
465 alias_list[nalias_list++] = *(struct alias **) nodep;
469 static void
470 get_aliases (void)
472 twalk (aliases, append_alias);
476 static int
477 module_compare (const void *p1, const void *p2)
479 const struct module *m1 = (const struct module *) p1;
480 const struct module *m2 = (const struct module *) p2;
481 int result;
483 result = strcmp (m1->fromname, m2->fromname);
484 if (result == 0)
485 result = strcmp (m1->toname, m2->toname);
487 return result;
491 /* Create new module record. */
492 static void
493 new_module (const char *fromname, size_t fromlen, const char *toname,
494 size_t tolen, const char *directory,
495 const char *filename, size_t filelen, int cost, size_t need_ext)
497 struct module *new_module;
498 size_t dirlen = strlen (directory) + 1;
499 char *tmp;
500 void **inserted;
502 new_module = (struct module *) xmalloc (sizeof (struct module)
503 + fromlen + tolen + filelen
504 + need_ext);
506 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
508 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
510 new_module->cost = cost;
511 new_module->next = NULL;
513 tmp = mempcpy (new_module->filename, filename, filelen);
514 if (need_ext)
516 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
517 filelen += need_ext;
519 new_module->directory = directory;
521 /* Now insert the new module data structure in our search tree. */
522 inserted = (void **) tsearch (new_module, &modules, module_compare);
523 if (inserted == NULL)
524 error (EXIT_FAILURE, errno, "while inserting in search tree");
525 if (*inserted != new_module)
526 free (new_module);
527 else
529 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
530 fromlen);
531 new_module->toname_strent = strtabadd (strtab, new_module->toname,
532 tolen);
533 new_module->filename_strent = strtabadd (strtab, new_module->filename,
534 filelen);
535 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
540 /* Add new module. */
541 static void
542 internal_function
543 add_module (char *rp, const char *directory)
545 /* We expect now
546 1. `from' name
547 2. `to' name
548 3. filename of the module
549 4. an optional cost value
551 char *from;
552 char *to;
553 char *module;
554 char *wp;
555 int need_ext;
556 int cost;
558 while (isspace (*rp))
559 ++rp;
560 from = rp;
561 while (*rp != '\0' && !isspace (*rp))
563 *rp = toupper (*rp);
564 ++rp;
566 if (*rp == '\0')
567 return;
568 *rp++ = '\0';
569 to = wp = rp;
570 while (isspace (*rp))
571 ++rp;
572 while (*rp != '\0' && !isspace (*rp))
573 *wp++ = toupper (*rp++);
574 if (*rp == '\0')
575 return;
576 *wp++ = '\0';
578 ++rp;
579 while (isspace (*rp));
580 module = wp;
581 while (*rp != '\0' && !isspace (*rp))
582 *wp++ = *rp++;
583 if (*rp == '\0')
585 /* There is no cost, use one by default. */
586 *wp++ = '\0';
587 cost = 1;
589 else
591 /* There might be a cost value. */
592 char *endp;
594 *wp++ = '\0';
595 cost = strtol (rp, &endp, 10);
596 if (rp == endp || cost < 1)
597 /* No useful information. */
598 cost = 1;
601 if (module[0] == '\0')
602 /* No module name given. */
603 return;
605 /* See whether we must add the ending. */
606 need_ext = 0;
607 if (wp - module < sizeof (gconv_module_ext)
608 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
609 sizeof (gconv_module_ext)) != 0)
610 /* We must add the module extension. */
611 need_ext = sizeof (gconv_module_ext) - 1;
613 assert (strlen (from) + 1 == to - from);
614 assert (strlen (to) + 1 == module - to);
615 assert (strlen (module) + 1 == wp - module);
617 new_module (from, to - from, to, module - to, directory, module, wp - module,
618 cost, need_ext);
622 /* Read the config file and add the data for this directory to that. */
623 static int
624 handle_dir (const char *dir)
626 char *infile;
627 char *cp;
628 FILE *fp;
629 char *line = NULL;
630 size_t linelen = 0;
631 size_t dirlen = strlen (dir);
633 if (dir[dirlen - 1] != '/')
635 char *newp = (char *) xmalloc (dirlen + 2);
636 dir = memcpy (newp, dir, dirlen);
637 newp[dirlen++] = '/';
638 newp[dirlen] = '\0';
641 cp = infile = (char *) alloca (prefix_len + dirlen + sizeof "gconv-modules");
642 if (dir[0] == '/')
643 cp = mempcpy (cp, prefix, prefix_len);
644 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
646 fp = fopen (infile, "r");
647 if (fp == NULL)
649 error (0, errno, "cannot open `%s'", infile);
650 return 1;
653 /* No threads present. */
654 __fsetlocking (fp, FSETLOCKING_BYCALLER);
656 while (!feof_unlocked (fp))
658 char *rp, *endp, *word;
659 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
661 if (n < 0)
662 /* An error occurred. */
663 break;
665 rp = line;
666 /* Terminate the line (excluding comments or newline) with a NUL
667 byte to simplify the following code. */
668 endp = strchr (rp, '#');
669 if (endp != NULL)
670 *endp = '\0';
671 else
672 if (rp[n - 1] == '\n')
673 rp[n - 1] = '\0';
675 while (isspace (*rp))
676 ++rp;
678 /* If this is an empty line go on with the next one. */
679 if (rp == endp)
680 continue;
682 word = rp;
683 while (*rp != '\0' && !isspace (*rp))
684 ++rp;
686 if (rp - word == sizeof ("alias") - 1
687 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
688 add_alias (rp);
689 else if (rp - word == sizeof ("module") - 1
690 && memcmp (word, "module", sizeof ("module") - 1) == 0)
691 add_module (rp, dir);
692 /* else */
693 /* Otherwise ignore the line. */
696 free (line);
698 fclose (fp);
700 return 0;
704 static void
705 append_module (const void *nodep, VISIT value, int level)
707 struct module *mo;
709 if (value != leaf && value != postorder)
710 return;
712 mo = *(struct module **) nodep;
714 if (nmodule_list > 0
715 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
717 /* Same name. */
718 mo->next = module_list[nmodule_list - 1];
719 module_list[nmodule_list - 1] = mo;
721 return;
724 if (nmodule_list_max == nmodule_list)
726 nmodule_list_max += 50;
727 module_list = (struct module **) xrealloc (module_list,
728 (nmodule_list_max
729 * sizeof (struct module *)));
732 module_list[nmodule_list++] = mo;
736 static void
737 get_modules (void)
739 twalk (modules, append_module);
743 static void
744 add_builtins (void)
746 size_t cnt;
748 /* Add all aliases. */
749 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
750 new_alias (builtin_alias[cnt].from,
751 strlen (builtin_alias[cnt].from) + 1,
752 builtin_alias[cnt].to,
753 strlen (builtin_alias[cnt].to) + 1);
755 /* add the builtin transformations. */
756 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
757 new_module (builtin_trans[cnt].from,
758 strlen (builtin_trans[cnt].from) + 1,
759 builtin_trans[cnt].to,
760 strlen (builtin_trans[cnt].to) + 1,
761 "", builtin_trans[cnt].module,
762 strlen (builtin_trans[cnt].module) + 1,
763 builtin_trans[cnt].cost, 0);
767 static int
768 name_compare (const void *p1, const void *p2)
770 const struct name *n1 = (const struct name *) p1;
771 const struct name *n2 = (const struct name *) p2;
773 return strcmp (n1->name, n2->name);
777 static struct name *
778 new_name (const char *str, struct Strent *strent)
780 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
782 newp->name = str;
783 newp->strent = strent;
784 newp->module_idx = -1;
785 newp->hashval = hash_string (str);
787 ++nnames;
789 return newp;
793 static void
794 generate_name_list (void)
796 size_t i;
798 /* A name we always need. */
799 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
800 sizeof ("INTERNAL"))),
801 &names, name_compare);
803 for (i = 0; i < nmodule_list; ++i)
805 struct module *runp;
807 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
808 tsearch (new_name (module_list[i]->fromname,
809 module_list[i]->fromname_strent),
810 &names, name_compare);
812 for (runp = module_list[i]; runp != NULL; runp = runp->next)
813 if (strcmp (runp->toname, "INTERNAL") != 0)
814 tsearch (new_name (runp->toname, runp->toname_strent),
815 &names, name_compare);
820 static int
821 name_to_module_idx (const char *name, int add)
823 struct name **res;
824 struct name fake_name = { .name = name };
825 int idx;
827 res = (struct name **) tfind (&fake_name, &names, name_compare);
828 if (res == NULL)
829 abort ();
831 idx = (*res)->module_idx;
832 if (idx == -1 && add)
833 /* No module index assigned yet. */
834 idx = (*res)->module_idx = nname_info++;
836 return idx;
840 static void
841 generate_name_info (void)
843 size_t i;
844 int idx;
846 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
847 sizeof (struct name_info));
849 /* First add a special entry for the INTERNAL name. This must have
850 index zero. */
851 idx = name_to_module_idx ("INTERNAL", 1);
852 name_info[0].canonical_name = "INTERNAL";
853 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
854 sizeof ("INTERNAL"));
855 assert (nname_info == 1);
857 for (i = 0; i < nmodule_list; ++i)
859 struct module *runp;
861 for (runp = module_list[i]; runp != NULL; runp = runp->next)
862 if (strcmp (runp->fromname, "INTERNAL") == 0)
864 idx = name_to_module_idx (runp->toname, 1);
865 name_info[idx].from_internal = runp;
866 assert (name_info[idx].canonical_name == NULL
867 || strcmp (name_info[idx].canonical_name,
868 runp->toname) == 0);
869 name_info[idx].canonical_name = runp->toname;
870 name_info[idx].canonical_strent = runp->toname_strent;
872 else if (strcmp (runp->toname, "INTERNAL") == 0)
874 idx = name_to_module_idx (runp->fromname, 1);
875 name_info[idx].to_internal = runp;
876 assert (name_info[idx].canonical_name == NULL
877 || strcmp (name_info[idx].canonical_name,
878 runp->fromname) == 0);
879 name_info[idx].canonical_name = runp->fromname;
880 name_info[idx].canonical_strent = runp->fromname_strent;
882 else
884 /* This is a transformation not to or from the INTERNAL
885 encoding. */
886 int from_idx = name_to_module_idx (runp->fromname, 1);
887 int to_idx = name_to_module_idx (runp->toname, 1);
888 struct other_conv_list *newp;
890 newp = (struct other_conv_list *)
891 xmalloc (sizeof (struct other_conv_list));
892 newp->other_conv.module_idx = to_idx;
893 newp->other_conv.module = runp;
894 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
895 newp->dest_idx = to_idx;
896 newp->next = name_info[from_idx].other_conv_list;
897 name_info[from_idx].other_conv_list = newp;
898 assert (name_info[from_idx].canonical_name == NULL
899 || strcmp (name_info[from_idx].canonical_name,
900 runp->fromname) == 0);
901 name_info[from_idx].canonical_name = runp->fromname;
902 name_info[from_idx].canonical_strent = runp->fromname_strent;
904 ++nextra_modules;
908 /* Now add the module index information for all the aliases. */
909 for (i = 0; i < nalias_list; ++i)
911 struct name fake_name = { .name = alias_list[i]->toname };
912 struct name **tonamep;
914 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
915 if (tonamep != NULL)
917 struct name *newp = new_name (alias_list[i]->fromname,
918 alias_list[i]->froment);
919 newp->module_idx = (*tonamep)->module_idx;
920 tsearch (newp, &names, name_compare);
926 static int
927 is_prime (unsigned long int candidate)
929 /* No even number and none less than 10 will be passed here. */
930 unsigned long int divn = 3;
931 unsigned long int sq = divn * divn;
933 while (sq < candidate && candidate % divn != 0)
935 ++divn;
936 sq += 4 * divn;
937 ++divn;
940 return candidate % divn != 0;
944 static uint32_t
945 next_prime (uint32_t seed)
947 /* Make it definitely odd. */
948 seed |= 1;
950 while (!is_prime (seed))
951 seed += 2;
953 return seed;
957 /* Format of the output file.
959 Offset Length Description
960 0000 4 Magic header bytes
961 0004 4 Offset of string table (stoff)
962 0008 4 Offset of name hashing table (hoff)
963 000C 4 Hashing table size (hsize)
964 0010 4 Offset of module table (moff)
965 0014 4 Offset of other conversion module table (ooff)
967 stoff ??? String table
969 hoff 8*hsize Array of tuples
970 string table offset
971 module index
973 moff ??? Array of tuples
974 canonical name offset
975 from-internal module dir name offset
976 from-internal module name off
977 to-internal module dir name offset
978 to-internal module name offset
979 offset into other conversion table
981 ooff ??? One or more of
982 number of steps/modules
983 one or more of tuple
984 canonical name offset for output
985 module dir name offset
986 module name offset
987 (following last entry with step count 0)
989 static int
990 write_output (void)
992 int fd;
993 char *string_table;
994 size_t string_table_size;
995 struct gconvcache_header header;
996 struct hash_entry *hash_table;
997 size_t hash_size;
998 struct module_entry *module_table;
999 char *extra_table;
1000 char *cur_extra_table;
1001 size_t n;
1002 int idx;
1003 struct iovec iov[6];
1004 static const gidx_t null_word;
1005 size_t total;
1006 char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1007 + strlen (".XXXXXX")];
1008 char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1010 /* Function to insert the names. */
1011 static void name_insert (const void *nodep, VISIT value, int level)
1013 struct name *name;
1014 unsigned int idx;
1015 unsigned int hval2;
1017 if (value != leaf && value != postorder)
1018 return;
1020 name = *(struct name **) nodep;
1021 idx = name->hashval % hash_size;
1022 hval2 = 1 + name->hashval % (hash_size - 2);
1024 while (hash_table[idx].string_offset != 0)
1025 if ((idx += hval2) >= hash_size)
1026 idx -= hash_size;
1028 hash_table[idx].string_offset = strtaboffset (name->strent);
1030 assert (name->module_idx != -1);
1031 hash_table[idx].module_idx = name->module_idx;
1034 /* Open the output file. */
1035 assert (GCONV_MODULES_CACHE[0] == '/');
1036 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1037 ".XXXXXX");
1038 fd = mkstemp (tmpfname);
1039 if (fd == -1)
1040 return 1;
1042 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1044 /* Create the string table. */
1045 string_table = strtabfinalize (strtab, &string_table_size);
1047 /* Create the hashing table. We know how many strings we have.
1048 Creating a perfect hash table is not reasonable here. Therefore
1049 we use open hashing and a table size which is the next prime 40%
1050 larger than the number of strings. */
1051 hash_size = next_prime (nnames * 1.4);
1052 hash_table = (struct hash_entry *) xcalloc (hash_size,
1053 sizeof (struct hash_entry));
1054 /* Fill the hash table. */
1055 twalk (names, name_insert);
1057 /* Create the section for the module list. */
1058 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1059 nname_info);
1061 /* Allocate memory for the non-INTERNAL conversions. The allocated
1062 memory can be more than is actually needed. */
1063 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1064 + sizeof (gidx_t)
1065 + sizeof (struct extra_entry_module),
1066 nextra_modules);
1067 cur_extra_table = extra_table;
1069 /* Fill in the module information. */
1070 for (n = 0; n < nname_info; ++n)
1072 module_table[n].canonname_offset =
1073 strtaboffset (name_info[n].canonical_strent);
1075 if (name_info[n].from_internal == NULL)
1077 module_table[n].fromdir_offset = 0;
1078 module_table[n].fromname_offset = 0;
1080 else
1082 module_table[n].fromdir_offset =
1083 strtaboffset (name_info[n].from_internal->directory_strent);
1084 module_table[n].fromname_offset =
1085 strtaboffset (name_info[n].from_internal->filename_strent);
1088 if (name_info[n].to_internal == NULL)
1090 module_table[n].todir_offset = 0;
1091 module_table[n].toname_offset = 0;
1093 else
1095 module_table[n].todir_offset =
1096 strtaboffset (name_info[n].to_internal->directory_strent);
1097 module_table[n].toname_offset =
1098 strtaboffset (name_info[n].to_internal->filename_strent);
1101 if (name_info[n].other_conv_list != NULL)
1103 struct other_conv_list *other = name_info[n].other_conv_list;
1105 /* Store the reference. We add 1 to distinguish the entry
1106 at offset zero from the case where no extra modules are
1107 available. The file reader has to account for the
1108 offset. */
1109 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1113 struct other_conv *runp;
1114 struct extra_entry *extra;
1116 /* Allocate new entry. */
1117 extra = (struct extra_entry *) cur_extra_table;
1118 cur_extra_table += sizeof (struct extra_entry);
1119 extra->module_cnt = 0;
1121 runp = &other->other_conv;
1124 cur_extra_table += sizeof (struct extra_entry_module);
1125 extra->module[extra->module_cnt].outname_offset =
1126 runp->next == NULL
1127 ? other->dest_idx : runp->next->module_idx;
1128 extra->module[extra->module_cnt].dir_offset =
1129 strtaboffset (runp->module->directory_strent);
1130 extra->module[extra->module_cnt].name_offset =
1131 strtaboffset (runp->module->filename_strent);
1132 ++extra->module_cnt;
1134 runp = runp->next;
1136 while (runp != NULL);
1138 other = other->next;
1140 while (other != NULL);
1142 /* Final module_cnt is zero. */
1143 *((gidx_t *) cur_extra_table) = 0;
1144 cur_extra_table += sizeof (gidx_t);
1148 header.magic = GCONVCACHE_MAGIC;
1150 iov[0].iov_base = &header;
1151 iov[0].iov_len = sizeof (struct gconvcache_header);
1152 total = iov[0].iov_len;
1154 header.string_offset = total;
1155 iov[1].iov_base = string_table;
1156 iov[1].iov_len = string_table_size;
1157 total += iov[1].iov_len;
1159 idx = 2;
1160 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1162 iov[2].iov_base = (void *) &null_word;
1163 iov[2].iov_len = (sizeof (gidx_t)
1164 - (string_table_size & (sizeof (gidx_t) - 1)));
1165 total += iov[2].iov_len;
1166 ++idx;
1169 header.hash_offset = total;
1170 header.hash_size = hash_size;
1171 iov[idx].iov_base = hash_table;
1172 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1173 total += iov[idx].iov_len;
1174 ++idx;
1176 header.module_offset = total;
1177 iov[idx].iov_base = module_table;
1178 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1179 total += iov[idx].iov_len;
1180 ++idx;
1182 assert (cur_extra_table - extra_table
1183 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1184 + sizeof (struct extra_entry_module))
1185 * nextra_modules));
1186 header.otherconv_offset = total;
1187 iov[idx].iov_base = extra_table;
1188 iov[idx].iov_len = cur_extra_table - extra_table;
1189 total += iov[idx].iov_len;
1190 ++idx;
1192 if (TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1193 /* The file was created with mode 0600. Make it world-readable. */
1194 || fchmod (fd, 0644) != 0
1195 /* Rename the file, possibly replacing an old one. */
1196 || rename (tmpfname, finalname) != 0)
1198 int save_errno = errno;
1199 close (fd);
1200 unlink (tmpfname);
1201 error (EXIT_FAILURE, save_errno,
1202 gettext ("cannot generate output file"));
1205 close (fd);
1207 return 0;