gencat.c revision 1.22 1 /* $NetBSD: gencat.c,v 1.22 2007/10/10 02:34:18 ginsbach 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.22 2007/10/10 02:34:18 ginsbach 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((const 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((const char *, const 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 const char *cptr;
197 const 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(msg)
211 const char *msg;
212 {
213 warning(NULL, msg);
214 exit(1);
215 }
216
217 #if 0 /* XXX unused */
218 static void
219 corrupt()
220 {
221 error("corrupt message catalog");
222 }
223 #endif
224
225 static void
226 nomem()
227 {
228 error("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("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 msgid = 0;
442 int setid = 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 no set directive specified, all messages
502 * shall be in default message set NL_SETD.
503 */
504 if (setid == 0) {
505 setid = NL_SETD;
506 MCAddSet(setid);
507 }
508 /*
509 * If we have a message ID, but no message,
510 * then this means "delete this message id
511 * from the catalog".
512 */
513 if (!*cptr) {
514 MCDelMsg(msgid);
515 } else {
516 str = getmsg(fd, cptr, quote);
517 MCAddMsg(msgid, str);
518 }
519 }
520 }
521 }
522
523 void
524 MCReadCat(fd)
525 int fd;
526 {
527 #if 0
528 MCHeaderT mcHead;
529 MCMsgT mcMsg;
530 MCSetT mcSet;
531 msgT *msg;
532 setT *set;
533 int i;
534 char *data;
535
536 /* XXX init sethead? */
537
538 if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
539 corrupt();
540 if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
541 corrupt();
542 if (mcHead.majorVer != MCMajorVer)
543 error("unrecognized catalog version");
544 if ((mcHead.flags & MCGetByteOrder()) == 0)
545 error("wrong byte order");
546
547 if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
548 corrupt();
549
550 for (;;) {
551 if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
552 corrupt();
553 if (mcSet.invalid)
554 continue;
555
556 set = xmalloc(sizeof(setT));
557 memset(set, '\0', sizeof(*set));
558 if (cat->first) {
559 cat->last->next = set;
560 set->prev = cat->last;
561 cat->last = set;
562 } else
563 cat->first = cat->last = set;
564
565 set->setId = mcSet.setId;
566
567 /* Get the data */
568 if (mcSet.dataLen) {
569 data = xmalloc(mcSet.dataLen);
570 if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
571 corrupt();
572 if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
573 corrupt();
574 if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
575 corrupt();
576
577 for (i = 0; i < mcSet.numMsgs; ++i) {
578 if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
579 corrupt();
580 if (mcMsg.invalid) {
581 --i;
582 continue;
583 }
584 msg = xmalloc(sizeof(msgT));
585 memset(msg, '\0', sizeof(*msg));
586 if (set->first) {
587 set->last->next = msg;
588 msg->prev = set->last;
589 set->last = msg;
590 } else
591 set->first = set->last = msg;
592
593 msg->msgId = mcMsg.msgId;
594 msg->str = xstrdup((char *) (data + mcMsg.msg.off));
595 }
596 free(data);
597 }
598 if (!mcSet.nextSet)
599 break;
600 if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
601 corrupt();
602 }
603 #endif
604 }
605
606 /*
607 * Write message catalog.
608 *
609 * The message catalog is first converted from its internal to its
610 * external representation in a chunk of memory allocated for this
611 * purpose. Then the completed catalog is written. This approach
612 * avoids additional housekeeping variables and/or a lot of seeks
613 * that would otherwise be required.
614 */
615 void
616 MCWriteCat(fd)
617 int fd;
618 {
619 int nsets; /* number of sets */
620 int nmsgs; /* number of msgs */
621 int string_size; /* total size of string pool */
622 int msgcat_size; /* total size of message catalog */
623 void *msgcat; /* message catalog data */
624 struct _nls_cat_hdr *cat_hdr;
625 struct _nls_set_hdr *set_hdr;
626 struct _nls_msg_hdr *msg_hdr;
627 char *strings;
628 struct _setT *set;
629 struct _msgT *msg;
630 int msg_index;
631 int msg_offset;
632
633 /* determine number of sets, number of messages, and size of the
634 * string pool */
635 nsets = 0;
636 nmsgs = 0;
637 string_size = 0;
638
639 for (set = sethead.lh_first; set != NULL;
640 set = set->entries.le_next) {
641 nsets++;
642
643 for (msg = set->msghead.lh_first; msg != NULL;
644 msg = msg->entries.le_next) {
645 nmsgs++;
646 string_size += strlen(msg->str) + 1;
647 }
648 }
649
650 #ifdef DEBUG
651 printf("number of sets: %d\n", nsets);
652 printf("number of msgs: %d\n", nmsgs);
653 printf("string pool size: %d\n", string_size);
654 #endif
655
656 /* determine size and then allocate buffer for constructing external
657 * message catalog representation */
658 msgcat_size = sizeof(struct _nls_cat_hdr)
659 + (nsets * sizeof(struct _nls_set_hdr))
660 + (nmsgs * sizeof(struct _nls_msg_hdr))
661 + string_size;
662
663 msgcat = xmalloc(msgcat_size);
664 memset(msgcat, '\0', msgcat_size);
665
666 /* fill in msg catalog header */
667 cat_hdr = (struct _nls_cat_hdr *) msgcat;
668 cat_hdr->__magic = htonl(_NLS_MAGIC);
669 cat_hdr->__nsets = htonl(nsets);
670 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
671 cat_hdr->__msg_hdr_offset =
672 htonl(nsets * sizeof(struct _nls_set_hdr));
673 cat_hdr->__msg_txt_offset =
674 htonl(nsets * sizeof(struct _nls_set_hdr) +
675 nmsgs * sizeof(struct _nls_msg_hdr));
676
677 /* compute offsets for set & msg header tables and string pool */
678 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
679 sizeof(struct _nls_cat_hdr));
680 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
681 sizeof(struct _nls_cat_hdr) +
682 nsets * sizeof(struct _nls_set_hdr));
683 strings = (char *) msgcat +
684 sizeof(struct _nls_cat_hdr) +
685 nsets * sizeof(struct _nls_set_hdr) +
686 nmsgs * sizeof(struct _nls_msg_hdr);
687
688 msg_index = 0;
689 msg_offset = 0;
690 for (set = sethead.lh_first; set != NULL;
691 set = set->entries.le_next) {
692
693 nmsgs = 0;
694 for (msg = set->msghead.lh_first; msg != NULL;
695 msg = msg->entries.le_next) {
696 int msg_len = strlen(msg->str) + 1;
697
698 msg_hdr->__msgno = htonl(msg->msgId);
699 msg_hdr->__msglen = htonl(msg_len);
700 msg_hdr->__offset = htonl(msg_offset);
701
702 memcpy(strings, msg->str, msg_len);
703 strings += msg_len;
704 msg_offset += msg_len;
705
706 nmsgs++;
707 msg_hdr++;
708 }
709
710 set_hdr->__setno = htonl(set->setId);
711 set_hdr->__nmsgs = htonl(nmsgs);
712 set_hdr->__index = htonl(msg_index);
713 msg_index += nmsgs;
714 set_hdr++;
715 }
716
717 /* write out catalog. XXX: should this be done in small chunks? */
718 write(fd, msgcat, msgcat_size);
719 }
720
721 void
722 MCAddSet(setId)
723 int setId;
724 {
725 struct _setT *p, *q;
726
727 if (setId <= 0) {
728 error("setId's must be greater than zero");
729 /* NOTREACHED */
730 }
731 if (setId > NL_SETMAX) {
732 error("setId exceeds limit");
733 /* NOTREACHED */
734 }
735
736 p = sethead.lh_first;
737 q = NULL;
738 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
739
740 if (p && p->setId == setId) {
741 ;
742 } else {
743 p = xmalloc(sizeof(struct _setT));
744 memset(p, '\0', sizeof(struct _setT));
745 LIST_INIT(&p->msghead);
746
747 p->setId = setId;
748
749 if (q == NULL) {
750 LIST_INSERT_HEAD(&sethead, p, entries);
751 } else {
752 LIST_INSERT_AFTER(q, p, entries);
753 }
754 }
755
756 curSet = p;
757 }
758
759 void
760 MCAddMsg(msgId, str)
761 int msgId;
762 const char *str;
763 {
764 struct _msgT *p, *q;
765
766 if (!curSet)
767 error("can't specify a message when no set exists");
768
769 if (msgId <= 0) {
770 error("msgId's must be greater than zero");
771 /* NOTREACHED */
772 }
773 if (msgId > NL_MSGMAX) {
774 error("msgID exceeds limit");
775 /* NOTREACHED */
776 }
777
778 p = curSet->msghead.lh_first;
779 q = NULL;
780 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
781
782 if (p && p->msgId == msgId) {
783 free(p->str);
784 } else {
785 p = xmalloc(sizeof(struct _msgT));
786 memset(p, '\0', sizeof(struct _msgT));
787
788 if (q == NULL) {
789 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
790 } else {
791 LIST_INSERT_AFTER(q, p, entries);
792 }
793 }
794
795 p->msgId = msgId;
796 p->str = xstrdup(str);
797 }
798
799 void
800 MCDelSet(setId)
801 int setId;
802 {
803 struct _setT *set;
804 struct _msgT *msg;
805
806 if (setId <= 0) {
807 error("setId's must be greater than zero");
808 /* NOTREACHED */
809 }
810 if (setId > NL_SETMAX) {
811 error("setId exceeds limit");
812 /* NOTREACHED */
813 }
814
815 set = sethead.lh_first;
816 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
817
818 if (set && set->setId == setId) {
819 LIST_REMOVE(set, entries);
820 while ((msg = set->msghead.lh_first) != NULL) {
821 LIST_REMOVE(msg, entries);
822 free(msg->str);
823 free(msg);
824 }
825 free(set);
826 return;
827 }
828 warning(NULL, "specified set doesn't exist");
829 }
830
831 void
832 MCDelMsg(msgId)
833 int msgId;
834 {
835 struct _msgT *msg;
836
837 if (!curSet)
838 error("you can't delete a message before defining the set");
839
840 msg = curSet->msghead.lh_first;
841 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
842
843 if (msg && msg->msgId == msgId) {
844 LIST_REMOVE(msg, entries);
845 free(msg->str);
846 free(msg);
847 return;
848 }
849 warning(NULL, "specified msg doesn't exist");
850 }
851