Update.
[glibc.git] / iconv / iconvconfig.c
blob6959d947f8bb0cfa7f122731097dd3cace0c291c
1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001 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 /* Function to print some extra text in the help message. */
117 static char *more_help (int key, const char *text, void *input);
119 /* Data structure to communicate with argp functions. */
120 static struct argp argp =
122 NULL, NULL, args_doc, doc, NULL, more_help
126 /* The function doing the actual work. */
127 static int handle_dir (const char *dir);
129 /* Add all known builtin conversions and aliases. */
130 static void add_builtins (void);
132 /* Create list of all aliases without circular aliases. */
133 static void get_aliases (void);
135 /* Create list of all modules. */
136 static void get_modules (void);
138 /* Get list of all the names and thereby indexing them. */
139 static void generate_name_list (void);
141 /* Collect information about all the names. */
142 static void generate_name_info (void);
144 /* Write the output file. */
145 static int write_output (void);
148 /* Search tree of the modules we know. */
149 static void *modules;
151 /* Search tree of the aliases we know. */
152 static void *aliases;
154 /* Search tree for name to index mapping. */
155 static void *names;
157 /* Number of names we know about. */
158 static int nnames;
160 /* List of all aliases. */
161 static struct alias **alias_list;
162 static size_t nalias_list;
163 static size_t nalias_list_max;
165 /* List of all modules. */
166 static struct module **module_list;
167 static size_t nmodule_list;
168 static size_t nmodule_list_max;
170 /* Names and information about them. */
171 static struct name_info *name_info;
172 static size_t nname_info;
174 /* Number of translations not from or to INTERNAL. */
175 static size_t nextra_modules;
178 /* Names and aliases for the builtin transformations. */
179 static struct
181 const char *from;
182 const char *to;
183 } builtin_alias[] =
185 #define BUILTIN_ALIAS(alias, real) \
186 { .from = alias, .to = real },
187 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
188 MinT, MaxT)
189 #include <gconv_builtin.h>
191 #undef BUILTIN_ALIAS
192 #undef BUILTIN_TRANSFORMATION
193 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
195 static struct
197 const char *from;
198 const char *to;
199 const char *module;
200 int cost;
201 } builtin_trans[] =
203 #define BUILTIN_ALIAS(alias, real)
204 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
205 MinT, MaxT) \
206 { .from = From, .to = To, .module = Name, .cost = Cost },
207 #include <gconv_builtin.h>
209 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
212 /* Filename extension for the modules. */
213 #ifndef MODULE_EXT
214 # define MODULE_EXT ".so"
215 #endif
216 static const char gconv_module_ext[] = MODULE_EXT;
219 extern void *xmalloc (size_t n) __attribute_malloc__;
220 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
221 extern void *xrealloc (void *p, size_t n);
224 /* C string table handling. */
225 struct Strtab;
226 struct Strent;
228 /* Create new C string table object in memory. */
229 extern struct Strtab *strtabinit (void);
231 /* Free resources allocated for C string table ST. */
232 extern void strtabfree (struct Strtab *st);
234 /* Add string STR (length LEN is != 0) to C string table ST. */
235 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
236 size_t len);
238 /* Finalize string table ST and store size in *SIZE and return a pointer. */
239 extern void *strtabfinalize (struct Strtab *st, size_t *size);
241 /* Get offset in string table for string associated with SE. */
242 extern size_t strtaboffset (struct Strent *se);
244 /* String table we construct. */
245 static struct Strtab *strtab;
250 main (int argc, char *argv[])
252 int remaining;
253 int status = 0;
254 char *path;
255 char *tp;
257 /* Enable memory use testing. */
258 /* mcheck_pedantic (NULL); */
259 mtrace ();
261 /* Set locale via LC_ALL. */
262 setlocale (LC_ALL, "");
264 /* Set the text message domain. */
265 textdomain (_libc_intl_domainname);
267 /* Parse and process arguments. */
268 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
270 /* Initialize the string table. */
271 strtab = strtabinit ();
273 /* Handle all directories mentioned. */
274 while (remaining < argc)
275 status |= handle_dir (argv[remaining++]);
277 /* In any case also handle the standard directory. */
278 path = strdupa (GCONV_PATH);
279 tp = strtok (path, ":");
280 while (tp != NULL)
282 status |= handle_dir (tp);
284 tp = strtok (NULL, ":");
287 /* Add the builtin transformations and aliases without overwriting
288 anything. */
289 add_builtins ();
291 /* Store aliases in an array. */
292 get_aliases ();
294 /* Get list of all modules. */
295 get_modules ();
297 /* Generate list of all the names we know to handle in some way. */
298 generate_name_list ();
300 /* Now we know all the names we will handle, collect information
301 about them. */
302 generate_name_info ();
304 /* Write the output file, but only if we haven't seen any error. */
305 if (status == 0)
306 status = write_output ();
307 else
308 error (1, 0, _("no output file produced because warning were issued"));
310 return status;
314 static char *
315 more_help (int key, const char *text, void *input)
317 switch (key)
319 case ARGP_KEY_HELP_EXTRA:
320 /* We print some extra information. */
321 return strdup (gettext ("\
322 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
323 default:
324 break;
326 return (char *) text;
330 /* Print the version information. */
331 static void
332 print_version (FILE *stream, struct argp_state *state)
334 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
335 fprintf (stream, gettext ("\
336 Copyright (C) %s Free Software Foundation, Inc.\n\
337 This is free software; see the source for copying conditions. There is NO\n\
338 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
339 "), "2001");
340 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
344 static int
345 alias_compare (const void *p1, const void *p2)
347 const struct alias *a1 = (const struct alias *) p1;
348 const struct alias *a2 = (const struct alias *) p2;
350 return strcmp (a1->fromname, a2->fromname);
354 static void
355 new_alias (const char *fromname, size_t fromlen, const char *toname,
356 size_t tolen)
358 struct alias *newp;
359 void **inserted;
361 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
363 newp->fromname = mempcpy (newp->toname, toname, tolen);
364 memcpy (newp->fromname, fromname, fromlen);
365 newp->module = NULL;
367 inserted = (void **) tsearch (newp, &aliases, alias_compare);
368 if (inserted == NULL)
369 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
370 if (*inserted != newp)
371 /* Something went wrong, free this entry. */
372 free (newp);
373 else
375 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
376 newp->toent = strtabadd (strtab, newp->toname, tolen);
381 /* Add new alias. */
382 static void
383 add_alias (char *rp)
385 /* We now expect two more string. The strings are normalized
386 (converted to UPPER case) and strored in the alias database. */
387 char *from;
388 char *to;
389 char *wp;
391 while (isspace (*rp))
392 ++rp;
393 from = wp = rp;
394 while (*rp != '\0' && !isspace (*rp))
395 *wp++ = toupper (*rp++);
396 if (*rp == '\0')
397 /* There is no `to' string on the line. Ignore it. */
398 return;
399 *wp++ = '\0';
400 to = ++rp;
401 while (isspace (*rp))
402 ++rp;
403 while (*rp != '\0' && !isspace (*rp))
404 *wp++ = toupper (*rp++);
405 if (to == wp)
406 /* No `to' string, ignore the line. */
407 return;
408 *wp++ = '\0';
410 assert (strlen (from) + 1 == to - from);
411 assert (strlen (to) + 1 == wp - to);
413 new_alias (from, to - from, to, wp - to);
417 static void
418 append_alias (const void *nodep, VISIT value, int level)
420 if (value != leaf && value != postorder)
421 return;
423 if (nalias_list_max == nalias_list)
425 nalias_list_max += 50;
426 alias_list = (struct alias **) xrealloc (alias_list,
427 (nalias_list_max
428 * sizeof (struct alias *)));
431 alias_list[nalias_list++] = *(struct alias **) nodep;
435 static void
436 get_aliases (void)
438 twalk (aliases, append_alias);
442 static int
443 module_compare (const void *p1, const void *p2)
445 const struct module *m1 = (const struct module *) p1;
446 const struct module *m2 = (const struct module *) p2;
447 int result;
449 result = strcmp (m1->fromname, m2->fromname);
450 if (result == 0)
451 result = strcmp (m1->toname, m2->toname);
453 return result;
457 /* Create new module record. */
458 static void
459 new_module (const char *fromname, size_t fromlen, const char *toname,
460 size_t tolen, const char *directory,
461 const char *filename, size_t filelen, int cost, size_t need_ext)
463 struct module *new_module;
464 size_t dirlen = strlen (directory) + 1;
465 char *tmp;
466 void **inserted;
468 new_module = (struct module *) xmalloc (sizeof (struct module)
469 + fromlen + tolen + filelen
470 + need_ext);
472 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
474 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
476 new_module->cost = cost;
477 new_module->next = NULL;
479 tmp = mempcpy (new_module->filename, filename, filelen);
480 if (need_ext)
482 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
483 filelen += need_ext;
485 new_module->directory = directory;
487 /* Now insert the new module data structure in our search tree. */
488 inserted = (void **) tsearch (new_module, &modules, module_compare);
489 if (inserted == NULL)
490 error (EXIT_FAILURE, errno, "while inserting in search tree");
491 if (*inserted != new_module)
492 free (new_module);
493 else
495 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
496 fromlen);
497 new_module->toname_strent = strtabadd (strtab, new_module->toname,
498 tolen);
499 new_module->filename_strent = strtabadd (strtab, new_module->filename,
500 filelen);
501 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
506 /* Add new module. */
507 static void
508 internal_function
509 add_module (char *rp, const char *directory)
511 /* We expect now
512 1. `from' name
513 2. `to' name
514 3. filename of the module
515 4. an optional cost value
517 char *from;
518 char *to;
519 char *module;
520 char *wp;
521 int need_ext;
522 int cost;
524 while (isspace (*rp))
525 ++rp;
526 from = rp;
527 while (*rp != '\0' && !isspace (*rp))
529 *rp = toupper (*rp);
530 ++rp;
532 if (*rp == '\0')
533 return;
534 *rp++ = '\0';
535 to = wp = rp;
536 while (isspace (*rp))
537 ++rp;
538 while (*rp != '\0' && !isspace (*rp))
539 *wp++ = toupper (*rp++);
540 if (*rp == '\0')
541 return;
542 *wp++ = '\0';
544 ++rp;
545 while (isspace (*rp));
546 module = wp;
547 while (*rp != '\0' && !isspace (*rp))
548 *wp++ = *rp++;
549 if (*rp == '\0')
551 /* There is no cost, use one by default. */
552 *wp++ = '\0';
553 cost = 1;
555 else
557 /* There might be a cost value. */
558 char *endp;
560 *wp++ = '\0';
561 cost = strtol (rp, &endp, 10);
562 if (rp == endp || cost < 1)
563 /* No useful information. */
564 cost = 1;
567 if (module[0] == '\0')
568 /* No module name given. */
569 return;
571 /* See whether we must add the ending. */
572 need_ext = 0;
573 if (wp - module < sizeof (gconv_module_ext)
574 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
575 sizeof (gconv_module_ext)) != 0)
576 /* We must add the module extension. */
577 need_ext = sizeof (gconv_module_ext) - 1;
579 assert (strlen (from) + 1 == to - from);
580 assert (strlen (to) + 1 == module - to);
581 assert (strlen (module) + 1 == wp - module);
583 new_module (from, to - from, to, module - to, directory, module, wp - module,
584 cost, need_ext);
588 /* Read the config file and add the data for this directory to that. */
589 static int
590 handle_dir (const char *dir)
592 char *infile;
593 FILE *fp;
594 char *line = NULL;
595 size_t linelen = 0;
596 size_t dirlen = strlen (dir);
598 if (dir[dirlen - 1] != '/')
600 char *newp = (char *) xmalloc (dirlen + 2);
601 dir = memcpy (newp, dir, dirlen);
602 newp[dirlen++] = '/';
603 newp[dirlen] = '\0';
606 infile = (char *) alloca (dirlen + sizeof "gconv-modules");
607 strcpy (mempcpy (infile, dir, dirlen), "gconv-modules");
609 fp = fopen (infile, "r");
610 if (fp == NULL)
612 error (0, errno, "cannot open `%s'", infile);
613 return 1;
616 /* No threads present. */
617 __fsetlocking (fp, FSETLOCKING_BYCALLER);
619 while (!feof_unlocked (fp))
621 char *rp, *endp, *word;
622 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
624 if (n < 0)
625 /* An error occurred. */
626 break;
628 rp = line;
629 /* Terminate the line (excluding comments or newline) with a NUL
630 byte to simplify the following code. */
631 endp = strchr (rp, '#');
632 if (endp != NULL)
633 *endp = '\0';
634 else
635 if (rp[n - 1] == '\n')
636 rp[n - 1] = '\0';
638 while (isspace (*rp))
639 ++rp;
641 /* If this is an empty line go on with the next one. */
642 if (rp == endp)
643 continue;
645 word = rp;
646 while (*rp != '\0' && !isspace (*rp))
647 ++rp;
649 if (rp - word == sizeof ("alias") - 1
650 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
651 add_alias (rp);
652 else if (rp - word == sizeof ("module") - 1
653 && memcmp (word, "module", sizeof ("module") - 1) == 0)
654 add_module (rp, dir);
655 /* else */
656 /* Otherwise ignore the line. */
659 free (line);
661 fclose (fp);
663 return 0;
667 static void
668 append_module (const void *nodep, VISIT value, int level)
670 struct module *mo;
672 if (value != leaf && value != postorder)
673 return;
675 mo = *(struct module **) nodep;
677 if (nmodule_list > 0
678 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
680 /* Same name. */
681 mo->next = module_list[nmodule_list - 1];
682 module_list[nmodule_list - 1] = mo;
684 return;
687 if (nmodule_list_max == nmodule_list)
689 nmodule_list_max += 50;
690 module_list = (struct module **) xrealloc (module_list,
691 (nmodule_list_max
692 * sizeof (struct module *)));
695 module_list[nmodule_list++] = mo;
699 static void
700 get_modules (void)
702 twalk (modules, append_module);
706 static void
707 add_builtins (void)
709 size_t cnt;
711 /* Add all aliases. */
712 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
713 new_alias (builtin_alias[cnt].from,
714 strlen (builtin_alias[cnt].from) + 1,
715 builtin_alias[cnt].to,
716 strlen (builtin_alias[cnt].to) + 1);
718 /* add the builtin transformations. */
719 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
720 new_module (builtin_trans[cnt].from,
721 strlen (builtin_trans[cnt].from) + 1,
722 builtin_trans[cnt].to,
723 strlen (builtin_trans[cnt].to) + 1,
724 "", builtin_trans[cnt].module,
725 strlen (builtin_trans[cnt].module) + 1,
726 builtin_trans[cnt].cost, 0);
730 static int
731 name_compare (const void *p1, const void *p2)
733 const struct name *n1 = (const struct name *) p1;
734 const struct name *n2 = (const struct name *) p2;
736 return strcmp (n1->name, n2->name);
740 static struct name *
741 new_name (const char *str, struct Strent *strent)
743 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
745 newp->name = str;
746 newp->strent = strent;
747 newp->module_idx = -1;
748 newp->hashval = hash_string (str);
750 ++nnames;
752 return newp;
756 static void
757 generate_name_list (void)
759 size_t i;
761 /* A name we always need. */
762 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
763 sizeof ("INTERNAL"))),
764 &names, name_compare);
766 for (i = 0; i < nmodule_list; ++i)
768 struct module *runp;
770 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
771 tsearch (new_name (module_list[i]->fromname,
772 module_list[i]->fromname_strent),
773 &names, name_compare);
775 for (runp = module_list[i]; runp != NULL; runp = runp->next)
776 if (strcmp (runp->toname, "INTERNAL") != 0)
777 tsearch (new_name (runp->toname, runp->toname_strent),
778 &names, name_compare);
783 static int
784 name_to_module_idx (const char *name, int add)
786 struct name **res;
787 struct name fake_name = { .name = name };
788 int idx;
790 res = (struct name **) tfind (&fake_name, &names, name_compare);
791 if (res == NULL)
792 abort ();
794 idx = (*res)->module_idx;
795 if (idx == -1 && add)
796 /* No module index assigned yet. */
797 idx = (*res)->module_idx = nname_info++;
799 return idx;
803 static void
804 generate_name_info (void)
806 size_t i;
807 int idx;
809 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
810 sizeof (struct name_info));
812 /* First add a special entry for the INTERNAL name. This must have
813 index zero. */
814 idx = name_to_module_idx ("INTERNAL", 1);
815 name_info[0].canonical_name = "INTERNAL";
816 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
817 sizeof ("INTERNAL"));
818 assert (nname_info == 1);
820 for (i = 0; i < nmodule_list; ++i)
822 struct module *runp;
824 for (runp = module_list[i]; runp != NULL; runp = runp->next)
825 if (strcmp (runp->fromname, "INTERNAL") == 0)
827 idx = name_to_module_idx (runp->toname, 1);
828 name_info[idx].from_internal = runp;
829 assert (name_info[idx].canonical_name == NULL
830 || strcmp (name_info[idx].canonical_name,
831 runp->toname) == 0);
832 name_info[idx].canonical_name = runp->toname;
833 name_info[idx].canonical_strent = runp->toname_strent;
835 else if (strcmp (runp->toname, "INTERNAL") == 0)
837 idx = name_to_module_idx (runp->fromname, 1);
838 name_info[idx].to_internal = runp;
839 assert (name_info[idx].canonical_name == NULL
840 || strcmp (name_info[idx].canonical_name,
841 runp->fromname) == 0);
842 name_info[idx].canonical_name = runp->fromname;
843 name_info[idx].canonical_strent = runp->fromname_strent;
845 else
847 /* This is a transformation not to or from the INTERNAL
848 encoding. */
849 int from_idx = name_to_module_idx (runp->fromname, 1);
850 int to_idx = name_to_module_idx (runp->toname, 1);
851 struct other_conv_list *newp;
853 newp = (struct other_conv_list *)
854 xmalloc (sizeof (struct other_conv_list));
855 newp->other_conv.module_idx = to_idx;
856 newp->other_conv.module = runp;
857 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
858 newp->dest_idx = to_idx;
859 newp->next = name_info[from_idx].other_conv_list;
860 name_info[from_idx].other_conv_list = newp;
861 assert (name_info[from_idx].canonical_name == NULL
862 || strcmp (name_info[from_idx].canonical_name,
863 runp->fromname) == 0);
864 name_info[from_idx].canonical_name = runp->fromname;
865 name_info[from_idx].canonical_strent = runp->fromname_strent;
867 ++nextra_modules;
871 /* Now add the module index information for all the aliases. */
872 for (i = 0; i < nalias_list; ++i)
874 struct name fake_name = { .name = alias_list[i]->toname };
875 struct name **tonamep;
877 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
878 if (tonamep != NULL)
880 struct name *newp = new_name (alias_list[i]->fromname,
881 alias_list[i]->froment);
882 newp->module_idx = (*tonamep)->module_idx;
883 tsearch (newp, &names, name_compare);
889 static int
890 is_prime (unsigned long int candidate)
892 /* No even number and none less than 10 will be passed here. */
893 unsigned long int divn = 3;
894 unsigned long int sq = divn * divn;
896 while (sq < candidate && candidate % divn != 0)
898 ++divn;
899 sq += 4 * divn;
900 ++divn;
903 return candidate % divn != 0;
907 static uint32_t
908 next_prime (uint32_t seed)
910 /* Make it definitely odd. */
911 seed |= 1;
913 while (!is_prime (seed))
914 seed += 2;
916 return seed;
920 /* Format of the output file.
922 Offset Length Description
923 0000 4 Magic header bytes
924 0004 4 Offset of string table (stoff)
925 0008 4 Offset of name hashing table (hoff)
926 000C 4 Hashing table size (hsize)
927 0010 4 Offset of module table (moff)
928 0014 4 Offset of other conversion module table (ooff)
930 stoff ??? String table
932 hoff 8*hsize Array of tuples
933 string table offset
934 module index
936 moff ??? Array of tuples
937 canonical name offset
938 from-internal module dir name offset
939 from-internal module name off
940 to-internal module dir name offset
941 to-internal module name offset
942 offset into other conversion table
944 ooff ??? One or more of
945 number of steps/modules
946 one or more of tuple
947 canonical name offset for output
948 module dir name offset
949 module name offset
950 (following last entry with step count 0)
952 static int
953 write_output (void)
955 int fd;
956 char *string_table;
957 size_t string_table_size;
958 struct gconvcache_header header;
959 struct hash_entry *hash_table;
960 size_t hash_size;
961 struct module_entry *module_table;
962 char *extra_table;
963 char *cur_extra_table;
964 size_t n;
965 int idx;
966 struct iovec iov[6];
967 static const gidx_t null_word;
968 size_t total;
969 char tmpfname[sizeof (GCONV_MODULES_CACHE) + strlen (".XXXXXX")];
971 /* Function to insert the names. */
972 static void name_insert (const void *nodep, VISIT value, int level)
974 struct name *name;
975 unsigned int idx;
976 unsigned int hval2;
978 if (value != leaf && value != postorder)
979 return;
981 name = *(struct name **) nodep;
982 idx = name->hashval % hash_size;
983 hval2 = 1 + name->hashval % (hash_size - 2);
985 while (hash_table[idx].string_offset != 0)
986 if ((idx += hval2) >= hash_size)
987 idx -= hash_size;
989 hash_table[idx].string_offset = strtaboffset (name->strent);
991 assert (name->module_idx != -1);
992 hash_table[idx].module_idx = name->module_idx;
995 /* Open the output file. */
996 strcpy (stpcpy (tmpfname, GCONV_MODULES_CACHE), ".XXXXXX");
997 fd = mkstemp (tmpfname);
998 if (fd == -1)
999 return 1;
1001 /* Create the string table. */
1002 string_table = strtabfinalize (strtab, &string_table_size);
1004 /* Create the hashing table. We know how many strings we have.
1005 Creating a perfect hash table is not reasonable here. Therefore
1006 we use open hashing and a table size which is the next prime 40%
1007 larger than the number of strings. */
1008 hash_size = next_prime (nnames * 1.4);
1009 hash_table = (struct hash_entry *) xcalloc (hash_size,
1010 sizeof (struct hash_entry));
1011 /* Fill the hash table. */
1012 twalk (names, name_insert);
1014 /* Create the section for the module list. */
1015 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1016 nname_info);
1018 /* Allocate memory for the non-INTERNAL conversions. The allocated
1019 memory can be more than is actually needed. */
1020 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1021 + sizeof (gidx_t)
1022 + sizeof (struct extra_entry_module),
1023 nextra_modules);
1024 cur_extra_table = extra_table;
1026 /* Fill in the module information. */
1027 for (n = 0; n < nname_info; ++n)
1029 module_table[n].canonname_offset =
1030 strtaboffset (name_info[n].canonical_strent);
1032 if (name_info[n].from_internal == NULL)
1034 module_table[n].fromdir_offset = 0;
1035 module_table[n].fromname_offset = 0;
1037 else
1039 module_table[n].fromdir_offset =
1040 strtaboffset (name_info[n].from_internal->directory_strent);
1041 module_table[n].fromname_offset =
1042 strtaboffset (name_info[n].from_internal->filename_strent);
1045 if (name_info[n].to_internal == NULL)
1047 module_table[n].todir_offset = 0;
1048 module_table[n].toname_offset = 0;
1050 else
1052 module_table[n].todir_offset =
1053 strtaboffset (name_info[n].to_internal->directory_strent);
1054 module_table[n].toname_offset =
1055 strtaboffset (name_info[n].to_internal->filename_strent);
1058 if (name_info[n].other_conv_list != NULL)
1060 struct other_conv_list *other = name_info[n].other_conv_list;
1062 /* Store the reference. We add 1 to distinguish the entry
1063 at offset zero from the case where no extra modules are
1064 available. The file reader has to account for the
1065 offset. */
1066 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1070 struct other_conv *runp;
1071 struct extra_entry *extra;
1073 /* Allocate new entry. */
1074 extra = (struct extra_entry *) cur_extra_table;
1075 cur_extra_table += sizeof (struct extra_entry);
1076 extra->module_cnt = 0;
1078 runp = &other->other_conv;
1081 cur_extra_table += sizeof (struct extra_entry_module);
1082 extra->module[extra->module_cnt].outname_offset =
1083 runp->next == NULL
1084 ? other->dest_idx : runp->next->module_idx;
1085 extra->module[extra->module_cnt].dir_offset =
1086 strtaboffset (runp->module->directory_strent);
1087 extra->module[extra->module_cnt].name_offset =
1088 strtaboffset (runp->module->filename_strent);
1089 ++extra->module_cnt;
1091 runp = runp->next;
1093 while (runp != NULL);
1095 other = other->next;
1097 while (other != NULL);
1099 /* Final module_cnt is zero. */
1100 *((gidx_t *) cur_extra_table) = 0;
1101 cur_extra_table += sizeof (gidx_t);
1105 header.magic = GCONVCACHE_MAGIC;
1107 iov[0].iov_base = &header;
1108 iov[0].iov_len = sizeof (struct gconvcache_header);
1109 total = iov[0].iov_len;
1111 header.string_offset = total;
1112 iov[1].iov_base = string_table;
1113 iov[1].iov_len = string_table_size;
1114 total += iov[1].iov_len;
1116 idx = 2;
1117 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1119 iov[2].iov_base = (void *) &null_word;
1120 iov[2].iov_len = (sizeof (gidx_t)
1121 - (string_table_size & (sizeof (gidx_t) - 1)));
1122 total += iov[2].iov_len;
1123 ++idx;
1126 header.hash_offset = total;
1127 header.hash_size = hash_size;
1128 iov[idx].iov_base = hash_table;
1129 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1130 total += iov[idx].iov_len;
1131 ++idx;
1133 header.module_offset = total;
1134 iov[idx].iov_base = module_table;
1135 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1136 total += iov[idx].iov_len;
1137 ++idx;
1139 assert (cur_extra_table - extra_table
1140 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1141 + sizeof (struct extra_entry_module))
1142 * nextra_modules));
1143 header.otherconv_offset = total;
1144 iov[idx].iov_base = extra_table;
1145 iov[idx].iov_len = cur_extra_table - extra_table;
1146 total += iov[idx].iov_len;
1147 ++idx;
1149 if (TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1150 /* The file was created with mode 0600. Make it world-readable. */
1151 || fchmod (fd, 0644) != 0
1152 /* Rename the file, possibly replacing an old one. */
1153 || rename (tmpfname, GCONV_MODULES_CACHE) != 0)
1155 int save_errno = errno;
1156 close (fd);
1157 unlink (tmpfname);
1158 error (EXIT_FAILURE, save_errno,
1159 gettext ("cannot generate output file"));
1162 close (fd);
1164 return 0;