1 /* $NetBSD: gencat.c,v 1.32 2011/09/21 14:33:35 christos 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.32 2011/09/21 14:33:35 christos 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 < 2) { 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 (((*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 char *tptr; 350 351 if (quote && *cptr == quote) { 352 ++cptr; 353 } 354 355 clen = strlen(cptr) + 1; 356 if (clen > msglen) { 357 if (msglen) 358 msg = xrealloc(msg, clen); 359 else 360 msg = xmalloc(clen); 361 msglen = clen; 362 } 363 tptr = msg; 364 365 while (*cptr) { 366 if (quote && *cptr == quote) { 367 char *tmp; 368 tmp = cptr + 1; 369 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) { 370 warning(cptr, "unexpected quote character, ignoring"); 371 *tptr++ = *cptr++; 372 } else { 373 *cptr = '\0'; 374 } 375 } else { 376 if (*cptr == '\\') { 377 ++cptr; 378 switch (*cptr) { 379 case '\0': 380 cptr = get_line(fd); 381 if (!cptr) 382 error("premature end of file"); 383 msglen += strlen(cptr); 384 i = tptr - msg; 385 msg = xrealloc(msg, msglen); 386 tptr = msg + i; 387 break; 388 case 'n': 389 *tptr++ = '\n'; 390 ++cptr; 391 break; 392 case 't': 393 *tptr++ = '\t'; 394 ++cptr; 395 break; 396 case 'v': 397 *tptr++ = '\v'; 398 ++cptr; 399 break; 400 case 'b': 401 *tptr++ = '\b'; 402 ++cptr; 403 break; 404 case 'r': 405 *tptr++ = '\r'; 406 ++cptr; 407 break; 408 case 'f': 409 *tptr++ = '\f'; 410 ++cptr; 411 break; 412 case '\\': 413 *tptr++ = '\\'; 414 ++cptr; 415 break; 416 default: 417 if (quote && *cptr == quote) { 418 *tptr++ = *cptr++; 419 } else if (isdigit((unsigned char) *cptr)) { 420 *tptr = 0; 421 for (i = 0; i < 3; ++i) { 422 if (!isdigit((unsigned char) *cptr)) 423 break; 424 if (*cptr > '7') 425 warning(cptr, "octal number greater than 7?!"); 426 *tptr *= 8; 427 *tptr += (*cptr - '0'); 428 ++cptr; 429 } 430 } else { 431 warning(cptr, "unrecognized escape sequence"); 432 } 433 break; 434 } 435 } else { 436 *tptr++ = *cptr++; 437 } 438 } 439 } 440 *tptr = '\0'; 441 return (msg); 442 } 443 444 static void 445 MCParse(int fd) 446 { 447 char *cptr, *str; 448 int msgid = 0; 449 int setid = 0; 450 char quote = 0; 451 452 /* XXX: init sethead? */ 453 454 while ((cptr = get_line(fd))) { 455 if (*cptr == '$') { 456 ++cptr; 457 if (strncmp(cptr, "set", 3) == 0) { 458 cptr += 3; 459 cptr = wskip(cptr); 460 setid = atoi(cptr); 461 MCAddSet(setid); 462 msgid = 0; 463 } else if (strncmp(cptr, "delset", 6) == 0) { 464 cptr += 6; 465 cptr = wskip(cptr); 466 setid = atoi(cptr); 467 MCDelSet(setid); 468 } else if (strncmp(cptr, "quote", 5) == 0) { 469 cptr += 5; 470 if (!*cptr) 471 quote = 0; 472 else { 473 cptr = wskip(cptr); 474 if (!*cptr) 475 quote = 0; 476 else 477 quote = *cptr; 478 } 479 } else if (isspace((unsigned char) *cptr)) { 480 ; 481 } else { 482 if (*cptr) { 483 cptr = wskip(cptr); 484 if (*cptr) 485 warning(cptr, "unrecognized line"); 486 } 487 } 488 } else { 489 /* 490 * First check for (and eat) empty lines.... 491 */ 492 if (!*cptr) 493 continue; 494 /* 495 * We have a digit? Start of a message. Else, 496 * syntax error. 497 */ 498 if (isdigit((unsigned char) *cptr)) { 499 msgid = atoi(cptr); 500 cptr = cskip(cptr); 501 if (*cptr) { 502 cptr = wskip(cptr); 503 if (!*cptr) { 504 MCAddMsg(msgid, ""); 505 continue; 506 } 507 } 508 } else { 509 warning(cptr, "neither blank line nor start of a message id"); 510 continue; 511 } 512 /* 513 * If no set directive specified, all messages 514 * shall be in default message set NL_SETD. 515 */ 516 if (setid == 0) { 517 setid = NL_SETD; 518 MCAddSet(setid); 519 } 520 /* 521 * If we have a message ID, but no message, 522 * then this means "delete this message id 523 * from the catalog". 524 */ 525 if (!*cptr) { 526 MCDelMsg(msgid); 527 } else { 528 str = getmsg(fd, cptr, quote); 529 MCAddMsg(msgid, str); 530 } 531 } 532 } 533 } 534 535 static void 536 MCReadCat(int fd) 537 { 538 void *msgcat; /* message catalog data */ 539 struct _nls_cat_hdr cat_hdr; 540 struct _nls_set_hdr *set_hdr; 541 struct _nls_msg_hdr *msg_hdr; 542 char *strings; 543 ssize_t n; 544 int m, s; 545 int msgno, setno; 546 547 /* XXX init sethead? */ 548 549 n = read(fd, &cat_hdr, sizeof(cat_hdr)); 550 if (n < (ssize_t)sizeof(cat_hdr)) { 551 if (n == 0) 552 return; /* empty file */ 553 else if (n == -1) 554 err(1, "header read"); 555 else 556 errx(1, CORRUPT); 557 } 558 if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC) 559 errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic); 560 561 cat_hdr.__mem = ntohl(cat_hdr.__mem); 562 563 cat_hdr.__nsets = ntohl(cat_hdr.__nsets); 564 cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset); 565 cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset); 566 if ((cat_hdr.__mem < 0) || 567 (cat_hdr.__msg_hdr_offset < 0) || 568 (cat_hdr.__msg_txt_offset < 0) || 569 (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) || 570 (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) || 571 (cat_hdr.__mem < cat_hdr.__msg_txt_offset)) 572 errx(1, "%s: catalog header", CORRUPT); 573 574 msgcat = xmalloc(cat_hdr.__mem); 575 576 n = read(fd, msgcat, cat_hdr.__mem); 577 if (n < cat_hdr.__mem) { 578 if (n == -1) 579 err(1, "data read"); 580 else 581 errx(1, CORRUPT); 582 } 583 584 set_hdr = (struct _nls_set_hdr *)msgcat; 585 msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat + 586 cat_hdr.__msg_hdr_offset); 587 strings = (char *)msgcat + cat_hdr.__msg_txt_offset; 588 589 setno = 0; 590 for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) { 591 set_hdr->__setno = ntohl(set_hdr->__setno); 592 if (set_hdr->__setno < setno) 593 errx(1, "%s: bad set number (%d)", 594 CORRUPT, set_hdr->__setno); 595 setno = set_hdr->__setno; 596 597 MCAddSet(setno); 598 599 set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs); 600 set_hdr->__index = ntohl(set_hdr->__index); 601 if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0) 602 errx(1, "%s: set header", CORRUPT); 603 604 /* Get the data */ 605 msgno = 0; 606 for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) { 607 msg_hdr->__msgno = ntohl(msg_hdr->__msgno); 608 msg_hdr->__offset = ntohl(msg_hdr->__offset); 609 if (msg_hdr->__msgno < msgno) 610 errx(1, "%s: bad message number (%d)", 611 CORRUPT, msg_hdr->__msgno); 612 if ((msg_hdr->__offset < 0) || 613 ((strings + msg_hdr->__offset) > 614 ((char *)msgcat + cat_hdr.__mem))) 615 errx(1, "%s: message header", CORRUPT); 616 617 msgno = msg_hdr->__msgno; 618 MCAddMsg(msgno, strings + msg_hdr->__offset); 619 } 620 } 621 free(msgcat); 622 } 623 624 /* 625 * Write message catalog. 626 * 627 * The message catalog is first converted from its internal to its 628 * external representation in a chunk of memory allocated for this 629 * purpose. Then the completed catalog is written. This approach 630 * avoids additional housekeeping variables and/or a lot of seeks 631 * that would otherwise be required. 632 */ 633 static void 634 MCWriteCat(int fd) 635 { 636 int nsets; /* number of sets */ 637 int nmsgs; /* number of msgs */ 638 int string_size; /* total size of string pool */ 639 int msgcat_size; /* total size of message catalog */ 640 void *msgcat; /* message catalog data */ 641 struct _nls_cat_hdr *cat_hdr; 642 struct _nls_set_hdr *set_hdr; 643 struct _nls_msg_hdr *msg_hdr; 644 char *strings; 645 struct _setT *set; 646 struct _msgT *msg; 647 int msg_index; 648 int msg_offset; 649 650 /* determine number of sets, number of messages, and size of the 651 * string pool */ 652 nsets = 0; 653 nmsgs = 0; 654 string_size = 0; 655 656 for (set = sethead.lh_first; set != NULL; 657 set = set->entries.le_next) { 658 nsets++; 659 660 for (msg = set->msghead.lh_first; msg != NULL; 661 msg = msg->entries.le_next) { 662 nmsgs++; 663 string_size += strlen(msg->str) + 1; 664 } 665 } 666 667 #ifdef DEBUG 668 printf("number of sets: %d\n", nsets); 669 printf("number of msgs: %d\n", nmsgs); 670 printf("string pool size: %d\n", string_size); 671 #endif 672 673 /* determine size and then allocate buffer for constructing external 674 * message catalog representation */ 675 msgcat_size = sizeof(struct _nls_cat_hdr) 676 + (nsets * sizeof(struct _nls_set_hdr)) 677 + (nmsgs * sizeof(struct _nls_msg_hdr)) 678 + string_size; 679 680 msgcat = xmalloc(msgcat_size); 681 memset(msgcat, '\0', msgcat_size); 682 683 /* fill in msg catalog header */ 684 cat_hdr = (struct _nls_cat_hdr *) msgcat; 685 cat_hdr->__magic = htonl(_NLS_MAGIC); 686 cat_hdr->__nsets = htonl(nsets); 687 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 688 cat_hdr->__msg_hdr_offset = 689 htonl(nsets * sizeof(struct _nls_set_hdr)); 690 cat_hdr->__msg_txt_offset = 691 htonl(nsets * sizeof(struct _nls_set_hdr) + 692 nmsgs * sizeof(struct _nls_msg_hdr)); 693 694 /* compute offsets for set & msg header tables and string pool */ 695 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat + 696 sizeof(struct _nls_cat_hdr)); 697 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat + 698 sizeof(struct _nls_cat_hdr) + 699 nsets * sizeof(struct _nls_set_hdr)); 700 strings = (char *) msgcat + 701 sizeof(struct _nls_cat_hdr) + 702 nsets * sizeof(struct _nls_set_hdr) + 703 nmsgs * sizeof(struct _nls_msg_hdr); 704 705 msg_index = 0; 706 msg_offset = 0; 707 for (set = sethead.lh_first; set != NULL; 708 set = set->entries.le_next) { 709 710 nmsgs = 0; 711 for (msg = set->msghead.lh_first; msg != NULL; 712 msg = msg->entries.le_next) { 713 int32_t msg_len = strlen(msg->str) + 1; 714 715 msg_hdr->__msgno = htonl(msg->msgId); 716 msg_hdr->__msglen = htonl(msg_len); 717 msg_hdr->__offset = htonl(msg_offset); 718 719 memcpy(strings, msg->str, msg_len); 720 strings += msg_len; 721 msg_offset += msg_len; 722 723 nmsgs++; 724 msg_hdr++; 725 } 726 727 set_hdr->__setno = htonl(set->setId); 728 set_hdr->__nmsgs = htonl(nmsgs); 729 set_hdr->__index = htonl(msg_index); 730 msg_index += nmsgs; 731 set_hdr++; 732 } 733 734 /* write out catalog. XXX: should this be done in small chunks? */ 735 write(fd, msgcat, msgcat_size); 736 } 737 738 static void 739 MCAddSet(int setId) 740 { 741 struct _setT *p, *q; 742 743 if (setId <= 0) { 744 error("setId's must be greater than zero"); 745 /* NOTREACHED */ 746 } 747 if (setId > NL_SETMAX) { 748 error("setId exceeds limit"); 749 /* NOTREACHED */ 750 } 751 752 p = sethead.lh_first; 753 q = NULL; 754 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next); 755 756 if (p && p->setId == setId) { 757 ; 758 } else { 759 p = xmalloc(sizeof(struct _setT)); 760 memset(p, '\0', sizeof(struct _setT)); 761 LIST_INIT(&p->msghead); 762 763 p->setId = setId; 764 765 if (q == NULL) { 766 LIST_INSERT_HEAD(&sethead, p, entries); 767 } else { 768 LIST_INSERT_AFTER(q, p, entries); 769 } 770 } 771 772 curSet = p; 773 } 774 775 static void 776 MCAddMsg(int msgId, const char *str) 777 { 778 struct _msgT *p, *q; 779 780 if (!curSet) 781 error("can't specify a message when no set exists"); 782 783 if (msgId <= 0) { 784 error("msgId's must be greater than zero"); 785 /* NOTREACHED */ 786 } 787 if (msgId > NL_MSGMAX) { 788 error("msgID exceeds limit"); 789 /* NOTREACHED */ 790 } 791 792 p = curSet->msghead.lh_first; 793 q = NULL; 794 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next); 795 796 if (p && p->msgId == msgId) { 797 free(p->str); 798 } else { 799 p = xmalloc(sizeof(struct _msgT)); 800 memset(p, '\0', sizeof(struct _msgT)); 801 802 if (q == NULL) { 803 LIST_INSERT_HEAD(&curSet->msghead, p, entries); 804 } else { 805 LIST_INSERT_AFTER(q, p, entries); 806 } 807 } 808 809 p->msgId = msgId; 810 p->str = xstrdup(str); 811 } 812 813 static void 814 MCDelSet(int setId) 815 { 816 struct _setT *set; 817 struct _msgT *msg; 818 819 if (setId <= 0) { 820 error("setId's must be greater than zero"); 821 /* NOTREACHED */ 822 } 823 if (setId > NL_SETMAX) { 824 error("setId exceeds limit"); 825 /* NOTREACHED */ 826 } 827 828 set = sethead.lh_first; 829 for (; set != NULL && set->setId < setId; set = set->entries.le_next); 830 831 if (set && set->setId == setId) { 832 LIST_REMOVE(set, entries); 833 while ((msg = set->msghead.lh_first) != NULL) { 834 LIST_REMOVE(msg, entries); 835 free(msg->str); 836 free(msg); 837 } 838 free(set); 839 return; 840 } 841 warning(NULL, "specified set doesn't exist"); 842 } 843 844 static void 845 MCDelMsg(int msgId) 846 { 847 struct _msgT *msg; 848 849 if (!curSet) 850 error("you can't delete a message before defining the set"); 851 852 msg = curSet->msghead.lh_first; 853 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next); 854 855 if (msg && msg->msgId == msgId) { 856 LIST_REMOVE(msg, entries); 857 free(msg->str); 858 free(msg); 859 return; 860 } 861 warning(NULL, "specified msg doesn't exist"); 862 } 863