1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000-2013 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 as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
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, 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 }
136 /* Data structure to communicate with argp functions. */
137 static struct argp argp =
139 options, parse_opt, args_doc, doc, NULL, more_help
143 /* The function doing the actual work. */
144 static int handle_dir (const char *dir);
146 /* Add all known builtin conversions and aliases. */
147 static void add_builtins (void);
149 /* Create list of all aliases without circular aliases. */
150 static void get_aliases (void);
152 /* Create list of all modules. */
153 static void get_modules (void);
155 /* Get list of all the names and thereby indexing them. */
156 static void generate_name_list (void);
158 /* Collect information about all the names. */
159 static void generate_name_info (void);
161 /* Write the output file. */
162 static int write_output (void);
165 /* Prefix to be used for all file accesses. */
166 static const char *prefix = "";
168 static size_t prefix_len;
170 /* Directory to place output file in. */
171 static const char *output_file;
173 static size_t output_file_len;
175 /* If true, omit the GCONV_PATH directories and require some arguments. */
176 static bool nostdlib;
178 /* Search tree of the modules we know. */
179 static void *modules;
181 /* Search tree of the aliases we know. */
182 static void *aliases;
184 /* Search tree for name to index mapping. */
187 /* Number of names we know about. */
190 /* List of all aliases. */
191 static struct alias **alias_list;
192 static size_t nalias_list;
193 static size_t nalias_list_max;
195 /* List of all modules. */
196 static struct module **module_list;
197 static size_t nmodule_list;
198 static size_t nmodule_list_max;
200 /* Names and information about them. */
201 static struct name_info *name_info;
202 static size_t nname_info;
204 /* Number of translations not from or to INTERNAL. */
205 static size_t nextra_modules;
208 /* Names and aliases for the builtin transformations. */
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>
222 #undef BUILTIN_TRANSFORMATION
223 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
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>
240 #undef BUILTIN_TRANSFORMATION
241 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
244 /* Filename extension for the modules. */
246 # define MODULE_EXT ".so"
248 static const char gconv_module_ext[] = MODULE_EXT;
251 #include <programs/xmalloc.h>
254 /* C string table handling. */
258 /* Create new C string table object in memory. */
259 extern struct Strtab *strtabinit (void);
261 /* Free resources allocated for C string table ST. */
262 extern void strtabfree (struct Strtab *st);
264 /* Add string STR (length LEN is != 0) to C string table ST. */
265 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
268 /* Finalize string table ST and store size in *SIZE and return a pointer. */
269 extern void *strtabfinalize (struct Strtab *st, size_t *size);
271 /* Get offset in string table for string associated with SE. */
272 extern size_t strtaboffset (struct Strent *se);
274 /* String table we construct. */
275 static struct Strtab *strtab;
280 main (int argc, char *argv[])
285 /* Enable memory use testing. */
286 /* mcheck_pedantic (NULL); */
289 /* Set locale via LC_ALL. */
290 setlocale (LC_ALL, "");
292 /* Set the text message domain. */
293 textdomain (_libc_intl_domainname);
295 /* Parse and process arguments. */
296 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
298 if (nostdlib && remaining == argc)
299 error (2, 0, _("Directory arguments required when using --nostdlib"));
301 /* Initialize the string table. */
302 strtab = strtabinit ();
304 /* Handle all directories mentioned. */
305 while (remaining < argc)
306 status |= handle_dir (argv[remaining++]);
310 /* In any case also handle the standard directory. */
311 char *path = strdupa (GCONV_PATH), *tp = strsep (&path, ":");
314 status |= handle_dir (tp);
316 tp = strsep (&path, ":");
320 /* Add the builtin transformations and aliases without overwriting
324 /* Store aliases in an array. */
327 /* Get list of all modules. */
330 /* Generate list of all the names we know to handle in some way. */
331 generate_name_list ();
333 /* Now we know all the names we will handle, collect information
335 generate_name_info ();
337 /* Write the output file, but only if we haven't seen any error. */
339 status = write_output ();
341 error (1, 0, _("no output file produced because warnings were issued"));
347 /* Handle program arguments. */
349 parse_opt (int key, char *arg, struct argp_state *state)
355 prefix_len = strlen (prefix);
359 output_file_len = strlen (output_file);
365 return ARGP_ERR_UNKNOWN;
372 more_help (int key, const char *text, void *input)
377 case ARGP_KEY_HELP_EXTRA:
378 /* We print some extra information. */
379 if (asprintf (&tp, gettext ("\
380 For bug reporting instructions, please see:\n\
381 %s.\n"), REPORT_BUGS_TO) < 0)
387 return (char *) text;
391 /* Print the version information. */
393 print_version (FILE *stream, struct argp_state *state)
395 fprintf (stream, "iconvconfig %s%s\n", PKGVERSION, VERSION);
396 fprintf (stream, gettext ("\
397 Copyright (C) %s Free Software Foundation, Inc.\n\
398 This is free software; see the source for copying conditions. There is NO\n\
399 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
401 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
406 alias_compare (const void *p1, const void *p2)
408 const struct alias *a1 = (const struct alias *) p1;
409 const struct alias *a2 = (const struct alias *) p2;
411 return strcmp (a1->fromname, a2->fromname);
416 new_alias (const char *fromname, size_t fromlen, const char *toname,
422 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
424 newp->fromname = mempcpy (newp->toname, toname, tolen);
425 memcpy (newp->fromname, fromname, fromlen);
428 inserted = (void **) tsearch (newp, &aliases, alias_compare);
429 if (inserted == NULL)
430 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
431 if (*inserted != newp)
432 /* Something went wrong, free this entry. */
436 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
437 newp->toent = strtabadd (strtab, newp->toname, tolen);
446 /* We now expect two more string. The strings are normalized
447 (converted to UPPER case) and strored in the alias database. */
452 while (isspace (*rp))
455 while (*rp != '\0' && !isspace (*rp))
456 *wp++ = toupper (*rp++);
458 /* There is no `to' string on the line. Ignore it. */
462 while (isspace (*rp))
464 while (*rp != '\0' && !isspace (*rp))
465 *wp++ = toupper (*rp++);
467 /* No `to' string, ignore the line. */
471 assert (strlen (from) + 1 == (size_t) (to - from));
472 assert (strlen (to) + 1 == (size_t) (wp - to));
474 new_alias (from, to - from, to, wp - to);
479 append_alias (const void *nodep, VISIT value, int level)
481 if (value != leaf && value != postorder)
484 if (nalias_list_max == nalias_list)
486 nalias_list_max += 50;
487 alias_list = (struct alias **) xrealloc (alias_list,
489 * sizeof (struct alias *)));
492 alias_list[nalias_list++] = *(struct alias **) nodep;
499 twalk (aliases, append_alias);
504 module_compare (const void *p1, const void *p2)
506 const struct module *m1 = (const struct module *) p1;
507 const struct module *m2 = (const struct module *) p2;
510 result = strcmp (m1->fromname, m2->fromname);
512 result = strcmp (m1->toname, m2->toname);
518 /* Create new module record. */
520 new_module (const char *fromname, size_t fromlen, const char *toname,
521 size_t tolen, const char *directory,
522 const char *filename, size_t filelen, int cost, size_t need_ext)
524 struct module *new_module;
525 size_t dirlen = strlen (directory) + 1;
529 new_module = (struct module *) xmalloc (sizeof (struct module)
530 + fromlen + tolen + filelen
533 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
535 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
537 new_module->cost = cost;
538 new_module->next = NULL;
540 tmp = mempcpy (new_module->filename, filename, filelen);
543 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
546 new_module->directory = directory;
548 /* Now insert the new module data structure in our search tree. */
549 inserted = (void **) tsearch (new_module, &modules, module_compare);
550 if (inserted == NULL)
551 error (EXIT_FAILURE, errno, "while inserting in search tree");
552 if (*inserted != new_module)
556 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
558 new_module->toname_strent = strtabadd (strtab, new_module->toname,
560 new_module->filename_strent = strtabadd (strtab, new_module->filename,
562 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
567 /* Add new module. */
570 add_module (char *rp, const char *directory)
575 3. filename of the module
576 4. an optional cost value
585 while (isspace (*rp))
588 while (*rp != '\0' && !isspace (*rp))
597 while (isspace (*rp))
599 while (*rp != '\0' && !isspace (*rp))
600 *wp++ = toupper (*rp++);
606 while (isspace (*rp));
608 while (*rp != '\0' && !isspace (*rp))
612 /* There is no cost, use one by default. */
618 /* There might be a cost value. */
622 cost = strtol (rp, &endp, 10);
623 if (rp == endp || cost < 1)
624 /* No useful information. */
628 if (module[0] == '\0')
629 /* No module name given. */
632 /* See whether we must add the ending. */
634 if ((size_t) (wp - module) < sizeof (gconv_module_ext)
635 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
636 sizeof (gconv_module_ext)) != 0)
637 /* We must add the module extension. */
638 need_ext = sizeof (gconv_module_ext) - 1;
640 assert (strlen (from) + 1 == (size_t) (to - from));
641 assert (strlen (to) + 1 == (size_t) (module - to));
642 assert (strlen (module) + 1 == (size_t) (wp - module));
644 new_module (from, to - from, to, module - to, directory, module, wp - module,
649 /* Read the config file and add the data for this directory to that. */
651 handle_dir (const char *dir)
657 size_t dirlen = strlen (dir);
659 if (dir[dirlen - 1] != '/')
661 char *newp = (char *) xmalloc (dirlen + 2);
662 dir = memcpy (newp, dir, dirlen);
663 newp[dirlen++] = '/';
667 char infile[prefix_len + dirlen + sizeof "gconv-modules"];
670 cp = mempcpy (cp, prefix, prefix_len);
671 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
673 fp = fopen (infile, "r");
676 error (0, errno, "cannot open `%s'", infile);
680 /* No threads present. */
681 __fsetlocking (fp, FSETLOCKING_BYCALLER);
683 while (!feof_unlocked (fp))
685 char *rp, *endp, *word;
686 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
689 /* An error occurred. */
693 /* Terminate the line (excluding comments or newline) with a NUL
694 byte to simplify the following code. */
695 endp = strchr (rp, '#');
699 if (rp[n - 1] == '\n')
702 while (isspace (*rp))
705 /* If this is an empty line go on with the next one. */
710 while (*rp != '\0' && !isspace (*rp))
713 if (rp - word == sizeof ("alias") - 1
714 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
716 else if (rp - word == sizeof ("module") - 1
717 && memcmp (word, "module", sizeof ("module") - 1) == 0)
718 add_module (rp, dir);
720 /* Otherwise ignore the line. */
732 append_module (const void *nodep, VISIT value, int level)
736 if (value != leaf && value != postorder)
739 mo = *(struct module **) nodep;
742 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
745 mo->next = module_list[nmodule_list - 1];
746 module_list[nmodule_list - 1] = mo;
751 if (nmodule_list_max == nmodule_list)
753 nmodule_list_max += 50;
754 module_list = (struct module **) xrealloc (module_list,
756 * sizeof (struct module *)));
759 module_list[nmodule_list++] = mo;
766 twalk (modules, append_module);
775 /* Add all aliases. */
776 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
777 new_alias (builtin_alias[cnt].from,
778 strlen (builtin_alias[cnt].from) + 1,
779 builtin_alias[cnt].to,
780 strlen (builtin_alias[cnt].to) + 1);
782 /* add the builtin transformations. */
783 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
784 new_module (builtin_trans[cnt].from,
785 strlen (builtin_trans[cnt].from) + 1,
786 builtin_trans[cnt].to,
787 strlen (builtin_trans[cnt].to) + 1,
788 "", builtin_trans[cnt].module,
789 strlen (builtin_trans[cnt].module) + 1,
790 builtin_trans[cnt].cost, 0);
795 name_compare (const void *p1, const void *p2)
797 const struct name *n1 = (const struct name *) p1;
798 const struct name *n2 = (const struct name *) p2;
800 return strcmp (n1->name, n2->name);
805 new_name (const char *str, struct Strent *strent)
807 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
810 newp->strent = strent;
811 newp->module_idx = -1;
812 newp->hashval = __hash_string (str);
821 generate_name_list (void)
825 /* A name we always need. */
826 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
827 sizeof ("INTERNAL"))),
828 &names, name_compare);
830 for (i = 0; i < nmodule_list; ++i)
834 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
835 tsearch (new_name (module_list[i]->fromname,
836 module_list[i]->fromname_strent),
837 &names, name_compare);
839 for (runp = module_list[i]; runp != NULL; runp = runp->next)
840 if (strcmp (runp->toname, "INTERNAL") != 0)
841 tsearch (new_name (runp->toname, runp->toname_strent),
842 &names, name_compare);
848 name_to_module_idx (const char *name, int add)
851 struct name fake_name = { .name = name };
854 res = (struct name **) tfind (&fake_name, &names, name_compare);
858 idx = (*res)->module_idx;
859 if (idx == -1 && add)
860 /* No module index assigned yet. */
861 idx = (*res)->module_idx = nname_info++;
868 generate_name_info (void)
873 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
874 sizeof (struct name_info));
876 /* First add a special entry for the INTERNAL name. This must have
878 idx = name_to_module_idx ("INTERNAL", 1);
879 name_info[0].canonical_name = "INTERNAL";
880 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
881 sizeof ("INTERNAL"));
882 assert (nname_info == 1);
884 for (i = 0; i < nmodule_list; ++i)
888 for (runp = module_list[i]; runp != NULL; runp = runp->next)
889 if (strcmp (runp->fromname, "INTERNAL") == 0)
891 idx = name_to_module_idx (runp->toname, 1);
892 name_info[idx].from_internal = runp;
893 assert (name_info[idx].canonical_name == NULL
894 || strcmp (name_info[idx].canonical_name,
896 name_info[idx].canonical_name = runp->toname;
897 name_info[idx].canonical_strent = runp->toname_strent;
899 else if (strcmp (runp->toname, "INTERNAL") == 0)
901 idx = name_to_module_idx (runp->fromname, 1);
902 name_info[idx].to_internal = runp;
903 assert (name_info[idx].canonical_name == NULL
904 || strcmp (name_info[idx].canonical_name,
905 runp->fromname) == 0);
906 name_info[idx].canonical_name = runp->fromname;
907 name_info[idx].canonical_strent = runp->fromname_strent;
911 /* This is a transformation not to or from the INTERNAL
913 int from_idx = name_to_module_idx (runp->fromname, 1);
914 int to_idx = name_to_module_idx (runp->toname, 1);
915 struct other_conv_list *newp;
917 newp = (struct other_conv_list *)
918 xmalloc (sizeof (struct other_conv_list));
919 newp->other_conv.module_idx = to_idx;
920 newp->other_conv.module = runp;
921 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
922 newp->dest_idx = to_idx;
923 newp->next = name_info[from_idx].other_conv_list;
924 name_info[from_idx].other_conv_list = newp;
925 assert (name_info[from_idx].canonical_name == NULL
926 || strcmp (name_info[from_idx].canonical_name,
927 runp->fromname) == 0);
928 name_info[from_idx].canonical_name = runp->fromname;
929 name_info[from_idx].canonical_strent = runp->fromname_strent;
935 /* Now add the module index information for all the aliases. */
936 for (i = 0; i < nalias_list; ++i)
938 struct name fake_name = { .name = alias_list[i]->toname };
939 struct name **tonamep;
941 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
944 struct name *newp = new_name (alias_list[i]->fromname,
945 alias_list[i]->froment);
946 newp->module_idx = (*tonamep)->module_idx;
947 tsearch (newp, &names, name_compare);
954 is_prime (unsigned long int candidate)
956 /* No even number and none less than 10 will be passed here. */
957 unsigned long int divn = 3;
958 unsigned long int sq = divn * divn;
960 while (sq < candidate && candidate % divn != 0)
967 return candidate % divn != 0;
972 next_prime (uint32_t seed)
974 /* Make it definitely odd. */
977 while (!is_prime (seed))
984 /* Format of the output file.
986 Offset Length Description
987 0000 4 Magic header bytes
988 0004 2 Offset of string table (stoff)
989 0006 2 Offset of name hashing table (hoff)
990 0008 2 Hashing table size (hsize)
991 000A 2 Offset of module table (moff)
992 000C 2 Offset of other conversion module table (ooff)
994 stoff ??? String table
996 hoff 8*hsize Array of tuples
1000 moff ??? Array of tuples
1001 canonical name offset
1002 from-internal module dir name offset
1003 from-internal module name off
1004 to-internal module dir name offset
1005 to-internal module name offset
1006 offset into other conversion table
1008 ooff ??? One or more of
1009 number of steps/modules
1010 one or more of tuple
1011 canonical name offset for output
1012 module dir name offset
1014 (following last entry with step count 0)
1017 static struct hash_entry *hash_table;
1018 static size_t hash_size;
1020 /* Function to insert the names. */
1021 static void name_insert (const void *nodep, VISIT value, int level)
1027 if (value != leaf && value != postorder)
1030 name = *(struct name **) nodep;
1031 idx = name->hashval % hash_size;
1032 hval2 = 1 + name->hashval % (hash_size - 2);
1034 while (hash_table[idx].string_offset != 0)
1035 if ((idx += hval2) >= hash_size)
1038 hash_table[idx].string_offset = strtaboffset (name->strent);
1040 assert (name->module_idx != -1);
1041 hash_table[idx].module_idx = name->module_idx;
1049 size_t string_table_size;
1050 struct gconvcache_header header;
1051 struct module_entry *module_table;
1053 char *cur_extra_table;
1056 struct iovec iov[6];
1057 static const gidx_t null_word;
1059 char finalname[prefix_len + sizeof GCONV_MODULES_CACHE];
1060 char tmpfname[(output_file == NULL ? sizeof finalname : output_file_len + 1)
1061 + strlen (".XXXXXX")];
1063 /* Open the output file. */
1064 if (output_file == NULL)
1066 assert (GCONV_MODULES_CACHE[0] == '/');
1067 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len),
1068 GCONV_MODULES_CACHE),
1070 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1073 strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX");
1074 fd = mkstemp (tmpfname);
1078 /* Create the string table. */
1079 string_table = strtabfinalize (strtab, &string_table_size);
1081 /* Create the hashing table. We know how many strings we have.
1082 Creating a perfect hash table is not reasonable here. Therefore
1083 we use open hashing and a table size which is the next prime 40%
1084 larger than the number of strings. */
1085 hash_size = next_prime (nnames * 1.4);
1086 hash_table = (struct hash_entry *) xcalloc (hash_size,
1087 sizeof (struct hash_entry));
1088 /* Fill the hash table. */
1089 twalk (names, name_insert);
1091 /* Create the section for the module list. */
1092 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1095 /* Allocate memory for the non-INTERNAL conversions. The allocated
1096 memory can be more than is actually needed. */
1097 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1099 + sizeof (struct extra_entry_module),
1101 cur_extra_table = extra_table;
1103 /* Fill in the module information. */
1104 for (n = 0; n < nname_info; ++n)
1106 module_table[n].canonname_offset =
1107 strtaboffset (name_info[n].canonical_strent);
1109 if (name_info[n].from_internal == NULL)
1111 module_table[n].fromdir_offset = 0;
1112 module_table[n].fromname_offset = 0;
1116 module_table[n].fromdir_offset =
1117 strtaboffset (name_info[n].from_internal->directory_strent);
1118 module_table[n].fromname_offset =
1119 strtaboffset (name_info[n].from_internal->filename_strent);
1122 if (name_info[n].to_internal == NULL)
1124 module_table[n].todir_offset = 0;
1125 module_table[n].toname_offset = 0;
1129 module_table[n].todir_offset =
1130 strtaboffset (name_info[n].to_internal->directory_strent);
1131 module_table[n].toname_offset =
1132 strtaboffset (name_info[n].to_internal->filename_strent);
1135 if (name_info[n].other_conv_list != NULL)
1137 struct other_conv_list *other = name_info[n].other_conv_list;
1139 /* Store the reference. We add 1 to distinguish the entry
1140 at offset zero from the case where no extra modules are
1141 available. The file reader has to account for the
1143 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1147 struct other_conv *runp;
1148 struct extra_entry *extra;
1150 /* Allocate new entry. */
1151 extra = (struct extra_entry *) cur_extra_table;
1152 cur_extra_table += sizeof (struct extra_entry);
1153 extra->module_cnt = 0;
1155 runp = &other->other_conv;
1158 cur_extra_table += sizeof (struct extra_entry_module);
1159 extra->module[extra->module_cnt].outname_offset =
1161 ? other->dest_idx : runp->next->module_idx;
1162 extra->module[extra->module_cnt].dir_offset =
1163 strtaboffset (runp->module->directory_strent);
1164 extra->module[extra->module_cnt].name_offset =
1165 strtaboffset (runp->module->filename_strent);
1166 ++extra->module_cnt;
1170 while (runp != NULL);
1172 other = other->next;
1174 while (other != NULL);
1176 /* Final module_cnt is zero. */
1177 *((gidx_t *) cur_extra_table) = 0;
1178 cur_extra_table += sizeof (gidx_t);
1182 /* Clear padding. */
1183 memset (&header, 0, sizeof (struct gconvcache_header));
1185 header.magic = GCONVCACHE_MAGIC;
1187 iov[0].iov_base = &header;
1188 iov[0].iov_len = sizeof (struct gconvcache_header);
1189 total = iov[0].iov_len;
1191 header.string_offset = total;
1192 iov[1].iov_base = string_table;
1193 iov[1].iov_len = string_table_size;
1194 total += iov[1].iov_len;
1197 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1199 iov[2].iov_base = (void *) &null_word;
1200 iov[2].iov_len = (sizeof (gidx_t)
1201 - (string_table_size & (sizeof (gidx_t) - 1)));
1202 total += iov[2].iov_len;
1206 header.hash_offset = total;
1207 header.hash_size = hash_size;
1208 iov[idx].iov_base = hash_table;
1209 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1210 total += iov[idx].iov_len;
1213 header.module_offset = total;
1214 iov[idx].iov_base = module_table;
1215 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1216 total += iov[idx].iov_len;
1219 assert ((size_t) (cur_extra_table - extra_table)
1220 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1221 + sizeof (struct extra_entry_module))
1223 header.otherconv_offset = total;
1224 iov[idx].iov_base = extra_table;
1225 iov[idx].iov_len = cur_extra_table - extra_table;
1226 total += iov[idx].iov_len;
1229 if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1230 /* The file was created with mode 0600. Make it world-readable. */
1231 || fchmod (fd, 0644) != 0
1232 /* Rename the file, possibly replacing an old one. */
1233 || rename (tmpfname, output_file ?: finalname) != 0)
1235 int save_errno = errno;
1238 error (EXIT_FAILURE, save_errno,
1239 gettext ("cannot generate output file"));