This is needed for compiling catalog files used in catgets(3) calls. Signed-off-by: Yaakov Selkowitz <yselkowi@redhat.com>
		
			
				
	
	
		
			697 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			697 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ex:ts=4
 | |
|  */
 | |
| 
 | |
| /*	$NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $	*/
 | |
| 
 | |
| /*-
 | |
|  * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND MIT
 | |
|  *
 | |
|  * Copyright (c) 1996 The NetBSD Foundation, Inc.
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * This code is derived from software contributed to The NetBSD Foundation
 | |
|  * by J.T. Conklin.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 | |
|  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | |
|  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 | |
|  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
|  * POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| /***********************************************************
 | |
| Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
 | |
| 
 | |
|                         All Rights Reserved
 | |
| 
 | |
| Permission to use, copy, modify, and distribute this software and its
 | |
| documentation for any purpose and without fee is hereby granted,
 | |
| provided that the above copyright notice appear in all copies and that
 | |
| both that copyright notice and this permission notice appear in
 | |
| supporting documentation, and that Alfalfa's name not be used in
 | |
| advertising or publicity pertaining to distribution of the software
 | |
| without specific, written prior permission.
 | |
| 
 | |
| ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 | |
| ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 | |
| ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 | |
| ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 | |
| WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 | |
| ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 | |
| SOFTWARE.
 | |
| 
 | |
| If you make any modifications, bugfixes or other changes to this software
 | |
| we'd appreciate it if you could send a copy to us so we can keep things
 | |
| up-to-date.  Many thanks.
 | |
| 				Kee Hinckley
 | |
| 				Alfalfa Software, Inc.
 | |
| 				267 Allston St., #3
 | |
| 				Cambridge, MA 02139  USA
 | |
| 				nazgul@alfalfa.com
 | |
| 
 | |
| ******************************************************************/
 | |
| 
 | |
| #include <sys/cdefs.h>
 | |
| __FBSDID("$FreeBSD$");
 | |
| 
 | |
| #define _NLS_PRIVATE
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/queue.h>
 | |
| 
 | |
| #include <arpa/inet.h>		/* for htonl() */
 | |
| 
 | |
| #include <ctype.h>
 | |
| #include <err.h>
 | |
| #include <fcntl.h>
 | |
| #include <limits.h>
 | |
| #include <nl_types.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| struct _msgT {
 | |
| 	long    msgId;
 | |
| 	char   *str;
 | |
| 	LIST_ENTRY(_msgT) entries;
 | |
| };
 | |
| 
 | |
| struct _setT {
 | |
| 	long    setId;
 | |
| 	LIST_HEAD(msghead, _msgT) msghead;
 | |
| 	LIST_ENTRY(_setT) entries;
 | |
| };
 | |
| 
 | |
| static LIST_HEAD(sethead, _setT) sethead;
 | |
| static struct _setT *curSet;
 | |
| 
 | |
| static char *curline = NULL;
 | |
| static long lineno = 0;
 | |
| 
 | |
| static	char   *cskip(char *);
 | |
| static	void	error(const char *);
 | |
| static	char   *get_line(int);
 | |
| static	char   *getmsg(int, char *, char);
 | |
| static	void	warning(const char *, const char *);
 | |
| static	char   *wskip(char *);
 | |
| static	char   *xstrdup(const char *);
 | |
| static	void   *xmalloc(size_t);
 | |
| static	void   *xrealloc(void *, size_t);
 | |
| 
 | |
| void	MCParse(int);
 | |
| void	MCReadCat(int);
 | |
| void	MCWriteCat(int);
 | |
| void	MCDelMsg(int);
 | |
| void	MCAddMsg(int, const char *);
 | |
| void	MCAddSet(int);
 | |
| void	MCDelSet(int);
 | |
| void	usage(void);
 | |
| int	main(int, char **);
 | |
| 
 | |
| void
 | |
| usage(void)
 | |
