1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2000.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library 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 GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
32 #include <stdio_ext.h>
36 #include <sys/cdefs.h>
39 #include "iconvconfig.h"
41 /* Get libc version number. */
42 #include "../version.h"
44 #define PACKAGE _libc_intl_domainname
47 /* The hashing function we use. */
48 #include "../intl/hash-string.h"
55 struct Strent *fromname_strent;
57 struct Strent *filename_strent;
58 const char *directory;
59 struct Strent *directory_strent;
62 struct Strent *toname_strent;
69 struct Strent *froment;
70 struct module *module;
78 struct Strent *strent;
85 const char *canonical_name;
86 struct Strent *canonical_strent;
88 struct module *from_internal;
89 struct module *to_internal;
91 struct other_conv_list
97 struct module *module;
98 struct other_conv *next;
100 struct other_conv_list *next;
105 /* Name and version of program. */
106 static void print_version (FILE *stream, struct argp_state *state);
107 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
109 /* Short description of program. */
110 static const char doc[] = N_("\
111 Create fastloading iconv module configuration file.");
113 /* Strings for arguments in help texts. */
114 static const char args_doc[] = N_("[DIR...]");
116 /* Prototype for option handler. */
117 static error_t parse_opt (int key, char *arg, struct argp_state *state);
119 /* Function to print some extra text in the help message. */
120 static char *more_help (int key, const char *text, void *input);
122 /* Definitions of arguments for argp functions. */
123 #define OPT_PREFIX 300
124 static const struct argp_option options[] =
126 { "prefix", OPT_PREFIX, "PATH", 0, N_("Prefix used for all file accesses") },
127 { NULL, 0, NULL, 0, NULL }
130 /* Data structure to communicate with argp functions. */
131 static struct argp argp =
134 options, parse_opt, args_doc, doc, NULL, more_help
138 /* The function doing the actual work. */
139 static int handle_dir (const char *dir);
141 /* Add all known builtin conversions and aliases. */
142 static void add_builtins (void);
144 /* Create list of all aliases without circular aliases. */
145 static void get_aliases (void);
147 /* Create list of all modules. */
148 static void get_modules (void);
150 /* Get list of all the names and thereby indexing them. */
151 static void generate_name_list (void);
153 /* Collect information about all the names. */
154 static void generate_name_info (void);
156 /* Write the output file. */
157 static int write_output (void);
160 /* Prefix to be used for all file accesses. */
161 static const char *prefix = "";
163 static size_t prefix_len;
165 /* Search tree of the modules we know. */
166 static void *modules;
168 /* Search tree of the aliases we know. */
169 static void *aliases;
171 /* Search tree for name to index mapping. */
174 /* Number of names we know about. */
177 /* List of all aliases. */
178 static struct alias **alias_list;
179 static size_t nalias_list;
180 static size_t nalias_list_max;
182 /* List of all modules. */
183 static struct module **module_list;
184 static size_t nmodule_list;
185 static size_t nmodule_list_max;
187 /* Names and information about them. */
188 static struct name_info *name_info;
189 static size_t nname_info;
191 /* Number of translations not from or to INTERNAL. */
192 static size_t nextra_modules;
195 /* Names and aliases for the builtin transformations. */
202 #define BUILTIN_ALIAS(alias, real) \
203 { .from = alias, .to = real },
204 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
205 MinF, MaxF, MinT, MaxT)
206 #include <gconv_builtin.h>
209 #undef BUILTIN_TRANSFORMATION
210 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
220 #define BUILTIN_ALIAS(alias, real)
221 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
222 MinF, MaxF, MinT, MaxT) \
223 { .from = From, .to = To, .module = Name, .cost = Cost },
224 #include <gconv_builtin.h>
227 #undef BUILTIN_TRANSFORMATION
228 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
231 /* Filename extension for the modules. */
233 # define MODULE_EXT ".so"
235 static const char gconv_module_ext[] = MODULE_EXT;
238 extern void *xmalloc (size_t n) __attribute_malloc__;
239 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
240 extern void *xrealloc (void *p, size_t n);
243 /* C string table handling. */
247 /* Create new C string table object in memory. */
248 extern struct Strtab *strtabinit (void);
250 /* Free resources allocated for C string table ST. */
251 extern void strtabfree (struct Strtab *st);
253 /* Add string STR (length LEN is != 0) to C string table ST. */
254 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
257 /* Finalize string table ST and store size in *SIZE and return a pointer. */
258 extern void *strtabfinalize (struct Strtab *st, size_t *size);
260 /* Get offset in string table for string associated with SE. */
261 extern size_t strtaboffset (struct Strent *se);
263 /* String table we construct. */
264 static struct Strtab *strtab;
269 main (int argc, char *argv[])
276 /* Enable memory use testing. */
277 /* mcheck_pedantic (NULL); */
280 /* Set locale via LC_ALL. */
281 setlocale (LC_ALL, "");
283 /* Set the text message domain. */
284 textdomain (_libc_intl_domainname);
286 /* Parse and process arguments. */
287 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
289 /* Initialize the string table. */
290 strtab = strtabinit ();
292 /* Handle all directories mentioned. */
293 while (remaining < argc)
294 status |= handle_dir (argv[remaining++]);
296 /* In any case also handle the standard directory. */
297 path = strdupa (GCONV_PATH);
298 tp = strtok (path, ":");
301 status |= handle_dir (tp);
303 tp = strtok (NULL, ":");
306 /* Add the builtin transformations and aliases without overwriting
310 /* Store aliases in an array. */
313 /* Get list of all modules. */
316 /* Generate list of all the names we know to handle in some way. */
317 generate_name_list ();
319 /* Now we know all the names we will handle, collect information
321 generate_name_info ();
323 /* Write the output file, but only if we haven't seen any error. */
325 status = write_output ();
327 error (1, 0, _("no output file produced because warning were issued"));
333 /* Handle program arguments. */
335 parse_opt (int key, char *arg, struct argp_state *state)
341 prefix_len = strlen (prefix);
344 return ARGP_ERR_UNKNOWN;
351 more_help (int key, const char *text, void *input)
355 case ARGP_KEY_HELP_EXTRA:
356 /* We print some extra information. */
357 return strdup (gettext ("\
358 For bug reporting instructions, please see:\n\
359 <http://www.gnu.org/software/libc/bugs.html>.\n"));
363 return (char *) text;
367 /* Print the version information. */
369 print_version (FILE *stream, struct argp_state *state)
371 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
372 fprintf (stream, gettext ("\
373 Copyright (C) %s Free Software Foundation, Inc.\n\
374 This is free software; see the source for copying conditions. There is NO\n\
375 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
377 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
382 alias_compare (const void *p1, const void *p2)
384 const struct alias *a1 = (const struct alias *) p1;
385 const struct alias *a2 = (const struct alias *) p2;
387 return strcmp (a1->fromname, a2->fromname);
392 new_alias (const char *fromname, size_t fromlen, const char *toname,
398 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
400 newp->fromname = mempcpy (newp->toname, toname, tolen);
401 memcpy (newp->fromname, fromname, fromlen);
404 inserted = (void **) tsearch (newp, &aliases, alias_compare);
405 if (inserted == NULL)
406 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
407 if (*inserted != newp)
408 /* Something went wrong, free this entry. */
412 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
413 newp->toent = strtabadd (strtab, newp->toname, tolen);
422 /* We now expect two more string. The strings are normalized
423 (converted to UPPER case) and strored in the alias database. */
428 while (isspace (*rp))
431 while (*rp != '\0' && !isspace (*rp))
432 *wp++ = toupper (*rp++);
434 /* There is no `to' string on the line. Ignore it. */
438 while (isspace (*rp))
440 while (*rp != '\0' && !isspace (*rp))
441 *wp++ = toupper (*rp++);
443 /* No `to' string, ignore the line. */
447 assert (strlen (from) + 1 == (size_t) (to - from));
448 assert (strlen (to) + 1 == (size_t) (wp - to));
450 new_alias (from, to - from, to, wp - to);
455 append_alias (const void *nodep, VISIT value, int level)
457 if (value != leaf && value != postorder)
460 if (nalias_list_max == nalias_list)
462 nalias_list_max += 50;
463 alias_list = (struct alias **) xrealloc (alias_list,
465 * sizeof (struct alias *)));
468 alias_list[nalias_list++] = *(struct alias **) nodep;
475 twalk (aliases, append_alias);
480 module_compare (const void *p1, const void *p2)
482 const struct module *m1 = (const struct module *) p1;
483 const struct module *m2 = (const struct module *) p2;
486 result = strcmp (m1->fromname, m2->fromname);
488 result = strcmp (m1->toname, m2->toname);
494 /* Create new module record. */
496 new_module (const char *fromname, size_t fromlen, const char *toname,
497 size_t tolen, const char *directory,
498 const char *filename, size_t filelen, int cost, size_t need_ext)
500 struct module *new_module;
501 size_t dirlen = strlen (directory) + 1;
505 new_module = (struct module *) xmalloc (sizeof (struct module)
506 + fromlen + tolen + filelen
509 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
511 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
513 new_module->cost = cost;
514 new_module->next = NULL;
516 tmp = mempcpy (new_module->filename, filename, filelen);
519 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
522 new_module->directory = directory;
524 /* Now insert the new module data structure in our search tree. */
525 inserted = (void **) tsearch (new_module, &modules, module_compare);
526 if (inserted == NULL)
527 error (EXIT_FAILURE, errno, "while inserting in search tree");
528 if (*inserted != new_module)
532 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
534 new_module->toname_strent = strtabadd (strtab, new_module->toname,
536 new_module->filename_strent = strtabadd (strtab, new_module->filename,
538 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
543 /* Add new module. */
546 add_module (char *rp, const char *directory)
551 3. filename of the module
552 4. an optional cost value
561 while (isspace (*rp))
564 while (*rp != '\0' && !isspace (*rp))
573 while (isspace (*rp))
575 while (*rp != '\0' && !isspace (*rp))
576 *wp++ = toupper (*rp++);
582 while (isspace (*rp));
584 while (*rp != '\0' && !isspace (*rp))
588 /* There is no cost, use one by default. */
594 /* There might be a cost value. */
598 cost = strtol (rp, &endp, 10);
599 if (rp == endp || cost < 1)
600 /* No useful information. */
604 if (module[0] == '\0')
605 /* No module name given. */
608 /* See whether we must add the ending. */
610 if ((size_t) (wp - module) < sizeof (gconv_module_ext)
611 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
612 sizeof (gconv_module_ext)) != 0)
613 /* We must add the module extension. */
614 need_ext = sizeof (gconv_module_ext) - 1;
616 assert (strlen (from) + 1 == (size_t) (to - from));
617 assert (strlen (to) + 1 == (size_t) (module - to));
618 assert (strlen (module) + 1 == (size_t) (wp - module));
620 new_module (from, to - from, to, module - to, directory, module, wp - module,
625 /* Read the config file and add the data for this directory to that. */
627 handle_dir (const char *dir)
634 size_t dirlen = strlen (dir);
636 if (dir[dirlen - 1] != '/')
638 char *newp = (char *) xmalloc (dirlen + 2);
639 dir = memcpy (newp, dir, dirlen);
640 newp[dirlen++] = '/';
644 cp = infile = (char *) alloca (prefix_len + dirlen + sizeof "gconv-modules");
646 cp = mempcpy (cp, prefix, prefix_len);
647 strcpy (mempcpy (cp, dir, dirlen), "gconv-modules");
649 fp = fopen (infile, "r");
652 error (0, errno, "cannot open `%s'", infile);
656 /* No threads present. */
657 __fsetlocking (fp, FSETLOCKING_BYCALLER);
659 while (!feof_unlocked (fp))
661 char *rp, *endp, *word;
662 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
665 /* An error occurred. */
669 /* Terminate the line (excluding comments or newline) with a NUL
670 byte to simplify the following code. */
671 endp = strchr (rp, '#');
675 if (rp[n - 1] == '\n')
678 while (isspace (*rp))
681 /* If this is an empty line go on with the next one. */
686 while (*rp != '\0' && !isspace (*rp))
689 if (rp - word == sizeof ("alias") - 1
690 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
692 else if (rp - word == sizeof ("module") - 1
693 && memcmp (word, "module", sizeof ("module") - 1) == 0)
694 add_module (rp, dir);
696 /* Otherwise ignore the line. */
708 append_module (const void *nodep, VISIT value, int level)
712 if (value != leaf && value != postorder)
715 mo = *(struct module **) nodep;
718 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
721 mo->next = module_list[nmodule_list - 1];
722 module_list[nmodule_list - 1] = mo;
727 if (nmodule_list_max == nmodule_list)
729 nmodule_list_max += 50;
730 module_list = (struct module **) xrealloc (module_list,
732 * sizeof (struct module *)));
735 module_list[nmodule_list++] = mo;
742 twalk (modules, append_module);
751 /* Add all aliases. */
752 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
753 new_alias (builtin_alias[cnt].from,
754 strlen (builtin_alias[cnt].from) + 1,
755 builtin_alias[cnt].to,
756 strlen (builtin_alias[cnt].to) + 1);
758 /* add the builtin transformations. */
759 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
760 new_module (builtin_trans[cnt].from,
761 strlen (builtin_trans[cnt].from) + 1,
762 builtin_trans[cnt].to,
763 strlen (builtin_trans[cnt].to) + 1,
764 "", builtin_trans[cnt].module,
765 strlen (builtin_trans[cnt].module) + 1,
766 builtin_trans[cnt].cost, 0);
771 name_compare (const void *p1, const void *p2)
773 const struct name *n1 = (const struct name *) p1;
774 const struct name *n2 = (const struct name *) p2;
776 return strcmp (n1->name, n2->name);
781 new_name (const char *str, struct Strent *strent)
783 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
786 newp->strent = strent;
787 newp->module_idx = -1;
788 newp->hashval = __hash_string (str);
797 generate_name_list (void)
801 /* A name we always need. */
802 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
803 sizeof ("INTERNAL"))),
804 &names, name_compare);
806 for (i = 0; i < nmodule_list; ++i)
810 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
811 tsearch (new_name (module_list[i]->fromname,
812 module_list[i]->fromname_strent),
813 &names, name_compare);
815 for (runp = module_list[i]; runp != NULL; runp = runp->next)
816 if (strcmp (runp->toname, "INTERNAL") != 0)
817 tsearch (new_name (runp->toname, runp->toname_strent),
818 &names, name_compare);
824 name_to_module_idx (const char *name, int add)
827 struct name fake_name = { .name = name };
830 res = (struct name **) tfind (&fake_name, &names, name_compare);
834 idx = (*res)->module_idx;
835 if (idx == -1 && add)
836 /* No module index assigned yet. */
837 idx = (*res)->module_idx = nname_info++;
844 generate_name_info (void)
849 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
850 sizeof (struct name_info));
852 /* First add a special entry for the INTERNAL name. This must have
854 idx = name_to_module_idx ("INTERNAL", 1);
855 name_info[0].canonical_name = "INTERNAL";
856 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
857 sizeof ("INTERNAL"));
858 assert (nname_info == 1);
860 for (i = 0; i < nmodule_list; ++i)
864 for (runp = module_list[i]; runp != NULL; runp = runp->next)
865 if (strcmp (runp->fromname, "INTERNAL") == 0)
867 idx = name_to_module_idx (runp->toname, 1);
868 name_info[idx].from_internal = runp;
869 assert (name_info[idx].canonical_name == NULL
870 || strcmp (name_info[idx].canonical_name,
872 name_info[idx].canonical_name = runp->toname;
873 name_info[idx].canonical_strent = runp->toname_strent;
875 else if (strcmp (runp->toname, "INTERNAL") == 0)
877 idx = name_to_module_idx (runp->fromname, 1);
878 name_info[idx].to_internal = runp;
879 assert (name_info[idx].canonical_name == NULL
880 || strcmp (name_info[idx].canonical_name,
881 runp->fromname) == 0);
882 name_info[idx].canonical_name = runp->fromname;
883 name_info[idx].canonical_strent = runp->fromname_strent;
887 /* This is a transformation not to or from the INTERNAL
889 int from_idx = name_to_module_idx (runp->fromname, 1);
890 int to_idx = name_to_module_idx (runp->toname, 1);
891 struct other_conv_list *newp;
893 newp = (struct other_conv_list *)
894 xmalloc (sizeof (struct other_conv_list));
895 newp->other_conv.module_idx = to_idx;
896 newp->other_conv.module = runp;
897 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
898 newp->dest_idx = to_idx;
899 newp->next = name_info[from_idx].other_conv_list;
900 name_info[from_idx].other_conv_list = newp;
901 assert (name_info[from_idx].canonical_name == NULL
902 || strcmp (name_info[from_idx].canonical_name,
903 runp->fromname) == 0);
904 name_info[from_idx].canonical_name = runp->fromname;
905 name_info[from_idx].canonical_strent = runp->fromname_strent;
911 /* Now add the module index information for all the aliases. */
912 for (i = 0; i < nalias_list; ++i)
914 struct name fake_name = { .name = alias_list[i]->toname };
915 struct name **tonamep;
917 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
920 struct name *newp = new_name (alias_list[i]->fromname,
921 alias_list[i]->froment);
922 newp->module_idx = (*tonamep)->module_idx;
923 tsearch (newp, &names, name_compare);
930 is_prime (unsigned long int candidate)
932 /* No even number and none less than 10 will be passed here. */
933 unsigned long int divn = 3;
934 unsigned long int sq = divn * divn;
936 while (sq < candidate && candidate % divn != 0)
943 return candidate % divn != 0;
948 next_prime (uint32_t seed)
950 /* Make it definitely odd. */
953 while (!is_prime (seed))
960 /* Format of the output file.
962 Offset Length Description
963 0000 4 Magic header bytes
964 0004 4 Offset of string table (stoff)
965 0008 4 Offset of name hashing table (hoff)
966 000C 4 Hashing table size (hsize)
967 0010 4 Offset of module table (moff)
968 0014 4 Offset of other conversion module table (ooff)
970 stoff ??? String table
972 hoff 8*hsize Array of tuples
976 moff ??? Array of tuples
977 canonical name offset
978 from-internal module dir name offset
979 from-internal module name off
980 to-internal module dir name offset
981 to-internal module name offset
982 offset into other conversion table
984 ooff ??? One or more of
985 number of steps/modules
987 canonical name offset for output
988 module dir name offset
990 (following last entry with step count 0)
997 size_t string_table_size;
998 struct gconvcache_header header;
999 struct hash_entry *hash_table;
1001 struct module_entry *module_table;
1003 char *cur_extra_table;
1006 struct iovec iov[6];
1007 static const gidx_t null_word;
1009 char tmpfname[prefix_len + sizeof (GCONV_MODULES_CACHE)
1010 + strlen (".XXXXXX")];
1011 char finalname[prefix_len + sizeof (GCONV_MODULES_CACHE)];
1013 /* Function to insert the names. */
1014 static void name_insert (const void *nodep, VISIT value, int level)
1020 if (value != leaf && value != postorder)
1023 name = *(struct name **) nodep;
1024 idx = name->hashval % hash_size;
1025 hval2 = 1 + name->hashval % (hash_size - 2);
1027 while (hash_table[idx].string_offset != 0)
1028 if ((idx += hval2) >= hash_size)
1031 hash_table[idx].string_offset = strtaboffset (name->strent);
1033 assert (name->module_idx != -1);
1034 hash_table[idx].module_idx = name->module_idx;
1037 /* Open the output file. */
1038 assert (GCONV_MODULES_CACHE[0] == '/');
1039 strcpy (stpcpy (mempcpy (tmpfname, prefix, prefix_len), GCONV_MODULES_CACHE),
1041 fd = mkstemp (tmpfname);
1045 strcpy (mempcpy (finalname, prefix, prefix_len), GCONV_MODULES_CACHE);
1047 /* Create the string table. */
1048 string_table = strtabfinalize (strtab, &string_table_size);
1050 /* Create the hashing table. We know how many strings we have.
1051 Creating a perfect hash table is not reasonable here. Therefore
1052 we use open hashing and a table size which is the next prime 40%
1053 larger than the number of strings. */
1054 hash_size = next_prime (nnames * 1.4);
1055 hash_table = (struct hash_entry *) xcalloc (hash_size,
1056 sizeof (struct hash_entry));
1057 /* Fill the hash table. */
1058 twalk (names, name_insert);
1060 /* Create the section for the module list. */
1061 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1064 /* Allocate memory for the non-INTERNAL conversions. The allocated
1065 memory can be more than is actually needed. */
1066 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1068 + sizeof (struct extra_entry_module),
1070 cur_extra_table = extra_table;
1072 /* Fill in the module information. */
1073 for (n = 0; n < nname_info; ++n)
1075 module_table[n].canonname_offset =
1076 strtaboffset (name_info[n].canonical_strent);
1078 if (name_info[n].from_internal == NULL)
1080 module_table[n].fromdir_offset = 0;
1081 module_table[n].fromname_offset = 0;
1085 module_table[n].fromdir_offset =
1086 strtaboffset (name_info[n].from_internal->directory_strent);
1087 module_table[n].fromname_offset =
1088 strtaboffset (name_info[n].from_internal->filename_strent);
1091 if (name_info[n].to_internal == NULL)
1093 module_table[n].todir_offset = 0;
1094 module_table[n].toname_offset = 0;
1098 module_table[n].todir_offset =
1099 strtaboffset (name_info[n].to_internal->directory_strent);
1100 module_table[n].toname_offset =
1101 strtaboffset (name_info[n].to_internal->filename_strent);
1104 if (name_info[n].other_conv_list != NULL)
1106 struct other_conv_list *other = name_info[n].other_conv_list;
1108 /* Store the reference. We add 1 to distinguish the entry
1109 at offset zero from the case where no extra modules are
1110 available. The file reader has to account for the
1112 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1116 struct other_conv *runp;
1117 struct extra_entry *extra;
1119 /* Allocate new entry. */
1120 extra = (struct extra_entry *) cur_extra_table;
1121 cur_extra_table += sizeof (struct extra_entry);
1122 extra->module_cnt = 0;
1124 runp = &other->other_conv;
1127 cur_extra_table += sizeof (struct extra_entry_module);
1128 extra->module[extra->module_cnt].outname_offset =
1130 ? other->dest_idx : runp->next->module_idx;
1131 extra->module[extra->module_cnt].dir_offset =
1132 strtaboffset (runp->module->directory_strent);
1133 extra->module[extra->module_cnt].name_offset =
1134 strtaboffset (runp->module->filename_strent);
1135 ++extra->module_cnt;
1139 while (runp != NULL);
1141 other = other->next;
1143 while (other != NULL);
1145 /* Final module_cnt is zero. */
1146 *((gidx_t *) cur_extra_table) = 0;
1147 cur_extra_table += sizeof (gidx_t);
1151 header.magic = GCONVCACHE_MAGIC;
1153 iov[0].iov_base = &header;
1154 iov[0].iov_len = sizeof (struct gconvcache_header);
1155 total = iov[0].iov_len;
1157 header.string_offset = total;
1158 iov[1].iov_base = string_table;
1159 iov[1].iov_len = string_table_size;
1160 total += iov[1].iov_len;
1163 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1165 iov[2].iov_base = (void *) &null_word;
1166 iov[2].iov_len = (sizeof (gidx_t)
1167 - (string_table_size & (sizeof (gidx_t) - 1)));
1168 total += iov[2].iov_len;
1172 header.hash_offset = total;
1173 header.hash_size = hash_size;
1174 iov[idx].iov_base = hash_table;
1175 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1176 total += iov[idx].iov_len;
1179 header.module_offset = total;
1180 iov[idx].iov_base = module_table;
1181 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1182 total += iov[idx].iov_len;
1185 assert ((size_t) (cur_extra_table - extra_table)
1186 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1187 + sizeof (struct extra_entry_module))
1189 header.otherconv_offset = total;
1190 iov[idx].iov_base = extra_table;
1191 iov[idx].iov_len = cur_extra_table - extra_table;
1192 total += iov[idx].iov_len;
1195 if ((size_t) TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1196 /* The file was created with mode 0600. Make it world-readable. */
1197 || fchmod (fd, 0644) != 0
1198 /* Rename the file, possibly replacing an old one. */
1199 || rename (tmpfname, finalname) != 0)
1201 int save_errno = errno;
1204 error (EXIT_FAILURE, save_errno,
1205 gettext ("cannot generate output file"));