1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001 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 Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 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 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
32 #include <stdio_ext.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 #define OPT_VERBOSE 1000
110 /* Definitions of arguments for argp functions. */
111 static const struct argp_option options[] =
113 { "verbose", OPT_VERBOSE, NULL, 0, N_("print progress information") },
114 { NULL, 0, NULL, 0, NULL }
117 /* Short description of program. */
118 static const char doc[] = N_("\
119 Create fastloading iconv module configuration file.");
121 /* Strings for arguments in help texts. */
122 static const char args_doc[] = N_("[DIR...]");
124 /* Prototype for option handler. */
125 static error_t parse_opt (int key, char *arg, struct argp_state *state);
127 /* Function to print some extra text in the help message. */
128 static char *more_help (int key, const char *text, void *input);
130 /* Data structure to communicate with argp functions. */
131 static struct argp argp =
133 options, parse_opt, args_doc, doc, NULL, more_help
137 /* The function doing the actual work. */
138 static int handle_dir (const char *dir);
140 /* Add all known builtin conversions and aliases. */
141 static void add_builtins (void);
143 /* Create list of all aliases without circular aliases. */
144 static void get_aliases (void);
146 /* Create list of all modules. */
147 static void get_modules (void);
149 /* Get list of all the names and thereby indexing them. */
150 static void generate_name_list (void);
152 /* Collect information about all the names. */
153 static void generate_name_info (void);
155 /* Write the output file. */
156 static int write_output (void);
159 /* Nonzero if verbose ouput is wanted. */
162 /* Search tree of the modules we know. */
163 static void *modules;
165 /* Search tree of the aliases we know. */
166 static void *aliases;
168 /* Search tree for name to index mapping. */
171 /* Number of names we know about. */
174 /* List of all aliases. */
175 static struct alias **alias_list;
176 static size_t nalias_list;
177 static size_t nalias_list_max;
179 /* List of all modules. */
180 static struct module **module_list;
181 static size_t nmodule_list;
182 static size_t nmodule_list_max;
184 /* Names and information about them. */
185 static struct name_info *name_info;
186 static size_t nname_info;
188 /* Number of translations not from or to INTERNAL. */
189 static size_t nextra_modules;
192 /* Names and aliases for the builtin transformations. */
199 #define BUILTIN_ALIAS(alias, real) \
200 { .from = alias, .to = real },
201 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
203 #include <gconv_builtin.h>
206 #undef BUILTIN_TRANSFORMATION
207 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
217 #define BUILTIN_ALIAS(alias, real)
218 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
220 { .from = From, .to = To, .module = Name, .cost = Cost },
221 #include <gconv_builtin.h>
223 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
226 /* Filename extension for the modules. */
228 # define MODULE_EXT ".so"
230 static const char gconv_module_ext[] = MODULE_EXT;
233 extern void *xmalloc (size_t n) __attribute__ ((__malloc__));
234 extern void *xcalloc (size_t n, size_t m) __attribute__ ((__malloc__));
235 extern void *xrealloc (void *p, size_t n);
238 /* C string table handling. */
242 /* Create new C string table object in memory. */
243 extern struct Strtab *strtabinit (void);
245 /* Free resources allocated for C string table ST. */
246 extern void strtabfree (struct Strtab *st);
248 /* Add string STR (length LEN is != 0) to C string table ST. */
249 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
252 /* Finalize string table ST and store size in *SIZE and return a pointer. */
253 extern void *strtabfinalize (struct Strtab *st, size_t *size);
255 /* Get offset in string table for string associated with SE. */
256 extern size_t strtaboffset (struct Strent *se);
258 /* String table we construct. */
259 static struct Strtab *strtab;
264 main (int argc, char *argv[])
271 /* Enable memory use testing. */
272 mcheck_pedantic (NULL);
274 /* Set locale via LC_ALL. */
275 setlocale (LC_ALL, "");
277 /* Set the text message domain. */
278 textdomain (_libc_intl_domainname);
280 /* Parse and process arguments. */
281 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
283 /* Initialize the string table. */
284 strtab = strtabinit ();
286 /* Handle all directories mentioned. */
287 while (remaining < argc)
288 status |= handle_dir (argv[remaining++]);
290 /* In any case also handle the standard directory. */
291 path = strdupa (GCONV_PATH);
292 tp = strtok (path, ":");
295 status |= handle_dir (tp);
297 tp = strtok (NULL, ":");
300 /* Add the builtin transformations and aliases without overwriting
304 /* Store aliases in an array. */
307 /* Get list of all modules. */
310 /* Generate list of all the names we know to handle in some way. */
311 generate_name_list ();
313 /* Now we know all the names we will handle, collect information
315 generate_name_info ();
317 /* Write the output file. */
318 status = write_output ();
324 /* Handle program arguments. */
326 parse_opt (int key, char *arg, struct argp_state *state)
334 return ARGP_ERR_UNKNOWN;
341 more_help (int key, const char *text, void *input)
345 case ARGP_KEY_HELP_EXTRA:
346 /* We print some extra information. */
347 return strdup (gettext ("\
348 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
352 return (char *) text;
356 /* Print the version information. */
358 print_version (FILE *stream, struct argp_state *state)
360 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
361 fprintf (stream, gettext ("\
362 Copyright (C) %s Free Software Foundation, Inc.\n\
363 This is free software; see the source for copying conditions. There is NO\n\
364 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
366 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
371 alias_compare (const void *p1, const void *p2)
373 const struct alias *a1 = (const struct alias *) p1;
374 const struct alias *a2 = (const struct alias *) p2;
376 return strcmp (a1->fromname, a2->fromname);
381 new_alias (const char *fromname, size_t fromlen, const char *toname,
387 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
389 newp->fromname = mempcpy (newp->toname, toname, tolen);
390 memcpy (newp->fromname, fromname, fromlen);
393 inserted = (void **) tsearch (newp, &aliases, alias_compare);
394 if (inserted == NULL)
395 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
396 if (*inserted != newp)
397 /* Something went wrong, free this entry. */
401 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
402 newp->toent = strtabadd (strtab, newp->toname, tolen);
411 /* We now expect two more string. The strings are normalized
412 (converted to UPPER case) and strored in the alias database. */
417 while (isspace (*rp))
420 while (*rp != '\0' && !isspace (*rp))
421 *wp++ = toupper (*rp++);
423 /* There is no `to' string on the line. Ignore it. */
427 while (isspace (*rp))
429 while (*rp != '\0' && !isspace (*rp))
430 *wp++ = toupper (*rp++);
432 /* No `to' string, ignore the line. */
436 new_alias (from, to - from, to, wp - to);
441 append_alias (const void *nodep, VISIT value, int level)
443 if (value != leaf && value != postorder)
446 if (nalias_list_max == nalias_list)
448 nalias_list_max += 50;
449 alias_list = (struct alias **) xrealloc (alias_list,
451 * sizeof (struct alias *)));
454 alias_list[nalias_list++] = *(struct alias **) nodep;
461 twalk (aliases, append_alias);
466 module_compare (const void *p1, const void *p2)
468 const struct module *m1 = (const struct module *) p1;
469 const struct module *m2 = (const struct module *) p2;
472 result = strcmp (m1->fromname, m2->fromname);
474 result = strcmp (m1->toname, m2->toname);
480 /* Create new module record. */
482 new_module (const char *fromname, size_t fromlen, const char *toname,
483 size_t tolen, const char *directory,
484 const char *filename, size_t filelen, int cost, size_t need_ext)
486 struct module *new_module;
487 size_t dirlen = strlen (directory) + 1;
491 new_module = (struct module *) xmalloc (sizeof (struct module)
492 + fromlen + tolen + filelen
495 new_module->fromname = mempcpy (new_module->toname, toname, tolen);
497 new_module->filename = mempcpy (new_module->fromname, fromname, fromlen);
499 new_module->cost = cost;
500 new_module->next = NULL;
502 tmp = mempcpy (new_module->filename, filename, filelen);
505 memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
506 filelen += sizeof (gconv_module_ext) - 1;
508 new_module->directory = directory;
510 /* Now insert the new module data structure in our search tree. */
511 inserted = (void **) tsearch (new_module, &modules, module_compare);
512 if (inserted == NULL)
513 error (EXIT_FAILURE, errno, "while inserting in search tree");
514 if (*inserted != new_module)
518 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
520 new_module->toname_strent = strtabadd (strtab, new_module->toname,
522 new_module->filename_strent = strtabadd (strtab, new_module->filename,
524 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
529 /* Add new module. */
532 add_module (char *rp, const char *directory)
537 3. filename of the module
538 4. an optional cost value
547 while (isspace (*rp))
550 while (*rp != '\0' && !isspace (*rp))
559 while (isspace (*rp))
561 while (*rp != '\0' && !isspace (*rp))
562 *wp++ = toupper (*rp++);
568 while (isspace (*rp));
570 while (*rp != '\0' && !isspace (*rp))
574 /* There is no cost, use one by default. */
580 /* There might be a cost value. */
584 cost = strtol (rp, &endp, 10);
585 if (rp == endp || cost < 1)
586 /* No useful information. */
590 if (module[0] == '\0')
591 /* No module name given. */
594 /* See whether we must add the ending. */
596 if (wp - module < sizeof (gconv_module_ext)
597 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
598 sizeof (gconv_module_ext)) != 0)
599 /* We must add the module extension. */
600 need_ext = sizeof (gconv_module_ext) - 1;
602 assert (strlen (from) + 1 == to - from);
603 assert (strlen (to) + 1 == module - to);
604 assert (strlen (module) + 1 == wp - module);
606 new_module (from, to - from, to, module - to, directory, module, wp - module,
611 /* Read the config file and add the data for this directory to that. */
613 handle_dir (const char *dir)
619 size_t dirlen = strlen (dir);
621 if (dir[dirlen - 1] != '/')
623 char *newp = (char *) xmalloc (dirlen + 2);
624 dir = memcpy (newp, dir, dirlen);
625 newp[dirlen++] = '/';
629 infile = (char *) alloca (dirlen + sizeof "gconv-modules");
630 strcpy (mempcpy (infile, dir, dirlen), "gconv-modules");
632 fp = fopen (infile, "r");
635 error (0, errno, "cannot open `%s'", infile);
639 /* No threads present. */
640 __fsetlocking (fp, FSETLOCKING_BYCALLER);
642 while (!feof_unlocked (fp))
644 char *rp, *endp, *word;
645 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
648 /* An error occurred. */
652 /* Terminate the line (excluding comments or newline) with a NUL
653 byte to simplify the following code. */
654 endp = strchr (rp, '#');
658 if (rp[n - 1] == '\n')
661 while (isspace (*rp))
664 /* If this is an empty line go on with the next one. */
669 while (*rp != '\0' && !isspace (*rp))
672 if (rp - word == sizeof ("alias") - 1
673 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
675 else if (rp - word == sizeof ("module") - 1
676 && memcmp (word, "module", sizeof ("module") - 1) == 0)
677 add_module (rp, dir);
679 /* Otherwise ignore the line. */
691 append_module (const void *nodep, VISIT value, int level)
695 if (value != leaf && value != postorder)
698 mo = *(struct module **) nodep;
701 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
704 mo->next = module_list[nmodule_list - 1];
705 module_list[nmodule_list - 1] = mo;
710 if (nmodule_list_max == nmodule_list)
712 nmodule_list_max += 50;
713 module_list = (struct module **) xrealloc (module_list,
715 * sizeof (struct module *)));
718 module_list[nmodule_list++] = mo;
725 twalk (modules, append_module);
734 /* Add all aliases. */
735 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
736 new_alias (builtin_alias[cnt].from,
737 strlen (builtin_alias[cnt].from) + 1,
738 builtin_alias[cnt].to,
739 strlen (builtin_alias[cnt].to) + 1);
741 /* add the builtin transformations. */
742 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
743 new_module (builtin_trans[cnt].from,
744 strlen (builtin_trans[cnt].from) + 1,
745 builtin_trans[cnt].to,
746 strlen (builtin_trans[cnt].to) + 1,
747 "", builtin_trans[cnt].module,
748 strlen (builtin_trans[cnt].module) + 1,
749 builtin_trans[cnt].cost, 0);
754 name_compare (const void *p1, const void *p2)
756 const struct name *n1 = (const struct name *) p1;
757 const struct name *n2 = (const struct name *) p2;
759 return strcmp (n1->name, n2->name);
764 new_name (const char *str, struct Strent *strent)
766 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
769 newp->strent = strent;
770 newp->module_idx = -1;
771 newp->hashval = hash_string (str);
780 generate_name_list (void)
784 for (i = 0; i < nmodule_list; ++i)
788 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
789 tsearch (new_name (module_list[i]->fromname,
790 module_list[i]->fromname_strent),
791 &names, name_compare);
793 for (runp = module_list[i]; runp != NULL; runp = runp->next)
794 if (strcmp (runp->toname, "INTERNAL") != 0)
795 tsearch (new_name (runp->toname, runp->toname_strent),
796 &names, name_compare);
802 name_to_module_idx (const char *name, int add)
805 struct name fake_name = { .name = name };
808 res = (struct name **) tfind (&fake_name, &names, name_compare);
812 idx = (*res)->module_idx;
813 if (idx == -1 && add)
814 /* No module index assigned yet. */
815 idx = (*res)->module_idx = nname_info++;
822 generate_name_info (void)
826 name_info = (struct name_info *) xcalloc (nmodule_list,
827 sizeof (struct name_info));
829 for (i = 0; i < nmodule_list; ++i)
833 for (runp = module_list[i]; runp != NULL; runp = runp->next)
834 if (strcmp (runp->fromname, "INTERNAL") == 0)
836 int idx = name_to_module_idx (runp->toname, 1);
837 name_info[idx].from_internal = runp;
838 assert (name_info[idx].canonical_name == NULL
839 || strcmp (name_info[idx].canonical_name,
841 name_info[idx].canonical_name = runp->toname;
842 name_info[idx].canonical_strent = runp->toname_strent;
844 else if (strcmp (runp->toname, "INTERNAL") == 0)
846 int idx = name_to_module_idx (runp->fromname, 1);
847 name_info[idx].to_internal = runp;
848 assert (name_info[idx].canonical_name == NULL
849 || strcmp (name_info[idx].canonical_name,
850 runp->fromname) == 0);
851 name_info[idx].canonical_name = runp->fromname;
852 name_info[idx].canonical_strent = runp->fromname_strent;
856 /* This is a transformation not to or from the INTERNAL
858 int from_idx = name_to_module_idx (runp->fromname, 1);
859 int to_idx = name_to_module_idx (runp->toname, 1);
860 struct other_conv_list *newp;
862 newp = (struct other_conv_list *)
863 xmalloc (sizeof (struct other_conv_list));
864 newp->other_conv.module_idx = to_idx;
865 newp->other_conv.module = runp;
866 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
867 newp->dest_idx = to_idx;
868 newp->next = name_info[from_idx].other_conv_list;
869 name_info[from_idx].other_conv_list = newp;
870 assert (name_info[from_idx].canonical_name == NULL
871 || strcmp (name_info[from_idx].canonical_name,
872 runp->fromname) == 0);
873 name_info[from_idx].canonical_name = runp->fromname;
874 name_info[from_idx].canonical_strent = runp->fromname_strent;
880 /* Now add the module index information for all the aliases. */
881 for (i = 0; i < nalias_list; ++i)
883 struct name fake_name = { .name = alias_list[i]->toname };
884 struct name **tonamep;
886 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
889 struct name *newp = new_name (alias_list[i]->fromname,
890 alias_list[i]->froment);
891 newp->module_idx = (*tonamep)->module_idx;
892 tsearch (newp, &names, name_compare);
899 is_prime (unsigned long int candidate)
901 /* No even number and none less than 10 will be passed here. */
902 unsigned long int divn = 3;
903 unsigned long int sq = divn * divn;
905 while (sq < candidate && candidate % divn != 0)
912 return candidate % divn != 0;
917 next_prime (uint32_t seed)
919 /* Make it definitely odd. */
922 while (!is_prime (seed))
929 /* Format of the output file.
931 Offset Length Description
932 0000 4 Magic header bytes
933 0004 4 Offset of string table (stoff)
934 0008 4 Offset of name hashing table (hoff)
935 000C 4 Hashing table size (hsize)
936 0010 4 Offset of module table (moff)
937 0014 4 Offset of other conversion module table (ooff)
939 stoff ??? String table
941 hoff 8*hsize Array of tuples
945 moff ??? Array of tuples
946 canonical name offset
947 from-internal module dir name offset
948 from-internal module name off
949 to-internal module dir name offset
950 to-internal module name offset
951 offset into other conversion table
953 ooff ??? Sequence of words
956 canonical name offset
957 module dir name offset
959 (following last entry canocical name offset is 0)
966 size_t string_table_size;
967 struct gconvcache_header header;
968 struct hash_entry *hash_table;
970 struct module_entry *module_table;
972 char *cur_extra_table;
976 static const gidx_t null_word;
979 /* Function to insert the names. */
980 void name_insert (const void *nodep, VISIT value, int level)
986 if (value != leaf && value != postorder)
989 name = *(struct name **) nodep;
990 idx = name->hashval % hash_size;
991 hval2 = 1 + name->hashval % (hash_size - 2);
993 while (hash_table[idx].string_offset != 0)
994 if ((idx += hval2) >= hash_size)
997 hash_table[idx].string_offset = strtaboffset (name->strent);
999 assert (name->module_idx != -1);
1000 hash_table[idx].module_idx = name->module_idx;
1003 /* Open the output file. */
1004 fd = open (GCONV_MODULES_CACHE, O_TRUNC | O_CREAT | O_RDWR, 0644);
1008 /* Create the string table. */
1009 string_table = strtabfinalize (strtab, &string_table_size);
1011 /* Create the hashing table. We know how many strings we have.
1012 Creating a perfect hash table is not reasonable here. Therefore
1013 we use open hashing and a table size which is the next prime 40%
1014 larger than the number of strings. */
1015 hash_size = next_prime (nnames * 1.4);
1016 hash_table = (struct hash_entry *) xcalloc (hash_size,
1017 sizeof (struct hash_entry));
1018 /* Fill the hash table. */
1019 twalk (names, name_insert);
1021 /* Create the section for the module list. */
1022 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1025 /* Allocate memory for the non-INTERNAL conversions. The allocated
1026 memory can be more than is actually needed. */
1027 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1029 + sizeof (struct extra_entry_module),
1031 cur_extra_table = extra_table;
1033 /* Fill in the module information. */
1034 for (n = 0; n < nname_info; ++n)
1036 module_table[n].canonname_offset =
1037 strtaboffset (name_info[n].canonical_strent);
1039 if (name_info[n].from_internal == NULL)
1041 module_table[n].fromdir_offset = 0;
1042 module_table[n].fromname_offset = 0;
1046 module_table[n].fromdir_offset =
1047 strtaboffset (name_info[n].from_internal->directory_strent);
1048 module_table[n].fromname_offset =
1049 strtaboffset (name_info[n].from_internal->filename_strent);
1052 if (name_info[n].to_internal == NULL)
1054 module_table[n].todir_offset = 0;
1055 module_table[n].toname_offset = 0;
1059 module_table[n].todir_offset =
1060 strtaboffset (name_info[n].to_internal->directory_strent);
1061 module_table[n].toname_offset =
1062 strtaboffset (name_info[n].to_internal->filename_strent);
1065 if (name_info[n].other_conv_list != NULL)
1067 struct other_conv_list *other = name_info[n].other_conv_list;
1069 /* Store the reference. We add 1 to distinguish the entry
1070 at offset zero from the case where no extra modules are
1071 available. The file reader has to account for the
1073 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1077 struct other_conv *runp;
1078 struct extra_entry *extra;
1080 /* Allocate new entry. */
1081 extra = (struct extra_entry *) cur_extra_table;
1082 cur_extra_table += sizeof (struct extra_entry);
1083 extra->module_cnt = 0;
1085 runp = &other->other_conv;
1088 cur_extra_table += sizeof (struct extra_entry_module);
1089 extra->module[extra->module_cnt].outname_offset =
1091 ? other->dest_idx : runp->next->module_idx;
1092 extra->module[extra->module_cnt].dir_offset =
1093 strtaboffset (runp->module->directory_strent);
1094 extra->module[extra->module_cnt].name_offset =
1095 strtaboffset (runp->module->filename_strent);
1096 ++extra->module_cnt;
1100 while (runp != NULL);
1102 other = other->next;
1104 while (other != NULL);
1106 /* Final module_cnt is zero. */
1107 *((gidx_t *) cur_extra_table) = 0;
1108 cur_extra_table += sizeof (gidx_t);
1112 header.magic = GCONVCACHE_MAGIC;
1114 iov[0].iov_base = &header;
1115 iov[0].iov_len = sizeof (struct gconvcache_header);
1116 total = iov[0].iov_len;
1118 header.string_offset = total;
1119 iov[1].iov_base = string_table;
1120 iov[1].iov_len = string_table_size;
1121 total += iov[1].iov_len;
1124 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1126 iov[2].iov_base = (void *) &null_word;
1127 iov[2].iov_len = (sizeof (gidx_t)
1128 - (string_table_size & (sizeof (gidx_t) - 1)));
1129 total += iov[2].iov_len;
1133 header.hash_offset = total;
1134 header.hash_size = hash_size;
1135 iov[idx].iov_base = hash_table;
1136 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1137 total += iov[idx].iov_len;
1140 header.module_offset = total;
1141 iov[idx].iov_base = module_table;
1142 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1143 total += iov[idx].iov_len;
1146 assert (cur_extra_table - extra_table
1147 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1148 + sizeof (struct extra_entry_module))
1150 header.otherconv_offset = total;
1151 iov[idx].iov_base = extra_table;
1152 iov[idx].iov_len = cur_extra_table - extra_table;
1153 total += iov[idx].iov_len;
1156 if (TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total)
1158 int save_errno = errno;
1160 unlink (GCONV_MODULES_CACHE);
1161 error (EXIT_FAILURE, save_errno, gettext ("cannot write output file"));