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