update from main archive 960921
[glibc.git] / catgets / gencat.c
blob55fea2ef4fad8b72b6f05744ab91f82bbdbdf211
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 fprintf (stderr, "%s - GNU %s %s\n", program_invocation_name,
169 PACKAGE, VERSION);
170 exit (EXIT_SUCCESS);
173 /* Help is requested. */
174 if (do_help)
175 usage (EXIT_SUCCESS);
177 /* Determine output file. */
178 if (output_name == NULL)
179 output_name = optind < argc ? argv[optind++] : "-";
181 /* Process all input files. */
182 setlocale (LC_CTYPE, "C");
183 if (optind < argc)
185 result = read_input_file (result, argv[optind]);
186 while (++optind < argc);
187 else
188 result = read_input_file (NULL, "-");
190 /* Write out the result. */
191 if (result != NULL)
192 write_out (result, output_name, header_name);
194 exit (EXIT_SUCCESS);
198 static void
199 usage (int status)
201 if (status != EXIT_SUCCESS)
202 fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
203 program_invocation_name);
204 else
206 printf(gettext ("\
207 Usage: %s [OPTION]... -o OUTPUT-FILE [INPUT-FILE]...\n\
208 %s [OPTION]... [OUTPUT-FILE [INPUT-FILE]...]\n\
209 Mandatory arguments to long options are mandatory for short options too.\n\
210 -H, --header create C header file containing symbol definitions\n\
211 -h, --help display this help and exit\n\
212 --new do not use existing catalog, force new output file\n\
213 -o, --output=NAME write output to file NAME\n\
214 -V, --version output version information and exit\n\
215 If INPUT-FILE is -, input is read from standard input. If OUTPUT-FILE\n\
216 is -, output is written to standard output.\n"),
217 program_invocation_name, program_invocation_name);
218 printf (gettext ("Report bugs to <bug-glibc@prep.ai.mit.edu>.\n"));
221 exit (status);
225 /* The address of this function will be assigned to the hook in the
226 error functions. */
227 static void
228 error_print ()
230 /* We don't want the program name to be printed in messages. Emacs'
231 compile.el does not like this. */
235 static struct catalog *
236 read_input_file (struct catalog *current, const char *fname)
238 FILE *fp;
239 char *buf;
240 size_t len;
241 size_t line_number;
243 if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
245 fp = stdin;
246 fname = gettext ("*standard input*");
248 else
249 fp = fopen (fname, "r");
250 if (fp == NULL)
252 error (0, errno, gettext ("cannot open input file `%s'"), fname);
253 return current;
256 /* If we haven't seen anything yet, allocate result structure. */
257 if (current == NULL)
259 current = (struct catalog *) xmalloc (sizeof (*current));
261 current->all_sets = NULL;
262 current->total_messages = 0;
263 current->last_set = 0;
264 current->current_set = find_set (current, NL_SETD);
266 #define obstack_chunk_alloc xmalloc
267 #define obstack_chunk_free free
268 obstack_init (&current->mem_pool);
271 buf = NULL;
272 len = 0;
273 line_number = 0;
274 while (!feof (fp))
276 int continued;
277 int used;
278 size_t start_line = line_number + 1;
279 char *this_line;
283 int act_len;
285 act_len = getline (&buf, &len, fp);
286 if (act_len <= 0)
287 break;
288 ++line_number;
290 /* It the line continued? */
291 if (buf[act_len - 1] == '\n')
293 --act_len;
294 continued = buf[act_len - 1] == '\\';
295 if (continued)
296 --act_len;
298 else
299 continued = 0;
301 /* Append to currently selected line. */
302 obstack_grow (&current->mem_pool, buf, act_len);
304 while (continued);
306 obstack_1grow (&current->mem_pool, '\0');
307 this_line = (char *) obstack_finish (&current->mem_pool);
309 used = 0;
310 if (this_line[0] == '$')
312 if (isspace (this_line[1]))
313 /* This is a comment line. Do nothing. */;
314 else if (strncmp (&this_line[1], "set", 3) == 0)
316 int cnt = sizeof ("cnt");
317 int set_number;
318 const char *symbol = NULL;
319 while (isspace (this_line[cnt]))
320 ++cnt;
322 if (isdigit (this_line[cnt]))
324 set_number = atol (&this_line[cnt]);
326 /* If the given number for the character set is
327 higher than any we used for symbolic set names
328 avoid clashing by using only higher numbers for
329 the following symbolic definitions. */
330 if (set_number > current->last_set)
331 current->last_set = set_number;
333 else
335 /* See whether it is a reasonable identifier. */
336 int start = cnt;
337 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
338 ++cnt;
340 if (cnt == start)
342 /* No correct character found. */
343 error_at_line (0, 0, fname, start_line,
344 gettext ("illegal set number"));
345 set_number = 0;
347 else
349 /* We have found seomthing which looks like a
350 correct identifier. */
351 struct set_list *runp;
353 this_line[cnt] = '\0';
354 used = 1;
355 symbol = &this_line[start];
357 /* Test whether the identifier was already used. */
358 runp = current->all_sets;
359 while (runp != 0)
360 if (runp->symbol != NULL
361 && strcmp (runp->symbol, symbol) == 0)
362 break;
363 else
364 runp = runp->next;
366 if (runp != NULL)
368 /* We cannot allow duplicate identifiers for
369 message sets. */
370 error_at_line (0, 0, fname, start_line,
371 gettext ("duplicate set definition"));
372 error_at_line (0, 0, runp->fname, runp->line,
373 gettext ("\
374 this is the first definition"));
375 set_number = 0;
377 else
378 /* Allocate next free message set for identifier. */
379 set_number = ++current->last_set;
383 if (set_number != 0)
385 /* We found a legal set number. */
386 current->current_set = find_set (current, set_number);
387 if (symbol != NULL)
388 used = 1;
389 current->current_set->symbol = symbol;
390 current->current_set->fname = fname;
391 current->current_set->line = start_line;
394 else if (strncmp (&this_line[1], "delset", 6) == 0)
396 int cnt = sizeof ("delset");
397 size_t set_number;
398 while (isspace (this_line[cnt]))
399 ++cnt;
401 if (isdigit (this_line[cnt]))
403 size_t set_number = atol (&this_line[cnt]);
404 struct set_list *set;
406 /* Mark the message set with the given number as
407 deleted. */
408 set = find_set (current, set_number);
409 set->deleted = 1;
411 else
413 /* See whether it is a reasonable identifier. */
414 int start = cnt;
415 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
416 ++cnt;
418 if (cnt == start)
420 error_at_line (0, 0, fname, start_line,
421 gettext ("illegal set number"));
422 set_number = 0;
424 else
426 const char *symbol;
427 struct set_list *runp;
429 this_line[cnt] = '\0';
430 used = 1;
431 symbol = &this_line[start];
433 /* We have a symbolic set name. This name must
434 appear somewhere else in the catalogs read so
435 far. */
436 set_number = 0;
437 for (runp = current->all_sets; runp != NULL;
438 runp = runp->next)
440 if (strcmp (runp->symbol, symbol) == 0)
442 runp->deleted = 1;
443 break;
446 if (runp == NULL)
447 /* Name does not exist before. */
448 error_at_line (0, 0, fname, start_line,
449 gettext ("unknown set `%s'"), symbol);
453 else if (strncmp (&this_line[1], "quote", 5) == 0)
455 int cnt = sizeof ("quote");
456 while (isspace (this_line[cnt]))
457 ++cnt;
458 /* Yes, the quote char can be '\0'; this means no quote
459 char. */
460 current->quote_char = this_line[cnt];
462 else
464 int cnt;
465 cnt = 2;
466 while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
467 ++cnt;
468 this_line[cnt] = '\0';
469 error_at_line (0, 0, fname, start_line,
470 gettext ("unknown directive `%s': line ignored"),
471 &this_line[1]);
474 else if (isalnum (this_line[0]) || this_line[0] == '_')
476 const char *ident = this_line;
477 int message_number;
480 ++this_line;
481 while (this_line[0] != '\0' && !isspace (this_line[0]));;
482 this_line[0] = '\0'; /* Terminate the identifier. */
485 ++this_line;
486 while (isspace (this_line[0]));
487 /* Now we found the beginning of the message itself. */
489 if (isdigit (ident[0]))
491 struct message_list *runp;
493 message_number = atoi (ident);
495 /* Find location to insert the new message. */
496 runp = current->current_set->messages;
497 while (runp != NULL)
498 if (runp->number == message_number)
499 break;
500 else
501 runp = runp->next;
502 if (runp != NULL)
504 /* Oh, oh. There is already a message with this
505 number is the message set. */
506 error_at_line (0, 0, fname, start_line,
507 gettext ("duplicated message number"));
508 error_at_line (0, 0, runp->fname, runp->line,
509 gettext ("this is the first definition"));
510 message_number = 0;
512 ident = NULL; /* We don't have a symbol. */
514 if (message_number != 0
515 && message_number > current->current_set->last_message)
516 current->current_set->last_message = message_number;
518 else if (ident[0] != '\0')
520 struct message_list *runp;
521 runp = current->current_set->messages;
523 /* Test whether the symbolic name was not used for
524 another message in this message set. */
525 while (runp != NULL)
526 if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
527 break;
528 else
529 runp = runp->next;
530 if (runp != NULL)
532 /* The name is already used. */
533 error_at_line (0, 0, fname, start_line,
534 gettext ("duplicated message identifier"));
535 error_at_line (0, 0, runp->fname, runp->line,
536 gettext ("this is the first definition"));
537 message_number = 0;
539 else
540 /* Give the message the next unused number. */
541 message_number = ++current->current_set->last_message;
543 else
544 message_number = 0;
546 if (message_number != 0)
548 struct message_list *newp;
550 used = 1; /* Yes, we use the line. */
552 /* Strip quote characters, change escape sequences into
553 correct characters etc. */
554 normalize_line (fname, start_line, this_line,
555 current->quote_char);
557 newp = (struct message_list *) xmalloc (sizeof (*newp));
558 newp->number = message_number;
559 newp->message = this_line;
560 /* Remember symbolic name; is NULL if no is given. */
561 newp->symbol = ident;
562 /* Remember where we found the character. */
563 newp->fname = fname;
564 newp->line = start_line;
566 /* Find place to insert to message. We keep them in a
567 sorted single linked list. */
568 if (current->current_set->messages == NULL
569 || current->current_set->messages->number > message_number)
571 newp->next = current->current_set->messages;
572 current->current_set->messages = newp;
574 else
576 struct message_list *runp;
577 runp = current->current_set->messages;
578 while (runp->next != NULL)
579 if (runp->next->number > message_number)
580 break;
581 else
582 runp = runp->next;
583 newp->next = runp->next;
584 runp->next = newp;
587 ++current->total_messages;
589 else
591 size_t cnt;
593 cnt = 0;
594 /* See whether we have any non-white space character in this
595 line. */
596 while (this_line[cnt] != '\0' && isspace (this_line[cnt]))
597 ++cnt;
599 if (this_line[cnt] != '\0')
600 /* Yes, some unknown characters found. */
601 error_at_line (0, 0, fname, start_line,
602 gettext ("malformed line ignored"));
605 /* We can save the memory for the line if it was not used. */
606 if (!used)
607 obstack_free (&current->mem_pool, this_line);
610 if (fp != stdin)
611 fclose (fp);
612 return current;
616 static void
617 write_out (struct catalog *catalog, const char *output_name,
618 const char *header_name)
620 /* Computing the "optimal" size. */
621 struct set_list *set_run;
622 size_t best_total, best_size, best_depth;
623 size_t act_size, act_depth;
624 struct catalog_obj obj;
625 struct obstack string_pool;
626 const char *strings;
627 size_t strings_size;
628 u_int32_t *array1, *array2;
629 size_t cnt;
630 int fd;
632 /* If not otherwise told try to read file with existing
633 translations. */
634 if (!force_new)
635 read_old (catalog, output_name);
637 /* Initialize best_size with a very high value. */
638 best_total = best_size = best_depth = UINT_MAX;
640 /* We need some start size for testing. Let's start with
641 TOTAL_MESSAGES / 5, which theoretically provides a mean depth of
642 5. */
643 act_size = 1 + catalog->total_messages / 5;
645 /* We determine the size of a hash table here. Because the message
646 numbers can be chosen arbitrary by the programmer we cannot use
647 the simple method of accessing the array using the message
648 number. The algorithm is based on the trivial hash function
649 NUMBER % TABLE_SIZE, where collisions are stored in a second
650 dimension up to TABLE_DEPTH. We here compute TABLE_SIZE so that
651 the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal. */
652 while (act_size <= best_total)
654 size_t deep[act_size];
656 act_depth = 1;
657 memset (deep, '\0', act_size * sizeof (size_t));
658 set_run = catalog->all_sets;
659 while (set_run != NULL)
661 struct message_list *message_run;
663 message_run = set_run->messages;
664 while (message_run != NULL)
666 size_t idx = (message_run->number * set_run->number) % act_size;
668 ++deep[idx];
669 if (deep[idx] > act_depth)
671 act_depth = deep[idx];
672 if (act_depth * act_size > best_total)
673 break;
675 message_run = message_run->next;
677 set_run = set_run->next;
680 if (act_depth * act_size <= best_total)
682 /* We have found a better solution. */
683 best_total = act_depth * act_size;
684 best_size = act_size;
685 best_depth = act_depth;
688 ++act_size;
691 /* let's be prepared for an empty message file. */
692 if (best_size == UINT_MAX)
694 best_size = 1;
695 best_depth = 1;
698 /* OK, now we have the size we will use. Fill in the header, build
699 the table and the second one with swapped byte order. */
700 obj.magic = CATGETS_MAGIC;
701 obj.plane_size = best_size;
702 obj.plane_depth = best_depth;
704 /* Allocate room for all needed arrays. */
705 array1 =
706 (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
707 memset (array1, '\0', best_size * best_depth * sizeof (u_int32_t) * 3);
708 array2
709 = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
710 obstack_init (&string_pool);
712 set_run = catalog->all_sets;
713 while (set_run != NULL)
715 struct message_list *message_run;
717 message_run = set_run->messages;
718 while (message_run != NULL)
720 size_t idx = (((message_run->number * set_run->number) % best_size)
721 * 3);
722 /* Determine collision depth. */
723 while (array1[idx] != 0)
724 idx += best_size * 3;
726 /* Store set number, message number and pointer into string
727 space, relative to the first string. */
728 array1[idx + 0] = set_run->number;
729 array1[idx + 1] = message_run->number;
730 array1[idx + 2] = obstack_object_size (&string_pool);
732 /* Add current string to the continuous space containing all
733 strings. */
734 obstack_grow0 (&string_pool, message_run->message,
735 strlen (message_run->message));
737 message_run = message_run->next;
740 set_run = set_run->next;
742 strings_size = obstack_object_size (&string_pool);
743 strings = obstack_finish (&string_pool);
745 /* Compute ARRAY2 by changing the byte order. */
746 for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt)
747 array2[cnt] = SWAPU32 (array1[cnt]);
749 /* Now we can write out the whole data. */
750 if (strcmp (output_name, "-") == 0
751 || strcmp (output_name, "/dev/stdout") == 0)
752 fd = STDOUT_FILENO;
753 else
755 fd = creat (output_name, 0666);
756 if (fd < 0)
757 error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"),
758 output_name);
761 /* Write out header. */
762 write (fd, &obj, sizeof (obj));
764 /* We always write out the little endian version of the index
765 arrays. */
766 #if __BYTE_ORDER == __LITTLE_ENDIAN
767 write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
768 write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
769 #elif __BYTE_ORDER == __BIG_ENDIAN
770 write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
771 write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
772 #else
773 # error Cannot handle __BYTE_ORDER byte order
774 #endif
776 /* Finally write the strings. */
777 write (fd, strings, strings_size);
779 if (fd != STDOUT_FILENO)
780 close (fd);
782 /* If requested now write out the header file. */
783 if (header_name != NULL)
785 int first = 1;
786 FILE *fp;
788 /* Open output file. "-" or "/dev/stdout" means write to
789 standard output. */
790 if (strcmp (header_name, "-") == 0
791 || strcmp (header_name, "/dev/stdout") == 0)
792 fp = stdout;
793 else
795 fp = fopen (header_name, "w");
796 if (fp == NULL)
797 error (EXIT_FAILURE, errno,
798 gettext ("cannot open output file `%s'"), header_name);
801 /* Iterate over all sets and all messages. */
802 set_run = catalog->all_sets;
803 while (set_run != NULL)
805 struct message_list *message_run;
807 /* If the current message set has a symbolic name write this
808 out first. */
809 if (set_run->symbol != NULL)
810 fprintf (fp, "%s#define %sSet %#x\t/* %s:%Zu */\n",
811 first ? "" : "\n", set_run->symbol, set_run->number - 1,
812 set_run->fname, set_run->line);
813 first = 0;
815 message_run = set_run->messages;
816 while (message_run != NULL)
818 /* If the current message has a symbolic name write
819 #define out. But we have to take care for the set
820 not having a symbolic name. */
821 if (message_run->symbol != NULL)
822 if (set_run->symbol == NULL)
823 fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu */\n",
824 set_run->number, message_run->symbol,
825 message_run->number, message_run->fname,
826 message_run->line);
827 else
828 fprintf (fp, "#define %s%s %#x\t/* %s:%Zu */\n",
829 set_run->symbol, message_run->symbol,
830 message_run->number, message_run->fname,
831 message_run->line);
833 message_run = message_run->next;
836 set_run = set_run->next;
839 if (fp != stdout)
840 fclose (fp);
845 static struct set_list *
846 find_set (struct catalog *current, int number)
848 struct set_list *result = current->all_sets;
850 /* We must avoid set number 0 because a set of this number signals
851 in the tables that the entry is not occupied. */
852 ++number;
854 while (result != NULL)
855 if (result->number == number)
856 return result;
857 else
858 result = result->next;
860 /* Prepare new message set. */
861 result = (struct set_list *) xmalloc (sizeof (*result));
862 result->number = number;
863 result->deleted = 0;
864 result->messages = NULL;
865 result->next = current->all_sets;
866 current->all_sets = result;
868 return result;
872 /* Normalize given string *in*place* by processing escape sequences
873 and quote characters. */
874 static void
875 normalize_line (const char *fname, size_t line, char *string, char quote_char)
877 int is_quoted;
878 char *rp = string;
879 char *wp = string;
881 if (quote_char != '\0' && *rp == quote_char)
883 is_quoted = 1;
884 ++rp;
886 else
887 is_quoted = 0;
889 while (*rp != '\0')
890 if (*rp == quote_char)
891 /* We simply end the string when we find the first time an
892 not-escaped quote character. */
893 break;
894 else if (*rp == '\\')
896 ++rp;
897 if (quote_char != '\0' && *rp == quote_char)
898 /* This is an extension to XPG. */
899 *wp++ = *rp++;
900 else
901 /* Recognize escape sequences. */
902 switch (*rp)
904 case 'n':
905 *wp++ = '\n';
906 ++rp;
907 break;
908 case 't':
909 *wp++ = '\t';
910 ++rp;
911 break;
912 case 'v':
913 *wp++ = '\v';
914 ++rp;
915 break;
916 case 'b':
917 *wp++ = '\b';
918 ++rp;
919 break;
920 case 'r':
921 *wp++ = '\r';
922 ++rp;
923 break;
924 case 'f':
925 *wp++ = '\f';
926 ++rp;
927 break;
928 case '\\':
929 *wp++ = '\\';
930 ++rp;
931 break;
932 case '0' ... '7':
934 int number = *rp++ - '0';
935 while (number <= (255 / 8) && *rp >= '0' && *rp <= '7')
937 number *= 8;
938 number += *rp++ - '0';
940 *wp++ = (char) number;
942 break;
943 default:
944 /* Simply ignore the backslash character. */
945 break;
948 else
949 *wp++ = *rp++;
951 /* If we saw a quote character at the beginning we expect another
952 one at the end. */
953 if (is_quoted && *rp != quote_char)
954 error (0, 0, fname, line, gettext ("unterminated message"));
956 /* Terminate string. */
957 *wp = '\0';
958 return;
962 static void
963 read_old (struct catalog *catalog, const char *file_name)
965 struct catalog_info old_cat_obj;
966 struct set_list *set = NULL;
967 int last_set = -1;
968 size_t cnt;
970 old_cat_obj.status = closed;
971 old_cat_obj.cat_name = file_name;
973 /* Try to open catalog, but don't look through the NLSPATH. */
974 __open_catalog (&old_cat_obj, 0);
976 if (old_cat_obj.status != mmaped && old_cat_obj.status != malloced)
977 if (errno == ENOENT)
978 /* No problem, the catalog simply does not exist. */
979 return;
980 else
981 error (EXIT_FAILURE, errno, gettext ("while opening old catalog file"));
983 /* OK, we have the catalog loaded. Now read all messages and merge
984 them. When set and message number clash for any message the new
985 one is used. */
986 for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
988 struct message_list *message, *last;
990 if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0)
991 /* No message in this slot. */
992 continue;
994 if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set)
996 last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1;
997 set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1);
1000 last = NULL;
1001 message = set->messages;
1002 while (message != NULL)
1004 if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
1005 break;
1006 last = message;
1007 message = message->next;
1010 if (message == NULL
1011 || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
1013 /* We have found a message which is not yet in the catalog.
1014 Insert it at the right position. */
1015 struct message_list *newp;
1017 newp = (struct message_list *) xmalloc (sizeof(*newp));
1018 newp->number = old_cat_obj.name_ptr[cnt * 3 + 1];
1019 newp->message =
1020 &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]];
1021 newp->fname = NULL;
1022 newp->line = 0;
1023 newp->symbol = NULL;
1024 newp->next = message;
1026 if (last == NULL)
1027 set->messages = newp;
1028 else
1029 last->next = newp;
1031 ++catalog->total_messages;