1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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, see <https://www.gnu.org/licenses/>. */
30 #include <stdio_ext.h>
34 #include <sys/cdefs.h>
37 #include "iconvconfig.h"
38 #include <gconv_parseconfdir.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>
252 #include <programs/xasprintf.h>
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 warnings 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)
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)
388 return (char *) text;
392 /* Print the version information. */
394 print_version (FILE *stream, struct argp_state *state)
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\
402 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
407 alias_compare (const void *p1, const void *p2)
409 const struct alias *a1 = (const struct alias *) p1;
410 const struct alias *a2 = (const struct alias *) p2;
412 return strcmp (a1->fromname, a2->fromname);
417 new_alias (const char *fromname, size_t fromlen, const char *toname,
423 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
425 newp->fromname = mempcpy (newp->toname, toname, tolen);
426 memcpy (newp->fromname, fromname, fromlen);
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. */
437 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
438 newp->toent = strtabadd (strtab, newp->toname, tolen);
447 /* We now expect two more string. The strings are normalized
448 (converted to UPPER case) and stored in the alias database. */
453 while (isspace (*rp))
456 while (*rp != '\0' && !isspace (*rp))
457 *wp++ = toupper (*rp++);
459 /* There is no `to' string on the line. Ignore it. */
463 while (isspace (*rp))
465 while (*rp != '\0' && !isspace (*rp))
466 *wp++ = toupper (*rp++);
468 /* No `to' string, ignore the line. */
472 assert (strlen (from) + 1 == (size_t) (to - from));
473 assert (strlen (to) + 1 == (size_t) (wp - to));
475 new_alias (from, to - from, to, wp - to);
480 append_alias (const void *nodep, VISIT value, int level)
482 if (value != leaf && value != postorder)
485 if (nalias_list_max == nalias_list)
487 nalias_list_max += 50;
488 alias_list = (struct alias **) xrealloc (alias_list,
490 * sizeof (struct alias *)));
493 alias_list[nalias_list++] = *(struct alias **) nodep;
500 twalk (aliases, append_alias);
505 module_compare (const void *p1, const void *p2)
507 const struct module *m1 = (const struct module *) p1;
508 const struct module *m2 = (const struct module *) p2;
511 result = strcmp (m1->fromname, m2->fromname);
513 result = strcmp (m1->toname, m2->toname);
519 /* Create new module record. */
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)
525 struct module *new_module;
526 size_t dirlen = strlen (dir_in) + 1;
527 const char *directory = xstrdup (dir_in);
531 new_module = (struct module *) xmalloc (sizeof (struct module)
532 + fromlen + tolen + filelen
535 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
537 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
539 new_module->cost = cost;
540 new_module->next = NULL;
542 tmp = mempcpy (new_module->filename, filename, filelen);
545 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
548 new_module->directory = directory;
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)
558 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
560 new_module->toname_strent = strtabadd (strtab, new_module->toname,
562 new_module->filename_strent = strtabadd (strtab, new_module->filename,
564 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
569 /* Add new module. */
571 add_module (char *rp, const char *directory,
572 size_t dirlen __attribute__ ((__unused__)),
573 int modcount __attribute__ ((__unused__)))
578 3. filename of the module
579 4. an optional cost value
588 while (isspace (*rp))
591 while (*rp != '\0' && !isspace (*rp))
600 while (isspace (*rp))
602 while (*rp != '\0' && !isspace (*rp))
603 *wp++ = toupper (*rp++);
609 while (isspace (*rp));
611 while (*rp != '\0' && !isspace (*rp))
615 /* There is no cost, use one by default. */
621 /* There might be a cost value. */
625 cost = strtol (rp, &endp, 10);
626 if (rp == endp || cost < 1)
627 /* No useful information. */
631 if (module[0] == '\0')
632 /* No module name given. */
635 /* See whether we must add the ending. */
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;
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));
647 new_module (from, to - from, to, module - to, directory, module, wp - module,
651 /* Read config files and add the data for this directory to cache. */
653 handle_dir (const char *dir)
656 size_t dirlen = strlen (dir);
659 /* End directory path with a '/' if it doesn't already. */
660 if (dir[dirlen - 1] != '/')
662 newp = xmalloc (dirlen + 2);
663 memcpy (newp, dir, dirlen);
664 newp[dirlen++] = '/';
669 found = gconv_parseconfdir (dir[0] == '/' ? prefix : NULL, dir, dirlen);
673 error (0, errno, "failed to open gconv configuration files in `%s'",
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.");
683 return found ? 0 : 1;
688 append_module (const void *nodep, VISIT value, int level)
692 if (value != leaf && value != postorder)
695 mo = *(struct module **) nodep;
698 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
701 mo->next = module_list[nmodule_list - 1];
702 module_list[nmodule_list - 1] = mo;
707 if (nmodule_list_max == nmodule_list)
709 nmodule_list_max += 50;
710 module_list = (struct module **) xrealloc (module_list,
712 * sizeof (struct module *)));
715 module_list[nmodule_list++] = mo;
722 twalk (modules, append_module);
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);
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);
751 name_compare (const void *p1, const void *p2)
753 const struct name *n1 = (const struct name *) p1;
754 const struct name *n2 = (const struct name *) p2;
756 return strcmp (n1->name, n2->name);
761 new_name (const char *str, struct Strent *strent)
763 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
766 newp->strent = strent;
767 newp->module_idx = -1;
768 newp->hashval = __hash_string (str);
777 generate_name_list (void)
781 /* A name we always need. */
782 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
783 sizeof ("INTERNAL"))),
784 &names, name_compare);
786 for (i = 0; i < nmodule_list; ++i)
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);
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);
804 name_to_module_idx (const char *name, int add)
807 struct name fake_name = { .name = name };
810 res = (struct name **) tfind (&fake_name, &names, name_compare);
814 idx = (*res)->module_idx;
815 if (idx == -1 && add)
816 /* No module index assigned yet. */
817 idx = (*res)->module_idx = nname_info++;
824 generate_name_info (void)
829 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
830 sizeof (struct name_info));
832 /* First add a special entry for the INTERNAL name. This must have
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);
840 for (i = 0; i < nmodule_list; ++i)
844 for (runp = module_list[i]; runp != NULL; runp = runp->next)
845 if (strcmp (runp->fromname, "INTERNAL") == 0)
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,
852 name_info[idx].canonical_name = runp->toname;
853 name_info[idx].canonical_strent = runp->toname_strent;
855 else if (strcmp (runp->toname, "INTERNAL") == 0)
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;
867 /* This is a transformation not to or from the INTERNAL
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;
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;
891 /* Now add the module index information for all the aliases. */
892 for (i = 0; i < nalias_list; ++i)
894 struct name fake_name = { .name = alias_list[i]->toname };
895 struct name **tonamep;
897 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
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);
910 is_prime (unsigned long int candidate)
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;
916 while (sq < candidate && candidate % divn != 0)
923 return candidate % divn != 0;
928 next_prime (uint32_t seed)
930 /* Make it definitely odd. */
933 while (!is_prime (seed))
940 /* Format of the output file.
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)
950 stoff ??? String table
952 hoff 8*hsize Array of tuples
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
964 ooff ??? One or more of
965 number of steps/modules
967 canonical name offset for output
968 module dir name offset
970 (following last entry with step count 0)
973 static struct hash_entry *hash_table;
974 static size_t hash_size;
976 /* Function to insert the names. */
977 static void name_insert (const void *nodep, VISIT value, int level)
983 if (value != leaf && value != postorder)
986 name = *(struct name **) nodep;
987 idx = name->hashval % hash_size;
988 hval2 = 1 + name->hashval % (hash_size - 2);
990 while (hash_table[idx].string_offset != 0)
991 if ((idx += hval2) >= hash_size)
994 hash_table[idx].string_offset = strtaboffset (name->strent);
996 assert (name->module_idx != -1);
997 hash_table[idx].module_idx = name->module_idx;
1005 size_t string_table_size;
1006 struct gconvcache_header header;
1007 struct module_entry *module_table;
1009 char *cur_extra_table;
1012 struct iovec iov[6];
1013 static const gidx_t null_word;
1015 char finalname[prefix_len + sizeof GCONV_MODULES_CACHE];
1016 char tmpfname[(output_file == NULL ? sizeof finalname : output_file_len + 1)
1017 + strlen (".XXXXXX")];
1019 /* Open the output file. */
1020 if (output_file == NULL)
1022 assert (GCONV_MODULES_CACHE[0] == '/');
1023 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len),
1024 GCONV_MODULES_CACHE),
1026 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1029 strcpy (mempcpy (tmpfname, output_file, output_file_len), ".XXXXXX");
1030 fd = mkstemp (tmpfname);
1034 /* Create the string table. */
1035 string_table = strtabfinalize (strtab, &string_table_size);
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);
1047 /* Create the section for the module list. */
1048 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
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)
1055 + sizeof (struct extra_entry_module),
1057 cur_extra_table = extra_table;
1059 /* Fill in the module information. */
1060 for (n = 0; n < nname_info; ++n)
1062 module_table[n].canonname_offset =
1063 strtaboffset (name_info[n].canonical_strent);
1065 if (name_info[n].from_internal == NULL)
1067 module_table[n].fromdir_offset = 0;
1068 module_table[n].fromname_offset = 0;
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);
1078 if (name_info[n].to_internal == NULL)
1080 module_table[n].todir_offset = 0;
1081 module_table[n].toname_offset = 0;
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);
1091 if (name_info[n].other_conv_list != NULL)
1093 struct other_conv_list *other = name_info[n].other_conv_list;
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
1099 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1103 struct other_conv *runp;
1104 struct extra_entry *extra;
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;
1111 runp = &other->other_conv;
1114 cur_extra_table += sizeof (struct extra_entry_module);
1115 extra->module[extra->module_cnt].outname_offset =
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;
1126 while (runp != NULL);
1128 other = other->next;
1130 while (other != NULL);
1132 /* Final module_cnt is zero. */
1133 *((gidx_t *) cur_extra_table) = 0;
1134 cur_extra_table += sizeof (gidx_t);
1138 /* Clear padding. */
1139 memset (&header, 0, sizeof (struct gconvcache_header));
1141 header.magic = GCONVCACHE_MAGIC;
1143 iov[0].iov_base = &header;
1144 iov[0].iov_len = sizeof (struct gconvcache_header);
1145 total = iov[0].iov_len;
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;
1153 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
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;
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;
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;
1175 assert ((size_t) (cur_extra_table - extra_table)
1176 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1177 + sizeof (struct extra_entry_module))
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;
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)
1191 int save_errno = errno;
1194 error (EXIT_FAILURE, save_errno,
1195 gettext ("cannot generate output file"));