update from main archive 961005
[glibc.git] / catgets / gencat.c
blob9e7404295ee0d6642118c84f0dab4c978290a169
1 /* Copyright (C) 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
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.
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.
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
17 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
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>
40 #include "version.h"
42 #include "catgetsinfo.h"
45 #define SWAPU32(w) \
46 (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24))
48 struct message_list
50 int number;
51 const char *message;
53 const char *fname;
54 size_t line;
55 const char *symbol;
57 struct message_list *next;
61 struct set_list
63 int number;
64 int deleted;
65 struct message_list *messages;
66 int last_message;
68 const char *fname;
69 size_t line;
70 const char *symbol;
72 struct set_list *next;
76 struct catalog
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;
84 struct obstack mem_pool;
88 /* If non-zero force creation of new file, not using existing one. */
89 static int force_new;
91 /* Long options. */
92 static const struct option long_options[] =
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 }
102 /* Wrapper functions with error checking for standard functions. */
103 extern void *xmalloc (size_t n);
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);
119 main (int argc, char *argv[])
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;
128 /* Set program name for messages. */
129 error_print_progname = error_print;
131 /* Set locale via LC_ALL. */
132 setlocale (LC_ALL, "");
134 /* Set the text message domain. */
135 textdomain (PACKAGE);
137 /* Initialize local variables. */
138 do_help = 0;
139 do_version = 0;
140 output_name = NULL;
141 header_name = NULL;
142 result = NULL;
144 while ((opt = getopt_long (argc, argv, "hH:o:V", long_options, NULL)) != EOF)
145 switch (opt)
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);
165 /* Version information is requested. */
166 if (do_version)
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");
174 printf (_("Written by %s.\n"), "Ulrich Drepper");
176 exit (EXIT_SUCCESS);
179 /* Help is requested. */
180 if (do_help)
181 usage (EXIT_SUCCESS);
183 /* Determine output file. */
184 if (output_name == NULL)
185 output_name = optind < argc ? argv[optind++] : "-";
187 /* Process all input files. */
188 setlocale (LC_CTYPE, "C");
189 if (optind < argc)
191 result = read_input_file (result, argv[optind]);
192 while (++optind < argc);
193 else
194 result = read_input_file (NULL, "-");
196 /* Write out the result. */
197 if (result != NULL)
198 write_out (result, output_name, header_name);
200 exit (EXIT_SUCCESS);
204 static void
205 usage (int status)
207 if (status != EXIT_SUCCESS)
208 fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
209 program_invocation_name);
210 else
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 ("Report bugs to <bug-glibc@prep.ai.mit.edu>.\n"),
225 stdout);
228 exit (status);
232 /* The address of this function will be assigned to the hook in the
233 error functions. */
234 static void
235 error_print ()
237 /* We don't want the program name to be printed in messages. Emacs'
238 compile.el does not like this. */
242 static struct catalog *
243 read_input_file (struct catalog *current, const char *fname)
245 FILE *fp;
246 char *buf;
247 size_t len;
248 size_t line_number;
250 if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
252 fp = stdin;
253 fname = gettext ("*standard input*");
255 else
256 fp = fopen (fname, "r");
257 if (fp == NULL)
259 error (0, errno, gettext ("cannot open input file `%s'"), fname);
260 return current;
263 /* If we haven't seen anything yet, allocate result structure. */
264 if (current == NULL)
266 current = (struct catalog *) xmalloc (sizeof (*current));
268 current->all_sets = NULL;
269 current->total_messages = 0;
270 current->last_set = 0;
271 current->current_set = find_set (current, NL_SETD);
273 #define obstack_chunk_alloc xmalloc
274 #define obstack_chunk_free free
275 obstack_init (&current->mem_pool);
278 buf = NULL;
279 len = 0;
280 line_number = 0;
281 while (!feof (fp))
283 int continued;
284 int used;
285 size_t start_line = line_number + 1;
286 char *this_line;
290 int act_len;
292 act_len = getline (&buf, &len, fp);
293 if (act_len <= 0)
294 break;
295 ++line_number;
297 /* It the line continued? */
298 if (buf[act_len - 1] == '\n')
300 --act_len;
301 continued = buf[act_len - 1] == '\\';
302 if (continued)
303 --act_len;
305 else
306 continued = 0;
308 /* Append to currently selected line. */
309 obstack_grow (&current->mem_pool, buf, act_len);
311 while (continued);
313 obstack_1grow (&current->mem_pool, '\0');
314 this_line = (char *) obstack_finish (&current->mem_pool);
316 used = 0;
317 if (this_line[0] == '$')
319 if (isspace (this_line[1]))
320 /* This is a comment line. Do nothing. */;
321 else if (strncmp (&this_line[1], "set", 3) == 0)
323 int cnt = sizeof ("cnt");
324 int set_number;
325 const char *symbol = NULL;
326 while (isspace (this_line[cnt]))
327 ++cnt;
329 if (isdigit (this_line[cnt]))
331 set_number = atol (&this_line[cnt]);
333 /* If the given number for the character set is
334 higher than any we used for symbolic set names
335 avoid clashing by using only higher numbers for
336 the following symbolic definitions. */
337 if (set_number > current->last_set)
338 current->last_set = set_number;
340 else
342 /* See whether it is a reasonable identifier. */
343 int start = cnt;
344 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
345 ++cnt;
347 if (cnt == start)
349 /* No correct character found. */
350 error_at_line (0, 0, fname, start_line,
351 gettext ("illegal set number"));
352 set_number = 0;
354 else
356 /* We have found seomthing which looks like a
357 correct identifier. */
358 struct set_list *runp;
360 this_line[cnt] = '\0';
361 used = 1;
362 symbol = &this_line[start];
364 /* Test whether the identifier was already used. */
365 runp = current->all_sets;
366 while (runp != 0)
367 if (runp->symbol != NULL
368 && strcmp (runp->symbol, symbol) == 0)
369 break;
370 else
371 runp = runp->next;
373 if (runp != NULL)
375 /* We cannot allow duplicate identifiers for
376 message sets. */
377 error_at_line (0, 0, fname, start_line,
378 gettext ("duplicate set definition"));
379 error_at_line (0, 0, runp->fname, runp->line,
380 gettext ("\
381 this is the first definition"));
382 set_number = 0;
384 else
385 /* Allocate next free message set for identifier. */
386 set_number = ++current->last_set;
390 if (set_number != 0)
392 /* We found a legal set number. */
393 current->current_set = find_set (current, set_number);
394 if (symbol != NULL)
395 used = 1;
396 current->current_set->symbol = symbol;
397 current->current_set->fname = fname;
398 current->current_set->line = start_line;
401 else if (strncmp (&this_line[1], "delset", 6) == 0)
403 int cnt = sizeof ("delset");
404 size_t set_number;
405 while (isspace (this_line[cnt]))
406 ++cnt;
408 if (isdigit (this_line[cnt]))
410 size_t set_number = atol (&this_line[cnt]);
411 struct set_list *set;
413 /* Mark the message set with the given number as
414 deleted. */
415 set = find_set (current, set_number);
416 set->deleted = 1;
418 else
420 /* See whether it is a reasonable identifier. */
421 int start = cnt;
422 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
423 ++cnt;
425 if (cnt == start)
427 error_at_line (0, 0, fname, start_line,
428 gettext ("illegal set number"));
429 set_number = 0;
431 else
433 const char *symbol;
434 struct set_list *runp;
436 this_line[cnt] = '\0';
437 used = 1;
438 symbol = &this_line[start];
440 /* We have a symbolic set name. This name must
441 appear somewhere else in the catalogs read so
442 far. */
443 set_number = 0;
444 for (runp = current->all_sets; runp != NULL;
445 runp = runp->next)
447 if (strcmp (runp->symbol, symbol) == 0)
449 runp->deleted = 1;
450 break;
453 if (runp == NULL)
454 /* Name does not exist before. */
455 error_at_line (0, 0, fname, start_line,
456 gettext ("unknown set `%s'"), symbol);
460 else if (strncmp (&this_line[1], "quote", 5) == 0)
462 int cnt = sizeof ("quote");
463 while (isspace (this_line[cnt]))
464 ++cnt;
465 /* Yes, the quote char can be '\0'; this means no quote
466 char. */
467 current->quote_char = this_line[cnt];
469 else
471 int cnt;
472 cnt = 2;
473 while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
474 ++cnt;
475 this_line[cnt] = '\0';
476 error_at_line (0, 0, fname, start_line,
477 gettext ("unknown directive `%s': line ignored"),
478 &this_line[1]);
481 else if (isalnum (this_line[0]) || this_line[0] == '_')
483 const char *ident = this_line;
484 int message_number;
487 ++this_line;
488 while (this_line[0] != '\0' && !isspace (this_line[0]));;
489 this_line[0] = '\0'; /* Terminate the identifier. */
492 ++this_line;
493 while (isspace (this_line[0]));
494 /* Now we found the beginning of the message itself. */
496 if (isdigit (ident[0]))
498 struct message_list *runp;
500 message_number = atoi (ident);
502 /* Find location to insert the new message. */
503 runp = current->current_set->messages;
504 while (runp != NULL)
505 if (runp->number == message_number)
506 break;
507 else
508 runp = runp->next;
509 if (runp != NULL)
511 /* Oh, oh. There is already a message with this
512 number is the message set. */
513 error_at_line (0, 0, fname, start_line,
514 gettext ("duplicated message number"));
515 error_at_line (0, 0, runp->fname, runp->line,
516 gettext ("this is the first definition"));
517 message_number = 0;
519 ident = NULL; /* We don't have a symbol. */
521 if (message_number != 0
522 && message_number > current->current_set->last_message)
523 current->current_set->last_message = message_number;
525 else if (ident[0] != '\0')
527 struct message_list *runp;
528 runp = current->current_set->messages;
530 /* Test whether the symbolic name was not used for
531 another message in this message set. */
532 while (runp != NULL)
533 if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
534 break;
535 else
536 runp = runp->next;
537 if (runp != NULL)
539 /* The name is already used. */
540 error_at_line (0, 0, fname, start_line,
541 gettext ("duplicated message identifier"));
542 error_at_line (0, 0, runp->fname, runp->line,
543 gettext ("this is the first definition"));
544 message_number = 0;
546 else
547 /* Give the message the next unused number. */
548 message_number = ++current->current_set->last_message;
550 else
551 message_number = 0;
553 if (message_number != 0)
555 struct message_list *newp;
557 used = 1; /* Yes, we use the line. */
559 /* Strip quote characters, change escape sequences into
560 correct characters etc. */
561 normalize_line (fname, start_line, this_line,
562 current->quote_char);
564 newp = (struct message_list *) xmalloc (sizeof (*newp));
565 newp->number = message_number;
566 newp->message = this_line;
567 /* Remember symbolic name; is NULL if no is given. */
568 newp->symbol = ident;
569 /* Remember where we found the character. */
570 newp->fname = fname;
571 newp->line = start_line;
573 /* Find place to insert to message. We keep them in a
574 sorted single linked list. */
575 if (current->current_set->messages == NULL
576 || current->current_set->messages->number > message_number)
578 newp->next = current->current_set->messages;
579 current->current_set->messages = newp;
581 else
583 struct message_list *runp;
584 runp = current->current_set->messages;
585 while (runp->next != NULL)
586 if (runp->next->number > message_number)
587 break;
588 else
589 runp = runp->next;
590 newp->next = runp->next;
591 runp->next = newp;
594 ++current->total_messages;
596 else
598 size_t cnt;
600 cnt = 0;
601 /* See whether we have any non-white space character in this
602 line. */
603 while (this_line[cnt] != '\0' && isspace (this_line[cnt]))
604 ++cnt;
606 if (this_line[cnt] != '\0')
607 /* Yes, some unknown characters found. */
608 error_at_line (0, 0, fname, start_line,
609 gettext ("malformed line ignored"));
612 /* We can save the memory for the line if it was not used. */
613 if (!used)
614 obstack_free (&current->mem_pool, this_line);
617 if (fp != stdin)
618 fclose (fp);
619 return current;
623 static void
624 write_out (struct catalog *catalog, const char *output_name,
625 const char *header_name)
627 /* Computing the "optimal" size. */
628 struct set_list *set_run;
629 size_t best_total, best_size, best_depth;
630 size_t act_size, act_depth;
631 struct catalog_obj obj;
632 struct obstack string_pool;
633 const char *strings;
634 size_t strings_size;
635 u_int32_t *array1, *array2;
636 size_t cnt;
637 int fd;
639 /* If not otherwise told try to read file with existing
640 translations. */
641 if (!force_new)
642 read_old (catalog, output_name);
644 /* Initialize best_size with a very high value. */
645 best_total = best_size = best_depth = UINT_MAX;
647 /* We need some start size for testing. Let's start with
648 TOTAL_MESSAGES / 5, which theoretically provides a mean depth of
649 5. */
650 act_size = 1 + catalog->total_messages / 5;
652 /* We determine the size of a hash table here. Because the message
653 numbers can be chosen arbitrary by the programmer we cannot use
654 the simple method of accessing the array using the message
655 number. The algorithm is based on the trivial hash function
656 NUMBER % TABLE_SIZE, where collisions are stored in a second
657 dimension up to TABLE_DEPTH. We here compute TABLE_SIZE so that
658 the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal. */
659 while (act_size <= best_total)
661 size_t deep[act_size];
663 act_depth = 1;
664 memset (deep, '\0', act_size * sizeof (size_t));
665 set_run = catalog->all_sets;
666 while (set_run != NULL)
668 struct message_list *message_run;
670 message_run = set_run->messages;
671 while (message_run != NULL)
673 size_t idx = (message_run->number * set_run->number) % act_size;
675 ++deep[idx];
676 if (deep[idx] > act_depth)
678 act_depth = deep[idx];
679 if (act_depth * act_size > best_total)
680 break;
682 message_run = message_run->next;
684 set_run = set_run->next;
687 if (act_depth * act_size <= best_total)
689 /* We have found a better solution. */
690 best_total = act_depth * act_size;
691 best_size = act_size;
692 best_depth = act_depth;
695 ++act_size;
698 /* let's be prepared for an empty message file. */
699 if (best_size == UINT_MAX)
701 best_size = 1;
702 best_depth = 1;
705 /* OK, now we have the size we will use. Fill in the header, build
706 the table and the second one with swapped byte order. */
707 obj.magic = CATGETS_MAGIC;
708 obj.plane_size = best_size;
709 obj.plane_depth = best_depth;
711 /* Allocate room for all needed arrays. */
712 array1 =
713 (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
714 memset (array1, '\0', best_size * best_depth * sizeof (u_int32_t) * 3);
715 array2
716 = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
717 obstack_init (&string_pool);
719 set_run = catalog->all_sets;
720 while (set_run != NULL)
722 struct message_list *message_run;
724 message_run = set_run->messages;
725 while (message_run != NULL)
727 size_t idx = (((message_run->number * set_run->number) % best_size)
728 * 3);
729 /* Determine collision depth. */
730 while (array1[idx] != 0)
731 idx += best_size * 3;
733 /* Store set number, message number and pointer into string
734 space, relative to the first string. */
735 array1[idx + 0] = set_run->number;
736 array1[idx + 1] = message_run->number;
737 array1[idx + 2] = obstack_object_size (&string_pool);
739 /* Add current string to the continuous space containing all
740 strings. */
741 obstack_grow0 (&string_pool, message_run->message,
742 strlen (message_run->message));
744 message_run = message_run->next;
747 set_run = set_run->next;
749 strings_size = obstack_object_size (&string_pool);
750 strings = obstack_finish (&string_pool);
752 /* Compute ARRAY2 by changing the byte order. */
753 for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt)
754 array2[cnt] = SWAPU32 (array1[cnt]);
756 /* Now we can write out the whole data. */
757 if (strcmp (output_name, "-") == 0
758 || strcmp (output_name, "/dev/stdout") == 0)
759 fd = STDOUT_FILENO;
760 else
762 fd = creat (output_name, 0666);
763 if (fd < 0)
764 error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"),
765 output_name);
768 /* Write out header. */
769 write (fd, &obj, sizeof (obj));
771 /* We always write out the little endian version of the index
772 arrays. */
773 #if __BYTE_ORDER == __LITTLE_ENDIAN
774 write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
775 write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
776 #elif __BYTE_ORDER == __BIG_ENDIAN
777 write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
778 write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
779 #else
780 # error Cannot handle __BYTE_ORDER byte order
781 #endif
783 /* Finally write the strings. */
784 write (fd, strings, strings_size);
786 if (fd != STDOUT_FILENO)
787 close (fd);
789 /* If requested now write out the header file. */
790 if (header_name != NULL)
792 int first = 1;
793 FILE *fp;
795 /* Open output file. "-" or "/dev/stdout" means write to
796 standard output. */
797 if (strcmp (header_name, "-") == 0
798 || strcmp (header_name, "/dev/stdout") == 0)
799 fp = stdout;
800 else
802 fp = fopen (header_name, "w");
803 if (fp == NULL)
804 error (EXIT_FAILURE, errno,
805 gettext ("cannot open output file `%s'"), header_name);
808 /* Iterate over all sets and all messages. */
809 set_run = catalog->all_sets;
810 while (set_run != NULL)
812 struct message_list *message_run;
814 /* If the current message set has a symbolic name write this
815 out first. */
816 if (set_run->symbol != NULL)
817 fprintf (fp, "%s#define %sSet %#x\t/* %s:%Zu */\n",
818 first ? "" : "\n", set_run->symbol, set_run->number - 1,
819 set_run->fname, set_run->line);
820 first = 0;
822 message_run = set_run->messages;
823 while (message_run != NULL)
825 /* If the current message has a symbolic name write
826 #define out. But we have to take care for the set
827 not having a symbolic name. */
828 if (message_run->symbol != NULL)
829 if (set_run->symbol == NULL)
830 fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu */\n",
831 set_run->number, message_run->symbol,
832 message_run->number, message_run->fname,
833 message_run->line);
834 else
835 fprintf (fp, "#define %s%s %#x\t/* %s:%Zu */\n",
836 set_run->symbol, message_run->symbol,
837 message_run->number, message_run->fname,
838 message_run->line);
840 message_run = message_run->next;
843 set_run = set_run->next;
846 if (fp != stdout)
847 fclose (fp);
852 static struct set_list *
853 find_set (struct catalog *current, int number)
855 struct set_list *result = current->all_sets;
857 /* We must avoid set number 0 because a set of this number signals
858 in the tables that the entry is not occupied. */
859 ++number;
861 while (result != NULL)
862 if (result->number == number)
863 return result;
864 else
865 result = result->next;
867 /* Prepare new message set. */
868 result = (struct set_list *) xmalloc (sizeof (*result));
869 result->number = number;
870 result->deleted = 0;
871 result->messages = NULL;
872 result->next = current->all_sets;
873 current->all_sets = result;
875 return result;
879 /* Normalize given string *in*place* by processing escape sequences
880 and quote characters. */
881 static void
882 normalize_line (const char *fname, size_t line, char *string, char quote_char)
884 int is_quoted;
885 char *rp = string;
886 char *wp = string;
888 if (quote_char != '\0' && *rp == quote_char)
890 is_quoted = 1;
891 ++rp;
893 else
894 is_quoted = 0;
896 while (*rp != '\0')
897 if (*rp == quote_char)
898 /* We simply end the string when we find the first time an
899 not-escaped quote character. */
900 break;
901 else if (*rp == '\\')
903 ++rp;
904 if (quote_char != '\0' && *rp == quote_char)
905 /* This is an extension to XPG. */
906 *wp++ = *rp++;
907 else
908 /* Recognize escape sequences. */
909 switch (*rp)
911 case 'n':
912 *wp++ = '\n';
913 ++rp;
914 break;
915 case 't':
916 *wp++ = '\t';
917 ++rp;
918 break;
919 case 'v':
920 *wp++ = '\v';
921 ++rp;
922 break;
923 case 'b':
924 *wp++ = '\b';
925 ++rp;
926 break;
927 case 'r':
928 *wp++ = '\r';
929 ++rp;
930 break;
931 case 'f':
932 *wp++ = '\f';
933 ++rp;
934 break;
935 case '\\':
936 *wp++ = '\\';
937 ++rp;
938 break;
939 case '0' ... '7':
941 int number = *rp++ - '0';
942 while (number <= (255 / 8) && *rp >= '0' && *rp <= '7')
944 number *= 8;
945 number += *rp++ - '0';
947 *wp++ = (char) number;
949 break;
950 default:
951 /* Simply ignore the backslash character. */
952 break;
955 else
956 *wp++ = *rp++;
958 /* If we saw a quote character at the beginning we expect another
959 one at the end. */
960 if (is_quoted && *rp != quote_char)
961 error (0, 0, fname, line, gettext ("unterminated message"));
963 /* Terminate string. */
964 *wp = '\0';
965 return;
969 static void
970 read_old (struct catalog *catalog, const char *file_name)
972 struct catalog_info old_cat_obj;
973 struct set_list *set = NULL;
974 int last_set = -1;
975 size_t cnt;
977 old_cat_obj.status = closed;
978 old_cat_obj.cat_name = file_name;
980 /* Try to open catalog, but don't look through the NLSPATH. */
981 __open_catalog (&old_cat_obj, 0);
983 if (old_cat_obj.status != mmaped && old_cat_obj.status != malloced)
984 if (errno == ENOENT)
985 /* No problem, the catalog simply does not exist. */
986 return;
987 else
988 error (EXIT_FAILURE, errno, gettext ("while opening old catalog file"));
990 /* OK, we have the catalog loaded. Now read all messages and merge
991 them. When set and message number clash for any message the new
992 one is used. */
993 for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
995 struct message_list *message, *last;
997 if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0)
998 /* No message in this slot. */
999 continue;
1001 if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set)
1003 last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1;
1004 set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1);
1007 last = NULL;
1008 message = set->messages;
1009 while (message != NULL)
1011 if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
1012 break;
1013 last = message;
1014 message = message->next;
1017 if (message == NULL
1018 || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
1020 /* We have found a message which is not yet in the catalog.
1021 Insert it at the right position. */
1022 struct message_list *newp;
1024 newp = (struct message_list *) xmalloc (sizeof(*newp));
1025 newp->number = old_cat_obj.name_ptr[cnt * 3 + 1];
1026 newp->message =
1027 &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]];
1028 newp->fname = NULL;
1029 newp->line = 0;
1030 newp->symbol = NULL;
1031 newp->next = message;
1033 if (last == NULL)
1034 set->messages = newp;
1035 else
1036 last->next = newp;
1038 ++catalog->total_messages;