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