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