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