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