update from main archive 970121
[glibc.git] / catgets / gencat.c
blob704705783292fe798a47a1c1eaeb3b0e70a2f02b
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.
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 not,
17 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)) != -1)
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, 1997");
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 ("\
225 Report bugs using the `glibcbug' script to <bugs@gnu.ai.mit.edu>.\n"),
226 stdout);
229 exit (status);
233 /* The address of this function will be assigned to the hook in the
234 error functions. */
235 static void
236 error_print ()
238 /* We don't want the program name to be printed in messages. Emacs'
239 compile.el does not like this. */
243 static struct catalog *
244 read_input_file (struct catalog *current, const char *fname)
246 FILE *fp;
247 char *buf;
248 size_t len;
249 size_t line_number;
251 if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
253 fp = stdin;
254 fname = gettext ("*standard input*");
256 else
257 fp = fopen (fname, "r");
258 if (fp == NULL)
260 error (0, errno, gettext ("cannot open input file `%s'"), fname);
261 return current;
264 /* If we haven't seen anything yet, allocate result structure. */
265 if (current == NULL)
267 current = (struct catalog *) xmalloc (sizeof (*current));
269 current->all_sets = NULL;
270 current->total_messages = 0;
271 current->last_set = 0;
272 current->current_set = find_set (current, NL_SETD);
274 #define obstack_chunk_alloc malloc
275 #define obstack_chunk_free free
276 obstack_init (&current->mem_pool);
279 buf = NULL;
280 len = 0;
281 line_number = 0;
282 while (!feof (fp))
284 int continued;
285 int used;
286 size_t start_line = line_number + 1;
287 char *this_line;
291 int act_len;
293 act_len = getline (&buf, &len, fp);
294 if (act_len <= 0)
295 break;
296 ++line_number;
298 /* It the line continued? */
299 if (buf[act_len - 1] == '\n')
301 --act_len;
302 continued = buf[act_len - 1] == '\\';
303 if (continued)
304 --act_len;
306 else
307 continued = 0;
309 /* Append to currently selected line. */
310 obstack_grow (&current->mem_pool, buf, act_len);
312 while (continued);
314 obstack_1grow (&current->mem_pool, '\0');
315 this_line = (char *) obstack_finish (&current->mem_pool);
317 used = 0;
318 if (this_line[0] == '$')
320 if (isspace (this_line[1]))
321 /* This is a comment line. Do nothing. */;
322 else if (strncmp (&this_line[1], "set", 3) == 0)
324 int cnt = sizeof ("cnt");
325 int set_number;
326 const char *symbol = NULL;
327 while (isspace (this_line[cnt]))
328 ++cnt;
330 if (isdigit (this_line[cnt]))
332 set_number = atol (&this_line[cnt]);
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;
341 else
343 /* See whether it is a reasonable identifier. */
344 int start = cnt;
345 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
346 ++cnt;
348 if (cnt == start)
350 /* No correct character found. */
351 error_at_line (0, 0, fname, start_line,
352 gettext ("illegal set number"));
353 set_number = 0;
355 else
357 /* We have found seomthing that looks like a
358 correct identifier. */
359 struct set_list *runp;
361 this_line[cnt] = '\0';
362 used = 1;
363 symbol = &this_line[start];
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;
374 if (runp != NULL)
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;
385 else
386 /* Allocate next free message set for identifier. */
387 set_number = ++current->last_set;
391 if (set_number != 0)
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;
402 else if (strncmp (&this_line[1], "delset", 6) == 0)
404 int cnt = sizeof ("delset");
405 size_t set_number;
406 while (isspace (this_line[cnt]))
407 ++cnt;
409 if (isdigit (this_line[cnt]))
411 size_t set_number = atol (&this_line[cnt]);
412 struct set_list *set;
414 /* Mark the message set with the given number as
415 deleted. */
416 set = find_set (current, set_number);
417 set->deleted = 1;
419 else
421 /* See whether it is a reasonable identifier. */
422 int start = cnt;
423 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
424 ++cnt;
426 if (cnt == start)
428 error_at_line (0, 0, fname, start_line,
429 gettext ("illegal set number"));
430 set_number = 0;
432 else
434 const char *symbol;
435 struct set_list *runp;
437 this_line[cnt] = '\0';
438 used = 1;
439 symbol = &this_line[start];
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)
448 if (strcmp (runp->symbol, symbol) == 0)
450 runp->deleted = 1;
451 break;
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);
461 else if (strncmp (&this_line[1], "quote", 5) == 0)
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];
470 else
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]);
482 else if (isalnum (this_line[0]) || this_line[0] == '_')
484 const char *ident = this_line;
485 int message_number;
488 ++this_line;
489 while (this_line[0] != '\0' && !isspace (this_line[0]));;
490 this_line[0] = '\0'; /* Terminate the identifier. */
493 ++this_line;
494 while (isspace (this_line[0]));
495 /* Now we found the beginning of the message itself. */
497 if (isdigit (ident[0]))
499 struct message_list *runp;
501 message_number = atoi (ident);
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)
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;
520 ident = NULL; /* We don't have a symbol. */
522 if (message_number != 0
523 && message_number > current->current_set->last_message)
524 current->current_set->last_message = message_number;
526 else if (ident[0] != '\0')
528 struct message_list *runp;
529 runp = current->current_set->messages;
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)
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;
547 else
548 /* Give the message the next unused number. */
549 message_number = ++current->current_set->last_message;
551 else
552 message_number = 0;
554 if (message_number != 0)
556 struct message_list *newp;
558 used = 1; /* Yes, we use the line. */
560 /* Strip quote characters, change escape sequences into
561 correct characters etc. */
562 normalize_line (fname, start_line, this_line,
563 current->quote_char);
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;
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)
579 newp->next = current->current_set->messages;
580 current->current_set->messages = newp;
582 else
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;
595 ++current->total_messages;
597 else
599 size_t cnt;
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;
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"));
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);
618 if (fp != stdin)
619 fclose (fp);
620 return current;
624 static void
625 write_out (struct catalog *catalog, const char *output_name,
626 const char *header_name)
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;
640 /* If not otherwise told try to read file with existing
641 translations. */
642 if (!force_new)
643 read_old (catalog, output_name);
645 /* Initialize best_size with a very high value. */
646 best_total = best_size = best_depth = UINT_MAX;
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;
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)
662 size_t deep[act_size];
664 act_depth = 1;
665 memset (deep, '\0', act_size * sizeof (size_t));
666 set_run = catalog->all_sets;
667 while (set_run != NULL)
669 struct message_list *message_run;
671 message_run = set_run->messages;
672 while (message_run != NULL)
674 size_t idx = (message_run->number * set_run->number) % act_size;
676 ++deep[idx];
677 if (deep[idx] > act_depth)
679 act_depth = deep[idx];
680 if (act_depth * act_size > best_total)
681 break;
683 message_run = message_run->next;
685 set_run = set_run->next;
688 if (act_depth * act_size <= best_total)
690 /* We have found a better solution. */
691 best_total = act_depth * act_size;
692 best_size = act_size;
693 best_depth = act_depth;
696 ++act_size;
699 /* let's be prepared for an empty message file. */
700 if (best_size == UINT_MAX)
702 best_size = 1;
703 best_depth = 1;
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;
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);
720 set_run = catalog->all_sets;
721 while (set_run != NULL)
723 struct message_list *message_run;
725 message_run = set_run->messages;
726 while (message_run != NULL)
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;
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);
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));
745 message_run = message_run->next;
748 set_run = set_run->next;
750 strings_size = obstack_object_size (&string_pool);
751 strings = obstack_finish (&string_pool);
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]);
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
763 fd = creat (output_name, 0666);
764 if (fd < 0)
765 error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"),
766 output_name);
769 /* Write out header. */
770 write (fd, &obj, sizeof (obj));
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
784 /* Finally write the strings. */
785 write (fd, strings, strings_size);
787 if (fd != STDOUT_FILENO)
788 close (fd);
790 /* If requested now write out the header file. */
791 if (header_name != NULL)
793 int first = 1;
794 FILE *fp;
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
803 fp = fopen (header_name, "w");
804 if (fp == NULL)
805 error (EXIT_FAILURE, errno,
806 gettext ("cannot open output file `%s'"), header_name);
809 /* Iterate over all sets and all messages. */
810 set_run = catalog->all_sets;
811 while (set_run != NULL)
813 struct message_list *message_run;
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;
823 message_run = set_run->messages;
824 while (message_run != NULL)
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);
841 message_run = message_run->next;
844 set_run = set_run->next;
847 if (fp != stdout)
848 fclose (fp);
853 static struct set_list *
854 find_set (struct catalog *current, int number)
856 struct set_list *result = current->all_sets;
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;
862 while (result != NULL)
863 if (result->number == number)
864 return result;
865 else
866 result = result->next;
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;
876 return result;
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)
885 int is_quoted;
886 char *rp = string;
887 char *wp = string;
889 if (quote_char != '\0' && *rp == quote_char)
891 is_quoted = 1;
892 ++rp;
894 else
895 is_quoted = 0;
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 == '\\')
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)
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':
942 int number = *rp++ - '0';
943 while (number <= (255 / 8) && *rp >= '0' && *rp <= '7')
945 number *= 8;
946 number += *rp++ - '0';
948 *wp++ = (char) number;
950 break;
951 default:
952 /* Simply ignore the backslash character. */
953 break;
956 else
957 *wp++ = *rp++;
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"));
964 /* Terminate string. */
965 *wp = '\0';
966 return;
970 static void
971 read_old (struct catalog *catalog, const char *file_name)
973 struct catalog_info old_cat_obj;
974 struct set_list *set = NULL;
975 int last_set = -1;
976 size_t cnt;
978 old_cat_obj.status = closed;
979 old_cat_obj.cat_name = file_name;
981 /* Try to open catalog, but don't look through the NLSPATH. */
982 __open_catalog (&old_cat_obj, 0);
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"));
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)
996 struct message_list *message, *last;
998 if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0)
999 /* No message in this slot. */
1000 continue;
1002 if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set)
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);
1008 last = NULL;
1009 message = set->messages;
1010 while (message != NULL)
1012 if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
1013 break;
1014 last = message;
1015 message = message->next;
1018 if (message == NULL
1019 || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
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;
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;
1034 if (last == NULL)
1035 set->messages = newp;
1036 else
1037 last->next = newp;
1039 ++catalog->total_messages;