1142662Sphantom/* ex:ts=4
2142662Sphantom */
3142662Sphantom
4142662Sphantom/*	$NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $	*/
5142662Sphantom
6142662Sphantom/*
7142662Sphantom * Copyright (c) 1996 The NetBSD Foundation, Inc.
8142662Sphantom * All rights reserved.
9142662Sphantom *
10142662Sphantom * This code is derived from software contributed to The NetBSD Foundation
11142662Sphantom * by J.T. Conklin.
12142662Sphantom *
13142662Sphantom * Redistribution and use in source and binary forms, with or without
14142662Sphantom * modification, are permitted provided that the following conditions
15142662Sphantom * are met:
16142662Sphantom * 1. Redistributions of source code must retain the above copyright
17142662Sphantom *    notice, this list of conditions and the following disclaimer.
18142662Sphantom * 2. Redistributions in binary form must reproduce the above copyright
19142662Sphantom *    notice, this list of conditions and the following disclaimer in the
20142662Sphantom *    documentation and/or other materials provided with the distribution.
21142662Sphantom *
22142662Sphantom * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23142662Sphantom * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24142662Sphantom * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25142662Sphantom * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26142662Sphantom * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27142662Sphantom * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28142662Sphantom * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29142662Sphantom * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30142662Sphantom * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31142662Sphantom * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32142662Sphantom * POSSIBILITY OF SUCH DAMAGE.
33142662Sphantom */
34142662Sphantom
357496Sjkh/***********************************************************
367496SjkhCopyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
377496Sjkh
387496Sjkh                        All Rights Reserved
397496Sjkh
407496SjkhPermission to use, copy, modify, and distribute this software and its
417496Sjkhdocumentation for any purpose and without fee is hereby granted,
427496Sjkhprovided that the above copyright notice appear in all copies and that
437496Sjkhboth that copyright notice and this permission notice appear in
447496Sjkhsupporting documentation, and that Alfalfa's name not be used in
457496Sjkhadvertising or publicity pertaining to distribution of the software
467496Sjkhwithout specific, written prior permission.
477496Sjkh
487496SjkhALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
497496SjkhALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
507496SjkhALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
517496SjkhANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
527496SjkhWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
537496SjkhARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
547496SjkhSOFTWARE.
557496Sjkh
567496SjkhIf you make any modifications, bugfixes or other changes to this software
577496Sjkhwe'd appreciate it if you could send a copy to us so we can keep things
587496Sjkhup-to-date.  Many thanks.
597496Sjkh				Kee Hinckley
607496Sjkh				Alfalfa Software, Inc.
617496Sjkh				267 Allston St., #3
627496Sjkh				Cambridge, MA 02139  USA
637496Sjkh				nazgul@alfalfa.com
648874Srgrimes
657496Sjkh******************************************************************/
667496Sjkh
6793218Scharnier#include <sys/cdefs.h>
6893218Scharnier__FBSDID("$FreeBSD$");
6993218Scharnier
70142662Sphantom#define _NLS_PRIVATE
71142662Sphantom
72142662Sphantom#include <sys/types.h>
73142662Sphantom#include <sys/queue.h>
74142662Sphantom
75142662Sphantom#include <arpa/inet.h>		/* for htonl() */
76142662Sphantom
77142662Sphantom#include <ctype.h>
7827274Scharnier#include <err.h>
79142662Sphantom#include <fcntl.h>
80142662Sphantom#include <limits.h>
81142662Sphantom#include <nl_types.h>
827496Sjkh#include <stdio.h>
8378717Sdd#include <stdlib.h>
8478717Sdd#include <string.h>
859251Sache#include <unistd.h>
867496Sjkh
87142662Sphantomstruct _msgT {
88142662Sphantom	long    msgId;
89142662Sphantom	char   *str;
90199236Sedwin	LIST_ENTRY(_msgT) entries;
91142662Sphantom};
927496Sjkh
93142662Sphantomstruct _setT {
94142662Sphantom	long    setId;
95199236Sedwin	LIST_HEAD(msghead, _msgT) msghead;
96199236Sedwin	LIST_ENTRY(_setT) entries;
97142662Sphantom};
987496Sjkh
99241737Sedstatic LIST_HEAD(sethead, _setT) sethead;
100142662Sphantomstatic struct _setT *curSet;
101142662Sphantom
102142662Sphantomstatic char *curline = NULL;
103142662Sphantomstatic long lineno = 0;
104142662Sphantom
105142662Sphantomstatic	char   *cskip(char *);
106142662Sphantomstatic	void	error(const char *);
107142662Sphantomstatic	char   *getline(int);
108142662Sphantomstatic	char   *getmsg(int, char *, char);
109142662Sphantomstatic	void	warning(const char *, const char *);
110142662Sphantomstatic	char   *wskip(char *);
111142662Sphantomstatic	char   *xstrdup(const char *);
112142662Sphantomstatic	void   *xmalloc(size_t);
113142662Sphantomstatic	void   *xrealloc(void *, size_t);
114142662Sphantom
115142662Sphantomvoid	MCParse(int);
116142662Sphantomvoid	MCReadCat(int);
117142662Sphantomvoid	MCWriteCat(int);
118142662Sphantomvoid	MCDelMsg(int);
119142662Sphantomvoid	MCAddMsg(int, const char *);
120142662Sphantomvoid	MCAddSet(int);
121142662Sphantomvoid	MCDelSet(int);
122142662Sphantomvoid	usage(void);
123142662Sphantomint	main(int, char **);
124142662Sphantom
125142662Sphantomvoid
126201227Sedusage(void)
12727274Scharnier{
128142662Sphantom	fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
129199236Sedwin	exit(1);
1307496Sjkh}
1317496Sjkh
13272164Sphantomint
133142662Sphantommain(int argc, char **argv)
1347496Sjkh{
135142662Sphantom	int     ofd, ifd;
136199236Sedwin	char	*catfile = NULL;
137142662Sphantom	int     c;
1388874Srgrimes
139142662Sphantom#define DEPRECATEDMSG	1
140142662Sphantom
141142662Sphantom#ifdef DEPRECATEDMSG
142142662Sphantom	while ((c = getopt(argc, argv, "new")) != -1) {
143142662Sphantom#else
144142662Sphantom	while ((c = getopt(argc, argv, "")) != -1) {
145142662Sphantom#endif
146142662Sphantom		switch (c) {
147142662Sphantom#ifdef DEPRECATEDMSG
148142662Sphantom		case 'n':
149142662Sphantom			fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
150142662Sphantom		case 'e':
151142662Sphantom		case 'w':
152142662Sphantom			break;
153142662Sphantom#endif
154142662Sphantom		case '?':
155142662Sphantom		default:
156142662Sphantom			usage();
157142662Sphantom			/* NOTREACHED */
158142662Sphantom		}
159142662Sphantom	}
160142662Sphantom	argc -= optind;
161142662Sphantom	argv += optind;
162142662Sphantom
163142662Sphantom	if (argc < 2) {
164142662Sphantom		usage();
165142662Sphantom		/* NOTREACHED */
166142662Sphantom	}
167142662Sphantom	catfile = *argv++;
168142662Sphantom
169142662Sphantom	for (; *argv; argv++) {
170142662Sphantom		if ((ifd = open(*argv, O_RDONLY)) < 0)
171142662Sphantom			err(1, "Unable to read %s", *argv);
172142662Sphantom		MCParse(ifd);
173142662Sphantom		close(ifd);
174142662Sphantom	}
175142662Sphantom
176142662Sphantom	if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
177142662Sphantom		err(1, "Unable to create a new %s", catfile);
178142662Sphantom	MCWriteCat(ofd);
179142662Sphantom	exit(0);
180142662Sphantom}
181142662Sphantom
182142662Sphantomstatic void
183142662Sphantomwarning(const char *cptr, const char *msg)
184142662Sphantom{
185142662Sphantom	fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
186142662Sphantom	fprintf(stderr, "%s\n", curline);
187142662Sphantom	if (cptr) {
188142662Sphantom		char   *tptr;
189142662Sphantom		for (tptr = curline; tptr < cptr; ++tptr)
190142662Sphantom			putc(' ', stderr);
191142662Sphantom		fprintf(stderr, "^\n");
192142662Sphantom	}
193142662Sphantom}
194142662Sphantom
195142662Sphantom#define	CORRUPT()	{ error("corrupt message catalog"); }
196142662Sphantom#define	NOMEM()		{ error("out of memory"); }
197142662Sphantom
198142662Sphantomstatic void
199142662Sphantomerror(const char *msg)
200142662Sphantom{
201142662Sphantom	warning(NULL, msg);
202142662Sphantom	exit(1);
203142662Sphantom}
204142662Sphantom
205142662Sphantomstatic void *
206142662Sphantomxmalloc(size_t len)
207142662Sphantom{
208142662Sphantom	void   *p;
209142662Sphantom
210142662Sphantom	if ((p = malloc(len)) == NULL)
211142662Sphantom		NOMEM();
212142662Sphantom	return (p);
213142662Sphantom}
214142662Sphantom
215142662Sphantomstatic void *
216142662Sphantomxrealloc(void *ptr, size_t size)
217142662Sphantom{
218142662Sphantom	if ((ptr = realloc(ptr, size)) == NULL)
219142662Sphantom		NOMEM();
220142662Sphantom	return (ptr);
221142662Sphantom}
222142662Sphantom
223142662Sphantomstatic char *
224142662Sphantomxstrdup(const char *str)
225142662Sphantom{
226142662Sphantom	char *nstr;
227142662Sphantom
228142662Sphantom	if ((nstr = strdup(str)) == NULL)
229142662Sphantom		NOMEM();
230142662Sphantom	return (nstr);
231142662Sphantom}
232142662Sphantom
233142662Sphantomstatic char *
234142662Sphantomgetline(int fd)
235142662Sphantom{
236142662Sphantom	static long curlen = BUFSIZ;
237142662Sphantom	static char buf[BUFSIZ], *bptr = buf, *bend = buf;
238142662Sphantom	char   *cptr, *cend;
239142662Sphantom	long    buflen;
240142662Sphantom
241142662Sphantom	if (!curline) {
242142662Sphantom		curline = xmalloc(curlen);
243142662Sphantom	}
244142662Sphantom	++lineno;
245142662Sphantom
246142662Sphantom	cptr = curline;
247142662Sphantom	cend = curline + curlen;
248142662Sphantom	for (;;) {
249142662Sphantom		for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
250142662Sphantom			if (*bptr == '\n') {
251142662Sphantom				*cptr = '\0';
252142662Sphantom				++bptr;
253142662Sphantom				return (curline);
254142662Sphantom			} else
255142662Sphantom				*cptr = *bptr;
256142662Sphantom		}
257142662Sphantom		if (cptr == cend) {
258142662Sphantom			cptr = curline = xrealloc(curline, curlen *= 2);
259142662Sphantom			cend = curline + curlen;
260142662Sphantom		}
261142662Sphantom		if (bptr == bend) {
262142662Sphantom			buflen = read(fd, buf, BUFSIZ);
263142662Sphantom			if (buflen <= 0) {
264142662Sphantom				if (cptr > curline) {
265142662Sphantom					*cptr = '\0';
266142662Sphantom					return (curline);
267142662Sphantom				}
268142662Sphantom				return (NULL);
269142662Sphantom			}
270142662Sphantom			bend = buf + buflen;
271142662Sphantom			bptr = buf;
272142662Sphantom		}
273142662Sphantom	}
274142662Sphantom}
275142662Sphantom
276142662Sphantomstatic char *
277142662Sphantomwskip(char *cptr)
278142662Sphantom{
279142662Sphantom	if (!*cptr || !isspace((unsigned char) *cptr)) {
280142662Sphantom		warning(cptr, "expected a space");
281142662Sphantom		return (cptr);
282142662Sphantom	}
283142662Sphantom	while (*cptr && isspace((unsigned char) *cptr))
284142662Sphantom		++cptr;
285142662Sphantom	return (cptr);
286142662Sphantom}
287142662Sphantom
288142662Sphantomstatic char *
289142662Sphantomcskip(char *cptr)
290142662Sphantom{
291142662Sphantom	if (!*cptr || isspace((unsigned char) *cptr)) {
292142662Sphantom		warning(cptr, "wasn't expecting a space");
293142662Sphantom		return (cptr);
294142662Sphantom	}
295142662Sphantom	while (*cptr && !isspace((unsigned char) *cptr))
296142662Sphantom		++cptr;
297142662Sphantom	return (cptr);
298142662Sphantom}
299142662Sphantom
300142662Sphantomstatic char *
301142662Sphantomgetmsg(int fd, char *cptr, char quote)
302142662Sphantom{
303142662Sphantom	static char *msg = NULL;
304142662Sphantom	static long msglen = 0;
305142662Sphantom	long    clen, i;
306142662Sphantom	char   *tptr;
307142662Sphantom
308142662Sphantom	if (quote && *cptr == quote) {
309142662Sphantom		++cptr;
310142662Sphantom	}
311142662Sphantom
312142662Sphantom	clen = strlen(cptr) + 1;
313142662Sphantom	if (clen > msglen) {
314142662Sphantom		if (msglen)
315142662Sphantom			msg = xrealloc(msg, clen);
316142662Sphantom		else
317142662Sphantom			msg = xmalloc(clen);
318142662Sphantom		msglen = clen;
319142662Sphantom	}
320142662Sphantom	tptr = msg;
321142662Sphantom
322142662Sphantom	while (*cptr) {
323142662Sphantom		if (quote && *cptr == quote) {
324142662Sphantom			char   *tmp;
325142662Sphantom			tmp = cptr + 1;
326142662Sphantom			if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
327142662Sphantom				warning(cptr, "unexpected quote character, ignoring");
328142662Sphantom				*tptr++ = *cptr++;
329142662Sphantom			} else {
330142662Sphantom				*cptr = '\0';
331142662Sphantom			}
332142662Sphantom		} else
333142662Sphantom			if (*cptr == '\\') {
334142662Sphantom				++cptr;
335142662Sphantom				switch (*cptr) {
336142662Sphantom				case '\0':
337142662Sphantom					cptr = getline(fd);
338142662Sphantom					if (!cptr)
339142662Sphantom						error("premature end of file");
340142662Sphantom					msglen += strlen(cptr);
341142662Sphantom					i = tptr - msg;
342142662Sphantom					msg = xrealloc(msg, msglen);
343142662Sphantom					tptr = msg + i;
344142662Sphantom					break;
345142662Sphantom
346142662Sphantom		#define	CASEOF(CS, CH)		\
347142662Sphantom			case CS:		\
348142662Sphantom				*tptr++ = CH;	\
349142662Sphantom				++cptr;		\
350142662Sphantom				break;		\
351142662Sphantom
352142662Sphantom				CASEOF('n', '\n');
353142662Sphantom				CASEOF('t', '\t');
354142662Sphantom				CASEOF('v', '\v');
355142662Sphantom				CASEOF('b', '\b');
356142662Sphantom				CASEOF('r', '\r');
357142662Sphantom				CASEOF('f', '\f');
358142662Sphantom				CASEOF('"', '"');
359142662Sphantom				CASEOF('\\', '\\');
360142662Sphantom
361142662Sphantom				default:
362142662Sphantom					if (quote && *cptr == quote) {
363142662Sphantom						*tptr++ = *cptr++;
364142662Sphantom					} else if (isdigit((unsigned char) *cptr)) {
365142662Sphantom						*tptr = 0;
366142662Sphantom						for (i = 0; i < 3; ++i) {
367142662Sphantom							if (!isdigit((unsigned char) *cptr))
368142662Sphantom								break;
369142662Sphantom							if (*cptr > '7')
370142662Sphantom								warning(cptr, "octal number greater than 7?!");
371142662Sphantom							*tptr *= 8;
372142662Sphantom							*tptr += (*cptr - '0');
373142662Sphantom							++cptr;
374142662Sphantom						}
375142662Sphantom					} else {
376142662Sphantom						warning(cptr, "unrecognized escape sequence");
377142662Sphantom					}
378142662Sphantom					break;
379142662Sphantom				}
380142662Sphantom			} else {
381142662Sphantom				*tptr++ = *cptr++;
382142662Sphantom			}
383142662Sphantom	}
384142662Sphantom	*tptr = '\0';
385142662Sphantom	return (msg);
386142662Sphantom}
387142662Sphantom
388142662Sphantomvoid
389142662SphantomMCParse(int fd)
390142662Sphantom{
391142662Sphantom	char   *cptr, *str;
392142662Sphantom	int     setid, msgid = 0;
393142662Sphantom	char    quote = 0;
394142662Sphantom
395142662Sphantom	/* XXX: init sethead? */
396142662Sphantom
397142662Sphantom	while ((cptr = getline(fd))) {
398142662Sphantom		if (*cptr == '$') {
399142662Sphantom			++cptr;
400142662Sphantom			if (strncmp(cptr, "set", 3) == 0) {
401142662Sphantom				cptr += 3;
402142662Sphantom				cptr = wskip(cptr);
403142662Sphantom				setid = atoi(cptr);
404142662Sphantom				MCAddSet(setid);
405142662Sphantom				msgid = 0;
406142662Sphantom			} else if (strncmp(cptr, "delset", 6) == 0) {
407142662Sphantom				cptr += 6;
408142662Sphantom				cptr = wskip(cptr);
409142662Sphantom				setid = atoi(cptr);
410142662Sphantom				MCDelSet(setid);
411142662Sphantom			} else if (strncmp(cptr, "quote", 5) == 0) {
412142662Sphantom				cptr += 5;
413142662Sphantom				if (!*cptr)
414142662Sphantom					quote = 0;
415199236Sedwin				else {
416142662Sphantom					cptr = wskip(cptr);
417142662Sphantom					if (!*cptr)
418142662Sphantom						quote = 0;
419142662Sphantom					else
420142662Sphantom						quote = *cptr;
421199236Sedwin				}
422142662Sphantom			} else if (isspace((unsigned char) *cptr)) {
423142662Sphantom				;
424199236Sedwin			} else {
425142662Sphantom				if (*cptr) {
426142662Sphantom					cptr = wskip(cptr);
427142662Sphantom					if (*cptr)
428142662Sphantom						warning(cptr, "unrecognized line");
429142662Sphantom				}
430199236Sedwin			}
431199236Sedwin		} else {
432142662Sphantom			/*
433142662Sphantom			 * First check for (and eat) empty lines....
434142662Sphantom			 */
435142662Sphantom			if (!*cptr)
436142662Sphantom				continue;
437142662Sphantom			/*
438142662Sphantom			 * We have a digit? Start of a message. Else,
439142662Sphantom			 * syntax error.
440142662Sphantom			 */
441142662Sphantom			if (isdigit((unsigned char) *cptr)) {
442142662Sphantom				msgid = atoi(cptr);
443142662Sphantom				cptr = cskip(cptr);
444142662Sphantom				cptr = wskip(cptr);
445142662Sphantom				/* if (*cptr) ++cptr; */
446142662Sphantom			} else {
447142662Sphantom				warning(cptr, "neither blank line nor start of a message id");
448142662Sphantom				continue;
449199236Sedwin			}
450142662Sphantom			/*
451142662Sphantom			 * If we have a message ID, but no message,
452142662Sphantom			 * then this means "delete this message id
453142662Sphantom			 * from the catalog".
454142662Sphantom			 */
455142662Sphantom			if (!*cptr) {
456142662Sphantom				MCDelMsg(msgid);
457199236Sedwin			} else {
458142662Sphantom				str = getmsg(fd, cptr, quote);
459142662Sphantom				MCAddMsg(msgid, str);
460199236Sedwin			}
461199236Sedwin		}
4627496Sjkh	}
463142662Sphantom}
464142662Sphantom
465142662Sphantom/*
466142662Sphantom * Write message catalog.
467142662Sphantom *
468142662Sphantom * The message catalog is first converted from its internal to its
469142662Sphantom * external representation in a chunk of memory allocated for this
470142662Sphantom * purpose.  Then the completed catalog is written.  This approach
471142662Sphantom * avoids additional housekeeping variables and/or a lot of seeks
472142662Sphantom * that would otherwise be required.
473142662Sphantom */
474142662Sphantomvoid
475142662SphantomMCWriteCat(int fd)
476142662Sphantom{
477142662Sphantom	int     nsets;		/* number of sets */
478142662Sphantom	int     nmsgs;		/* number of msgs */
479142662Sphantom	int     string_size;	/* total size of string pool */
480142662Sphantom	int     msgcat_size;	/* total size of message catalog */
481142662Sphantom	void   *msgcat;		/* message catalog data */
482142662Sphantom	struct _nls_cat_hdr *cat_hdr;
483142662Sphantom	struct _nls_set_hdr *set_hdr;
484142662Sphantom	struct _nls_msg_hdr *msg_hdr;
485142662Sphantom	char   *strings;
486142662Sphantom	struct _setT *set;
487142662Sphantom	struct _msgT *msg;
488142662Sphantom	int     msg_index;
489142662Sphantom	int     msg_offset;
490142662Sphantom
491142662Sphantom	/* determine number of sets, number of messages, and size of the
492142662Sphantom	 * string pool */
493142662Sphantom	nsets = 0;
494142662Sphantom	nmsgs = 0;
495142662Sphantom	string_size = 0;
496142662Sphantom
497142662Sphantom	for (set = sethead.lh_first; set != NULL;
498142662Sphantom	    set = set->entries.le_next) {
499142662Sphantom		nsets++;
500142662Sphantom
501142662Sphantom		for (msg = set->msghead.lh_first; msg != NULL;
502142662Sphantom		    msg = msg->entries.le_next) {
503142662Sphantom			nmsgs++;
504142662Sphantom			string_size += strlen(msg->str) + 1;
505142662Sphantom		}
506142662Sphantom	}
507142662Sphantom
508142662Sphantom#ifdef DEBUG
509142662Sphantom	printf("number of sets: %d\n", nsets);
510142662Sphantom	printf("number of msgs: %d\n", nmsgs);
511142662Sphantom	printf("string pool size: %d\n", string_size);
512142662Sphantom#endif
513142662Sphantom
514142662Sphantom	/* determine size and then allocate buffer for constructing external
515142662Sphantom	 * message catalog representation */
516142662Sphantom	msgcat_size = sizeof(struct _nls_cat_hdr)
517142662Sphantom	    + (nsets * sizeof(struct _nls_set_hdr))
518142662Sphantom	    + (nmsgs * sizeof(struct _nls_msg_hdr))
519142662Sphantom	    + string_size;
520142662Sphantom
521142662Sphantom	msgcat = xmalloc(msgcat_size);
522142662Sphantom	memset(msgcat, '\0', msgcat_size);
523142662Sphantom
524142662Sphantom	/* fill in msg catalog header */
525142662Sphantom	cat_hdr = (struct _nls_cat_hdr *) msgcat;
526142662Sphantom	cat_hdr->__magic = htonl(_NLS_MAGIC);
527142662Sphantom	cat_hdr->__nsets = htonl(nsets);
528142662Sphantom	cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
529142662Sphantom	cat_hdr->__msg_hdr_offset =
530142662Sphantom	    htonl(nsets * sizeof(struct _nls_set_hdr));
531142662Sphantom	cat_hdr->__msg_txt_offset =
532142662Sphantom	    htonl(nsets * sizeof(struct _nls_set_hdr) +
533142662Sphantom	    nmsgs * sizeof(struct _nls_msg_hdr));
534142662Sphantom
535142662Sphantom	/* compute offsets for set & msg header tables and string pool */
536142684Sru	set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
537142662Sphantom	    sizeof(struct _nls_cat_hdr));
538142684Sru	msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
539142662Sphantom	    sizeof(struct _nls_cat_hdr) +
540142662Sphantom	    nsets * sizeof(struct _nls_set_hdr));
541142662Sphantom	strings = (char *) msgcat +
542142662Sphantom	    sizeof(struct _nls_cat_hdr) +
543142662Sphantom	    nsets * sizeof(struct _nls_set_hdr) +
544142662Sphantom	    nmsgs * sizeof(struct _nls_msg_hdr);
545142662Sphantom
546142662Sphantom	msg_index = 0;
547142662Sphantom	msg_offset = 0;
548142662Sphantom	for (set = sethead.lh_first; set != NULL;
549142662Sphantom	    set = set->entries.le_next) {
550142662Sphantom
551142662Sphantom		nmsgs = 0;
552142662Sphantom		for (msg = set->msghead.lh_first; msg != NULL;
553142662Sphantom		    msg = msg->entries.le_next) {
554142662Sphantom			int     msg_len = strlen(msg->str) + 1;
555142662Sphantom
556142662Sphantom			msg_hdr->__msgno = htonl(msg->msgId);
557142662Sphantom			msg_hdr->__msglen = htonl(msg_len);
558142662Sphantom			msg_hdr->__offset = htonl(msg_offset);
559142662Sphantom
560142662Sphantom			memcpy(strings, msg->str, msg_len);
561142662Sphantom			strings += msg_len;
562142662Sphantom			msg_offset += msg_len;
563142662Sphantom
564142662Sphantom			nmsgs++;
565142662Sphantom			msg_hdr++;
566142662Sphantom		}
567142662Sphantom
568142662Sphantom		set_hdr->__setno = htonl(set->setId);
569142662Sphantom		set_hdr->__nmsgs = htonl(nmsgs);
570142662Sphantom		set_hdr->__index = htonl(msg_index);
571142662Sphantom		msg_index += nmsgs;
572142662Sphantom		set_hdr++;
573142662Sphantom	}
574142662Sphantom
575142662Sphantom	/* write out catalog.  XXX: should this be done in small chunks? */
576142662Sphantom	write(fd, msgcat, msgcat_size);
577142662Sphantom}
578142662Sphantom
579142662Sphantomvoid
580142662SphantomMCAddSet(int setId)
581142662Sphantom{
582142662Sphantom	struct _setT *p, *q;
583142662Sphantom
584142662Sphantom	if (setId <= 0) {
585142662Sphantom		error("setId's must be greater than zero");
586142662Sphantom		/* NOTREACHED */
587142662Sphantom	}
588142662Sphantom	if (setId > NL_SETMAX) {
589142662Sphantom		error("setId exceeds limit");
590142662Sphantom		/* NOTREACHED */
591142662Sphantom	}
592142662Sphantom
593142662Sphantom	p = sethead.lh_first;
594142662Sphantom	q = NULL;
595142662Sphantom	for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
596142662Sphantom
597142662Sphantom	if (p && p->setId == setId) {
598142662Sphantom		;
599199236Sedwin	} else {
600142662Sphantom		p = xmalloc(sizeof(struct _setT));
601142662Sphantom		memset(p, '\0', sizeof(struct _setT));
602142662Sphantom		LIST_INIT(&p->msghead);
603142662Sphantom
604142662Sphantom		p->setId = setId;
605142662Sphantom
606142662Sphantom		if (q == NULL) {
607142662Sphantom			LIST_INSERT_HEAD(&sethead, p, entries);
608142662Sphantom		} else {
609142662Sphantom			LIST_INSERT_AFTER(q, p, entries);
610199236Sedwin		}
611199236Sedwin	}
6127496Sjkh
613142662Sphantom	curSet = p;
614142662Sphantom}
615142662Sphantom
616142662Sphantomvoid
617142662SphantomMCAddMsg(int msgId, const char *str)
6187496Sjkh{
619142662Sphantom	struct _msgT *p, *q;
6207496Sjkh
621142662Sphantom	if (!curSet)
622142662Sphantom		error("can't specify a message when no set exists");
623142662Sphantom
624142662Sphantom	if (msgId <= 0) {
625142662Sphantom		error("msgId's must be greater than zero");
626142662Sphantom		/* NOTREACHED */
627142662Sphantom	}
628142662Sphantom	if (msgId > NL_MSGMAX) {
629142662Sphantom		error("msgID exceeds limit");
630142662Sphantom		/* NOTREACHED */
631199236Sedwin	}
6327496Sjkh
633142662Sphantom	p = curSet->msghead.lh_first;
634142662Sphantom	q = NULL;
635142662Sphantom	for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
6367496Sjkh
637142662Sphantom	if (p && p->msgId == msgId) {
638142662Sphantom		free(p->str);
639142662Sphantom	} else {
640142662Sphantom		p = xmalloc(sizeof(struct _msgT));
641142662Sphantom		memset(p, '\0', sizeof(struct _msgT));
6427496Sjkh
643142662Sphantom		if (q == NULL) {
644142662Sphantom			LIST_INSERT_HEAD(&curSet->msghead, p, entries);
645142662Sphantom		} else {
646142662Sphantom			LIST_INSERT_AFTER(q, p, entries);
647142662Sphantom		}
648142662Sphantom	}
6497496Sjkh
650142662Sphantom	p->msgId = msgId;
651142662Sphantom	p->str = xstrdup(str);
652199236Sedwin}
6537496Sjkh
654142662Sphantomvoid
655142662SphantomMCDelSet(int setId)
656142662Sphantom{
657142662Sphantom	struct _setT *set;
658142662Sphantom	struct _msgT *msg;
659142662Sphantom
660142662Sphantom	set = sethead.lh_first;
661142662Sphantom	for (; set != NULL && set->setId < setId; set = set->entries.le_next);
662142662Sphantom
663142662Sphantom	if (set && set->setId == setId) {
664142662Sphantom
665142662Sphantom		msg = set->msghead.lh_first;
666142662Sphantom		while (msg) {
667142662Sphantom			free(msg->str);
668142662Sphantom			LIST_REMOVE(msg, entries);
669199236Sedwin		}
670142662Sphantom
671142662Sphantom		LIST_REMOVE(set, entries);
672142662Sphantom		return;
673199236Sedwin	}
674142662Sphantom	warning(NULL, "specified set doesn't exist");
675199236Sedwin}
676142662Sphantom
677142662Sphantomvoid
678142662SphantomMCDelMsg(int msgId)
679142662Sphantom{
680142662Sphantom	struct _msgT *msg;
681142662Sphantom
682142662Sphantom	if (!curSet)
683142662Sphantom		error("you can't delete a message before defining the set");
684142662Sphantom
685142662Sphantom	msg = curSet->msghead.lh_first;
686142662Sphantom	for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
687142662Sphantom
688142662Sphantom	if (msg && msg->msgId == msgId) {
689142662Sphantom		free(msg->str);
690142662Sphantom		LIST_REMOVE(msg, entries);
691142662Sphantom		return;
692199236Sedwin	}
693142662Sphantom	warning(NULL, "specified msg doesn't exist");
6947496Sjkh}
695