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