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