1 /* Generate fastloading iconv module configuration files.
2 Copyright (C) 2000, 2001, 2002 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 /* Function to print some extra text in the help message. */
117 static char *more_help (int key, const char *text, void *input);
119 /* Data structure to communicate with argp functions. */
120 static struct argp argp =
122 NULL, NULL, args_doc, doc, NULL, more_help
126 /* The function doing the actual work. */
127 static int handle_dir (const char *dir);
129 /* Add all known builtin conversions and aliases. */
130 static void add_builtins (void);
132 /* Create list of all aliases without circular aliases. */
133 static void get_aliases (void);
135 /* Create list of all modules. */
136 static void get_modules (void);
138 /* Get list of all the names and thereby indexing them. */
139 static void generate_name_list (void);
141 /* Collect information about all the names. */
142 static void generate_name_info (void);
144 /* Write the output file. */
145 static int write_output (void);
148 /* Search tree of the modules we know. */
149 static void *modules;
151 /* Search tree of the aliases we know. */
152 static void *aliases;
154 /* Search tree for name to index mapping. */
157 /* Number of names we know about. */
160 /* List of all aliases. */
161 static struct alias **alias_list;
162 static size_t nalias_list;
163 static size_t nalias_list_max;
165 /* List of all modules. */
166 static struct module **module_list;
167 static size_t nmodule_list;
168 static size_t nmodule_list_max;
170 /* Names and information about them. */
171 static struct name_info *name_info;
172 static size_t nname_info;
174 /* Number of translations not from or to INTERNAL. */
175 static size_t nextra_modules;
178 /* Names and aliases for the builtin transformations. */
185 #define BUILTIN_ALIAS(alias, real) \
186 { .from = alias, .to = real },
187 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
189 #include <gconv_builtin.h>
192 #undef BUILTIN_TRANSFORMATION
193 #define nbuiltin_alias (sizeof (builtin_alias) / sizeof (builtin_alias[0]))
203 #define BUILTIN_ALIAS(alias, real)
204 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, MinF, MaxF, \
206 { .from = From, .to = To, .module = Name, .cost = Cost },
207 #include <gconv_builtin.h>
209 #define nbuiltin_trans (sizeof (builtin_trans) / sizeof (builtin_trans[0]))
212 /* Filename extension for the modules. */
214 # define MODULE_EXT ".so"
216 static const char gconv_module_ext[] = MODULE_EXT;
219 extern void *xmalloc (size_t n) __attribute_malloc__;
220 extern void *xcalloc (size_t n, size_t m) __attribute_malloc__;
221 extern void *xrealloc (void *p, size_t n);
224 /* C string table handling. */
228 /* Create new C string table object in memory. */
229 extern struct Strtab *strtabinit (void);
231 /* Free resources allocated for C string table ST. */
232 extern void strtabfree (struct Strtab *st);
234 /* Add string STR (length LEN is != 0) to C string table ST. */
235 extern struct Strent *strtabadd (struct Strtab *st, const char *str,
238 /* Finalize string table ST and store size in *SIZE and return a pointer. */
239 extern void *strtabfinalize (struct Strtab *st, size_t *size);
241 /* Get offset in string table for string associated with SE. */
242 extern size_t strtaboffset (struct Strent *se);
244 /* String table we construct. */
245 static struct Strtab *strtab;
250 main (int argc, char *argv[])
256 const char *old = GCONV_PATH;
257 size_t len = strlen (old) + 1;
258 char *new = alloca(len);
260 /* Enable memory use testing. */
261 /* mcheck_pedantic (NULL); */
264 /* Set locale via LC_ALL. */
265 setlocale (LC_ALL, "");
267 /* Set the text message domain. */
268 textdomain (_libc_intl_domainname);
270 /* Parse and process arguments. */
271 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
273 /* Initialize the string table. */
274 strtab = strtabinit ();
276 /* Handle all directories mentioned. */
277 while (remaining < argc)
278 status |= handle_dir (argv[remaining++]);
280 /* In any case also handle the standard directory. */
281 path = memcpy (new, old, len);
282 tp = strtok (path, ":");
285 status |= handle_dir (tp);
287 tp = strtok (NULL, ":");
290 /* Add the builtin transformations and aliases without overwriting
294 /* Store aliases in an array. */
297 /* Get list of all modules. */
300 /* Generate list of all the names we know to handle in some way. */
301 generate_name_list ();
303 /* Now we know all the names we will handle, collect information
305 generate_name_info ();
307 /* Write the output file, but only if we haven't seen any error. */
309 status = write_output ();
311 error (1, 0, _("no output file produced because warning were issued"));
318 more_help (int key, const char *text, void *input)
322 case ARGP_KEY_HELP_EXTRA:
323 /* We print some extra information. */
324 return strdup (gettext ("\
325 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
329 return (char *) text;
333 /* Print the version information. */
335 print_version (FILE *stream, struct argp_state *state)
337 fprintf (stream, "iconvconfig (GNU %s) %s\n", PACKAGE, VERSION);
338 fprintf (stream, gettext ("\
339 Copyright (C) %s Free Software Foundation, Inc.\n\
340 This is free software; see the source for copying conditions. There is NO\n\
341 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
343 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
348 alias_compare (const void *p1, const void *p2)
350 const struct alias *a1 = (const struct alias *) p1;
351 const struct alias *a2 = (const struct alias *) p2;
353 return strcmp (a1->fromname, a2->fromname);
358 new_alias (const char *fromname, size_t fromlen, const char *toname,
364 newp = (struct alias *) xmalloc (sizeof (struct alias) + fromlen + tolen);
366 newp->fromname = memcpy (newp->toname, toname, tolen);
367 newp->fromname += tolen;
368 memcpy (newp->fromname, fromname, fromlen);
371 inserted = (void **) tsearch (newp, &aliases, alias_compare);
372 if (inserted == NULL)
373 error (EXIT_FAILURE, errno, gettext ("while inserting in search tree"));
374 if (*inserted != newp)
375 /* Something went wrong, free this entry. */
379 newp->froment = strtabadd (strtab, newp->fromname, fromlen);
380 newp->toent = strtabadd (strtab, newp->toname, tolen);
389 /* We now expect two more string. The strings are normalized
390 (converted to UPPER case) and strored in the alias database. */
395 while (isspace (*rp))
398 while (*rp != '\0' && !isspace (*rp))
399 *wp++ = toupper (*rp++);
401 /* There is no `to' string on the line. Ignore it. */
405 while (isspace (*rp))
407 while (*rp != '\0' && !isspace (*rp))
408 *wp++ = toupper (*rp++);
410 /* No `to' string, ignore the line. */
414 assert (strlen (from) + 1 == to - from);
415 assert (strlen (to) + 1 == wp - to);
417 new_alias (from, to - from, to, wp - to);
422 append_alias (const void *nodep, VISIT value, int level)
424 if (value != leaf && value != postorder)
427 if (nalias_list_max == nalias_list)
429 nalias_list_max += 50;
430 alias_list = (struct alias **) xrealloc (alias_list,
432 * sizeof (struct alias *)));
435 alias_list[nalias_list++] = *(struct alias **) nodep;
442 twalk (aliases, append_alias);
447 module_compare (const void *p1, const void *p2)
449 const struct module *m1 = (const struct module *) p1;
450 const struct module *m2 = (const struct module *) p2;
453 result = strcmp (m1->fromname, m2->fromname);
455 result = strcmp (m1->toname, m2->toname);
461 /* Create new module record. */
463 new_module (const char *fromname, size_t fromlen, const char *toname,
464 size_t tolen, const char *directory,
465 const char *filename, size_t filelen, int cost, size_t need_ext)
467 struct module *new_module;
468 size_t dirlen = strlen (directory) + 1;
472 new_module = (struct module *) xmalloc (sizeof (struct module)
473 + fromlen + tolen + filelen
476 new_module->fromname = memcpy (new_module->toname, toname, tolen);
477 new_module->fromname += tolen;
479 new_module->filename = memcpy (new_module->fromname, fromname, fromlen);
480 new_module->filename += fromlen;
482 new_module->cost = cost;
483 new_module->next = NULL;
485 tmp = memcpy (new_module->filename, filename, filelen);
490 memcpy (tmp - 1, gconv_module_ext, need_ext + 1);
493 new_module->directory = directory;
495 /* Now insert the new module data structure in our search tree. */
496 inserted = (void **) tsearch (new_module, &modules, module_compare);
497 if (inserted == NULL)
498 error (EXIT_FAILURE, errno, "while inserting in search tree");
499 if (*inserted != new_module)
503 new_module->fromname_strent = strtabadd (strtab, new_module->fromname,
505 new_module->toname_strent = strtabadd (strtab, new_module->toname,
507 new_module->filename_strent = strtabadd (strtab, new_module->filename,
509 new_module->directory_strent = strtabadd (strtab, directory, dirlen);
514 /* Add new module. */
517 add_module (char *rp, const char *directory)
522 3. filename of the module
523 4. an optional cost value
532 while (isspace (*rp))
535 while (*rp != '\0' && !isspace (*rp))
544 while (isspace (*rp))
546 while (*rp != '\0' && !isspace (*rp))
547 *wp++ = toupper (*rp++);
553 while (isspace (*rp));
555 while (*rp != '\0' && !isspace (*rp))
559 /* There is no cost, use one by default. */
565 /* There might be a cost value. */
569 cost = strtol (rp, &endp, 10);
570 if (rp == endp || cost < 1)
571 /* No useful information. */
575 if (module[0] == '\0')
576 /* No module name given. */
579 /* See whether we must add the ending. */
581 if (wp - module < sizeof (gconv_module_ext)
582 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
583 sizeof (gconv_module_ext)) != 0)
584 /* We must add the module extension. */
585 need_ext = sizeof (gconv_module_ext) - 1;
587 assert (strlen (from) + 1 == to - from);
588 assert (strlen (to) + 1 == module - to);
589 assert (strlen (module) + 1 == wp - module);
591 new_module (from, to - from, to, module - to, directory, module, wp - module,
596 /* Read the config file and add the data for this directory to that. */
598 handle_dir (const char *dir)
604 size_t dirlen = strlen (dir);
607 if (dir[dirlen - 1] != '/')
609 char *newp = (char *) xmalloc (dirlen + 2);
610 dir = memcpy (newp, dir, dirlen);
611 newp[dirlen++] = '/';
615 infile = (char *) alloca (dirlen + sizeof "gconv-modules");
616 tmp = mempcpy (infile, dir, dirlen);
618 strcpy (tmp, "gconv-modules");
620 fp = fopen (infile, "r");
623 error (0, errno, "cannot open `%s'", infile);
627 /* No threads present. */
628 __fsetlocking (fp, FSETLOCKING_BYCALLER);
630 while (!feof_unlocked (fp))
632 char *rp, *endp, *word;
633 ssize_t n = __getdelim (&line, &linelen, '\n', fp);
636 /* An error occurred. */
640 /* Terminate the line (excluding comments or newline) with a NUL
641 byte to simplify the following code. */
642 endp = strchr (rp, '#');
646 if (rp[n - 1] == '\n')
649 while (isspace (*rp))
652 /* If this is an empty line go on with the next one. */
657 while (*rp != '\0' && !isspace (*rp))
660 if (rp - word == sizeof ("alias") - 1
661 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
663 else if (rp - word == sizeof ("module") - 1
664 && memcmp (word, "module", sizeof ("module") - 1) == 0)
665 add_module (rp, dir);
667 /* Otherwise ignore the line. */
679 append_module (const void *nodep, VISIT value, int level)
683 if (value != leaf && value != postorder)
686 mo = *(struct module **) nodep;
689 && strcmp (module_list[nmodule_list - 1]->fromname, mo->fromname) == 0)
692 mo->next = module_list[nmodule_list - 1];
693 module_list[nmodule_list - 1] = mo;
698 if (nmodule_list_max == nmodule_list)
700 nmodule_list_max += 50;
701 module_list = (struct module **) xrealloc (module_list,
703 * sizeof (struct module *)));
706 module_list[nmodule_list++] = mo;
713 twalk (modules, append_module);
722 /* Add all aliases. */
723 for (cnt = 0; cnt < nbuiltin_alias; ++cnt)
724 new_alias (builtin_alias[cnt].from,
725 strlen (builtin_alias[cnt].from) + 1,
726 builtin_alias[cnt].to,
727 strlen (builtin_alias[cnt].to) + 1);
729 /* add the builtin transformations. */
730 for (cnt = 0; cnt < nbuiltin_trans; ++cnt)
731 new_module (builtin_trans[cnt].from,
732 strlen (builtin_trans[cnt].from) + 1,
733 builtin_trans[cnt].to,
734 strlen (builtin_trans[cnt].to) + 1,
735 "", builtin_trans[cnt].module,
736 strlen (builtin_trans[cnt].module) + 1,
737 builtin_trans[cnt].cost, 0);
742 name_compare (const void *p1, const void *p2)
744 const struct name *n1 = (const struct name *) p1;
745 const struct name *n2 = (const struct name *) p2;
747 return strcmp (n1->name, n2->name);
752 new_name (const char *str, struct Strent *strent)
754 struct name *newp = (struct name *) xmalloc (sizeof (struct name));
757 newp->strent = strent;
758 newp->module_idx = -1;
759 newp->hashval = hash_string (str);
768 generate_name_list (void)
772 /* A name we always need. */
773 tsearch (new_name ("INTERNAL", strtabadd (strtab, "INTERNAL",
774 sizeof ("INTERNAL"))),
775 &names, name_compare);
777 for (i = 0; i < nmodule_list; ++i)
781 if (strcmp (module_list[i]->fromname, "INTERNAL") != 0)
782 tsearch (new_name (module_list[i]->fromname,
783 module_list[i]->fromname_strent),
784 &names, name_compare);
786 for (runp = module_list[i]; runp != NULL; runp = runp->next)
787 if (strcmp (runp->toname, "INTERNAL") != 0)
788 tsearch (new_name (runp->toname, runp->toname_strent),
789 &names, name_compare);
795 name_to_module_idx (const char *name, int add)
798 struct name fake_name = { .name = name };
801 res = (struct name **) tfind (&fake_name, &names, name_compare);
805 idx = (*res)->module_idx;
806 if (idx == -1 && add)
807 /* No module index assigned yet. */
808 idx = (*res)->module_idx = nname_info++;
815 generate_name_info (void)
820 name_info = (struct name_info *) xcalloc (nmodule_list + 1,
821 sizeof (struct name_info));
823 /* First add a special entry for the INTERNAL name. This must have
825 idx = name_to_module_idx ("INTERNAL", 1);
826 name_info[0].canonical_name = "INTERNAL";
827 name_info[0].canonical_strent = strtabadd (strtab, "INTERNAL",
828 sizeof ("INTERNAL"));
829 assert (nname_info == 1);
831 for (i = 0; i < nmodule_list; ++i)
835 for (runp = module_list[i]; runp != NULL; runp = runp->next)
836 if (strcmp (runp->fromname, "INTERNAL") == 0)
838 idx = name_to_module_idx (runp->toname, 1);
839 name_info[idx].from_internal = runp;
840 assert (name_info[idx].canonical_name == NULL
841 || strcmp (name_info[idx].canonical_name,
843 name_info[idx].canonical_name = runp->toname;
844 name_info[idx].canonical_strent = runp->toname_strent;
846 else if (strcmp (runp->toname, "INTERNAL") == 0)
848 idx = name_to_module_idx (runp->fromname, 1);
849 name_info[idx].to_internal = runp;
850 assert (name_info[idx].canonical_name == NULL
851 || strcmp (name_info[idx].canonical_name,
852 runp->fromname) == 0);
853 name_info[idx].canonical_name = runp->fromname;
854 name_info[idx].canonical_strent = runp->fromname_strent;
858 /* This is a transformation not to or from the INTERNAL
860 int from_idx = name_to_module_idx (runp->fromname, 1);
861 int to_idx = name_to_module_idx (runp->toname, 1);
862 struct other_conv_list *newp;
864 newp = (struct other_conv_list *)
865 xmalloc (sizeof (struct other_conv_list));
866 newp->other_conv.module_idx = to_idx;
867 newp->other_conv.module = runp;
868 newp->other_conv.next = NULL; /* XXX Allow multiple module sequence */
869 newp->dest_idx = to_idx;
870 newp->next = name_info[from_idx].other_conv_list;
871 name_info[from_idx].other_conv_list = newp;
872 assert (name_info[from_idx].canonical_name == NULL
873 || strcmp (name_info[from_idx].canonical_name,
874 runp->fromname) == 0);
875 name_info[from_idx].canonical_name = runp->fromname;
876 name_info[from_idx].canonical_strent = runp->fromname_strent;
882 /* Now add the module index information for all the aliases. */
883 for (i = 0; i < nalias_list; ++i)
885 struct name fake_name = { .name = alias_list[i]->toname };
886 struct name **tonamep;
888 tonamep = (struct name **) tfind (&fake_name, &names, name_compare);
891 struct name *newp = new_name (alias_list[i]->fromname,
892 alias_list[i]->froment);
893 newp->module_idx = (*tonamep)->module_idx;
894 tsearch (newp, &names, name_compare);
901 is_prime (unsigned long int candidate)
903 /* No even number and none less than 10 will be passed here. */
904 unsigned long int divn = 3;
905 unsigned long int sq = divn * divn;
907 while (sq < candidate && candidate % divn != 0)
914 return candidate % divn != 0;
919 next_prime (uint32_t seed)
921 /* Make it definitely odd. */
924 while (!is_prime (seed))
931 /* Format of the output file.
933 Offset Length Description
934 0000 4 Magic header bytes
935 0004 4 Offset of string table (stoff)
936 0008 4 Offset of name hashing table (hoff)
937 000C 4 Hashing table size (hsize)
938 0010 4 Offset of module table (moff)
939 0014 4 Offset of other conversion module table (ooff)
941 stoff ??? String table
943 hoff 8*hsize Array of tuples
947 moff ??? Array of tuples
948 canonical name offset
949 from-internal module dir name offset
950 from-internal module name off
951 to-internal module dir name offset
952 to-internal module name offset
953 offset into other conversion table
955 ooff ??? One or more of
956 number of steps/modules
958 canonical name offset for output
959 module dir name offset
961 (following last entry with step count 0)
968 size_t string_table_size;
969 struct gconvcache_header header;
970 struct hash_entry *hash_table;
972 struct module_entry *module_table;
974 char *cur_extra_table;
978 static const gidx_t null_word;
980 char tmpfname[sizeof (GCONV_MODULES_CACHE) + strlen (".XXXXXX")];
982 /* Function to insert the names. */
983 static void name_insert (const void *nodep, VISIT value, int level)
989 if (value != leaf && value != postorder)
992 name = *(struct name **) nodep;
993 idx = name->hashval % hash_size;
994 hval2 = 1 + name->hashval % (hash_size - 2);
996 while (hash_table[idx].string_offset != 0)
997 if ((idx += hval2) >= hash_size)
1000 hash_table[idx].string_offset = strtaboffset (name->strent);
1002 assert (name->module_idx != -1);
1003 hash_table[idx].module_idx = name->module_idx;
1006 /* Open the output file. */
1007 strcpy (stpcpy (tmpfname, GCONV_MODULES_CACHE), ".XXXXXX");
1008 fd = mkstemp (tmpfname);
1012 /* Create the string table. */
1013 string_table = strtabfinalize (strtab, &string_table_size);
1015 /* Create the hashing table. We know how many strings we have.
1016 Creating a perfect hash table is not reasonable here. Therefore
1017 we use open hashing and a table size which is the next prime 40%
1018 larger than the number of strings. */
1019 hash_size = next_prime (nnames * 1.4);
1020 hash_table = (struct hash_entry *) xcalloc (hash_size,
1021 sizeof (struct hash_entry));
1022 /* Fill the hash table. */
1023 twalk (names, name_insert);
1025 /* Create the section for the module list. */
1026 module_table = (struct module_entry *) xcalloc (sizeof (struct module_entry),
1029 /* Allocate memory for the non-INTERNAL conversions. The allocated
1030 memory can be more than is actually needed. */
1031 extra_table = (char *) xcalloc (sizeof (struct extra_entry)
1033 + sizeof (struct extra_entry_module),
1035 cur_extra_table = extra_table;
1037 /* Fill in the module information. */
1038 for (n = 0; n < nname_info; ++n)
1040 module_table[n].canonname_offset =
1041 strtaboffset (name_info[n].canonical_strent);
1043 if (name_info[n].from_internal == NULL)
1045 module_table[n].fromdir_offset = 0;
1046 module_table[n].fromname_offset = 0;
1050 module_table[n].fromdir_offset =
1051 strtaboffset (name_info[n].from_internal->directory_strent);
1052 module_table[n].fromname_offset =
1053 strtaboffset (name_info[n].from_internal->filename_strent);
1056 if (name_info[n].to_internal == NULL)
1058 module_table[n].todir_offset = 0;
1059 module_table[n].toname_offset = 0;
1063 module_table[n].todir_offset =
1064 strtaboffset (name_info[n].to_internal->directory_strent);
1065 module_table[n].toname_offset =
1066 strtaboffset (name_info[n].to_internal->filename_strent);
1069 if (name_info[n].other_conv_list != NULL)
1071 struct other_conv_list *other = name_info[n].other_conv_list;
1073 /* Store the reference. We add 1 to distinguish the entry
1074 at offset zero from the case where no extra modules are
1075 available. The file reader has to account for the
1077 module_table[n].extra_offset = 1 + cur_extra_table - extra_table;
1081 struct other_conv *runp;
1082 struct extra_entry *extra;
1084 /* Allocate new entry. */
1085 extra = (struct extra_entry *) cur_extra_table;
1086 cur_extra_table += sizeof (struct extra_entry);
1087 extra->module_cnt = 0;
1089 runp = &other->other_conv;
1092 cur_extra_table += sizeof (struct extra_entry_module);
1093 extra->module[extra->module_cnt].outname_offset =
1095 ? other->dest_idx : runp->next->module_idx;
1096 extra->module[extra->module_cnt].dir_offset =
1097 strtaboffset (runp->module->directory_strent);
1098 extra->module[extra->module_cnt].name_offset =
1099 strtaboffset (runp->module->filename_strent);
1100 ++extra->module_cnt;
1104 while (runp != NULL);
1106 other = other->next;
1108 while (other != NULL);
1110 /* Final module_cnt is zero. */
1111 *((gidx_t *) cur_extra_table) = 0;
1112 cur_extra_table += sizeof (gidx_t);
1116 header.magic = GCONVCACHE_MAGIC;
1118 iov[0].iov_base = &header;
1119 iov[0].iov_len = sizeof (struct gconvcache_header);
1120 total = iov[0].iov_len;
1122 header.string_offset = total;
1123 iov[1].iov_base = string_table;
1124 iov[1].iov_len = string_table_size;
1125 total += iov[1].iov_len;
1128 if ((string_table_size & (sizeof (gidx_t) - 1)) != 0)
1130 iov[2].iov_base = (void *) &null_word;
1131 iov[2].iov_len = (sizeof (gidx_t)
1132 - (string_table_size & (sizeof (gidx_t) - 1)));
1133 total += iov[2].iov_len;
1137 header.hash_offset = total;
1138 header.hash_size = hash_size;
1139 iov[idx].iov_base = hash_table;
1140 iov[idx].iov_len = hash_size * sizeof (struct hash_entry);
1141 total += iov[idx].iov_len;
1144 header.module_offset = total;
1145 iov[idx].iov_base = module_table;
1146 iov[idx].iov_len = nname_info * sizeof (struct module_entry);
1147 total += iov[idx].iov_len;
1150 assert (cur_extra_table - extra_table
1151 <= ((sizeof (struct extra_entry) + sizeof (gidx_t)
1152 + sizeof (struct extra_entry_module))
1154 header.otherconv_offset = total;
1155 iov[idx].iov_base = extra_table;
1156 iov[idx].iov_len = cur_extra_table - extra_table;
1157 total += iov[idx].iov_len;
1160 if (TEMP_FAILURE_RETRY (writev (fd, iov, idx)) != total
1161 /* The file was created with mode 0600. Make it world-readable. */
1162 || fchmod (fd, 0644) != 0
1163 /* Rename the file, possibly replacing an old one. */
1164 || rename (tmpfname, GCONV_MODULES_CACHE) != 0)
1166 int save_errno = errno;
1169 error (EXIT_FAILURE, save_errno,
1170 gettext ("cannot generate output file"));