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