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 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 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 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 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, 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 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 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 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 == 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 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 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 527 set_msgid(int setid, int msgid) 528 { 529 if (msgid_table[setid] < msgid) { 530 msgid_table[setid] = msgid; 531 } 532 } 533 534 void 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 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 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 * 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 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 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 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 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