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