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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
23 *
24 * sgsmsg generates several message files from an input template file. Messages
25 * are constructed for use with gettext(3i) - the default - or catgets(3c). The
26 * files generate are:
27 *
28 * msg.h a header file containing definitions for each message. The -h
29 * option triggers the creation of these definitions and specifies
30 * the name to use.
31 *
32 * msg.c a data array of message strings. The msg.h definitions are
33 * offsets into this array. The -d option triggers the creation of
34 * these definitions and specifies the name to use.
35 *
36 * messages a message file suitable for catgets(3c) or gettext(3i) use. The
37 * -m option triggers this output and specifies the filename to be
38 * used.
39 *
40 * The template file is processed based on the first character of each line:
41 *
42 * # or $ entries are copied (as is) to the message file (messages).
43 *
44 * @ token(s) entries are translated. Two translations are possible dependent
45 * on whether one or more tokens are supplied:
46 *
47 * A single token is interpreted as one of two reserved message
48 * output indicators, or a message identifier. The reserved output
49 * indicator _START_ enables output to the message file - Note that
50 * the occurance of any other @ token will also enable message
51 * output. The reserved output indicator _END_ disables output to
52 * the message file. The use of these two indicators provides for
53 * only those message strings that require translation to be output
54 * to the message file.
55 *
56 * Besides the reserved output indicators, a single token is taken
57 * to be a message identifier which will be subsituted for a
58 * `setid' for catgets(3c) output, or a `domain' name for
59 * gettext(3i) output. This value is determine by substituting the
60 * token for the associated definition found in the message
61 * identifier file (specified with the -i option).
62 *
63 * Multiple tokens are taken to be a message definition followed by
64 * the associated message string. The message string is copied to
65 * the data array being built in msg.c. The index into this array
66 * becomes the `message' identifier created in the msg.h file.
67 */
68
69 #include <fcntl.h>
70 #include <stdlib.h>
71 #include <stdio.h>
72 #include <unistd.h>
73 #include <limits.h>
74 #include <string.h>
75 #include <ctype.h>
76 #include <errno.h>
77 #include <sys/param.h>
78
79 #include <sgs.h>
80 #include <_string_table.h>
81
82 /*
83 * Define any error message strings.
84 */
85 static const char
86 * Errmsg_malt = "sgsmsg: file %s: line %d: malformed input "
87 "at line\n",
88 * Errmsg_nmem = "sgsmsg: memory allocation failed: %s\n",
89 * Errmsg_opne = "sgsmsg: file %s: open failed: %s\n",
90 * Errmsg_wrte = "sgsmsg: file %s: write failed: %s\n",
91 * Errmsg_read = "sgsmsg: file %s: read failed %s\n",
92 * Errmsg_stnw = "sgsmsg: st_new(): failed: %s\n",
93 * Errmsg_stin = "sgsmsg: Str_tbl insert failed: %s\n",
94 * Errmsg_mnfn = "sgsmsg: message not found in Str_tbl: %s\n",
95 * Errmsg_use = "usage: sgsmsg [-clv] [-d mesgdata] [-h mesgdefs] "
96 "[-m messages] [-n name] [-i mesgident] file ...\n";
97
98 /*
99 * Define all output filenames and associated descriptors.
100 */
101 static FILE *fddefs, *fddata, *fdmsgs, *fdmids, *fddesc;
102 static char *fldefs, *fldata, *flmsgs, *flmids, *fldesc;
103 static FILE *fdlint;
104 static char fllint[MAXPATHLEN];
105
106 static uint_t vflag; /* verbose flag */
107 static Str_tbl *stp; /* string table */
108
109 /*
110 * Define any default strings.
111 */
112 static const char
113 *nmlint = "/tmp/sgsmsg.lint",
114 *interface = "sgs_msg",
115 *start = "_START_",
116 *end = "_END_";
117
118 /*
119 * Define any default flags and data items.
120 */
121 static int cflag = 0, lflag = 0, prtmsgs = 0, line, ptr = 1, msgid = 0;
122 static char *mesgid = 0, *setid = 0, *domain = 0;
123
124 typedef struct msg_string {
125 char *ms_defn;
126 char *ms_message;
127 struct msg_string *ms_next;
128 } msg_string;
129
130 static msg_string *msg_head;
131 static msg_string *msg_tail;
132
133 /*
134 * message_append() is responsible for both inserting strings into
135 * the master Str_tbl as well as maintaining a list of the
136 * DEFINITIONS associated with each string.
137 *
138 * The list of strings is traversed at the end once the full
139 * Str_tbl has been constructed - and string offsets can be
140 * assigned.
141 */
142 static void
message_append(const char * defn,const char * message)143 message_append(const char *defn, const char *message)
144 {
145 msg_string *msg;
146 if ((msg = calloc(sizeof (msg_string), 1)) == 0) {
147 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
148 exit(1);
149 }
150
151 /*
152 * Initialize the string table.
153 */
154 if ((stp == 0) && ((stp = st_new(FLG_STNEW_COMPRESS)) == NULL)) {
155 (void) fprintf(stderr, Errmsg_stnw, strerror(errno));
156 exit(1);
157 }
158
159
160 if ((msg->ms_defn = strdup(defn)) == 0) {
161 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
162 exit(1);
163 }
164 if ((msg->ms_message = strdup(message)) == 0) {
165 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
166 exit(1);
167 }
168
169 if (st_insert(stp, msg->ms_message) == -1) {
170 (void) fprintf(stderr, Errmsg_stin,
171 message);
172 exit(1);
173 }
174
175 if (msg_head == 0) {
176 msg_head = msg_tail = msg;
177 return;
178 }
179 msg_tail->ms_next = msg;
180 msg_tail = msg;
181 }
182
183 /*
184 * Initialize a setid value. Given a setid definition determine its numeric
185 * value from the specified message identifier file (specified with the -i
186 * option). Return a pointer to the numeric string.
187 */
188 static int
getmesgid(char * id)189 getmesgid(char *id)
190 {
191 char *buffer, *token, *_mesgid = 0, *_setid = 0, *_domain = 0;
192
193 /*
194 * If we're being asked to interpret a message id but the user didn't
195 * provide the required message identifier file (-i option) we're in
196 * trouble.
197 */
198 if (flmids == 0) {
199 (void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
200 "unable to process mesgid\n\t"
201 "no message identifier file specified "
202 "(see -i option)\n", fldesc, line, id);
203 return (1);
204 }
205
206 if ((buffer = malloc(LINE_MAX)) == 0) {
207 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
208 return (1);
209 }
210
211 /*
212 * Read the message identifier file and locate the required mesgid.
213 */
214 rewind(fdmids);
215 while (fgets(buffer, LINE_MAX, fdmids) != NULL) {
216 if ((token = strstr(buffer, id)) == NULL)
217 continue;
218
219 /*
220 * Establish individual strings for the mesgid, setid and domain
221 * values.
222 */
223 _mesgid = token;
224 while (!(isspace(*token)))
225 token++;
226 *token++ = 0;
227
228 while (isspace(*token))
229 token++;
230 _setid = token;
231 while (!(isspace(*token)))
232 token++;
233 *token++ = 0;
234
235 while (isspace(*token))
236 token++;
237 _domain = token;
238 while (!(isspace(*token)))
239 token++;
240 *token = 0;
241 break;
242 }
243
244 /*
245 * Did we find a match?
246 */
247 if ((_mesgid == 0) || (_setid == 0) || (_domain == 0)) {
248 (void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
249 "unable to process mesgid\n\t"
250 "identifier does not exist in file %s\n",
251 fldesc, line, id, flmids);
252 return (1);
253 }
254
255 /*
256 * Have we been here before?
257 */
258 if (mesgid) {
259 if (cflag == 1) {
260 /*
261 * If we're being asked to process more than one mesgid
262 * warn the user that only one mesgid can be used for
263 * the catgets(3c) call.
264 */
265 (void) fprintf(stderr, "sgsmsg: file %s: line %d: "
266 "setid %s: warning: multiple mesgids "
267 "encountered\n\t"
268 "last setting used in messaging code\n",
269 fldesc, line, id);
270 }
271 }
272
273 mesgid = _mesgid;
274 setid = _setid;
275 domain = _domain;
276
277 /*
278 * Generate the message file output (insure output flag is enabled).
279 */
280 if (prtmsgs != -1)
281 prtmsgs = 1;
282 if (fdmsgs && (prtmsgs == 1)) {
283 if (cflag == 1) {
284 if (fprintf(fdmsgs, "$quote \"\n$set %s\n",
285 setid) < 0) {
286 (void) fprintf(stderr, Errmsg_wrte, flmsgs,
287 strerror(errno));
288 return (1);
289 }
290 } else {
291 if (fprintf(fdmsgs, "domain\t\"%s\"\n", domain) < 0) {
292 (void) fprintf(stderr, Errmsg_wrte, flmsgs,
293 strerror(errno));
294 return (1);
295 }
296 }
297 }
298
299 /*
300 * For catgets(3c) output generate a setid definition in the message
301 * definition file.
302 */
303 if (fddefs && (cflag == 1) &&
304 (fprintf(fddefs, "#define\t%s\t%s\n\n", mesgid, setid) < 0)) {
305 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
306 return (1);
307 }
308
309 return (0);
310 }
311
312 /*
313 * Dump contents of String Table to standard out
314 */
315 static void
dump_stringtab(Str_tbl * stp)316 dump_stringtab(Str_tbl *stp)
317 {
318 uint_t cnt;
319
320 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
321 (void) printf("string table full size: %ld: uncompressed\n",
322 stp->st_fullstrsize);
323 return;
324 }
325
326 (void) printf("string table full size: %ld compressed down to: %ld\n\n",
327 stp->st_fullstrsize, stp->st_strsize);
328 (void) printf("string table compression information [%d buckets]:\n",
329 stp->st_hbckcnt);
330
331 for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) {
332 Str_hash *sthash = stp->st_hashbcks[cnt];
333
334 if (sthash == 0)
335 continue;
336
337 (void) printf(" bucket: [%d]\n", cnt);
338
339 while (sthash) {
340 size_t stroff = sthash->hi_mstr->sm_strlen -
341 sthash->hi_strlen;
342
343 if (stroff == 0) {
344 (void) printf(" [%ld]: '%s' <master>\n",
345 sthash->hi_refcnt, sthash->hi_mstr->sm_str);
346 } else {
347 (void) printf(" [%ld]: '%s' <suffix of: "
348 "'%s'>\n", sthash->hi_refcnt,
349 &sthash->hi_mstr->sm_str[stroff],
350 sthash->hi_mstr->sm_str);
351 }
352 sthash = sthash->hi_next;
353 }
354 }
355 }
356
357 /*
358 * Initialize the message definition header file stream.
359 */
360 static int
init_defs(void)361 init_defs(void)
362 {
363 static char guard[FILENAME_MAX + 6];
364 char *optr;
365 const char *iptr, *_ptr;
366
367 /*
368 * Establish a header guard name using the files basename.
369 */
370 for (iptr = 0, _ptr = fldefs; _ptr && (*_ptr != '\0'); _ptr++) {
371 if (*_ptr == '/')
372 iptr = _ptr + 1;
373 }
374 if (iptr == 0)
375 iptr = fldefs;
376
377 optr = guard;
378 for (*optr++ = '_'; iptr && (*iptr != '\0'); iptr++, optr++) {
379 if (*iptr == '.') {
380 *optr++ = '_';
381 *optr++ = 'D';
382 *optr++ = 'O';
383 *optr++ = 'T';
384 *optr = '_';
385 } else
386 *optr = toupper(*iptr);
387 }
388
389 if (fprintf(fddefs, "#ifndef\t%s\n#define\t%s\n\n", guard, guard) < 0) {
390 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
391 return (1);
392 }
393
394 if (fprintf(fddefs, "#include <sgsmsg.h>\t/* Msg typedef */\n\n") < 0) {
395 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
396 return (1);
397 }
398
399 if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) {
400 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
401 return (1);
402 }
403
404 /*
405 * The MSG_SGS_ARRAY_NAME macro supplies a generic way to
406 * reference the string table regardless of its name.
407 */
408 if (fprintf(fddefs, "#define\tMSG_SGS_LOCAL_ARRAY\t__%s\n\n",
409 interface) < 0) {
410 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
411 return (1);
412 }
413
414 /*
415 * If the associated data array is global define a prototype.
416 * Define a macro to access the array elements.
417 */
418 if (lflag == 0) {
419 if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
420 interface) < 0) {
421 (void) fprintf(stderr, Errmsg_wrte, fldefs,
422 strerror(errno));
423 return (1);
424 }
425 }
426 if (fprintf(fddefs,
427 "#define\tMSG_ORIG_STRTAB(_x, _s)\t&_s[_x]\n\n") < 0) {
428 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
429 return (1);
430 }
431 if (fprintf(fddefs,
432 "#define\tMSG_ORIG(x)\tMSG_ORIG_STRTAB(x, __%s)\n\n",
433 interface) < 0) {
434 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
435 return (1);
436 }
437
438 /*
439 * Generate a prototype to access the associated data array.
440 */
441 if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
442 interface) < 0) {
443 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
444 return (1);
445 }
446 if (fprintf(fddefs, "#define\tMSG_INTL(x)\t_%s(x)\n\n",
447 interface) < 0) {
448 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
449 return (1);
450 }
451
452 return (0);
453 }
454
455
456 /*
457 * Finish the message definition header file.
458 */
459 static int
fini_defs(void)460 fini_defs(void)
461 {
462 if (fprintf(fddefs, "\n#else\t/* __lint */\n\n") < 0) {
463 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
464 return (1);
465 }
466
467 if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
468 interface) < 0) {
469 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
470 return (1);
471 }
472
473 if (fprintf(fddefs, "#ifndef MSG_SGS_LOCAL_ARRAY\n"
474 "#define\tMSG_SGS_LOCAL_ARRAY\t\"\"\n#endif\n\n") < 0) {
475 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
476 return (1);
477 }
478
479 if (lflag == 0) {
480 if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
481 interface) < 0) {
482 (void) fprintf(stderr, Errmsg_wrte, fldefs,
483 strerror(errno));
484 return (1);
485 }
486 }
487
488 if (fprintf(fddefs,
489 "#define MSG_ORIG_STRTAB(_x, _s)\t_x\n"
490 "#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) {
491 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
492 return (1);
493 }
494
495 /*
496 * Provide a way to get the array and function declarations above
497 * without also getting the actual messages. This is useful in
498 * our lintsup.c files that include more than one message header.
499 * lintsup doesn't need the actual messages, and this prevents
500 * macro name collisions.
501 */
502 if (fprintf(fddefs, "\n#ifndef LINTSUP_SUPPRESS_STRINGS\n") < 0) {
503 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
504 return (1);
505 }
506
507 /*
508 * Copy the temporary lint defs file into the new header.
509 */
510 if (fdlint) {
511 long size;
512 char *buf;
513
514 size = ftell(fdlint);
515 (void) rewind(fdlint);
516
517 if ((buf = malloc(size)) == 0) {
518 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
519 return (1);
520 }
521 if (fread(buf, size, 1, fdlint) == 0) {
522 (void) fprintf(stderr, Errmsg_read, fllint,
523 strerror(errno));
524 return (1);
525 }
526 if (fwrite(buf, size, 1, fddefs) == 0) {
527 (void) fprintf(stderr, Errmsg_wrte, fldefs,
528 strerror(errno));
529 return (1);
530 }
531 (void) free(buf);
532 }
533
534 if (fprintf(fddefs, "\n#endif\t/* LINTSUP_SUPPRESS_STRINGS */\n") < 0) {
535 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
536 return (1);
537 }
538
539 if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) {
540 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
541 return (1);
542 }
543
544 if (fprintf(fddefs, "\n#endif\n") < 0) {
545 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
546 return (1);
547 }
548
549 return (0);
550 }
551
552 /*
553 * The entire messaging file has been scanned - and all strings have been
554 * inserted into the string_table. We can now walk the message queue
555 * and create the '#define <DEFN>' for each string - with the strings
556 * assigned offset into the string_table.
557 */
558 static int
output_defs(void)559 output_defs(void)
560 {
561 msg_string *msg;
562 size_t stbufsize;
563 char *stbuf;
564
565 stbufsize = st_getstrtab_sz(stp);
566 if ((stbuf = malloc(stbufsize)) == 0) {
567 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
568 exit(1);
569 }
570 (void) st_setstrbuf(stp, stbuf, stbufsize);
571 for (msg = msg_head; msg; msg = msg->ms_next) {
572 size_t stoff;
573 if ((st_setstring(stp, msg->ms_message, &stoff)) == -1) {
574 (void) fprintf(stderr, Errmsg_mnfn, msg->ms_message);
575 return (1);
576 }
577 if (fprintf(fddefs, "\n#define\t%s\t%ld\n",
578 msg->ms_defn, stoff) < 0) {
579 (void) fprintf(stderr, Errmsg_wrte,
580 fldefs, strerror(errno));
581 return (1);
582 }
583 if (fddefs && fprintf(fddefs, "#define\t%s_SIZE\t%d\n",
584 msg->ms_defn, strlen(msg->ms_message)) < 0) {
585 (void) fprintf(stderr, Errmsg_wrte,
586 fldefs, strerror(errno));
587 return (1);
588 }
589 }
590 return (0);
591 }
592
593
594 /*
595 * Finish off the data structure definition.
596 */
597 static int
output_data(void)598 output_data(void)
599 {
600 size_t stbufsize;
601 size_t ndx;
602 size_t column = 1;
603 const char *stbuf;
604 const char *fmtstr;
605
606 stbufsize = st_getstrtab_sz(stp);
607 stbuf = st_getstrbuf(stp);
608
609 assert(stbuf);
610
611 /*
612 * Determine from the local flag whether the data declaration should
613 * be static.
614 */
615 if (lflag)
616 fmtstr = (const char *)"static const";
617 else
618 fmtstr = (const char *)"const";
619
620 if (fprintf(fddata, "\n%s char __%s[%ld] __attribute__((unused)) = { ",
621 fmtstr, interface, stbufsize) < 0) {
622 (void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
623 return (1);
624 }
625
626 for (ndx = 0; ndx < (stbufsize - 1); ndx++) {
627 if (column == 1) {
628 if (fddata && fprintf(fddata,
629 "\n/* %4ld */ 0x%.2x,", ndx,
630 (unsigned char)stbuf[ndx]) < 0) {
631 (void) fprintf(stderr, Errmsg_wrte,
632 fldata, strerror(errno));
633 return (1);
634 }
635 } else {
636 if (fddata && fprintf(fddata, " 0x%.2x,",
637 (unsigned char)stbuf[ndx]) < 0) {
638 (void) fprintf(stderr, Errmsg_wrte,
639 fldata, strerror(errno));
640 return (1);
641 }
642 }
643
644 if (column++ == 10)
645 column = 1;
646 }
647
648 if (column == 1)
649 fmtstr = "\n\t0x%.2x };\n";
650 else
651 fmtstr = " 0x%.2x };\n";
652
653 if (fprintf(fddata, fmtstr, (unsigned char)stbuf[stbufsize - 1]) < 0) {
654 (void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
655 return (1);
656 }
657
658 return (0);
659 }
660
661 static int
file()662 file()
663 {
664 char buffer[LINE_MAX], * token;
665 uint_t bufsize;
666 char *token_buffer;
667 int escape = 0;
668 int len = 0;
669
670 if ((token_buffer = malloc(LINE_MAX)) == 0) {
671 (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
672 return (1);
673 }
674 bufsize = LINE_MAX;
675
676 line = 1;
677
678 while ((token = fgets(buffer, LINE_MAX, fddesc)) != NULL) {
679 char defn[PATH_MAX], * _defn, * str;
680
681 switch (*token) {
682 case '#':
683 case '$':
684 if (escape) {
685 (void) fprintf(stderr, Errmsg_malt, fldesc,
686 line);
687 return (1);
688 }
689
690 /*
691 * If a msgid has been output a msgstr must follow
692 * before we digest the new token. A msgid is only set
693 * if fdmsgs is in use.
694 */
695 if (msgid) {
696 msgid = 0;
697 if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
698 (void) fprintf(stderr, Errmsg_wrte,
699 flmsgs, strerror(errno));
700 return (1);
701 }
702 }
703
704 /*
705 * Pass lines directly through to the output message
706 * file.
707 */
708 if (fdmsgs && (prtmsgs == 1)) {
709 char comment;
710
711 if (cflag == 0)
712 comment = '#';
713 else
714 comment = '$';
715
716 if (fprintf(fdmsgs, "%c%s", comment,
717 ++token) < 0) {
718 (void) fprintf(stderr, Errmsg_wrte,
719 flmsgs, strerror(errno));
720 return (1);
721 }
722 }
723 break;
724
725 case '@':
726 if (escape) {
727 (void) fprintf(stderr, Errmsg_malt, fldesc,
728 line);
729 return (1);
730 }
731
732 /*
733 * If a msgid has been output a msgstr must follow
734 * before we digest the new token.
735 */
736 if (msgid) {
737 msgid = 0;
738 if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
739 (void) fprintf(stderr, Errmsg_wrte,
740 flmsgs, strerror(errno));
741 return (1);
742 }
743 }
744
745 /*
746 * Determine whether we have one or more tokens.
747 */
748 token++;
749 while (isspace(*token)) /* rid any whitespace */
750 token++;
751 _defn = token; /* definition start */
752 while (!(isspace(*token)))
753 token++;
754 *token++ = 0;
755
756 while (isspace(*token)) /* rid any whitespace */
757 token++;
758
759 /*
760 * Determine whether the single token is one of the
761 * reserved message output delimiters otherwise
762 * translate it as a message identifier.
763 */
764 if (*token == 0) {
765 if (strcmp(_defn, start) == 0)
766 prtmsgs = 1;
767 else if (strcmp(_defn, end) == 0)
768 prtmsgs = -1;
769 else if (getmesgid(_defn) == 1)
770 return (1);
771 break;
772 }
773
774 /*
775 * Multiple tokens are translated by taking the first
776 * token as the message definition, and the rest of the
777 * line as the message itself. A message line ending
778 * with an escape ('\') is expected to be continued on
779 * the next line.
780 */
781 if (prtmsgs != -1)
782 prtmsgs = 1;
783 if (fdmsgs && (prtmsgs == 1)) {
784 /*
785 * For catgets(3c) make sure a message
786 * identifier has been established (this is
787 * normally a domain for gettext(3i), but for
788 * sgsmsg use this could be argued as being
789 * redundent). Also make sure that the message
790 * definitions haven't exceeeded the maximum
791 * value allowed by gencat(1) before generating
792 * any message file entries.
793 */
794 if (cflag == 1) {
795 if (setid == 0) {
796 (void) fprintf(stderr, "file "
797 "%s: no message identifier "
798 "has been established\n",
799 fldesc);
800 return (1);
801 }
802 if (ptr > NL_MSGMAX) {
803 (void) fprintf(stderr, "file "
804 "%s: message definition "
805 "(%d) exceeds allowable "
806 "limit (NL_MSGMAX)\n",
807 fldesc, ptr);
808 return (1);
809 }
810 }
811
812 /*
813 * For catgets(3c) write the definition and the
814 * message string to the message file. For
815 * gettext(3i) write the message string as a
816 * mesgid - indicate a mesgid has been output
817 * so that a msgstr can follow.
818 */
819 if (cflag == 1) {
820 if (fprintf(fdmsgs, "%d\t%s", ptr,
821 token) < 0) {
822 (void) fprintf(stderr,
823 Errmsg_wrte, flmsgs,
824 strerror(errno));
825 return (1);
826 }
827 } else {
828 if (fprintf(fdmsgs, "msgid\t\"") < 0) {
829 (void) fprintf(stderr,
830 Errmsg_wrte, flmsgs,
831 strerror(errno));
832 return (1);
833 }
834 msgid = 1;
835 }
836 }
837
838 /*
839 * The message itself is a quoted string as this makes
840 * embedding spaces at the start (or the end) of the
841 * string very easy.
842 */
843 if (*token != '"') {
844 (void) fprintf(stderr, Errmsg_malt, fldesc,
845 line);
846 return (1);
847 }
848
849 (void) strcpy(defn, _defn);
850
851 /*
852 * Write the tag to the lint definitions.
853 */
854 if (fdlint) {
855 if (fprintf(fdlint, "\n#define\t%s\t",
856 _defn) < 0) {
857 (void) fprintf(stderr, Errmsg_wrte,
858 fllint, strerror(errno));
859 return (1);
860 }
861 }
862
863 len = 0;
864
865 /*
866 * Write each character of the message string to the
867 * data array. Translate any escaped characters - use
868 * the same specially recognized characters as defined
869 * by gencat(1).
870 */
871 message:
872 if (*token == '"') {
873 if (fdlint &&
874 (fprintf(fdlint, "%c", *token) < 0)) {
875 (void) fprintf(stderr, Errmsg_wrte,
876 fllint, strerror(errno));
877 return (1);
878 }
879 token++;
880 }
881 while (*token) {
882 char _token;
883
884 if ((*token == '\\') && (escape == 0)) {
885 escape = 1;
886 if (fdlint && (*(token + 1) != '\n') &&
887 fprintf(fdlint, "%c", *token) < 0) {
888 (void) fprintf(stderr,
889 Errmsg_wrte, fllint,
890 strerror(errno));
891 return (1);
892 }
893 token++;
894 continue;
895 }
896 if (escape) {
897 if (*token == 'n')
898 _token = '\n';
899 else if (*token == 't')
900 _token = '\t';
901 else if (*token == 'v')
902 _token = '\v';
903 else if (*token == 'b')
904 _token = '\b';
905 else if (*token == 'f')
906 _token = '\f';
907 else if (*token == '\\')
908 _token = '\\';
909 else if (*token == '"')
910 _token = '"';
911 else if (*token == '\n')
912 break;
913 else
914 _token = *token;
915
916 if (fdmsgs && (prtmsgs == 1) &&
917 (fprintf(fdmsgs, "\\") < 0)) {
918 (void) fprintf(stderr,
919 Errmsg_wrte, flmsgs,
920 strerror(errno));
921 return (1);
922 }
923 } else {
924 /*
925 * If this is the trailing quote then
926 * thats the last of the message string.
927 * Eat up any remaining white space and
928 * unless an escape character is found
929 * terminate the data string with a 0.
930 */
931 /* BEGIN CSTYLED */
932 if (*token == '"') {
933 if (fdlint && (fprintf(fdlint,
934 "%c", *token) < 0)) {
935 (void) fprintf(stderr,
936 Errmsg_wrte, fllint,
937 strerror(errno));
938 return (1);
939 }
940
941 if (fdmsgs && (prtmsgs == 1) &&
942 (fprintf(fdmsgs, "%c",
943 *token) < 0)) {
944 (void) fprintf(stderr,
945 Errmsg_wrte, flmsgs,
946 strerror(errno));
947 return (1);
948 }
949
950 while (*++token) {
951 if (*token == '\n')
952 break;
953 }
954 _token = '\0';
955 } else
956 _token = *token;
957 /* END CSTYLED */
958 }
959
960 if (fdmsgs && (prtmsgs == 1) &&
961 (fprintf(fdmsgs, "%c", *token) < 0)) {
962 (void) fprintf(stderr, Errmsg_wrte,
963 flmsgs, strerror(errno));
964 return (1);
965 }
966
967 if (fdlint && fprintf(fdlint,
968 "%c", *token) < 0) {
969 (void) fprintf(stderr, Errmsg_wrte,
970 fllint, strerror(errno));
971 return (1);
972 }
973
974 if (len >= bufsize) {
975 bufsize += LINE_MAX;
976 if ((token_buffer = realloc(
977 token_buffer, bufsize)) == 0) {
978 (void) fprintf(stderr,
979 Errmsg_nmem,
980 strerror(errno));
981 return (1);
982 }
983 }
984 token_buffer[len] = _token;
985 ptr++, token++, len++;
986 escape = 0;
987
988 if (_token == '\0')
989 break;
990 }
991
992 /*
993 * After the complete message string has been processed
994 * (including its continuation beyond one line), create
995 * a string size definition.
996 */
997 if (escape == 0) {
998 const char *form = "#define\t%s_SIZE\t%d\n";
999
1000 token_buffer[len] = '\0';
1001
1002 message_append(defn, token_buffer);
1003
1004 if (fdlint && fprintf(fdlint, form, defn,
1005 (len - 1)) < 0) {
1006 (void) fprintf(stderr, Errmsg_wrte,
1007 fllint, strerror(errno));
1008 return (1);
1009 }
1010 }
1011 break;
1012
1013 default:
1014 /*
1015 * Empty lines are passed through to the message file.
1016 */
1017 while (isspace(*token))
1018 token++;
1019
1020 if (*token == 0) {
1021 if (msgid || (fdmsgs && (prtmsgs == 1))) {
1022 /*
1023 * If a msgid has been output a msgstr
1024 * must follow before we digest the new
1025 * token.
1026 */
1027 if (msgid) {
1028 msgid = 0;
1029 str = "msgstr\t\"\"\n\n";
1030 } else
1031 str = "\n";
1032
1033 if (fprintf(fdmsgs, str) < 0) {
1034 (void) fprintf(stderr,
1035 Errmsg_wrte, flmsgs,
1036 strerror(errno));
1037 return (1);
1038 }
1039 }
1040 break;
1041 }
1042
1043 /*
1044 * If an escape is in effect then any tokens are taken
1045 * to be message continuations.
1046 */
1047 if (escape) {
1048 escape = 0;
1049 goto message;
1050 }
1051
1052 (void) fprintf(stderr, "file %s: line %d: invalid "
1053 "input does not start with #, $ or @\n", fldesc,
1054 line);
1055 return (1);
1056 }
1057 line++;
1058 }
1059
1060 free(token_buffer);
1061
1062 return (0);
1063 }
1064
1065 int
main(int argc,char ** argv)1066 main(int argc, char ** argv)
1067 {
1068 opterr = 0;
1069 while ((line = getopt(argc, argv, "cd:h:lm:n:i:v")) != EOF) {
1070 switch (line) {
1071 case 'c': /* catgets instead of gettext */
1072 cflag = 1;
1073 break;
1074 case 'd': /* new message data filename */
1075 fldata = optarg; /* (msg.c is default) */
1076 break;
1077 case 'h': /* new message defs filename */
1078 fldefs = optarg; /* (msg.h is default) */
1079 break;
1080 case 'i': /* input message ids from */
1081 flmids = optarg; /* from this file */
1082 break;
1083 case 'l': /* define message data arrays */
1084 lflag = 1; /* to be local (static) */
1085 break;
1086 case 'm': /* generate message database */
1087 flmsgs = optarg; /* to this file */
1088 break;
1089 case 'n': /* new data array and func */
1090 interface = optarg; /* name (msg is default) */
1091 break;
1092 case 'v':
1093 vflag = 1; /* set verbose flag */
1094 break;
1095 case '?':
1096 (void) fprintf(stderr, Errmsg_use, argv[0]);
1097 exit(1);
1098 default:
1099 break;
1100 }
1101 }
1102
1103 /*
1104 * Validate the we have been given at least one input file.
1105 */
1106 if ((argc - optind) < 1) {
1107 (void) fprintf(stderr, Errmsg_use);
1108 exit(1);
1109 }
1110
1111 /*
1112 * Open all the required output files.
1113 */
1114 if (fldefs) {
1115 if ((fddefs = fopen(fldefs, "w+")) == NULL) {
1116 (void) fprintf(stderr, Errmsg_opne, fldefs,
1117 strerror(errno));
1118 return (1);
1119 }
1120 }
1121 if (fldata) {
1122 if (fldefs && (strcmp(fldefs, fldata) == 0))
1123 fddata = fddefs;
1124 else if ((fddata = fopen(fldata, "w+")) == NULL) {
1125 (void) fprintf(stderr, Errmsg_opne, fldata,
1126 strerror(errno));
1127 return (1);
1128 }
1129 }
1130 if (fddefs && fddata) {
1131 (void) sprintf(fllint, "%s.%d.XXXXXX", nmlint, (int)getpid());
1132 if ((mkstemp(fllint) == -1) ||
1133 ((fdlint = fopen(fllint, "w+")) == NULL)) {
1134 (void) fprintf(stderr, Errmsg_opne, fllint,
1135 strerror(errno));
1136 return (1);
1137 }
1138 }
1139 if (flmsgs) {
1140 if ((fdmsgs = fopen(flmsgs, "w+")) == NULL) {
1141 (void) fprintf(stderr, Errmsg_opne, flmsgs,
1142 strerror(errno));
1143 return (1);
1144 }
1145 }
1146 if (flmids) {
1147 if ((fdmids = fopen(flmids, "r")) == NULL) {
1148 (void) fprintf(stderr, Errmsg_opne, flmids,
1149 strerror(errno));
1150 return (1);
1151 }
1152 }
1153
1154
1155 /*
1156 * Initialize the message definition and message data streams.
1157 */
1158 if (fddefs) {
1159 if (init_defs())
1160 return (1);
1161 }
1162
1163 /*
1164 * Read the input message file, and for each line process accordingly.
1165 */
1166 for (; optind < argc; optind++) {
1167 int err;
1168
1169 fldesc = argv[optind];
1170
1171 if ((fddesc = fopen(fldesc, "r")) == NULL) {
1172 (void) fprintf(stderr, Errmsg_opne, fldesc,
1173 strerror(errno));
1174 return (1);
1175 }
1176 err = file();
1177 (void) fclose(fddesc);
1178
1179 if (err != 0)
1180 return (1);
1181 }
1182
1183 /*
1184 * If a msgid has been output a msgstr must follow before we end the
1185 * file.
1186 */
1187 if (msgid) {
1188 msgid = 0;
1189 if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
1190 (void) fprintf(stderr, Errmsg_wrte, flmsgs,
1191 strerror(errno));
1192 return (1);
1193 }
1194 }
1195
1196 if (fdmids)
1197 (void) fclose(fdmids);
1198 if (fdmsgs)
1199 (void) fclose(fdmsgs);
1200
1201 if (fddefs) {
1202 if (output_defs())
1203 return (1);
1204 }
1205
1206 /*
1207 * Finish off any generated data and header file.
1208 */
1209 if (fldata) {
1210 if (output_data())
1211 return (1);
1212 }
1213 if (fddefs) {
1214 if (fini_defs())
1215 return (1);
1216 }
1217
1218 if (vflag)
1219 dump_stringtab(stp);
1220
1221 /*
1222 * Close up everything and go home.
1223 */
1224 if (fddata)
1225 (void) fclose(fddata);
1226 if (fddefs && (fddefs != fddata))
1227 (void) fclose(fddefs);
1228 if (fddefs && fddata) {
1229 (void) fclose(fdlint);
1230 (void) unlink(fllint);
1231 }
1232
1233 if (stp)
1234 st_destroy(stp);
1235
1236 return (0);
1237 }
1238