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