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