Update.
[platform/upstream/glibc.git] / catgets / gencat.c
1 /* Copyright (C) 1996-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@redhat.com>, 1996.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library 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 GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <argp.h>
25 #include <assert.h>
26 #include <ctype.h>
27 #include <endian.h>
28 #include <errno.h>
29 #include <error.h>
30 #include <fcntl.h>
31 #include <iconv.h>
32 #include <langinfo.h>
33 #include <locale.h>
34 #include <libintl.h>
35 #include <limits.h>
36 #include <nl_types.h>
37 #include <obstack.h>
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <wchar.h>
44
45 #include "version.h"
46
47 #include "catgetsinfo.h"
48
49
50 #define SWAPU32(w) \
51   (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
52
53 struct message_list
54 {
55   int number;
56   const char *message;
57
58   const char *fname;
59   size_t line;
60   const char *symbol;
61
62   struct message_list *next;
63 };
64
65
66 struct set_list
67 {
68   int number;
69   int deleted;
70   struct message_list *messages;
71   int last_message;
72
73   const char *fname;
74   size_t line;
75   const char *symbol;
76
77   struct set_list *next;
78 };
79
80
81 struct catalog
82 {
83   struct set_list *all_sets;
84   struct set_list *current_set;
85   size_t total_messages;
86   wint_t quote_char;
87   int last_set;
88
89   struct obstack mem_pool;
90 };
91
92
93 /* If non-zero force creation of new file, not using existing one.  */
94 static int force_new;
95
96 /* Name of output file.  */
97 static const char *output_name;
98
99 /* Name of generated C header file.  */
100 static const char *header_name;
101
102 /* Name and version of program.  */
103 static void print_version (FILE *stream, struct argp_state *state);
104 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
105
106 #define OPT_NEW 1
107
108 /* Definitions of arguments for argp functions.  */
109 static const struct argp_option options[] =
110 {
111   { "header", 'H', N_("NAME"), 0,
112     N_("Create C header file NAME containing symbol definitions") },
113   { "new", OPT_NEW, NULL, 0,
114     N_("Do not use existing catalog, force new output file") },
115   { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") },
116   { NULL, 0, NULL, 0, NULL }
117 };
118
119 /* Short description of program.  */
120 static const char doc[] = N_("Generate message catalog.\
121 \vIf INPUT-FILE is -, input is read from standard input.  If OUTPUT-FILE\n\
122 is -, output is written to standard output.\n");
123
124 /* Strings for arguments in help texts.  */
125 static const char args_doc[] = N_("\
126 -o OUTPUT-FILE [INPUT-FILE]...\n[OUTPUT-FILE [INPUT-FILE]...]");
127
128 /* Prototype for option handler.  */
129 static error_t parse_opt (int key, char *arg, struct argp_state *state);
130
131 /* Function to print some extra text in the help message.  */
132 static char *more_help (int key, const char *text, void *input);
133
134 /* Data structure to communicate with argp functions.  */
135 static struct argp argp =
136 {
137   options, parse_opt, args_doc, doc, NULL, more_help
138 };
139
140
141 /* Wrapper functions with error checking for standard functions.  */
142 extern void *xmalloc (size_t n);
143 extern void *xcalloc (size_t n, size_t s);
144 extern void *xrealloc (void *o, size_t n);
145 extern char *xstrdup (const char *);
146
147 /* Prototypes for local functions.  */
148 static void error_print (void);
149 static struct catalog *read_input_file (struct catalog *current,
150                                         const char *fname);
151 static void write_out (struct catalog *result, const char *output_name,
152                        const char *header_name);
153 static struct set_list *find_set (struct catalog *current, int number);
154 static void normalize_line (const char *fname, size_t line, iconv_t cd,
155                             wchar_t *string, wchar_t quote_char,
156                             wchar_t escape_char);
157 static void read_old (struct catalog *catalog, const char *file_name);
158 static int open_conversion (const char *codesetp, iconv_t *cd_towcp,
159                             iconv_t *cd_tombp, wchar_t *escape_charp);
160
161
162 int
163 main (int argc, char *argv[])
164 {
165   struct catalog *result;
166   int remaining;
167
168   /* Set program name for messages.  */
169   error_print_progname = error_print;
170
171   /* Set locale via LC_ALL.  */
172   setlocale (LC_ALL, "");
173
174   /* Set the text message domain.  */
175   textdomain (PACKAGE);
176
177   /* Initialize local variables.  */
178   result = NULL;
179
180   /* Parse and process arguments.  */
181   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
182
183   /* Determine output file.  */
184   if (output_name == NULL)
185     output_name = remaining < argc ? argv[remaining++] : "-";
186
187   /* Process all input files.  */
188   setlocale (LC_CTYPE, "C");
189   if (remaining < argc)
190     do
191       result = read_input_file (result, argv[remaining]);
192     while (++remaining < argc);
193   else
194     result = read_input_file (NULL, "-");
195
196   /* Write out the result.  */
197   if (result != NULL)
198     write_out (result, output_name, header_name);
199
200   return EXIT_SUCCESS;
201 }
202
203
204 /* Handle program arguments.  */
205 static error_t
206 parse_opt (int key, char *arg, struct argp_state *state)
207 {
208   switch (key)
209     {
210     case 'H':
211       header_name = arg;
212       break;
213     case OPT_NEW:
214       force_new = 1;
215       break;
216     case 'o':
217       output_name = arg;
218       break;
219     default:
220       return ARGP_ERR_UNKNOWN;
221     }
222   return 0;
223 }
224
225
226 static char *
227 more_help (int key, const char *text, void *input)
228 {
229   switch (key)
230     {
231     case ARGP_KEY_HELP_EXTRA:
232       /* We print some extra information.  */
233       return strdup (gettext ("\
234 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
235     default:
236       break;
237     }
238   return (char *) text;
239 }
240
241 /* Print the version information.  */
242 static void
243 print_version (FILE *stream, struct argp_state *state)
244 {
245   fprintf (stream, "gencat (GNU %s) %s\n", PACKAGE, VERSION);
246   fprintf (stream, gettext ("\
247 Copyright (C) %s Free Software Foundation, Inc.\n\
248 This is free software; see the source for copying conditions.  There is NO\n\
249 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
250 "), "2002");
251   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
252 }
253
254
255 /* The address of this function will be assigned to the hook in the
256    error functions.  */
257 static void
258 error_print ()
259 {
260   /* We don't want the program name to be printed in messages.  Emacs'
261      compile.el does not like this.  */
262 }
263
264
265 static struct catalog *
266 read_input_file (struct catalog *current, const char *fname)
267 {
268   FILE *fp;
269   char *buf;
270   size_t len;
271   size_t line_number;
272   wchar_t *wbuf;
273   size_t wbufsize;
274   iconv_t cd_towc = (iconv_t) -1;
275   iconv_t cd_tomb = (iconv_t) -1;
276   wchar_t escape_char = L'\\';
277   char *codeset = NULL;
278
279   if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
280     {
281       fp = stdin;
282       fname = gettext ("*standard input*");
283     }
284   else
285     fp = fopen (fname, "r");
286   if (fp == NULL)
287     {
288       error (0, errno, gettext ("cannot open input file `%s'"), fname);
289       return current;
290     }
291
292   /* If we haven't seen anything yet, allocate result structure.  */
293   if (current == NULL)
294     {
295       current = (struct catalog *) xcalloc (1, sizeof (*current));
296
297 #define obstack_chunk_alloc malloc
298 #define obstack_chunk_free free
299       obstack_init (&current->mem_pool);
300
301       current->current_set = find_set (current, NL_SETD);
302     }
303
304   buf = NULL;
305   len = 0;
306   line_number = 0;
307
308   wbufsize = 1024;
309   wbuf = (wchar_t *) xmalloc (wbufsize);
310
311   while (!feof (fp))
312     {
313       int continued;
314       int used;
315       size_t start_line = line_number + 1;
316       char *this_line;
317
318       do
319         {
320           int act_len;
321
322           act_len = getline (&buf, &len, fp);
323           if (act_len <= 0)
324             break;
325           ++line_number;
326
327           /* It the line continued?  */
328           if (buf[act_len - 1] == '\n')
329             {
330               --act_len;
331               continued = buf[act_len - 1] == '\\';
332               if (continued)
333                 --act_len;
334             }
335           else
336             continued = 0;
337
338           /* Append to currently selected line.  */
339           obstack_grow (&current->mem_pool, buf, act_len);
340         }
341       while (continued);
342
343       obstack_1grow (&current->mem_pool, '\0');
344       this_line = (char *) obstack_finish (&current->mem_pool);
345
346       used = 0;
347       if (this_line[0] == '$')
348         {
349           if (isblank (this_line[1]))
350             {
351               int cnt = 1;
352               while (isblank (this_line[cnt]))
353                 ++cnt;
354               if (strncmp (&this_line[cnt], "codeset=", 8) != 0)
355                 /* This is a comment line. Do nothing.  */;
356               else if (codeset != NULL)
357                 /* Ignore multiple codeset. */;
358               else
359                 {
360                   int start = cnt + 8;
361                   cnt = start;
362                   while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
363                     ++cnt;
364                   if (cnt != start)
365                     {
366                       int len = cnt - start;
367                       codeset = xmalloc (len + 1);
368                       *((char *) mempcpy (codeset, &this_line[start], len))
369                         = '\0';
370                     }
371                 }
372             }
373           else if (strncmp (&this_line[1], "set", 3) == 0)
374             {
375               int cnt = sizeof ("set");
376               int set_number;
377               const char *symbol = NULL;
378               while (isspace (this_line[cnt]))
379                 ++cnt;
380
381               if (isdigit (this_line[cnt]))
382                 {
383                   set_number = atol (&this_line[cnt]);
384
385                   /* If the given number for the character set is
386                      higher than any we used for symbolic set names
387                      avoid clashing by using only higher numbers for
388                      the following symbolic definitions.  */
389                   if (set_number > current->last_set)
390                     current->last_set = set_number;
391                 }
392               else
393                 {
394                   /* See whether it is a reasonable identifier.  */
395                   int start = cnt;
396                   while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
397                     ++cnt;
398
399                   if (cnt == start)
400                     {
401                       /* No correct character found.  */
402                       error_at_line (0, 0, fname, start_line,
403                                      gettext ("illegal set number"));
404                       set_number = 0;
405                     }
406                   else
407                     {
408                       /* We have found seomthing that looks like a
409                          correct identifier.  */
410                       struct set_list *runp;
411
412                       this_line[cnt] = '\0';
413                       used = 1;
414                       symbol = &this_line[start];
415
416                       /* Test whether the identifier was already used.  */
417                       runp = current->all_sets;
418                       while (runp != 0)
419                         if (runp->symbol != NULL
420                             && strcmp (runp->symbol, symbol) == 0)
421                           break;
422                         else
423                           runp = runp->next;
424
425                       if (runp != NULL)
426                         {
427                           /* We cannot allow duplicate identifiers for
428                              message sets.  */
429                           error_at_line (0, 0, fname, start_line,
430                                          gettext ("duplicate set definition"));
431                           error_at_line (0, 0, runp->fname, runp->line,
432                                          gettext ("\
433 this is the first definition"));
434                           set_number = 0;
435                         }
436                       else
437                         /* Allocate next free message set for identifier.  */
438                         set_number = ++current->last_set;
439                     }
440                 }
441
442               if (set_number != 0)
443                 {
444                   /* We found a legal set number.  */
445                   current->current_set = find_set (current, set_number);
446                   if (symbol != NULL)
447                       used = 1;
448                   current->current_set->symbol = symbol;
449                   current->current_set->fname = fname;
450                   current->current_set->line = start_line;
451                 }
452             }
453           else if (strncmp (&this_line[1], "delset", 6) == 0)
454             {
455               int cnt = sizeof ("delset");
456               size_t set_number;
457               while (isspace (this_line[cnt]))
458                 ++cnt;
459
460               if (isdigit (this_line[cnt]))
461                 {
462                   size_t set_number = atol (&this_line[cnt]);
463                   struct set_list *set;
464
465                   /* Mark the message set with the given number as
466                      deleted.  */
467                   set = find_set (current, set_number);
468                   set->deleted = 1;
469                 }
470               else
471                 {
472                   /* See whether it is a reasonable identifier.  */
473                   int start = cnt;
474                   while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
475                     ++cnt;
476
477                   if (cnt == start)
478                     {
479                       error_at_line (0, 0, fname, start_line,
480                                      gettext ("illegal set number"));
481                       set_number = 0;
482                     }
483                   else
484                     {
485                       const char *symbol;
486                       struct set_list *runp;
487
488                       this_line[cnt] = '\0';
489                       used = 1;
490                       symbol = &this_line[start];
491
492                       /* We have a symbolic set name.  This name must
493                          appear somewhere else in the catalogs read so
494                          far.  */
495                       set_number = 0;
496                       for (runp = current->all_sets; runp != NULL;
497                            runp = runp->next)
498                         {
499                           if (strcmp (runp->symbol, symbol) == 0)
500                             {
501                               runp->deleted = 1;
502                               break;
503                             }
504                         }
505                       if (runp == NULL)
506                         /* Name does not exist before.  */
507                         error_at_line (0, 0, fname, start_line,
508                                        gettext ("unknown set `%s'"), symbol);
509                     }
510                 }
511             }
512           else if (strncmp (&this_line[1], "quote", 5) == 0)
513             {
514               char buf[2];
515               char *bufptr;
516               size_t buflen;
517               char *wbufptr;
518               size_t wbuflen;
519               int cnt;
520
521               cnt = sizeof ("quote");
522               while (isspace (this_line[cnt]))
523                 ++cnt;
524
525               /* We need the conversion.  */
526               if (cd_towc == (iconv_t) -1
527                   && open_conversion (codeset, &cd_towc, &cd_tomb,
528                                       &escape_char) != 0)
529                 /* Something is wrong.  */
530                 goto out;
531
532               /* Yes, the quote char can be '\0'; this means no quote
533                  char.  The function using the information works on
534                  wide characters so we have to convert it here.  */
535               buf[0] = this_line[cnt];
536               buf[1] = '\0';
537               bufptr = buf;
538               buflen = 2;
539
540               wbufptr = (char *) wbuf;
541               wbuflen = wbufsize;
542
543               /* Flush the state.  */
544               iconv (cd_towc, NULL, NULL, NULL, NULL);
545
546               iconv (cd_towc, &bufptr, &buflen, &wbufptr, &wbuflen);
547               if (buflen != 0 || (wchar_t *) wbufptr != &wbuf[2])
548                 error_at_line (0, 0, fname, start_line,
549                                gettext ("invalid quote character"));
550               else
551                 /* Use the converted wide character.  */
552                 current->quote_char = wbuf[0];
553             }
554           else
555             {
556               int cnt;
557               cnt = 2;
558               while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
559                 ++cnt;
560               this_line[cnt] = '\0';
561               error_at_line (0, 0, fname, start_line,
562                              gettext ("unknown directive `%s': line ignored"),
563                              &this_line[1]);
564             }
565         }
566       else if (isalnum (this_line[0]) || this_line[0] == '_')
567         {
568           const char *ident = this_line;
569           char *line = this_line;
570           int message_number;
571
572           do
573             ++line;
574           while (line[0] != '\0' && !isspace (line[0]));
575           if (line[0] != '\0')
576             *line++ = '\0';     /* Terminate the identifier.  */
577
578           /* Now we found the beginning of the message itself.  */
579
580           if (isdigit (ident[0]))
581             {
582               struct message_list *runp;
583               struct message_list *lastp;
584
585               message_number = atoi (ident);
586
587               /* Find location to insert the new message.  */
588               runp = current->current_set->messages;
589               lastp = NULL;
590               while (runp != NULL)
591                 if (runp->number == message_number)
592                   break;
593                 else
594                   {
595                     lastp = runp;
596                     runp = runp->next;
597                   }
598               if (runp != NULL)
599                 {
600                   /* Oh, oh.  There is already a message with this
601                      number in the message set.  */
602                   error_at_line (0, 0, fname, start_line,
603                                  gettext ("duplicated message number"));
604                   error_at_line (0, 0, runp->fname, runp->line,
605                                  gettext ("this is the first definition"));
606                   message_number = 0;
607                 }
608               ident = NULL;     /* We don't have a symbol.  */
609
610               if (message_number != 0
611                   && message_number > current->current_set->last_message)
612                 current->current_set->last_message = message_number;
613             }
614           else if (ident[0] != '\0')
615             {
616               struct message_list *runp;
617               struct message_list *lastp;
618
619               /* Test whether the symbolic name was not used for
620                  another message in this message set.  */
621               runp = current->current_set->messages;
622               lastp = NULL;
623               while (runp != NULL)
624                 if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
625                   break;
626                 else
627                   runp = runp->next;
628               if (runp != NULL)
629                 {
630                   /* The name is already used.  */
631                   error_at_line (0, 0, fname, start_line, gettext ("\
632 duplicated message identifier"));
633                   error_at_line (0, 0, runp->fname, runp->line,
634                                  gettext ("this is the first definition"));
635                   message_number = 0;
636                 }
637               else
638                 /* Give the message the next unused number.  */
639                 message_number = ++current->current_set->last_message;
640             }
641           else
642             message_number = 0;
643
644           if (message_number != 0)
645             {
646               char *inbuf;
647               size_t inlen;
648               char *outbuf;
649               size_t outlen;
650               struct message_list *newp;
651               size_t line_len = strlen (line) + 1;
652               size_t ident_len = 0;
653
654               /* We need the conversion.  */
655               if (cd_towc == (iconv_t) -1
656                   && open_conversion (codeset, &cd_towc, &cd_tomb,
657                                       &escape_char) != 0)
658                 /* Something is wrong.  */
659                 goto out;
660
661               /* Convert to a wide character string.  We have to
662                  interpret escape sequences which will be impossible
663                  without doing the conversion if the codeset of the
664                  message is stateful.  */
665               while (1)
666                 {
667                   inbuf = line;
668                   inlen = line_len;
669                   outbuf = (char *) wbuf;
670                   outlen = wbufsize;
671
672                   /* Flush the state.  */
673                   iconv (cd_towc, NULL, NULL, NULL, NULL);
674
675                   iconv (cd_towc, &inbuf, &inlen, &outbuf, &outlen);
676                   if (inlen == 0)
677                     {
678                       /* The string is converted.  */
679                       assert (outlen < wbufsize);
680                       assert (wbuf[(wbufsize - outlen) / sizeof (wchar_t) - 1]
681                               == L'\0');
682                       break;
683                     }
684
685                   if (outlen != 0)
686                     {
687                       /* Something is wrong with this string, we ignore it.  */
688                       error_at_line (0, 0, fname, start_line, gettext ("\
689 invalid character: message ignored"));
690                       goto ignore;
691                     }
692
693                   /* The output buffer is too small.  */
694                   wbufsize *= 2;
695                   wbuf = (wchar_t *) xrealloc (wbuf, wbufsize);
696                 }
697
698               /* Strip quote characters, change escape sequences into
699                  correct characters etc.  */
700               normalize_line (fname, start_line, cd_towc, wbuf,
701                               current->quote_char, escape_char);
702
703               if (ident)
704                 ident_len = line - this_line;
705
706               /* Now the string is free of escape sequences.  Convert it
707                  back into a multibyte character string.  First free the
708                  memory allocated for the original string.  */
709               obstack_free (&current->mem_pool, this_line);
710
711               used = 1; /* Yes, we use the line.  */
712
713               /* Now fill in the new string.  It should never happen that
714                  the replaced string is longer than the original.  */
715               inbuf = (char *) wbuf;
716               inlen = (wcslen (wbuf) + 1) * sizeof (wchar_t);
717
718               outlen = obstack_room (&current->mem_pool);
719               obstack_blank (&current->mem_pool, outlen);
720               this_line = (char *) obstack_base (&current->mem_pool);
721               outbuf = this_line + ident_len;
722               outlen -= ident_len;
723
724               /* Flush the state.  */
725               iconv (cd_tomb, NULL, NULL, NULL, NULL);
726
727               iconv (cd_tomb, &inbuf, &inlen, &outbuf, &outlen);
728               if (inlen != 0)
729                 {
730                   error_at_line (0, 0, fname, start_line,
731                                  gettext ("invalid line"));
732                   goto ignore;
733                 }
734               assert (outbuf[-1] == '\0');
735
736               /* Free the memory in the obstack we don't use.  */
737               obstack_blank (&current->mem_pool, -(int) outlen);
738               line = obstack_finish (&current->mem_pool);
739
740               newp = (struct message_list *) xmalloc (sizeof (*newp));
741               newp->number = message_number;
742               newp->message = line + ident_len;
743               /* Remember symbolic name; is NULL if no is given.  */
744               newp->symbol = ident ? line : NULL;
745               /* Remember where we found the character.  */
746               newp->fname = fname;
747               newp->line = start_line;
748
749               /* Find place to insert to message.  We keep them in a
750                  sorted single linked list.  */
751               if (current->current_set->messages == NULL
752                   || current->current_set->messages->number > message_number)
753                 {
754                   newp->next = current->current_set->messages;
755                   current->current_set->messages = newp;
756                 }
757               else
758                 {
759                   struct message_list *runp;
760                   runp = current->current_set->messages;
761                   while (runp->next != NULL)
762                     if (runp->next->number > message_number)
763                       break;
764                     else
765                       runp = runp->next;
766                   newp->next = runp->next;
767                   runp->next = newp;
768                 }
769             }
770           ++current->total_messages;
771         }
772       else
773         {
774           size_t cnt;
775
776           cnt = 0;
777           /* See whether we have any non-white space character in this
778              line.  */
779           while (this_line[cnt] != '\0' && isspace (this_line[cnt]))
780             ++cnt;
781
782           if (this_line[cnt] != '\0')
783             /* Yes, some unknown characters found.  */
784             error_at_line (0, 0, fname, start_line,
785                            gettext ("malformed line ignored"));
786         }
787
788     ignore:
789       /* We can save the memory for the line if it was not used.  */
790       if (!used)
791         obstack_free (&current->mem_pool, this_line);
792     }
793
794   /* Close the conversion modules.  */
795   iconv_close (cd_towc);
796   iconv_close (cd_tomb);
797   free (codeset);
798
799  out:
800   free (wbuf);
801
802   if (fp != stdin)
803     fclose (fp);
804   return current;
805 }
806
807
808 static void
809 write_out (struct catalog *catalog, const char *output_name,
810            const char *header_name)
811 {
812   /* Computing the "optimal" size.  */
813   struct set_list *set_run;
814   size_t best_total, best_size, best_depth;
815   size_t act_size, act_depth;
816   struct catalog_obj obj;
817   struct obstack string_pool;
818   const char *strings;
819   size_t strings_size;
820   uint32_t *array1, *array2;
821   size_t cnt;
822   int fd;
823
824   /* If not otherwise told try to read file with existing
825      translations.  */
826   if (!force_new)
827     read_old (catalog, output_name);
828
829   /* Initialize best_size with a very high value.  */
830   best_total = best_size = best_depth = UINT_MAX;
831
832   /* We need some start size for testing.  Let's start with
833      TOTAL_MESSAGES / 5, which theoretically provides a mean depth of
834      5.  */
835   act_size = 1 + catalog->total_messages / 5;
836
837   /* We determine the size of a hash table here.  Because the message
838      numbers can be chosen arbitrary by the programmer we cannot use
839      the simple method of accessing the array using the message
840      number.  The algorithm is based on the trivial hash function
841      NUMBER % TABLE_SIZE, where collisions are stored in a second
842      dimension up to TABLE_DEPTH.  We here compute TABLE_SIZE so that
843      the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal.  */
844   while (act_size <= best_total)
845     {
846       size_t deep[act_size];
847
848       act_depth = 1;
849       memset (deep, '\0', act_size * sizeof (size_t));
850       set_run = catalog->all_sets;
851       while (set_run != NULL)
852         {
853           struct message_list *message_run;
854
855           message_run = set_run->messages;
856           while (message_run != NULL)
857             {
858               size_t idx = (message_run->number * set_run->number) % act_size;
859
860               ++deep[idx];
861               if (deep[idx] > act_depth)
862                 {
863                   act_depth = deep[idx];
864                   if (act_depth * act_size > best_total)
865                     break;
866                 }
867               message_run = message_run->next;
868             }
869           set_run = set_run->next;
870         }
871
872       if (act_depth * act_size <= best_total)
873         {
874           /* We have found a better solution.  */
875           best_total = act_depth * act_size;
876           best_size = act_size;
877           best_depth = act_depth;
878         }
879
880       ++act_size;
881     }
882
883   /* let's be prepared for an empty message file.  */
884   if (best_size == UINT_MAX)
885     {
886       best_size = 1;
887       best_depth = 1;
888     }
889
890   /* OK, now we have the size we will use.  Fill in the header, build
891      the table and the second one with swapped byte order.  */
892   obj.magic = CATGETS_MAGIC;
893   obj.plane_size = best_size;
894   obj.plane_depth = best_depth;
895
896   /* Allocate room for all needed arrays.  */
897   array1 =
898     (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
899   memset (array1, '\0', best_size * best_depth * sizeof (uint32_t) * 3);
900   array2
901     = (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
902   obstack_init (&string_pool);
903
904   set_run = catalog->all_sets;
905   while (set_run != NULL)
906     {
907       struct message_list *message_run;
908
909       message_run = set_run->messages;
910       while (message_run != NULL)
911         {
912           size_t idx = (((message_run->number * set_run->number) % best_size)
913                         * 3);
914           /* Determine collision depth.  */
915           while (array1[idx] != 0)
916             idx += best_size * 3;
917
918           /* Store set number, message number and pointer into string
919              space, relative to the first string.  */
920           array1[idx + 0] = set_run->number;
921           array1[idx + 1] = message_run->number;
922           array1[idx + 2] = obstack_object_size (&string_pool);
923
924           /* Add current string to the continuous space containing all
925              strings.  */
926           obstack_grow0 (&string_pool, message_run->message,
927                          strlen (message_run->message));
928
929           message_run = message_run->next;
930         }
931
932       set_run = set_run->next;
933     }
934   strings_size = obstack_object_size (&string_pool);
935   strings = obstack_finish (&string_pool);
936
937   /* Compute ARRAY2 by changing the byte order.  */
938   for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt)
939     array2[cnt] = SWAPU32 (array1[cnt]);
940
941   /* Now we can write out the whole data.  */
942   if (strcmp (output_name, "-") == 0
943       || strcmp (output_name, "/dev/stdout") == 0)
944     fd = STDOUT_FILENO;
945   else
946     {
947       fd = creat (output_name, 0666);
948       if (fd < 0)
949         error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"),
950                output_name);
951     }
952
953   /* Write out header.  */
954   write (fd, &obj, sizeof (obj));
955
956   /* We always write out the little endian version of the index
957      arrays.  */
958 #if __BYTE_ORDER == __LITTLE_ENDIAN
959   write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
960   write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
961 #elif __BYTE_ORDER == __BIG_ENDIAN
962   write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
963   write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
964 #else
965 # error Cannot handle __BYTE_ORDER byte order
966 #endif
967
968   /* Finally write the strings.  */
969   write (fd, strings, strings_size);
970
971   if (fd != STDOUT_FILENO)
972     close (fd);
973
974   /* If requested now write out the header file.  */
975   if (header_name != NULL)
976     {
977       int first = 1;
978       FILE *fp;
979
980       /* Open output file.  "-" or "/dev/stdout" means write to
981          standard output.  */
982       if (strcmp (header_name, "-") == 0
983           || strcmp (header_name, "/dev/stdout") == 0)
984         fp = stdout;
985       else
986         {
987           fp = fopen (header_name, "w");
988           if (fp == NULL)
989             error (EXIT_FAILURE, errno,
990                    gettext ("cannot open output file `%s'"), header_name);
991         }
992
993       /* Iterate over all sets and all messages.  */
994       set_run = catalog->all_sets;
995       while (set_run != NULL)
996         {
997           struct message_list *message_run;
998
999           /* If the current message set has a symbolic name write this
1000              out first.  */
1001           if (set_run->symbol != NULL)
1002             fprintf (fp, "%s#define %sSet %#x\t/* %s:%Zu */\n",
1003                      first ? "" : "\n", set_run->symbol, set_run->number - 1,
1004                      set_run->fname, set_run->line);
1005           first = 0;
1006
1007           message_run = set_run->messages;
1008           while (message_run != NULL)
1009             {
1010               /* If the current message has a symbolic name write
1011                  #define out.  But we have to take care for the set
1012                  not having a symbolic name.  */
1013               if (message_run->symbol != NULL)
1014                 {
1015                   if (set_run->symbol == NULL)
1016                     fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu */\n",
1017                              set_run->number, message_run->symbol,
1018                              message_run->number, message_run->fname,
1019                              message_run->line);
1020                   else
1021                     fprintf (fp, "#define %s%s %#x\t/* %s:%Zu */\n",
1022                              set_run->symbol, message_run->symbol,
1023                              message_run->number, message_run->fname,
1024                              message_run->line);
1025                 }
1026
1027               message_run = message_run->next;
1028             }
1029
1030           set_run = set_run->next;
1031         }
1032
1033       if (fp != stdout)
1034         fclose (fp);
1035     }
1036 }
1037
1038
1039 static struct set_list *
1040 find_set (struct catalog *current, int number)
1041 {
1042   struct set_list *result = current->all_sets;
1043
1044   /* We must avoid set number 0 because a set of this number signals
1045      in the tables that the entry is not occupied.  */
1046   ++number;
1047
1048   while (result != NULL)
1049     if (result->number == number)
1050       return result;
1051     else
1052       result = result->next;
1053
1054   /* Prepare new message set.  */
1055   result = (struct set_list *) xcalloc (1, sizeof (*result));
1056   result->number = number;
1057   result->next = current->all_sets;
1058   current->all_sets = result;
1059
1060   return result;
1061 }
1062
1063
1064 /* Normalize given string *in*place* by processing escape sequences
1065    and quote characters.  */
1066 static void
1067 normalize_line (const char *fname, size_t line, iconv_t cd, wchar_t *string,
1068                 wchar_t quote_char, wchar_t escape_char)
1069 {
1070   int is_quoted;
1071   wchar_t *rp = string;
1072   wchar_t *wp = string;
1073
1074   if (quote_char != L'\0' && *rp == quote_char)
1075     {
1076       is_quoted = 1;
1077       ++rp;
1078     }
1079   else
1080     is_quoted = 0;
1081
1082   while (*rp != L'\0')
1083     if (*rp == quote_char)
1084       /* We simply end the string when we find the first time an
1085          not-escaped quote character.  */
1086         break;
1087     else if (*rp == escape_char)
1088       {
1089         ++rp;
1090         if (quote_char != L'\0' && *rp == quote_char)
1091           /* This is an extension to XPG.  */
1092           *wp++ = *rp++;
1093         else
1094           /* Recognize escape sequences.  */
1095           switch (*rp)
1096             {
1097             case L'n':
1098               *wp++ = L'\n';
1099               ++rp;
1100               break;
1101             case L't':
1102               *wp++ = L'\t';
1103               ++rp;
1104               break;
1105             case L'v':
1106               *wp++ = L'\v';
1107               ++rp;
1108               break;
1109             case L'b':
1110               *wp++ = L'\b';
1111               ++rp;
1112               break;
1113             case L'r':
1114               *wp++ = L'\r';
1115               ++rp;
1116               break;
1117             case L'f':
1118               *wp++ = L'\f';
1119               ++rp;
1120               break;
1121             case L'0' ... L'7':
1122               {
1123                 int number;
1124                 char cbuf[2];
1125                 char *cbufptr;
1126                 size_t cbufin;
1127                 wchar_t wcbuf[2];
1128                 char *wcbufptr;
1129                 size_t wcbufin;
1130
1131                 number = *rp++ - L'0';
1132                 while (number <= (255 / 8) && *rp >= L'0' && *rp <= L'7')
1133                   {
1134                     number *= 8;
1135                     number += *rp++ - L'0';
1136                   }
1137
1138                 cbuf[0] = (char) number;
1139                 cbuf[1] = '\0';
1140                 cbufptr = cbuf;
1141                 cbufin = 2;
1142
1143                 wcbufptr = (char *) wcbuf;
1144                 wcbufin = sizeof (wcbuf);
1145
1146                 /* Flush the state.  */
1147                 iconv (cd, NULL, NULL, NULL, NULL);
1148
1149                 iconv (cd, &cbufptr, &cbufin, &wcbufptr, &wcbufin);
1150                 if (cbufptr != &cbuf[2] || (wchar_t *) wcbufptr != &wcbuf[2])
1151                   error_at_line (0, 0, fname, line,
1152                                  gettext ("invalid escape sequence"));
1153                 else
1154                   *wp++ = wcbuf[0];
1155               }
1156               break;
1157             default:
1158               if (*rp == escape_char)
1159                 {
1160                   *wp++ = escape_char;
1161                   ++rp;
1162                 }
1163               else
1164                 /* Simply ignore the backslash character.  */;
1165               break;
1166             }
1167       }
1168     else
1169       *wp++ = *rp++;
1170
1171   /* If we saw a quote character at the beginning we expect another
1172      one at the end.  */
1173   if (is_quoted && *rp != quote_char)
1174     error_at_line (0, 0, fname, line, gettext ("unterminated message"));
1175
1176   /* Terminate string.  */
1177   *wp = L'\0';
1178   return;
1179 }
1180
1181
1182 static void
1183 read_old (struct catalog *catalog, const char *file_name)
1184 {
1185   struct catalog_info old_cat_obj;
1186   struct set_list *set = NULL;
1187   int last_set = -1;
1188   size_t cnt;
1189
1190   /* Try to open catalog, but don't look through the NLSPATH.  */
1191   if (__open_catalog (file_name, NULL, NULL, &old_cat_obj) != 0)
1192     {
1193       if (errno == ENOENT)
1194         /* No problem, the catalog simply does not exist.  */
1195         return;
1196       else
1197         error (EXIT_FAILURE, errno,
1198                gettext ("while opening old catalog file"));
1199     }
1200
1201   /* OK, we have the catalog loaded.  Now read all messages and merge
1202      them.  When set and message number clash for any message the new
1203      one is used.  If the new one is empty it indicates that the
1204      message should be deleted.  */
1205   for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
1206     {
1207       struct message_list *message, *last;
1208
1209       if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0)
1210         /* No message in this slot.  */
1211         continue;
1212
1213       if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (uint32_t) last_set)
1214         {
1215           last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1;
1216           set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1);
1217         }
1218
1219       last = NULL;
1220       message = set->messages;
1221       while (message != NULL)
1222         {
1223           if ((uint32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
1224             break;
1225           last = message;
1226           message = message->next;
1227         }
1228
1229       if (message == NULL
1230           || (uint32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
1231         {
1232           /* We have found a message which is not yet in the catalog.
1233              Insert it at the right position.  */
1234           struct message_list *newp;
1235
1236           newp = (struct message_list *) xmalloc (sizeof(*newp));
1237           newp->number = old_cat_obj.name_ptr[cnt * 3 + 1];
1238           newp->message =
1239             &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]];
1240           newp->fname = NULL;
1241           newp->line = 0;
1242           newp->symbol = NULL;
1243           newp->next = message;
1244
1245           if (last == NULL)
1246             set->messages = newp;
1247           else
1248             last->next = newp;
1249
1250           ++catalog->total_messages;
1251         }
1252       else if (*message->message == '\0')
1253         {
1254           /* The new empty message has overridden the old one thus
1255              "deleting" it as required.  Now remove the empty remains. */
1256           if (last == NULL)
1257             set->messages = message->next;
1258           else
1259             last->next = message->next;
1260         }
1261     }
1262 }
1263
1264
1265 static int
1266 open_conversion (const char *codeset, iconv_t *cd_towcp, iconv_t *cd_tombp,
1267                  wchar_t *escape_charp)
1268 {
1269   char buf[2];
1270   char *bufptr;
1271   size_t bufsize;
1272   wchar_t wbuf[2];
1273   char *wbufptr;
1274   size_t wbufsize;
1275
1276   /* If the input file does not specify the codeset use the locale's.  */
1277   if (codeset == NULL)
1278     {
1279       setlocale (LC_ALL, "");
1280       codeset = nl_langinfo (CODESET);
1281       setlocale (LC_ALL, "C");
1282     }
1283
1284   /* Get the conversion modules.  */
1285   *cd_towcp = iconv_open ("WCHAR_T", codeset);
1286   *cd_tombp = iconv_open (codeset, "WCHAR_T");
1287   if (*cd_towcp == (iconv_t) -1 || *cd_tombp == (iconv_t) -1)
1288     {
1289       error (0, 0, gettext ("conversion modules not available"));
1290       if (*cd_towcp != (iconv_t) -1)
1291         iconv_close (*cd_towcp);
1292
1293       return 1;
1294     }
1295
1296   /* One special case for historical reasons is the backslash
1297      character.  In some codesets the byte value 0x5c is not mapped to
1298      U005c in Unicode.  These charsets then don't have a backslash
1299      character at all.  Therefore we have to live with whatever the
1300      codeset provides and recognize, instead of the U005c, the character
1301      the byte value 0x5c is mapped to.  */
1302   buf[0] = '\\';
1303   buf[1] = '\0';
1304   bufptr = buf;
1305   bufsize = 2;
1306
1307   wbufptr = (char *) wbuf;
1308   wbufsize = sizeof (wbuf);
1309
1310   iconv (*cd_towcp, &bufptr, &bufsize, &wbufptr, &wbufsize);
1311   if (bufsize != 0 || wbufsize != 0)
1312     {
1313       /* Something went wrong, we couldn't convert the byte 0x5c.  Go
1314          on with using U005c.  */
1315       error (0, 0, gettext ("cannot determine escape character"));
1316       *escape_charp = L'\\';
1317     }
1318   else
1319     *escape_charp = wbuf[0];
1320
1321   return 0;
1322 }