gencat.c revision 1.19 1 /* $NetBSD: gencat.c,v 1.19 2004/01/05 23:23:34 jmmv 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.19 2004/01/05 23:23:34 jmmv 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 (at) alfalfa.com
73
74 ******************************************************************/
75
76 #if HAVE_NBTOOL_CONFIG_H
77 #include "nbtool_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