1 /* $NetBSD: gencat.c,v 1.35 2012/03/09 18:54:28 ginsbach Exp $ */ 2 3 /* 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by J.T. Conklin. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #if defined(__RCSID) && !defined(lint) 34 __RCSID("$NetBSD: gencat.c,v 1.35 2012/03/09 18:54:28 ginsbach Exp $"); 35 #endif 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 #if HAVE_NBTOOL_CONFIG_H 70 #include "nbtool_config.h" 71 #endif 72 73 #define _NLS_PRIVATE 74 75 #include <sys/types.h> 76 #include <sys/queue.h> 77 78 #include <netinet/in.h> /* Needed by arpa/inet.h on NetBSD */ 79 #include <arpa/inet.h> /* Needed for htonl() on POSIX systems */ 80 81 #include <ctype.h> 82 #include <err.h> 83 #include <errno.h> 84 #include <fcntl.h> 85 #include <limits.h> 86 #include <nl_types.h> 87 #include <stdio.h> 88 #include <stdlib.h> 89 #include <string.h> 90 #include <unistd.h> 91 92 #ifndef NL_SETMAX 93 #define NL_SETMAX 255 94 #endif 95 #ifndef NL_MSGMAX 96 #define NL_MSGMAX 2048 97 #endif 98 99 struct _msgT { 100 long msgId; 101 char *str; 102 LIST_ENTRY(_msgT) entries; 103 }; 104 105 struct _setT { 106 long setId; 107 LIST_HEAD(msghead, _msgT) msghead; 108 LIST_ENTRY(_setT) entries; 109 }; 110 111 static LIST_HEAD(sethead, _setT) sethead; 112 static struct _setT *curSet; 113 114 static const char *curfile; 115 static char *curline = NULL; 116 static long lineno = 0; 117 118 static char *cskip(char *); 119 __dead static void error(const char *); 120 static char *get_line(int); 121 static char *getmsg(int, char *, char); 122 static void warning(const char *, const char *); 123 static char *wskip(char *); 124 static char *xstrdup(const char *); 125 static void *xmalloc(size_t); 126 static void *xrealloc(void *, size_t); 127 128 static void MCParse(int fd); 129 static void MCReadCat(int fd); 130 static void MCWriteCat(int fd); 131 static void MCDelMsg(int msgId); 132 static void MCAddMsg(int msgId, const char *msg); 133 static void MCAddSet(int setId); 134 static void MCDelSet(int setId); 135 __dead static void usage(void); 136 137 #define CORRUPT "corrupt message catalog" 138 #define NOMEMORY "out of memory" 139 140 static void 141 usage(void) 142 { 143 fprintf(stderr, "usage: %s catfile [msgfile|- ...]\n", getprogname()); 144 exit(1); 145 } 146 147 int 148 main(int argc, char *argv[]) 149 { 150 int ofd, ifd; 151 char *catfile = NULL; 152 int c; 153 int updatecat = 0; 154 155 while ((c = getopt(argc, argv, "")) != -1) { 156 switch (c) { 157 case '?': 158 default: 159 usage(); 160 /* NOTREACHED */ 161 } 162 } 163 argc -= optind; 164 argv += optind; 165 166 if (argc < 1) { 167 usage(); 168 /* NOTREACHED */ 169 } 170 catfile = *argv++; 171 172 if ((catfile[0] == '-') && (catfile[1] == '\0')) { 173 ofd = STDOUT_FILENO; 174 } else { 175 ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666); 176 if (ofd < 0) { 177 if (errno == EEXIST) { 178 if ((ofd = open(catfile, O_RDWR)) < 0) { 179 err(1, "Unable to open %s", catfile); 180 /* NOTREACHED */ 181 } 182 } else { 183 err(1, "Unable to create new %s", catfile); 184 /* NOTREACHED */ 185 } 186 curfile = catfile; 187 updatecat = 1; 188 MCReadCat(ofd); 189 if (lseek(ofd, (off_t)0, SEEK_SET) == (off_t)-1) { 190 err(1, "Unable to seek on %s", catfile); 191 /* NOTREACHED */ 192 } 193 } 194 } 195 196 if (argc < 2 || (((*argv)[0] == '-') && ((*argv)[1] == '\0'))) { 197 if (argc > 2) 198 usage(); 199 /* NOTREACHED */ 200 MCParse(STDIN_FILENO); 201 } else { 202 for (; *argv; argv++) { 203 if ((ifd = open(*argv, O_RDONLY)) < 0) 204 err(1, "Unable to read %s", *argv); 205 curfile = *argv; 206 lineno = 0; 207 MCParse(ifd); 208 close(ifd); 209 } 210 } 211 212 if (updatecat) { 213 if (ftruncate(ofd, 0) != 0) { 214 err(1, "Unable to truncate %s", catfile); 215 /* NOTREACHED */ 216 } 217 } 218 219 MCWriteCat(ofd); 220 exit(0); 221 } 222 223 static void 224 warning(const char *cptr, const char *msg) 225 { 226 if (lineno) { 227 fprintf(stderr, "%s: %s on line %ld, %s\n", 228 getprogname(), msg, lineno, curfile); 229 fprintf(stderr, "%s\n", curline); 230 if (cptr) { 231 char *tptr; 232 for (tptr = curline; tptr < cptr; ++tptr) 233 putc(' ', stderr); 234 fprintf(stderr, "^\n"); 235 } 236 } else { 237 fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile); 238 } 239 } 240 241 static void 242 error(const char *msg) 243 { 244 warning(NULL, msg); 245 exit(1); 246 } 247 248 static void * 249 xmalloc(size_t len) 250 { 251 void *p; 252 253 if ((p = malloc(len)) == NULL) 254 errx(1, NOMEMORY); 255 return (p); 256 } 257 258 static void * 259 xrealloc(void *ptr, size_t size) 260 { 261 if ((ptr = realloc(ptr, size)) == NULL) 262 errx(1, NOMEMORY); 263 return (ptr); 264 } 265 266 static char * 267 xstrdup(const char *str) 268 { 269 char *nstr; 270 271 if ((nstr = strdup(str)) == NULL) 272 errx(1, NOMEMORY); 273 return (nstr); 274 } 275 276 static char * 277 get_line(int fd) 278 { 279 static long curlen = BUFSIZ; 280 static char buf[BUFSIZ], *bptr = buf, *bend = buf; 281 char *cptr, *cend; 282 long buflen; 283 284 if (!curline) { 285 curline = xmalloc(curlen); 286 } 287 ++lineno; 288 289 cptr = curline; 290 cend = curline + curlen; 291 for (;;) { 292 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 293 if (*bptr == '\n') { 294 *cptr = '\0'; 295 ++bptr; 296 return (curline); 297 } else 298 *cptr = *bptr; 299 } 300 if (cptr == cend) { 301 cptr = curline = xrealloc(curline, curlen *= 2); 302 cend = curline + curlen; 303 } 304 if (bptr == bend) { 305 buflen = read(fd, buf, BUFSIZ); 306 if (buflen <= 0) { 307 if (cptr > curline) { 308 *cptr = '\0'; 309 return (curline); 310 } 311 return (NULL); 312 } 313 bend = buf + buflen; 314 bptr = buf; 315 } 316 } 317 } 318 319 static char * 320 wskip(char *cptr) 321 { 322 if (!*cptr || !isspace((unsigned char) *cptr)) { 323 warning(cptr, "expected a space"); 324 return (cptr); 325 } 326 while (*cptr && isspace((unsigned char) *cptr)) 327 ++cptr; 328 return (cptr); 329 } 330 331 static char * 332 cskip(char *cptr) 333 { 334 if (!*cptr || isspace((unsigned char) *cptr)) { 335 warning(cptr, "wasn't expecting a space"); 336 return (cptr); 337 } 338 while (*cptr && !isspace((unsigned char) *cptr)) 339 ++cptr; 340 return (cptr); 341 } 342 343 static char * 344 getmsg(int fd, char *cptr, char quote) 345 { 346 static char *msg = NULL; 347 static size_t msglen = 0; 348 size_t clen, i; 349 int in_quote = 0; 350 char *tptr; 351 352 if (quote && *cptr == quote) { 353 ++cptr; 354 in_quote = 1; 355 } 356 357 clen = strlen(cptr) + 1; 358 if (clen > msglen) { 359 if (msglen) 360 msg = xrealloc(msg, clen); 361 else 362 msg = xmalloc(clen); 363 msglen = clen; 364 } 365 tptr = msg; 366 367 while (*cptr) { 368 if (quote && *cptr == quote) { 369 char *tmp; 370 tmp = cptr + 1; 371 if (!in_quote) { 372 /* XXX hard error? */ 373 warning(cptr, "unexpected quote character, ignoring"); 374 *tptr++ = *cptr++; 375 } else { 376 cptr++; 377 /* don't use wskip() */ 378 while (*cptr && isspace((unsigned char) *cptr)) 379 #ifndef _BACKWARDS_COMPAT 380 cptr++; 381 #else 382 *tptr++ = *cptr++; 383 #endif 384 /* XXX hard error? */ 385 if (*cptr) 386 warning(tmp, "unexpected extra characters, ignoring"); 387 in_quote = 0; 388 #ifndef _BACKWARDS_COMPAT 389 break; 390 #endif 391 } 392 } else { 393 if (*cptr == '\\') { 394 ++cptr; 395 switch (*cptr) { 396 case '\0': 397 cptr = get_line(fd); 398 if (!cptr) 399 error("premature end of file"); 400 msglen += strlen(cptr); 401 i = tptr - msg; 402 msg = xrealloc(msg, msglen); 403 tptr = msg + i; 404 break; 405 case 'n': 406 *tptr++ = '\n'; 407 ++cptr; 408 break; 409 case 't': 410 *tptr++ = '\t'; 411 ++cptr; 412 break; 413 case 'v': 414 *tptr++ = '\v'; 415 ++cptr; 416 break; 417 case 'b': 418 *tptr++ = '\b'; 419 ++cptr; 420 break; 421 case 'r': 422 *tptr++ = '\r'; 423 ++cptr; 424 break; 425 case 'f': 426 *tptr++ = '\f'; 427 ++cptr; 428 break; 429 case '\\': 430 *tptr++ = '\\'; 431 ++cptr; 432 break; 433 default: 434 if (quote && *cptr == quote) { 435 *tptr++ = *cptr++; 436 } else if (isdigit((unsigned char) *cptr)) { 437 *tptr = 0; 438 for (i = 0; i < 3; ++i) { 439 if (!isdigit((unsigned char) *cptr)) 440 break; 441 if (*cptr > '7') 442 warning(cptr, "octal number greater than 7?!"); 443 *tptr *= 8; 444 *tptr += (*cptr - '0'); 445 ++cptr; 446 } 447 } else { 448 warning(cptr, "unrecognized escape sequence"); 449 } 450 break; 451 } 452 } else { 453 *tptr++ = *cptr++; 454 } 455 } 456 } 457 458 if (in_quote) 459 warning(cptr, "unterminated quoted message, ignoring"); 460 461 *tptr = '\0'; 462 return (msg); 463 } 464 465 static void 466 MCParse(int fd) 467 { 468 char *cptr, *str; 469 int msgid = 0; 470 int setid = 0; 471 char quote = 0; 472 473 /* XXX: init sethead? */ 474 475 while ((cptr = get_line(fd))) { 476 if (*cptr == '$') { 477 ++cptr; 478 if (strncmp(cptr, "set", 3) == 0) { 479 cptr += 3; 480 cptr = wskip(cptr); 481 setid = atoi(cptr); 482 MCAddSet(setid); 483 msgid = 0; 484 } else if (strncmp(cptr, "delset", 6) == 0) { 485 cptr += 6; 486 cptr = wskip(cptr); 487 setid = atoi(cptr); 488 MCDelSet(setid); 489 } else if (strncmp(cptr, "quote", 5) == 0) { 490 cptr += 5; 491 if (!*cptr) 492 quote = 0; 493 else { 494 cptr = wskip(cptr); 495 if (!*cptr) 496 quote = 0; 497 else 498 quote = *cptr; 499 } 500 } else if (isspace((unsigned char) *cptr)) { 501 ; 502 } else { 503 if (*cptr) { 504 cptr = wskip(cptr); 505 if (*cptr) 506 warning(cptr, "unrecognized line"); 507 } 508 } 509 } else { 510 /* 511 * First check for (and eat) empty lines.... 512 */ 513 if (!*cptr) 514 continue; 515 /* 516 * We have a digit? Start of a message. Else, 517 * syntax error. 518 */ 519 if (isdigit((unsigned char) *cptr)) { 520 msgid = atoi(cptr); 521 cptr = cskip(cptr); 522 if (*cptr) { 523 cptr = wskip(cptr); 524 if (!*cptr) { 525 MCAddMsg(msgid, ""); 526 continue; 527 } 528 } 529 } else { 530 warning(cptr, "neither blank line nor start of a message id"); 531 continue; 532 } 533 /* 534 * If no set directive specified, all messages 535 * shall be in default message set NL_SETD. 536 */ 537 if (setid == 0) { 538 setid = NL_SETD; 539 MCAddSet(setid); 540 } 541 /* 542 * If we have a message ID, but no message, 543 * then this means "delete this message id 544 * from the catalog". 545 */ 546 if (!*cptr) { 547 MCDelMsg(msgid); 548 } else { 549 str = getmsg(fd, cptr, quote); 550 MCAddMsg(msgid, str); 551 } 552 } 553 } 554 } 555 556 static void 557 MCReadCat(int fd) 558 { 559 void *msgcat; /* message catalog data */ 560 struct _nls_cat_hdr cat_hdr; 561 struct _nls_set_hdr *set_hdr; 562 struct _nls_msg_hdr *msg_hdr; 563 char *strings; 564 ssize_t n; 565 int m, s; 566 int msgno, setno; 567 568 /* XXX init sethead? */ 569 570 n = read(fd, &cat_hdr, sizeof(cat_hdr)); 571 if (n < (ssize_t)sizeof(cat_hdr)) { 572 if (n == 0) 573 return; /* empty file */ 574 else if (n == -1) 575 err(1, "header read"); 576 else 577 errx(1, CORRUPT); 578 } 579 if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC) 580 errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic); 581 582 cat_hdr.__mem = ntohl(cat_hdr.__mem); 583 584 cat_hdr.__nsets = ntohl(cat_hdr.__nsets); 585 cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset); 586 cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset); 587 if ((cat_hdr.__mem < 0) || 588 (cat_hdr.__msg_hdr_offset < 0) || 589 (cat_hdr.__msg_txt_offset < 0) || 590 (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) || 591 (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) || 592 (cat_hdr.__mem < cat_hdr.__msg_txt_offset)) 593 errx(1, "%s: catalog header", CORRUPT); 594 595 msgcat = xmalloc(cat_hdr.__mem); 596 597 n = read(fd, msgcat, cat_hdr.__mem); 598 if (n < cat_hdr.__mem) { 599 if (n == -1) 600 err(1, "data read"); 601 else 602 errx(1, CORRUPT); 603 } 604 605 set_hdr = (struct _nls_set_hdr *)msgcat; 606 msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat + 607 cat_hdr.__msg_hdr_offset); 608 strings = (char *)msgcat + cat_hdr.__msg_txt_offset; 609 610 setno = 0; 611 for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) { 612 set_hdr->__setno = ntohl(set_hdr->__setno); 613 if (set_hdr->__setno < setno) 614 errx(1, "%s: bad set number (%d)", 615 CORRUPT, set_hdr->__setno); 616 setno = set_hdr->__setno; 617 618 MCAddSet(setno); 619 620 set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs); 621 set_hdr->__index = ntohl(set_hdr->__index); 622 if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0) 623 errx(1, "%s: set header", CORRUPT); 624 625 /* Get the data */ 626 msgno = 0; 627 for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) { 628 msg_hdr->__msgno = ntohl(msg_hdr->__msgno); 629 msg_hdr->__offset = ntohl(msg_hdr->__offset); 630 if (msg_hdr->__msgno < msgno) 631 errx(1, "%s: bad message number (%d)", 632 CORRUPT, msg_hdr->__msgno); 633 if ((msg_hdr->__offset < 0) || 634 ((strings + msg_hdr->__offset) > 635 ((char *)msgcat + cat_hdr.__mem))) 636 errx(1, "%s: message header", CORRUPT); 637 638 msgno = msg_hdr->__msgno; 639 MCAddMsg(msgno, strings + msg_hdr->__offset); 640 } 641 } 642 free(msgcat); 643 } 644 645 /* 646 * Write message catalog. 647 * 648 * The message catalog is first converted from its internal to its 649 * external representation in a chunk of memory allocated for this 650 * purpose. Then the completed catalog is written. This approach 651 * avoids additional housekeeping variables and/or a lot of seeks 652 * that would otherwise be required. 653 */ 654 static void 655 MCWriteCat(int fd) 656 { 657 int nsets; /* number of sets */ 658 int nmsgs; /* number of msgs */ 659 int string_size; /* total size of string pool */ 660 int msgcat_size; /* total size of message catalog */ 661 void *msgcat; /* message catalog data */ 662 struct _nls_cat_hdr *cat_hdr; 663 struct _nls_set_hdr *set_hdr; 664 struct _nls_msg_hdr *msg_hdr; 665 char *strings; 666 struct _setT *set; 667 struct _msgT *msg; 668 int msg_index; 669 int msg_offset; 670 671 /* determine number of sets, number of messages, and size of the 672 * string pool */ 673 nsets = 0; 674 nmsgs = 0; 675 string_size = 0; 676 677 for (set = sethead.lh_first; set != NULL; 678 set = set->entries.le_next) { 679 nsets++; 680 681 for (msg = set->msghead.lh_first; msg != NULL; 682 msg = msg->entries.le_next) { 683 nmsgs++; 684 string_size += strlen(msg->str) + 1; 685 } 686 } 687 688 #ifdef DEBUG 689 printf("number of sets: %d\n", nsets); 690 printf("number of msgs: %d\n", nmsgs); 691 printf("string pool size: %d\n", string_size); 692 #endif 693 694 /* determine size and then allocate buffer for constructing external 695 * message catalog representation */ 696 msgcat_size = sizeof(struct _nls_cat_hdr) 697 + (nsets * sizeof(struct _nls_set_hdr)) 698 + (nmsgs * sizeof(struct _nls_msg_hdr)) 699 + string_size; 700 701 msgcat = xmalloc(msgcat_size); 702 memset(msgcat, '\0', msgcat_size); 703 704 /* fill in msg catalog header */ 705 cat_hdr = (struct _nls_cat_hdr *) msgcat; 706 cat_hdr->__magic = htonl(_NLS_MAGIC); 707 cat_hdr->__nsets = htonl(nsets); 708 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 709 cat_hdr->__msg_hdr_offset = 710 htonl(nsets * sizeof(struct _nls_set_hdr)); 711 cat_hdr->__msg_txt_offset = 712 htonl(nsets * sizeof(struct _nls_set_hdr) + 713 nmsgs * sizeof(struct _nls_msg_hdr)); 714 715 /* compute offsets for set & msg header tables and string pool */ 716 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat + 717 sizeof(struct _nls_cat_hdr)); 718 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat + 719 sizeof(struct _nls_cat_hdr) + 720 nsets * sizeof(struct _nls_set_hdr)); 721 strings = (char *) msgcat + 722 sizeof(struct _nls_cat_hdr) + 723 nsets * sizeof(struct _nls_set_hdr) + 724 nmsgs * sizeof(struct _nls_msg_hdr); 725 726 msg_index = 0; 727 msg_offset = 0; 728 for (set = sethead.lh_first; set != NULL; 729 set = set->entries.le_next) { 730 731 nmsgs = 0; 732 for (msg = set->msghead.lh_first; msg != NULL; 733 msg = msg->entries.le_next) { 734 int32_t msg_len = strlen(msg->str) + 1; 735 736 msg_hdr->__msgno = htonl(msg->msgId); 737 msg_hdr->__msglen = htonl(msg_len); 738 msg_hdr->__offset = htonl(msg_offset); 739 740 memcpy(strings, msg->str, msg_len); 741 strings += msg_len; 742 msg_offset += msg_len; 743 744 nmsgs++; 745 msg_hdr++; 746 } 747 748 set_hdr->__setno = htonl(set->setId); 749 set_hdr->__nmsgs = htonl(nmsgs); 750 set_hdr->__index = htonl(msg_index); 751 msg_index += nmsgs; 752 set_hdr++; 753 } 754 755 /* write out catalog. XXX: should this be done in small chunks? */ 756 write(fd, msgcat, msgcat_size); 757 } 758 759 static void 760 MCAddSet(int setId) 761 { 762 struct _setT *p, *q; 763 764 if (setId <= 0) { 765 error("setId's must be greater than zero"); 766 /* NOTREACHED */ 767 } 768 if (setId > NL_SETMAX) { 769 error("setId exceeds limit"); 770 /* NOTREACHED */ 771 } 772 773 p = sethead.lh_first; 774 q = NULL; 775 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next); 776 777 if (p && p->setId == setId) { 778 ; 779 } else { 780 p = xmalloc(sizeof(struct _setT)); 781 memset(p, '\0', sizeof(struct _setT)); 782 LIST_INIT(&p->msghead); 783 784 p->setId = setId; 785 786 if (q == NULL) { 787 LIST_INSERT_HEAD(&sethead, p, entries); 788 } else { 789 LIST_INSERT_AFTER(q, p, entries); 790 } 791 } 792 793 curSet = p; 794 } 795 796 static void 797 MCAddMsg(int msgId, const char *str) 798 { 799 struct _msgT *p, *q; 800 801 if (!curSet) 802 error("can't specify a message when no set exists"); 803 804 if (msgId <= 0) { 805 error("msgId's must be greater than zero"); 806 /* NOTREACHED */ 807 } 808 if (msgId > NL_MSGMAX) { 809 error("msgID exceeds limit"); 810 /* NOTREACHED */ 811 } 812 813 p = curSet->msghead.lh_first; 814 q = NULL; 815 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next); 816 817 if (p && p->msgId == msgId) { 818 free(p->str); 819 } else { 820 p = xmalloc(sizeof(struct _msgT)); 821 memset(p, '\0', sizeof(struct _msgT)); 822 823 if (q == NULL) { 824 LIST_INSERT_HEAD(&curSet->msghead, p, entries); 825 } else { 826 LIST_INSERT_AFTER(q, p, entries); 827 } 828 } 829 830 p->msgId = msgId; 831 p->str = xstrdup(str); 832 } 833 834 static void 835 MCDelSet(int setId) 836 { 837 struct _setT *set; 838 struct _msgT *msg; 839 840 if (setId <= 0) { 841 error("setId's must be greater than zero"); 842 /* NOTREACHED */ 843 } 844 if (setId > NL_SETMAX) { 845 error("setId exceeds limit"); 846 /* NOTREACHED */ 847 } 848 849 set = sethead.lh_first; 850 for (; set != NULL && set->setId < setId; set = set->entries.le_next); 851 852 if (set && set->setId == setId) { 853 LIST_REMOVE(set, entries); 854 while ((msg = set->msghead.lh_first) != NULL) { 855 LIST_REMOVE(msg, entries); 856 free(msg->str); 857 free(msg); 858 } 859 free(set); 860 return; 861 } 862 warning(NULL, "specified set doesn't exist"); 863 } 864 865 static void 866 MCDelMsg(int msgId) 867 { 868 struct _msgT *msg; 869 870 if (!curSet) 871 error("you can't delete a message before defining the set"); 872 873 msg = curSet->msghead.lh_first; 874 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next); 875 876 if (msg && msg->msgId == msgId) { 877 LIST_REMOVE(msg, entries); 878 free(msg->str); 879 free(msg); 880 return; 881 } 882 warning(NULL, "specified msg doesn't exist"); 883 } 884