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