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