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.
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.
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.
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. */
31 #include <stdio_ext.h>
35 #include <sys/cdefs.h>
38 #include "iconvconfig.h"
40 /* Get libc version number. */
41 #include "../version.h"
43 #define PACKAGE _libc_intl_domainname
46 /* The hashing function we use. */
47 #include "../intl/hash-string.h"
54 struct Strent *fromname_strent;
56 struct Strent *filename_strent;
57 const char *directory;
58 struct Strent *directory_strent;
61 struct Strent *toname_strent;
68 struct Strent *froment;
69 struct module *module;
77 struct Strent *strent;
84 const char *canonical_name;
85 struct Strent *canonical_strent;
87 struct module *from_internal;
88 struct module *to_internal;
90 struct other_conv_list
96 struct module *module;
97 struct other_conv *next;
99 struct other_conv_list *next;
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;
108 /* Short description of program. */
109 static const char doc[] = N_("\
110 Create fastloading iconv module configuration file.");
112 /* Strings for arguments in help texts. */
113 static const char args_doc[] = N_("[DIR...]");
115 /* Prototype for option handler. */
116 static error_t parse_opt (int key, char *arg, struct argp_state *state);
118 /* Function to print some extra text in the help message. */
119 static char *more_help (int key, const char *text, void *input);
121 /* Definitions of arguments for argp functions. */
122 #define OPT_PREFIX 300
123 #define OPT_NOSTDLIB 301
124 static const struct argp_option options[] =
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 }
135 /* Data structure to communicate with argp functions. */
136 static struct argp argp =
138 options, parse_opt, args_doc, doc, NULL, more_help
142 /* The function doing the actual work. */
143 static int handle_dir (const char *dir);
145 /* Add all known builtin conversions and aliases. */
146 static void add_builtins (void);
148 /* Create list of all aliases without circular aliases. */
149 static void get_aliases (void);
151 /* Create list of all modules. */
152 static void get_modules (void);
154 /* Get list of all the names and thereby indexing them. */
155 static void generate_name_list (void);
157 /* Collect information about all the names. */
158 static void generate_name_info (void);
160 /* Write the output file. */
161 static int write_output (void);
164 /* Prefix to be used for all file accesses. */
165 static const char *prefix = "";
167 static size_t prefix_len;
169 /* Directory to place output file in. */
170 static const char *output_file;
172 static size_t output_file_len;
174 /* If true, omit the GCONV_PATH directories and require some arguments. */
175 static bool nostdlib;
177 /* Search tree of the modules we know. */
178 static void *modules;
180 /* Search tree of the aliases we know. */
181 static void *aliases;
183 /* Search tree for name to index mapping. */
186 /* Number of names we know about. */
189 /* List of all aliases. */
190 static struct alias **alias_list;
191 static size_t nalias_list;
192 static size_t nalias_list_max;
194 /* List of all modules. */
195 static struct module **module_list;
196 static size_t nmodule_list;
197 static size_t nmodule_list_max;
199 /* Names and information about them. */
200 static struct name_info *name_info;
201 static size_t nname_info;
203 /* Number of translations not from or to INTERNAL. */
204 static size_t nextra_modules;
207 /* Names and aliases for the builtin transformations. */
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>
221 #undef BUILTIN_TRANSFORMATION
222 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
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>
239 #undef BUILTIN_TRANSFORMATION
240 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
243 /* Filename extension for the modules. */
245 # define MODULE_EXT ".so"
247 static const char gconv_module_ext[] = MODULE_EXT;
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);
255 /* C string table handling. */
259 /* Create new C string table object in memory. */
260 extern struct Strtab *strtabinit (void);
262 /* Free resources allocated for C string table ST. */
263 extern void strtabfree (struct Strtab *st);
265 /* Add string STR (length LEN is != 0) to C string table ST. */
266 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
269 /* Finalize string table ST and store size in *SIZE and return a pointer. */
270 extern void *strtabfinalize (struct Strtab *st, size_t *size);
272 /* Get offset in string table for string associated with SE. */
273 extern size_t strtaboffset (struct Strent *se);
275 /* String table we construct. */
276 static struct Strtab *strtab;
281 main (int argc, char *argv[])
286 /* Enable memory use testing. */
287 /* mcheck_pedantic (NULL); */
290 /* Set locale via LC_ALL. */
291 setlocale (LC_ALL, "");
293 /* Set the text message domain. */
294 textdomain (_libc_intl_domainname);
296 /* Parse and process arguments. */
297 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
299 if (nostdlib && remaining == argc)
300 error (2, 0, _("Directory arguments required when using --nostdlib"));
302 /* Initialize the string table. */
303 strtab = strtabinit ();
305 /* Handle all directories mentioned. */
306 while (remaining < argc)
307 status |= handle_dir (argv[remaining++]);
311 /* In any case also handle the standard directory. */
312 char *path = strdupa (GCONV_PATH), *tp = strsep (&path, ":");
315 status |= handle_dir (tp);
317 tp = strsep (&path, ":");
321 /* Add the builtin transformations and aliases without overwriting
325 /* Store aliases in an array. */
328 /* Get list of all modules. */
331 /* Generate list of all the names we know to handle in some way. */
332 generate_name_list ();
334 /* Now we know all the names we will handle, collect information
336 generate_name_info ();
338 /* Write the output file, but only if we haven't seen any error. */
340 status = write_output ();
342 error (1, 0, _("no output file produced because warning were issued"));
348 /* Handle program arguments. */
350 parse_opt (int key, char *arg, struct argp_state *state)
356 prefix_len = strlen (prefix);
360 output_file_len = strlen (output_file);
366 return ARGP_ERR_UNKNOWN;
373 more_help (int key, const char *text, void *input)
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"));
385 return (char *) text;
389 /* Print the version information. */
391 print_version (FILE *stream, struct argp_state *state)
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\
399 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
404 alias_compare (const void *p1, const void *p2)
406 const struct alias *a1 = (const struct alias *) p1;
407 const struct alias *a2 = (const struct alias *) p2;
409 return strcmp (a1->fromname, a2->fromname);
414 new_alias (const char *fromname, size_t fromlen, const char *toname,
420 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
422 newp->fromname = mempcpy (newp->toname, toname, tolen);
423 memcpy (newp->fromname, fromname, fromlen);
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. */
434 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
435 newp->toent = strtabadd (strtab, newp->toname, tolen);
444 /* We now expect two more string. The strings are normalized
445 (converted to UPPER case) and strored in the alias database. */
450 while (isspace (*rp))
453 while (*rp != '\0' && !isspace (*rp))
454 *wp++ = toupper (*rp++);
456 /* There is no `to' string on the line. Ignore it. */
460 while (isspace (*rp))
462 while (*rp != '\0' && !isspace (*rp))
463 *wp++ = toupper (*rp++);
465 /* No `to' string, ignore the line. */
469 assert (strlen (from) + 1 == (size_t) (to - from));
470 assert (strlen (to) + 1 == (size_t) (wp - to));
472 new_alias (from, to - from, to, wp - to);
477 append_alias (const void *nodep, VISIT value, int level)
479 if (value != leaf && value != postorder)
482 if (nalias_list_max == nalias_list)
484 nalias_list_max += 50;
485 alias_list = (struct alias **) xrealloc (alias_list,
487 * sizeof (struct alias *)));
490 alias_list[nalias_list++] = *(struct alias **) nodep;
497 twalk (aliases, append_alias);
502 module_compare (const void *p1, const void *p2)
504 const struct module *m1 = (const struct module *) p1;
505 const struct module *m2 = (const struct module *) p2;
508 result = strcmp (m1->fromname, m2->fromname);
510 result = strcmp (m1->toname, m2->toname);
516 /* Create new module record. */
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)
522 struct module *new_module;
523 size_t dirlen = strlen (directory) + 1;
527 new_module = (struct module *) xmalloc (sizeof (struct module)
528 + fromlen + tolen + filelen
531 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
533 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
535 new_module->cost = cost;
536 new_module->next = NULL;
538 tmp = mempcpy (new_module->filename, filename, filelen);
541 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
544 new_module->directory = directory;
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)
554 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
556 new_module->toname_strent = strtabadd (strtab, new_module->toname,
558 new_module->filename_strent = strtabadd (strtab, new_module->filename,
560 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
565 /* Add new module. */
568 add_module (char *rp, const char *directory)
573 3. filename of the module
574 4. an optional cost value
583 while (isspace (*rp))
586 while (*rp != '\0' && !isspace (*rp))
595 while (isspace (*rp))
597 while (*rp != '\0' && !isspace (*rp))
598 *wp++ = toupper (*rp++);
604 while (isspace (*rp));
606 while (*rp != '\0' && !isspace (*rp))
610 /* There is no cost, use one by default. */
616 /* There might be a cost value. */
620 cost = strtol (rp, &endp, 10);
621 if (rp == endp || cost < 1)
622 /* No useful information. */
626 if (module[0] == '\0')
627 /* No module name given. */
630 /* See whether we must add the ending. */
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;
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));
642 new_module (from, to - from, to, module - to, directory, module, wp - module,
647 /* Read the config file and add the data for this directory to that. */
649 handle_dir (const char *dir)
655 size_t dirlen = strlen (dir);
657 if (dir[dirlen - 1] != '/')
659 char *newp = (char *) xmalloc (dirlen + 2);
660 dir = memcpy (newp, dir, dirlen);
661 newp[dirlen++] = '/';
665 char infile[prefix_len + dirlen + sizeof "gconv-modules"];
668 cp = mempcpy (cp, prefix, prefix_len);
669 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
671 fp = fopen (infile, "r");
674 error (0, errno, "cannot open `%s'", infile);
678 /* No threads present. */
679 __fsetlocking (fp, FSETLOCKING_BYCALLER);
681 while (!feof_unlocked (fp))
683 char *rp, *endp, *word;
684 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
687 /* An error occurred. */
691 /* Terminate the line (excluding comments or newline) with a NUL
692 byte to simplify the following code. */
693 endp = strchr (rp, '#');
697 if (rp[n - 1] == '\n')
700 while (isspace (*rp))
703 /* If this is an empty line go on with the next one. */
708 while (*rp != '\0' && !isspace (*rp))
711 if (rp - word == sizeof ("alias") - 1
712 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
714 else if (rp - word == sizeof ("module") - 1
715 && memcmp (word, "module", sizeof ("module") - 1) == 0)
716 add_module (rp, dir);
718 /* Otherwise ignore the line. */
730 append_module (const void *nodep, VISIT value, int level)
734 if (value != leaf && value != postorder)
737 mo = *(struct module **) nodep;
740 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
743 mo->next = module_list[nmodule_list - 1];
744 module_list[nmodule_list - 1] = mo;
749 if (nmodule_list_max == nmodule_list)
751 nmodule_list_max += 50;
752 module_list = (struct module **) xrealloc (module_list,
754 * sizeof (struct module *)));
757 module_list[nmodule_list++] = mo;
764 twalk (modules, append_module);
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);
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);
793 name_compare (const void *p1, const void *p2)
795 const struct name *n1 = (const struct name *) p1;
796 const struct name *n2 = (const struct name *) p2;
798 return strcmp (n1->name, n2->name);
803 new_name (const char *str, struct Strent *strent)
805 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
808 newp->strent = strent;
809 newp->module_idx = -1;
810 newp->hashval = __hash_string (str);
819 generate_name_list (void)
823 /* A name we always need. */
824 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
825 sizeof ("INTERNAL"))),
826 &names, name_compare);
828 for (i = 0; i < nmodule_list; ++i)
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);
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);
846 name_to_module_idx (const char *name, int add)
849 struct name fake_name = { .name = name };
852 res = (struct name **) tfind (&fake_name, &names, name_compare);
856 idx = (*res)->module_idx;
857 if (idx == -1 && add)
858 /* No module index assigned yet. */
859 idx = (*res)->module_idx = nname_info++;
866 generate_name_info (void)
871 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
872 sizeof (struct name_info));
874 /* First add a special entry for the INTERNAL name. This must have
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);
882 for (i = 0; i < nmodule_list; ++i)
886 for (runp = module_list[i]; runp != NULL; runp = runp->next)
887 if (strcmp (runp->fromname, "INTERNAL") == 0)
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,
894 name_info[idx].canonical_name = runp->toname;
895 name_info[idx].canonical_strent = runp->toname_strent;
897 else if (strcmp (runp->toname, "INTERNAL") == 0)
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;
909 /* This is a transformation not to or from the INTERNAL
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;
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;
933 /* Now add the module index information for all the aliases. */
934 for (i = 0; i < nalias_list; ++i)
936 struct name fake_name = { .name = alias_list[i]->toname };
937 struct name **tonamep;
939 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
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);
952 is_prime (unsigned long int candidate)
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;
958 while (sq < candidate && candidate % divn != 0)
965 return candidate % divn != 0;
970 next_prime (uint32_t seed)
972 /* Make it definitely odd. */
975 while (!is_prime (seed))
982 /* Format of the output file.
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)
992 stoff ??? String table
994 hoff 8*hsize Array of tuples
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
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
1012 (following last entry with step count 0)
1019 size_t string_table_size;
1020 struct gconvcache_header header;
1021 struct hash_entry *hash_table;
1023 struct module_entry *module_table;
1025 char *cur_extra_table;
1028 struct iovec iov[6];
1029 static const gidx_t null_word;
1031 char finalname[prefix_len + sizeof GCONV_MODULES_CACHE];
1032 char tmpfname[(output_file == NULL ? sizeof finalname : output_file_len + 1)
1033 + strlen (".XXXXXX")];
1035 /* Function to insert the names. */
1037 name_insert (const void *nodep, VISIT value, int level)
1043 if (value != leaf && value != postorder)
1046 name = *(struct name **) nodep;
1047 idx = name->hashval % hash_size;
1048 hval2 = 1 + name->hashval % (hash_size - 2);
1050 while (hash_table[idx].string_offset != 0)
1051 if ((idx += hval2) >= hash_size)
1054 hash_table[idx].string_offset = strtaboffset (name->strent);
1056 assert (name->module_idx != -1);
1057 hash_table[idx].module_idx = name->module_idx;
1060 /* Open the output file. */
1061 if (output_file == NULL)
1063 assert (GCONV_MODULES_CACHE[0] == '/');
1064 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len),
1065 GCONV_MODULES_CACHE),
1067 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1070 strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX");
1071 fd = mkstemp (tmpfname);
1075 /* Create the string table. */
1076 string_table = strtabfinalize (strtab, &string_table_size);
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);
1088 /* Create the section for the module list. */
1089 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
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)
1096 + sizeof (struct extra_entry_module),
1098 cur_extra_table = extra_table;
1100 /* Fill in the module information. */
1101 for (n = 0; n < nname_info; ++n)
1103 module_table[n].canonname_offset =
1104 strtaboffset (name_info[n].canonical_strent);
1106 if (name_info[n].from_internal == NULL)
1108 module_table[n].fromdir_offset = 0;
1109 module_table[n].fromname_offset = 0;
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);
1119 if (name_info[n].to_internal == NULL)
1121 module_table[n].todir_offset = 0;
1122 module_table[n].toname_offset = 0;
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);
1132 if (name_info[n].other_conv_list != NULL)
1134 struct other_conv_list *other = name_info[n].other_conv_list;
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
1140 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1144 struct other_conv *runp;
1145 struct extra_entry *extra;
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;
1152 runp = &other->other_conv;
1155 cur_extra_table += sizeof (struct extra_entry_module);
1156 extra->module[extra->module_cnt].outname_offset =
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;
1167 while (runp != NULL);
1169 other = other->next;
1171 while (other != NULL);
1173 /* Final module_cnt is zero. */
1174 *((gidx_t *) cur_extra_table) = 0;
1175 cur_extra_table += sizeof (gidx_t);
1179 /* Clear padding. */
1180 memset (&header, 0, sizeof (struct gconvcache_header));
1182 header.magic = GCONVCACHE_MAGIC;
1184 iov[0].iov_base = &header;
1185 iov[0].iov_len = sizeof (struct gconvcache_header);
1186 total = iov[0].iov_len;
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;
1194 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
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;
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;
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;
1216 assert ((size_t) (cur_extra_table - extra_table)
1217 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1218 + sizeof (struct extra_entry_module))
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;
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)
1232 int save_errno = errno;
1235 error (EXIT_FAILURE, save_errno,
1236 gettext ("cannot generate output file"));