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