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