]> git.ipfire.org Git - thirdparty/glibc.git/blame - iconv/iconvconfig.c
Must flee the whitespace police.
[thirdparty/glibc.git] / iconv / iconvconfig.c
CommitLineData
6b98979f
UD
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.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
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 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
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/uio.h>
37
38#include "iconvconfig.h"
39
40/* Get libc version number. */
41#include "../version.h"
42
43#define PACKAGE _libc_intl_domainname
44
45
46/* The hashing function we use. */
47#include "../intl/hash-string.h"
48
49
50/* Types used. */
51struct module
52{
53 char *fromname;
54 struct Strent *fromname_strent;
55 char *filename;
56 struct Strent *filename_strent;
57 const char *directory;
58 struct Strent *directory_strent;
59 struct module *next;
60 int cost;
61 struct Strent *toname_strent;
62 char toname[0];
63};
64
65struct alias
66{
67 char *fromname;
68 struct Strent *froment;
69 struct module *module;
70 struct Strent *toent;
71 char toname[0];
72};
73
74struct name
75{
76 const char *name;
77 struct Strent *strent;
78 int module_idx;
79 uint32_t hashval;
80};
81
82struct name_info
83{
84 const char *canonical_name;
85 struct Strent *canonical_strent;
86
87 struct module *from_internal;
88 struct module *to_internal;
89
90 struct other_conv_list
91 {
92 int dest_idx;
93 struct other_conv
94 {
95 gidx_t module_idx;
96 struct module *module;
97 struct other_conv *next;
98 } other_conv;
99 struct other_conv_list *next;
100 } *other_conv_list;
101};
102
103
104/* Name and version of program. */
105static void print_version (FILE *stream, struct argp_state *state);
106void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
107
6b98979f
UD
108/* Short description of program. */
109static const char doc[] = N_("\
110Create fastloading iconv module configuration file.");
111
112/* Strings for arguments in help texts. */
113static const char args_doc[] = N_("[DIR...]");
114
6b98979f
UD
115/* Function to print some extra text in the help message. */
116static char *more_help (int key, const char *text, void *input);
117
118/* Data structure to communicate with argp functions. */
119static struct argp argp =
120{
47bd306e 121 NULL, NULL, args_doc, doc, NULL, more_help
6b98979f
UD
122};
123
124
125/* The function doing the actual work. */
126static int handle_dir (const char *dir);
127
128/* Add all known builtin conversions and aliases. */
129static void add_builtins (void);
130
131/* Create list of all aliases without circular aliases. */
132static void get_aliases (void);
133
134/* Create list of all modules. */
135static void get_modules (void);
136
137/* Get list of all the names and thereby indexing them. */
138static void generate_name_list (void);
139
140/* Collect information about all the names. */
141static void generate_name_info (void);
142
143/* Write the output file. */
144static int write_output (void);
145
146
6b98979f
UD
147/* Search tree of the modules we know. */
148static void *modules;
149
150/* Search tree of the aliases we know. */
151static void *aliases;
152
153/* Search tree for name to index mapping. */
154static void *names;
155
156/* Number of names we know about. */
157static int nnames;
158
159/* List of all aliases. */
160static struct alias **alias_list;
161static size_t nalias_list;
162static size_t nalias_list_max;
163
164/* List of all modules. */
165static struct module **module_list;
166static size_t nmodule_list;
167static size_t nmodule_list_max;
168
169/* Names and information about them. */
170static struct name_info *name_info;
171static size_t nname_info;
172
173/* Number of translations not from or to INTERNAL. */
174static size_t nextra_modules;
175
176
177/* Names and aliases for the builtin transformations. */
178static struct
179{
180 const char *from;
181 const char *to;
182} builtin_alias[] =
183 {
184#define BUILTIN_ALIAS(alias, real) \
185 { .from = alias, .to = real },
186#define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
187 MinT, MaxT)
188#include <gconv_builtin.h>
189 };
874d8461
UD
190#undef BUILTIN_ALIAS
191#undef BUILTIN_TRANSFORMATION
6b98979f
UD
192#define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
193
194static struct
195{
196 const char *from;
197 const char *to;
198 const char *module;
199 int cost;
200} builtin_trans[] =
201 {
202#define BUILTIN_ALIAS(alias, real)
203#define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
204 MinT, MaxT) \
205 { .from = From, .to = To, .module = Name, .cost = Cost },
206#include <gconv_builtin.h>
207 };
208#define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
209
210
211/* Filename extension for the modules. */
212#ifndef MODULE_EXT
213# define MODULE_EXT ".so"
214#endif
215static const char gconv_module_ext[] = MODULE_EXT;
216
217
218extern void *xmalloc (size_t n) __attribute__ ((__malloc__));
219extern void *xcalloc (size_t n, size_t m) __attribute__ ((__malloc__));
220extern void *xrealloc (void *p, size_t n);
221
222
223/* C string table handling. */
224struct Strtab;
225struct Strent;
226
227/* Create new C string table object in memory. */
228extern struct Strtab *strtabinit (void);
229
230/* Free resources allocated for C string table ST. */
231extern void strtabfree (struct Strtab *st);
232
233/* Add string STR (length LEN is != 0) to C string table ST. */
234extern struct Strent *strtabadd (struct Strtab *st, const char *str,
235 size_t len);
236
237/* Finalize string table ST and store size in *SIZE and return a pointer. */
238extern void *strtabfinalize (struct Strtab *st, size_t *size);
239
240/* Get offset in string table for string associated with SE. */
241extern size_t strtaboffset (struct Strent *se);
242
243/* String table we construct. */
244static struct Strtab *strtab;
245
246
247
248int
249main (int argc, char *argv[])
250{
251 int remaining;
252 int status = 0;
253 char *path;
254 char *tp;
255
256 /* Enable memory use testing. */
47bd306e
UD
257 /* mcheck_pedantic (NULL); */
258 mtrace ();
6b98979f
UD
259
260 /* Set locale via LC_ALL. */
261 setlocale (LC_ALL, "");
262
263 /* Set the text message domain. */
264 textdomain (_libc_intl_domainname);
265
266 /* Parse and process arguments. */
267 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
268
269 /* Initialize the string table. */
270 strtab = strtabinit ();
271
272 /* Handle all directories mentioned. */
273 while (remaining < argc)
274 status |= handle_dir (argv[remaining++]);
275
276 /* In any case also handle the standard directory. */
277 path = strdupa (GCONV_PATH);
278 tp = strtok (path, ":");
279 while (tp != NULL)
280 {
281 status |= handle_dir (tp);
282
283 tp = strtok (NULL, ":");
284 }
285
286 /* Add the builtin transformations and aliases without overwriting
287 anything. */
288 add_builtins ();
289
290 /* Store aliases in an array. */
291 get_aliases ();
292
293 /* Get list of all modules. */
294 get_modules ();
295
296 /* Generate list of all the names we know to handle in some way. */
297 generate_name_list ();
298
299 /* Now we know all the names we will handle, collect information
300 about them. */
301 generate_name_info ();
302
303 /* Write the output file. */
304 status = write_output ();
305
306 return status;
307}
308
309
6b98979f
UD
310static char *
311more_help (int key, const char *text, void *input)
312{
313 switch (key)
314 {
315 case ARGP_KEY_HELP_EXTRA:
316 /* We print some extra information. */
317 return strdup (gettext ("\
318Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
319 default:
320 break;
321 }
322 return (char *) text;
323}
324
325
326/* Print the version information. */
327static void
328print_version (FILE *stream, struct argp_state *state)
329{
330 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
331 fprintf (stream, gettext ("\
332Copyright (C) %s Free Software Foundation, Inc.\n\
333This is free software; see the source for copying conditions. There is NO\n\
334warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
335"), "2001");
336 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
337}
338
339
340static int
341alias_compare (const void *p1, const void *p2)
342{
343 const struct alias *a1 = (const struct alias *) p1;
344 const struct alias *a2 = (const struct alias *) p2;
345
346 return strcmp (a1->fromname, a2->fromname);
347}
348
349
350static void
351new_alias (const char *fromname, size_t fromlen, const char *toname,
352 size_t tolen)
353{
354 struct alias *newp;
355 void **inserted;
356
357 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
358
359 newp->fromname = mempcpy (newp->toname, toname, tolen);
360 memcpy (newp->fromname, fromname, fromlen);
361 newp->module = NULL;
362
363 inserted = (void **) tsearch (newp, &aliases, alias_compare);
364 if (inserted == NULL)
365 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
366 if (*inserted != newp)
367 /* Something went wrong, free this entry. */
368 free (newp);
369 else
370 {
371 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
372 newp->toent = strtabadd (strtab, newp->toname, tolen);
373 }
374}
375
376
377/* Add new alias. */
378static void
379add_alias (char *rp)
380{
381 /* We now expect two more string. The strings are normalized
382 (converted to UPPER case) and strored in the alias database. */
383 char *from;
384 char *to;
385 char *wp;
386
387 while (isspace (*rp))
388 ++rp;
389 from = wp = rp;
390 while (*rp != '\0' && !isspace (*rp))
391 *wp++ = toupper (*rp++);
392 if (*rp == '\0')
393 /* There is no `to' string on the line. Ignore it. */
394 return;
395 *wp++ = '\0';
396 to = ++rp;
397 while (isspace (*rp))
398 ++rp;
399 while (*rp != '\0' && !isspace (*rp))
400 *wp++ = toupper (*rp++);
401 if (to == wp)
402 /* No `to' string, ignore the line. */
403 return;
404 *wp++ = '\0';
405
47bd306e
UD
406 assert (strlen (from) + 1 == to - from);
407 assert (strlen (to) + 1 == wp - to);
408
6b98979f
UD
409 new_alias (from, to - from, to, wp - to);
410}
411
412
413static void
414append_alias (const void *nodep, VISIT value, int level)
415{
416 if (value != leaf && value != postorder)
417 return;
418
419 if (nalias_list_max == nalias_list)
420 {
421 nalias_list_max += 50;
422 alias_list = (struct alias **) xrealloc (alias_list,
423 (nalias_list_max
424 * sizeof (struct alias *)));
425 }
426
427 alias_list[nalias_list++] = *(struct alias **) nodep;
428}
429
430
431static void
432get_aliases (void)
433{
434 twalk (aliases, append_alias);
435}
436
437
438static int
439module_compare (const void *p1, const void *p2)
440{
441 const struct module *m1 = (const struct module *) p1;
442 const struct module *m2 = (const struct module *) p2;
443 int result;
444
445 result = strcmp (m1->fromname, m2->fromname);
446 if (result == 0)
447 result = strcmp (m1->toname, m2->toname);
448
449 return result;
450}
451
452
453/* Create new module record. */
454static void
455new_module (const char *fromname, size_t fromlen, const char *toname,
456 size_t tolen, const char *directory,
457 const char *filename, size_t filelen, int cost, size_t need_ext)
458{
459 struct module *new_module;
460 size_t dirlen = strlen (directory) + 1;
461 char *tmp;
462 void **inserted;
463
464 new_module = (struct module *) xmalloc (sizeof (struct module)
465 + fromlen + tolen + filelen
466 + need_ext);
467
468 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
469
470 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
471
472 new_module->cost = cost;
473 new_module->next = NULL;
474
475 tmp = mempcpy (new_module->filename, filename, filelen);
476 if (need_ext)
477 {
47bd306e
UD
478 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
479 filelen += need_ext;
6b98979f
UD
480 }
481 new_module->directory = directory;
482
483 /* Now insert the new module data structure in our search tree. */
484 inserted = (void **) tsearch (new_module, &modules, module_compare);
485 if (inserted == NULL)
486 error (EXIT_FAILURE, errno, "while inserting in search tree");
487 if (*inserted != new_module)
488 free (new_module);
489 else
490 {
491 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
492 fromlen);
493 new_module->toname_strent = strtabadd (strtab, new_module->toname,
494 tolen);
495 new_module->filename_strent = strtabadd (strtab, new_module->filename,
496 filelen);
497 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
498 }
499}
500
501
502/* Add new module. */
503static void
504internal_function
505add_module (char *rp, const char *directory)
506{
507 /* We expect now
508 1. `from' name
509 2. `to' name
510 3. filename of the module
511 4. an optional cost value
512 */
513 char *from;
514 char *to;
515 char *module;
516 char *wp;
517 int need_ext;
518 int cost;
519
520 while (isspace (*rp))
521 ++rp;
522 from = rp;
523 while (*rp != '\0' && !isspace (*rp))
524 {
525 *rp = toupper (*rp);
526 ++rp;
527 }
528 if (*rp == '\0')
529 return;
530 *rp++ = '\0';
531 to = wp = rp;
532 while (isspace (*rp))
533 ++rp;
534 while (*rp != '\0' && !isspace (*rp))
535 *wp++ = toupper (*rp++);
536 if (*rp == '\0')
537 return;
538 *wp++ = '\0';
539 do
540 ++rp;
541 while (isspace (*rp));
542 module = wp;
543 while (*rp != '\0' && !isspace (*rp))
544 *wp++ = *rp++;
545 if (*rp == '\0')
546 {
547 /* There is no cost, use one by default. */
548 *wp++ = '\0';
549 cost = 1;
550 }
551 else
552 {
553 /* There might be a cost value. */
554 char *endp;
555
556 *wp++ = '\0';
557 cost = strtol (rp, &endp, 10);
558 if (rp == endp || cost < 1)
559 /* No useful information. */
560 cost = 1;
561 }
562
563 if (module[0] == '\0')
564 /* No module name given. */
565 return;
566
567 /* See whether we must add the ending. */
568 need_ext = 0;
569 if (wp - module < sizeof (gconv_module_ext)
570 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
571 sizeof (gconv_module_ext)) != 0)
572 /* We must add the module extension. */
573 need_ext = sizeof (gconv_module_ext) - 1;
574
575 assert (strlen (from) + 1 == to - from);
576 assert (strlen (to) + 1 == module - to);
577 assert (strlen (module) + 1 == wp - module);
578
579 new_module (from, to - from, to, module - to, directory, module, wp - module,
580 cost, need_ext);
581}
582
583
584/* Read the config file and add the data for this directory to that. */
585static int
586handle_dir (const char *dir)
587{
588 char *infile;
589 FILE *fp;
590 char *line = NULL;
591 size_t linelen = 0;
592 size_t dirlen = strlen (dir);
593
594 if (dir[dirlen - 1] != '/')
595 {
596 char *newp = (char *) xmalloc (dirlen + 2);
597 dir = memcpy (newp, dir, dirlen);
598 newp[dirlen++] = '/';
599 newp[dirlen] = '\0';
600 }
601
602 infile = (char *) alloca (dirlen + sizeof "gconv-modules");
603 strcpy (mempcpy (infile, dir, dirlen), "gconv-modules");
604
605 fp = fopen (infile, "r");
606 if (fp == NULL)
607 {
608 error (0, errno, "cannot open `%s'", infile);
609 return 1;
610 }
611
612 /* No threads present. */
613 __fsetlocking (fp, FSETLOCKING_BYCALLER);
614
615 while (!feof_unlocked (fp))
616 {
617 char *rp, *endp, *word;
618 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
619
620 if (n < 0)
621 /* An error occurred. */
622 break;
623
624 rp = line;
625 /* Terminate the line (excluding comments or newline) with a NUL
626 byte to simplify the following code. */
627 endp = strchr (rp, '#');
628 if (endp != NULL)
629 *endp = '\0';
630 else
631 if (rp[n - 1] == '\n')
632 rp[n - 1] = '\0';
633
634 while (isspace (*rp))
635 ++rp;
636
637 /* If this is an empty line go on with the next one. */
638 if (rp == endp)
639 continue;
640
641 word = rp;
642 while (*rp != '\0' && !isspace (*rp))
643 ++rp;
644
645 if (rp - word == sizeof ("alias") - 1
646 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
647 add_alias (rp);
648 else if (rp - word == sizeof ("module") - 1
649 && memcmp (word, "module", sizeof ("module") - 1) == 0)
650 add_module (rp, dir);
651 /* else */
652 /* Otherwise ignore the line. */
653 }
654
655 free (line);
656
657 fclose (fp);
658
659 return 0;
660}
661
662
663static void
664append_module (const void *nodep, VISIT value, int level)
665{
666 struct module *mo;
667
668 if (value != leaf && value != postorder)
669 return;
670
671 mo = *(struct module **) nodep;
672
673 if (nmodule_list > 0
674 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
675 {
676 /* Same name. */
677 mo->next = module_list[nmodule_list - 1];
678 module_list[nmodule_list - 1] = mo;
679
680 return;
681 }
682
683 if (nmodule_list_max == nmodule_list)
684 {
685 nmodule_list_max += 50;
686 module_list = (struct module **) xrealloc (module_list,
687 (nmodule_list_max
688 * sizeof (struct module *)));
689 }
690
691 module_list[nmodule_list++] = mo;
692}
693
694
695static void
696get_modules (void)
697{
698 twalk (modules, append_module);
699}
700
701
702static void
703add_builtins (void)
704{
705 size_t cnt;
706
707 /* Add all aliases. */
708 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
709 new_alias (builtin_alias[cnt].from,
710 strlen (builtin_alias[cnt].from) + 1,
711 builtin_alias[cnt].to,
712 strlen (builtin_alias[cnt].to) + 1);
713
714 /* add the builtin transformations. */
715 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
716 new_module (builtin_trans[cnt].from,
717 strlen (builtin_trans[cnt].from) + 1,
718 builtin_trans[cnt].to,
719 strlen (builtin_trans[cnt].to) + 1,
720 "", builtin_trans[cnt].module,
721 strlen (builtin_trans[cnt].module) + 1,
722 builtin_trans[cnt].cost, 0);
723}
724
725
726static int
727name_compare (const void *p1, const void *p2)
728{
729 const struct name *n1 = (const struct name *) p1;
730 const struct name *n2 = (const struct name *) p2;
731
732 return strcmp (n1->name, n2->name);
733}
734
735
736static struct name *
737new_name (const char *str, struct Strent *strent)
738{
739 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
740
741 newp->name = str;
742 newp->strent = strent;
743 newp->module_idx = -1;
744 newp->hashval = hash_string (str);
745
746 ++nnames;
747
748 return newp;
749}
750
751
752static void
753generate_name_list (void)
754{
755 size_t i;
756
757 for (i = 0; i < nmodule_list; ++i)
758 {
759 struct module *runp;
760
761 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
762 tsearch (new_name (module_list[i]->fromname,
763 module_list[i]->fromname_strent),
764 &names, name_compare);
765
766 for (runp = module_list[i]; runp != NULL; runp = runp->next)
767 if (strcmp (runp->toname, "INTERNAL") != 0)
768 tsearch (new_name (runp->toname, runp->toname_strent),
769 &names, name_compare);
770 }
771}
772
773
774static int
775name_to_module_idx (const char *name, int add)
776{
777 struct name **res;
778 struct name fake_name = { .name = name };
779 int idx;
780
781 res = (struct name **) tfind (&fake_name, &names, name_compare);
782 if (res == NULL)
783 abort ();
784
785 idx = (*res)->module_idx;
786 if (idx == -1 && add)
787 /* No module index assigned yet. */
788 idx = (*res)->module_idx = nname_info++;
789
790 return idx;
791}
792
793
794static void
795generate_name_info (void)
796{
797 size_t i;
798
799 name_info = (struct name_info *) xcalloc (nmodule_list,
800 sizeof (struct name_info));
801
802 for (i = 0; i < nmodule_list; ++i)
803 {
804 struct module *runp;
805
806 for (runp = module_list[i]; runp != NULL; runp = runp->next)
807 if (strcmp (runp->fromname, "INTERNAL") == 0)
808 {
809 int idx = name_to_module_idx (runp->toname, 1);
810 name_info[idx].from_internal = runp;
811 assert (name_info[idx].canonical_name == NULL
812 || strcmp (name_info[idx].canonical_name,
813 runp->toname) == 0);
814 name_info[idx].canonical_name = runp->toname;
815 name_info[idx].canonical_strent = runp->toname_strent;
816 }
817 else if (strcmp (runp->toname, "INTERNAL") == 0)
818 {
819 int idx = name_to_module_idx (runp->fromname, 1);
820 name_info[idx].to_internal = runp;
821 assert (name_info[idx].canonical_name == NULL
822 || strcmp (name_info[idx].canonical_name,
823 runp->fromname) == 0);
824 name_info[idx].canonical_name = runp->fromname;
825 name_info[idx].canonical_strent = runp->fromname_strent;
826 }
827 else
828 {
829 /* This is a transformation not to or from the INTERNAL
830 encoding. */
831 int from_idx = name_to_module_idx (runp->fromname, 1);
832 int to_idx = name_to_module_idx (runp->toname, 1);
833 struct other_conv_list *newp;
834
835 newp = (struct other_conv_list *)
836 xmalloc (sizeof (struct other_conv_list));
837 newp->other_conv.module_idx = to_idx;
838 newp->other_conv.module = runp;
839 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
840 newp->dest_idx = to_idx;
841 newp->next = name_info[from_idx].other_conv_list;
842 name_info[from_idx].other_conv_list = newp;
843 assert (name_info[from_idx].canonical_name == NULL
844 || strcmp (name_info[from_idx].canonical_name,
845 runp->fromname) == 0);
846 name_info[from_idx].canonical_name = runp->fromname;
847 name_info[from_idx].canonical_strent = runp->fromname_strent;
848
849 ++nextra_modules;
850 }
851 }
852
853 /* Now add the module index information for all the aliases. */
854 for (i = 0; i < nalias_list; ++i)
855 {
856 struct name fake_name = { .name = alias_list[i]->toname };
857 struct name **tonamep;
858
859 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
860 if (tonamep != NULL)
861 {
862 struct name *newp = new_name (alias_list[i]->fromname,
863 alias_list[i]->froment);
864 newp->module_idx = (*tonamep)->module_idx;
865 tsearch (newp, &names, name_compare);
866 }
867 }
868}
869
870
871static int
872is_prime (unsigned long int candidate)
873{
874 /* No even number and none less than 10 will be passed here. */
875 unsigned long int divn = 3;
876 unsigned long int sq = divn * divn;
877
878 while (sq < candidate && candidate % divn != 0)
879 {
880 ++divn;
881 sq += 4 * divn;
882 ++divn;
883 }
884
885 return candidate % divn != 0;
886}
887
888
889static uint32_t
890next_prime (uint32_t seed)
891{
892 /* Make it definitely odd. */
893 seed |= 1;
894
895 while (!is_prime (seed))
896 seed += 2;
897
898 return seed;
899}
900
901
902/* Format of the output file.
903
904 Offset Length Description
905 0000 4 Magic header bytes
906 0004 4 Offset of string table (stoff)
907 0008 4 Offset of name hashing table (hoff)
908 000C 4 Hashing table size (hsize)
909 0010 4 Offset of module table (moff)
910 0014 4 Offset of other conversion module table (ooff)
911
912 stoff ??? String table
913
914 hoff 8*hsize Array of tuples
915 string table offset
916 module index
917
918 moff ??? Array of tuples
919 canonical name offset
920 from-internal module dir name offset
921 from-internal module name off
922 to-internal module dir name offset
923 to-internal module name offset
924 offset into other conversion table
925
bbdf8285
UD
926 ooff ??? One or more of
927 number of steps/modules
6b98979f 928 one or more of tuple
bbdf8285 929 canonical name offset for output
6b98979f
UD
930 module dir name offset
931 module name offset
bbdf8285 932 (following last entry with step count 0)
6b98979f
UD
933*/
934static int
935write_output (void)
936{
937 int fd;
938 char *string_table;
939 size_t string_table_size;
940 struct gconvcache_header header;
941 struct hash_entry *hash_table;
942 size_t hash_size;
943 struct module_entry *module_table;
944 char *extra_table;
945 char *cur_extra_table;
946 size_t n;
947 int idx;
948 struct iovec iov[6];
949 static const gidx_t null_word;
950 size_t total;
951
952 /* Function to insert the names. */
953 void name_insert (const void *nodep, VISIT value, int level)
954 {
955 struct name *name;
956 unsigned int idx;
957 unsigned int hval2;
958
959 if (value != leaf && value != postorder)
960 return;
961
962 name = *(struct name **) nodep;
963 idx = name->hashval % hash_size;
964 hval2 = 1 + name->hashval % (hash_size - 2);
965
966 while (hash_table[idx].string_offset != 0)
967 if ((idx += hval2) >= hash_size)
968 idx -= hash_size;
969
970 hash_table[idx].string_offset = strtaboffset (name->strent);
971
972 assert (name->module_idx != -1);
973 hash_table[idx].module_idx = name->module_idx;
974 }
975
976 /* Open the output file. */
977 fd = open (GCONV_MODULES_CACHE, O_TRUNC | O_CREAT | O_RDWR, 0644);
978 if (fd == -1)
979 return 1;
980
981 /* Create the string table. */
982 string_table = strtabfinalize (strtab, &string_table_size);
983
984 /* Create the hashing table. We know how many strings we have.
985 Creating a perfect hash table is not reasonable here. Therefore
986 we use open hashing and a table size which is the next prime 40%
987 larger than the number of strings. */
988 hash_size = next_prime (nnames * 1.4);
989 hash_table = (struct hash_entry *) xcalloc (hash_size,
990 sizeof (struct hash_entry));
991 /* Fill the hash table. */
992 twalk (names, name_insert);
993
994 /* Create the section for the module list. */
995 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
996 nname_info);
997
998 /* Allocate memory for the non-INTERNAL conversions. The allocated
999 memory can be more than is actually needed. */
1000 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1001 + sizeof (gidx_t)
1002 + sizeof (struct extra_entry_module),
1003 nextra_modules);
1004 cur_extra_table = extra_table;
1005
1006 /* Fill in the module information. */
1007 for (n = 0; n < nname_info; ++n)
1008 {
1009 module_table[n].canonname_offset =
1010 strtaboffset (name_info[n].canonical_strent);
1011
1012 if (name_info[n].from_internal == NULL)
1013 {
1014 module_table[n].fromdir_offset = 0;
1015 module_table[n].fromname_offset = 0;
1016 }
1017 else
1018 {
1019 module_table[n].fromdir_offset =
1020 strtaboffset (name_info[n].from_internal->directory_strent);
1021 module_table[n].fromname_offset =
1022 strtaboffset (name_info[n].from_internal->filename_strent);
1023 }
1024
1025 if (name_info[n].to_internal == NULL)
1026 {
1027 module_table[n].todir_offset = 0;
1028 module_table[n].toname_offset = 0;
1029 }
1030 else
1031 {
1032 module_table[n].todir_offset =
1033 strtaboffset (name_info[n].to_internal->directory_strent);
1034 module_table[n].toname_offset =
1035 strtaboffset (name_info[n].to_internal->filename_strent);
1036 }
1037
1038 if (name_info[n].other_conv_list != NULL)
1039 {
1040 struct other_conv_list *other = name_info[n].other_conv_list;
1041
1042 /* Store the reference. We add 1 to distinguish the entry
1043 at offset zero from the case where no extra modules are
1044 available. The file reader has to account for the
1045 offset. */
1046 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1047
1048 do
1049 {
1050 struct other_conv *runp;
1051 struct extra_entry *extra;
1052
1053 /* Allocate new entry. */
1054 extra = (struct extra_entry *) cur_extra_table;
1055 cur_extra_table += sizeof (struct extra_entry);
1056 extra->module_cnt = 0;
1057
1058 runp = &other->other_conv;
1059 do
1060 {
1061 cur_extra_table += sizeof (struct extra_entry_module);
1062 extra->module[extra->module_cnt].outname_offset =
1063 runp->next == NULL
1064 ? other->dest_idx : runp->next->module_idx;
1065 extra->module[extra->module_cnt].dir_offset =
1066 strtaboffset (runp->module->directory_strent);
1067 extra->module[extra->module_cnt].name_offset =
1068 strtaboffset (runp->module->filename_strent);
1069 ++extra->module_cnt;
1070
1071 runp = runp->next;
1072 }
1073 while (runp != NULL);
1074
1075 other = other->next;
1076 }
1077 while (other != NULL);
1078
1079 /* Final module_cnt is zero. */
1080 *((gidx_t *) cur_extra_table) = 0;
1081 cur_extra_table += sizeof (gidx_t);
1082 }
1083 }
1084
1085 header.magic = GCONVCACHE_MAGIC;
1086
1087 iov[0].iov_base = &header;
1088 iov[0].iov_len = sizeof (struct gconvcache_header);
1089 total = iov[0].iov_len;
1090
1091 header.string_offset = total;
1092 iov[1].iov_base = string_table;
1093 iov[1].iov_len = string_table_size;
1094 total += iov[1].iov_len;
1095
1096 idx = 2;
1097 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1098 {
1099 iov[2].iov_base = (void *) &null_word;
1100 iov[2].iov_len = (sizeof (gidx_t)
1101 - (string_table_size & (sizeof (gidx_t) - 1)));
1102 total += iov[2].iov_len;
1103 ++idx;
1104 }
1105
1106 header.hash_offset = total;
1107 header.hash_size = hash_size;
1108 iov[idx].iov_base = hash_table;
1109 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1110 total += iov[idx].iov_len;
1111 ++idx;
1112
1113 header.module_offset = total;
1114 iov[idx].iov_base = module_table;
1115 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1116 total += iov[idx].iov_len;
1117 ++idx;
1118
1119 assert (cur_extra_table - extra_table
1120 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1121 + sizeof (struct extra_entry_module))
1122 * nextra_modules));
1123 header.otherconv_offset = total;
1124 iov[idx].iov_base = extra_table;
1125 iov[idx].iov_len = cur_extra_table - extra_table;
1126 total += iov[idx].iov_len;
1127 ++idx;
1128
1129 if (TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total)
1130 {
1131 int save_errno = errno;
1132 close (fd);
1133 unlink (GCONV_MODULES_CACHE);
1134 error (EXIT_FAILURE, save_errno, gettext ("cannot write output file"));
1135 }
1136
1137 close (fd);
1138
1139 return 0;
1140}