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 #ifdef BOOTSTRAPPING 88 #define getline get_line /* help bootstrap previous stdio.h */ 89 #endif 90 91 struct _msgT { 92 long msgId; 93 char *str; 94 LIST_ENTRY(_msgT) entries; 95 }; 96 97 struct _setT { 98 long setId; 99 LIST_HEAD(msghead, _msgT) msghead; 100 LIST_ENTRY(_setT) entries; 101 }; 102 103 static LIST_HEAD(sethead, _setT) sethead; 104 static struct _setT *curSet; 105 106 static char *curline = NULL; 107 static long lineno = 0; 108 109 static char *cskip(char *); 110 static void error(const char *); 111 static char *get_line(int); 112 static char *getmsg(int, char *, char); 113 static void warning(const char *, const char *); 114 static char *wskip(char *); 115 static char *xstrdup(const char *); 116 static void *xmalloc(size_t); 117 static void *xrealloc(void *, size_t); 118 119 void MCParse(int); 120 void MCReadCat(int); 121 void MCWriteCat(int); 122 void MCDelMsg(int); 123 void MCAddMsg(int, const char *); 124 void MCAddSet(int); 125 void MCDelSet(int); 126 void usage(void); 127 128 void 129 usage(void) 130 { 131 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname()); 132 exit(1); 133 } 134 135 int 136 main(int argc, char **argv) 137 { 138 int ofd, ifd; 139 char *catfile = NULL; 140 int c; 141 142 #define DEPRECATEDMSG 1 143 144 #ifdef DEPRECATEDMSG 145 while ((c = getopt(argc, argv, "new")) != -1) { 146 #else 147 while ((c = getopt(argc, argv, "")) != -1) { 148 #endif 149 switch (c) { 150 #ifdef DEPRECATEDMSG 151 case 'n': 152 fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n"); 153 case 'e': 154 case 'w': 155 break; 156 #endif 157 case '?': 158 default: 159 usage(); 160 /* NOTREACHED */ 161 } 162 } 163 argc -= optind; 164 argv += optind; 165 166 if (argc < 2) { 167 usage(); 168 /* NOTREACHED */ 169 } 170 catfile = *argv++; 171 172 for (; *argv; argv++) { 173 if ((ifd = open(*argv, O_RDONLY)) < 0) 174 err(1, "Unable to read %s", *argv); 175 MCParse(ifd); 176 close(ifd); 177 } 178 179 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) 180 err(1, "Unable to create a new %s", catfile); 181 MCWriteCat(ofd); 182 exit(0); 183 } 184 185 static void 186 warning(const char *cptr, const char *msg) 187 { 188 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno); 189 fprintf(stderr, "%s\n", curline); 190 if (cptr) { 191 char *tptr; 192 for (tptr = curline; tptr < cptr; ++tptr) 193 putc(' ', stderr); 194 fprintf(stderr, "^\n"); 195 } 196 } 197 198 #define CORRUPT() { error("corrupt message catalog"); } 199 #define NOMEM() { error("out of memory"); } 200 201 static void 202 error(const char *msg) 203 { 204 warning(NULL, msg); 205 exit(1); 206 } 207 208 static void * 209 xmalloc(size_t len) 210 { 211 void *p; 212 213 if ((p = malloc(len)) == NULL) 214 NOMEM(); 215 return (p); 216 } 217 218 static void * 219 xrealloc(void *ptr, size_t size) 220 { 221 if ((ptr = realloc(ptr, size)) == NULL) 222 NOMEM(); 223 return (ptr); 224 } 225 226 static char * 227 xstrdup(const char *str) 228 { 229 char *nstr; 230 231 if ((nstr = strdup(str)) == NULL) 232 NOMEM(); 233 return (nstr); 234 } 235 236 static char * 237 get_line(int fd) 238 { 239 static long curlen = BUFSIZ; 240 static char buf[BUFSIZ], *bptr = buf, *bend = buf; 241 char *cptr, *cend; 242 long buflen; 243 244 if (!curline) { 245 curline = xmalloc(curlen); 246 } 247 ++lineno; 248 249 cptr = curline; 250 cend = curline + curlen; 251 for (;;) { 252 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 253 if (*bptr == '\n') { 254 *cptr = '\0'; 255 ++bptr; 256 return (curline); 257 } else 258 *cptr = *bptr; 259 } 260 if (cptr == cend) { 261 cptr = curline = xrealloc(curline, curlen *= 2); 262 cend = curline + curlen; 263 } 264 if (bptr == bend) { 265 buflen = read(fd, buf, BUFSIZ); 266 if (buflen <= 0) { 267 if (cptr > curline) { 268 *cptr = '\0'; 269 return (curline); 270 } 271 return (NULL); 272 } 273 bend = buf + buflen; 274 bptr = buf; 275 } 276 } 277 } 278 279 static char * 280 wskip(char *cptr) 281 { 282 if (!*cptr || !isspace((unsigned char) *cptr)) { 283 warning(cptr, "expected a space"); 284 return (cptr); 285 } 286 while (*cptr && isspace((unsigned char) *cptr)) 287 ++cptr; 288 return (cptr); 289 } 290 291 static char * 292 cskip(char *cptr) 293 { 294 if (!*cptr || isspace((unsigned char) *cptr)) { 295 warning(cptr, "wasn't expecting a space"); 296 return (cptr); 297 } 298 while (*cptr && !isspace((unsigned char) *cptr)) 299 ++cptr; 300 return (cptr); 301 } 302 303 static char * 304 getmsg(int fd, char *cptr, char quote) 305 { 306 static char *msg = NULL; 307 static long msglen = 0; 308 long clen, i; 309 char *tptr; 310 311 if (quote && *cptr == quote) { 312 ++cptr; 313 } 314 315 clen = strlen(cptr) + 1; 316 if (clen > msglen) { 317 if (msglen) 318 msg = xrealloc(msg, clen); 319 else 320 msg = xmalloc(clen); 321 msglen = clen; 322 } 323 tptr = msg; 324 325 while (*cptr) { 326 if (quote && *cptr == quote) { 327 char *tmp; 328 tmp = cptr + 1; 329 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) { 330 warning(cptr, "unexpected quote character, ignoring"); 331 *tptr++ = *cptr++; 332 } else { 333 *cptr = '\0'; 334 } 335 } else 336 if (*cptr == '\\') { 337 ++cptr; 338 switch (*cptr) { 339 case '\0': 340 cptr = get_line(fd); 341 if (!cptr) 342 error("premature end of file"); 343 msglen += strlen(cptr); 344 i = tptr - msg; 345 msg = xrealloc(msg, msglen); 346 tptr = msg + i; 347 break; 348 349 #define CASEOF(CS, CH) \ 350 case CS: \ 351 *tptr++ = CH; \ 352 ++cptr; \ 353 break; \ 354 355 CASEOF('n', '\n'); 356 CASEOF('t', '\t'); 357 CASEOF('v', '\v'); 358 CASEOF('b', '\b'); 359 CASEOF('r', '\r'); 360 CASEOF('f', '\f'); 361 CASEOF('"', '"'); 362 CASEOF('\\', '\\'); 363 364 default: 365 if (quote && *cptr == quote) { 366 *tptr++ = *cptr++; 367 } else if (isdigit((unsigned char) *cptr)) { 368 *tptr = 0; 369 for (i = 0; i < 3; ++i) { 370 if (!isdigit((unsigned char) *cptr)) 371 break; 372 if (*cptr > '7') 373 warning(cptr, "octal number greater than 7?!"); 374 *tptr *= 8; 375 *tptr += (*cptr - '0'); 376 ++cptr; 377 } 378 } else { 379 warning(cptr, "unrecognized escape sequence"); 380 } 381 break; 382 } 383 } else { 384 *tptr++ = *cptr++; 385 } 386 } 387 *tptr = '\0'; 388 return (msg); 389 } 390 391 void 392 MCParse(int fd) 393 { 394 char *cptr, *str; 395 int setid, msgid = 0; 396 char quote = 0; 397 398 /* XXX: init sethead? */ 399 400 while ((cptr = get_line(fd))) { 401 if (*cptr == '$') { 402 ++cptr; 403 if (strncmp(cptr, "set", 3) == 0) { 404 cptr += 3; 405 cptr = wskip(cptr); 406 setid = atoi(cptr); 407 MCAddSet(setid); 408 msgid = 0; 409 } else if (strncmp(cptr, "delset", 6) == 0) { 410 cptr += 6; 411 cptr = wskip(cptr); 412 setid = atoi(cptr); 413 MCDelSet(setid); 414 } else if (strncmp(cptr, "quote", 5) == 0) { 415 cptr += 5; 416 if (!*cptr) 417 quote = 0; 418 else { 419 cptr = wskip(cptr); 420 if (!*cptr) 421 quote = 0; 422 else 423 quote = *cptr; 424 } 425 } else if (isspace((unsigned char) *cptr)) { 426 ; 427 } else { 428 if (*cptr) { 429 cptr = wskip(cptr); 430 if (*cptr) 431 warning(cptr, "unrecognized line"); 432 } 433 } 434 } else { 435 /* 436 * First check for (and eat) empty lines.... 437 */ 438 if (!*cptr) 439 continue; 440 /* 441 * We have a digit? Start of a message. Else, 442 * syntax error. 443 */ 444 if (isdigit((unsigned char) *cptr)) { 445 msgid = atoi(cptr); 446 cptr = cskip(cptr); 447 cptr = wskip(cptr); 448 /* if (*cptr) ++cptr; */ 449 } else { 450 warning(cptr, "neither blank line nor start of a message id"); 451 continue; 452 } 453 /* 454 * If we have a message ID, but no message, 455 * then this means "delete this message id 456 * from the catalog". 457 */ 458 if (!*cptr) { 459 MCDelMsg(msgid); 460 } else { 461 str = getmsg(fd, cptr, quote); 462 MCAddMsg(msgid, str); 463 } 464 } 465 } 466 } 467 468 /* 469 * Write message catalog. 470 * 471 * The message catalog is first converted from its internal to its 472 * external representation in a chunk of memory allocated for this 473 * purpose. Then the completed catalog is written. This approach 474 * avoids additional housekeeping variables and/or a lot of seeks 475 * that would otherwise be required. 476 */ 477 void 478 MCWriteCat(int fd) 479 { 480 int nsets; /* number of sets */ 481 int nmsgs; /* number of msgs */ 482 int string_size; /* total size of string pool */ 483 int msgcat_size; /* total size of message catalog */ 484 void *msgcat; /* message catalog data */ 485 struct _nls_cat_hdr *cat_hdr; 486 struct _nls_set_hdr *set_hdr; 487 struct _nls_msg_hdr *msg_hdr; 488 char *strings; 489 struct _setT *set; 490 struct _msgT *msg; 491 int msg_index; 492 int msg_offset; 493 494 /* determine number of sets, number of messages, and size of the 495 * string pool */ 496 nsets = 0; 497 nmsgs = 0; 498 string_size = 0; 499 500 for (set = sethead.lh_first; set != NULL; 501 set = set->entries.le_next) { 502 nsets++; 503 504 for (msg = set->msghead.lh_first; msg != NULL; 505 msg = msg->entries.le_next) { 506 nmsgs++; 507 string_size += strlen(msg->str) + 1; 508 } 509 } 510 511 #ifdef DEBUG 512 printf("number of sets: %d\n", nsets); 513 printf("number of msgs: %d\n", nmsgs); 514 printf("string pool size: %d\n", string_size); 515 #endif 516 517 /* determine size and then allocate buffer for constructing external 518 * message catalog representation */ 519 msgcat_size = sizeof(struct _nls_cat_hdr) 520 + (nsets * sizeof(struct _nls_set_hdr)) 521 + (nmsgs * sizeof(struct _nls_msg_hdr)) 522 + string_size; 523 524 msgcat = xmalloc(msgcat_size); 525 memset(msgcat, '\0', msgcat_size); 526 527 /* fill in msg catalog header */ 528 cat_hdr = (struct _nls_cat_hdr *) msgcat; 529 cat_hdr->__magic = htonl(_NLS_MAGIC); 530 cat_hdr->__nsets = htonl(nsets); 531 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 532 cat_hdr->__msg_hdr_offset = 533 htonl(nsets * sizeof(struct _nls_set_hdr)); 534 cat_hdr->__msg_txt_offset = 535 htonl(nsets * sizeof(struct _nls_set_hdr) + 536 nmsgs * sizeof(struct _nls_msg_hdr)); 537 538 /* compute offsets for set & msg header tables and string pool */ 539 set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat + 540 sizeof(struct _nls_cat_hdr)); 541 msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat + 542 sizeof(struct _nls_cat_hdr) + 543 nsets * sizeof(struct _nls_set_hdr)); 544 strings = (char *) msgcat + 545 sizeof(struct _nls_cat_hdr) + 546 nsets * sizeof(struct _nls_set_hdr) + 547 nmsgs * sizeof(struct _nls_msg_hdr); 548 549 msg_index = 0; 550 msg_offset = 0; 551 for (set = sethead.lh_first; set != NULL; 552 set = set->entries.le_next) { 553 554 nmsgs = 0; 555 for (msg = set->msghead.lh_first; msg != NULL; 556 msg = msg->entries.le_next) { 557 int msg_len = strlen(msg->str) + 1; 558 559 msg_hdr->__msgno = htonl(msg->msgId); 560 msg_hdr->__msglen = htonl(msg_len); 561 msg_hdr->__offset = htonl(msg_offset); 562 563 memcpy(strings, msg->str, msg_len); 564 strings += msg_len; 565 msg_offset += msg_len; 566 567 nmsgs++; 568 msg_hdr++; 569 } 570 571 set_hdr->__setno = htonl(set->setId); 572 set_hdr->__nmsgs = htonl(nmsgs); 573 set_hdr->__index = htonl(msg_index); 574 msg_index += nmsgs; 575 set_hdr++; 576 } 577 578 /* write out catalog. XXX: should this be done in small chunks? */ 579 write(fd, msgcat, msgcat_size); 580 } 581 582 void 583 MCAddSet(int setId) 584 { 585 struct _setT *p, *q; 586 587 if (setId <= 0) { 588 error("setId's must be greater than zero"); 589 /* NOTREACHED */ 590 } 591 if (setId > NL_SETMAX) { 592 error("setId exceeds limit"); 593 /* NOTREACHED */ 594 } 595 596 p = sethead.lh_first; 597 q = NULL; 598 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next); 599 600 if (p && p->setId == setId) { 601 ; 602 } else { 603 p = xmalloc(sizeof(struct _setT)); 604 memset(p, '\0', sizeof(struct _setT)); 605 LIST_INIT(&p->msghead); 606 607 p->setId = setId; 608 609 if (q == NULL) { 610 LIST_INSERT_HEAD(&sethead, p, entries); 611 } else { 612 LIST_INSERT_AFTER(q, p, entries); 613 } 614 } 615 616 curSet = p; 617 } 618 619 void 620 MCAddMsg(int msgId, const char *str) 621 { 622 struct _msgT *p, *q; 623 624 if (!curSet) 625 error("can't specify a message when no set exists"); 626 627 if (msgId <= 0) { 628 error("msgId's must be greater than zero"); 629 /* NOTREACHED */ 630 } 631 if (msgId > NL_MSGMAX) { 632 error("msgID exceeds limit"); 633 /* NOTREACHED */ 634 } 635 636 p = curSet->msghead.lh_first; 637 q = NULL; 638 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next); 639 640 if (p && p->msgId == msgId) { 641 free(p->str); 642 } else { 643 p = xmalloc(sizeof(struct _msgT)); 644 memset(p, '\0', sizeof(struct _msgT)); 645 646 if (q == NULL) { 647 LIST_INSERT_HEAD(&curSet->msghead, p, entries); 648 } else { 649 LIST_INSERT_AFTER(q, p, entries); 650 } 651 } 652 653 p->msgId = msgId; 654 p->str = xstrdup(str); 655 } 656 657 void 658 MCDelSet(int setId) 659 { 660 struct _setT *set; 661 struct _msgT *msg; 662 663 set = sethead.lh_first; 664 for (; set != NULL && set->setId < setId; set = set->entries.le_next); 665 666 if (set && set->setId == setId) { 667 668 msg = set->msghead.lh_first; 669 while (msg) { 670 free(msg->str); 671 LIST_REMOVE(msg, entries); 672 } 673 674 LIST_REMOVE(set, entries); 675 return; 676 } 677 warning(NULL, "specified set doesn't exist"); 678 } 679 680 void 681 MCDelMsg(int msgId) 682 { 683 struct _msgT *msg; 684 685 if (!curSet) 686 error("you can't delete a message before defining the set"); 687 688 msg = curSet->msghead.lh_first; 689 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next); 690 691 if (msg && msg->msgId == msgId) { 692 free(msg->str); 693 LIST_REMOVE(msg, entries); 694 return; 695 } 696 warning(NULL, "specified msg doesn't exist"); 697 } 698