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