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