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