1 /* $NetBSD: gencat.c,v 1.30 2009/07/13 19:05:41 roy 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.30 2009/07/13 19:05:41 roy 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 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 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 void MCParse(int fd); 129 void MCReadCat(int fd); 130 void MCWriteCat(int fd); 131 void MCDelMsg(int msgId); 132 void MCAddMsg(int msgId, const char *msg); 133 void MCAddSet(int setId); 134 void MCDelSet(int setId); 135 int main(int, char **); 136 void usage(void); 137 138 #define CORRUPT "corrupt message catalog" 139 #define NOMEMORY "out of memory" 140 141 void 142 usage(void) 143 { 144 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname()); 145 exit(1); 146 } 147 148 int 149 main(int argc, char *argv[]) 150 { 151 int ofd, ifd; 152 char *catfile = NULL; 153 int c; 154 int updatecat = 0; 155 156 while ((c = getopt(argc, argv, "")) != -1) { 157 switch (c) { 158 case '?': 159 default: 160 usage(); 161 /* NOTREACHED */ 162 } 163 } 164 argc -= optind; 165 argv += optind; 166 167 if (argc < 2) { 168 usage(); 169 /* NOTREACHED */ 170 } 171 catfile = *argv++; 172 173 if ((catfile[0] == '-') && (catfile[1] == '\0')) { 174 ofd = STDOUT_FILENO; 175 } else { 176 ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666); 177 if (ofd < 0) { 178 if (errno == EEXIST) { 179 if ((ofd = open(catfile, O_RDWR)) < 0) { 180 err(1, "Unable to open %s", catfile); 181 /* NOTREACHED */ 182 } 183 } else { 184 err(1, "Unable to create new %s", catfile); 185 /* NOTREACHED */ 186 } 187 curfile = catfile; 188 updatecat = 1; 189 MCReadCat(ofd); 190 if (lseek(ofd, SEEK_SET, 0) < 0) { 191 err(1, "Unable to seek on %s", catfile); 192 /* NOTREACHED */ 193 } 194 } 195 } 196 197 if (((*argv)[0] == '-') && ((*argv)[1] == '\0')) { 198 if (argc != 2) 199 usage(); 200 /* NOTREACHED */ 201 MCParse(STDIN_FILENO); 202 } else { 203 for (; *argv; argv++) { 204 if ((ifd = open(*argv, O_RDONLY)) < 0) 205 err(1, "Unable to read %s", *argv); 206 curfile = *argv; 207 lineno = 0; 208 MCParse(ifd); 209 close(ifd); 210 } 211 } 212 213 if (updatecat) { 214 if (ftruncate(ofd, 0) != 0) { 215 err(1, "Unable to truncate %s", catfile); 216 /* NOTREACHED */ 217 } 218 } 219 220 MCWriteCat(ofd); 221 exit(0); 222 } 223 224 static void 225 warning(const char *cptr, const char *msg) 226 { 227 if (lineno) { 228 fprintf(stderr, "%s: %s on line %ld, %s\n", 229 getprogname(), msg, lineno, curfile); 230 fprintf(stderr, "%s\n", curline); 231 if (cptr) { 232 char *tptr; 233 for (tptr = curline; tptr < cptr; ++tptr) 234 putc(' ', stderr); 235 fprintf(stderr, "^\n"); 236 } 237 } else { 238 fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile); 239 } 240 } 241 242 static void 243 error(const char *msg) 244 { 245 warning(NULL, msg); 246 exit(1); 247 } 248 249 static void * 250 xmalloc(size_t len) 251 { 252 void *p; 253 254 if ((p = malloc(len)) == NULL) 255 errx(1, NOMEMORY); 256 return (p); 257 } 258 259 static void * 260 xrealloc(void *ptr, size_t size) 261 { 262 if ((ptr = realloc(ptr, size)) == NULL) 263 errx(1, NOMEMORY); 264 return (ptr); 265 } 266 267 static char * 268 xstrdup(const char *str) 269 { 270 char *nstr; 271 272 if ((nstr = strdup(str)) == NULL) 273 errx(1, NOMEMORY); 274 return (nstr); 275 } 276 277 static char * 278 get_line(int fd) 279 { 280 static long curlen = BUFSIZ; 281 static char buf[BUFSIZ], *bptr = buf, *bend = buf; 282 char *cptr, *cend; 283 long buflen; 284 285 if (!curline) { 286 curline = xmalloc(curlen); 287 } 288 ++lineno; 289 290 cptr = curline; 291 cend = curline + curlen; 292 for (;;) { 293 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 294 if (*bptr == '\n') { 295 *cptr = '\0'; 296 ++bptr; 297 return (curline); 298 } else 299 *cptr = *bptr; 300 } 301 if (cptr == cend) { 302 cptr = curline = xrealloc(curline, curlen *= 2); 303 cend = curline + curlen; 304 } 305 if (bptr == bend) { 306 buflen = read(fd, buf, BUFSIZ); 307 if (buflen <= 0) { 308 if (cptr > curline) { 309 *cptr = '\0'; 310 return (curline); 311 } 312 return (NULL); 313 } 314 bend = buf + buflen; 315 bptr = buf; 316 } 317 } 318 } 319 320 static char * 321 wskip(char *cptr) 322 { 323 if (!*cptr || !isspace((unsigned char) *cptr)) { 324 warning(cptr, "expected a space"); 325 return (cptr); 326 } 327 while (*cptr && isspace((unsigned char) *cptr)) 328 ++cptr; 329 return (cptr); 330 } 331 332 static char * 333 cskip(char *cptr) 334 { 335 if (!*cptr || isspace((unsigned char) *cptr)) { 336 warning(cptr, "wasn't expecting a space"); 337 return (cptr); 338 } 339 while (*cptr && !isspace((unsigned char) *cptr)) 340 ++cptr; 341 return (cptr); 342 } 343 344 static char * 345 getmsg(int fd, char *cptr, char quote) 346 { 347 static char *msg = NULL; 348 static size_t msglen = 0; 349 size_t clen, i; 350 char *tptr; 351 352 if (quote && *cptr == quote) { 353 ++cptr; 354 } 355 356 clen = strlen(cptr) + 1; 357 if (clen > msglen) { 358 if (msglen) 359 msg = xrealloc(msg, clen); 360 else 361 msg = xmalloc(clen); 362 msglen = clen; 363 } 364 tptr = msg; 365 366 while (*cptr) { 367 if (quote && *cptr == quote) { 368 char *tmp; 369 tmp = cptr + 1; 370 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) { 371 warning(cptr, "unexpected quote character, ignoring"); 372 *tptr++ = *cptr++; 373 } else { 374 *cptr = '\0'; 375 } 376 } else { 377 if (*cptr == '\\') { 378 ++cptr; 379 switch (*cptr) { 380 case '\0': 381 cptr = get_line(fd); 382 if (!cptr) 383 error("premature end of file"); 384 msglen += strlen(cptr); 385 i = tptr - msg; 386 msg = xrealloc(msg, msglen); 387 tptr = msg + i; 388 break; 389 case 'n': 390 *tptr++ = '\n'; 391 ++cptr; 392 break; 393 case 't': 394 *tptr++ = '\t'; 395 ++cptr; 396 break; 397 case 'v': 398 *tptr++ = '\v'; 399 ++cptr; 400 break; 401 case 'b': 402 *tptr++ = '\b'; 403 ++cptr; 404 break; 405 case 'r': 406 *tptr++ = '\r'; 407 ++cptr; 408 break; 409 case 'f': 410 *tptr++ = '\f'; 411 ++cptr; 412 break; 413 case '\\': 414 *tptr++ = '\\'; 415 ++cptr; 416 break; 417 default: 418 if (quote && *cptr == quote) { 419 *tptr++ = *cptr++; 420 } else if (isdigit((unsigned char) *cptr)) { 421 *tptr = 0; 422 for (i = 0; i < 3; ++i) { 423 if (!isdigit((unsigned char) *cptr)) 424 break; 425 if (*cptr > '7') 426 warning(cptr, "octal number greater than 7?!"); 427 *tptr *= 8; 428 *tptr += (*cptr - '0'); 429 ++cptr; 430 } 431 } else { 432 warning(cptr, "unrecognized escape sequence"); 433 } 434 break; 435 } 436 } else { 437 *tptr++ = *cptr++; 438 } 439 } 440 } 441 *tptr = '\0'; 442 return (msg); 443 } 444 445 void 446 MCParse(int fd) 447 { 448 char *cptr, *str; 449 int msgid = 0; 450 int setid = 0; 451 char quote = 0; 452 453 /* XXX: init sethead? */ 454 455 while ((cptr = get_line(fd))) { 456 if (*cptr == '$') { 457 ++cptr; 458 if (strncmp(cptr, "set", 3) == 0) { 459 cptr += 3; 460 cptr = wskip(cptr); 461 setid = atoi(cptr); 462 MCAddSet(setid); 463 msgid = 0; 464 } else if (strncmp(cptr, "delset", 6) == 0) { 465 cptr += 6; 466 cptr = wskip(cptr); 467 setid = atoi(cptr); 468 MCDelSet(setid); 469 } else if (strncmp(cptr, "quote", 5) == 0) { 470 cptr += 5; 471 if (!*cptr) 472 quote = 0; 473 else { 474 cptr = wskip(cptr); 475 if (!*cptr) 476 quote = 0; 477 else 478 quote = *cptr; 479 } 480 } else if (isspace((unsigned char) *cptr)) { 481 ; 482 } else { 483 if (*cptr) { 484 cptr = wskip(cptr); 485 if (*cptr) 486 warning(cptr, "unrecognized line"); 487 } 488 } 489 } else { 490 /* 491 * First check for (and eat) empty lines.... 492 */ 493 if (!*cptr) 494 continue; 495 /* 496 * We have a digit? Start of a message. Else, 497 * syntax error. 498 */ 499 if (isdigit((unsigned char) *cptr)) { 500 msgid = atoi(cptr); 501 cptr = cskip(cptr); 502 if (*cptr) { 503 cptr = wskip(cptr); 504 if (!*cptr) { 505 MCAddMsg(msgid, ""); 506 continue; 507 } 508 } 509 } else { 510 warning(cptr, "neither blank line nor start of a message id"); 511 continue; 512 } 513 /* 514 * If no set directive specified, all messages 515 * shall be in default message set NL_SETD. 516 */ 517 if (setid == 0) { 518 setid = NL_SETD; 519 MCAddSet(setid); 520 } 521 /* 522 * If we have a message ID, but no message, 523 * then this means "delete this message id 524 * from the catalog". 525 */ 526 if (!*cptr) { 527 MCDelMsg(msgid); 528 } else { 529 str = getmsg(fd, cptr, quote); 530 MCAddMsg(msgid, str); 531 } 532 } 533 } 534 } 535 536 void 537 MCReadCat(int fd) 538 { 539 void *msgcat; /* message catalog data */ 540 struct _nls_cat_hdr cat_hdr; 541 struct _nls_set_hdr *set_hdr; 542 struct _nls_msg_hdr *msg_hdr; 543 char *strings; 544 ssize_t n; 545 int m, s; 546 int msgno, setno; 547 548 /* XXX init sethead? */ 549 550 n = read(fd, &cat_hdr, sizeof(cat_hdr)); 551 if (n < (ssize_t)sizeof(cat_hdr)) { 552 if (n == 0) 553 return; /* empty file */ 554 else if (n == -1) 555 err(1, "header read"); 556 else 557 errx(1, CORRUPT); 558 } 559 if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC) 560 errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic); 561 562 cat_hdr.__mem = ntohl(cat_hdr.__mem); 563 564 cat_hdr.__nsets = ntohl(cat_hdr.__nsets); 565 cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset); 566 cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset); 567 if ((cat_hdr.__mem < 0) || 568 (cat_hdr.__msg_hdr_offset < 0) || 569 (cat_hdr.__msg_txt_offset < 0) || 570 (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) || 571 (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) || 572 (cat_hdr.__mem < cat_hdr.__msg_txt_offset)) 573 errx(1, "%s: catalog header", CORRUPT); 574 575 msgcat = xmalloc(cat_hdr.__mem); 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 int32_t 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