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