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