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