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