]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/gencat/gencat.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.bin / gencat / gencat.c
1 /* ex:ts=4
2  */
3
4 /*      $NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $        */
5
6 /*
7  * Copyright (c) 1996 The NetBSD Foundation, Inc.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by J.T. Conklin.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 /***********************************************************
36 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
37
38                         All Rights Reserved
39
40 Permission to use, copy, modify, and distribute this software and its
41 documentation for any purpose and without fee is hereby granted,
42 provided that the above copyright notice appear in all copies and that
43 both that copyright notice and this permission notice appear in
44 supporting documentation, and that Alfalfa's name not be used in
45 advertising or publicity pertaining to distribution of the software
46 without specific, written prior permission.
47
48 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
49 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
50 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
51 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
52 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
53 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
54 SOFTWARE.
55
56 If you make any modifications, bugfixes or other changes to this software
57 we'd appreciate it if you could send a copy to us so we can keep things
58 up-to-date.  Many thanks.
59                                 Kee Hinckley
60                                 Alfalfa Software, Inc.
61                                 267 Allston St., #3
62                                 Cambridge, MA 02139  USA
63                                 nazgul@alfalfa.com
64
65 ******************************************************************/
66
67 #include <sys/cdefs.h>
68 __FBSDID("$FreeBSD$");
69
70 #define _NLS_PRIVATE
71
72 #include <sys/types.h>
73 #include <sys/queue.h>
74
75 #include <arpa/inet.h>          /* for htonl() */
76
77 #include <ctype.h>
78 #include <err.h>
79 #include <fcntl.h>
80 #include <limits.h>
81 #include <nl_types.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <unistd.h>
86
87 struct _msgT {
88         long    msgId;
89         char   *str;
90         LIST_ENTRY(_msgT) entries;
91 };
92
93 struct _setT {
94         long    setId;
95         LIST_HEAD(msghead, _msgT) msghead;
96         LIST_ENTRY(_setT) entries;
97 };
98
99 LIST_HEAD(sethead, _setT) sethead;
100 static struct _setT *curSet;
101
102 static char *curline = NULL;
103 static long lineno = 0;
104
105 static  char   *cskip(char *);
106 static  void    error(const char *);
107 static  char   *getline(int);
108 static  char   *getmsg(int, char *, char);
109 static  void    warning(const char *, const char *);
110 static  char   *wskip(char *);
111 static  char   *xstrdup(const char *);
112 static  void   *xmalloc(size_t);
113 static  void   *xrealloc(void *, size_t);
114
115 void    MCParse(int);
116 void    MCReadCat(int);
117 void    MCWriteCat(int);
118 void    MCDelMsg(int);
119 void    MCAddMsg(int, const char *);
120 void    MCAddSet(int);
121 void    MCDelSet(int);
122 void    usage(void);
123 int     main(int, char **);
124
125 void
126 usage(void)
127 {
128         fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
129         exit(1);
130 }
131
132 int
133 main(int argc, char **argv)
134 {
135         int     ofd, ifd;
136         char    *catfile = NULL;
137         int     c;
138
139 #define DEPRECATEDMSG   1
140
141 #ifdef DEPRECATEDMSG
142         while ((c = getopt(argc, argv, "new")) != -1) {
143 #else
144         while ((c = getopt(argc, argv, "")) != -1) {
145 #endif
146                 switch (c) {
147 #ifdef DEPRECATEDMSG
148                 case 'n':
149                         fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
150                 case 'e':
151                 case 'w':
152                         break;
153 #endif
154                 case '?':
155                 default:
156                         usage();
157                         /* NOTREACHED */
158                 }
159         }
160         argc -= optind;
161         argv += optind;
162
163         if (argc < 2) {
164                 usage();
165                 /* NOTREACHED */
166         }
167         catfile = *argv++;
168
169         for (; *argv; argv++) {
170                 if ((ifd = open(*argv, O_RDONLY)) < 0)
171                         err(1, "Unable to read %s", *argv);
172                 MCParse(ifd);
173                 close(ifd);
174         }
175
176         if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
177                 err(1, "Unable to create a new %s", catfile);
178         MCWriteCat(ofd);
179         exit(0);
180 }
181
182 static void
183 warning(const char *cptr, const char *msg)
184 {
185         fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
186         fprintf(stderr, "%s\n", curline);
187         if (cptr) {
188                 char   *tptr;
189                 for (tptr = curline; tptr < cptr; ++tptr)
190                         putc(' ', stderr);
191                 fprintf(stderr, "^\n");
192         }
193 }
194
195 #define CORRUPT()       { error("corrupt message catalog"); }
196 #define NOMEM()         { error("out of memory"); }
197
198 static void
199 error(const char *msg)
200 {
201         warning(NULL, msg);
202         exit(1);
203 }
204
205 static void *
206 xmalloc(size_t len)
207 {
208         void   *p;
209
210         if ((p = malloc(len)) == NULL)
211                 NOMEM();
212         return (p);
213 }
214
215 static void *
216 xrealloc(void *ptr, size_t size)
217 {
218         if ((ptr = realloc(ptr, size)) == NULL)
219                 NOMEM();
220         return (ptr);
221 }
222
223 static char *
224 xstrdup(const char *str)
225 {
226         char *nstr;
227
228         if ((nstr = strdup(str)) == NULL)
229                 NOMEM();
230         return (nstr);
231 }
232
233 static char *
234 getline(int fd)
235 {
236         static long curlen = BUFSIZ;
237         static char buf[BUFSIZ], *bptr = buf, *bend = buf;
238         char   *cptr, *cend;
239         long    buflen;
240
241         if (!curline) {
242                 curline = xmalloc(curlen);
243         }
244         ++lineno;
245
246         cptr = curline;
247         cend = curline + curlen;
248         for (;;) {
249                 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
250                         if (*bptr == '\n') {
251                                 *cptr = '\0';
252                                 ++bptr;
253                                 return (curline);
254                         } else
255                                 *cptr = *bptr;
256                 }
257                 if (cptr == cend) {
258                         cptr = curline = xrealloc(curline, curlen *= 2);
259                         cend = curline + curlen;
260                 }
261                 if (bptr == bend) {
262                         buflen = read(fd, buf, BUFSIZ);
263                         if (buflen <= 0) {
264                                 if (cptr > curline) {
265                                         *cptr = '\0';
266                                         return (curline);
267                                 }
268                                 return (NULL);
269                         }
270                         bend = buf + buflen;
271                         bptr = buf;
272                 }
273         }
274 }
275
276 static char *
277 wskip(char *cptr)
278 {
279         if (!*cptr || !isspace((unsigned char) *cptr)) {
280                 warning(cptr, "expected a space");
281                 return (cptr);
282         }
283         while (*cptr && isspace((unsigned char) *cptr))
284                 ++cptr;
285         return (cptr);
286 }
287
288 static char *
289 cskip(char *cptr)
290 {
291         if (!*cptr || isspace((unsigned char) *cptr)) {
292                 warning(cptr, "wasn't expecting a space");
293                 return (cptr);
294         }
295         while (*cptr && !isspace((unsigned char) *cptr))
296                 ++cptr;
297         return (cptr);
298 }
299
300 static char *
301 getmsg(int fd, char *cptr, char quote)
302 {
303         static char *msg = NULL;
304         static long msglen = 0;
305         long    clen, i;
306         char   *tptr;
307
308         if (quote && *cptr == quote) {
309                 ++cptr;
310         } 
311
312         clen = strlen(cptr) + 1;
313         if (clen > msglen) {
314                 if (msglen)
315                         msg = xrealloc(msg, clen);
316                 else
317                         msg = xmalloc(clen);
318                 msglen = clen;
319         }
320         tptr = msg;
321
322         while (*cptr) {
323                 if (quote && *cptr == quote) {
324                         char   *tmp;
325                         tmp = cptr + 1;
326                         if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
327                                 warning(cptr, "unexpected quote character, ignoring");
328                                 *tptr++ = *cptr++;
329                         } else {
330                                 *cptr = '\0';
331                         }
332                 } else
333                         if (*cptr == '\\') {
334                                 ++cptr;
335                                 switch (*cptr) {
336                                 case '\0':
337                                         cptr = getline(fd);
338                                         if (!cptr)
339                                                 error("premature end of file");
340                                         msglen += strlen(cptr);
341                                         i = tptr - msg;
342                                         msg = xrealloc(msg, msglen);
343                                         tptr = msg + i;
344                                         break;
345
346                 #define CASEOF(CS, CH)          \
347                         case CS:                \
348                                 *tptr++ = CH;   \
349                                 ++cptr;         \
350                                 break;          \
351
352                                 CASEOF('n', '\n');
353                                 CASEOF('t', '\t');
354                                 CASEOF('v', '\v');
355                                 CASEOF('b', '\b');
356                                 CASEOF('r', '\r');
357                                 CASEOF('f', '\f');
358                                 CASEOF('"', '"');
359                                 CASEOF('\\', '\\');
360
361                                 default:
362                                         if (quote && *cptr == quote) {
363                                                 *tptr++ = *cptr++;
364                                         } else if (isdigit((unsigned char) *cptr)) {
365                                                 *tptr = 0;
366                                                 for (i = 0; i < 3; ++i) {
367                                                         if (!isdigit((unsigned char) *cptr))
368                                                                 break;
369                                                         if (*cptr > '7')
370                                                                 warning(cptr, "octal number greater than 7?!");
371                                                         *tptr *= 8;
372                                                         *tptr += (*cptr - '0');
373                                                         ++cptr;
374                                                 }
375                                         } else {
376                                                 warning(cptr, "unrecognized escape sequence");
377                                         }
378                                         break;
379                                 }
380                         } else {
381                                 *tptr++ = *cptr++;
382                         }
383         }
384         *tptr = '\0';
385         return (msg);
386 }
387
388 void
389 MCParse(int fd)
390 {
391         char   *cptr, *str;
392         int     setid, msgid = 0;
393         char    quote = 0;
394
395         /* XXX: init sethead? */
396
397         while ((cptr = getline(fd))) {
398                 if (*cptr == '$') {
399                         ++cptr;
400                         if (strncmp(cptr, "set", 3) == 0) {
401                                 cptr += 3;
402                                 cptr = wskip(cptr);
403                                 setid = atoi(cptr);
404                                 MCAddSet(setid);
405                                 msgid = 0;
406                         } else if (strncmp(cptr, "delset", 6) == 0) {
407                                 cptr += 6;
408                                 cptr = wskip(cptr);
409                                 setid = atoi(cptr);
410                                 MCDelSet(setid);
411                         } else if (strncmp(cptr, "quote", 5) == 0) {
412                                 cptr += 5;
413                                 if (!*cptr)
414                                         quote = 0;
415                                 else {
416                                         cptr = wskip(cptr);
417                                         if (!*cptr)
418                                                 quote = 0;
419                                         else
420                                                 quote = *cptr;
421                                 }
422                         } else if (isspace((unsigned char) *cptr)) {
423                                 ;
424                         } else {
425                                 if (*cptr) {
426                                         cptr = wskip(cptr);
427                                         if (*cptr)
428                                                 warning(cptr, "unrecognized line");
429                                 }
430                         }
431                 } else {
432                         /*
433                          * First check for (and eat) empty lines....
434                          */
435                         if (!*cptr)
436                                 continue;
437                         /*
438                          * We have a digit? Start of a message. Else,
439                          * syntax error.
440                          */
441                         if (isdigit((unsigned char) *cptr)) {
442                                 msgid = atoi(cptr);
443                                 cptr = cskip(cptr);
444                                 cptr = wskip(cptr);
445                                 /* if (*cptr) ++cptr; */
446                         } else {
447                                 warning(cptr, "neither blank line nor start of a message id");
448                                 continue;
449                         }
450                         /*
451                          * If we have a message ID, but no message,
452                          * then this means "delete this message id
453                          * from the catalog".
454                          */
455                         if (!*cptr) {
456                                 MCDelMsg(msgid);
457                         } else {
458                                 str = getmsg(fd, cptr, quote);
459                                 MCAddMsg(msgid, str);
460                         }
461                 }
462         }
463 }
464
465 void
466 MCReadCat(int fd)
467 {
468         fd = 0;
469 #if 0
470         MCHeaderT mcHead;
471         MCMsgT  mcMsg;
472         MCSetT  mcSet;
473         msgT   *msg;
474         setT   *set;
475         int     i;
476         char   *data;
477
478         /* XXX init sethead? */
479
480         if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
481                 CORRUPT();
482         if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
483                 CORRUPT();
484         if (mcHead.majorVer != MCMajorVer)
485                 error("unrecognized catalog version");
486         if ((mcHead.flags & MCGetByteOrder()) == 0)
487                 error("wrong byte order");
488
489         if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
490                 CORRUPT();
491
492         for (;;) {
493                 if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
494                         CORRUPT();
495                 if (mcSet.invalid)
496                         continue;
497
498                 set = xmalloc(sizeof(setT));
499                 memset(set, '\0', sizeof(*set));
500                 if (cat->first) {
501                         cat->last->next = set;
502                         set->prev = cat->last;
503                         cat->last = set;
504                 } else
505                         cat->first = cat->last = set;
506
507                 set->setId = mcSet.setId;
508
509                 /* Get the data */
510                 if (mcSet.dataLen) {
511                         data = xmalloc(mcSet.dataLen);
512                         if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
513                                 CORRUPT();
514                         if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
515                                 CORRUPT();
516                         if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
517                                 CORRUPT();
518
519                         for (i = 0; i < mcSet.numMsgs; ++i) {
520                                 if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
521                                         CORRUPT();
522                                 if (mcMsg.invalid) {
523                                         --i;
524                                         continue;
525                                 }
526                                 msg = xmalloc(sizeof(msgT));
527                                 memset(msg, '\0', sizeof(*msg));
528                                 if (set->first) {
529                                         set->last->next = msg;
530                                         msg->prev = set->last;
531                                         set->last = msg;
532                                 } else
533                                         set->first = set->last = msg;
534
535                                 msg->msgId = mcMsg.msgId;
536                                 msg->str = xstrdup((char *) (data + mcMsg.msg.off));
537                         }
538                         free(data);
539                 }
540                 if (!mcSet.nextSet)
541                         break;
542                 if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
543                         CORRUPT();
544         }
545 #endif
546 }
547
548 /*
549  * Write message catalog.
550  *
551  * The message catalog is first converted from its internal to its
552  * external representation in a chunk of memory allocated for this
553  * purpose.  Then the completed catalog is written.  This approach
554  * avoids additional housekeeping variables and/or a lot of seeks
555  * that would otherwise be required.
556  */
557 void
558 MCWriteCat(int fd)
559 {
560         int     nsets;          /* number of sets */
561         int     nmsgs;          /* number of msgs */
562         int     string_size;    /* total size of string pool */
563         int     msgcat_size;    /* total size of message catalog */
564         void   *msgcat;         /* message catalog data */
565         struct _nls_cat_hdr *cat_hdr;
566         struct _nls_set_hdr *set_hdr;
567         struct _nls_msg_hdr *msg_hdr;
568         char   *strings;
569         struct _setT *set;
570         struct _msgT *msg;
571         int     msg_index;
572         int     msg_offset;
573
574         /* determine number of sets, number of messages, and size of the
575          * string pool */
576         nsets = 0;
577         nmsgs = 0;
578         string_size = 0;
579
580         for (set = sethead.lh_first; set != NULL;
581             set = set->entries.le_next) {
582                 nsets++;
583
584                 for (msg = set->msghead.lh_first; msg != NULL;
585                     msg = msg->entries.le_next) {
586                         nmsgs++;
587                         string_size += strlen(msg->str) + 1;
588                 }
589         }
590
591 #ifdef DEBUG
592         printf("number of sets: %d\n", nsets);
593         printf("number of msgs: %d\n", nmsgs);
594         printf("string pool size: %d\n", string_size);
595 #endif
596
597         /* determine size and then allocate buffer for constructing external
598          * message catalog representation */
599         msgcat_size = sizeof(struct _nls_cat_hdr)
600             + (nsets * sizeof(struct _nls_set_hdr))
601             + (nmsgs * sizeof(struct _nls_msg_hdr))
602             + string_size;
603
604         msgcat = xmalloc(msgcat_size);
605         memset(msgcat, '\0', msgcat_size);
606
607         /* fill in msg catalog header */
608         cat_hdr = (struct _nls_cat_hdr *) msgcat;
609         cat_hdr->__magic = htonl(_NLS_MAGIC);
610         cat_hdr->__nsets = htonl(nsets);
611         cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
612         cat_hdr->__msg_hdr_offset =
613             htonl(nsets * sizeof(struct _nls_set_hdr));
614         cat_hdr->__msg_txt_offset =
615             htonl(nsets * sizeof(struct _nls_set_hdr) +
616             nmsgs * sizeof(struct _nls_msg_hdr));
617
618         /* compute offsets for set & msg header tables and string pool */
619         set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
620             sizeof(struct _nls_cat_hdr));
621         msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
622             sizeof(struct _nls_cat_hdr) +
623             nsets * sizeof(struct _nls_set_hdr));
624         strings = (char *) msgcat +
625             sizeof(struct _nls_cat_hdr) +
626             nsets * sizeof(struct _nls_set_hdr) +
627             nmsgs * sizeof(struct _nls_msg_hdr);
628
629         msg_index = 0;
630         msg_offset = 0;
631         for (set = sethead.lh_first; set != NULL;
632             set = set->entries.le_next) {
633
634                 nmsgs = 0;
635                 for (msg = set->msghead.lh_first; msg != NULL;
636                     msg = msg->entries.le_next) {
637                         int     msg_len = strlen(msg->str) + 1;
638
639                         msg_hdr->__msgno = htonl(msg->msgId);
640                         msg_hdr->__msglen = htonl(msg_len);
641                         msg_hdr->__offset = htonl(msg_offset);
642
643                         memcpy(strings, msg->str, msg_len);
644                         strings += msg_len;
645                         msg_offset += msg_len;
646
647                         nmsgs++;
648                         msg_hdr++;
649                 }
650
651                 set_hdr->__setno = htonl(set->setId);
652                 set_hdr->__nmsgs = htonl(nmsgs);
653                 set_hdr->__index = htonl(msg_index);
654                 msg_index += nmsgs;
655                 set_hdr++;
656         }
657
658         /* write out catalog.  XXX: should this be done in small chunks? */
659         write(fd, msgcat, msgcat_size);
660 }
661
662 void
663 MCAddSet(int setId)
664 {
665         struct _setT *p, *q;
666
667         if (setId <= 0) {
668                 error("setId's must be greater than zero");
669                 /* NOTREACHED */
670         }
671         if (setId > NL_SETMAX) {
672                 error("setId exceeds limit");
673                 /* NOTREACHED */
674         }
675
676         p = sethead.lh_first;
677         q = NULL;
678         for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
679
680         if (p && p->setId == setId) {
681                 ;
682         } else {
683                 p = xmalloc(sizeof(struct _setT));
684                 memset(p, '\0', sizeof(struct _setT));
685                 LIST_INIT(&p->msghead);
686
687                 p->setId = setId;
688
689                 if (q == NULL) {
690                         LIST_INSERT_HEAD(&sethead, p, entries);
691                 } else {
692                         LIST_INSERT_AFTER(q, p, entries);
693                 }
694         }
695
696         curSet = p;
697 }
698
699 void
700 MCAddMsg(int msgId, const char *str)
701 {
702         struct _msgT *p, *q;
703
704         if (!curSet)
705                 error("can't specify a message when no set exists");
706
707         if (msgId <= 0) {
708                 error("msgId's must be greater than zero");
709                 /* NOTREACHED */
710         }
711         if (msgId > NL_MSGMAX) {
712                 error("msgID exceeds limit");
713                 /* NOTREACHED */
714         }
715
716         p = curSet->msghead.lh_first;
717         q = NULL;
718         for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
719
720         if (p && p->msgId == msgId) {
721                 free(p->str);
722         } else {
723                 p = xmalloc(sizeof(struct _msgT));
724                 memset(p, '\0', sizeof(struct _msgT));
725
726                 if (q == NULL) {
727                         LIST_INSERT_HEAD(&curSet->msghead, p, entries);
728                 } else {
729                         LIST_INSERT_AFTER(q, p, entries);
730                 }
731         }
732
733         p->msgId = msgId;
734         p->str = xstrdup(str);
735 }
736
737 void
738 MCDelSet(int setId)
739 {
740         struct _setT *set;
741         struct _msgT *msg;
742
743         set = sethead.lh_first;
744         for (; set != NULL && set->setId < setId; set = set->entries.le_next);
745
746         if (set && set->setId == setId) {
747
748                 msg = set->msghead.lh_first;
749                 while (msg) {
750                         free(msg->str);
751                         LIST_REMOVE(msg, entries);
752                 }
753
754                 LIST_REMOVE(set, entries);
755                 return;
756         }
757         warning(NULL, "specified set doesn't exist");
758 }
759
760 void
761 MCDelMsg(int msgId)
762 {
763         struct _msgT *msg;
764
765         if (!curSet)
766                 error("you can't delete a message before defining the set");
767
768         msg = curSet->msghead.lh_first;
769         for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
770
771         if (msg && msg->msgId == msgId) {
772                 free(msg->str);
773                 LIST_REMOVE(msg, entries);
774                 return;
775         }
776         warning(NULL, "specified msg doesn't exist");
777 }