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