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 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * sgsmsg generates several message files from an input template file. Messages 27 * are constructed for use with gettext(3i) - the default - or catgets(3c). The 28 * files generate are: 29 * 30 * msg.h a header file containing definitions for each message. The -h 31 * option triggers the creation of these definitions and specifies 32 * the name to use. 33 * 34 * msg.c a data array of message strings. The msg.h definitions are 35 * offsets into this array. The -d option triggers the creation of 36 * these definitions and specifies the name to use. 37 * 38 * messages a message file suitable for catgets(3c) or gettext(3i) use. The 39 * -m option triggers this output and specifies the filename to be 40 * used. 41 * 42 * The template file is processed based on the first character of each line: 43 * 44 * # or $ entries are copied (as is) to the message file (messages). 45 * 46 * @ token(s) entries are translated. Two translations are possible dependent 47 * on whether one or more tokens are supplied: 48 * 49 * A single token is interpreted as one of two reserved message 50 * output indicators, or a message identifier. The reserved output 51 * indicator _START_ enables output to the message file - Note that 52 * the occurance of any other @ token will also enable message 53 * output. The reserved output indicator _END_ disables output to 54 * the message file. The use of these two indicators provides for 55 * only those message strings that require translation to be output 56 * to the message file. 57 * 58 * Besides the reserved output indicators, a single token is taken 59 * to be a message identifier which will be subsituted for a 60 * `setid' for catgets(3c) output, or a `domain' name for 61 * gettext(3i) output. This value is determine by substituting the 62 * token for the associated definition found in the message 63 * identifier file (specified with the -i option). 64 * 65 * Multiple tokens are taken to be a message definition followed by 66 * the associated message string. The message string is copied to 67 * the data array being built in msg.c. The index into this array 68 * becomes the `message' identifier created in the msg.h file. 69 */ 70 #pragma ident "%Z%%M% %I% %E% SMI" 71 72 #include <fcntl.h> 73 #include <stdlib.h> 74 #include <stdio.h> 75 #include <unistd.h> 76 #include <limits.h> 77 #include <string.h> 78 #include <ctype.h> 79 #include <errno.h> 80 #include <sys/param.h> 81 82 #include <sgs.h> 83 #include <string_table.h> 84 85 /* 86 * Define any error message strings. 87 */ 88 static const char 89 * Errmsg_malt = "sgsmsg: file %s: line %d: malformed input " 90 "at line\n", 91 * Errmsg_nmem = "sgsmsg: memory allocation failed: %s\n", 92 * Errmsg_opne = "sgsmsg: file %s: open failed: %s\n", 93 * Errmsg_wrte = "sgsmsg: file %s: write failed: %s\n", 94 * Errmsg_read = "sgsmsg: file %s: read failed %s\n", 95 * Errmsg_stnw = "sgsmsg: st_new(): failed: %s\n", 96 * Errmsg_stin = "sgsmsg: Str_tbl insert failed: %s\n", 97 * Errmsg_mnfn = "sgsmsg: message not found in Str_tbl: %s\n", 98 * Errmsg_use = "usage: sgsmsg [-clv] [-d mesgdata] [-h mesgdefs] " 99 "[-m messages] [-n name] [-i mesgident] file ...\n"; 100 101 /* 102 * Define all output filenames and associated descriptors. 103 */ 104 static FILE *fddefs, *fddata, *fdmsgs, *fdmids, *fddesc; 105 static char *fldefs, *fldata, *flmsgs, *flmids, *fldesc; 106 static FILE *fdlint; 107 static char fllint[MAXPATHLEN]; 108 109 static uint_t vflag; /* verbose flag */ 110 static Str_tbl *stp; /* string table */ 111 112 /* 113 * Define any default strings. 114 */ 115 static const char 116 *nmlint = "/tmp/sgsmsg.lint", 117 *interface = "sgs_msg", 118 *start = "_START_", 119 *end = "_END_"; 120 121 /* 122 * Define any default flags and data items. 123 */ 124 static int cflag = 0, lflag = 0, prtmsgs = 0, line, ptr = 1, msgid = 0; 125 static char *mesgid = 0, *setid = 0, *domain = 0; 126 127 typedef struct msg_string { 128 char *ms_defn; 129 char *ms_message; 130 struct msg_string *ms_next; 131 } msg_string; 132 133 static msg_string *msg_head; 134 static msg_string *msg_tail; 135 136 /* 137 * message_append() is responsible for both inserting strings into 138 * the master Str_tbl as well as maintaining a list of the 139 * DEFINITIONS associated with each string. 140 * 141 * The list of strings is traversed at the end once the full 142 * Str_tbl has been constructed - and string offsets can be 143 * assigned. 144 */ 145 static void 146 message_append(const char *defn, const char *message) 147 { 148 msg_string *msg; 149 if ((msg = calloc(sizeof (msg_string), 1)) == 0) { 150 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 151 exit(1); 152 } 153 if (stp == 0) { 154 /* 155 * Initialize string table 156 */ 157 if ((stp = st_new(FLG_STNEW_COMPRESS)) == 0) { 158 (void) fprintf(stderr, Errmsg_stnw, strerror(errno)); 159 exit(1); 160 } 161 } 162 163 164 if ((msg->ms_defn = strdup(defn)) == 0) { 165 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 166 exit(1); 167 } 168 if ((msg->ms_message = strdup(message)) == 0) { 169 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 170 exit(1); 171 } 172 173 if (st_insert(stp, msg->ms_message) == -1) { 174 (void) fprintf(stderr, Errmsg_stin, 175 message); 176 exit(1); 177 } 178 179 if (msg_head == 0) { 180 msg_head = msg_tail = msg; 181 return; 182 } 183 msg_tail->ms_next = msg; 184 msg_tail = msg; 185 } 186 187 /* 188 * Initialize a setid value. Given a setid definition determine its numeric 189 * value from the specified message identifier file (specified with the -i 190 * option). Return a pointer to the numeric string. 191 */ 192 static int 193 getmesgid(char *id) 194 { 195 char *buffer, *token, *_mesgid = 0, *_setid = 0, *_domain = 0; 196 197 /* 198 * If we're being asked to interpret a message id but the user didn't 199 * provide the required message identifier file (-i option) we're in 200 * trouble. 201 */ 202 if (flmids == 0) { 203 (void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: " 204 "unable to process mesgid\n\t" 205 "no message identifier file specified " 206 "(see -i option)\n", fldesc, line, id); 207 return (1); 208 } 209 210 if ((buffer = malloc(LINE_MAX)) == 0) { 211 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 212 return (1); 213 } 214 215 /* 216 * Read the message identifier file and locate the required mesgid. 217 */ 218 rewind(fdmids); 219 while (fgets(buffer, LINE_MAX, fdmids) != NULL) { 220 if ((token = strstr(buffer, id)) == NULL) 221 continue; 222 223 /* 224 * Establish individual strings for the mesgid, setid and domain 225 * values. 226 */ 227 _mesgid = token; 228 while (!(isspace(*token))) 229 token++; 230 *token++ = 0; 231 232 while (isspace(*token)) 233 token++; 234 _setid = token; 235 while (!(isspace(*token))) 236 token++; 237 *token++ = 0; 238 239 while (isspace(*token)) 240 token++; 241 _domain = token; 242 while (!(isspace(*token))) 243 token++; 244 *token = 0; 245 break; 246 } 247 248 /* 249 * Did we find a match? 250 */ 251 if ((_mesgid == 0) || (_setid == 0) || (_domain == 0)) { 252 (void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: " 253 "unable to process mesgid\n\t" 254 "identifier does not exist in file %s\n", 255 fldesc, line, id, flmids); 256 return (1); 257 } 258 259 /* 260 * Have we been here before? 261 */ 262 if (mesgid) { 263 if (cflag == 1) { 264 /* 265 * If we're being asked to process more than one mesgid 266 * warn the user that only one mesgid can be used for 267 * the catgets(3c) call. 268 */ 269 (void) fprintf(stderr, "sgsmsg: file %s: line %d: " 270 "setid %s: warning: multiple mesgids " 271 "encountered\n\t" 272 "last setting used in messaging code\n", 273 fldesc, line, id); 274 } 275 } 276 277 mesgid = _mesgid; 278 setid = _setid; 279 domain = _domain; 280 281 /* 282 * Generate the message file output (insure output flag is enabled). 283 */ 284 if (prtmsgs != -1) 285 prtmsgs = 1; 286 if (fdmsgs && (prtmsgs == 1)) { 287 if (cflag == 1) { 288 if (fprintf(fdmsgs, "$quote \"\n$set %s\n", 289 setid) < 0) { 290 (void) fprintf(stderr, Errmsg_wrte, flmsgs, 291 strerror(errno)); 292 return (1); 293 } 294 } else { 295 if (fprintf(fdmsgs, "domain\t\"%s\"\n", domain) < 0) { 296 (void) fprintf(stderr, Errmsg_wrte, flmsgs, 297 strerror(errno)); 298 return (1); 299 } 300 } 301 } 302 303 /* 304 * For catgets(3c) output generate a setid definition in the message 305 * definition file. 306 */ 307 if (fddefs && (cflag == 1) && 308 (fprintf(fddefs, "#define\t%s\t%s\n\n", mesgid, setid) < 0)) { 309 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 310 return (1); 311 } 312 313 return (0); 314 } 315 316 317 /* 318 * Dump contents of String Table to standard out 319 */ 320 static void 321 dump_stringtab(Str_tbl *stp) 322 { 323 uint_t i; 324 325 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) { 326 (void) printf("uncompressed strings: %d\n", 327 stp->st_fullstringsize); 328 return; 329 } 330 331 332 for (i = 0; i < stp->st_hbckcnt; i++) { 333 Str_hash *sthash; 334 (void) printf("Bucket: [%3d]\n", i); 335 for (sthash = stp->st_hashbcks[i]; sthash; 336 sthash = sthash->hi_next) { 337 uint_t stroff; 338 stroff = sthash->hi_mstr->sm_stlen - sthash->hi_stlen; 339 if (stroff == 0) { 340 (void) printf(" %2d %s <master>\n", 341 sthash->hi_refcnt, 342 sthash->hi_mstr->sm_str); 343 } else { 344 const char *str; 345 str = &sthash->hi_mstr->sm_str[stroff]; 346 (void) printf(" %2d %s <suffix of> -> %s\n", 347 sthash->hi_refcnt, 348 str, sthash->hi_mstr->sm_str); 349 } 350 } 351 } 352 (void) printf("fullstringsize: %d compressed: %d\n", 353 stp->st_fullstringsize, stp->st_stringsize); 354 } 355 /* 356 * Initialize the message definition header file stream. 357 */ 358 static int 359 init_defs(void) 360 { 361 static char guard[FILENAME_MAX + 6]; 362 char *optr; 363 const char *iptr, *_ptr; 364 365 /* 366 * Establish a header guard name using the files basename. 367 */ 368 for (iptr = 0, _ptr = fldefs; _ptr && (*_ptr != '\0'); _ptr++) { 369 if (*_ptr == '/') 370 iptr = _ptr + 1; 371 } 372 if (iptr == 0) 373 iptr = fldefs; 374 375 optr = guard; 376 for (*optr++ = '_'; iptr && (*iptr != '\0'); iptr++, optr++) { 377 if (*iptr == '.') { 378 *optr++ = '_'; 379 *optr++ = 'D'; 380 *optr++ = 'O'; 381 *optr++ = 'T'; 382 *optr = '_'; 383 } else 384 *optr = toupper(*iptr); 385 } 386 387 if (fprintf(fddefs, "#ifndef\t%s\n#define\t%s\n\n", guard, guard) < 0) { 388 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 389 return (1); 390 } 391 392 if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) { 393 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 394 return (1); 395 } 396 397 /* 398 * add "typedef int Msg;" 399 */ 400 if (fprintf(fddefs, "typedef int\tMsg;\n\n") < 0) { 401 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 402 return (1); 403 } 404 405 /* 406 * If the associated data array is global define a prototype. 407 * Define a macro to access the array elements. 408 */ 409 if (lflag == 0) { 410 if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n", 411 interface) < 0) { 412 (void) fprintf(stderr, Errmsg_wrte, fldefs, 413 strerror(errno)); 414 return (1); 415 } 416 } 417 if (fprintf(fddefs, "#define\tMSG_ORIG(x)\t&__%s[x]\n\n", 418 interface) < 0) { 419 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 420 return (1); 421 } 422 423 /* 424 * Generate a prototype to access the associated data array. 425 */ 426 if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n", 427 interface) < 0) { 428 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 429 return (1); 430 } 431 if (fprintf(fddefs, "#define\tMSG_INTL(x)\t_%s(x)\n\n", 432 interface) < 0) { 433 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 434 return (1); 435 } 436 437 return (0); 438 } 439 440 441 /* 442 * Finish the message definition header file. 443 */ 444 static int 445 fini_defs(void) 446 { 447 if (fprintf(fddefs, "\n#else\t/* __lint */\n\n") < 0) { 448 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 449 return (1); 450 } 451 452 /* 453 * When __lint is defined, Msg is a char *. This allows lint to 454 * check our format strings against it's arguments. 455 */ 456 if (fprintf(fddefs, "\ntypedef char *\tMsg;\n\n") < 0) { 457 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 458 return (1); 459 } 460 461 if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n", 462 interface) < 0) { 463 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 464 return (1); 465 } 466 467 if (lflag == 0) { 468 if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n", 469 interface) < 0) { 470 (void) fprintf(stderr, Errmsg_wrte, fldefs, 471 strerror(errno)); 472 return (1); 473 } 474 } 475 476 if (fprintf(fddefs, 477 "#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) { 478 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 479 return (1); 480 } 481 482 /* 483 * Copy the temporary lint defs file into the new header. 484 */ 485 if (fdlint) { 486 long size; 487 char *buf; 488 489 size = ftell(fdlint); 490 (void) rewind(fdlint); 491 492 if ((buf = malloc(size)) == 0) { 493 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 494 return (1); 495 } 496 if (fread(buf, size, 1, fdlint) == 0) { 497 (void) fprintf(stderr, Errmsg_read, fllint, 498 strerror(errno)); 499 return (1); 500 } 501 if (fwrite(buf, size, 1, fddefs) == 0) { 502 (void) fprintf(stderr, Errmsg_wrte, fldefs, 503 strerror(errno)); 504 return (1); 505 } 506 (void) free(buf); 507 } 508 509 if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) { 510 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 511 return (1); 512 } 513 514 if (fprintf(fddefs, "\n#endif\n") < 0) { 515 (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); 516 return (1); 517 } 518 519 return (0); 520 } 521 522 523 /* 524 * The entire messaging file has been scanned - and all strings have been 525 * inserted into the string_table. We can now walk the message queue 526 * and create the '#define <DEFN>' for each string - with the strings 527 * assigned offset into the string_table. 528 */ 529 static int 530 output_defs(void) 531 { 532 msg_string *msg; 533 uint_t stbufsize; 534 char *stbuf; 535 536 stbufsize = st_getstrtab_sz(stp); 537 if ((stbuf = malloc(stbufsize)) == 0) { 538 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 539 exit(1); 540 } 541 (void) st_setstrbuf(stp, stbuf, stbufsize); 542 for (msg = msg_head; msg; msg = msg->ms_next) { 543 uint_t stoff; 544 if ((st_setstring(stp, msg->ms_message, &stoff)) == -1) { 545 (void) fprintf(stderr, Errmsg_mnfn, msg->ms_message); 546 return (1); 547 } 548 if (fprintf(fddefs, "\n#define\t%s\t%d\n", 549 msg->ms_defn, stoff) < 0) { 550 (void) fprintf(stderr, Errmsg_wrte, 551 fldefs, strerror(errno)); 552 return (1); 553 } 554 if (fddefs && fprintf(fddefs, "#define\t%s_SIZE\t%d\n", 555 msg->ms_defn, strlen(msg->ms_message)) < 0) { 556 (void) fprintf(stderr, Errmsg_wrte, 557 fldefs, strerror(errno)); 558 return (1); 559 } 560 } 561 return (0); 562 } 563 564 565 /* 566 * Finish off the data structure definition. 567 */ 568 static int 569 output_data(void) 570 { 571 uint_t stbufsize; 572 uint_t ndx; 573 uint_t column = 1; 574 const char *stbuf; 575 const char *fmtstr; 576 577 stbufsize = st_getstrtab_sz(stp); 578 stbuf = st_getstrbuf(stp); 579 580 assert(stbuf); 581 582 /* 583 * Determine from the local flag whether the data declaration should 584 * be static. 585 */ 586 if (lflag) 587 fmtstr = (const char *)"static const"; 588 else 589 fmtstr = (const char *)"const"; 590 591 if (fprintf(fddata, "\n%s char __%s[%d] = { ", 592 fmtstr, interface, stbufsize) < 0) { 593 (void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno)); 594 return (1); 595 } 596 597 for (ndx = 0; ndx < (stbufsize - 1); ndx++) { 598 if (column == 1) { 599 if (fddata && fprintf(fddata, 600 "\n/* %4d */ 0x%.2x,", ndx, 601 (unsigned char)stbuf[ndx]) < 0) { 602 (void) fprintf(stderr, Errmsg_wrte, 603 fldata, strerror(errno)); 604 return (1); 605 } 606 } else { 607 if (fddata && fprintf(fddata, " 0x%.2x,", 608 (unsigned char)stbuf[ndx]) < 0) { 609 (void) fprintf(stderr, Errmsg_wrte, 610 fldata, strerror(errno)); 611 return (1); 612 } 613 } 614 615 if (column++ == 10) 616 column = 1; 617 } 618 619 if (column == 1) 620 fmtstr = "\n\t0x%.2x };\n"; 621 else 622 fmtstr = " 0x%.2x };\n"; 623 624 if (fprintf(fddata, fmtstr, (unsigned char)stbuf[stbufsize - 1]) < 0) { 625 (void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno)); 626 return (1); 627 } 628 629 return (0); 630 } 631 632 static int 633 file() 634 { 635 char buffer[LINE_MAX], * token; 636 uint_t bufsize; 637 char *token_buffer; 638 int escape = 0; 639 640 if ((token_buffer = malloc(LINE_MAX)) == 0) { 641 (void) fprintf(stderr, Errmsg_nmem, strerror(errno)); 642 return (1); 643 } 644 bufsize = LINE_MAX; 645 646 line = 1; 647 648 while ((token = fgets(buffer, LINE_MAX, fddesc)) != NULL) { 649 char defn[PATH_MAX], * _defn, * str; 650 int len; 651 652 switch (*token) { 653 case '#': 654 case '$': 655 if (escape) { 656 (void) fprintf(stderr, Errmsg_malt, fldesc, 657 line); 658 return (1); 659 } 660 661 /* 662 * If a msgid has been output a msgstr must follow 663 * before we digest the new token. A msgid is only set 664 * if fdmsgs is in use. 665 */ 666 if (msgid) { 667 msgid = 0; 668 if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) { 669 (void) fprintf(stderr, Errmsg_wrte, 670 flmsgs, strerror(errno)); 671 return (1); 672 } 673 } 674 675 /* 676 * Pass lines directly through to the output message 677 * file. 678 */ 679 if (fdmsgs && (prtmsgs == 1)) { 680 char comment; 681 682 if (cflag == 0) 683 comment = '#'; 684 else 685 comment = '$'; 686 687 if (fprintf(fdmsgs, "%c%s", comment, 688 ++token) < 0) { 689 (void) fprintf(stderr, Errmsg_wrte, 690 flmsgs, strerror(errno)); 691 return (1); 692 } 693 } 694 break; 695 696 case '@': 697 if (escape) { 698 (void) fprintf(stderr, Errmsg_malt, fldesc, 699 line); 700 return (1); 701 } 702 703 /* 704 * If a msgid has been output a msgstr must follow 705 * before we digest the new token. 706 */ 707 if (msgid) { 708 msgid = 0; 709 if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) { 710 (void) fprintf(stderr, Errmsg_wrte, 711 flmsgs, strerror(errno)); 712 return (1); 713 } 714 } 715 716 /* 717 * Determine whether we have one or more tokens. 718 */ 719 token++; 720 while (isspace(*token)) /* rid any whitespace */ 721 token++; 722 _defn = token; /* definition start */ 723 while (!(isspace(*token))) 724 token++; 725 *token++ = 0; 726 727 while (isspace(*token)) /* rid any whitespace */ 728 token++; 729 730 /* 731 * Determine whether the single token is one of the 732 * reserved message output delimiters otherwise 733 * translate it as a message identifier. 734 */ 735 if (*token == 0) { 736 if (strcmp(_defn, start) == 0) 737 prtmsgs = 1; 738 else if (strcmp(_defn, end) == 0) 739 prtmsgs = -1; 740 else if (getmesgid(_defn) == 1) 741 return (1); 742 break; 743 } 744 745 /* 746 * Multiple tokens are translated by taking the first 747 * token as the message definition, and the rest of the 748 * line as the message itself. A message line ending 749 * with an escape ('\') is expected to be continued on 750 * the next line. 751 */ 752 if (prtmsgs != -1) 753 prtmsgs = 1; 754 if (fdmsgs && (prtmsgs == 1)) { 755 /* 756 * For catgets(3c) make sure a message 757 * identifier has been established (this is 758 * normally a domain for gettext(3i), but for 759 * sgsmsg use this could be argued as being 760 * redundent). Also make sure that the message 761 * definitions haven't exceeeded the maximum 762 * value allowed by gencat(1) before generating 763 * any message file entries. 764 */ 765 if (cflag == 1) { 766 if (setid == 0) { 767 (void) fprintf(stderr, "file " 768 "%s: no message identifier " 769 "has been established\n", 770 fldesc); 771 return (1); 772 } 773 if (ptr > NL_MSGMAX) { 774 (void) fprintf(stderr, "file " 775 "%s: message definition " 776 "(%d) exceeds allowable " 777 "limit (NL_MSGMAX)\n", 778 fldesc, ptr); 779 return (1); 780 } 781 } 782 783 /* 784 * For catgets(3c) write the definition and the 785 * message string to the message file. For 786 * gettext(3i) write the message string as a 787 * mesgid - indicate a mesgid has been output 788 * so that a msgstr can follow. 789 */ 790 if (cflag == 1) { 791 if (fprintf(fdmsgs, "%d\t%s", ptr, 792 token) < 0) { 793 (void) fprintf(stderr, 794 Errmsg_wrte, flmsgs, 795 strerror(errno)); 796 return (1); 797 } 798 } else { 799 if (fprintf(fdmsgs, "msgid\t\"") < 0) { 800 (void) fprintf(stderr, 801 Errmsg_wrte, flmsgs, 802 strerror(errno)); 803 return (1); 804 } 805 msgid = 1; 806 } 807 } 808 809 /* 810 * The message itself is a quoted string as this makes 811 * embedding spaces at the start (or the end) of the 812 * string very easy. 813 */ 814 if (*token != '"') { 815 (void) fprintf(stderr, Errmsg_malt, fldesc, 816 line); 817 return (1); 818 } 819 820 (void) strcpy(defn, _defn); 821 822 /* 823 * Write the tag to the lint definitions. 824 */ 825 if (fdlint) { 826 if (fprintf(fdlint, "\n#define\t%s\t", 827 _defn) < 0) { 828 (void) fprintf(stderr, Errmsg_wrte, 829 fllint, strerror(errno)); 830 return (1); 831 } 832 } 833 834 len = 0; 835 836 /* 837 * Write each character of the message string to the 838 * data array. Translate any escaped characters - use 839 * the same specially recognized characters as defined 840 * by gencat(1). 841 */ 842 message: 843 if (*token == '"') { 844 if (fdlint && 845 (fprintf(fdlint, "%c", *token) < 0)) { 846 (void) fprintf(stderr, Errmsg_wrte, 847 fllint, strerror(errno)); 848 return (1); 849 } 850 token++; 851 } 852 while (*token) { 853 char _token; 854 855 if ((*token == '\\') && (escape == 0)) { 856 escape = 1; 857 if (fdlint && (*(token + 1) != '\n') && 858 fprintf(fdlint, "%c", *token) < 0) { 859 (void) fprintf(stderr, 860 Errmsg_wrte, fllint, 861 strerror(errno)); 862 return (1); 863 } 864 token++; 865 continue; 866 } 867 if (escape) { 868 if (*token == 'n') 869 _token = '\n'; 870 else if (*token == 't') 871 _token = '\t'; 872 else if (*token == 'v') 873 _token = '\v'; 874 else if (*token == 'b') 875 _token = '\b'; 876 else if (*token == 'f') 877 _token = '\f'; 878 else if (*token == '\\') 879 _token = '\\'; 880 else if (*token == '"') 881 _token = '"'; 882 else if (*token == '\n') 883 break; 884 else 885 _token = *token; 886 887 if (fdmsgs && (prtmsgs == 1) && 888 (fprintf(fdmsgs, "\\") < 0)) { 889 (void) fprintf(stderr, 890 Errmsg_wrte, flmsgs, 891 strerror(errno)); 892 return (1); 893 } 894 } else { 895 /* 896 * If this is the trailing quote then 897 * thats the last of the message string. 898 * Eat up any remaining white space and 899 * unless an escape character is found 900 * terminate the data string with a 0. 901 */ 902 if (*token == '"') { 903 if (fdlint && (fprintf(fdlint, 904 "%c", *token) < 0)) { 905 (void) fprintf(stderr, 906 Errmsg_wrte, fllint, 907 strerror(errno)); 908 return (1); 909 } 910 911 if (fdmsgs && (prtmsgs == 1) && 912 (fprintf(fdmsgs, "%c", 913 *token) < 0)) { 914 (void) fprintf(stderr, 915 Errmsg_wrte, flmsgs, 916 strerror(errno)); 917 return (1); 918 } 919 920 while (*++token) { 921 if (*token == '\n') 922 break; 923 } 924 _token = '\0'; 925 } else 926 _token = *token; 927 } 928 929 if (fdmsgs && (prtmsgs == 1) && 930 (fprintf(fdmsgs, "%c", *token) < 0)) { 931 (void) fprintf(stderr, Errmsg_wrte, 932 flmsgs, strerror(errno)); 933 return (1); 934 } 935 936 if (fdlint && fprintf(fdlint, 937 "%c", *token) < 0) { 938 (void) fprintf(stderr, Errmsg_wrte, 939 fllint, strerror(errno)); 940 return (1); 941 } 942 943 if (len >= bufsize) { 944 bufsize += LINE_MAX; 945 if ((token_buffer = realloc( 946 token_buffer, bufsize)) == 0) { 947 (void) fprintf(stderr, 948 Errmsg_nmem, 949 strerror(errno)); 950 return (1); 951 } 952 } 953 token_buffer[len] = _token; 954 ptr++, token++, len++; 955 escape = 0; 956 957 if (_token == '\0') 958 break; 959 } 960 961 /* 962 * After the complete message string has been processed 963 * (including its continuation beyond one line), create 964 * a string size definition. 965 */ 966 if (escape == 0) { 967 const char *form = "#define\t%s_SIZE\t%d\n"; 968 969 token_buffer[len] = '\0'; 970 971 message_append(defn, token_buffer); 972 973 if (fdlint && fprintf(fdlint, form, defn, 974 (len - 1)) < 0) { 975 (void) fprintf(stderr, Errmsg_wrte, 976 fllint, strerror(errno)); 977 return (1); 978 } 979 } 980 break; 981 982 default: 983 /* 984 * Empty lines are passed through to the message file. 985 */ 986 while (isspace(*token)) 987 token++; 988 989 if (*token == 0) { 990 if (msgid || (fdmsgs && (prtmsgs == 1))) { 991 /* 992 * If a msgid has been output a msgstr 993 * must follow before we digest the new 994 * token. 995 */ 996 if (msgid) { 997 msgid = 0; 998 str = "msgstr\t\"\"\n\n"; 999 } else 1000 str = "\n"; 1001 1002 if (fprintf(fdmsgs, str) < 0) { 1003 (void) fprintf(stderr, 1004 Errmsg_wrte, flmsgs, 1005 strerror(errno)); 1006 return (1); 1007 } 1008 } 1009 break; 1010 } 1011 1012 /* 1013 * If an escape is in effect then any tokens are taken 1014 * to be message continuations. 1015 */ 1016 if (escape) { 1017 escape = 0; 1018 goto message; 1019 } 1020 1021 (void) fprintf(stderr, "file %s: line %d: invalid " 1022 "input does not start with #, $ or @\n", fldesc, 1023 line); 1024 return (1); 1025 } 1026 line++; 1027 } 1028 1029 free(token_buffer); 1030 1031 return (0); 1032 } 1033 1034 int 1035 main(int argc, char ** argv) 1036 { 1037 opterr = 0; 1038 while ((line = getopt(argc, argv, "cd:h:lm:n:i:v")) != EOF) { 1039 switch (line) { 1040 case 'c': /* catgets instead of gettext */ 1041 cflag = 1; 1042 break; 1043 case 'd': /* new message data filename */ 1044 fldata = optarg; /* (msg.c is default) */ 1045 break; 1046 case 'h': /* new message defs filename */ 1047 fldefs = optarg; /* (msg.h is default) */ 1048 break; 1049 case 'i': /* input message ids from */ 1050 flmids = optarg; /* from this file */ 1051 break; 1052 case 'l': /* define message data arrays */ 1053 lflag = 1; /* to be local (static) */ 1054 break; 1055 case 'm': /* generate message database */ 1056 flmsgs = optarg; /* to this file */ 1057 break; 1058 case 'n': /* new data array and func */ 1059 interface = optarg; /* name (msg is default) */ 1060 break; 1061 case 'v': 1062 vflag = 1; /* set verbose flag */ 1063 break; 1064 case '?': 1065 (void) fprintf(stderr, Errmsg_use, argv[0]); 1066 exit(1); 1067 default: 1068 break; 1069 } 1070 } 1071 1072 /* 1073 * Validate the we have been given at least one input file. 1074 */ 1075 if ((argc - optind) < 1) { 1076 (void) fprintf(stderr, Errmsg_use); 1077 exit(1); 1078 } 1079 1080 /* 1081 * Open all the required output files. 1082 */ 1083 if (fldefs) { 1084 if ((fddefs = fopen(fldefs, "w+")) == NULL) { 1085 (void) fprintf(stderr, Errmsg_opne, fldefs, 1086 strerror(errno)); 1087 return (1); 1088 } 1089 } 1090 if (fldata) { 1091 if (fldefs && (strcmp(fldefs, fldata) == 0)) 1092 fddata = fddefs; 1093 else if ((fddata = fopen(fldata, "w+")) == NULL) { 1094 (void) fprintf(stderr, Errmsg_opne, fldata, 1095 strerror(errno)); 1096 return (1); 1097 } 1098 } 1099 if (fddefs && fddata) { 1100 (void) sprintf(fllint, "%s.%d", nmlint, (int)getpid()); 1101 if ((fdlint = fopen(fllint, "w+")) == NULL) { 1102 (void) fprintf(stderr, Errmsg_opne, fllint, 1103 strerror(errno)); 1104 return (1); 1105 } 1106 } 1107 if (flmsgs) { 1108 if ((fdmsgs = fopen(flmsgs, "w+")) == NULL) { 1109 (void) fprintf(stderr, Errmsg_opne, flmsgs, 1110 strerror(errno)); 1111 return (1); 1112 } 1113 } 1114 if (flmids) { 1115 if ((fdmids = fopen(flmids, "r")) == NULL) { 1116 (void) fprintf(stderr, Errmsg_opne, flmids, 1117 strerror(errno)); 1118 return (1); 1119 } 1120 } 1121 1122 1123 /* 1124 * Initialize the message definition and message data streams. 1125 */ 1126 if (fddefs) { 1127 if (init_defs()) 1128 return (1); 1129 } 1130 1131 /* 1132 * Read the input message file, and for each line process accordingly. 1133 */ 1134 for (; optind < argc; optind++) { 1135 int err; 1136 1137 fldesc = argv[optind]; 1138 1139 if ((fddesc = fopen(fldesc, "r")) == NULL) { 1140 (void) fprintf(stderr, Errmsg_opne, fldesc, 1141 strerror(errno)); 1142 return (1); 1143 } 1144 err = file(); 1145 (void) fclose(fddesc); 1146 1147 if (err != 0) 1148 return (1); 1149 } 1150 1151 /* 1152 * If a msgid has been output a msgstr must follow before we end the 1153 * file. 1154 */ 1155 if (msgid) { 1156 msgid = 0; 1157 if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) { 1158 (void) fprintf(stderr, Errmsg_wrte, flmsgs, 1159 strerror(errno)); 1160 return (1); 1161 } 1162 } 1163 1164 if (fdmids) 1165 (void) fclose(fdmids); 1166 if (fdmsgs) 1167 (void) fclose(fdmsgs); 1168 1169 if (fddefs) { 1170 if (output_defs()) 1171 return (1); 1172 } 1173 1174 /* 1175 * Finish off any generated data and header file. 1176 */ 1177 if (fldata) { 1178 if (output_data()) 1179 return (1); 1180 } 1181 if (fddefs) { 1182 if (fini_defs()) 1183 return (1); 1184 } 1185 1186 if (vflag) 1187 dump_stringtab(stp); 1188 1189 1190 /* 1191 * Close up everything and go home. 1192 */ 1193 if (fddata) 1194 (void) fclose(fddata); 1195 if (fddefs && (fddefs != fddata)) 1196 (void) fclose(fddefs); 1197 if (fddefs && fddata) { 1198 (void) fclose(fdlint); 1199 (void) unlink(fllint); 1200 } 1201 1202 if (stp) 1203 st_destroy(stp); 1204 1205 return (0); 1206 } 1207