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