| {
 | |
| 	fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| int
 | |
| main(int argc, char **argv)
 | |
| {
 | |
| 	int     ofd, ifd;
 | |
| 	char	*catfile = NULL;
 | |
| 	int     c;
 | |
| 
 | |
| #define DEPRECATEDMSG	1
 | |
| 
 | |
| #ifdef DEPRECATEDMSG
 | |
| 	while ((c = getopt(argc, argv, "new")) != -1) {
 | |
| #else
 | |
| 	while ((c = getopt(argc, argv, "")) != -1) {
 | |
| #endif
 | |
| 		switch (c) {
 | |
| #ifdef DEPRECATEDMSG
 | |
| 		case 'n':
 | |
| 			fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
 | |
| 		case 'e':
 | |
| 		case 'w':
 | |
| 			break;
 | |
| #endif
 | |
| 		case '?':
 | |
| 		default:
 | |
| 			usage();
 | |
| 			/* NOTREACHED */
 | |
| 		}
 | |
| 	}
 | |
| 	argc -= optind;
 | |
| 	argv += optind;
 | |
| 
 | |
| 	if (argc < 2) {
 | |
| 		usage();
 | |
| 		/* NOTREACHED */
 | |
| 	}
 | |
| 	catfile = *argv++;
 | |
| 
 | |
| 	for (; *argv; argv++) {
 | |
| 		if ((ifd = open(*argv, O_RDONLY)) < 0)
 | |
| 			err(1, "Unable to read %s", *argv);
 | |
| 		MCParse(ifd);
 | |
| 		close(ifd);
 | |
| 	}
 | |
| 
 | |
| 	if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
 | |
| 		err(1, "Unable to create a new %s", catfile);
 | |
| 	MCWriteCat(ofd);
 | |
| 	exit(0);
 | |
| }
 | |
| 
 | |
| static void
 | |
| warning(const char *cptr, const char *msg)
 | |
| {
 | |
| 	fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
 | |
| 	fprintf(stderr, "%s\n", curline);
 | |
| 	if (cptr) {
 | |
| 		char   *tptr;
 | |
| 		for (tptr = curline; tptr < cptr; ++tptr)
 | |
| 			putc(' ', stderr);
 | |
| 		fprintf(stderr, "^\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #define	CORRUPT()	{ error("corrupt message catalog"); }
 | |
| #define	NOMEM()		{ error("out of memory"); }
 | |
| 
 | |
| static void
 | |
| error(const char *msg)
 | |
| {
 | |
| 	warning(NULL, msg);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| static void *
 | |
| xmalloc(size_t len)
 | |
| {
 | |
| 	void   *p;
 | |
| 
 | |
| 	if ((p = malloc(len)) == NULL)
 | |
| 		NOMEM();
 | |
| 	return (p);
 | |
| }
 | |
| 
 | |
| static void *
 | |
| xrealloc(void *ptr, size_t size)
 | |
| {
 | |
| 	if ((ptr = realloc(ptr, size)) == NULL)
 | |
| 		NOMEM();
 | |
| 	return (ptr);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| xstrdup(const char *str)
 | |
| {
 | |
| 	char *nstr;
 | |
| 
 | |
| 	if ((nstr = strdup(str)) == NULL)
 | |
| 		NOMEM();
 | |
| 	return (nstr);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| get_line(int fd)
 | |
| {
 | |
| 	static long curlen = BUFSIZ;
 | |
| 	static char buf[BUFSIZ], *bptr = buf, *bend = buf;
 | |
| 	char   *cptr, *cend;
 | |
| 	long    buflen;
 | |
| 
 | |
| 	if (!curline) {
 | |
| 		curline = xmalloc(curlen);
 | |
| 	}
 | |
| 	++lineno;
 | |
| 
 | |
| 	cptr = curline;
 | |
| 	cend = curline + curlen;
 | |
| 	for (;;) {
 | |
| 		for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
 | |
| 			if (*bptr == '\n') {
 | |
| 				*cptr = '\0';
 | |
| 				++bptr;
 | |
| 				return (curline);
 | |
| 			} else
 | |
| 				*cptr = *bptr;
 | |
| 		}
 | |
| 		if (cptr == cend) {
 | |
| 			cptr = curline = xrealloc(curline, curlen *= 2);
 | |
| 			cend = curline + curlen;
 | |
| 		}
 | |
| 		if (bptr == bend) {
 | |
| 			buflen = read(fd, buf, BUFSIZ);
 | |
| 			if (buflen <= 0) {
 | |
| 				if (cptr > curline) {
 | |
| 					*cptr = '\0';
 | |
| 					return (curline);
 | |
| 				}
 | |
| 				return (NULL);
 | |
| 			}
 | |
| 			bend = buf + buflen;
 | |
| 			bptr = buf;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static char *
 | |
| wskip(char *cptr)
 | |
| {
 | |
| 	if (!*cptr || !isspace((unsigned char) *cptr)) {
 | |
| 		warning(cptr, "expected a space");
 | |
| 		return (cptr);
 | |
| 	}
 | |
| 	while (*cptr && isspace((unsigned char) *cptr))
 | |
| 		++cptr;
 | |
| 	return (cptr);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| cskip(char *cptr)
 | |
| {
 | |
| 	if (!*cptr || isspace((unsigned char) *cptr)) {
 | |
| 		warning(cptr, "wasn't expecting a space");
 | |
| 		return (cptr);
 | |
| 	}
 | |
| 	while (*cptr && !isspace((unsigned char) *cptr))
 | |
| 		++cptr;
 | |
| 	return (cptr);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| getmsg(int fd, char *cptr, char quote)
 | |
| {
 | |
| 	static char *msg = NULL;
 | |
| 	static long msglen = 0;
 | |
| 	long    clen, i;
 | |
| 	char   *tptr;
 | |
| 
 | |
| 	if (quote && *cptr == quote) {
 | |
| 		++cptr;
 | |
| 	}
 | |
| 
 | |
| 	clen = strlen(cptr) + 1;
 | |
| 	if (clen > msglen) {
 | |
| 		if (msglen)
 | |
| 			msg = xrealloc(msg, clen);
 | |
| 		else
 | |
| 			msg = xmalloc(clen);
 | |
| 		msglen = clen;
 | |
| 	}
 | |
| 	tptr = msg;
 | |
| 
 | |
| 	while (*cptr) {
 | |
| 		if (quote && *cptr == quote) {
 | |
| 			char   *tmp;
 | |
| 			tmp = cptr + 1;
 | |
| 			if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
 | |
| 				warning(cptr, "unexpected quote character, ignoring");
 | |
| 				*tptr++ = *cptr++;
 | |
| 			} else {
 | |
| 				*cptr = '\0';
 | |
| 			}
 | |
| 		} else
 | |
| 			if (*cptr == '\\') {
 | |
| 				++cptr;
 | |
| 				switch (*cptr) {
 | |
| 				case '\0':
 | |
| 					cptr = get_line(fd);
 | |
| 					if (!cptr)
 | |
| 						error("premature end of file");
 | |
| 					msglen += strlen(cptr);
 | |
| 					i = tptr - msg;
 | |
| 					msg = xrealloc(msg, msglen);
 | |
| 					tptr = msg + i;
 | |
| 					break;
 | |
| 
 | |
| 		#define	CASEOF(CS, CH)		\
 | |
| 			case CS:		\
 | |
| 				*tptr++ = CH;	\
 | |
| 				++cptr;		\
 | |
| 				break;		\
 | |
| 
 | |
| 				CASEOF('n', '\n');
 | |
| 				CASEOF('t', '\t');
 | |
| 				CASEOF('v', '\v');
 | |
| 				CASEOF('b', '\b');
 | |
| 				CASEOF('r', '\r');
 | |
| 				CASEOF('f', '\f');
 | |
| 				CASEOF('"', '"');
 | |
| 				CASEOF('\\', '\\');
 | |
| 
 | |
| 				default:
 | |
| 					if (quote && *cptr == quote) {
 | |
| 						*tptr++ = *cptr++;
 | |
| 					} else if (isdigit((unsigned char) *cptr)) {
 | |
| 						*tptr = 0;
 | |
| 						for (i = 0; i < 3; ++i) {
 | |
| 							if (!isdigit((unsigned char) *cptr))
 | |
| 								break;
 | |
| 							if (*cptr > '7')
 | |
| 								warning(cptr, "octal number greater than 7?!");
 | |
| 							*tptr *= 8;
 | |
| 							*tptr += (*cptr - '0');
 | |
| 							++cptr;
 | |
| 						}
 | |
| 					} else {
 | |
| 						warning(cptr, "unrecognized escape sequence");
 | |
| 					}
 | |
| 					break;
 | |
| 				}
 | |
| 			} else {
 | |
| 				*tptr++ = *cptr++;
 | |
| 			}
 | |
| 	}
 | |
| 	*tptr = '\0';
 | |
| 	return (msg);
 | |
| }
 | |
| 
 | |
| void
 | |
| MCParse(int fd)
 | |
| {
 | |
| 	char   *cptr, *str;
 | |
| 	int     setid, msgid = 0;
 | |
| 	char    quote = 0;
 | |
| 
 | |
| 	/* XXX: init sethead? */
 | |
| 
 | |
| 	while ((cptr = get_line(fd))) {
 | |
| 		if (*cptr == '$') {
 | |
| 			++cptr;
 | |
| 			if (strncmp(cptr, "set", 3) == 0) {
 | |
| 				cptr += 3;
 | |
| 				cptr = wskip(cptr);
 | |
| 				setid = atoi(cptr);
 | |
| 				MCAddSet(setid);
 | |
| 				msgid = 0;
 | |
| 			} else if (strncmp(cptr, "delset", 6) == 0) {
 | |
| 				cptr += 6;
 | |
| 				cptr = wskip(cptr);
 | |
| 				setid = atoi(cptr);
 | |
| 				MCDelSet(setid);
 | |
| 			} else if (strncmp(cptr, "quote", 5) == 0) {
 | |
| 				cptr += 5;
 | |
| 				if (!*cptr)
 | |
| 					quote = 0;
 | |
| 				else {
 | |
| 					cptr = wskip(cptr);
 | |
| 					if (!*cptr)
 | |
| 						quote = 0;
 | |
| 					else
 | |
| 						quote = *cptr;
 | |
| 				}
 | |
| 			} else if (isspace((unsigned char) *cptr)) {
 | |
| 				;
 | |
| 			} else {
 | |
| 				if (*cptr) {
 | |
| 					cptr = wskip(cptr);
 | |
| 					if (*cptr)
 | |
| 						warning(cptr, "unrecognized line");
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			/*
 | |
| 			 * First check for (and eat) empty lines....
 | |
| 			 */
 | |
| 			if (!*cptr)
 | |
| 				continue;
 | |
| 			/*
 | |
| 			 * We have a digit? Start of a message. Else,
 | |
| 			 * syntax error.
 | |
| 			 */
 | |
| 			if (isdigit((unsigned char) *cptr)) {
 | |
| 				msgid = atoi(cptr);
 | |
| 				cptr = cskip(cptr);
 | |
| 				cptr = wskip(cptr);
 | |
| 				/* if (*cptr) ++cptr; */
 | |
| 			} else {
 | |
| 				warning(cptr, "neither blank line nor start of a message id");
 | |
| 				continue;
 | |
| 			}
 | |
| 			/*
 | |
| 			 * If we have a message ID, but no message,
 | |
| 			 * then this means "delete this message id
 | |
| 			 * from the catalog".
 | |
| 			 */
 | |
| 			if (!*cptr) {
 | |
| 				MCDelMsg(msgid);
 | |
| 			} else {
 | |
| 				str = getmsg(fd, cptr, quote);
 | |
| 				MCAddMsg(msgid, str);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Write message catalog.
 | |
|  *
 | |
|  * The message catalog is first converted from its internal to its
 | |
|  * external representation in a chunk of memory allocated for this
 | |
|  * purpose.  Then the completed catalog is written.  This approach
 | |
|  * avoids additional housekeeping variables and/or a lot of seeks
 | |
|  * that would otherwise be required.
 | |
|  */
 | |
| void
 | |
| MCWriteCat(int fd)
 | |
| {
 | |
| 	int     nsets;		/* number of sets */
 | |
| 	int     nmsgs;		/* number of msgs */
 | |
| 	int     string_size;	/* total size of string pool */
 | |
| 	int     msgcat_size;	/* total size of message catalog */
 | |
| 	void   *msgcat;		/* message catalog data */
 | |
| 	struct _nls_cat_hdr *cat_hdr;
 | |
| 	struct _nls_set_hdr *set_hdr;
 | |
| 	struct _nls_msg_hdr *msg_hdr;
 | |
| 	char   *strings;
 | |
| 	struct _setT *set;
 | |
| 	struct _msgT *msg;
 | |
| 	int     msg_index;
 | |
| 	int     msg_offset;
 | |
| 
 | |
| 	/* determine number of sets, number of messages, and size of the
 | |
| 	 * string pool */
 | |
| 	nsets = 0;
 | |
| 	nmsgs = 0;
 | |
| 	string_size = 0;
 | |
| 
 | |
| 	for (set = sethead.lh_first; set != NULL;
 | |
| 	    set = set->entries.le_next) {
 | |
| 		nsets++;
 | |
| 
 | |
| 		for (msg = set->msghead.lh_first; msg != NULL;
 | |
| 		    msg = msg->entries.le_next) {
 | |
| 			nmsgs++;
 | |
| 			string_size += strlen(msg->str) + 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| #ifdef DEBUG
 | |
| 	printf("number of sets: %d\n", nsets);
 | |
| 	printf("number of msgs: %d\n", nmsgs);
 | |
| 	printf("string pool size: %d\n", string_size);
 | |
| #endif
 | |
| 
 | |
| 	/* determine size and then allocate buffer for constructing external
 | |
| 	 * message catalog representation */
 | |
| 	msgcat_size = sizeof(struct _nls_cat_hdr)
 | |
| 	    + (nsets * sizeof(struct _nls_set_hdr))
 | |
| 	    + (nmsgs * sizeof(struct _nls_msg_hdr))
 | |
| 	    + string_size;
 | |
| 
 | |
| 	msgcat = xmalloc(msgcat_size);
 | |
| 	memset(msgcat, '\0', msgcat_size);
 | |
| 
 | |
| 	/* fill in msg catalog header */
 | |
| 	cat_hdr = (struct _nls_cat_hdr *) msgcat;
 | |
| 	cat_hdr->__magic = htonl(_NLS_MAGIC);
 | |
| 	cat_hdr->__nsets = htonl(nsets);
 | |
| 	cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
 | |
| 	cat_hdr->__msg_hdr_offset =
 | |
| 	    htonl(nsets * sizeof(struct _nls_set_hdr));
 | |
| 	cat_hdr->__msg_txt_offset =
 | |
| 	    htonl(nsets * sizeof(struct _nls_set_hdr) +
 | |
| 	    nmsgs * sizeof(struct _nls_msg_hdr));
 | |
| 
 | |
| 	/* compute offsets for set & msg header tables and string pool */
 | |
| 	set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
 | |
| 	    sizeof(struct _nls_cat_hdr));
 | |
| 	msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
 | |
| 	    sizeof(struct _nls_cat_hdr) +
 | |
| 	    nsets * sizeof(struct _nls_set_hdr));
 | |
| 	strings = (char *) msgcat +
 | |
| 	    sizeof(struct _nls_cat_hdr) +
 | |
| 	    nsets * sizeof(struct _nls_set_hdr) +
 | |
| 	    nmsgs * sizeof(struct _nls_msg_hdr);
 | |
| 
 | |
| 	msg_index = 0;
 | |
| 	msg_offset = 0;
 | |
| 	for (set = sethead.lh_first; set != NULL;
 | |
| 	    set = set->entries.le_next) {
 | |
| 
 | |
| 		nmsgs = 0;
 | |
| 		for (msg = set->msghead.lh_first; msg != NULL;
 | |
| 		    msg = msg->entries.le_next) {
 | |
| 			int     msg_len = strlen(msg->str) + 1;
 | |
| 
 | |
| 			msg_hdr->__msgno = htonl(msg->msgId);
 | |
| 			msg_hdr->__msglen = htonl(msg_len);
 | |
| 			msg_hdr->__offset = htonl(msg_offset);
 | |
| 
 | |
| 			memcpy(strings, msg->str, msg_len);
 | |
| 			strings += msg_len;
 | |
| 			msg_offset += msg_len;
 | |
| 
 | |
| 			nmsgs++;
 | |
| 			msg_hdr++;
 | |
| 		}
 | |
| 
 | |
| 		set_hdr->__setno = htonl(set->setId);
 | |
| 		set_hdr->__nmsgs = htonl(nmsgs);
 | |
| 		set_hdr->__index = htonl(msg_index);
 | |
| 		msg_index += nmsgs;
 | |
| 		set_hdr++;
 | |
| 	}
 | |
| 
 | |
| 	/* write out catalog.  XXX: should this be done in small chunks? */
 | |
| 	write(fd, msgcat, msgcat_size);
 | |
| }
 | |
| 
 | |
| void
 | |
| MCAddSet(int setId)
 | |
| {
 | |
| 	struct _setT *p, *q;
 | |
| 
 | |
| 	if (setId <= 0) {
 | |
| 		error("setId's must be greater than zero");
 | |
| 		/* NOTREACHED */
 | |
| 	}
 | |
| 	if (setId > NL_SETMAX) {
 | |
| 		error("setId exceeds limit");
 | |
| 		/* NOTREACHED */
 | |
| 	}
 | |
| 
 | |
| 	p = sethead.lh_first;
 | |
| 	q = NULL;
 | |
| 	for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
 | |
| 
 | |
| 	if (p && p->setId == setId) {
 | |
| 		;
 | |
| 	} else {
 | |
| 		p = xmalloc(sizeof(struct _setT));
 | |
| 		memset(p, '\0', sizeof(struct _setT));
 | |
| 		LIST_INIT(&p->msghead);
 | |
| 
 | |
| 		p->setId = setId;
 | |
| 
 | |
| 		if (q == NULL) {
 | |
| 			LIST_INSERT_HEAD(&sethead, p, entries);
 | |
| 		} else {
 | |
| 			LIST_INSERT_AFTER(q, p, entries);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	curSet = p;
 | |
| }
 | |
| 
 | |
| void
 | |
| MCAddMsg(int msgId, const char *str)
 | |
| {
 | |
| 	struct _msgT *p, *q;
 | |
| 
 | |
| 	if (!curSet)
 | |
| 		error("can't specify a message when no set exists");
 | |
| 
 | |
| 	if (msgId <= 0) {
 | |
| 		error("msgId's must be greater than zero");
 | |
| 		/* NOTREACHED */
 | |
| 	}
 | |
| 	if (msgId > NL_MSGMAX) {
 | |
| 		error("msgID exceeds limit");
 | |
| 		/* NOTREACHED */
 | |
| 	}
 | |
| 
 | |
| 	p = curSet->msghead.lh_first;
 | |
| 	q = NULL;
 | |
| 	for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
 | |
| 
 | |
| 	if (p && p->msgId == msgId) {
 | |
| 		free(p->str);
 | |
| 	} else {
 | |
| 		p = xmalloc(sizeof(struct _msgT));
 | |
| 		memset(p, '\0', sizeof(struct _msgT));
 | |
| 
 | |
| 		if (q == NULL) {
 | |
| 			LIST_INSERT_HEAD(&curSet->msghead, p, entries);
 | |
| 		} else {
 | |
| 			LIST_INSERT_AFTER(q, p, entries);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	p->msgId = msgId;
 | |
| 	p->str = xstrdup(str);
 | |
| }
 | |
| 
 | |
| void
 | |
| MCDelSet(int setId)
 | |
| {
 | |
| 	struct _setT *set;
 | |
| 	struct _msgT *msg;
 | |
| 
 | |
| 	set = sethead.lh_first;
 | |
| 	for (; set != NULL && set->setId < setId; set = set->entries.le_next);
 | |
| 
 | |
| 	if (set && set->setId == setId) {
 | |
| 
 | |
| 		msg = set->msghead.lh_first;
 | |
| 		while (msg) {
 | |
| 			free(msg->str);
 | |
| 			LIST_REMOVE(msg, entries);
 | |
| 		}
 | |
| 
 | |
| 		LIST_REMOVE(set, entries);
 | |
| 		return;
 | |
| 	}
 | |
| 	warning(NULL, "specified set doesn't exist");
 | |
| }
 | |
| 
 | |
| void
 | |
| MCDelMsg(int msgId)
 | |
| {
 | |
| 	struct _msgT *msg;
 | |
| 
 | |
| 	if (!curSet)
 | |
| 		error("you can't delete a message before defining the set");
 | |
| 
 | |
| 	msg = curSet->msghead.lh_first;
 | |
| 	for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
 | |
| 
 | |
| 	if (msg && msg->msgId == msgId) {
 | |
| 		free(msg->str);
 | |
| 		LIST_REMOVE(msg, entries);
 | |
| 		return;
 | |
| 	}
 | |
| 	warning(NULL, "specified msg doesn't exist");
 | |
| }
 |