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