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) 1990, 1991, 1994, Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #ident "%Z%%M% %I% %E% SMI" 28 29 #include <nl_types.h> 30 #include <ctype.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <limits.h> 34 #include <memory.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <locale.h> 40 #include <libintl.h> 41 42 #ifndef NL_MSGMAX 43 #define NL_MSGMAX 32767 44 #endif 45 46 #ifndef NL_SETMAX 47 #define NL_SETMAX 255 48 #endif 49 50 #ifndef NL_TEXTMAX 51 #define NL_TEXTMAX 2048 52 #endif 53 54 #define BS '\b' 55 #define CR '\r' 56 #define DOLLAR '$' 57 #define FF '\f' 58 #define NEWLINE '\n' 59 #define NUL '\000' 60 #define REVERSE_SOLIDUS '\\' 61 #define SPACE ' ' 62 #define TAB '\t' 63 #define VTAB '\v' 64 65 #define FPRINTF (void) fprintf 66 #define FREE(x) free((char *)(x)) 67 #define MALLOC(n) malloc((unsigned)(n)) 68 #define MEMCPY(dst, src, n) \ 69 (void) memcpy((char *)(dst), (char *)(src), (int)(n)) 70 #define MEMSET(s, c, n) (void) memset((char *)(s), (int)(c), (int)(n)); 71 #define MSG(n) gettext(MSG ## n) 72 #define READ(fd, p, n) read((int)(fd), (char *)(p), (unsigned)(n)) 73 #define REALLOC(x, n) realloc((char *)(x), (unsigned)(n)) 74 75 /* double linked list */ 76 struct cat_set { 77 struct cat_set *prev; 78 struct cat_set *next; 79 int set_no; 80 struct cat_msg *first_msg; 81 }; 82 83 /* double linked list */ 84 struct cat_msg { 85 struct cat_msg *prev; 86 struct cat_msg *next; 87 int msg_no; 88 int msg_len; 89 char s[1]; 90 }; 91 92 int catfd; /* File descriptor of catalog file */ 93 char *catfname; /* Catalog file name */ 94 char *msgfname; /* message source file name */ 95 int ateof; /* boolean indicating END-OF-FILE */ 96 int lineno; /* the line number of message source file */ 97 int quoting; /* boolean indicating quotes is used */ 98 int quote; /* the current quote */ 99 int text_len; /* message text length */ 100 int text_size; /* the size of allocated text memory */ 101 char *text; /* messsge text */ 102 103 struct _cat_hdr hdr; 104 int current_set_no; /* the current set number */ 105 struct cat_set *first_set; /* the pointer to the first set */ 106 struct cat_set *current_set; /* the pointer to the current set */ 107 struct cat_msg *current_msg; /* the pointer to the first message */ 108 109 110 /* Error message */ 111 /* 0 */ 112 #define MSG0 "" 113 /* 1 */ 114 #define MSG1 "usage: gencat catfile msgfile ...\n" 115 /* 2 */ 116 #define MSG2 "gencat: cannot open \"%s\"\n" 117 /* 3 */ 118 #define MSG3 "gencat: read error on \"%s\"\n" 119 /* 4 */ 120 #define MSG4 "gencat: bad magic number (%#lx)\n" 121 /* 5 */ 122 #define MSG5 "gencat: corrupt catalogue file \"%s\"\n" 123 /* 6 */ 124 #define MSG6 "gencat: memory limit exceeded\n" 125 /* 7 */ 126 #define MSG7 "gencat: seek error on \"%s\"\n" 127 /* 8 */ 128 #define MSG8 "gencat: write error on \"%s\"\n" 129 /* 9 */ 130 #define MSG9 "gencat: \"%s\", line %d: number too large (%s)\n" 131 /* 10 */ 132 #define MSG10 "gencat: \"%s\", line %d: 0 is not a permissible " \ 133 "message number\n" 134 /* 11 */ 135 #define MSG11 "gencat: \"%s\", line %d: warning, message number %d " \ 136 "exceeds limit (%d)\n" 137 /* 12 */ 138 #define MSG12 "gencat: \"%s\", line %d: missing quote (%wc)\n" 139 /* 13 */ 140 #define MSG13 "gencat: \"%s\", line %d: character value too large ('\\%o')\n" 141 /* 14 */ 142 #define MSG14 "gencat: \"%s\", line %d: extra characters following " \ 143 "message text\n" 144 /* 15 */ 145 #define MSG15 "gencat: \"%s\", line %d: extra characters following " \ 146 "$quote directive\n" 147 /* 16 */ 148 #define MSG16 "gencat: \"%s\", line %d: no set number specified in " \ 149 "$set directive\n" 150 /* 17 */ 151 #define MSG17 "getcat: \"%s\", line %d: 0 is not a permissible set number\n" 152 /* 18 */ 153 #define MSG18 "gencat: \"%s\", line %d: warning, set number %d " \ 154 "exceeds limit (%d)\n" 155 /* 19 */ 156 #define MSG19 "gencat: \"%s\", line %d: unknown directive %s\n" 157 /* 20 */ 158 #define MSG20 "gencat: \"%s\", line %d: no set number specified in " \ 159 "$delset directive\n" 160 /* 21 */ 161 #define MSG21 "stdin" 162 /* 22 */ 163 #define MSG22 "gencat: \"%s\", line %d: number or $ expected\n" 164 165 struct cat_set * 166 new_set(n) 167 int n; 168 { 169 struct cat_set *p; 170 171 p = (struct cat_set *) MALLOC(sizeof (struct cat_set)); 172 if (p == NULL) { 173 FPRINTF(stderr, MSG(6)); 174 exit(1); 175 } 176 p->next = NULL; 177 p->prev = NULL; 178 p->set_no = n; 179 p->first_msg = NULL; 180 return (p); 181 } 182 183 void 184 find_set(no) 185 int no; 186 { 187 struct cat_set *prev, *next; 188 189 if (current_set && current_set->set_no == no) { 190 return; 191 } 192 193 current_set_no = no; 194 current_msg = NULL; 195 /* if no set exists, create a new set */ 196 if (current_set == NULL) { 197 if (first_set == NULL) { 198 current_set = first_set = new_set(no); 199 return; 200 } 201 current_set = first_set; 202 if (current_set->set_no == no) 203 return; 204 } 205 206 if (current_set->set_no > no) { 207 if (first_set->set_no > no) { 208 /* prepend a new set */ 209 current_set = new_set(no); 210 current_set->next = first_set; 211 first_set->prev = current_set; 212 first_set = current_set; 213 return; 214 } 215 current_set = first_set; 216 if (current_set->set_no == no) 217 return; 218 } 219 220 /* search for the set number 'no' */ 221 while (current_set->next && current_set->next->set_no < no) 222 current_set = current_set->next; 223 224 if (current_set->next && current_set->next->set_no == no) { 225 /* set number 'no' found */ 226 current_set = current_set->next; 227 return; 228 } 229 230 /* If set number is not found, insert a new set in the middle */ 231 prev = current_set; 232 next = current_set->next; 233 current_set = new_set(no); 234 current_set->prev = prev; 235 current_set->next = next; 236 if (prev) 237 prev->next = current_set; 238 else 239 first_set = current_set; 240 if (next) 241 next->prev = current_set; 242 } 243 244 void 245 delete_set(no) 246 int no; 247 { 248 struct cat_set *prev, *next, *setp; 249 struct cat_msg *p, *q; 250 251 for (setp = first_set; setp && setp->set_no < no; setp = setp->next) 252 continue; 253 254 if (setp == NULL || setp->set_no != no) /* set not found */ 255 return; 256 257 if (setp == current_set) { 258 current_set = NULL; 259 current_msg = NULL; 260 } 261 262 /* free all messages in the set */ 263 for (p = setp->first_msg; p; p) { 264 q = p->next; 265 FREE(p); 266 p = q; 267 } 268 269 /* do the link operation to delete the set */ 270 prev = setp->prev; 271 next = setp->next; 272 FREE(setp); 273 if (prev) 274 prev->next = next; 275 else 276 first_set = next; 277 if (next) 278 next->prev = prev; 279 } 280 281 struct cat_msg * 282 new_msg(no, len, text) 283 int no; 284 int len; 285 char *text; 286 { 287 struct cat_msg *p; 288 289 p = (struct cat_msg *) MALLOC(sizeof (struct cat_msg) + len); 290 if (p == NULL) { 291 FPRINTF(stderr, MSG(6)); 292 exit(1); 293 } 294 p->next = NULL; 295 p->prev = NULL; 296 p->msg_no = no; 297 p->msg_len = len; 298 MEMCPY(p->s, text, len); 299 return (p); 300 } 301 302 303 void 304 insert_msg(no, len, text) 305 int no; 306 int len; 307 char *text; 308 { 309 struct cat_msg *prev, *next; 310 311 if (current_msg == NULL) { 312 if (current_set == NULL) 313 find_set(current_set_no); 314 current_msg = current_set->first_msg; 315 if (current_msg == NULL) { 316 current_msg = new_msg(no, len, text); 317 current_set->first_msg = current_msg; 318 return; 319 } 320 } 321 if (current_msg->msg_no >= no) { 322 current_msg = current_set->first_msg; 323 if (current_msg->msg_no > no) { 324 current_msg = new_msg(no, len, text); 325 current_msg->next = current_set->first_msg; 326 current_set->first_msg->prev = current_msg; 327 current_set->first_msg = current_msg; 328 return; 329 } 330 if (current_msg->msg_no == no) { 331 current_msg = new_msg(no, len, text); 332 current_msg->next = current_set->first_msg->next; 333 if (current_set->first_msg->next) 334 current_set->first_msg->next->prev = 335 current_msg; 336 FREE(current_set->first_msg); 337 current_set->first_msg = current_msg; 338 return; 339 } 340 } 341 while (current_msg->next && current_msg->next->msg_no < no) 342 current_msg = current_msg->next; 343 344 /* 345 * if the same msg number is found, then delte the message and 346 * insert the new message. This is same as replacing message. 347 */ 348 if (current_msg->next && current_msg->next->msg_no == no) { 349 current_msg = current_msg->next; 350 prev = current_msg->prev; 351 next = current_msg->next; 352 FREE(current_msg); 353 } else { 354 prev = current_msg; 355 next = current_msg->next; 356 } 357 358 current_msg = new_msg(no, len, text); 359 current_msg->prev = prev; 360 current_msg->next = next; 361 if (prev) 362 prev->next = current_msg; 363 else 364 current_set->first_msg = current_msg; 365 if (next) 366 next->prev = current_msg; 367 } 368 369 void 370 delete_msg(no) 371 int no; 372 { 373 struct cat_set *p = current_set; 374 struct cat_msg *prev, *next; 375 376 if (current_msg == NULL) { 377 if (current_set == NULL) 378 for (p = first_set; p && p->set_no < current_set_no; 379 p = p->next) 380 continue; 381 if (p == NULL || p->set_no != current_set_no) 382 return; 383 current_set = p; 384 current_msg = current_set->first_msg; 385 if (current_msg == NULL) 386 return; 387 } 388 if (current_msg->msg_no > no) 389 current_msg = current_set->first_msg; 390 391 while (current_msg && current_msg->msg_no != no) 392 current_msg = current_msg->next; 393 394 if (current_msg && current_msg->msg_no == no) { 395 prev = current_msg->prev; 396 next = current_msg->next; 397 FREE(current_msg); 398 if (prev) { 399 current_msg = prev; 400 prev->next = next; 401 } else { 402 current_set->first_msg = next; 403 current_msg = next; 404 } 405 if (next) 406 next->prev = prev; 407 } 408 } 409 410 int 411 read_block(fd, p, n, pathname) 412 int fd; 413 char *p; 414 int n; 415 char *pathname; 416 { 417 int nbytes, bytes_read; 418 419 if (n == 0) 420 return (0); 421 422 nbytes = 0; 423 while (nbytes < n) { 424 bytes_read = READ(fd, p + nbytes, n - nbytes); 425 if (bytes_read < 0) { 426 if (errno != EINTR) { 427 FPRINTF(stderr, MSG(3), pathname); 428 perror(""); 429 exit(1); 430 } 431 } else if (bytes_read == 0) 432 break; 433 else 434 nbytes += bytes_read; 435 } 436 437 return (nbytes); 438 } 439 440 /* 441 * Check if catalog file read is valid 442 * 443 */ 444 int 445 cat_ok(cat) 446 char *cat; 447 { 448 int i, j; 449 int nmsgs; 450 int msg_no; 451 struct _cat_msg_hdr *msg; 452 int set_no; 453 int first_msg_hdr; 454 struct _cat_set_hdr *set; 455 456 set = (struct _cat_set_hdr *) cat; 457 set_no = 0; 458 for (i = 0; i < hdr.__nsets; ++set, ++i) { 459 if (set->__set_no < set_no) 460 return (0); 461 set_no = set->__set_no; 462 nmsgs = set->__nmsgs; 463 if (nmsgs < 0) 464 return (0); 465 if (nmsgs == 0) 466 continue; 467 first_msg_hdr = set->__first_msg_hdr; 468 if (first_msg_hdr < 0) 469 return (0); 470 if (hdr.__msg_hdr_offset + (first_msg_hdr + nmsgs) * 471 _CAT_MSG_HDR_SIZE > hdr.__mem) 472 return (0); 473 474 msg = (struct _cat_msg_hdr *) (cat + hdr.__msg_hdr_offset) + 475 first_msg_hdr; 476 msg_no = 0; 477 for (j = 0; j < nmsgs; ++msg, ++j) { 478 if (msg->__msg_no < msg_no) 479 return (0); 480 msg_no = msg->__msg_no; 481 if (msg->__msg_offset < 0) 482 return (0); 483 if (hdr.__msg_text_offset + msg->__msg_offset + 484 msg->__msg_len > hdr.__mem) 485 return (0); 486 } 487 } 488 489 return (1); 490 } 491 492 /* 493 * convert a chunk of catalog file into double linked list format 494 */ 495 void 496 initcat(cat) 497 char *cat; 498 { 499 int i, j; 500 int nmsgs; 501 struct _cat_set_hdr *set; 502 struct _cat_msg_hdr *msg; 503 504 set = (struct _cat_set_hdr *) cat; 505 for (i = 0; i < hdr.__nsets; ++set, ++i) { 506 nmsgs = set->__nmsgs; 507 if (nmsgs == 0) 508 continue; 509 find_set(set->__set_no); 510 msg = (struct _cat_msg_hdr *) (cat + hdr.__msg_hdr_offset) 511 + set->__first_msg_hdr; 512 current_msg = current_set->first_msg; 513 for (j = 0; j < nmsgs; ++msg, ++j) { 514 insert_msg(msg->__msg_no, msg->__msg_len, 515 cat + hdr.__msg_text_offset + msg->__msg_offset); 516 } 517 } 518 } 519 520 /* 521 * read a catalog file in a chunk and convert it to double linked list. 522 */ 523 void 524 readcat(fd, pathname) 525 int fd; 526 char *pathname; 527 { 528 int i; 529 char *cat; 530 531 i = read_block(fd, (char *) &hdr, _CAT_HDR_SIZE, pathname); 532 if (i == 0) 533 return; 534 535 if (i >= 4 && hdr.__hdr_magic != _CAT_MAGIC) { 536 FPRINTF(stderr, MSG(4), hdr.__hdr_magic); 537 exit(1); 538 } 539 if (i < _CAT_HDR_SIZE || hdr.__nsets < 0) { 540 FPRINTF(stderr, MSG(5), pathname); 541 exit(1); 542 } 543 if (hdr.__nsets == 0) 544 return; 545 546 if (hdr.__mem < 0 || 547 hdr.__msg_hdr_offset < 0 || 548 hdr.__msg_text_offset < 0 || 549 hdr.__mem < hdr.__nsets * _CAT_SET_HDR_SIZE || 550 hdr.__mem < hdr.__msg_hdr_offset || 551 hdr.__mem < hdr.__msg_text_offset) { 552 FPRINTF(stderr, MSG(5), pathname); 553 exit(1); 554 } 555 cat = MALLOC(hdr.__mem); 556 if (cat == NULL) { 557 FPRINTF(stderr, MSG(6)); 558 exit(1); 559 } 560 i = read_block(fd, cat, hdr.__mem, pathname); 561 if (i < hdr.__mem || !cat_ok(cat)) { 562 FPRINTF(stderr, MSG(5), pathname); 563 exit(1); 564 } 565 initcat(cat); 566 567 FREE(cat); 568 } 569 570 /* 571 * Extend the memory in 1000 byte chunks whenever runs out of text space. 572 */ 573 void 574 extend_text() 575 { 576 text_size += 1000; 577 if (text) 578 text = REALLOC(text, text_size); 579 else 580 text = MALLOC(text_size); 581 if (text == NULL) { 582 FPRINTF(stderr, MSG(6)); 583 exit(1); 584 } 585 } 586 587 int 588 get_number(fp, c) 589 FILE *fp; 590 int c; 591 { 592 int i, n; 593 char *s, *t; 594 595 i = 0; 596 do { 597 while (i >= text_size) 598 extend_text(); 599 text[i] = c; 600 ++i; 601 c = getc(fp); 602 } 603 while (isdigit(c)); 604 (void) ungetc(c, fp); 605 606 while (i >= text_size) 607 extend_text(); 608 text[i] = NUL; 609 610 for (s = text; *s == '0'; ++s) 611 continue; 612 613 n = 0; 614 for (t = s; isdigit(*t); ++t) { 615 if (n > INT_MAX / 10 || 616 (n == INT_MAX / 10 && *t > '0' + INT_MAX % 10)) { 617 FPRINTF(stderr, MSG(9), msgfname, lineno, s); 618 exit(1); 619 } 620 n = 10 * n + (*t - '0'); 621 } 622 623 return (n); 624 } 625 626 void 627 get_text(fp) 628 FILE *fp; 629 { 630 int c; 631 int n; 632 633 text_len = 0; 634 c = fgetwc(fp); 635 if (quoting && c == quote) { /* quote is used */ 636 c = fgetwc(fp); 637 while (c != quote) { 638 if (c == NEWLINE || c == EOF) { 639 FPRINTF(stderr, MSG(12), msgfname, lineno, 640 quote); 641 exit(1); 642 } 643 if (c == REVERSE_SOLIDUS) { 644 c = fgetwc(fp); 645 switch (c) { 646 case EOF: 647 FPRINTF(stderr, MSG(12), msgfname, 648 lineno, quote); 649 exit(1); 650 break; 651 case NEWLINE: 652 ++lineno; 653 c = fgetwc(fp); 654 continue; 655 /* NOTREACHED */ 656 break; 657 case '0': 658 case '1': 659 case '2': 660 case '3': 661 case '4': 662 case '5': 663 case '6': 664 case '7': 665 n = (c - '0'); 666 c = fgetwc(fp); 667 if (c >= '0' && c <= '7') { 668 n = 8 * n + (c - '0'); 669 c = fgetwc(fp); 670 if (c >= '0' && c <= '7') 671 n = 8 * n + (c - '0'); 672 else 673 (void) ungetwc(c, fp); 674 } else 675 (void) ungetwc(c, fp); 676 if (n > UCHAR_MAX) { 677 FPRINTF(stderr, MSG(13), 678 msgfname, lineno, n); 679 exit(1); 680 } 681 c = n; 682 break; 683 684 case 'n': 685 c = NEWLINE; 686 break; 687 688 case 't': 689 c = TAB; 690 break; 691 692 case 'v': 693 c = VTAB; 694 break; 695 696 case 'b': 697 c = BS; 698 break; 699 700 case 'r': 701 c = CR; 702 break; 703 704 case 'f': 705 c = FF; 706 break; 707 } 708 } 709 while ((text_len + (int)MB_CUR_MAX + 1) >= text_size) 710 extend_text(); 711 if ((n = wctomb(&text[text_len], c)) > 0) 712 text_len += n; 713 c = fgetwc(fp); 714 } 715 716 while ((text_len + 1) >= text_size) 717 extend_text(); 718 text[text_len] = '\0'; 719 ++text_len; 720 721 do { 722 c = getc(fp); 723 } while (c == SPACE || c == TAB); 724 if (c == NEWLINE) { 725 ++lineno; 726 return; 727 } 728 if (c == EOF) { 729 ateof = 1; 730 return; 731 } 732 FPRINTF(stderr, MSG(14), msgfname, lineno); 733 exit(1); 734 } 735 736 while (c != NEWLINE && c != EOF) { /* quote is not used */ 737 if (c == REVERSE_SOLIDUS) { 738 c = fgetwc(fp); 739 switch (c) { 740 case EOF: 741 return; 742 743 case NEWLINE: 744 ++lineno; 745 c = fgetwc(fp); 746 continue; 747 748 case '0': 749 case '1': 750 case '2': 751 case '3': 752 case '4': 753 case '5': 754 case '6': 755 case '7': 756 n = (c - '0'); 757 c = fgetwc(fp); 758 if (c >= '0' && c <= '7') { 759 n = 8 * n + (c - '0'); 760 c = fgetwc(fp); 761 if (c >= '0' && c <= '7') 762 n = 8 * n + (c - '0'); 763 else 764 (void) ungetwc(c, fp); 765 } else 766 (void) ungetwc(c, fp); 767 if (n > UCHAR_MAX) { 768 FPRINTF(stderr, MSG(13), msgfname, 769 lineno, n); 770 exit(1); 771 } 772 c = n; 773 break; 774 775 case 'n': 776 c = NEWLINE; 777 break; 778 779 case 't': 780 c = TAB; 781 break; 782 783 case 'v': 784 c = VTAB; 785 break; 786 787 case 'b': 788 c = BS; 789 break; 790 791 case 'r': 792 c = CR; 793 break; 794 795 case 'f': 796 c = FF; 797 break; 798 } 799 } 800 while ((text_len + (int)MB_CUR_MAX + 1) >= text_size) 801 extend_text(); 802 if ((n = wctomb(&text[text_len], c)) > 0) 803 text_len += n; 804 c = fgetwc(fp); 805 } 806 807 while ((text_len + 1) >= text_size) 808 extend_text(); 809 text[text_len] = '\0'; 810 ++text_len; 811 812 if (c == NEWLINE) 813 ++lineno; 814 else 815 ateof = 1; 816 } 817 818 /* 819 * This routine handles $ <comment>, $set, $delset, $quote 820 */ 821 void 822 directive(fp) 823 FILE *fp; 824 { 825 int c; 826 int n; 827 828 c = fgetwc(fp); 829 if (c == SPACE || c == TAB) { /* $ <comment */ 830 do { 831 c = fgetwc(fp); 832 } while (c != NEWLINE && c != EOF); 833 } 834 if (c == NEWLINE) { 835 ++lineno; 836 return; 837 } 838 if (c == EOF) { 839 ateof = 1; 840 return; 841 } 842 text_len = 1; 843 while (text_len >= text_size) 844 extend_text(); 845 text[0] = DOLLAR; 846 while (isascii(c) && isalpha(c)) { 847 while ((text_len + 1) >= text_size) 848 extend_text(); 849 text[text_len] = c; 850 ++text_len; 851 c = fgetwc(fp); 852 } 853 854 while ((text_len + 1) >= text_size) 855 extend_text(); 856 text[text_len] = NUL; 857 858 if (strcmp(text, "$set") == 0) { 859 while (c == SPACE || c == TAB) 860 c = fgetwc(fp); 861 if (!isascii(c) || !isdigit(c)) { 862 FPRINTF(stderr, MSG(16), msgfname, lineno); 863 exit(1); 864 } 865 n = get_number(fp, c); 866 if (n == 0) { 867 FPRINTF(stderr, MSG(17), msgfname, lineno); 868 exit(1); 869 } 870 if (n > NL_SETMAX) { 871 FPRINTF(stderr, MSG(18), msgfname, lineno, 872 n, NL_SETMAX); 873 } 874 find_set(n); 875 do { /* skip comment */ 876 c = getc(fp); 877 } while (c != NEWLINE && c != EOF); 878 if (c == NEWLINE) 879 ++lineno; 880 else 881 ateof = 1; 882 return; 883 } else if (strcmp(text, "$delset") == 0) { 884 while (c == SPACE || c == TAB) 885 c = fgetwc(fp); 886 if (!isascii(c) || !isdigit(c)) { 887 FPRINTF(stderr, MSG(20), msgfname, lineno); 888 exit(1); 889 } 890 n = get_number(fp, c); 891 if (n == 0) { 892 FPRINTF(stderr, MSG(17), msgfname, lineno); 893 exit(1); 894 } 895 if (n > NL_SETMAX) { 896 FPRINTF(stderr, MSG(18), msgfname, lineno, 897 n, NL_SETMAX); 898 } 899 delete_set(n); 900 do { /* skip comment */ 901 c = getc(fp); 902 } while (c != NEWLINE && c != EOF); 903 if (c == NEWLINE) 904 ++lineno; 905 else 906 ateof = 1; 907 return; 908 } else if (strcmp(text, "$quote") == 0) { 909 if (c == NEWLINE) { 910 quoting = 0; 911 ++lineno; 912 return; 913 } 914 if (c == EOF) { 915 quoting = 0; 916 ateof = 1; 917 return; 918 } 919 if (c == SPACE || c == TAB) 920 c = fgetwc(fp); 921 if (c == NEWLINE) { 922 quoting = 0; 923 ++lineno; 924 return; 925 } 926 if (c == EOF) { 927 quoting = 0; 928 ateof = 1; 929 return; 930 } 931 quoting = 1; 932 quote = c; 933 do { /* skip comment */ 934 c = getc(fp); 935 } while (c == SPACE || c == TAB); 936 if (c == NEWLINE) { 937 ++lineno; 938 return; 939 } 940 if (c == EOF) { 941 ateof = 1; 942 return; 943 } 944 FPRINTF(stderr, MSG(15), msgfname, lineno); 945 exit(1); 946 } else { 947 FPRINTF(stderr, MSG(19), msgfname, lineno, text); 948 exit(1); 949 } 950 } 951 952 /* 953 * Read message source file and update double linked list message catalog. 954 */ 955 void 956 read_msgfile(fp, pathname) 957 FILE *fp; 958 char *pathname; 959 { 960 int c; 961 int no; 962 963 ateof = 0; 964 msgfname = pathname; 965 lineno = 1; 966 quoting = 0; 967 current_set_no = NL_SETD; 968 current_set = NULL; 969 current_msg = NULL; 970 971 for (;;) { 972 if (ateof) 973 return; 974 do { 975 c = fgetwc(fp); 976 } while (c == SPACE || c == TAB); 977 if (c == DOLLAR) { 978 directive(fp); 979 continue; 980 } 981 982 if (isascii(c) && isdigit(c)) { 983 no = get_number(fp, c); 984 if (no == 0) { 985 FPRINTF(stderr, MSG(10), msgfname, lineno); 986 exit(1); 987 } 988 if (no > NL_MSGMAX) { 989 FPRINTF(stderr, MSG(11), msgfname, 990 lineno, no, NL_MSGMAX); 991 } 992 c = fgetwc(fp); 993 if (c == NEWLINE || c == EOF) { 994 delete_msg(no); 995 if (c == NEWLINE) 996 ++lineno; 997 else 998 return; 999 continue; 1000 } else { 1001 if (c != SPACE && c != TAB) 1002 (void) ungetwc(c, fp); 1003 get_text(fp); 1004 insert_msg(no, text_len, text); 1005 continue; 1006 } 1007 } 1008 1009 if (c == NEWLINE) { 1010 ++lineno; 1011 continue; 1012 } 1013 if (c == EOF) 1014 return; 1015 1016 FPRINTF(stderr, MSG(22), msgfname, lineno); 1017 exit(1); 1018 } 1019 } 1020 1021 /* 1022 * Write double linked list to the file. 1023 * It first converts a linked list to one chunk of memory and 1024 * write it to file. 1025 */ 1026 void 1027 writecat(fd, pathname) 1028 int fd; 1029 char *pathname; 1030 { 1031 int i, n; 1032 int nsets; 1033 int mem; 1034 int nmsgs; 1035 int text_size; 1036 int first_msg_hdr; 1037 int msg_offset; 1038 unsigned nbytes; 1039 char *cat; 1040 struct _cat_hdr *hdrp; 1041 struct cat_set *setp; 1042 struct cat_msg *msgp; 1043 struct _cat_set_hdr *set; 1044 struct _cat_msg_hdr *msg; 1045 char *text; 1046 1047 /* compute number of sets, number of messages, the total text size */ 1048 nsets = 0; 1049 nmsgs = 0; 1050 text_size = 0; 1051 for (setp = first_set; setp; setp = setp->next) { 1052 ++nsets; 1053 for (msgp = setp->first_msg; msgp; msgp = msgp->next) { 1054 ++nmsgs; 1055 text_size += msgp->msg_len; 1056 } 1057 } 1058 1059 mem = nsets * _CAT_SET_HDR_SIZE + nmsgs * _CAT_MSG_HDR_SIZE + text_size; 1060 n = _CAT_HDR_SIZE + mem; 1061 cat = MALLOC(n); 1062 if (cat == 0) { 1063 FPRINTF(stderr, MSG(6)); 1064 exit(1); 1065 } 1066 MEMSET(cat, 0, n); 1067 1068 hdrp = (struct _cat_hdr *) cat; 1069 hdrp->__hdr_magic = _CAT_MAGIC; 1070 hdrp->__nsets = nsets; 1071 hdrp->__mem = mem; 1072 hdrp->__msg_hdr_offset = nsets * _CAT_SET_HDR_SIZE; 1073 hdrp->__msg_text_offset = nsets * _CAT_SET_HDR_SIZE + 1074 nmsgs * _CAT_MSG_HDR_SIZE; 1075 1076 set = (struct _cat_set_hdr *) (cat + _CAT_HDR_SIZE); 1077 msg = (struct _cat_msg_hdr *) (set + nsets); 1078 text = (char *) (msg + nmsgs); 1079 1080 /* convert linked list to one chunk of memory */ 1081 first_msg_hdr = 0; 1082 msg_offset = 0; 1083 for (setp = first_set; setp; ++set, setp = setp->next) { 1084 set->__set_no = setp->set_no; 1085 set->__first_msg_hdr = first_msg_hdr; 1086 nmsgs = 0; 1087 for (msgp = setp->first_msg; msgp; ++msg, msgp = msgp->next) { 1088 ++nmsgs; 1089 msg->__msg_no = msgp->msg_no; 1090 msg->__msg_len = msgp->msg_len; 1091 msg->__msg_offset = msg_offset; 1092 if (msgp->msg_len > 0) { 1093 MEMCPY(text, msgp->s, msgp->msg_len); 1094 text += msgp->msg_len; 1095 msg_offset += msgp->msg_len; 1096 } 1097 } 1098 set->__nmsgs = nmsgs; 1099 first_msg_hdr += nmsgs; 1100 } 1101 1102 /* write one chunk of memory to file */ 1103 nbytes = 0; 1104 while (nbytes < n) { 1105 i = write(fd, cat + nbytes, n - nbytes); 1106 if (i < 0) { 1107 if (errno != EINTR) { 1108 FPRINTF(stderr, MSG(8), pathname); 1109 perror(""); 1110 exit(1); 1111 } 1112 } else { 1113 nbytes += n; 1114 } 1115 } 1116 1117 free(cat); 1118 } 1119 1120 int 1121 main(argc, argv) 1122 int argc; 1123 char *argv[]; 1124 { 1125 int i; 1126 int cat_exists; 1127 1128 (void) setlocale(LC_ALL, ""); 1129 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1130 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1131 #endif 1132 (void) textdomain(TEXT_DOMAIN); 1133 1134 if (argc < 3) { 1135 FPRINTF(stderr, MSG(1)); 1136 exit(1); 1137 } 1138 catfname = argv[1]; 1139 cat_exists = 0; 1140 if ((*catfname == '-') && (*(catfname + 1) == '\0')) { 1141 catfd = 1; /* Use stdout */ 1142 } else { 1143 catfd = open(catfname, O_WRONLY | O_CREAT | O_EXCL, 0666); 1144 if (catfd < 0) { /* file exists */ 1145 if (errno != EEXIST || 1146 (catfd = open(catfname, O_RDWR)) < 0) { 1147 /* cannot open file */ 1148 FPRINTF(stderr, MSG(2), catfname); 1149 perror(""); 1150 exit(1); 1151 } 1152 cat_exists = 1; 1153 /* read catalog file into memory */ 1154 readcat(catfd, catfname); 1155 if (lseek(catfd, 0L, 0) < 0) { 1156 FPRINTF(stderr, MSG(7), catfname); 1157 perror(""); 1158 exit(1); 1159 } 1160 } 1161 } 1162 1163 /* process all message source files */ 1164 if ((**(argv + 2) == '-') && (*(*(argv + 2) + 1) == '\0')) { 1165 if (argc != 3) { 1166 FPRINTF(stderr, MSG(1)); 1167 exit(1); 1168 } else { 1169 read_msgfile(stdin, MSG(21)); 1170 } 1171 } else { 1172 for (i = 2; i < argc; ++i) { 1173 FILE *fp; 1174 fp = fopen(*(argv + i), "r"); 1175 if (fp == NULL) { 1176 FPRINTF(stderr, MSG(2), *(argv + i)); 1177 perror(""); 1178 exit(1); 1179 } 1180 read_msgfile(fp, *(argv + i)); 1181 (void) fclose(fp); 1182 } 1183 } 1184 1185 if (cat_exists) 1186 (void) ftruncate(catfd, 0L); 1187 1188 /* write catalog to file */ 1189 writecat(catfd, catfname); 1190 return (0); 1191 } 1192