1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1990, 1991, 1994, Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 #ident "%Z%%M% %I% %E% SMI"
28
29 #include <nl_types.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <memory.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <locale.h>
40 #include <libintl.h>
41
42 #ifndef NL_MSGMAX
43 #define NL_MSGMAX 32767
44 #endif
45
46 #ifndef NL_SETMAX
47 #define NL_SETMAX 255
48 #endif
49
50 #ifndef NL_TEXTMAX
51 #define NL_TEXTMAX 2048
52 #endif
53
54 #define BS '\b'
55 #define CR '\r'
56 #define DOLLAR '$'
57 #define FF '\f'
58 #define NEWLINE '\n'
59 #define NUL '\000'
60 #define REVERSE_SOLIDUS '\\'
61 #define SPACE ' '
62 #define TAB '\t'
63 #define VTAB '\v'
64
65 #define FPRINTF (void) fprintf
66 #define FREE(x) free((char *)(x))
67 #define MALLOC(n) malloc((unsigned)(n))
68 #define MEMCPY(dst, src, n) \
69 (void) memcpy((char *)(dst), (char *)(src), (int)(n))
70 #define MEMSET(s, c, n) (void) memset((char *)(s), (int)(c), (int)(n));
71 #define MSG(n) gettext(MSG ## n)
72 #define READ(fd, p, n) read((int)(fd), (char *)(p), (unsigned)(n))
73 #define REALLOC(x, n) realloc((char *)(x), (unsigned)(n))
74
75 /* double linked list */
76 struct cat_set {
77 struct cat_set *prev;
78 struct cat_set *next;
79 int set_no;
80 struct cat_msg *first_msg;
81 };
82
83 /* double linked list */
84 struct cat_msg {
85 struct cat_msg *prev;
86 struct cat_msg *next;
87 int msg_no;
88 int msg_len;
89 char s[1];
90 };
91
92 int catfd; /* File descriptor of catalog file */
93 char *catfname; /* Catalog file name */
94 char *msgfname; /* message source file name */
95 int ateof; /* boolean indicating END-OF-FILE */
96 int lineno; /* the line number of message source file */
97 int quoting; /* boolean indicating quotes is used */
98 int quote; /* the current quote */
99 int text_len; /* message text length */
100 int text_size; /* the size of allocated text memory */
101 char *text; /* messsge text */
102
103 struct _cat_hdr hdr;
104 int current_set_no; /* the current set number */
105 struct cat_set *first_set; /* the pointer to the first set */
106 struct cat_set *current_set; /* the pointer to the current set */
107 struct cat_msg *current_msg; /* the pointer to the first message */
108
109
110 /* Error message */
111 /* 0 */
112 #define MSG0 ""
113 /* 1 */
114 #define MSG1 "usage: gencat catfile msgfile ...\n"
115 /* 2 */
116 #define MSG2 "gencat: cannot open \"%s\"\n"
117 /* 3 */
118 #define MSG3 "gencat: read error on \"%s\"\n"
119 /* 4 */
120 #define MSG4 "gencat: bad magic number (%#lx)\n"
121 /* 5 */
122 #define MSG5 "gencat: corrupt catalogue file \"%s\"\n"
123 /* 6 */
124 #define MSG6 "gencat: memory limit exceeded\n"
125 /* 7 */
126 #define MSG7 "gencat: seek error on \"%s\"\n"
127 /* 8 */
128 #define MSG8 "gencat: write error on \"%s\"\n"
129 /* 9 */
130 #define MSG9 "gencat: \"%s\", line %d: number too large (%s)\n"
131 /* 10 */
132 #define MSG10 "gencat: \"%s\", line %d: 0 is not a permissible " \
133 "message number\n"
134 /* 11 */
135 #define MSG11 "gencat: \"%s\", line %d: warning, message number %d " \
136 "exceeds limit (%d)\n"
137 /* 12 */
138 #define MSG12 "gencat: \"%s\", line %d: missing quote (%wc)\n"
139 /* 13 */
140 #define MSG13 "gencat: \"%s\", line %d: character value too large ('\\%o')\n"
141 /* 14 */
142 #define MSG14 "gencat: \"%s\", line %d: extra characters following " \
143 "message text\n"
144 /* 15 */
145 #define MSG15 "gencat: \"%s\", line %d: extra characters following " \
146 "$quote directive\n"
147 /* 16 */
148 #define MSG16 "gencat: \"%s\", line %d: no set number specified in " \
149 "$set directive\n"
150 /* 17 */
151 #define MSG17 "getcat: \"%s\", line %d: 0 is not a permissible set number\n"
152 /* 18 */
153 #define MSG18 "gencat: \"%s\", line %d: warning, set number %d " \
154 "exceeds limit (%d)\n"
155 /* 19 */
156 #define MSG19 "gencat: \"%s\", line %d: unknown directive %s\n"
157 /* 20 */
158 #define MSG20 "gencat: \"%s\", line %d: no set number specified in " \
159 "$delset directive\n"
160 /* 21 */
161 #define MSG21 "stdin"
162 /* 22 */
163 #define MSG22 "gencat: \"%s\", line %d: number or $ expected\n"
164
165 struct cat_set *
new_set(n)166 new_set(n)
167 int n;
168 {
169 struct cat_set *p;
170
171 p = (struct cat_set *) MALLOC(sizeof (struct cat_set));
172 if (p == NULL) {
173 FPRINTF(stderr, MSG(6));
174 exit(1);
175 }
176 p->next = NULL;
177 p->prev = NULL;
178 p->set_no = n;
179 p->first_msg = NULL;
180 return (p);
181 }
182
183 void
find_set(no)184 find_set(no)
185 int no;
186 {
187 struct cat_set *prev, *next;
188
189 if (current_set && current_set->set_no == no) {
190 return;
191 }
192
193 current_set_no = no;
194 current_msg = NULL;
195 /* if no set exists, create a new set */
196 if (current_set == NULL) {
197 if (first_set == NULL) {
198 current_set = first_set = new_set(no);
199 return;
200 }
201 current_set = first_set;
202 if (current_set->set_no == no)
203 return;
204 }
205
206 if (current_set->set_no > no) {
207 if (first_set->set_no > no) {
208 /* prepend a new set */
209 current_set = new_set(no);
210 current_set->next = first_set;
211 first_set->prev = current_set;
212 first_set = current_set;
213 return;
214 }
215 current_set = first_set;
216 if (current_set->set_no == no)
217 return;
218 }
219
220 /* search for the set number 'no' */
221 while (current_set->next && current_set->next->set_no < no)
222 current_set = current_set->next;
223
224 if (current_set->next && current_set->next->set_no == no) {
225 /* set number 'no' found */
226 current_set = current_set->next;
227 return;
228 }
229
230 /* If set number is not found, insert a new set in the middle */
231 prev = current_set;
232 next = current_set->next;
233 current_set = new_set(no);
234 current_set->prev = prev;
235 current_set->next = next;
236 if (prev)
237 prev->next = current_set;
238 else
239 first_set = current_set;
240 if (next)
241 next->prev = current_set;
242 }
243
244 void
delete_set(no)245 delete_set(no)
246 int no;
247 {
248 struct cat_set *prev, *next, *setp;
249 struct cat_msg *p, *q;
250
251 for (setp = first_set; setp && setp->set_no < no; setp = setp->next)
252 continue;
253
254 if (setp == NULL || setp->set_no != no) /* set not found */
255 return;
256
257 if (setp == current_set) {
258 current_set = NULL;
259 current_msg = NULL;
260 }
261
262 /* free all messages in the set */
263 for (p = setp->first_msg; p; p) {
264 q = p->next;
265 FREE(p);
266 p = q;
267 }
268
269 /* do the link operation to delete the set */
270 prev = setp->prev;
271 next = setp->next;
272 FREE(setp);
273 if (prev)
274 prev->next = next;
275 else
276 first_set = next;
277 if (next)
278 next->prev = prev;
279 }
280
281 struct cat_msg *
new_msg(no,len,text)282 new_msg(no, len, text)
283 int no;
284 int len;
285 char *text;
286 {
287 struct cat_msg *p;
288
289 p = (struct cat_msg *) MALLOC(sizeof (struct cat_msg) + len);
290 if (p == NULL) {
291 FPRINTF(stderr, MSG(6));
292 exit(1);
293 }
294 p->next = NULL;
295 p->prev = NULL;
296 p->msg_no = no;
297 p->msg_len = len;
298 MEMCPY(p->s, text, len);
299 return (p);
300 }
301
302
303 void
insert_msg(no,len,text)304 insert_msg(no, len, text)
305 int no;
306 int len;
307 char *text;
308 {
309 struct cat_msg *prev, *next;
310
311 if (current_msg == NULL) {
312 if (current_set == NULL)
313 find_set(current_set_no);
314 current_msg = current_set->first_msg;
315 if (current_msg == NULL) {
316 current_msg = new_msg(no, len, text);
317 current_set->first_msg = current_msg;
318 return;
319 }
320 }
321 if (current_msg->msg_no >= no) {
322 current_msg = current_set->first_msg;
323 if (current_msg->msg_no > no) {
324 current_msg = new_msg(no, len, text);
325 current_msg->next = current_set->first_msg;
326 current_set->first_msg->prev = current_msg;
327 current_set->first_msg = current_msg;
328 return;
329 }
330 if (current_msg->msg_no == no) {
331 current_msg = new_msg(no, len, text);
332 current_msg->next = current_set->first_msg->next;
333 if (current_set->first_msg->next)
334 current_set->first_msg->next->prev =
335 current_msg;
336 FREE(current_set->first_msg);
337 current_set->first_msg = current_msg;
338 return;
339 }
340 }
341 while (current_msg->next && current_msg->next->msg_no < no)
342 current_msg = current_msg->next;
343
344 /*
345 * if the same msg number is found, then delte the message and
346 * insert the new message. This is same as replacing message.
347 */
348 if (current_msg->next && current_msg->next->msg_no == no) {
349 current_msg = current_msg->next;
350 prev = current_msg->prev;
351 next = current_msg->next;
352 FREE(current_msg);
353 } else {
354 prev = current_msg;
355 next = current_msg->next;
356 }
357
358 current_msg = new_msg(no, len, text);
359 current_msg->prev = prev;
360 current_msg->next = next;
361 if (prev)
362 prev->next = current_msg;
363 else
364 current_set->first_msg = current_msg;
365 if (next)
366 next->prev = current_msg;
367 }
368
369 void
delete_msg(no)370 delete_msg(no)
371 int no;
372 {
373 struct cat_set *p = current_set;
374 struct cat_msg *prev, *next;
375
376 if (current_msg == NULL) {
377 if (current_set == NULL)
378 for (p = first_set; p && p->set_no < current_set_no;
379 p = p->next)
380 continue;
381 if (p == NULL || p->set_no != current_set_no)
382 return;
383 current_set = p;
384 current_msg = current_set->first_msg;
385 if (current_msg == NULL)
386 return;
387 }
388 if (current_msg->msg_no > no)
389 current_msg = current_set->first_msg;
390
391 while (current_msg && current_msg->msg_no != no)
392 current_msg = current_msg->next;
393
394 if (current_msg && current_msg->msg_no == no) {
395 prev = current_msg->prev;
396 next = current_msg->next;
397 FREE(current_msg);
398 if (prev) {
399 current_msg = prev;
400 prev->next = next;
401 } else {
402 current_set->first_msg = next;
403 current_msg = next;
404 }
405 if (next)
406 next->prev = prev;
407 }
408 }
409
410 int
read_block(fd,p,n,pathname)411 read_block(fd, p, n, pathname)
412 int fd;
413 char *p;
414 int n;
415 char *pathname;
416 {
417 int nbytes, bytes_read;
418
419 if (n == 0)
420 return (0);
421
422 nbytes = 0;
423 while (nbytes < n) {
424 bytes_read = READ(fd, p + nbytes, n - nbytes);
425 if (bytes_read < 0) {
426 if (errno != EINTR) {
427 FPRINTF(stderr, MSG(3), pathname);
428 perror("");
429 exit(1);
430 }
431 } else if (bytes_read == 0)
432 break;
433 else
434 nbytes += bytes_read;
435 }
436
437 return (nbytes);
438 }
439
440 /*
441 * Check if catalog file read is valid
442 *
443 */
444 int
cat_ok(cat)445 cat_ok(cat)
446 char *cat;
447 {
448 int i, j;
449 int nmsgs;
450 int msg_no;
451 struct _cat_msg_hdr *msg;
452 int set_no;
453 int first_msg_hdr;
454 struct _cat_set_hdr *set;
455
456 set = (struct _cat_set_hdr *) cat;
457 set_no = 0;
458 for (i = 0; i < hdr.__nsets; ++set, ++i) {
459 if (set->__set_no < set_no)
460 return (0);
461 set_no = set->__set_no;
462 nmsgs = set->__nmsgs;
463 if (nmsgs < 0)
464 return (0);
465 if (nmsgs == 0)
466 continue;
467 first_msg_hdr = set->__first_msg_hdr;
468 if (first_msg_hdr < 0)
469 return (0);
470 if (hdr.__msg_hdr_offset + (first_msg_hdr + nmsgs) *
471 _CAT_MSG_HDR_SIZE > hdr.__mem)
472 return (0);
473
474 msg = (struct _cat_msg_hdr *) (cat + hdr.__msg_hdr_offset) +
475 first_msg_hdr;
476 msg_no = 0;
477 for (j = 0; j < nmsgs; ++msg, ++j) {
478 if (msg->__msg_no < msg_no)
479 return (0);
480 msg_no = msg->__msg_no;
481 if (msg->__msg_offset < 0)
482 return (0);
483 if (hdr.__msg_text_offset + msg->__msg_offset +
484 msg->__msg_len > hdr.__mem)
485 return (0);
486 }
487 }
488
489 return (1);
490 }
491
492 /*
493 * convert a chunk of catalog file into double linked list format
494 */
495 void
initcat(cat)496 initcat(cat)
497 char *cat;
498 {
499 int i, j;
500 int nmsgs;
501 struct _cat_set_hdr *set;
502 struct _cat_msg_hdr *msg;
503
504 set = (struct _cat_set_hdr *) cat;
505 for (i = 0; i < hdr.__nsets; ++set, ++i) {
506 nmsgs = set->__nmsgs;
507 if (nmsgs == 0)
508 continue;
509 find_set(set->__set_no);
510 msg = (struct _cat_msg_hdr *) (cat + hdr.__msg_hdr_offset)
511 + set->__first_msg_hdr;
512 current_msg = current_set->first_msg;
513 for (j = 0; j < nmsgs; ++msg, ++j) {
514 insert_msg(msg->__msg_no, msg->__msg_len,
515 cat + hdr.__msg_text_offset + msg->__msg_offset);
516 }
517 }
518 }
519
520 /*
521 * read a catalog file in a chunk and convert it to double linked list.
522 */
523 void
readcat(fd,pathname)524 readcat(fd, pathname)
525 int fd;
526 char *pathname;
527 {
528 int i;
529 char *cat;
530
531 i = read_block(fd, (char *) &hdr, _CAT_HDR_SIZE, pathname);
532 if (i == 0)
533 return;
534
535 if (i >= 4 && hdr.__hdr_magic != _CAT_MAGIC) {
536 FPRINTF(stderr, MSG(4), hdr.__hdr_magic);
537 exit(1);
538 }
539 if (i < _CAT_HDR_SIZE || hdr.__nsets < 0) {
540 FPRINTF(stderr, MSG(5), pathname);
541 exit(1);
542 }
543 if (hdr.__nsets == 0)
544 return;
545
546 if (hdr.__mem < 0 ||
547 hdr.__msg_hdr_offset < 0 ||
548 hdr.__msg_text_offset < 0 ||
549 hdr.__mem < hdr.__nsets * _CAT_SET_HDR_SIZE ||
550 hdr.__mem < hdr.__msg_hdr_offset ||
551 hdr.__mem < hdr.__msg_text_offset) {
552 FPRINTF(stderr, MSG(5), pathname);
553 exit(1);
554 }
555 cat = MALLOC(hdr.__mem);
556 if (cat == NULL) {
557 FPRINTF(stderr, MSG(6));
558 exit(1);
559 }
560 i = read_block(fd, cat, hdr.__mem, pathname);
561 if (i < hdr.__mem || !cat_ok(cat)) {
562 FPRINTF(stderr, MSG(5), pathname);
563 exit(1);
564 }
565 initcat(cat);
566
567 FREE(cat);
568 }
569
570 /*
571 * Extend the memory in 1000 byte chunks whenever runs out of text space.
572 */
573 void
extend_text()574 extend_text()
575 {
576 text_size += 1000;
577 if (text)
578 text = REALLOC(text, text_size);
579 else
580 text = MALLOC(text_size);
581 if (text == NULL) {
582 FPRINTF(stderr, MSG(6));
583 exit(1);
584 }
585 }
586
587 int
get_number(fp,c)588 get_number(fp, c)
589 FILE *fp;
590 int c;
591 {
592 int i, n;
593 char *s, *t;
594
595 i = 0;
596 do {
597 while (i >= text_size)
598 extend_text();
599 text[i] = c;
600 ++i;
601 c = getc(fp);
602 }
603 while (isdigit(c));
604 (void) ungetc(c, fp);
605
606 while (i >= text_size)
607 extend_text();
608 text[i] = NUL;
609
610 for (s = text; *s == '0'; ++s)
611 continue;
612
613 n = 0;
614 for (t = s; isdigit(*t); ++t) {
615 if (n > INT_MAX / 10 ||
616 (n == INT_MAX / 10 && *t > '0' + INT_MAX % 10)) {
617 FPRINTF(stderr, MSG(9), msgfname, lineno, s);
618 exit(1);
619 }
620 n = 10 * n + (*t - '0');
621 }
622
623 return (n);
624 }
625
626 void
get_text(fp)627 get_text(fp)
628 FILE *fp;
629 {
630 int c;
631 int n;
632
633 text_len = 0;
634 c = fgetwc(fp);
635 if (quoting && c == quote) { /* quote is used */
636 c = fgetwc(fp);
637 while (c != quote) {
638 if (c == NEWLINE || c == EOF) {
639 FPRINTF(stderr, MSG(12), msgfname, lineno,
640 quote);
641 exit(1);
642 }
643 if (c == REVERSE_SOLIDUS) {
644 c = fgetwc(fp);
645 switch (c) {
646 case EOF:
647 FPRINTF(stderr, MSG(12), msgfname,
648 lineno, quote);
649 exit(1);
650 break;
651 case NEWLINE:
652 ++lineno;
653 c = fgetwc(fp);
654 continue;
655 /* NOTREACHED */
656 break;
657 case '0':
658 case '1':
659 case '2':
660 case '3':
661 case '4':
662 case '5':
663 case '6':
664 case '7':
665 n = (c - '0');
666 c = fgetwc(fp);
667 if (c >= '0' && c <= '7') {
668 n = 8 * n + (c - '0');
669 c = fgetwc(fp);
670 if (c >= '0' && c <= '7')
671 n = 8 * n + (c - '0');
672 else
673 (void) ungetwc(c, fp);
674 } else
675 (void) ungetwc(c, fp);
676 if (n > UCHAR_MAX) {
677 FPRINTF(stderr, MSG(13),
678 msgfname, lineno, n);
679 exit(1);
680 }
681 c = n;
682 break;
683
684 case 'n':
685 c = NEWLINE;
686 break;
687
688 case 't':
689 c = TAB;
690 break;
691
692 case 'v':
693 c = VTAB;
694 break;
695
696 case 'b':
697 c = BS;
698 break;
699
700 case 'r':
701 c = CR;
702 break;
703
704 case 'f':
705 c = FF;
706 break;
707 }
708 }
709 while ((text_len + (int)MB_CUR_MAX + 1) >= text_size)
710 extend_text();
711 if ((n = wctomb(&text[text_len], c)) > 0)
712 text_len += n;
713 c = fgetwc(fp);
714 }
715
716 while ((text_len + 1) >= text_size)
717 extend_text();
718 text[text_len] = '\0';
719 ++text_len;
720
721 do {
722 c = getc(fp);
723 } while (c == SPACE || c == TAB);
724 if (c == NEWLINE) {
725 ++lineno;
726 return;
727 }
728 if (c == EOF) {
729 ateof = 1;
730 return;
731 }
732 FPRINTF(stderr, MSG(14), msgfname, lineno);
733 exit(1);
734 }
735
736 while (c != NEWLINE && c != EOF) { /* quote is not used */
737 if (c == REVERSE_SOLIDUS) {
738 c = fgetwc(fp);
739 switch (c) {
740 case EOF:
741 return;
742
743 case NEWLINE:
744 ++lineno;
745 c = fgetwc(fp);
746 continue;
747
748 case '0':
749 case '1':
750 case '2':
751 case '3':
752 case '4':
753 case '5':
754 case '6':
755 case '7':
756 n = (c - '0');
757 c = fgetwc(fp);
758 if (c >= '0' && c <= '7') {
759 n = 8 * n + (c - '0');
760 c = fgetwc(fp);
761 if (c >= '0' && c <= '7')
762 n = 8 * n + (c - '0');
763 else
764 (void) ungetwc(c, fp);
765 } else
766 (void) ungetwc(c, fp);
767 if (n > UCHAR_MAX) {
768 FPRINTF(stderr, MSG(13), msgfname,
769 lineno, n);
770 exit(1);
771 }
772 c = n;
773 break;
774
775 case 'n':
776 c = NEWLINE;
777 break;
778
779 case 't':
780 c = TAB;
781 break;
782
783 case 'v':
784 c = VTAB;
785 break;
786
787 case 'b':
788 c = BS;
789 break;
790
791 case 'r':
792 c = CR;
793 break;
794
795 case 'f':
796 c = FF;
797 break;
798 }
799 }
800 while ((text_len + (int)MB_CUR_MAX + 1) >= text_size)
801 extend_text();
802 if ((n = wctomb(&text[text_len], c)) > 0)
803 text_len += n;
804 c = fgetwc(fp);
805 }
806
807 while ((text_len + 1) >= text_size)
808 extend_text();
809 text[text_len] = '\0';
810 ++text_len;
811
812 if (c == NEWLINE)
813 ++lineno;
814 else
815 ateof = 1;
816 }
817
818 /*
819 * This routine handles $ <comment>, $set, $delset, $quote
820 */
821 void
directive(fp)822 directive(fp)
823 FILE *fp;
824 {
825 int c;
826 int n;
827
828 c = fgetwc(fp);
829 if (c == SPACE || c == TAB) { /* $ <comment */
830 do {
831 c = fgetwc(fp);
832 } while (c != NEWLINE && c != EOF);
833 }
834 if (c == NEWLINE) {
835 ++lineno;
836 return;
837 }
838 if (c == EOF) {
839 ateof = 1;
840 return;
841 }
842 text_len = 1;
843 while (text_len >= text_size)
844 extend_text();
845 text[0] = DOLLAR;
846 while (isascii(c) && isalpha(c)) {
847 while ((text_len + 1) >= text_size)
848 extend_text();
849 text[text_len] = c;
850 ++text_len;
851 c = fgetwc(fp);
852 }
853
854 while ((text_len + 1) >= text_size)
855 extend_text();
856 text[text_len] = NUL;
857
858 if (strcmp(text, "$set") == 0) {
859 while (c == SPACE || c == TAB)
860 c = fgetwc(fp);
861 if (!isascii(c) || !isdigit(c)) {
862 FPRINTF(stderr, MSG(16), msgfname, lineno);
863 exit(1);
864 }
865 n = get_number(fp, c);
866 if (n == 0) {
867 FPRINTF(stderr, MSG(17), msgfname, lineno);
868 exit(1);
869 }
870 if (n > NL_SETMAX) {
871 FPRINTF(stderr, MSG(18), msgfname, lineno,
872 n, NL_SETMAX);
873 }
874 find_set(n);
875 do { /* skip comment */
876 c = getc(fp);
877 } while (c != NEWLINE && c != EOF);
878 if (c == NEWLINE)
879 ++lineno;
880 else
881 ateof = 1;
882 return;
883 } else if (strcmp(text, "$delset") == 0) {
884 while (c == SPACE || c == TAB)
885 c = fgetwc(fp);
886 if (!isascii(c) || !isdigit(c)) {
887 FPRINTF(stderr, MSG(20), msgfname, lineno);
888 exit(1);
889 }
890 n = get_number(fp, c);
891 if (n == 0) {
892 FPRINTF(stderr, MSG(17), msgfname, lineno);
893 exit(1);
894 }
895 if (n > NL_SETMAX) {
896 FPRINTF(stderr, MSG(18), msgfname, lineno,
897 n, NL_SETMAX);
898 }
899 delete_set(n);
900 do { /* skip comment */
901 c = getc(fp);
902 } while (c != NEWLINE && c != EOF);
903 if (c == NEWLINE)
904 ++lineno;
905 else
906 ateof = 1;
907 return;
908 } else if (strcmp(text, "$quote") == 0) {
909 if (c == NEWLINE) {
910 quoting = 0;
911 ++lineno;
912 return;
913 }
914 if (c == EOF) {
915 quoting = 0;
916 ateof = 1;
917 return;
918 }
919 if (c == SPACE || c == TAB)
920 c = fgetwc(fp);
921 if (c == NEWLINE) {
922 quoting = 0;
923 ++lineno;
924 return;
925 }
926 if (c == EOF) {
927 quoting = 0;
928 ateof = 1;
929 return;
930 }
931 quoting = 1;
932 quote = c;
933 do { /* skip comment */
934 c = getc(fp);
935 } while (c == SPACE || c == TAB);
936 if (c == NEWLINE) {
937 ++lineno;
938 return;
939 }
940 if (c == EOF) {
941 ateof = 1;
942 return;
943 }
944 FPRINTF(stderr, MSG(15), msgfname, lineno);
945 exit(1);
946 } else {
947 FPRINTF(stderr, MSG(19), msgfname, lineno, text);
948 exit(1);
949 }
950 }
951
952 /*
953 * Read message source file and update double linked list message catalog.
954 */
955 void
read_msgfile(fp,pathname)956 read_msgfile(fp, pathname)
957 FILE *fp;
958 char *pathname;
959 {
960 int c;
961 int no;
962
963 ateof = 0;
964 msgfname = pathname;
965 lineno = 1;
966 quoting = 0;
967 current_set_no = NL_SETD;
968 current_set = NULL;
969 current_msg = NULL;
970
971 for (;;) {
972 if (ateof)
973 return;
974 do {
975 c = fgetwc(fp);
976 } while (c == SPACE || c == TAB);
977 if (c == DOLLAR) {
978 directive(fp);
979 continue;
980 }
981
982 if (isascii(c) && isdigit(c)) {
983 no = get_number(fp, c);
984 if (no == 0) {
985 FPRINTF(stderr, MSG(10), msgfname, lineno);
986 exit(1);
987 }
988 if (no > NL_MSGMAX) {
989 FPRINTF(stderr, MSG(11), msgfname,
990 lineno, no, NL_MSGMAX);
991 }
992 c = fgetwc(fp);
993 if (c == NEWLINE || c == EOF) {
994 delete_msg(no);
995 if (c == NEWLINE)
996 ++lineno;
997 else
998 return;
999 continue;
1000 } else {
1001 if (c != SPACE && c != TAB)
1002 (void) ungetwc(c, fp);
1003 get_text(fp);
1004 insert_msg(no, text_len, text);
1005 continue;
1006 }
1007 }
1008
1009 if (c == NEWLINE) {
1010 ++lineno;
1011 continue;
1012 }
1013 if (c == EOF)
1014 return;
1015
1016 FPRINTF(stderr, MSG(22), msgfname, lineno);
1017 exit(1);
1018 }
1019 }
1020
1021 /*
1022 * Write double linked list to the file.
1023 * It first converts a linked list to one chunk of memory and
1024 * write it to file.
1025 */
1026 void
writecat(fd,pathname)1027 writecat(fd, pathname)
1028 int fd;
1029 char *pathname;
1030 {
1031 int i, n;
1032 int nsets;
1033 int mem;
1034 int nmsgs;
1035 int text_size;
1036 int first_msg_hdr;
1037 int msg_offset;
1038 unsigned nbytes;
1039 char *cat;
1040 struct _cat_hdr *hdrp;
1041 struct cat_set *setp;
1042 struct cat_msg *msgp;
1043 struct _cat_set_hdr *set;
1044 struct _cat_msg_hdr *msg;
1045 char *text;
1046
1047 /* compute number of sets, number of messages, the total text size */
1048 nsets = 0;
1049 nmsgs = 0;
1050 text_size = 0;
1051 for (setp = first_set; setp; setp = setp->next) {
1052 ++nsets;
1053 for (msgp = setp->first_msg; msgp; msgp = msgp->next) {
1054 ++nmsgs;
1055 text_size += msgp->msg_len;
1056 }
1057 }
1058
1059 mem = nsets * _CAT_SET_HDR_SIZE + nmsgs * _CAT_MSG_HDR_SIZE + text_size;
1060 n = _CAT_HDR_SIZE + mem;
1061 cat = MALLOC(n);
1062 if (cat == 0) {
1063 FPRINTF(stderr, MSG(6));
1064 exit(1);
1065 }
1066 MEMSET(cat, 0, n);
1067
1068 hdrp = (struct _cat_hdr *) cat;
1069 hdrp->__hdr_magic = _CAT_MAGIC;
1070 hdrp->__nsets = nsets;
1071 hdrp->__mem = mem;
1072 hdrp->__msg_hdr_offset = nsets * _CAT_SET_HDR_SIZE;
1073 hdrp->__msg_text_offset = nsets * _CAT_SET_HDR_SIZE +
1074 nmsgs * _CAT_MSG_HDR_SIZE;
1075
1076 set = (struct _cat_set_hdr *) (cat + _CAT_HDR_SIZE);
1077 msg = (struct _cat_msg_hdr *) (set + nsets);
1078 text = (char *) (msg + nmsgs);
1079
1080 /* convert linked list to one chunk of memory */
1081 first_msg_hdr = 0;
1082 msg_offset = 0;
1083 for (setp = first_set; setp; ++set, setp = setp->next) {
1084 set->__set_no = setp->set_no;
1085 set->__first_msg_hdr = first_msg_hdr;
1086 nmsgs = 0;
1087 for (msgp = setp->first_msg; msgp; ++msg, msgp = msgp->next) {
1088 ++nmsgs;
1089 msg->__msg_no = msgp->msg_no;
1090 msg->__msg_len = msgp->msg_len;
1091 msg->__msg_offset = msg_offset;
1092 if (msgp->msg_len > 0) {
1093 MEMCPY(text, msgp->s, msgp->msg_len);
1094 text += msgp->msg_len;
1095 msg_offset += msgp->msg_len;
1096 }
1097 }
1098 set->__nmsgs = nmsgs;
1099 first_msg_hdr += nmsgs;
1100 }
1101
1102 /* write one chunk of memory to file */
1103 nbytes = 0;
1104 while (nbytes < n) {
1105 i = write(fd, cat + nbytes, n - nbytes);
1106 if (i < 0) {
1107 if (errno != EINTR) {
1108 FPRINTF(stderr, MSG(8), pathname);
1109 perror("");
1110 exit(1);
1111 }
1112 } else {
1113 nbytes += n;
1114 }
1115 }
1116
1117 free(cat);
1118 }
1119
1120 int
main(argc,argv)1121 main(argc, argv)
1122 int argc;
1123 char *argv[];
1124 {
1125 int i;
1126 int cat_exists;
1127
1128 (void) setlocale(LC_ALL, "");
1129 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
1130 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
1131 #endif
1132 (void) textdomain(TEXT_DOMAIN);
1133
1134 if (argc < 3) {
1135 FPRINTF(stderr, MSG(1));
1136 exit(1);
1137 }
1138 catfname = argv[1];
1139 cat_exists = 0;
1140 if ((*catfname == '-') && (*(catfname + 1) == '\0')) {
1141 catfd = 1; /* Use stdout */
1142 } else {
1143 catfd = open(catfname, O_WRONLY | O_CREAT | O_EXCL, 0666);
1144 if (catfd < 0) { /* file exists */
1145 if (errno != EEXIST ||
1146 (catfd = open(catfname, O_RDWR)) < 0) {
1147 /* cannot open file */
1148 FPRINTF(stderr, MSG(2), catfname);
1149 perror("");
1150 exit(1);
1151 }
1152 cat_exists = 1;
1153 /* read catalog file into memory */
1154 readcat(catfd, catfname);
1155 if (lseek(catfd, 0L, 0) < 0) {
1156 FPRINTF(stderr, MSG(7), catfname);
1157 perror("");
1158 exit(1);
1159 }
1160 }
1161 }
1162
1163 /* process all message source files */
1164 if ((**(argv + 2) == '-') && (*(*(argv + 2) + 1) == '\0')) {
1165 if (argc != 3) {
1166 FPRINTF(stderr, MSG(1));
1167 exit(1);
1168 } else {
1169 read_msgfile(stdin, MSG(21));
1170 }
1171 } else {
1172 for (i = 2; i < argc; ++i) {
1173 FILE *fp;
1174 fp = fopen(*(argv + i), "r");
1175 if (fp == NULL) {
1176 FPRINTF(stderr, MSG(2), *(argv + i));
1177 perror("");
1178 exit(1);
1179 }
1180 read_msgfile(fp, *(argv + i));
1181 (void) fclose(fp);
1182 }
1183 }
1184
1185 if (cat_exists)
1186 (void) ftruncate(catfd, 0L);
1187
1188 /* write catalog to file */
1189 writecat(catfd, catfname);
1190 return (0);
1191 }
1192