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