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