1 /* $OpenBSD: gencat.c,v 1.6 2000/09/27 23:53:29 danh Exp $ */ 2 /* $NetBSD: gencat.c,v 1.9 1998/10/09 17:00:56 itohy Exp $ */ 3 4 /*- 5 * Copyright (c) 1996 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by J.T. Conklin. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgment: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/cdefs.h> 41 #ifndef lint 42 static char rcsid[] = 43 "$OpenBSD: gencat.c,v 1.6 2000/09/27 23:53:29 danh Exp $"; 44 #endif /* not lint */ 45 46 /*********************************************************** 47 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. 48 49 All Rights Reserved 50 51 Permission to use, copy, modify, and distribute this software and its 52 documentation for any purpose and without fee is hereby granted, 53 provided that the above copyright notice appear in all copies and that 54 both that copyright notice and this permission notice appear in 55 supporting documentation, and that Alfalfa's name not be used in 56 advertising or publicity pertaining to distribution of the software 57 without specific, written prior permission. 58 59 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 60 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 61 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 62 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 63 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 64 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 65 SOFTWARE. 66 67 If you make any modifications, bugfixes or other changes to this software 68 we'd appreciate it if you could send a copy to us so we can keep things 69 up-to-date. Many thanks. 70 Kee Hinckley 71 Alfalfa Software, Inc. 72 267 Allston St., #3 73 Cambridge, MA 02139 USA 74 nazgul@alfalfa.com 75 76 ******************************************************************/ 77 78 #define _NLS_PRIVATE 79 80 /* ensure 8-bit cleanliness */ 81 #define ISSPACE(c) \ 82 (isascii((unsigned char)c) && isspace((unsigned char)c)) 83 84 #include <sys/queue.h> 85 #include <ctype.h> 86 #include <err.h> 87 #include <fcntl.h> 88 #include <nl_types.h> 89 #include <stdio.h> 90 #include <stdlib.h> 91 #include <string.h> 92 #include <unistd.h> 93 94 struct _msgT { 95 long msgId; 96 char *str; 97 LIST_ENTRY(_msgT) entries; 98 }; 99 100 struct _setT { 101 long setId; 102 LIST_HEAD(msghead, _msgT) msghead; 103 LIST_ENTRY(_setT) entries; 104 }; 105 106 LIST_HEAD(sethead, _setT) sethead; 107 static struct _setT *curSet; 108 109 static char *curline = NULL; 110 static long lineno = 0; 111 112 extern char *__progname; /* from crt0.o */ 113 114 static char *cskip __P((char *)); 115 static void error __P((char *, char *)); 116 static void nomem __P((void)); 117 static char *getline __P((int)); 118 static char *getmsg __P((int, char *, char)); 119 static void warning __P((char *, char *)); 120 static char *wskip __P((char *)); 121 static char *xstrdup __P((const char *)); 122 static void *xmalloc __P((size_t)); 123 static void *xrealloc __P((void *, size_t)); 124 125 void MCParse __P((int fd)); 126 void MCWriteCat __P((int fd)); 127 void MCDelMsg __P((int msgId)); 128 void MCAddMsg __P((int msgId, const char *msg)); 129 void MCAddSet __P((int setId)); 130 void MCDelSet __P((int setId)); 131 int main __P((int, char **)); 132 void usage __P((void)); 133 134 135 void 136 usage() 137 { 138 fprintf(stderr, "Usage: %s catfile msgfile ...\n", __progname); 139 exit(1); 140 } 141 142 int 143 main(argc, argv) 144 int argc; 145 char *argv[]; 146 { 147 int ofd, ifd; 148 char *catfile = NULL; 149 int c; 150 151 while ((c = getopt(argc, argv, "")) != -1) { 152 switch (c) { 153 case '?': 154 default: 155 usage(); 156 /* NOTREACHED */ 157 } 158 } 159 argc -= optind; 160 argv += optind; 161 162 if (argc < 2) { 163 usage(); 164 /* NOTREACHED */ 165 } 166 catfile = *argv++; 167 168 for (; *argv; argv++) { 169 if ((ifd = open(*argv, O_RDONLY)) < 0) 170 err(1, "Unable to read %s", *argv); 171 MCParse(ifd); 172 close(ifd); 173 } 174 175 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) 176 err(1, "Unable to create a new %s", catfile); 177 MCWriteCat(ofd); 178 exit(0); 179 } 180 181 static void 182 warning(cptr, msg) 183 char *cptr; 184 char *msg; 185 { 186 fprintf(stderr, "%s: %s on line %ld\n", __progname, msg, lineno); 187 fprintf(stderr, "%s\n", curline); 188 if (cptr) { 189 char *tptr; 190 for (tptr = curline; tptr < cptr; ++tptr) 191 putc(' ', stderr); 192 fprintf(stderr, "^\n"); 193 } 194 } 195 196 static void 197 error(cptr, msg) 198 char *cptr; 199 char *msg; 200 { 201 warning(cptr, msg); 202 exit(1); 203 } 204 205 static void 206 nomem() 207 { 208 error(NULL, "out of memory"); 209 } 210 211 static void * 212 xmalloc(len) 213 size_t len; 214 { 215 void *p; 216 217 if ((p = malloc(len)) == NULL) 218 nomem(); 219 return (p); 220 } 221 222 static void * 223 xrealloc(ptr, size) 224 void *ptr; 225 size_t size; 226 { 227 if ((ptr = realloc(ptr, size)) == NULL) 228 nomem(); 229 return (ptr); 230 } 231 232 static char * 233 xstrdup(str) 234 const char *str; 235 { 236 char *nstr; 237 238 if ((nstr = strdup(str)) == NULL) 239 nomem(); 240 return (nstr); 241 } 242 243 static char * 244 getline(fd) 245 int fd; 246 { 247 static long curlen = BUFSIZ; 248 static char buf[BUFSIZ], *bptr = buf, *bend = buf; 249 char *cptr, *cend; 250 long buflen; 251 252 if (!curline) { 253 curline = xmalloc(curlen); 254 } 255 ++lineno; 256 257 cptr = curline; 258 cend = curline + curlen; 259 for (;;) { 260 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 261 if (*bptr == '\n') { 262 *cptr = '\0'; 263 ++bptr; 264 return (curline); 265 } else 266 *cptr = *bptr; 267 } 268 if (bptr == bend) { 269 buflen = read(fd, buf, BUFSIZ); 270 if (buflen <= 0) { 271 if (cptr > curline) { 272 *cptr = '\0'; 273 return (curline); 274 } 275 return (NULL); 276 } 277 bend = buf + buflen; 278 bptr = buf; 279 } 280 if (cptr == cend) { 281 cptr = curline = xrealloc(curline, curlen *= 2); 282 cend = curline + curlen; 283 } 284 } 285 } 286 287 static char * 288 wskip(cptr) 289 char *cptr; 290 { 291 if (!*cptr || !ISSPACE(*cptr)) { 292 warning(cptr, "expected a space"); 293 return (cptr); 294 } 295 while (*cptr && ISSPACE(*cptr)) 296 ++cptr; 297 return (cptr); 298 } 299 300 static char * 301 cskip(cptr) 302 char *cptr; 303 { 304 if (!*cptr || ISSPACE(*cptr)) { 305 warning(cptr, "wasn't expecting a space"); 306 return (cptr); 307 } 308 while (*cptr && !ISSPACE(*cptr)) 309 ++cptr; 310 return (cptr); 311 } 312 313 static char * 314 getmsg(fd, cptr, quote) 315 int fd; 316 char *cptr; 317 char quote; 318 { 319 static char *msg = NULL; 320 static long msglen = 0; 321 long clen, i; 322 char *tptr; 323 324 if (quote && *cptr == quote) { 325 ++cptr; 326 } 327 328 clen = strlen(cptr) + 1; 329 if (clen > msglen) { 330 if (msglen) 331 msg = xrealloc(msg, clen); 332 else 333 msg = xmalloc(clen); 334 msglen = clen; 335 } 336 tptr = msg; 337 338 while (*cptr) { 339 if (quote && *cptr == quote) { 340 char *tmp; 341 tmp = cptr + 1; 342 if (*tmp && (!ISSPACE(*tmp) || *wskip(tmp))) { 343 warning(cptr, "unexpected quote character, ignoring"); 344 *tptr++ = *cptr++; 345 } else { 346 *cptr = '\0'; 347 } 348 } else 349 if (*cptr == '\\') { 350 ++cptr; 351 switch (*cptr) { 352 case '\0': 353 cptr = getline(fd); 354 if (!cptr) 355 error(NULL, "premature end of file"); 356 msglen += strlen(cptr); 357 i = tptr - msg; 358 msg = xrealloc(msg, msglen); 359 tptr = msg + i; 360 break; 361 case 'n': 362 *tptr++ = '\n'; 363 ++cptr; 364 break; 365 case 't': 366 *tptr++ = '\t'; 367 ++cptr; 368 break; 369 case 'v': 370 *tptr++ = '\v'; 371 ++cptr; 372 break; 373 case 'b': 374 *tptr++ = '\b'; 375 ++cptr; 376 break; 377 case 'r': 378 *tptr++ = '\r'; 379 ++cptr; 380 break; 381 case 'f': 382 *tptr++ = '\f'; 383 ++cptr; 384 break; 385 case '\\': 386 *tptr++ = '\\'; 387 ++cptr; 388 break; 389 case '"': 390 /* FALLTHROUGH */ 391 case '\'': 392 /* 393 * While it isn't necessary to 394 * escape ' and ", let's accept 395 * them escaped and not complain. 396 * (XPG4 states that '\' should be 397 * ignored when not used in a 398 * valid escape sequence) 399 */ 400 *tptr++ = '"'; 401 ++cptr; 402 break; 403 default: 404 if (quote && *cptr == quote) { 405 *tptr++ = *cptr++; 406 } else if (isdigit((unsigned char) *cptr)) { 407 *tptr = 0; 408 for (i = 0; i < 3; ++i) { 409 if (!isdigit((unsigned char) *cptr)) 410 break; 411 if (*cptr > '7') 412 warning(cptr, "octal number greater than 7?!"); 413 *tptr *= 8; 414 *tptr += (*cptr - '0'); 415 ++cptr; 416 } 417 } else { 418 warning(cptr, "unrecognized escape sequence; ignoring esacpe character"); 419 } 420 break; 421 } 422 } else { 423 *tptr++ = *cptr++; 424 } 425 } 426 *tptr = '\0'; 427 return (msg); 428 } 429 430 void 431 MCParse(fd) 432 int fd; 433 { 434 char *cptr, *str; 435 int setid, msgid = 0; 436 char quote = 0; 437 438 /* XXX: init sethead? */ 439 440 while ((cptr = getline(fd))) { 441 if (*cptr == '$') { 442 ++cptr; 443 if (strncmp(cptr, "set", 3) == 0) { 444 cptr += 3; 445 cptr = wskip(cptr); 446 setid = atoi(cptr); 447 MCAddSet(setid); 448 msgid = 0; 449 } else if (strncmp(cptr, "delset", 6) == 0) { 450 cptr += 6; 451 cptr = wskip(cptr); 452 setid = atoi(cptr); 453 MCDelSet(setid); 454 } else if (strncmp(cptr, "quote", 5) == 0) { 455 cptr += 5; 456 if (!*cptr) 457 quote = 0; 458 else { 459 cptr = wskip(cptr); 460 if (!*cptr) 461 quote = 0; 462 else 463 quote = *cptr; 464 } 465 } else if (ISSPACE(*cptr)) { 466 ; 467 } else { 468 if (*cptr) { 469 cptr = wskip(cptr); 470 if (*cptr) 471 warning(cptr, "unrecognized line"); 472 } 473 } 474 } else { 475 /* 476 * First check for (and eat) empty lines.... 477 */ 478 if (!*cptr) 479 continue; 480 /* 481 * We have a digit? Start of a message. Else, 482 * syntax error. 483 */ 484 if (isdigit((unsigned char) *cptr)) { 485 msgid = atoi(cptr); 486 cptr = cskip(cptr); 487 cptr = wskip(cptr); 488 /* if (*cptr) ++cptr; */ 489 } else { 490 warning(cptr, "neither blank line nor start of a message id"); 491 continue; 492 } 493 /* 494 * If we have a message ID, but no message, 495 * then this means "delete this message id 496 * from the catalog". 497 */ 498 if (!*cptr) { 499 MCDelMsg(msgid); 500 } else { 501 str = getmsg(fd, cptr, quote); 502 MCAddMsg(msgid, str); 503 } 504 } 505 } 506 } 507 508 /* 509 * Write message catalog. 510 * 511 * The message catalog is first converted from its internal to its 512 * external representation in a chunk of memory allocated for this 513 * purpose. Then the completed catalog is written. This approach 514 * avoids additional housekeeping variables and/or a lot of seeks 515 * that would otherwise be required. 516 */ 517 void 518 MCWriteCat(fd) 519 int fd; 520 { 521 int nsets; /* number of sets */ 522 int nmsgs; /* number of msgs */ 523 int string_size; /* total size of string pool */ 524 int msgcat_size; /* total size of message catalog */ 525 void *msgcat; /* message catalog data */ 526 struct _nls_cat_hdr *cat_hdr; 527 struct _nls_set_hdr *set_hdr; 528 struct _nls_msg_hdr *msg_hdr; 529 char *strings; 530 struct _setT *set; 531 struct _msgT *msg; 532 int msg_index; 533 int msg_offset; 534 535 /* determine number of sets, number of messages, and size of the 536 * string pool */ 537 nsets = 0; 538 nmsgs = 0; 539 string_size = 0; 540 541 for (set = sethead.lh_first; set != NULL; 542 set = set->entries.le_next) { 543 nsets++; 544 545 for (msg = set->msghead.lh_first; msg != NULL; 546 msg = msg->entries.le_next) { 547 nmsgs++; 548 string_size += strlen(msg->str) + 1; 549 } 550 } 551 552 #ifdef DEBUG 553 printf("number of sets: %d\n", nsets); 554 printf("number of msgs: %d\n", nmsgs); 555 printf("string pool size: %d\n", string_size); 556 #endif 557 558 /* determine size and then allocate buffer for constructing external 559 * message catalog representation */ 560 msgcat_size = sizeof(struct _nls_cat_hdr) 561 + (nsets * sizeof(struct _nls_set_hdr)) 562 + (nmsgs * sizeof(struct _nls_msg_hdr)) 563 + string_size; 564 565 msgcat = xmalloc(msgcat_size); 566 memset(msgcat, '\0', msgcat_size); 567 568 /* fill in msg catalog header */ 569 cat_hdr = (struct _nls_cat_hdr *) msgcat; 570 cat_hdr->__magic = htonl(_NLS_MAGIC); 571 cat_hdr->__nsets = htonl(nsets); 572 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 573 cat_hdr->__msg_hdr_offset = 574 htonl(nsets * sizeof(struct _nls_set_hdr)); 575 cat_hdr->__msg_txt_offset = 576 htonl(nsets * sizeof(struct _nls_set_hdr) + 577 nmsgs * sizeof(struct _nls_msg_hdr)); 578 579 /* compute offsets for set & msg header tables and string pool */ 580 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat + 581 sizeof(struct _nls_cat_hdr)); 582 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat + 583 sizeof(struct _nls_cat_hdr) + 584 nsets * sizeof(struct _nls_set_hdr)); 585 strings = (char *) msgcat + 586 sizeof(struct _nls_cat_hdr) + 587 nsets * sizeof(struct _nls_set_hdr) + 588 nmsgs * sizeof(struct _nls_msg_hdr); 589 590 msg_index = 0; 591 msg_offset = 0; 592 for (set = sethead.lh_first; set != NULL; 593 set = set->entries.le_next) { 594 595 nmsgs = 0; 596 for (msg = set->msghead.lh_first; msg != NULL; 597 msg = msg->entries.le_next) { 598 int msg_len = strlen(msg->str) + 1; 599 600 msg_hdr->__msgno = htonl(msg->msgId); 601 msg_hdr->__msglen = htonl(msg_len); 602 msg_hdr->__offset = htonl(msg_offset); 603 604 memcpy(strings, msg->str, msg_len); 605 strings += msg_len; 606 msg_offset += msg_len; 607 608 nmsgs++; 609 msg_hdr++; 610 } 611 612 set_hdr->__setno = htonl(set->setId); 613 set_hdr->__nmsgs = htonl(nmsgs); 614 set_hdr->__index = htonl(msg_index); 615 msg_index += nmsgs; 616 set_hdr++; 617 } 618 619 /* write out catalog. XXX: should this be done in small chunks? */ 620 write(fd, msgcat, msgcat_size); 621 } 622 623 void 624 MCAddSet(setId) 625 int setId; 626 { 627 struct _setT *p, *q; 628 629 if (setId <= 0) { 630 error(NULL, "setId's must be greater than zero"); 631 /* NOTREACHED */ 632 } 633 #if 0 634 /* XXX */ 635 if (setId > NL_SETMAX) { 636 error(NULL, "setId %d exceeds limit (%d)"); 637 /* NOTREACHED */ 638 } 639 #endif 640 641 p = sethead.lh_first; 642 q = NULL; 643 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next); 644 645 if (p && p->setId == setId) { 646 ; 647 } else { 648 p = xmalloc(sizeof(struct _setT)); 649 memset(p, '\0', sizeof(struct _setT)); 650 LIST_INIT(&p->msghead); 651 652 p->setId = setId; 653 654 if (q == NULL) { 655 LIST_INSERT_HEAD(&sethead, p, entries); 656 } else { 657 LIST_INSERT_AFTER(q, p, entries); 658 } 659 } 660 661 curSet = p; 662 } 663 664 void 665 MCAddMsg(msgId, str) 666 int msgId; 667 const char *str; 668 { 669 struct _msgT *p, *q; 670 671 if (!curSet) 672 error(NULL, "can't specify a message when no set exists"); 673 674 if (msgId <= 0) { 675 error(NULL, "msgId's must be greater than zero"); 676 /* NOTREACHED */ 677 } 678 #if 0 679 /* XXX */ 680 if (msgId > NL_SETMAX) { 681 error(NULL, "msgId %d exceeds limit (%d)"); 682 /* NOTREACHED */ 683 } 684 #endif 685 686 p = curSet->msghead.lh_first; 687 q = NULL; 688 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next); 689 690 if (p && p->msgId == msgId) { 691 free(p->str); 692 } else { 693 p = xmalloc(sizeof(struct _msgT)); 694 memset(p, '\0', sizeof(struct _msgT)); 695 696 if (q == NULL) { 697 LIST_INSERT_HEAD(&curSet->msghead, p, entries); 698 } else { 699 LIST_INSERT_AFTER(q, p, entries); 700 } 701 } 702 703 p->msgId = msgId; 704 p->str = xstrdup(str); 705 } 706 707 void 708 MCDelSet(setId) 709 int setId; 710 { 711 struct _setT *set; 712 struct _msgT *msg; 713 714 set = sethead.lh_first; 715 for (; set != NULL && set->setId < setId; set = set->entries.le_next); 716 717 if (set && set->setId == setId) { 718 719 msg = set->msghead.lh_first; 720 while (msg) { 721 free(msg->str); 722 LIST_REMOVE(msg, entries); 723 } 724 725 LIST_REMOVE(set, entries); 726 return; 727 } 728 warning(NULL, "specified set doesn't exist"); 729 } 730 731 void 732 MCDelMsg(msgId) 733 int msgId; 734 { 735 struct _msgT *msg; 736 737 if (!curSet) 738 error(NULL, "you can't delete a message before defining the set"); 739 740 msg = curSet->msghead.lh_first; 741 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next); 742 743 if (msg && msg->msgId == msgId) { 744 free(msg->str); 745 LIST_REMOVE(msg, entries); 746 return; 747 } 748 warning(NULL, "specified msg doesn't exist"); 749 } 750