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