Home | History | Annotate | Line # | Download | only in gencat
gencat.c revision 1.9
      1 /*	$NetBSD: gencat.c,v 1.9 1998/10/09 17:00:56 itohy 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.9 1998/10/09 17:00:56 itohy 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((unsigned char) *cptr)) {
    298 		warning(cptr, "expected a space");
    299 		return (cptr);
    300 	}
    301 	while (*cptr && isspace((unsigned char) *cptr))
    302 		++cptr;
    303 	return (cptr);
    304 }
    305 
    306 static char *
    307 cskip(cptr)
    308 	char   *cptr;
    309 {
    310 	if (!*cptr || isspace((unsigned char) *cptr)) {
    311 		warning(cptr, "wasn't expecting a space");
    312 		return (cptr);
    313 	}
    314 	while (*cptr && !isspace((unsigned char) *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((unsigned char) *tmp) || *wskip(tmp))) {
    349 				warning(cptr, "unexpected quote character, ignoring");
    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 (quote && *cptr == quote) {
    397 						*tptr++ = *cptr++;
    398 					} else if (isdigit((unsigned char) *cptr)) {
    399 						*tptr = 0;
    400 						for (i = 0; i < 3; ++i) {
    401 							if (!isdigit((unsigned char) *cptr))
    402 								break;
    403 							if (*cptr > '7')
    404 								warning(cptr, "octal number greater than 7?!");
    405 							*tptr *= 8;
    406 							*tptr += (*cptr - '0');
    407 							++cptr;
    408 						}
    409 					} else {
    410 						warning(cptr, "unrecognized escape sequence");
    411 					}
    412 					break;
    413 				}
    414 			} else {
    415 				*tptr++ = *cptr++;
    416 			}
    417 	}
    418 	*tptr = '\0';
    419 	return (msg);
    420 }
    421 
    422 void
    423 MCParse(fd)
    424 	int     fd;
    425 {
    426 	char   *cptr, *str;
    427 	int     setid, msgid = 0;
    428 	char    quote = 0;
    429 
    430 	/* XXX: init sethead? */
    431 
    432 	while ((cptr = getline(fd))) {
    433 		if (*cptr == '$') {
    434 			++cptr;
    435 			if (strncmp(cptr, "set", 3) == 0) {
    436 				cptr += 3;
    437 				cptr = wskip(cptr);
    438 				setid = atoi(cptr);
    439 				MCAddSet(setid);
    440 				msgid = 0;
    441 			} else if (strncmp(cptr, "delset", 6) == 0) {
    442 				cptr += 6;
    443 				cptr = wskip(cptr);
    444 				setid = atoi(cptr);
    445 				MCDelSet(setid);
    446 			} else if (strncmp(cptr, "quote", 5) == 0) {
    447 				cptr += 5;
    448 				if (!*cptr)
    449 					quote = 0;
    450 				else {
    451 					cptr = wskip(cptr);
    452 					if (!*cptr)
    453 						quote = 0;
    454 					else
    455 						quote = *cptr;
    456 				}
    457 			} else if (isspace((unsigned char) *cptr)) {
    458 				;
    459 			} else {
    460 				if (*cptr) {
    461 					cptr = wskip(cptr);
    462 					if (*cptr)
    463 						warning(cptr, "unrecognized line");
    464 				}
    465 			}
    466 		} else {
    467 			/*
    468 			 * First check for (and eat) empty lines....
    469 			 */
    470 			if (!*cptr)
    471 				continue;
    472 			/*
    473 			 * We have a digit? Start of a message. Else,
    474 			 * syntax error.
    475 			 */
    476 			if (isdigit((unsigned char) *cptr)) {
    477 				msgid = atoi(cptr);
    478 				cptr = cskip(cptr);
    479 				cptr = wskip(cptr);
    480 				/* if (*cptr) ++cptr; */
    481 			} else {
    482 				warning(cptr, "neither blank line nor start of a message id");
    483 				continue;
    484 			}
    485 			/*
    486 			 * If we have a message ID, but no message,
    487 			 * then this means "delete this message id
    488 			 * from the catalog".
    489 			 */
    490 			if (!*cptr) {
    491 				MCDelMsg(msgid);
    492 			} else {
    493 				str = getmsg(fd, cptr, quote);
    494 				MCAddMsg(msgid, str);
    495 			}
    496 		}
    497 	}
    498 }
    499 
    500 void
    501 MCReadCat(fd)
    502 	int     fd;
    503 {
    504 #if 0
    505 	MCHeaderT mcHead;
    506 	MCMsgT  mcMsg;
    507 	MCSetT  mcSet;
    508 	msgT   *msg;
    509 	setT   *set;
    510 	int     i;
    511 	char   *data;
    512 
    513 	/* XXX init sethead? */
    514 
    515 	if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
    516 		corrupt();
    517 	if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
    518 		corrupt();
    519 	if (mcHead.majorVer != MCMajorVer)
    520 		error(NULL, "unrecognized catalog version");
    521 	if ((mcHead.flags & MCGetByteOrder()) == 0)
    522 		error(NULL, "wrong byte order");
    523 
    524 	if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
    525 		corrupt();
    526 
    527 	for (;;) {
    528 		if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
    529 			corrupt();
    530 		if (mcSet.invalid)
    531 			continue;
    532 
    533 		set = xmalloc(sizeof(setT));
    534 		memset(set, '\0', sizeof(*set));
    535 		if (cat->first) {
    536 			cat->last->next = set;
    537 			set->prev = cat->last;
    538 			cat->last = set;
    539 		} else
    540 			cat->first = cat->last = set;
    541 
    542 		set->setId = mcSet.setId;
    543 
    544 		/* Get the data */
    545 		if (mcSet.dataLen) {
    546 			data = xmalloc(mcSet.dataLen);
    547 			if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
    548 				corrupt();
    549 			if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
    550 				corrupt();
    551 			if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
    552 				corrupt();
    553 
    554 			for (i = 0; i < mcSet.numMsgs; ++i) {
    555 				if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
    556 					corrupt();
    557 				if (mcMsg.invalid) {
    558 					--i;
    559 					continue;
    560 				}
    561 				msg = xmalloc(sizeof(msgT));
    562 				memset(msg, '\0', sizeof(*msg));
    563 				if (set->first) {
    564 					set->last->next = msg;
    565 					msg->prev = set->last;
    566 					set->last = msg;
    567 				} else
    568 					set->first = set->last = msg;
    569 
    570 				msg->msgId = mcMsg.msgId;
    571 				msg->str = xstrdup((char *) (data + mcMsg.msg.off));
    572 			}
    573 			free(data);
    574 		}
    575 		if (!mcSet.nextSet)
    576 			break;
    577 		if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
    578 			corrupt();
    579 	}
    580 #endif
    581 }
    582 
    583 /*
    584  * Write message catalog.
    585  *
    586  * The message catalog is first converted from its internal to its
    587  * external representation in a chunk of memory allocated for this
    588  * purpose.  Then the completed catalog is written.  This approach
    589  * avoids additional housekeeping variables and/or a lot of seeks
    590  * that would otherwise be required.
    591  */
    592 void
    593 MCWriteCat(fd)
    594 	int     fd;
    595 {
    596 	int     nsets;		/* number of sets */
    597 	int     nmsgs;		/* number of msgs */
    598 	int     string_size;	/* total size of string pool */
    599 	int     msgcat_size;	/* total size of message catalog */
    600 	void   *msgcat;		/* message catalog data */
    601 	struct _nls_cat_hdr *cat_hdr;
    602 	struct _nls_set_hdr *set_hdr;
    603 	struct _nls_msg_hdr *msg_hdr;
    604 	char   *strings;
    605 	struct _setT *set;
    606 	struct _msgT *msg;
    607 	int     msg_index;
    608 	int     msg_offset;
    609 
    610 	/* determine number of sets, number of messages, and size of the
    611 	 * string pool */
    612 	nsets = 0;
    613 	nmsgs = 0;
    614 	string_size = 0;
    615 
    616 	for (set = sethead.lh_first; set != NULL;
    617 	    set = set->entries.le_next) {
    618 		nsets++;
    619 
    620 		for (msg = set->msghead.lh_first; msg != NULL;
    621 		    msg = msg->entries.le_next) {
    622 			nmsgs++;
    623 			string_size += strlen(msg->str) + 1;
    624 		}
    625 	}
    626 
    627 #ifdef DEBUG
    628 	printf("number of sets: %d\n", nsets);
    629 	printf("number of msgs: %d\n", nmsgs);
    630 	printf("string pool size: %d\n", string_size);
    631 #endif
    632 
    633 	/* determine size and then allocate buffer for constructing external
    634 	 * message catalog representation */
    635 	msgcat_size = sizeof(struct _nls_cat_hdr)
    636 	    + (nsets * sizeof(struct _nls_set_hdr))
    637 	    + (nmsgs * sizeof(struct _nls_msg_hdr))
    638 	    + string_size;
    639 
    640 	msgcat = xmalloc(msgcat_size);
    641 	memset(msgcat, '\0', msgcat_size);
    642 
    643 	/* fill in msg catalog header */
    644 	cat_hdr = (struct _nls_cat_hdr *) msgcat;
    645 	cat_hdr->__magic = htonl(_NLS_MAGIC);
    646 	cat_hdr->__nsets = htonl(nsets);
    647 	cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
    648 	cat_hdr->__msg_hdr_offset =
    649 	    htonl(nsets * sizeof(struct _nls_set_hdr));
    650 	cat_hdr->__msg_txt_offset =
    651 	    htonl(nsets * sizeof(struct _nls_set_hdr) +
    652 	    nmsgs * sizeof(struct _nls_msg_hdr));
    653 
    654 	/* compute offsets for set & msg header tables and string pool */
    655 	set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
    656 	    sizeof(struct _nls_cat_hdr));
    657 	msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
    658 	    sizeof(struct _nls_cat_hdr) +
    659 	    nsets * sizeof(struct _nls_set_hdr));
    660 	strings = (char *) msgcat +
    661 	    sizeof(struct _nls_cat_hdr) +
    662 	    nsets * sizeof(struct _nls_set_hdr) +
    663 	    nmsgs * sizeof(struct _nls_msg_hdr);
    664 
    665 	msg_index = 0;
    666 	msg_offset = 0;
    667 	for (set = sethead.lh_first; set != NULL;
    668 	    set = set->entries.le_next) {
    669 
    670 		nmsgs = 0;
    671 		for (msg = set->msghead.lh_first; msg != NULL;
    672 		    msg = msg->entries.le_next) {
    673 			int     msg_len = strlen(msg->str) + 1;
    674 
    675 			msg_hdr->__msgno = htonl(msg->msgId);
    676 			msg_hdr->__msglen = htonl(msg_len);
    677 			msg_hdr->__offset = htonl(msg_offset);
    678 
    679 			memcpy(strings, msg->str, msg_len);
    680 			strings += msg_len;
    681 			msg_offset += msg_len;
    682 
    683 			nmsgs++;
    684 			msg_hdr++;
    685 		}
    686 
    687 		set_hdr->__setno = htonl(set->setId);
    688 		set_hdr->__nmsgs = htonl(nmsgs);
    689 		set_hdr->__index = htonl(msg_index);
    690 		msg_index += nmsgs;
    691 		set_hdr++;
    692 	}
    693 
    694 	/* write out catalog.  XXX: should this be done in small chunks? */
    695 	write(fd, msgcat, msgcat_size);
    696 }
    697 
    698 void
    699 MCAddSet(setId)
    700 	int     setId;
    701 {
    702 	struct _setT *p, *q;
    703 
    704 	if (setId <= 0) {
    705 		error(NULL, "setId's must be greater than zero");
    706 		/* NOTREACHED */
    707 	}
    708 #if 0
    709 	/* XXX */
    710 	if (setId > NL_SETMAX) {
    711 		error(NULL, "setId %d exceeds limit (%d)");
    712 		/* NOTREACHED */
    713 	}
    714 #endif
    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 0
    754 	/* XXX */
    755 	if (msgId > NL_SETMAX) {
    756 		error(NULL, "msgID %d exceeds limit (%d)");
    757 		/* NOTREACHED */
    758 	}
    759 #endif
    760 
    761 	p = curSet->msghead.lh_first;
    762 	q = NULL;
    763 	for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
    764 
    765 	if (p && p->msgId == msgId) {
    766 		free(p->str);
    767 	} else {
    768 		p = xmalloc(sizeof(struct _msgT));
    769 		memset(p, '\0', sizeof(struct _msgT));
    770 
    771 		if (q == NULL) {
    772 			LIST_INSERT_HEAD(&curSet->msghead, p, entries);
    773 		} else {
    774 			LIST_INSERT_AFTER(q, p, entries);
    775 		}
    776 	}
    777 
    778 	p->msgId = msgId;
    779 	p->str = xstrdup(str);
    780 }
    781 
    782 void
    783 MCDelSet(setId)
    784 	int     setId;
    785 {
    786 	struct _setT *set;
    787 	struct _msgT *msg;
    788 
    789 	set = sethead.lh_first;
    790 	for (; set != NULL && set->setId < setId; set = set->entries.le_next);
    791 
    792 	if (set && set->setId == setId) {
    793 
    794 		msg = set->msghead.lh_first;
    795 		while (msg) {
    796 			free(msg->str);
    797 			LIST_REMOVE(msg, entries);
    798 		}
    799 
    800 		LIST_REMOVE(set, entries);
    801 		return;
    802 	}
    803 	warning(NULL, "specified set doesn't exist");
    804 }
    805 
    806 void
    807 MCDelMsg(msgId)
    808 	int     msgId;
    809 {
    810 	struct _msgT *msg;
    811 
    812 	if (!curSet)
    813 		error(NULL, "you can't delete a message before defining the set");
    814 
    815 	msg = curSet->msghead.lh_first;
    816 	for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
    817 
    818 	if (msg && msg->msgId == msgId) {
    819 		free(msg->str);
    820 		LIST_REMOVE(msg, entries);
    821 		return;
    822 	}
    823 	warning(NULL, "specified msg doesn't exist");
    824 }
    825