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