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