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