Update.
[glibc.git] / catgets / gencat.c
blobeaeb59ca60a788a43c8c44684cd449ba32cb9656
1 /* Copyright (C) 1996, 1997, 1998, 1999 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 <argp.h>
25 #include <ctype.h>
26 #include <endian.h>
27 #include <errno.h>
28 #include <error.h>
29 #include <fcntl.h>
30 #include <locale.h>
31 #include <libintl.h>
32 #include <limits.h>
33 #include <nl_types.h>
34 #include <obstack.h>
35 #include <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 /* Name of output file. */
92 static const char *output_name;
94 /* Name of generated C header file. */
95 static const char *header_name;
97 /* Name and version of program. */
98 static void print_version (FILE *stream, struct argp_state *state);
99 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
101 #define OPT_NEW 1
103 /* Definitions of arguments for argp functions. */
104 static const struct argp_option options[] =
106 { "header", 'H', N_("NAME"), 0,
107 N_("Create C header file NAME containing symbol definitions") },
108 { "new", OPT_NEW, NULL, 0,
109 N_("Do not use existing catalog, force new output file") },
110 { "output", 'o', N_("NAME"), 0, N_("Write output to file NAME") },
111 { NULL, 0, NULL, 0, NULL }
114 /* Short description of program. */
115 static const char doc[] = N_("Generate message catalog.\
116 \vIf INPUT-FILE is -, input is read from standard input. If OUTPUT-FILE\n\
117 is -, output is written to standard output.\n");
119 /* Strings for arguments in help texts. */
120 static const char args_doc[] = N_("\
121 -o OUTPUT-FILE [INPUT-FILE]...\n[OUTPUT-FILE [INPUT-FILE]...]");
123 /* Prototype for option handler. */
124 static error_t parse_opt (int key, char *arg, struct argp_state *state);
126 /* Function to print some extra text in the help message. */
127 static char *more_help (int key, const char *text, void *input);
129 /* Data structure to communicate with argp functions. */
130 static struct argp argp =
132 options, parse_opt, args_doc, doc, NULL, more_help
136 /* Wrapper functions with error checking for standard functions. */
137 extern void *xmalloc (size_t n);
138 extern void *xcalloc (size_t n, size_t s);
140 /* Prototypes for local functions. */
141 static void error_print (void);
142 static struct catalog *read_input_file (struct catalog *current,
143 const char *fname);
144 static void write_out (struct catalog *result, const char *output_name,
145 const char *header_name);
146 static struct set_list *find_set (struct catalog *current, int number);
147 static void normalize_line (const char *fname, size_t line, char *string,
148 char quote_char);
149 static void read_old (struct catalog *catalog, const char *file_name);
153 main (int argc, char *argv[])
155 struct catalog *result;
156 int remaining;
158 /* Set program name for messages. */
159 error_print_progname = error_print;
161 /* Set locale via LC_ALL. */
162 setlocale (LC_ALL, "");
164 /* Set the text message domain. */
165 textdomain (PACKAGE);
167 /* Initialize local variables. */
168 result = NULL;
170 /* Parse and process arguments. */
171 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
173 /* Determine output file. */
174 if (output_name == NULL)
175 output_name = remaining < argc ? argv[remaining++] : "-";
177 /* Process all input files. */
178 setlocale (LC_CTYPE, "C");
179 if (remaining < argc)
181 result = read_input_file (result, argv[remaining]);
182 while (++remaining < argc);
183 else
184 result = read_input_file (NULL, "-");
186 /* Write out the result. */
187 if (result != NULL)
188 write_out (result, output_name, header_name);
190 exit (EXIT_SUCCESS);
194 /* Handle program arguments. */
195 static error_t
196 parse_opt (int key, char *arg, struct argp_state *state)
198 switch (key)
200 case 'H':
201 header_name = arg;
202 break;
203 case OPT_NEW:
204 force_new = 1;
205 break;
206 case 'o':
207 output_name = arg;
208 break;
209 default:
210 return ARGP_ERR_UNKNOWN;
212 return 0;
216 static char *
217 more_help (int key, const char *text, void *input)
219 switch (key)
221 case ARGP_KEY_HELP_EXTRA:
222 /* We print some extra information. */
223 return strdup (gettext ("\
224 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
225 default:
226 break;
228 return (char *) text;
231 /* Print the version information. */
232 static void
233 print_version (FILE *stream, struct argp_state *state)
235 fprintf (stream, "gencat (GNU %s) %s\n", PACKAGE, VERSION);
236 fprintf (stream, gettext ("\
237 Copyright (C) %s Free Software Foundation, Inc.\n\
238 This is free software; see the source for copying conditions. There is NO\n\
239 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
240 "), "1999");
241 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
245 /* The address of this function will be assigned to the hook in the
246 error functions. */
247 static void
248 error_print ()
250 /* We don't want the program name to be printed in messages. Emacs'
251 compile.el does not like this. */
255 static struct catalog *
256 read_input_file (struct catalog *current, const char *fname)
258 FILE *fp;
259 char *buf;
260 size_t len;
261 size_t line_number;
263 if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
265 fp = stdin;
266 fname = gettext ("*standard input*");
268 else
269 fp = fopen (fname, "r");
270 if (fp == NULL)
272 error (0, errno, gettext ("cannot open input file `%s'"), fname);
273 return current;
276 /* If we haven't seen anything yet, allocate result structure. */
277 if (current == NULL)
279 current = (struct catalog *) xcalloc (1, sizeof (*current));
281 #define obstack_chunk_alloc malloc
282 #define obstack_chunk_free free
283 obstack_init (&current->mem_pool);
285 current->current_set = find_set (current, NL_SETD);
288 buf = NULL;
289 len = 0;
290 line_number = 0;
291 while (!feof (fp))
293 int continued;
294 int used;
295 size_t start_line = line_number + 1;
296 char *this_line;
300 int act_len;
302 act_len = getline (&buf, &len, fp);
303 if (act_len <= 0)
304 break;
305 ++line_number;
307 /* It the line continued? */
308 if (buf[act_len - 1] == '\n')
310 --act_len;
311 continued = buf[act_len - 1] == '\\';
312 if (continued)
313 --act_len;
315 else
316 continued = 0;
318 /* Append to currently selected line. */
319 obstack_grow (&current->mem_pool, buf, act_len);
321 while (continued);
323 obstack_1grow (&current->mem_pool, '\0');
324 this_line = (char *) obstack_finish (&current->mem_pool);
326 used = 0;
327 if (this_line[0] == '$')
329 if (isspace (this_line[1]))
330 /* This is a comment line. Do nothing. */;
331 else if (strncmp (&this_line[1], "set", 3) == 0)
333 int cnt = sizeof ("set");
334 int set_number;
335 const char *symbol = NULL;
336 while (isspace (this_line[cnt]))
337 ++cnt;
339 if (isdigit (this_line[cnt]))
341 set_number = atol (&this_line[cnt]);
343 /* If the given number for the character set is
344 higher than any we used for symbolic set names
345 avoid clashing by using only higher numbers for
346 the following symbolic definitions. */
347 if (set_number > current->last_set)
348 current->last_set = set_number;
350 else
352 /* See whether it is a reasonable identifier. */
353 int start = cnt;
354 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
355 ++cnt;
357 if (cnt == start)
359 /* No correct character found. */
360 error_at_line (0, 0, fname, start_line,
361 gettext ("illegal set number"));
362 set_number = 0;
364 else
366 /* We have found seomthing that looks like a
367 correct identifier. */
368 struct set_list *runp;
370 this_line[cnt] = '\0';
371 used = 1;
372 symbol = &this_line[start];
374 /* Test whether the identifier was already used. */
375 runp = current->all_sets;
376 while (runp != 0)
377 if (runp->symbol != NULL
378 && strcmp (runp->symbol, symbol) == 0)
379 break;
380 else
381 runp = runp->next;
383 if (runp != NULL)
385 /* We cannot allow duplicate identifiers for
386 message sets. */
387 error_at_line (0, 0, fname, start_line,
388 gettext ("duplicate set definition"));
389 error_at_line (0, 0, runp->fname, runp->line,
390 gettext ("\
391 this is the first definition"));
392 set_number = 0;
394 else
395 /* Allocate next free message set for identifier. */
396 set_number = ++current->last_set;
400 if (set_number != 0)
402 /* We found a legal set number. */
403 current->current_set = find_set (current, set_number);
404 if (symbol != NULL)
405 used = 1;
406 current->current_set->symbol = symbol;
407 current->current_set->fname = fname;
408 current->current_set->line = start_line;
411 else if (strncmp (&this_line[1], "delset", 6) == 0)
413 int cnt = sizeof ("delset");
414 size_t set_number;
415 while (isspace (this_line[cnt]))
416 ++cnt;
418 if (isdigit (this_line[cnt]))
420 size_t set_number = atol (&this_line[cnt]);
421 struct set_list *set;
423 /* Mark the message set with the given number as
424 deleted. */
425 set = find_set (current, set_number);
426 set->deleted = 1;
428 else
430 /* See whether it is a reasonable identifier. */
431 int start = cnt;
432 while (isalnum (this_line[cnt]) || this_line[cnt] == '_')
433 ++cnt;
435 if (cnt == start)
437 error_at_line (0, 0, fname, start_line,
438 gettext ("illegal set number"));
439 set_number = 0;
441 else
443 const char *symbol;
444 struct set_list *runp;
446 this_line[cnt] = '\0';
447 used = 1;
448 symbol = &this_line[start];
450 /* We have a symbolic set name. This name must
451 appear somewhere else in the catalogs read so
452 far. */
453 set_number = 0;
454 for (runp = current->all_sets; runp != NULL;
455 runp = runp->next)
457 if (strcmp (runp->symbol, symbol) == 0)
459 runp->deleted = 1;
460 break;
463 if (runp == NULL)
464 /* Name does not exist before. */
465 error_at_line (0, 0, fname, start_line,
466 gettext ("unknown set `%s'"), symbol);
470 else if (strncmp (&this_line[1], "quote", 5) == 0)
472 int cnt = sizeof ("quote");
473 while (isspace (this_line[cnt]))
474 ++cnt;
475 /* Yes, the quote char can be '\0'; this means no quote
476 char. */
477 current->quote_char = this_line[cnt];
479 else
481 int cnt;
482 cnt = 2;
483 while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
484 ++cnt;
485 this_line[cnt] = '\0';
486 error_at_line (0, 0, fname, start_line,
487 gettext ("unknown directive `%s': line ignored"),
488 &this_line[1]);
491 else if (isalnum (this_line[0]) || this_line[0] == '_')
493 const char *ident = this_line;
494 int message_number;
495 int any_space;
498 ++this_line;
499 while (this_line[0] != '\0' && !isspace (this_line[0]));
500 any_space = isspace (*this_line);
501 *this_line++ = '\0'; /* Terminate the identifier. */
503 /* Now we found the beginning of the message itself. */
505 if (isdigit (ident[0]))
507 struct message_list *runp;
508 struct message_list *lastp;
510 message_number = atoi (ident);
512 /* Find location to insert the new message. */
513 runp = current->current_set->messages;
514 lastp = NULL;
515 while (runp != NULL)
516 if (runp->number == message_number)
517 break;
518 else
520 lastp = runp;
521 runp = runp->next;
523 if (runp != NULL)
525 if (any_space)
527 /* Oh, oh. There is already a message with this
528 number in the message set. */
529 error_at_line (0, 0, fname, start_line,
530 gettext ("duplicated message number"));
531 error_at_line (0, 0, runp->fname, runp->line,
532 gettext ("this is the first definition"));
534 else
536 /* We have to remove this message. */
537 if (lastp != NULL)
538 lastp->next = runp->next;
539 else
540 current->current_set->messages = runp->next;
541 free (runp);
543 message_number = 0;
545 ident = NULL; /* We don't have a symbol. */
547 if (message_number != 0
548 && message_number > current->current_set->last_message)
549 current->current_set->last_message = message_number;
551 else if (ident[0] != '\0')
553 struct message_list *runp;
554 struct message_list *lastp;
556 /* Test whether the symbolic name was not used for
557 another message in this message set. */
558 runp = current->current_set->messages;
559 lastp = NULL;
560 while (runp != NULL)
561 if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
562 break;
563 else
564 runp = runp->next;
565 if (runp != NULL)
567 if (any_space)
569 /* The name is already used. */
570 error_at_line (0, 0, fname, start_line,
571 gettext ("\
572 duplicated message identifier"));
573 error_at_line (0, 0, runp->fname, runp->line,
574 gettext ("this is the first definition"));
576 else
578 /* We have to remove this message. */
579 if (lastp != NULL)
580 lastp->next = runp->next;
581 else
582 current->current_set->messages = runp->next;
583 free (runp);
585 message_number = 0;
587 else
588 /* Give the message the next unused number. */
589 message_number = ++current->current_set->last_message;
591 else
592 message_number = 0;
594 if (message_number != 0)
596 struct message_list *newp;
598 used = 1; /* Yes, we use the line. */
600 /* Strip quote characters, change escape sequences into
601 correct characters etc. */
602 normalize_line (fname, start_line, this_line,
603 current->quote_char);
605 newp = (struct message_list *) xmalloc (sizeof (*newp));
606 newp->number = message_number;
607 newp->message = this_line;
608 /* Remember symbolic name; is NULL if no is given. */
609 newp->symbol = ident;
610 /* Remember where we found the character. */
611 newp->fname = fname;
612 newp->line = start_line;
614 /* Find place to insert to message. We keep them in a
615 sorted single linked list. */
616 if (current->current_set->messages == NULL
617 || current->current_set->messages->number > message_number)
619 newp->next = current->current_set->messages;
620 current->current_set->messages = newp;
622 else
624 struct message_list *runp;
625 runp = current->current_set->messages;
626 while (runp->next != NULL)
627 if (runp->next->number > message_number)
628 break;
629 else
630 runp = runp->next;
631 newp->next = runp->next;
632 runp->next = newp;
635 ++current->total_messages;
637 else
639 size_t cnt;
641 cnt = 0;
642 /* See whether we have any non-white space character in this
643 line. */
644 while (this_line[cnt] != '\0' && isspace (this_line[cnt]))
645 ++cnt;
647 if (this_line[cnt] != '\0')
648 /* Yes, some unknown characters found. */
649 error_at_line (0, 0, fname, start_line,
650 gettext ("malformed line ignored"));
653 /* We can save the memory for the line if it was not used. */
654 if (!used)
655 obstack_free (&current->mem_pool, this_line);
658 if (fp != stdin)
659 fclose (fp);
660 return current;
664 static void
665 write_out (struct catalog *catalog, const char *output_name,
666 const char *header_name)
668 /* Computing the "optimal" size. */
669 struct set_list *set_run;
670 size_t best_total, best_size, best_depth;
671 size_t act_size, act_depth;
672 struct catalog_obj obj;
673 struct obstack string_pool;
674 const char *strings;
675 size_t strings_size;
676 u_int32_t *array1, *array2;
677 size_t cnt;
678 int fd;
680 /* If not otherwise told try to read file with existing
681 translations. */
682 if (!force_new)
683 read_old (catalog, output_name);
685 /* Initialize best_size with a very high value. */
686 best_total = best_size = best_depth = UINT_MAX;
688 /* We need some start size for testing. Let's start with
689 TOTAL_MESSAGES / 5, which theoretically provides a mean depth of
690 5. */
691 act_size = 1 + catalog->total_messages / 5;
693 /* We determine the size of a hash table here. Because the message
694 numbers can be chosen arbitrary by the programmer we cannot use
695 the simple method of accessing the array using the message
696 number. The algorithm is based on the trivial hash function
697 NUMBER % TABLE_SIZE, where collisions are stored in a second
698 dimension up to TABLE_DEPTH. We here compute TABLE_SIZE so that
699 the needed space (= TABLE_SIZE * TABLE_DEPTH) is minimal. */
700 while (act_size <= best_total)
702 size_t deep[act_size];
704 act_depth = 1;
705 memset (deep, '\0', act_size * sizeof (size_t));
706 set_run = catalog->all_sets;
707 while (set_run != NULL)
709 struct message_list *message_run;
711 message_run = set_run->messages;
712 while (message_run != NULL)
714 size_t idx = (message_run->number * set_run->number) % act_size;
716 ++deep[idx];
717 if (deep[idx] > act_depth)
719 act_depth = deep[idx];
720 if (act_depth * act_size > best_total)
721 break;
723 message_run = message_run->next;
725 set_run = set_run->next;
728 if (act_depth * act_size <= best_total)
730 /* We have found a better solution. */
731 best_total = act_depth * act_size;
732 best_size = act_size;
733 best_depth = act_depth;
736 ++act_size;
739 /* let's be prepared for an empty message file. */
740 if (best_size == UINT_MAX)
742 best_size = 1;
743 best_depth = 1;
746 /* OK, now we have the size we will use. Fill in the header, build
747 the table and the second one with swapped byte order. */
748 obj.magic = CATGETS_MAGIC;
749 obj.plane_size = best_size;
750 obj.plane_depth = best_depth;
752 /* Allocate room for all needed arrays. */
753 array1 =
754 (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
755 memset (array1, '\0', best_size * best_depth * sizeof (u_int32_t) * 3);
756 array2
757 = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
758 obstack_init (&string_pool);
760 set_run = catalog->all_sets;
761 while (set_run != NULL)
763 struct message_list *message_run;
765 message_run = set_run->messages;
766 while (message_run != NULL)
768 size_t idx = (((message_run->number * set_run->number) % best_size)
769 * 3);
770 /* Determine collision depth. */
771 while (array1[idx] != 0)
772 idx += best_size * 3;
774 /* Store set number, message number and pointer into string
775 space, relative to the first string. */
776 array1[idx + 0] = set_run->number;
777 array1[idx + 1] = message_run->number;
778 array1[idx + 2] = obstack_object_size (&string_pool);
780 /* Add current string to the continuous space containing all
781 strings. */
782 obstack_grow0 (&string_pool, message_run->message,
783 strlen (message_run->message));
785 message_run = message_run->next;
788 set_run = set_run->next;
790 strings_size = obstack_object_size (&string_pool);
791 strings = obstack_finish (&string_pool);
793 /* Compute ARRAY2 by changing the byte order. */
794 for (cnt = 0; cnt < best_size * best_depth * 3; ++cnt)
795 array2[cnt] = SWAPU32 (array1[cnt]);
797 /* Now we can write out the whole data. */
798 if (strcmp (output_name, "-") == 0
799 || strcmp (output_name, "/dev/stdout") == 0)
800 fd = STDOUT_FILENO;
801 else
803 fd = creat (output_name, 0666);
804 if (fd < 0)
805 error (EXIT_FAILURE, errno, gettext ("cannot open output file `%s'"),
806 output_name);
809 /* Write out header. */
810 write (fd, &obj, sizeof (obj));
812 /* We always write out the little endian version of the index
813 arrays. */
814 #if __BYTE_ORDER == __LITTLE_ENDIAN
815 write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
816 write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
817 #elif __BYTE_ORDER == __BIG_ENDIAN
818 write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
819 write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
820 #else
821 # error Cannot handle __BYTE_ORDER byte order
822 #endif
824 /* Finally write the strings. */
825 write (fd, strings, strings_size);
827 if (fd != STDOUT_FILENO)
828 close (fd);
830 /* If requested now write out the header file. */
831 if (header_name != NULL)
833 int first = 1;
834 FILE *fp;
836 /* Open output file. "-" or "/dev/stdout" means write to
837 standard output. */
838 if (strcmp (header_name, "-") == 0
839 || strcmp (header_name, "/dev/stdout") == 0)
840 fp = stdout;
841 else
843 fp = fopen (header_name, "w");
844 if (fp == NULL)
845 error (EXIT_FAILURE, errno,
846 gettext ("cannot open output file `%s'"), header_name);
849 /* Iterate over all sets and all messages. */
850 set_run = catalog->all_sets;
851 while (set_run != NULL)
853 struct message_list *message_run;
855 /* If the current message set has a symbolic name write this
856 out first. */
857 if (set_run->symbol != NULL)
858 fprintf (fp, "%s#define %sSet %#x\t/* %s:%Zu */\n",
859 first ? "" : "\n", set_run->symbol, set_run->number - 1,
860 set_run->fname, set_run->line);
861 first = 0;
863 message_run = set_run->messages;
864 while (message_run != NULL)
866 /* If the current message has a symbolic name write
867 #define out. But we have to take care for the set
868 not having a symbolic name. */
869 if (message_run->symbol != NULL)
871 if (set_run->symbol == NULL)
872 fprintf (fp, "#define AutomaticSet%d%s %#x\t/* %s:%Zu */\n",
873 set_run->number, message_run->symbol,
874 message_run->number, message_run->fname,
875 message_run->line);
876 else
877 fprintf (fp, "#define %s%s %#x\t/* %s:%Zu */\n",
878 set_run->symbol, message_run->symbol,
879 message_run->number, message_run->fname,
880 message_run->line);
883 message_run = message_run->next;
886 set_run = set_run->next;
889 if (fp != stdout)
890 fclose (fp);
895 static struct set_list *
896 find_set (struct catalog *current, int number)
898 struct set_list *result = current->all_sets;
900 /* We must avoid set number 0 because a set of this number signals
901 in the tables that the entry is not occupied. */
902 ++number;
904 while (result != NULL)
905 if (result->number == number)
906 return result;
907 else
908 result = result->next;
910 /* Prepare new message set. */
911 result = (struct set_list *) xcalloc (1, sizeof (*result));
912 result->number = number;
913 result->next = current->all_sets;
914 current->all_sets = result;
916 return result;
920 /* Normalize given string *in*place* by processing escape sequences
921 and quote characters. */
922 static void
923 normalize_line (const char *fname, size_t line, char *string, char quote_char)
925 int is_quoted;
926 char *rp = string;
927 char *wp = string;
929 if (quote_char != '\0' && *rp == quote_char)
931 is_quoted = 1;
932 ++rp;
934 else
935 is_quoted = 0;
937 while (*rp != '\0')
938 if (*rp == quote_char)
939 /* We simply end the string when we find the first time an
940 not-escaped quote character. */
941 break;
942 else if (*rp == '\\')
944 ++rp;
945 if (quote_char != '\0' && *rp == quote_char)
946 /* This is an extension to XPG. */
947 *wp++ = *rp++;
948 else
949 /* Recognize escape sequences. */
950 switch (*rp)
952 case 'n':
953 *wp++ = '\n';
954 ++rp;
955 break;
956 case 't':
957 *wp++ = '\t';
958 ++rp;
959 break;
960 case 'v':
961 *wp++ = '\v';
962 ++rp;
963 break;
964 case 'b':
965 *wp++ = '\b';
966 ++rp;
967 break;
968 case 'r':
969 *wp++ = '\r';
970 ++rp;
971 break;
972 case 'f':
973 *wp++ = '\f';
974 ++rp;
975 break;
976 case '\\':
977 *wp++ = '\\';
978 ++rp;
979 break;
980 case '0' ... '7':
982 int number = *rp++ - '0';
983 while (number <= (255 / 8) && *rp >= '0' && *rp <= '7')
985 number *= 8;
986 number += *rp++ - '0';
988 *wp++ = (char) number;
990 break;
991 default:
992 /* Simply ignore the backslash character. */
993 break;
996 else
997 *wp++ = *rp++;
999 /* If we saw a quote character at the beginning we expect another
1000 one at the end. */
1001 if (is_quoted && *rp != quote_char)
1002 error (0, 0, fname, line, gettext ("unterminated message"));
1004 /* Terminate string. */
1005 *wp = '\0';
1006 return;
1010 static void
1011 read_old (struct catalog *catalog, const char *file_name)
1013 struct catalog_info old_cat_obj;
1014 struct set_list *set = NULL;
1015 int last_set = -1;
1016 size_t cnt;
1018 old_cat_obj.status = closed;
1019 old_cat_obj.cat_name = file_name;
1020 old_cat_obj.nlspath = NULL;
1021 __libc_lock_init (old_cat_obj.lock);
1023 /* Try to open catalog, but don't look through the NLSPATH. */
1024 __open_catalog (&old_cat_obj);
1026 if (old_cat_obj.status != mmapped && old_cat_obj.status != malloced)
1028 if (errno == ENOENT)
1029 /* No problem, the catalog simply does not exist. */
1030 return;
1031 else
1032 error (EXIT_FAILURE, errno, gettext ("while opening old catalog file"));
1035 /* OK, we have the catalog loaded. Now read all messages and merge
1036 them. When set and message number clash for any message the new
1037 one is used. */
1038 for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
1040 struct message_list *message, *last;
1042 if (old_cat_obj.name_ptr[cnt * 3 + 0] == 0)
1043 /* No message in this slot. */
1044 continue;
1046 if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set)
1048 last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1;
1049 set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1);
1052 last = NULL;
1053 message = set->messages;
1054 while (message != NULL)
1056 if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
1057 break;
1058 last = message;
1059 message = message->next;
1062 if (message == NULL
1063 || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
1065 /* We have found a message which is not yet in the catalog.
1066 Insert it at the right position. */
1067 struct message_list *newp;
1069 newp = (struct message_list *) xmalloc (sizeof(*newp));
1070 newp->number = old_cat_obj.name_ptr[cnt * 3 + 1];
1071 newp->message =
1072 &old_cat_obj.strings[old_cat_obj.name_ptr[cnt * 3 + 2]];
1073 newp->fname = NULL;
1074 newp->line = 0;
1075 newp->symbol = NULL;
1076 newp->next = message;
1078 if (last == NULL)
1079 set->messages = newp;
1080 else
1081 last->next = newp;
1083 ++catalog->total_messages;