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