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) 1995 by Sun Microsystems, Inc. 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 cat_msg(char *, char *, int); 105 static void makeup_msg(char **); 106 107 void 108 add_msg(int setid, int msgid, char *msg, char *file, int line, int no_write) 109 { 110 SetID si; 111 112 if (si = lookup_setid(setid)) { 113 if (lookup_msgid(si, msgid, msg, file, line)) { 114 return; /* we already have the one. */ 115 } else { 116 add_msgid(si, msgid, msg, file, line, no_write); 117 } 118 } else { 119 add_setid(setid, msgid, msg, file, line, no_write); 120 } 121 } 122 123 int 124 is_writable(char *file) 125 { 126 struct stat buf; 127 128 if (stat(file, &buf) == -1) 129 return (TRUE); 130 131 if (access(file, W_OK) == 0) 132 return (TRUE); 133 134 return (FALSE); 135 } 136 137 void 138 write_msgfile(char *file) 139 { 140 FILE *fp; 141 SetID si = setid_top; 142 char *mode = "w"; 143 char pquote[2]; 144 145 if (is_writable(file) == FALSE) { 146 prg_err(gettext("cannot create \"%s\": permission denied"), 147 file); 148 return; 149 } 150 151 if (IsActiveMode(AppendMode)) { 152 mode = "a"; 153 } 154 155 if ((fp = fopen(file, mode)) == NULL) { 156 prg_err(gettext("cannot create \"%s\""), file); 157 return; 158 } 159 160 if (quote) { 161 (void) sprintf(pquote, "%c", quote); 162 } else { 163 (void) sprintf(pquote, ""); 164 } 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) sprintf(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 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 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, 277 gettext("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 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 = (MsgID) 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 = (char *) ustrdup(msg); 330 newmi->file = (char *) ustrdup(file); 331 newmi->line = line; 332 newmi->next = mi; 333 334 if (msg_comment) { 335 newmi->comment = (char *) 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 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 = (SetID) 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 = (char *) 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 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 410 read_projfile(char *file) 411 { 412 FILE *fp; 413 char line[LINE_MAX]; 414 415 if (!file) { 416 return (0); 417 } 418 419 if ((fp = fopen(file, "r")) == NULL) { 420 return (0); 421 } 422 423 while (fgets(line, LINE_MAX, 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( 444 /* for stupid cstyle */ 445 gettext("warning: %s: missing or invalid entry"), 446 file); 447 } 448 } 449 450 (void) fclose(fp); 451 452 return (1); 453 } 454 455 void 456 write_projfile(char *file) 457 { 458 FILE *fp; 459 register int i; 460 461 if (is_writable(file) == FALSE) { 462 prg_err(gettext("cannot create \"%s\": permission denied"), 463 file); 464 return; 465 } 466 467 if ((fp = fopen(file, "w")) == NULL) { 468 prg_err(gettext("cannot create \"%s\""), file); 469 return; 470 } 471 472 for (i = 1; i <= NL_SETMAX; i++) { 473 if (msgid_table[i] > 0) { 474 SetID si; 475 char *com = NULL; 476 477 if (IsActiveMode(SetCommentMode) && 478 (si = lookup_setid(i)) && si->comment) { 479 com = si->comment; 480 } 481 482 if (com && !IsActiveMode(BackCommentMode)) { 483 print_prefix(fp, "# ", TRUE, com); 484 } 485 486 (void) fprintf(fp, "%d\t%d\n", i, msgid_table[i]); 487 488 if (com && IsActiveMode(BackCommentMode)) { 489 print_prefix(fp, "# ", TRUE, com); 490 } 491 } 492 } 493 494 (void) fclose(fp); 495 } 496 497 int 498 get_msgid(char *file, int line, int setid, char *str) 499 { 500 SetID si = setid_top; 501 int id = msgid_table[setid]; 502 503 while (si) { 504 if (si->id == setid) { 505 MsgID mi = si->top; 506 while (mi) { 507 if (strcmp(mi->msg, str) == 0) { 508 return (mi->id); 509 } 510 mi = mi->next; 511 } 512 } 513 si = si->next; 514 } 515 516 id++; 517 518 if (id > NL_MSGMAX) { 519 src_err(file, line, 520 gettext("run out of message number in set number: %d"), 521 setid); 522 return (NOMSGID); 523 } 524 525 return (msgid_table[setid] = id); 526 } 527 528 void 529 set_msgid(int setid, int msgid) 530 { 531 if (msgid_table[setid] < msgid) { 532 msgid_table[setid] = msgid; 533 } 534 } 535 536 void 537 add_comment(Mode mode, char *str) 538 { 539 char *tag = (mode == MsgCommentMode) ? mctag : sctag; 540 char **comment = (mode == MsgCommentMode) 541 ? &msg_comment : &set_comment; 542 543 if (!strstr(str, tag)) { 544 return; 545 } 546 547 if (*comment) { 548 free(*comment); 549 } 550 551 *comment = (char *) ustrdup(str); 552 } 553 554 void 555 read_msgfile(char *file) 556 { 557 FILE *fp; 558 char c = 0; 559 int line = 0; 560 int inmsg = FALSE; 561 int setid = 0, unsetid = -1, msgid = 0; 562 struct stat buf; 563 564 if ((fp = fopen(file, "r")) == NULL) { 565 prg_err(gettext("cannot open \"%s\""), file); 566 ResetActiveMode(AppendMode); 567 return; 568 } 569 570 if (stat(file, &buf) == -1 && buf.st_size == 0) { 571 ResetActiveMode(AppendMode); 572 return; 573 } 574 575 quote = c; 576 577 /*CONSTCOND*/ 578 while (1) { 579 char buf[LINE_MAX]; 580 char *ptr; 581 char msg[NL_TEXTMAX]; 582 583 if (fgets(buf, sizeof (buf), fp) == NULL) { 584 break; 585 } 586 587 line++; 588 589 ptr = &buf[0]; 590 591 SkipSpace(ptr); 592 593 if ((*ptr == '$' && (*(ptr+1) == ' ' || *(ptr+1) == '\t')) || 594 ((*ptr == '\n') && inmsg == FALSE)) { 595 inmsg = FALSE; 596 continue; 597 } 598 599 if (strncmp(ptr, SET_TOKEN, sizeof (SET_TOKEN) - 1) == 0) { 600 if (sscanf(ptr, "%*s %d", &setid) != 1) { 601 setid = 0; 602 } 603 inmsg = FALSE; 604 continue; 605 } else if (strncmp(ptr, DELSET_TOKEN, 606 sizeof (DELSET_TOKEN) - 1) == 0) { 607 if (sscanf(ptr, "%*s %d", &unsetid) != 1) { 608 unsetid = -1; 609 } 610 inmsg = FALSE; 611 continue; 612 } else if (strncmp(ptr, QUOTE_TOKEN, 613 sizeof (QUOTE_TOKEN) - 1) == 0) { 614 if (sscanf(ptr, "%*s %c", &c) != 1) { 615 c = 0; 616 } 617 quote = c; 618 inmsg = FALSE; 619 continue; 620 } 621 622 if (setid == unsetid) { 623 continue; 624 } 625 626 if (inmsg) { 627 if (is_bs_terminated(ptr)) { 628 (void) strcat(msg, ptr); 629 inmsg = TRUE; 630 } else { 631 int len = strlen(ptr); 632 *(ptr + len - 1) = '\0'; 633 if (c && (*(ptr + len - 2) == c)) { 634 *(ptr + len - 2) = '\0'; 635 } 636 (void) strcat(msg, ptr); 637 add_msg(setid, msgid, msg, file, line, TRUE); 638 inmsg = FALSE; 639 } 640 continue; 641 } 642 643 if (isdigit(*ptr)) { 644 char tmp[32]; 645 int i = 0; 646 647 SkipSpace(ptr); 648 649 while (isdigit(*ptr)) { 650 tmp[i++] = *ptr++; 651 } 652 tmp[i] = '\0'; 653 msgid = atoi(tmp); 654 655 SkipSpace(ptr); 656 657 if (is_bs_terminated(ptr)) { 658 (void) memset(msg, 0, NL_TEXTMAX); 659 if (c && (*ptr == c)) { 660 ptr++; 661 } 662 (void) strcpy(msg, ptr); 663 inmsg = TRUE; 664 } else { 665 int len = strlen(ptr); 666 *(ptr + len - 1) = '\0'; 667 if (c && ((*ptr == c) && 668 (*(ptr + len - 2) == c))) { 669 *(ptr + len - 2) = '\0'; 670 ptr++; 671 } 672 add_msg(setid, msgid, ptr, file, line, TRUE); 673 inmsg = FALSE; 674 } 675 } 676 } 677 678 (void) fclose(fp); 679 } 680 681 static int 682 is_bs_terminated(char *msg) 683 { 684 int len = strlen(msg); 685 686 while (--len >= 0) { 687 if (msg[len] == ' ' || msg[len] == '\t' || msg[len] == '\n') { 688 continue; 689 } else if (msg[len] == '\\') { 690 len--; 691 if (len >= 0 && msg[len] == '\\') 692 return (0); 693 return (1); 694 } else { 695 return (0); 696 } 697 } 698 return (0); 699 } 700 701 static char * 702 ustrdup(char *str) 703 { 704 char *tmp = strdup(str); 705 if (!tmp) { 706 prg_err(gettext("fatal: out of memory")); 707 exit(EXIT_FAILURE); 708 } 709 return (tmp); 710 } 711 712 int 713 file_copy(char *in, char *out) 714 { 715 int ret = TRUE; 716 FILE *fin, *fout; 717 int c; 718 sigset_t newmask, oldmask; 719 720 (void) sigemptyset(&newmask); 721 (void) sigaddset(&newmask, SIGQUIT); 722 (void) sigaddset(&newmask, SIGINT); 723 (void) sigaddset(&newmask, SIGHUP); 724 (void) sigaddset(&newmask, SIGTERM); 725 (void) sigprocmask(SIG_BLOCK, &newmask, &oldmask); 726 727 if ((fin = fopen(in, "r")) == NULL) { 728 prg_err(gettext("cannot open \"%s\""), in); 729 ret = FALSE; 730 goto done; 731 } 732 733 if ((fout = fopen(out, "w")) == NULL) { 734 prg_err(gettext("cannot create \"%s\""), out); 735 ret = FALSE; 736 goto done; 737 } 738 739 while ((c = getc(fin)) != EOF) 740 (void) putc(c, fout); 741 742 (void) fclose(fin); 743 (void) fclose(fout); 744 745 done: 746 (void) sigprocmask(SIG_SETMASK, &oldmask, NULL); 747 return (ret); 748 } 749 750 static void 751 cat_msg(char *out, char *msg, int max) 752 { 753 if (strlen(out) > max) { 754 return; 755 } 756 757 (void) strncat(out, msg, max); 758 759 if (strlen(out) > max) { 760 out[max] = '\0'; 761 } 762 } 763 764 static void 765 makeup_msg(char **pmsg) 766 { 767 char buf[NL_TEXTMAX]; 768 char *msg; 769 770 msg = *pmsg; 771 (void) memset((void *) buf, 0, NL_TEXTMAX); 772 773 if (IsActiveMode(TripleMode) && !strchr(msg, '%')) { 774 /* there is no '%' in message. */ 775 int len = strlen(msg); 776 777 if (msg[len-2] == '\\' && msg[len-1] == 'n') { 778 msg[len-2] = '\0'; 779 cat_msg(buf, msg, (NL_TEXTMAX - 2)); 780 cat_msg(buf, msg, (NL_TEXTMAX - 2)); 781 cat_msg(buf, msg, (NL_TEXTMAX - 2)); 782 cat_msg(buf, "\\n", NL_TEXTMAX); 783 } else { 784 cat_msg(buf, msg, NL_TEXTMAX); 785 cat_msg(buf, msg, NL_TEXTMAX); 786 cat_msg(buf, msg, NL_TEXTMAX); 787 } 788 free(msg); 789 *pmsg = (char *) ustrdup(buf); 790 } 791 792 msg = *pmsg; 793 (void) memset((void *) buf, 0, NL_TEXTMAX); 794 795 if (IsActiveMode(PrefixMode)) { 796 cat_msg(buf, premsg, NL_TEXTMAX); 797 cat_msg(buf, msg, NL_TEXTMAX); 798 free(msg); 799 *pmsg = (char *) ustrdup(buf); 800 } 801 802 msg = *pmsg; 803 (void) memset((void *) buf, 0, NL_TEXTMAX); 804 805 if (IsActiveMode(SuffixMode)) { 806 int len = strlen(msg); 807 808 if (msg[len-2] == '\\' && msg[len-1] == 'n') { 809 msg[len-2] = '\0'; 810 cat_msg(buf, msg, (NL_TEXTMAX - 2)); 811 cat_msg(buf, sufmsg, (NL_TEXTMAX - 2)); 812 cat_msg(buf, "\\n", NL_TEXTMAX); 813 } else { 814 cat_msg(buf, msg, NL_TEXTMAX); 815 cat_msg(buf, sufmsg, NL_TEXTMAX); 816 } 817 free(msg); 818 *pmsg = (char *) ustrdup(buf); 819 } 820 } 821 822 void 823 prg_err(char *fmt, ...) 824 { 825 va_list ap; 826 char buf[BUFSIZ]; 827 828 va_start(ap, fmt); 829 830 (void) vsprintf(buf, fmt, ap); 831 832 (void) fprintf(stderr, "%s: %s\n", program, buf); 833 834 va_end(ap); 835 } 836 837 void 838 src_err(char *file, int line, char *fmt, ...) 839 { 840 va_list ap; 841 char buf[BUFSIZ]; 842 char sbuf[BUFSIZ/2]; 843 char vbuf[BUFSIZ/2]; 844 845 if (suppress_error == TRUE) { 846 return; 847 } 848 849 va_start(ap, fmt); 850 851 (void) sprintf(sbuf, gettext("\"%s\", line %d: "), file, line); 852 (void) vsprintf(vbuf, fmt, ap); 853 854 (void) sprintf(buf, "%s%s\n", sbuf, vbuf); 855 (void) fputs(buf, stderr); 856 857 va_end(ap); 858 } 859