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 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <stdio.h>
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <libintl.h>
34 #include <locale.h>
35 #include <libgen.h>
36 #include <ctype.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41
42 #include "genmsg.h"
43
44 #define SET_TOKEN "$set"
45 #define DELSET_TOKEN "$delset"
46 #define QUOTE_TOKEN "$quote"
47
48 #define SkipSpace(s) while (*(s) == ' ' || *(s) == '\t') s++
49
50 extern char *program; /* from main.c */
51 extern char *mctag; /* from main.c */
52 extern char *sctag; /* from main.c */
53 extern char *premsg; /* from main.c */
54 extern char *sufmsg; /* from main.c */
55 extern int suppress_error; /* from main.c */
56 extern void warning(char *); /* from genmsg.l */
57
58 typedef struct _SetID *SetID;
59 typedef struct _MsgID *MsgID;
60
61 typedef struct _SetID SetIDRec;
62 struct _SetID {
63 int id;
64 char *comment;
65 MsgID top;
66 SetID next;
67 };
68
69 typedef struct _MsgID MsgIDRec;
70 struct _MsgID {
71 int no_write;
72 int id;
73 char *msg;
74 int line;
75 char *file;
76 char *comment;
77 MsgID next;
78 };
79
80
81 /* Top pointer of the setid list. */
82 static SetID setid_top;
83
84 /* comment for messages. */
85 static char *msg_comment;
86
87 /* comment for set numbers. */
88 static char *set_comment;
89
90 /* List of set number's maximum message numbers. */
91 static int msgid_table[NL_SETMAX+1];
92
93 /* Quote character to surround messages. */
94 static char quote = QUOTE;
95
96 /* Internal functions. */
97 static void add_msgid(SetID, int, char *, char *, int, int);
98 static void add_setid(int, int, char *, char *, int, int);
99 static SetID lookup_setid(int);
100 static MsgID lookup_msgid(SetID, int, char *, char *, int);
101 static void print_prefix(FILE *, char *, int, char *);
102 static int is_bs_terminated(char *);
103 static char *ustrdup(char *);
104 static void makeup_msg(char **);
105
106 void
add_msg(int setid,int msgid,char * msg,char * file,int line,int no_write)107 add_msg(int setid, int msgid, char *msg, char *file, int line, int no_write)
108 {
109 SetID si;
110
111 if (si = lookup_setid(setid)) {
112 if (lookup_msgid(si, msgid, msg, file, line)) {
113 return; /* we already have the one. */
114 } else {
115 add_msgid(si, msgid, msg, file, line, no_write);
116 }
117 } else {
118 add_setid(setid, msgid, msg, file, line, no_write);
119 }
120 }
121
122 int
is_writable(char * file)123 is_writable(char *file)
124 {
125 struct stat buf;
126
127 if (stat(file, &buf) == -1)
128 return (TRUE);
129
130 if (access(file, W_OK) == 0)
131 return (TRUE);
132
133 return (FALSE);
134 }
135
136 void
write_msgfile(char * file)137 write_msgfile(char *file)
138 {
139 FILE *fp;
140 SetID si = setid_top;
141 char *mode = "w";
142 char pquote[2];
143
144 if (is_writable(file) == FALSE) {
145 prg_err(gettext("cannot create \"%s\": permission denied"),
146 file);
147 return;
148 }
149
150 if (IsActiveMode(AppendMode)) {
151 mode = "a";
152 }
153
154 if ((fp = fopen(file, mode)) == NULL) {
155 prg_err(gettext("cannot create \"%s\""), file);
156 return;
157 }
158
159 if (quote) {
160 pquote[0] = quote;
161 } else {
162 pquote[0] = '\0';
163 }
164 pquote[1] = '\0';
165
166 /* AppendMode is already turned off if the file doesn't exist. */
167 if (!IsActiveMode(AppendMode)) {
168 (void) fprintf(fp, "\n$quote %s\n\n", pquote);
169 }
170
171 while (si) {
172 int is_set = FALSE;
173 MsgID mi = si->top;
174 while (mi) {
175 char msg[NL_TEXTMAX+32]; /* 32 is some other stuff. */
176
177 if (mi->no_write) {
178 mi = mi->next;
179 continue;
180 }
181 if (is_set == FALSE) {
182 if (si->comment &&
183 !IsActiveMode(BackCommentMode)) {
184 (void) fprintf(fp, "\n");
185 print_prefix(fp, "$ ", TRUE,
186 si->comment);
187 (void) fprintf(fp, "$set\t%d\n",
188 si->id);
189 } else {
190 (void) fprintf(fp, "\n$set\t%d\n",
191 si->id);
192 }
193 if (si->comment &&
194 IsActiveMode(BackCommentMode)) {
195 print_prefix(fp, "$ ", TRUE,
196 si->comment);
197 }
198 (void) fprintf(fp, "\n");
199 is_set = TRUE;
200 }
201
202 makeup_msg(&(mi->msg));
203
204 (void) snprintf(msg, sizeof (msg), "%d\t%s%s%s\n",
205 mi->id, pquote, mi->msg, pquote);
206
207 if (!IsActiveMode(BackCommentMode)) {
208 if (mi->line && mi->file &&
209 IsActiveMode(LineInfoMode)) {
210 (void) fprintf(fp,
211 "$ File:%s, line:%d\n",
212 basename(mi->file), mi->line);
213 }
214
215 if (mi->comment) {
216 print_prefix(fp, "$ ", TRUE,
217 mi->comment);
218 }
219
220 if (IsActiveMode(DoubleLineMode)) {
221 print_prefix(fp, "$ ", FALSE, msg);
222 }
223 }
224
225 (void) fprintf(fp, "%s", msg);
226
227 if (IsActiveMode(BackCommentMode)) {
228 if (mi->line && mi->file &&
229 IsActiveMode(LineInfoMode)) {
230 (void) fprintf(fp,
231 "$ File:%s, line:%d\n",
232 basename(mi->file), mi->line);
233 }
234
235 if (mi->comment) {
236 print_prefix(fp, "$ ", TRUE,
237 mi->comment);
238 }
239
240 if (IsActiveMode(DoubleLineMode)) {
241 print_prefix(fp, "$ ", FALSE, msg);
242 }
243 }
244
245 (void) fprintf(fp, "\n");
246
247 mi = mi->next;
248 }
249 si = si->next;
250 }
251
252 (void) fclose(fp);
253 }
254
255 static SetID
lookup_setid(int id)256 lookup_setid(int id)
257 {
258 SetID si = setid_top;
259 while (si) {
260 if (si->id == id) {
261 return (si);
262 }
263 si = si->next;
264 }
265 return (NULL);
266 }
267
268 static MsgID
lookup_msgid(SetID si,int msgid,char * msg,char * file,int line)269 lookup_msgid(SetID si, int msgid, char *msg, char *file, int line)
270 {
271 MsgID mi = si->top;
272 while (mi) {
273 if (mi->id == msgid) {
274 /* same setid & msgid, but different msg. */
275 if (strcmp(mi->msg, msg)) {
276 src_err(file, line, gettext(
277 "multiple messages: set number %d, message number %d\n"
278 " current : \"%s\"\n"
279 " previous: \"%s\" : \"%s\", line %d"),
280 si->id, mi->id,
281 msg,
282 mi->msg, mi->file, mi->line);
283 }
284 return (mi);
285 }
286 mi = mi->next;
287 }
288 return (NULL);
289 }
290
291 static void
add_msgid(SetID si,int msgid,char * msg,char * file,int line,int no_write)292 add_msgid(SetID si, int msgid, char *msg, char *file, int line, int no_write)
293 {
294 MsgID mi = si->top, newmi, prev = NULL;
295 int len = strlen(msg);
296
297 if (msgid == 0) {
298 src_err(file, line, gettext("improper message number: %d"),
299 msgid);
300 return;
301 }
302
303 if (msgid > NL_MSGMAX) {
304 src_err(file, line, gettext("too large message number: %d"),
305 msgid);
306 return;
307 }
308
309 if (len > NL_TEXTMAX) {
310 src_err(file, line, gettext("too long message text"));
311 return;
312 }
313
314 while (mi) {
315 if (mi->id > msgid) {
316 break;
317 }
318 prev = mi;
319 mi = mi->next;
320 }
321
322 if ((newmi = malloc(sizeof (MsgIDRec))) == NULL) {
323 prg_err(gettext("fatal: out of memory"));
324 exit(EXIT_FAILURE);
325 }
326
327 newmi->no_write = no_write;
328 newmi->id = msgid;
329 newmi->msg = ustrdup(msg);
330 newmi->file = ustrdup(file);
331 newmi->line = line;
332 newmi->next = mi;
333
334 if (msg_comment) {
335 newmi->comment = ustrdup(msg_comment);
336 free(msg_comment);
337 msg_comment = NULL;
338 } else {
339 newmi->comment = NULL;
340 }
341
342 if (prev == NULL) {
343 si->top = newmi;
344 } else {
345 prev->next = newmi;
346 }
347 }
348
349 static void
add_setid(int setid,int msgid,char * msg,char * file,int line,int no_write)350 add_setid(int setid, int msgid, char *msg, char *file, int line, int no_write)
351 {
352 SetID si = setid_top, newsi, prev = NULL;
353
354 while (si) {
355 if (si->id > setid) {
356 break;
357 }
358 prev = si;
359 si = si->next;
360 }
361
362 if ((newsi = malloc(sizeof (SetIDRec))) == NULL) {
363 prg_err(gettext("fatal: out of memory"));
364 exit(EXIT_FAILURE);
365 }
366
367 newsi->id = setid;
368 newsi->top = NULL;
369 newsi->next = si;
370
371 if (set_comment) {
372 newsi->comment = ustrdup(set_comment);
373 free(set_comment);
374 set_comment = NULL;
375 } else {
376 newsi->comment = NULL;
377 }
378
379 if (prev == NULL) {
380 setid_top = newsi;
381 } else {
382 prev->next = newsi;
383 }
384
385 add_msgid(newsi, msgid, msg, file, line, no_write);
386 }
387
388 static void
print_prefix(FILE * fp,char * prefix,int rm_blank,char * str)389 print_prefix(FILE *fp, char *prefix, int rm_blank, char *str)
390 {
391 (void) fprintf(fp, "%s", prefix);
392 while (*str) {
393 (void) fputc(*str, fp);
394 if (*str == '\n' && *(str+1) != '\0') {
395 (void) fprintf(fp, "%s", prefix);
396 if (rm_blank == TRUE) {
397 str++;
398 SkipSpace(str);
399 continue;
400 }
401 }
402 str++;
403 }
404 if (*(str-1) != '\n') {
405 (void) fputc('\n', fp);
406 }
407 }
408
409 int
read_projfile(char * file)410 read_projfile(char *file)
411 {
412 FILE *fp;
413 char line[LINE_MAX];
414
415 if (file == NULL) {
416 return (0);
417 }
418
419 if ((fp = fopen(file, "r")) == NULL) {
420 return (0);
421 }
422
423 while (fgets(line, sizeof (line), fp) != NULL) {
424 char *p = line;
425 int n, setid, msgid;
426
427 SkipSpace(p);
428
429 if (*p == '#' || *p == '\n') {
430 continue;
431 }
432
433 n = sscanf(p, "%d %d", &setid, &msgid);
434
435 if (n == 2) {
436 if (setid > NL_SETMAX) {
437 prg_err(gettext("%s: too large set number: %d"),
438 file, setid);
439 continue;
440 }
441 msgid_table[setid] = msgid;
442 } else {
443 prg_err(gettext(
444 "warning: %s: missing or invalid entry"), file);
445 }
446 }
447
448 (void) fclose(fp);
449
450 return (1);
451 }
452
453 void
write_projfile(char * file)454 write_projfile(char *file)
455 {
456 FILE *fp;
457 register int i;
458
459 if (is_writable(file) == FALSE) {
460 prg_err(gettext("cannot create \"%s\": permission denied"),
461 file);
462 return;
463 }
464
465 if ((fp = fopen(file, "w")) == NULL) {
466 prg_err(gettext("cannot create \"%s\""), file);
467 return;
468 }
469
470 for (i = 1; i <= NL_SETMAX; i++) {
471 if (msgid_table[i] > 0) {
472 SetID si;
473 char *com = NULL;
474
475 if (IsActiveMode(SetCommentMode) &&
476 (si = lookup_setid(i)) && si->comment) {
477 com = si->comment;
478 }
479
480 if (com && !IsActiveMode(BackCommentMode)) {
481 print_prefix(fp, "# ", TRUE, com);
482 }
483
484 (void) fprintf(fp, "%d\t%d\n", i, msgid_table[i]);
485
486 if (com && IsActiveMode(BackCommentMode)) {
487 print_prefix(fp, "# ", TRUE, com);
488 }
489 }
490 }
491
492 (void) fclose(fp);
493 }
494
495 int
get_msgid(char * file,int line,int setid,char * str)496 get_msgid(char *file, int line, int setid, char *str)
497 {
498 SetID si = setid_top;
499 int id = msgid_table[setid];
500
501 while (si) {
502 if (si->id == setid) {
503 MsgID mi = si->top;
504 while (mi) {
505 if (strcmp(mi->msg, str) == 0) {
506 return (mi->id);
507 }
508 mi = mi->next;
509 }
510 }
511 si = si->next;
512 }
513
514 id++;
515
516 if (id > NL_MSGMAX) {
517 src_err(file, line,
518 gettext("run out of message number in set number: %d"),
519 setid);
520 return (NOMSGID);
521 }
522
523 return (msgid_table[setid] = id);
524 }
525
526 void
set_msgid(int setid,int msgid)527 set_msgid(int setid, int msgid)
528 {
529 if (msgid_table[setid] < msgid) {
530 msgid_table[setid] = msgid;
531 }
532 }
533
534 void
add_comment(Mode mode,char * str)535 add_comment(Mode mode, char *str)
536 {
537 char *tag = (mode == MsgCommentMode) ? mctag : sctag;
538 char **comment = (mode == MsgCommentMode)
539 ? &msg_comment : &set_comment;
540
541 if (strstr(str, tag) == NULL) {
542 return;
543 }
544
545 if (*comment) {
546 free(*comment);
547 }
548
549 *comment = ustrdup(str);
550 }
551
552 void
read_msgfile(char * file)553 read_msgfile(char *file)
554 {
555 FILE *fp;
556 char c = 0;
557 int line = 0;
558 int inmsg = FALSE;
559 int setid = 0, unsetid = -1, msgid = 0;
560 struct stat buf;
561
562 if ((fp = fopen(file, "r")) == NULL) {
563 prg_err(gettext("cannot open \"%s\""), file);
564 ResetActiveMode(AppendMode);
565 return;
566 }
567
568 if (stat(file, &buf) == -1 && buf.st_size == 0) {
569 ResetActiveMode(AppendMode);
570 return;
571 }
572
573 quote = c;
574
575 /*CONSTCOND*/
576 while (1) {
577 char buf[LINE_MAX];
578 char *ptr;
579 char msg[NL_TEXTMAX];
580
581 if (fgets(buf, sizeof (buf), fp) == NULL) {
582 break;
583 }
584
585 line++;
586
587 ptr = &buf[0];
588
589 SkipSpace(ptr);
590
591 if ((*ptr == '$' && (*(ptr+1) == ' ' || *(ptr+1) == '\t')) ||
592 ((*ptr == '\n') && inmsg == FALSE)) {
593 inmsg = FALSE;
594 continue;
595 }
596
597 if (strncmp(ptr, SET_TOKEN, sizeof (SET_TOKEN) - 1) == 0) {
598 if (sscanf(ptr, "%*s %d", &setid) != 1) {
599 setid = 0;
600 }
601 inmsg = FALSE;
602 continue;
603 } else if (strncmp(ptr, DELSET_TOKEN,
604 sizeof (DELSET_TOKEN) - 1) == 0) {
605 if (sscanf(ptr, "%*s %d", &unsetid) != 1) {
606 unsetid = -1;
607 }
608 inmsg = FALSE;
609 continue;
610 } else if (strncmp(ptr, QUOTE_TOKEN,
611 sizeof (QUOTE_TOKEN) - 1) == 0) {
612 if (sscanf(ptr, "%*s %c", &c) != 1) {
613 c = 0;
614 }
615 quote = c;
616 inmsg = FALSE;
617 continue;
618 }
619
620 if (setid == unsetid) {
621 continue;
622 }
623
624 if (inmsg) {
625 if (is_bs_terminated(ptr)) {
626 (void) strlcat(msg, ptr, sizeof (msg));
627 inmsg = TRUE;
628 } else {
629 int len = strlen(ptr);
630 *(ptr + len - 1) = '\0';
631 if (c && (*(ptr + len - 2) == c)) {
632 *(ptr + len - 2) = '\0';
633 }
634 (void) strlcat(msg, ptr, sizeof (msg));
635 add_msg(setid, msgid, msg, file, line, TRUE);
636 inmsg = FALSE;
637 }
638 continue;
639 }
640
641 if (isdigit((unsigned char)*ptr)) {
642 char *pptr;
643
644 SkipSpace(ptr);
645
646 msgid = (int)strtol(ptr, &pptr, 10);
647 ptr = pptr;
648
649 SkipSpace(ptr);
650
651 if (is_bs_terminated(ptr)) {
652 (void) memset(msg, 0, sizeof (msg));
653 if (c && (*ptr == c)) {
654 ptr++;
655 }
656 (void) strlcpy(msg, ptr, sizeof (msg));
657 inmsg = TRUE;
658 } else {
659 int len = strlen(ptr);
660 *(ptr + len - 1) = '\0';
661 if (c && ((*ptr == c) &&
662 (*(ptr + len - 2) == c))) {
663 *(ptr + len - 2) = '\0';
664 ptr++;
665 }
666 add_msg(setid, msgid, ptr, file, line, TRUE);
667 inmsg = FALSE;
668 }
669 }
670 }
671
672 (void) fclose(fp);
673 }
674
675 static int
is_bs_terminated(char * msg)676 is_bs_terminated(char *msg)
677 {
678 int len = strlen(msg);
679
680 while (--len >= 0) {
681 if (msg[len] == ' ' || msg[len] == '\t' || msg[len] == '\n') {
682 continue;
683 } else if (msg[len] == '\\') {
684 len--;
685 if (len >= 0 && msg[len] == '\\')
686 return (0);
687 return (1);
688 } else {
689 return (0);
690 }
691 }
692 return (0);
693 }
694
695 static char *
ustrdup(char * str)696 ustrdup(char *str)
697 {
698 char *tmp = strdup(str);
699 if (tmp == NULL) {
700 prg_err(gettext("fatal: out of memory"));
701 exit(EXIT_FAILURE);
702 }
703 return (tmp);
704 }
705
706 int
file_copy(char * in,char * out)707 file_copy(char *in, char *out)
708 {
709 int ret = TRUE;
710 FILE *fin, *fout;
711 int c;
712 sigset_t newmask, oldmask;
713
714 (void) sigemptyset(&newmask);
715 (void) sigaddset(&newmask, SIGQUIT);
716 (void) sigaddset(&newmask, SIGINT);
717 (void) sigaddset(&newmask, SIGHUP);
718 (void) sigaddset(&newmask, SIGTERM);
719 (void) sigprocmask(SIG_BLOCK, &newmask, &oldmask);
720
721 if ((fin = fopen(in, "r")) == NULL) {
722 prg_err(gettext("cannot open \"%s\""), in);
723 ret = FALSE;
724 goto done;
725 }
726
727 if ((fout = fopen(out, "w")) == NULL) {
728 prg_err(gettext("cannot create \"%s\""), out);
729 ret = FALSE;
730 goto done;
731 }
732
733 while ((c = getc(fin)) != EOF)
734 (void) putc(c, fout);
735
736 (void) fclose(fin);
737 (void) fclose(fout);
738
739 done:
740 (void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
741 return (ret);
742 }
743
744 static void
makeup_msg(char ** pmsg)745 makeup_msg(char **pmsg)
746 {
747 char buf[NL_TEXTMAX];
748 char *msg;
749
750 msg = *pmsg;
751 buf[0] = '\0';
752
753 if (IsActiveMode(TripleMode) && strchr(msg, '%') == NULL) {
754 /* there is no '%' in message. */
755 int len = strlen(msg);
756
757 if (msg[len-2] == '\\' && msg[len-1] == 'n') {
758 msg[len-2] = '\0';
759 (void) strlcat(buf, msg, sizeof (buf));
760 (void) strlcat(buf, msg, sizeof (buf));
761 (void) strlcat(buf, msg, sizeof (buf));
762 (void) strlcat(buf, "\\n", sizeof (buf));
763 } else {
764 (void) strlcat(buf, msg, sizeof (buf));
765 (void) strlcat(buf, msg, sizeof (buf));
766 (void) strlcat(buf, msg, sizeof (buf));
767 }
768 free(msg);
769 *pmsg = ustrdup(buf);
770 }
771
772 msg = *pmsg;
773 buf[0] = '\0';
774
775 if (IsActiveMode(PrefixMode)) {
776 (void) strlcat(buf, premsg, sizeof (buf));
777 (void) strlcat(buf, msg, sizeof (buf));
778 free(msg);
779 *pmsg = ustrdup(buf);
780 }
781
782 msg = *pmsg;
783 buf[0] = '\0';
784
785 if (IsActiveMode(SuffixMode)) {
786 int len = strlen(msg);
787
788 if (msg[len-2] == '\\' && msg[len-1] == 'n') {
789 msg[len-2] = '\0';
790 (void) strlcat(buf, msg, sizeof (buf));
791 (void) strlcat(buf, sufmsg, sizeof (buf));
792 (void) strlcat(buf, "\\n", sizeof (buf));
793 } else {
794 (void) strlcat(buf, msg, sizeof (buf));
795 (void) strlcat(buf, sufmsg, sizeof (buf));
796 }
797 free(msg);
798 *pmsg = ustrdup(buf);
799 }
800 }
801
802 void
prg_err(char * fmt,...)803 prg_err(char *fmt, ...)
804 {
805 va_list ap;
806
807 va_start(ap, fmt);
808
809 (void) fprintf(stderr, "%s: ", program);
810 /* LINTED: E_SEC_PRINTF_VAR_FMT */
811 (void) vfprintf(stderr, fmt, ap);
812 (void) fprintf(stderr, "\n");
813
814 va_end(ap);
815 }
816
817 void
src_err(char * file,int line,char * fmt,...)818 src_err(char *file, int line, char *fmt, ...)
819 {
820 va_list ap;
821
822 if (suppress_error == TRUE) {
823 return;
824 }
825
826 va_start(ap, fmt);
827
828 (void) fprintf(stderr, gettext("\"%s\", line %d: "), file, line);
829 /* LINTED: E_SEC_PRINTF_VAR_FMT */
830 (void) vfprintf(stderr, fmt, ap);
831 (void) fprintf(stderr, "\n");
832
833 va_end(ap);
834 }
835