1 /* $NetBSD: gencat.c,v 1.17 2002/04/24 22:44:40 bjh21 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.17 2002/04/24 22:44:40 bjh21 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_CONFIG_H 77 #include "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 <fcntl.h> 91 #include <limits.h> 92 #include <nl_types.h> 93 #include <stdio.h> 94 #include <stdlib.h> 95 #include <string.h> 96 #include <unistd.h> 97 98 #ifndef NL_SETMAX 99 #define NL_SETMAX 255 100 #endif 101 #ifndef NL_MSGMAX 102 #define NL_MSGMAX 2048 103 #endif 104 105 struct _msgT { 106 long msgId; 107 char *str; 108 LIST_ENTRY(_msgT) entries; 109 }; 110 111 struct _setT { 112 long setId; 113 LIST_HEAD(msghead, _msgT) msghead; 114 LIST_ENTRY(_setT) entries; 115 }; 116 117 LIST_HEAD(sethead, _setT) sethead; 118 static struct _setT *curSet; 119 120 static char *curline = NULL; 121 static long lineno = 0; 122 123 #if 0 /* XXX unused */ 124 static void corrupt __P((void)); 125 #endif 126 static char *cskip __P((char *)); 127 static void error __P((char *, char *)); 128 static void nomem __P((void)); 129 static char *getline __P((int)); 130 static char *getmsg __P((int, char *, char)); 131 static void warning __P((char *, char *)); 132 static char *wskip __P((char *)); 133 static char *xstrdup __P((const char *)); 134 static void *xmalloc __P((size_t)); 135 static void *xrealloc __P((void *, size_t)); 136 137 void MCParse __P((int fd)); 138 void MCReadCat __P((int fd)); 139 void MCWriteCat __P((int fd)); 140 void MCDelMsg __P((int msgId)); 141 void MCAddMsg __P((int msgId, const char *msg)); 142 void MCAddSet __P((int setId)); 143 void MCDelSet __P((int setId)); 144 int main __P((int, char **)); 145 void usage __P((void)); 146 147 148 void 149 usage() 150 { 151 fprintf(stderr, "Usage: %s catfile msgfile ...\n", getprogname()); 152 exit(1); 153 } 154 155 int 156 main(argc, argv) 157 int argc; 158 char *argv[]; 159 { 160 int ofd, ifd; 161 char *catfile = NULL; 162 int c; 163 164 while ((c = getopt(argc, argv, "")) != -1) { 165 switch (c) { 166 case '?': 167 default: 168 usage(); 169 /* NOTREACHED */ 170 } 171 } 172 argc -= optind; 173 argv += optind; 174 175 if (argc < 2) { 176 usage(); 177 /* NOTREACHED */ 178 } 179 catfile = *argv++; 180 181 for (; *argv; argv++) { 182 if ((ifd = open(*argv, O_RDONLY)) < 0) 183 err(1, "Unable to read %s", *argv); 184 MCParse(ifd); 185 close(ifd); 186 } 187 188 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) 189 err(1, "Unable to create a new %s", catfile); 190 MCWriteCat(ofd); 191 exit(0); 192 } 193 194 static void 195 warning(cptr, msg) 196 char *cptr; 197 char *msg; 198 { 199 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno); 200 fprintf(stderr, "%s\n", curline); 201 if (cptr) { 202 char *tptr; 203 for (tptr = curline; tptr < cptr; ++tptr) 204 putc(' ', stderr); 205 fprintf(stderr, "^\n"); 206 } 207 } 208 209 static void 210 error(cptr, msg) 211 char *cptr; 212 char *msg; 213 { 214 warning(cptr, msg); 215 exit(1); 216 } 217 218 #if 0 /* XXX unused */ 219 static void 220 corrupt() 221 { 222 error(NULL, "corrupt message catalog"); 223 } 224 #endif 225 226 static void 227 nomem() 228 { 229 error(NULL, "out of memory"); 230 } 231 232 static void * 233 xmalloc(len) 234 size_t len; 235 { 236 void *p; 237 238 if ((p = malloc(len)) == NULL) 239 nomem(); 240 return (p); 241 } 242 243 static void * 244 xrealloc(ptr, size) 245 void *ptr; 246 size_t size; 247 { 248 if ((ptr = realloc(ptr, size)) == NULL) 249 nomem(); 250 return (ptr); 251 } 252 253 static char * 254 xstrdup(str) 255 const char *str; 256 { 257 char *nstr; 258 259 if ((nstr = strdup(str)) == NULL) 260 nomem(); 261 return (nstr); 262 } 263 264 static char * 265 getline(fd) 266 int fd; 267 { 268 static long curlen = BUFSIZ; 269 static char buf[BUFSIZ], *bptr = buf, *bend = buf; 270 char *cptr, *cend; 271 long buflen; 272 273 if (!curline) { 274 curline = xmalloc(curlen); 275 } 276 ++lineno; 277 278 cptr = curline; 279 cend = curline + curlen; 280 for (;;) { 281 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) { 282 if (*bptr == '\n') { 283 *cptr = '\0'; 284 ++bptr; 285 return (curline); 286 } else 287 *cptr = *bptr; 288 } 289 if (cptr == cend) { 290 cptr = curline = xrealloc(curline, curlen *= 2); 291 cend = curline + curlen; 292 } 293 if (bptr == bend) { 294 buflen = read(fd, buf, BUFSIZ); 295 if (buflen <= 0) { 296 if (cptr > curline) { 297 *cptr = '\0'; 298 return (curline); 299 } 300 return (NULL); 301 } 302 bend = buf + buflen; 303 bptr = buf; 304 } 305 } 306 } 307 308 static char * 309 wskip(cptr) 310 char *cptr; 311 { 312 if (!*cptr || !isspace((unsigned char) *cptr)) { 313 warning(cptr, "expected a space"); 314 return (cptr); 315 } 316 while (*cptr && isspace((unsigned char) *cptr)) 317 ++cptr; 318 return (cptr); 319 } 320 321 static char * 322 cskip(cptr) 323 char *cptr; 324 { 325 if (!*cptr || isspace((unsigned char) *cptr)) { 326 warning(cptr, "wasn't expecting a space"); 327 return (cptr); 328 } 329 while (*cptr && !isspace((unsigned char) *cptr)) 330 ++cptr; 331 return (cptr); 332 } 333 334 static char * 335 getmsg(fd, cptr, quote) 336 int fd; 337 char *cptr; 338 char quote; 339 { 340 static char *msg = NULL; 341 static long msglen = 0; 342 long clen, i; 343 char *tptr; 344 345 if (quote && *cptr == quote) { 346 ++cptr; 347 } 348 349 clen = strlen(cptr) + 1; 350 if (clen > msglen) { 351 if (msglen) 352 msg = xrealloc(msg, clen); 353 else 354 msg = xmalloc(clen); 355 msglen = clen; 356 } 357 tptr = msg; 358 359 while (*cptr) { 360 if (quote && *cptr == quote) { 361 char *tmp; 362 tmp = cptr + 1; 363 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) { 364 warning(cptr, "unexpected quote character, ignoring"); 365 *tptr++ = *cptr++; 366 } else { 367 *cptr = '\0'; 368 } 369 } else 370 if (*cptr == '\\') { 371 ++cptr; 372 switch (*cptr) { 373 case '\0': 374 cptr = getline(fd); 375 if (!cptr) 376 error(NULL, "premature end of file"); 377 msglen += strlen(cptr); 378 i = tptr - msg; 379 msg = xrealloc(msg, msglen); 380 tptr = msg + i; 381 break; 382 case 'n': 383 *tptr++ = '\n'; 384 ++cptr; 385 break; 386 case 't': 387 *tptr++ = '\t'; 388 ++cptr; 389 break; 390 case 'v': 391 *tptr++ = '\v'; 392 ++cptr; 393 break; 394 case 'b': 395 *tptr++ = '\b'; 396 ++cptr; 397 break; 398 case 'r': 399 *tptr++ = '\r'; 400 ++cptr; 401 break; 402 case 'f': 403 *tptr++ = '\f'; 404 ++cptr; 405 break; 406 case '\\': 407 *tptr++ = '\\'; 408 ++cptr; 409 break; 410 default: 411 if (quote && *cptr == quote) { 412 *tptr++ = *cptr++; 413 } else if (isdigit((unsigned char) *cptr)) { 414 *tptr = 0; 415 for (i = 0; i < 3; ++i) { 416 if (!isdigit((unsigned char) *cptr)) 417 break; 418 if (*cptr > '7') 419 warning(cptr, "octal number greater than 7?!"); 420 *tptr *= 8; 421 *tptr += (*cptr - '0'); 422 ++cptr; 423 } 424 } else { 425 warning(cptr, "unrecognized escape sequence"); 426 } 427 break; 428 } 429 } else { 430 *tptr++ = *cptr++; 431 } 432 } 433 *tptr = '\0'; 434 return (msg); 435 } 436 437 void 438 MCParse(fd) 439 int fd; 440 { 441 char *cptr, *str; 442 int setid, msgid = 0; 443 char quote = 0; 444 445 /* XXX: init sethead? */ 446 447 while ((cptr = getline(fd))) { 448 if (*cptr == '$') { 449 ++cptr; 450 if (strncmp(cptr, "set", 3) == 0) { 451 cptr += 3; 452 cptr = wskip(cptr); 453 setid = atoi(cptr); 454 MCAddSet(setid); 455 msgid = 0; 456 } else if (strncmp(cptr, "delset", 6) == 0) { 457 cptr += 6; 458 cptr = wskip(cptr); 459 setid = atoi(cptr); 460 MCDelSet(setid); 461 } else if (strncmp(cptr, "quote", 5) == 0) { 462 cptr += 5; 463 if (!*cptr) 464 quote = 0; 465 else { 466 cptr = wskip(cptr); 467 if (!*cptr) 468 quote = 0; 469 else 470 quote = *cptr; 471 } 472 } else if (isspace((unsigned char) *cptr)) { 473 ; 474 } else { 475 if (*cptr) { 476 cptr = wskip(cptr); 477 if (*cptr) 478 warning(cptr, "unrecognized line"); 479 } 480 } 481 } else { 482 /* 483 * First check for (and eat) empty lines.... 484 */ 485 if (!*cptr) 486 continue; 487 /* 488 * We have a digit? Start of a message. Else, 489 * syntax error. 490 */ 491 if (isdigit((unsigned char) *cptr)) { 492 msgid = atoi(cptr); 493 cptr = cskip(cptr); 494 cptr = wskip(cptr); 495 /* if (*cptr) ++cptr; */ 496 } else { 497 warning(cptr, "neither blank line nor start of a message id"); 498 continue; 499 } 500 /* 501 * If we have a message ID, but no message, 502 * then this means "delete this message id 503 * from the catalog". 504 */ 505 if (!*cptr) { 506 MCDelMsg(msgid); 507 } else { 508 str = getmsg(fd, cptr, quote); 509 MCAddMsg(msgid, str); 510 } 511 } 512 } 513 } 514 515 void 516 MCReadCat(fd) 517 int fd; 518 { 519 #if 0 520 MCHeaderT mcHead; 521 MCMsgT mcMsg; 522 MCSetT mcSet; 523 msgT *msg; 524 setT *set; 525 int i; 526 char *data; 527 528 /* XXX init sethead? */ 529 530 if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead)) 531 corrupt(); 532 if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0) 533 corrupt(); 534 if (mcHead.majorVer != MCMajorVer) 535 error(NULL, "unrecognized catalog version"); 536 if ((mcHead.flags & MCGetByteOrder()) == 0) 537 error(NULL, "wrong byte order"); 538 539 if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1) 540 corrupt(); 541 542 for (;;) { 543 if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet)) 544 corrupt(); 545 if (mcSet.invalid) 546 continue; 547 548 set = xmalloc(sizeof(setT)); 549 memset(set, '\0', sizeof(*set)); 550 if (cat->first) { 551 cat->last->next = set; 552 set->prev = cat->last; 553 cat->last = set; 554 } else 555 cat->first = cat->last = set; 556 557 set->setId = mcSet.setId; 558 559 /* Get the data */ 560 if (mcSet.dataLen) { 561 data = xmalloc(mcSet.dataLen); 562 if (lseek(fd, mcSet.data.off, SEEK_SET) == -1) 563 corrupt(); 564 if (read(fd, data, mcSet.dataLen) != mcSet.dataLen) 565 corrupt(); 566 if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1) 567 corrupt(); 568 569 for (i = 0; i < mcSet.numMsgs; ++i) { 570 if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg)) 571 corrupt(); 572 if (mcMsg.invalid) { 573 --i; 574 continue; 575 } 576 msg = xmalloc(sizeof(msgT)); 577 memset(msg, '\0', sizeof(*msg)); 578 if (set->first) { 579 set->last->next = msg; 580 msg->prev = set->last; 581 set->last = msg; 582 } else 583 set->first = set->last = msg; 584 585 msg->msgId = mcMsg.msgId; 586 msg->str = xstrdup((char *) (data + mcMsg.msg.off)); 587 } 588 free(data); 589 } 590 if (!mcSet.nextSet) 591 break; 592 if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1) 593 corrupt(); 594 } 595 #endif 596 } 597 598 /* 599 * Write message catalog. 600 * 601 * The message catalog is first converted from its internal to its 602 * external representation in a chunk of memory allocated for this 603 * purpose. Then the completed catalog is written. This approach 604 * avoids additional housekeeping variables and/or a lot of seeks 605 * that would otherwise be required. 606 */ 607 void 608 MCWriteCat(fd) 609 int fd; 610 { 611 int nsets; /* number of sets */ 612 int nmsgs; /* number of msgs */ 613 int string_size; /* total size of string pool */ 614 int msgcat_size; /* total size of message catalog */ 615 void *msgcat; /* message catalog data */ 616 struct _nls_cat_hdr *cat_hdr; 617 struct _nls_set_hdr *set_hdr; 618 struct _nls_msg_hdr *msg_hdr; 619 char *strings; 620 struct _setT *set; 621 struct _msgT *msg; 622 int msg_index; 623 int msg_offset; 624 625 /* determine number of sets, number of messages, and size of the 626 * string pool */ 627 nsets = 0; 628 nmsgs = 0; 629 string_size = 0; 630 631 for (set = sethead.lh_first; set != NULL; 632 set = set->entries.le_next) { 633 nsets++; 634 635 for (msg = set->msghead.lh_first; msg != NULL; 636 msg = msg->entries.le_next) { 637 nmsgs++; 638 string_size += strlen(msg->str) + 1; 639 } 640 } 641 642 #ifdef DEBUG 643 printf("number of sets: %d\n", nsets); 644 printf("number of msgs: %d\n", nmsgs); 645 printf("string pool size: %d\n", string_size); 646 #endif 647 648 /* determine size and then allocate buffer for constructing external 649 * message catalog representation */ 650 msgcat_size = sizeof(struct _nls_cat_hdr) 651 + (nsets * sizeof(struct _nls_set_hdr)) 652 + (nmsgs * sizeof(struct _nls_msg_hdr)) 653 + string_size; 654 655 msgcat = xmalloc(msgcat_size); 656 memset(msgcat, '\0', msgcat_size); 657 658 /* fill in msg catalog header */ 659 cat_hdr = (struct _nls_cat_hdr *) msgcat; 660 cat_hdr->__magic = htonl(_NLS_MAGIC); 661 cat_hdr->__nsets = htonl(nsets); 662 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr)); 663 cat_hdr->__msg_hdr_offset = 664 htonl(nsets * sizeof(struct _nls_set_hdr)); 665 cat_hdr->__msg_txt_offset = 666 htonl(nsets * sizeof(struct _nls_set_hdr) + 667 nmsgs * sizeof(struct _nls_msg_hdr)); 668 669 /* compute offsets for set & msg header tables and string pool */ 670 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat + 671 sizeof(struct _nls_cat_hdr)); 672 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat + 673 sizeof(struct _nls_cat_hdr) + 674 nsets * sizeof(struct _nls_set_hdr)); 675 strings = (char *) msgcat + 676 sizeof(struct _nls_cat_hdr) + 677 nsets * sizeof(struct _nls_set_hdr) + 678 nmsgs * sizeof(struct _nls_msg_hdr); 679 680 msg_index = 0; 681 msg_offset = 0; 682 for (set = sethead.lh_first; set != NULL; 683 set = set->entries.le_next) { 684 685 nmsgs = 0; 686 for (msg = set->msghead.lh_first; msg != NULL; 687 msg = msg->entries.le_next) { 688 int msg_len = strlen(msg->str) + 1; 689 690 msg_hdr->__msgno = htonl(msg->msgId); 691 msg_hdr->__msglen = htonl(msg_len); 692 msg_hdr->__offset = htonl(msg_offset); 693 694 memcpy(strings, msg->str, msg_len); 695 strings += msg_len; 696 msg_offset += msg_len; 697 698 nmsgs++; 699 msg_hdr++; 700 } 701 702 set_hdr->__setno = htonl(set->setId); 703 set_hdr->__nmsgs = htonl(nmsgs); 704 set_hdr->__index = htonl(msg_index); 705 msg_index += nmsgs; 706 set_hdr++; 707 } 708 709 /* write out catalog. XXX: should this be done in small chunks? */ 710 write(fd, msgcat, msgcat_size); 711 } 712 713 void 714 MCAddSet(setId) 715 int setId; 716 { 717 struct _setT *p, *q; 718 719 if (setId <= 0) { 720 error(NULL, "setId's must be greater than zero"); 721 /* NOTREACHED */ 722 } 723 if (setId > NL_SETMAX) { 724 error(NULL, "setId exceeds limit"); 725 /* NOTREACHED */ 726 } 727 728 p = sethead.lh_first; 729 q = NULL; 730 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next); 731 732 if (p && p->setId == setId) { 733 ; 734 } else { 735 p = xmalloc(sizeof(struct _setT)); 736 memset(p, '\0', sizeof(struct _setT)); 737 LIST_INIT(&p->msghead); 738 739 p->setId = setId; 740 741 if (q == NULL) { 742 LIST_INSERT_HEAD(&sethead, p, entries); 743 } else { 744 LIST_INSERT_AFTER(q, p, entries); 745 } 746 } 747 748 curSet = p; 749 } 750 751 void 752 MCAddMsg(msgId, str) 753 int msgId; 754 const char *str; 755 { 756 struct _msgT *p, *q; 757 758 if (!curSet) 759 error(NULL, "can't specify a message when no set exists"); 760 761 if (msgId <= 0) { 762 error(NULL, "msgId's must be greater than zero"); 763 /* NOTREACHED */ 764 } 765 if (msgId > NL_MSGMAX) { 766 error(NULL, "msgID exceeds limit"); 767 /* NOTREACHED */ 768 } 769 770 p = curSet->msghead.lh_first; 771 q = NULL; 772 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next); 773 774 if (p && p->msgId == msgId) { 775 free(p->str); 776 } else { 777 p = xmalloc(sizeof(struct _msgT)); 778 memset(p, '\0', sizeof(struct _msgT)); 779 780 if (q == NULL) { 781 LIST_INSERT_HEAD(&curSet->msghead, p, entries); 782 } else { 783 LIST_INSERT_AFTER(q, p, entries); 784 } 785 } 786 787 p->msgId = msgId; 788 p->str = xstrdup(str); 789 } 790 791 void 792 MCDelSet(setId) 793 int setId; 794 { 795 struct _setT *set; 796 struct _msgT *msg; 797 798 set = sethead.lh_first; 799 for (; set != NULL && set->setId < setId; set = set->entries.le_next); 800 801 if (set && set->setId == setId) { 802 803 msg = set->msghead.lh_first; 804 while (msg) { 805 free(msg->str); 806 LIST_REMOVE(msg, entries); 807 } 808 809 LIST_REMOVE(set, entries); 810 return; 811 } 812 warning(NULL, "specified set doesn't exist"); 813 } 814 815 void 816 MCDelMsg(msgId) 817 int msgId; 818 { 819 struct _msgT *msg; 820 821 if (!curSet) 822 error(NULL, "you can't delete a message before defining the set"); 823 824 msg = curSet->msghead.lh_first; 825 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next); 826 827 if (msg && msg->msgId == msgId) { 828 free(msg->str); 829 LIST_REMOVE(msg, entries); 830 return; 831 } 832 warning(NULL, "specified msg doesn't exist"); 833 } 834