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
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.
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]
23 * Copyright (c) 1990, 1991, 1994, Sun Microsystems, Inc.
24 * All rights reserved.
27 #ident "%Z%%M% %I% %E% SMI"
43 #define NL_MSGMAX 32767
51 #define NL_TEXTMAX 2048
60 #define REVERSE_SOLIDUS '\\'
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))
75 /* double linked list */
80 struct cat_msg
*first_msg
;
83 /* double linked list */
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 */
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 */
114 #define MSG1 "usage: gencat catfile msgfile ...\n"
116 #define MSG2 "gencat: cannot open \"%s\"\n"
118 #define MSG3 "gencat: read error on \"%s\"\n"
120 #define MSG4 "gencat: bad magic number (%#lx)\n"
122 #define MSG5 "gencat: corrupt catalogue file \"%s\"\n"
124 #define MSG6 "gencat: memory limit exceeded\n"
126 #define MSG7 "gencat: seek error on \"%s\"\n"
128 #define MSG8 "gencat: write error on \"%s\"\n"
130 #define MSG9 "gencat: \"%s\", line %d: number too large (%s)\n"
132 #define MSG10 "gencat: \"%s\", line %d: 0 is not a permissible " \
135 #define MSG11 "gencat: \"%s\", line %d: warning, message number %d " \
136 "exceeds limit (%d)\n"
138 #define MSG12 "gencat: \"%s\", line %d: missing quote (%wc)\n"
140 #define MSG13 "gencat: \"%s\", line %d: character value too large ('\\%o')\n"
142 #define MSG14 "gencat: \"%s\", line %d: extra characters following " \
145 #define MSG15 "gencat: \"%s\", line %d: extra characters following " \
148 #define MSG16 "gencat: \"%s\", line %d: no set number specified in " \
151 #define MSG17 "getcat: \"%s\", line %d: 0 is not a permissible set number\n"
153 #define MSG18 "gencat: \"%s\", line %d: warning, set number %d " \
154 "exceeds limit (%d)\n"
156 #define MSG19 "gencat: \"%s\", line %d: unknown directive %s\n"
158 #define MSG20 "gencat: \"%s\", line %d: no set number specified in " \
159 "$delset directive\n"
161 #define MSG21 "stdin"
163 #define MSG22 "gencat: \"%s\", line %d: number or $ expected\n"
171 p
= (struct cat_set
*) MALLOC(sizeof (struct cat_set
));
173 FPRINTF(stderr
, MSG(6));
187 struct cat_set
*prev
, *next
;
189 if (current_set
&& current_set
->set_no
== no
) {
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
);
201 current_set
= first_set
;
202 if (current_set
->set_no
== no
)
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
;
215 current_set
= first_set
;
216 if (current_set
->set_no
== no
)
220 /* search for the set number 'no' */
221 while (current_set
->next
&& current_set
->next
->set_no
< no
)
222 current_set
= current_set
->next
;
224 if (current_set
->next
&& current_set
->next
->set_no
== no
) {
225 /* set number 'no' found */
226 current_set
= current_set
->next
;
230 /* If set number is not found, insert a new set in the middle */
232 next
= current_set
->next
;
233 current_set
= new_set(no
);
234 current_set
->prev
= prev
;
235 current_set
->next
= next
;
237 prev
->next
= current_set
;
239 first_set
= current_set
;
241 next
->prev
= current_set
;
248 struct cat_set
*prev
, *next
, *setp
;
249 struct cat_msg
*p
, *q
;
251 for (setp
= first_set
; setp
&& setp
->set_no
< no
; setp
= setp
->next
)
254 if (setp
== NULL
|| setp
->set_no
!= no
) /* set not found */
257 if (setp
== current_set
) {
262 /* free all messages in the set */
263 for (p
= setp
->first_msg
; p
; p
) {
269 /* do the link operation to delete the set */
282 new_msg(no
, len
, text
)
289 p
= (struct cat_msg
*) MALLOC(sizeof (struct cat_msg
) + len
);
291 FPRINTF(stderr
, MSG(6));
298 MEMCPY(p
->s
, text
, len
);
304 insert_msg(no
, len
, text
)
309 struct cat_msg
*prev
, *next
;
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
;
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
;
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
=
336 FREE(current_set
->first_msg
);
337 current_set
->first_msg
= current_msg
;
341 while (current_msg
->next
&& current_msg
->next
->msg_no
< no
)
342 current_msg
= current_msg
->next
;
345 * if the same msg number is found, then delte the message and
346 * insert the new message. This is same as replacing message.
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
;
355 next
= current_msg
->next
;
358 current_msg
= new_msg(no
, len
, text
);
359 current_msg
->prev
= prev
;
360 current_msg
->next
= next
;
362 prev
->next
= current_msg
;
364 current_set
->first_msg
= current_msg
;
366 next
->prev
= current_msg
;
373 struct cat_set
*p
= current_set
;
374 struct cat_msg
*prev
, *next
;
376 if (current_msg
== NULL
) {
377 if (current_set
== NULL
)
378 for (p
= first_set
; p
&& p
->set_no
< current_set_no
;
381 if (p
== NULL
|| p
->set_no
!= current_set_no
)
384 current_msg
= current_set
->first_msg
;
385 if (current_msg
== NULL
)
388 if (current_msg
->msg_no
> no
)
389 current_msg
= current_set
->first_msg
;
391 while (current_msg
&& current_msg
->msg_no
!= no
)
392 current_msg
= current_msg
->next
;
394 if (current_msg
&& current_msg
->msg_no
== no
) {
395 prev
= current_msg
->prev
;
396 next
= current_msg
->next
;
402 current_set
->first_msg
= next
;
411 read_block(fd
, p
, n
, pathname
)
417 int nbytes
, bytes_read
;
424 bytes_read
= READ(fd
, p
+ nbytes
, n
- nbytes
);
425 if (bytes_read
< 0) {
426 if (errno
!= EINTR
) {
427 FPRINTF(stderr
, MSG(3), pathname
);
431 } else if (bytes_read
== 0)
434 nbytes
+= bytes_read
;
441 * Check if catalog file read is valid
451 struct _cat_msg_hdr
*msg
;
454 struct _cat_set_hdr
*set
;
456 set
= (struct _cat_set_hdr
*) cat
;
458 for (i
= 0; i
< hdr
.__nsets
; ++set
, ++i
) {
459 if (set
->__set_no
< set_no
)
461 set_no
= set
->__set_no
;
462 nmsgs
= set
->__nmsgs
;
467 first_msg_hdr
= set
->__first_msg_hdr
;
468 if (first_msg_hdr
< 0)
470 if (hdr
.__msg_hdr_offset
+ (first_msg_hdr
+ nmsgs
) *
471 _CAT_MSG_HDR_SIZE
> hdr
.__mem
)
474 msg
= (struct _cat_msg_hdr
*) (cat
+ hdr
.__msg_hdr_offset
) +
477 for (j
= 0; j
< nmsgs
; ++msg
, ++j
) {
478 if (msg
->__msg_no
< msg_no
)
480 msg_no
= msg
->__msg_no
;
481 if (msg
->__msg_offset
< 0)
483 if (hdr
.__msg_text_offset
+ msg
->__msg_offset
+
484 msg
->__msg_len
> hdr
.__mem
)
493 * convert a chunk of catalog file into double linked list format
501 struct _cat_set_hdr
*set
;
502 struct _cat_msg_hdr
*msg
;
504 set
= (struct _cat_set_hdr
*) cat
;
505 for (i
= 0; i
< hdr
.__nsets
; ++set
, ++i
) {
506 nmsgs
= set
->__nmsgs
;
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
);
521 * read a catalog file in a chunk and convert it to double linked list.
524 readcat(fd
, pathname
)
531 i
= read_block(fd
, (char *) &hdr
, _CAT_HDR_SIZE
, pathname
);
535 if (i
>= 4 && hdr
.__hdr_magic
!= _CAT_MAGIC
) {
536 FPRINTF(stderr
, MSG(4), hdr
.__hdr_magic
);
539 if (i
< _CAT_HDR_SIZE
|| hdr
.__nsets
< 0) {
540 FPRINTF(stderr
, MSG(5), pathname
);
543 if (hdr
.__nsets
== 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
);
555 cat
= MALLOC(hdr
.__mem
);
557 FPRINTF(stderr
, MSG(6));
560 i
= read_block(fd
, cat
, hdr
.__mem
, pathname
);
561 if (i
< hdr
.__mem
|| !cat_ok(cat
)) {
562 FPRINTF(stderr
, MSG(5), pathname
);
571 * Extend the memory in 1000 byte chunks whenever runs out of text space.
578 text
= REALLOC(text
, text_size
);
580 text
= MALLOC(text_size
);
582 FPRINTF(stderr
, MSG(6));
597 while (i
>= text_size
)
604 (void) ungetc(c
, fp
);
606 while (i
>= text_size
)
610 for (s
= text
; *s
== '0'; ++s
)
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
);
620 n
= 10 * n
+ (*t
- '0');
635 if (quoting
&& c
== quote
) { /* quote is used */
638 if (c
== NEWLINE
|| c
== EOF
) {
639 FPRINTF(stderr
, MSG(12), msgfname
, lineno
,
643 if (c
== REVERSE_SOLIDUS
) {
647 FPRINTF(stderr
, MSG(12), msgfname
,
667 if (c
>= '0' && c
<= '7') {
668 n
= 8 * n
+ (c
- '0');
670 if (c
>= '0' && c
<= '7')
671 n
= 8 * n
+ (c
- '0');
673 (void) ungetwc(c
, fp
);
675 (void) ungetwc(c
, fp
);
677 FPRINTF(stderr
, MSG(13),
678 msgfname
, lineno
, n
);
709 while ((text_len
+ (int)MB_CUR_MAX
+ 1) >= text_size
)
711 if ((n
= wctomb(&text
[text_len
], c
)) > 0)
716 while ((text_len
+ 1) >= text_size
)
718 text
[text_len
] = '\0';
723 } while (c
== SPACE
|| c
== TAB
);
732 FPRINTF(stderr
, MSG(14), msgfname
, lineno
);
736 while (c
!= NEWLINE
&& c
!= EOF
) { /* quote is not used */
737 if (c
== REVERSE_SOLIDUS
) {
758 if (c
>= '0' && c
<= '7') {
759 n
= 8 * n
+ (c
- '0');
761 if (c
>= '0' && c
<= '7')
762 n
= 8 * n
+ (c
- '0');
764 (void) ungetwc(c
, fp
);
766 (void) ungetwc(c
, fp
);
768 FPRINTF(stderr
, MSG(13), msgfname
,
800 while ((text_len
+ (int)MB_CUR_MAX
+ 1) >= text_size
)
802 if ((n
= wctomb(&text
[text_len
], c
)) > 0)
807 while ((text_len
+ 1) >= text_size
)
809 text
[text_len
] = '\0';
819 * This routine handles $ <comment>, $set, $delset, $quote
829 if (c
== SPACE
|| c
== TAB
) { /* $ <comment */
832 } while (c
!= NEWLINE
&& c
!= EOF
);
843 while (text_len
>= text_size
)
846 while (isascii(c
) && isalpha(c
)) {
847 while ((text_len
+ 1) >= text_size
)
854 while ((text_len
+ 1) >= text_size
)
856 text
[text_len
] = NUL
;
858 if (strcmp(text
, "$set") == 0) {
859 while (c
== SPACE
|| c
== TAB
)
861 if (!isascii(c
) || !isdigit(c
)) {
862 FPRINTF(stderr
, MSG(16), msgfname
, lineno
);
865 n
= get_number(fp
, c
);
867 FPRINTF(stderr
, MSG(17), msgfname
, lineno
);
871 FPRINTF(stderr
, MSG(18), msgfname
, lineno
,
875 do { /* skip comment */
877 } while (c
!= NEWLINE
&& c
!= EOF
);
883 } else if (strcmp(text
, "$delset") == 0) {
884 while (c
== SPACE
|| c
== TAB
)
886 if (!isascii(c
) || !isdigit(c
)) {
887 FPRINTF(stderr
, MSG(20), msgfname
, lineno
);
890 n
= get_number(fp
, c
);
892 FPRINTF(stderr
, MSG(17), msgfname
, lineno
);
896 FPRINTF(stderr
, MSG(18), msgfname
, lineno
,
900 do { /* skip comment */
902 } while (c
!= NEWLINE
&& c
!= EOF
);
908 } else if (strcmp(text
, "$quote") == 0) {
919 if (c
== SPACE
|| c
== TAB
)
933 do { /* skip comment */
935 } while (c
== SPACE
|| c
== TAB
);
944 FPRINTF(stderr
, MSG(15), msgfname
, lineno
);
947 FPRINTF(stderr
, MSG(19), msgfname
, lineno
, text
);
953 * Read message source file and update double linked list message catalog.
956 read_msgfile(fp
, pathname
)
967 current_set_no
= NL_SETD
;
976 } while (c
== SPACE
|| c
== TAB
);
982 if (isascii(c
) && isdigit(c
)) {
983 no
= get_number(fp
, c
);
985 FPRINTF(stderr
, MSG(10), msgfname
, lineno
);
988 if (no
> NL_MSGMAX
) {
989 FPRINTF(stderr
, MSG(11), msgfname
,
990 lineno
, no
, NL_MSGMAX
);
993 if (c
== NEWLINE
|| c
== EOF
) {
1001 if (c
!= SPACE
&& c
!= TAB
)
1002 (void) ungetwc(c
, fp
);
1004 insert_msg(no
, text_len
, text
);
1016 FPRINTF(stderr
, MSG(22), msgfname
, lineno
);
1022 * Write double linked list to the file.
1023 * It first converts a linked list to one chunk of memory and
1027 writecat(fd
, pathname
)
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
;
1047 /* compute number of sets, number of messages, the total text size */
1051 for (setp
= first_set
; setp
; setp
= setp
->next
) {
1053 for (msgp
= setp
->first_msg
; msgp
; msgp
= msgp
->next
) {
1055 text_size
+= msgp
->msg_len
;
1059 mem
= nsets
* _CAT_SET_HDR_SIZE
+ nmsgs
* _CAT_MSG_HDR_SIZE
+ text_size
;
1060 n
= _CAT_HDR_SIZE
+ mem
;
1063 FPRINTF(stderr
, MSG(6));
1068 hdrp
= (struct _cat_hdr
*) cat
;
1069 hdrp
->__hdr_magic
= _CAT_MAGIC
;
1070 hdrp
->__nsets
= nsets
;
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
;
1076 set
= (struct _cat_set_hdr
*) (cat
+ _CAT_HDR_SIZE
);
1077 msg
= (struct _cat_msg_hdr
*) (set
+ nsets
);
1078 text
= (char *) (msg
+ nmsgs
);
1080 /* convert linked list to one chunk of memory */
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
;
1087 for (msgp
= setp
->first_msg
; msgp
; ++msg
, msgp
= msgp
->next
) {
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
;
1098 set
->__nmsgs
= nmsgs
;
1099 first_msg_hdr
+= nmsgs
;
1102 /* write one chunk of memory to file */
1104 while (nbytes
< n
) {
1105 i
= write(fd
, cat
+ nbytes
, n
- nbytes
);
1107 if (errno
!= EINTR
) {
1108 FPRINTF(stderr
, MSG(8), pathname
);
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 */
1132 (void) textdomain(TEXT_DOMAIN
);
1135 FPRINTF(stderr
, MSG(1));
1140 if ((*catfname
== '-') && (*(catfname
+ 1) == '\0')) {
1141 catfd
= 1; /* Use stdout */
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
);
1153 /* read catalog file into memory */
1154 readcat(catfd
, catfname
);
1155 if (lseek(catfd
, 0L, 0) < 0) {
1156 FPRINTF(stderr
, MSG(7), catfname
);
1163 /* process all message source files */
1164 if ((**(argv
+ 2) == '-') && (*(*(argv
+ 2) + 1) == '\0')) {
1166 FPRINTF(stderr
, MSG(1));
1169 read_msgfile(stdin
, MSG(21));
1172 for (i
= 2; i
< argc
; ++i
) {
1174 fp
= fopen(*(argv
+ i
), "r");
1176 FPRINTF(stderr
, MSG(2), *(argv
+ i
));
1180 read_msgfile(fp
, *(argv
+ i
));
1186 (void) ftruncate(catfd
, 0L);
1188 /* write catalog to file */
1189 writecat(catfd
, catfname
);