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