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