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