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