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