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