gencat.c revision 1.14 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 (at) 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