Home | History | Annotate | Line # | Download | only in gencat
      1  1.36       apb /*	$NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb Exp $	*/
      2   1.5     veego 
      3   1.5     veego /*
      4   1.3       jtc  * Copyright (c) 1996 The NetBSD Foundation, Inc.
      5   1.3       jtc  * All rights reserved.
      6   1.3       jtc  *
      7   1.3       jtc  * This code is derived from software contributed to The NetBSD Foundation
      8   1.3       jtc  * by J.T. Conklin.
      9   1.3       jtc  *
     10   1.3       jtc  * Redistribution and use in source and binary forms, with or without
     11   1.3       jtc  * modification, are permitted provided that the following conditions
     12   1.3       jtc  * are met:
     13   1.3       jtc  * 1. Redistributions of source code must retain the above copyright
     14   1.3       jtc  *    notice, this list of conditions and the following disclaimer.
     15   1.3       jtc  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.3       jtc  *    notice, this list of conditions and the following disclaimer in the
     17   1.3       jtc  *    documentation and/or other materials provided with the distribution.
     18   1.3       jtc  *
     19   1.3       jtc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.3       jtc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.3       jtc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.6       jtc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.6       jtc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.3       jtc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.3       jtc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.3       jtc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.3       jtc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.3       jtc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.3       jtc  * POSSIBILITY OF SUCH DAMAGE.
     30   1.3       jtc  */
     31   1.1       jtc 
     32   1.7     lukem #include <sys/cdefs.h>
     33  1.14        tv #if defined(__RCSID) && !defined(lint)
     34  1.36       apb __RCSID("$NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb Exp $");
     35   1.7     lukem #endif
     36   1.7     lukem 
     37   1.1       jtc /***********************************************************
     38   1.1       jtc Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
     39   1.1       jtc 
     40   1.1       jtc                         All Rights Reserved
     41   1.1       jtc 
     42   1.1       jtc Permission to use, copy, modify, and distribute this software and its
     43   1.1       jtc documentation for any purpose and without fee is hereby granted,
     44   1.1       jtc provided that the above copyright notice appear in all copies and that
     45   1.1       jtc both that copyright notice and this permission notice appear in
     46   1.1       jtc supporting documentation, and that Alfalfa's name not be used in
     47   1.1       jtc advertising or publicity pertaining to distribution of the software
     48   1.1       jtc without specific, written prior permission.
     49   1.1       jtc 
     50   1.1       jtc ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     51   1.1       jtc ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     52   1.1       jtc ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     53   1.1       jtc ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     54   1.1       jtc WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     55   1.1       jtc ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     56   1.1       jtc SOFTWARE.
     57   1.1       jtc 
     58   1.1       jtc If you make any modifications, bugfixes or other changes to this software
     59   1.1       jtc we'd appreciate it if you could send a copy to us so we can keep things
     60   1.1       jtc up-to-date.  Many thanks.
     61   1.1       jtc 				Kee Hinckley
     62   1.1       jtc 				Alfalfa Software, Inc.
     63   1.1       jtc 				267 Allston St., #3
     64   1.1       jtc 				Cambridge, MA 02139  USA
     65   1.1       jtc 				nazgul (at) alfalfa.com
     66   1.3       jtc 
     67   1.1       jtc ******************************************************************/
     68   1.1       jtc 
     69  1.18     lukem #if HAVE_NBTOOL_CONFIG_H
     70  1.18     lukem #include "nbtool_config.h"
     71  1.13        tv #endif
     72  1.13        tv 
     73   1.3       jtc #define _NLS_PRIVATE
     74   1.1       jtc 
     75  1.17     bjh21 #include <sys/types.h>
     76   1.3       jtc #include <sys/queue.h>
     77  1.15     bjh21 
     78  1.16     bjh21 #include <netinet/in.h>	/* Needed by arpa/inet.h on NetBSD */
     79  1.16     bjh21 #include <arpa/inet.h>	/* Needed for htonl() on POSIX systems */
     80  1.15     bjh21 
     81   1.3       jtc #include <ctype.h>
     82  1.14        tv #include <err.h>
     83  1.24  ginsbach #include <errno.h>
     84   1.7     lukem #include <fcntl.h>
     85  1.12      yamt #include <limits.h>
     86   1.7     lukem #include <nl_types.h>
     87   1.1       jtc #include <stdio.h>
     88   1.3       jtc #include <stdlib.h>
     89   1.3       jtc #include <string.h>
     90   1.3       jtc #include <unistd.h>
     91  1.13        tv 
     92  1.14        tv #ifndef NL_SETMAX
     93  1.14        tv #define NL_SETMAX 255
     94  1.14        tv #endif
     95  1.14        tv #ifndef NL_MSGMAX
     96  1.14        tv #define NL_MSGMAX 2048
     97  1.13        tv #endif
     98   1.3       jtc 
     99   1.3       jtc struct _msgT {
    100   1.3       jtc 	long    msgId;
    101   1.3       jtc 	char   *str;
    102   1.3       jtc         LIST_ENTRY(_msgT) entries;
    103   1.3       jtc };
    104   1.3       jtc 
    105   1.3       jtc struct _setT {
    106   1.3       jtc 	long    setId;
    107   1.3       jtc         LIST_HEAD(msghead, _msgT) msghead;
    108   1.3       jtc         LIST_ENTRY(_setT) entries;
    109   1.3       jtc };
    110   1.1       jtc 
    111  1.36       apb static LIST_HEAD(sethead, _setT) sethead = LIST_HEAD_INITIALIZER(sethead);
    112   1.3       jtc static struct _setT *curSet;
    113   1.1       jtc 
    114  1.24  ginsbach static const char *curfile;
    115   1.3       jtc static char *curline = NULL;
    116   1.3       jtc static long lineno = 0;
    117   1.1       jtc 
    118  1.23  ginsbach static	char   *cskip(char *);
    119  1.31     joerg __dead static	void	error(const char *);
    120  1.30       roy static	char   *get_line(int);
    121  1.23  ginsbach static	char   *getmsg(int, char *, char);
    122  1.23  ginsbach static	void	warning(const char *, const char *);
    123  1.23  ginsbach static	char   *wskip(char *);
    124  1.23  ginsbach static	char   *xstrdup(const char *);
    125  1.23  ginsbach static	void   *xmalloc(size_t);
    126  1.23  ginsbach static	void   *xrealloc(void *, size_t);
    127  1.23  ginsbach 
    128  1.31     joerg static void	MCParse(int fd);
    129  1.31     joerg static void	MCReadCat(int fd);
    130  1.31     joerg static void	MCWriteCat(int fd);
    131  1.31     joerg static void	MCDelMsg(int msgId);
    132  1.31     joerg static void	MCAddMsg(int msgId, const char *msg);
    133  1.31     joerg static void	MCAddSet(int setId);
    134  1.31     joerg static void	MCDelSet(int setId);
    135  1.31     joerg __dead static void	usage(void);
    136   1.7     lukem 
    137  1.24  ginsbach #define CORRUPT			"corrupt message catalog"
    138  1.24  ginsbach #define NOMEMORY		"out of memory"
    139   1.7     lukem 
    140  1.31     joerg static void
    141  1.23  ginsbach usage(void)
    142   1.3       jtc {
    143  1.34       wiz 	fprintf(stderr, "usage: %s catfile [msgfile|- ...]\n", getprogname());
    144   1.3       jtc 	exit(1);
    145   1.3       jtc }
    146   1.1       jtc 
    147   1.3       jtc int
    148  1.23  ginsbach main(int argc, char *argv[])
    149   1.3       jtc {
    150   1.3       jtc 	int     ofd, ifd;
    151   1.3       jtc 	char   *catfile = NULL;
    152   1.3       jtc 	int     c;
    153  1.24  ginsbach 	int	updatecat = 0;
    154   1.3       jtc 
    155   1.3       jtc 	while ((c = getopt(argc, argv, "")) != -1) {
    156   1.3       jtc 		switch (c) {
    157   1.3       jtc 		case '?':
    158   1.3       jtc 		default:
    159   1.3       jtc 			usage();
    160   1.3       jtc 			/* NOTREACHED */
    161   1.3       jtc 		}
    162   1.3       jtc 	}
    163   1.3       jtc 	argc -= optind;
    164   1.3       jtc 	argv += optind;
    165   1.1       jtc 
    166  1.33  christos 	if (argc < 1) {
    167   1.1       jtc 		usage();
    168   1.3       jtc 		/* NOTREACHED */
    169   1.3       jtc 	}
    170   1.3       jtc 	catfile = *argv++;
    171   1.3       jtc 
    172  1.24  ginsbach 	if ((catfile[0] == '-') && (catfile[1] == '\0')) {
    173  1.24  ginsbach 		ofd = STDOUT_FILENO;
    174  1.24  ginsbach 	} else {
    175  1.24  ginsbach 		ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
    176  1.24  ginsbach 		if (ofd < 0) {
    177  1.24  ginsbach 			if (errno == EEXIST) {
    178  1.24  ginsbach 				if ((ofd = open(catfile, O_RDWR)) < 0) {
    179  1.24  ginsbach 					err(1, "Unable to open %s", catfile);
    180  1.24  ginsbach 					/* NOTREACHED */
    181  1.24  ginsbach 				}
    182  1.24  ginsbach 			} else {
    183  1.24  ginsbach 				err(1, "Unable to create new %s", catfile);
    184  1.24  ginsbach 				/* NOTREACHED */
    185  1.24  ginsbach 			}
    186  1.24  ginsbach 			curfile = catfile;
    187  1.24  ginsbach 			updatecat = 1;
    188  1.24  ginsbach 			MCReadCat(ofd);
    189  1.32  christos 			if (lseek(ofd, (off_t)0, SEEK_SET) == (off_t)-1) {
    190  1.24  ginsbach 				err(1, "Unable to seek on %s", catfile);
    191  1.24  ginsbach 				/* NOTREACHED */
    192  1.24  ginsbach 			}
    193  1.24  ginsbach 		}
    194  1.24  ginsbach 	}
    195  1.24  ginsbach 
    196  1.33  christos 	if (argc < 2 || (((*argv)[0] == '-') && ((*argv)[1] == '\0'))) {
    197  1.33  christos 		if (argc > 2)
    198  1.24  ginsbach 			usage();
    199  1.24  ginsbach 			/* NOTREACHED */
    200  1.24  ginsbach 		MCParse(STDIN_FILENO);
    201  1.24  ginsbach 	} else {
    202  1.24  ginsbach 		for (; *argv; argv++) {
    203  1.24  ginsbach 			if ((ifd = open(*argv, O_RDONLY)) < 0)
    204  1.24  ginsbach 				err(1, "Unable to read %s", *argv);
    205  1.24  ginsbach 			curfile = *argv;
    206  1.24  ginsbach 			lineno = 0;
    207  1.24  ginsbach 			MCParse(ifd);
    208  1.24  ginsbach 			close(ifd);
    209  1.24  ginsbach 		}
    210  1.24  ginsbach 	}
    211  1.24  ginsbach 
    212  1.24  ginsbach 	if (updatecat) {
    213  1.24  ginsbach 		if (ftruncate(ofd, 0) != 0) {
    214  1.24  ginsbach 			err(1, "Unable to truncate %s", catfile);
    215  1.24  ginsbach 			/* NOTREACHED */
    216  1.24  ginsbach 		}
    217   1.1       jtc 	}
    218   1.3       jtc 
    219   1.1       jtc 	MCWriteCat(ofd);
    220   1.1       jtc 	exit(0);
    221   1.3       jtc }
    222   1.3       jtc 
    223   1.3       jtc static void
    224  1.23  ginsbach warning(const char *cptr, const char *msg)
    225   1.3       jtc {
    226  1.24  ginsbach 	if (lineno) {
    227  1.24  ginsbach 		fprintf(stderr, "%s: %s on line %ld, %s\n",
    228  1.24  ginsbach 			getprogname(), msg, lineno, curfile);
    229  1.24  ginsbach 		fprintf(stderr, "%s\n", curline);
    230  1.24  ginsbach 		if (cptr) {
    231  1.24  ginsbach 			char   *tptr;
    232  1.24  ginsbach 			for (tptr = curline; tptr < cptr; ++tptr)
    233  1.24  ginsbach 				putc(' ', stderr);
    234  1.24  ginsbach 			fprintf(stderr, "^\n");
    235  1.24  ginsbach 		}
    236  1.24  ginsbach 	} else {
    237  1.24  ginsbach 		fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile);
    238   1.3       jtc 	}
    239   1.3       jtc }
    240   1.3       jtc 
    241   1.3       jtc static void
    242  1.23  ginsbach error(const char *msg)
    243   1.3       jtc {
    244  1.22  ginsbach 	warning(NULL, msg);
    245   1.1       jtc 	exit(1);
    246   1.1       jtc }
    247   1.1       jtc 
    248   1.3       jtc static void *
    249  1.23  ginsbach xmalloc(size_t len)
    250   1.3       jtc {
    251   1.3       jtc 	void   *p;
    252   1.3       jtc 
    253   1.3       jtc 	if ((p = malloc(len)) == NULL)
    254  1.24  ginsbach 		errx(1, NOMEMORY);
    255   1.3       jtc 	return (p);
    256   1.3       jtc }
    257   1.3       jtc 
    258   1.3       jtc static void *
    259  1.23  ginsbach xrealloc(void *ptr, size_t size)
    260   1.3       jtc {
    261   1.3       jtc 	if ((ptr = realloc(ptr, size)) == NULL)
    262  1.24  ginsbach 		errx(1, NOMEMORY);
    263   1.3       jtc 	return (ptr);
    264   1.3       jtc }
    265   1.3       jtc 
    266   1.3       jtc static char *
    267  1.23  ginsbach xstrdup(const char *str)
    268   1.3       jtc {
    269   1.7     lukem 	char *nstr;
    270   1.7     lukem 
    271   1.7     lukem 	if ((nstr = strdup(str)) == NULL)
    272  1.24  ginsbach 		errx(1, NOMEMORY);
    273   1.7     lukem 	return (nstr);
    274   1.3       jtc }
    275   1.3       jtc 
    276   1.3       jtc static char *
    277  1.30       roy get_line(int fd)
    278   1.3       jtc {
    279   1.3       jtc 	static long curlen = BUFSIZ;
    280   1.3       jtc 	static char buf[BUFSIZ], *bptr = buf, *bend = buf;
    281   1.3       jtc 	char   *cptr, *cend;
    282   1.3       jtc 	long    buflen;
    283   1.3       jtc 
    284   1.3       jtc 	if (!curline) {
    285   1.3       jtc 		curline = xmalloc(curlen);
    286   1.3       jtc 	}
    287   1.3       jtc 	++lineno;
    288   1.3       jtc 
    289   1.3       jtc 	cptr = curline;
    290   1.3       jtc 	cend = curline + curlen;
    291   1.3       jtc 	for (;;) {
    292   1.3       jtc 		for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
    293   1.3       jtc 			if (*bptr == '\n') {
    294   1.3       jtc 				*cptr = '\0';
    295   1.3       jtc 				++bptr;
    296   1.3       jtc 				return (curline);
    297   1.3       jtc 			} else
    298   1.3       jtc 				*cptr = *bptr;
    299   1.3       jtc 		}
    300  1.11      yamt 		if (cptr == cend) {
    301  1.11      yamt 			cptr = curline = xrealloc(curline, curlen *= 2);
    302  1.11      yamt 			cend = curline + curlen;
    303  1.11      yamt 		}
    304   1.3       jtc 		if (bptr == bend) {
    305   1.3       jtc 			buflen = read(fd, buf, BUFSIZ);
    306   1.3       jtc 			if (buflen <= 0) {
    307   1.3       jtc 				if (cptr > curline) {
    308   1.3       jtc 					*cptr = '\0';
    309   1.3       jtc 					return (curline);
    310   1.3       jtc 				}
    311   1.3       jtc 				return (NULL);
    312   1.3       jtc 			}
    313   1.3       jtc 			bend = buf + buflen;
    314   1.3       jtc 			bptr = buf;
    315   1.3       jtc 		}
    316   1.3       jtc 	}
    317   1.3       jtc }
    318   1.3       jtc 
    319   1.3       jtc static char *
    320  1.23  ginsbach wskip(char *cptr)
    321   1.3       jtc {
    322   1.9     itohy 	if (!*cptr || !isspace((unsigned char) *cptr)) {
    323   1.3       jtc 		warning(cptr, "expected a space");
    324   1.3       jtc 		return (cptr);
    325   1.3       jtc 	}
    326   1.9     itohy 	while (*cptr && isspace((unsigned char) *cptr))
    327   1.3       jtc 		++cptr;
    328   1.3       jtc 	return (cptr);
    329   1.3       jtc }
    330   1.3       jtc 
    331   1.3       jtc static char *
    332  1.23  ginsbach cskip(char *cptr)
    333   1.3       jtc {
    334   1.9     itohy 	if (!*cptr || isspace((unsigned char) *cptr)) {
    335   1.3       jtc 		warning(cptr, "wasn't expecting a space");
    336   1.3       jtc 		return (cptr);
    337   1.3       jtc 	}
    338   1.9     itohy 	while (*cptr && !isspace((unsigned char) *cptr))
    339   1.3       jtc 		++cptr;
    340   1.3       jtc 	return (cptr);
    341   1.3       jtc }
    342   1.3       jtc 
    343   1.3       jtc static char *
    344  1.23  ginsbach getmsg(int fd, char *cptr, char quote)
    345   1.3       jtc {
    346   1.3       jtc 	static char *msg = NULL;
    347  1.28     lukem 	static size_t msglen = 0;
    348  1.35  ginsbach 	size_t  clen, i;
    349  1.35  ginsbach 	int     in_quote = 0;
    350   1.3       jtc 	char   *tptr;
    351   1.3       jtc 
    352   1.3       jtc 	if (quote && *cptr == quote) {
    353   1.3       jtc 		++cptr;
    354  1.35  ginsbach 		in_quote = 1;
    355   1.3       jtc 	}
    356   1.3       jtc 
    357   1.3       jtc 	clen = strlen(cptr) + 1;
    358   1.3       jtc 	if (clen > msglen) {
    359   1.3       jtc 		if (msglen)
    360   1.3       jtc 			msg = xrealloc(msg, clen);
    361   1.3       jtc 		else
    362   1.3       jtc 			msg = xmalloc(clen);
    363   1.3       jtc 		msglen = clen;
    364   1.3       jtc 	}
    365   1.3       jtc 	tptr = msg;
    366   1.3       jtc 
    367   1.3       jtc 	while (*cptr) {
    368   1.3       jtc 		if (quote && *cptr == quote) {
    369   1.3       jtc 			char   *tmp;
    370   1.3       jtc 			tmp = cptr + 1;
    371  1.35  ginsbach 			if (!in_quote) {
    372  1.35  ginsbach 				/* XXX hard error? */
    373   1.8    mjacob 				warning(cptr, "unexpected quote character, ignoring");
    374   1.3       jtc 				*tptr++ = *cptr++;
    375   1.3       jtc 			} else {
    376  1.35  ginsbach 				cptr++;
    377  1.35  ginsbach 				/* don't use wskip() */
    378  1.35  ginsbach 				while (*cptr && isspace((unsigned char) *cptr))
    379  1.35  ginsbach #ifndef _BACKWARDS_COMPAT
    380  1.35  ginsbach 					cptr++;
    381  1.35  ginsbach #else
    382  1.35  ginsbach 					*tptr++ = *cptr++;
    383  1.35  ginsbach #endif
    384  1.35  ginsbach 				/* XXX hard error? */
    385  1.35  ginsbach 				if (*cptr)
    386  1.35  ginsbach 					warning(tmp, "unexpected extra characters, ignoring");
    387  1.35  ginsbach 				in_quote = 0;
    388  1.35  ginsbach #ifndef _BACKWARDS_COMPAT
    389  1.35  ginsbach 				break;
    390  1.35  ginsbach #endif
    391   1.3       jtc 			}
    392  1.26  ginsbach 		} else {
    393   1.3       jtc 			if (*cptr == '\\') {
    394   1.3       jtc 				++cptr;
    395   1.3       jtc 				switch (*cptr) {
    396   1.3       jtc 				case '\0':
    397  1.30       roy 					cptr = get_line(fd);
    398   1.3       jtc 					if (!cptr)
    399  1.22  ginsbach 						error("premature end of file");
    400   1.3       jtc 					msglen += strlen(cptr);
    401   1.3       jtc 					i = tptr - msg;
    402   1.3       jtc 					msg = xrealloc(msg, msglen);
    403   1.3       jtc 					tptr = msg + i;
    404   1.3       jtc 					break;
    405   1.3       jtc 				case 'n':
    406   1.3       jtc 					*tptr++ = '\n';
    407   1.3       jtc 					++cptr;
    408   1.3       jtc 					break;
    409   1.3       jtc 				case 't':
    410   1.3       jtc 					*tptr++ = '\t';
    411   1.3       jtc 					++cptr;
    412   1.3       jtc 					break;
    413   1.3       jtc 				case 'v':
    414   1.3       jtc 					*tptr++ = '\v';
    415   1.3       jtc 					++cptr;
    416   1.3       jtc 					break;
    417   1.3       jtc 				case 'b':
    418   1.3       jtc 					*tptr++ = '\b';
    419   1.3       jtc 					++cptr;
    420   1.3       jtc 					break;
    421   1.3       jtc 				case 'r':
    422   1.3       jtc 					*tptr++ = '\r';
    423   1.3       jtc 					++cptr;
    424   1.3       jtc 					break;
    425   1.3       jtc 				case 'f':
    426   1.3       jtc 					*tptr++ = '\f';
    427   1.3       jtc 					++cptr;
    428   1.3       jtc 					break;
    429   1.3       jtc 				case '\\':
    430   1.3       jtc 					*tptr++ = '\\';
    431   1.3       jtc 					++cptr;
    432   1.3       jtc 					break;
    433   1.3       jtc 				default:
    434   1.8    mjacob 					if (quote && *cptr == quote) {
    435   1.8    mjacob 						*tptr++ = *cptr++;
    436   1.9     itohy 					} else if (isdigit((unsigned char) *cptr)) {
    437   1.3       jtc 						*tptr = 0;
    438   1.3       jtc 						for (i = 0; i < 3; ++i) {
    439   1.9     itohy 							if (!isdigit((unsigned char) *cptr))
    440   1.3       jtc 								break;
    441   1.3       jtc 							if (*cptr > '7')
    442   1.3       jtc 								warning(cptr, "octal number greater than 7?!");
    443   1.3       jtc 							*tptr *= 8;
    444   1.3       jtc 							*tptr += (*cptr - '0');
    445   1.3       jtc 							++cptr;
    446   1.3       jtc 						}
    447   1.3       jtc 					} else {
    448   1.3       jtc 						warning(cptr, "unrecognized escape sequence");
    449   1.3       jtc 					}
    450   1.8    mjacob 					break;
    451   1.3       jtc 				}
    452   1.3       jtc 			} else {
    453   1.3       jtc 				*tptr++ = *cptr++;
    454   1.3       jtc 			}
    455  1.26  ginsbach 		}
    456   1.3       jtc 	}
    457  1.35  ginsbach 
    458  1.35  ginsbach 	if (in_quote)
    459  1.35  ginsbach 		warning(cptr, "unterminated quoted message, ignoring");
    460  1.35  ginsbach 
    461   1.3       jtc 	*tptr = '\0';
    462   1.3       jtc 	return (msg);
    463   1.3       jtc }
    464   1.3       jtc 
    465  1.31     joerg static void
    466  1.23  ginsbach MCParse(int fd)
    467   1.3       jtc {
    468   1.3       jtc 	char   *cptr, *str;
    469  1.21  ginsbach 	int	msgid = 0;
    470  1.21  ginsbach 	int     setid = 0;
    471   1.3       jtc 	char    quote = 0;
    472   1.3       jtc 
    473  1.30       roy 	while ((cptr = get_line(fd))) {
    474   1.3       jtc 		if (*cptr == '$') {
    475   1.3       jtc 			++cptr;
    476   1.3       jtc 			if (strncmp(cptr, "set", 3) == 0) {
    477   1.3       jtc 				cptr += 3;
    478   1.3       jtc 				cptr = wskip(cptr);
    479   1.3       jtc 				setid = atoi(cptr);
    480   1.3       jtc 				MCAddSet(setid);
    481   1.3       jtc 				msgid = 0;
    482   1.3       jtc 			} else if (strncmp(cptr, "delset", 6) == 0) {
    483   1.3       jtc 				cptr += 6;
    484   1.3       jtc 				cptr = wskip(cptr);
    485   1.3       jtc 				setid = atoi(cptr);
    486   1.3       jtc 				MCDelSet(setid);
    487   1.3       jtc 			} else if (strncmp(cptr, "quote", 5) == 0) {
    488   1.3       jtc 				cptr += 5;
    489   1.3       jtc 				if (!*cptr)
    490   1.3       jtc 					quote = 0;
    491   1.3       jtc 				else {
    492   1.3       jtc 					cptr = wskip(cptr);
    493   1.3       jtc 					if (!*cptr)
    494   1.3       jtc 						quote = 0;
    495   1.3       jtc 					else
    496   1.3       jtc 						quote = *cptr;
    497   1.3       jtc 				}
    498   1.9     itohy 			} else if (isspace((unsigned char) *cptr)) {
    499   1.3       jtc 				;
    500   1.3       jtc 			} else {
    501   1.3       jtc 				if (*cptr) {
    502   1.3       jtc 					cptr = wskip(cptr);
    503   1.3       jtc 					if (*cptr)
    504   1.3       jtc 						warning(cptr, "unrecognized line");
    505   1.3       jtc 				}
    506   1.3       jtc 			}
    507   1.3       jtc 		} else {
    508   1.8    mjacob 			/*
    509   1.8    mjacob 			 * First check for (and eat) empty lines....
    510   1.8    mjacob 			 */
    511   1.8    mjacob 			if (!*cptr)
    512   1.8    mjacob 				continue;
    513   1.8    mjacob 			/*
    514   1.8    mjacob 			 * We have a digit? Start of a message. Else,
    515   1.8    mjacob 			 * syntax error.
    516   1.8    mjacob 			 */
    517   1.9     itohy 			if (isdigit((unsigned char) *cptr)) {
    518   1.3       jtc 				msgid = atoi(cptr);
    519   1.3       jtc 				cptr = cskip(cptr);
    520  1.26  ginsbach 				if (*cptr) {
    521  1.24  ginsbach 					cptr = wskip(cptr);
    522  1.26  ginsbach 					if (!*cptr) {
    523  1.26  ginsbach 						MCAddMsg(msgid, "");
    524  1.26  ginsbach 						continue;
    525  1.26  ginsbach 					}
    526  1.26  ginsbach 				}
    527   1.8    mjacob 			} else {
    528   1.8    mjacob 				warning(cptr, "neither blank line nor start of a message id");
    529   1.8    mjacob 				continue;
    530   1.3       jtc 			}
    531   1.8    mjacob 			/*
    532  1.21  ginsbach 			 * If no set directive specified, all messages
    533  1.21  ginsbach 			 * shall be in default message set NL_SETD.
    534  1.21  ginsbach 			 */
    535  1.21  ginsbach 			if (setid == 0) {
    536  1.21  ginsbach 				setid = NL_SETD;
    537  1.21  ginsbach 				MCAddSet(setid);
    538  1.21  ginsbach 			}
    539  1.21  ginsbach 			/*
    540   1.8    mjacob 			 * If we have a message ID, but no message,
    541   1.8    mjacob 			 * then this means "delete this message id
    542   1.8    mjacob 			 * from the catalog".
    543   1.8    mjacob 			 */
    544   1.8    mjacob 			if (!*cptr) {
    545   1.3       jtc 				MCDelMsg(msgid);
    546   1.8    mjacob 			} else {
    547   1.3       jtc 				str = getmsg(fd, cptr, quote);
    548   1.3       jtc 				MCAddMsg(msgid, str);
    549   1.3       jtc 			}
    550   1.3       jtc 		}
    551   1.3       jtc 	}
    552   1.3       jtc }
    553   1.3       jtc 
    554  1.31     joerg static void
    555  1.23  ginsbach MCReadCat(int fd)
    556   1.3       jtc {
    557  1.24  ginsbach 	void   *msgcat;		/* message catalog data */
    558  1.24  ginsbach 	struct _nls_cat_hdr cat_hdr;
    559  1.24  ginsbach 	struct _nls_set_hdr *set_hdr;
    560  1.24  ginsbach 	struct _nls_msg_hdr *msg_hdr;
    561  1.24  ginsbach 	char   *strings;
    562  1.28     lukem 	ssize_t	n;
    563  1.28     lukem 	int	m, s;
    564  1.24  ginsbach 	int	msgno, setno;
    565   1.3       jtc 
    566  1.24  ginsbach 	n = read(fd, &cat_hdr, sizeof(cat_hdr));
    567  1.28     lukem 	if (n < (ssize_t)sizeof(cat_hdr)) {
    568  1.24  ginsbach 		if (n == 0)
    569  1.24  ginsbach 			return;		/* empty file */
    570  1.24  ginsbach 		else if (n == -1)
    571  1.24  ginsbach 			err(1, "header read");
    572  1.24  ginsbach 		else
    573  1.24  ginsbach 			errx(1, CORRUPT);
    574  1.24  ginsbach 	}
    575  1.29      tron 	if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC)
    576  1.24  ginsbach 		errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic);
    577   1.3       jtc 
    578  1.24  ginsbach 	cat_hdr.__mem = ntohl(cat_hdr.__mem);
    579   1.3       jtc 
    580  1.24  ginsbach 	cat_hdr.__nsets = ntohl(cat_hdr.__nsets);
    581  1.24  ginsbach 	cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset);
    582  1.24  ginsbach 	cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset);
    583  1.24  ginsbach 	if ((cat_hdr.__mem < 0) ||
    584  1.24  ginsbach 	    (cat_hdr.__msg_hdr_offset < 0) ||
    585  1.24  ginsbach 	    (cat_hdr.__msg_txt_offset < 0) ||
    586  1.28     lukem 	    (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) ||
    587  1.24  ginsbach 	    (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) ||
    588  1.24  ginsbach 	    (cat_hdr.__mem < cat_hdr.__msg_txt_offset))
    589  1.24  ginsbach 		errx(1, "%s: catalog header", CORRUPT);
    590  1.24  ginsbach 
    591  1.27  christos 	msgcat = xmalloc(cat_hdr.__mem);
    592  1.27  christos 
    593  1.24  ginsbach 	n = read(fd, msgcat, cat_hdr.__mem);
    594  1.24  ginsbach 	if (n < cat_hdr.__mem) {
    595  1.24  ginsbach 		if (n == -1)
    596  1.24  ginsbach 			err(1, "data read");
    597  1.24  ginsbach 		else
    598  1.24  ginsbach 			errx(1, CORRUPT);
    599  1.24  ginsbach 	}
    600   1.3       jtc 
    601  1.24  ginsbach 	set_hdr = (struct _nls_set_hdr *)msgcat;
    602  1.24  ginsbach 	msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat +
    603  1.24  ginsbach 	    cat_hdr.__msg_hdr_offset);
    604  1.24  ginsbach 	strings = (char *)msgcat + cat_hdr.__msg_txt_offset;
    605  1.24  ginsbach 
    606  1.24  ginsbach 	setno = 0;
    607  1.24  ginsbach 	for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) {
    608  1.24  ginsbach 		set_hdr->__setno = ntohl(set_hdr->__setno);
    609  1.24  ginsbach 		if (set_hdr->__setno < setno)
    610  1.24  ginsbach 			errx(1, "%s: bad set number (%d)",
    611  1.24  ginsbach 		       	     CORRUPT, set_hdr->__setno);
    612  1.24  ginsbach 		setno = set_hdr->__setno;
    613  1.24  ginsbach 
    614  1.24  ginsbach 		MCAddSet(setno);
    615  1.24  ginsbach 
    616  1.24  ginsbach 		set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs);
    617  1.24  ginsbach 		set_hdr->__index = ntohl(set_hdr->__index);
    618  1.24  ginsbach 		if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0)
    619  1.24  ginsbach 			errx(1, "%s: set header", CORRUPT);
    620   1.3       jtc 
    621   1.3       jtc 		/* Get the data */
    622  1.24  ginsbach 		msgno = 0;
    623  1.24  ginsbach 		for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) {
    624  1.24  ginsbach 			msg_hdr->__msgno = ntohl(msg_hdr->__msgno);
    625  1.24  ginsbach 			msg_hdr->__offset = ntohl(msg_hdr->__offset);
    626  1.24  ginsbach 			if (msg_hdr->__msgno < msgno)
    627  1.24  ginsbach 				errx(1, "%s: bad message number (%d)",
    628  1.24  ginsbach 				     CORRUPT, msg_hdr->__msgno);
    629  1.24  ginsbach 		        if ((msg_hdr->__offset < 0) ||
    630  1.24  ginsbach 			    ((strings + msg_hdr->__offset) >
    631  1.24  ginsbach 			     ((char *)msgcat + cat_hdr.__mem)))
    632  1.24  ginsbach 				errx(1, "%s: message header", CORRUPT);
    633   1.3       jtc 
    634  1.24  ginsbach 			msgno = msg_hdr->__msgno;
    635  1.24  ginsbach 			MCAddMsg(msgno, strings + msg_hdr->__offset);
    636   1.3       jtc 		}
    637   1.3       jtc 	}
    638  1.24  ginsbach 	free(msgcat);
    639   1.3       jtc }
    640   1.3       jtc 
    641   1.3       jtc /*
    642   1.3       jtc  * Write message catalog.
    643   1.3       jtc  *
    644   1.3       jtc  * The message catalog is first converted from its internal to its
    645   1.3       jtc  * external representation in a chunk of memory allocated for this
    646   1.3       jtc  * purpose.  Then the completed catalog is written.  This approach
    647   1.3       jtc  * avoids additional housekeeping variables and/or a lot of seeks
    648   1.3       jtc  * that would otherwise be required.
    649   1.3       jtc  */
    650  1.31     joerg static void
    651  1.23  ginsbach MCWriteCat(int fd)
    652   1.1       jtc {
    653   1.3       jtc 	int     nsets;		/* number of sets */
    654   1.3       jtc 	int     nmsgs;		/* number of msgs */
    655   1.3       jtc 	int     string_size;	/* total size of string pool */
    656   1.3       jtc 	int     msgcat_size;	/* total size of message catalog */
    657   1.3       jtc 	void   *msgcat;		/* message catalog data */
    658   1.3       jtc 	struct _nls_cat_hdr *cat_hdr;
    659   1.3       jtc 	struct _nls_set_hdr *set_hdr;
    660   1.3       jtc 	struct _nls_msg_hdr *msg_hdr;
    661   1.3       jtc 	char   *strings;
    662   1.3       jtc 	struct _setT *set;
    663   1.3       jtc 	struct _msgT *msg;
    664   1.3       jtc 	int     msg_index;
    665   1.3       jtc 	int     msg_offset;
    666   1.3       jtc 
    667   1.3       jtc 	/* determine number of sets, number of messages, and size of the
    668   1.3       jtc 	 * string pool */
    669   1.3       jtc 	nsets = 0;
    670   1.3       jtc 	nmsgs = 0;
    671   1.3       jtc 	string_size = 0;
    672   1.3       jtc 
    673  1.36       apb 	LIST_FOREACH(set, &sethead, entries) {
    674   1.3       jtc 		nsets++;
    675   1.3       jtc 
    676  1.36       apb 		LIST_FOREACH(msg, &set->msghead, entries) {
    677   1.3       jtc 			nmsgs++;
    678   1.3       jtc 			string_size += strlen(msg->str) + 1;
    679   1.3       jtc 		}
    680   1.3       jtc 	}
    681   1.3       jtc 
    682   1.3       jtc #ifdef DEBUG
    683   1.3       jtc 	printf("number of sets: %d\n", nsets);
    684   1.3       jtc 	printf("number of msgs: %d\n", nmsgs);
    685   1.3       jtc 	printf("string pool size: %d\n", string_size);
    686   1.3       jtc #endif
    687   1.3       jtc 
    688   1.3       jtc 	/* determine size and then allocate buffer for constructing external
    689   1.3       jtc 	 * message catalog representation */
    690   1.3       jtc 	msgcat_size = sizeof(struct _nls_cat_hdr)
    691   1.3       jtc 	    + (nsets * sizeof(struct _nls_set_hdr))
    692   1.3       jtc 	    + (nmsgs * sizeof(struct _nls_msg_hdr))
    693   1.3       jtc 	    + string_size;
    694   1.3       jtc 
    695   1.3       jtc 	msgcat = xmalloc(msgcat_size);
    696   1.3       jtc 	memset(msgcat, '\0', msgcat_size);
    697   1.3       jtc 
    698   1.3       jtc 	/* fill in msg catalog header */
    699   1.3       jtc 	cat_hdr = (struct _nls_cat_hdr *) msgcat;
    700   1.3       jtc 	cat_hdr->__magic = htonl(_NLS_MAGIC);
    701   1.3       jtc 	cat_hdr->__nsets = htonl(nsets);
    702   1.3       jtc 	cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
    703   1.3       jtc 	cat_hdr->__msg_hdr_offset =
    704   1.3       jtc 	    htonl(nsets * sizeof(struct _nls_set_hdr));
    705   1.3       jtc 	cat_hdr->__msg_txt_offset =
    706   1.3       jtc 	    htonl(nsets * sizeof(struct _nls_set_hdr) +
    707   1.3       jtc 	    nmsgs * sizeof(struct _nls_msg_hdr));
    708   1.3       jtc 
    709   1.3       jtc 	/* compute offsets for set & msg header tables and string pool */
    710   1.3       jtc 	set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
    711   1.3       jtc 	    sizeof(struct _nls_cat_hdr));
    712   1.3       jtc 	msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
    713   1.3       jtc 	    sizeof(struct _nls_cat_hdr) +
    714   1.3       jtc 	    nsets * sizeof(struct _nls_set_hdr));
    715   1.3       jtc 	strings = (char *) msgcat +
    716   1.3       jtc 	    sizeof(struct _nls_cat_hdr) +
    717   1.3       jtc 	    nsets * sizeof(struct _nls_set_hdr) +
    718   1.3       jtc 	    nmsgs * sizeof(struct _nls_msg_hdr);
    719   1.3       jtc 
    720   1.3       jtc 	msg_index = 0;
    721   1.3       jtc 	msg_offset = 0;
    722  1.36       apb 	LIST_FOREACH(set, &sethead, entries) {
    723   1.3       jtc 
    724   1.3       jtc 		nmsgs = 0;
    725  1.36       apb 		LIST_FOREACH(msg, &set->msghead, entries) {
    726  1.28     lukem 			int32_t     msg_len = strlen(msg->str) + 1;
    727   1.3       jtc 
    728   1.3       jtc 			msg_hdr->__msgno = htonl(msg->msgId);
    729   1.3       jtc 			msg_hdr->__msglen = htonl(msg_len);
    730   1.3       jtc 			msg_hdr->__offset = htonl(msg_offset);
    731   1.3       jtc 
    732   1.3       jtc 			memcpy(strings, msg->str, msg_len);
    733   1.3       jtc 			strings += msg_len;
    734   1.3       jtc 			msg_offset += msg_len;
    735   1.3       jtc 
    736   1.3       jtc 			nmsgs++;
    737   1.3       jtc 			msg_hdr++;
    738   1.3       jtc 		}
    739   1.3       jtc 
    740   1.3       jtc 		set_hdr->__setno = htonl(set->setId);
    741   1.3       jtc 		set_hdr->__nmsgs = htonl(nmsgs);
    742   1.3       jtc 		set_hdr->__index = htonl(msg_index);
    743   1.3       jtc 		msg_index += nmsgs;
    744   1.3       jtc 		set_hdr++;
    745   1.3       jtc 	}
    746   1.3       jtc 
    747   1.3       jtc 	/* write out catalog.  XXX: should this be done in small chunks? */
    748   1.3       jtc 	write(fd, msgcat, msgcat_size);
    749   1.3       jtc }
    750   1.3       jtc 
    751  1.31     joerg static void
    752  1.23  ginsbach MCAddSet(int setId)
    753   1.3       jtc {
    754   1.3       jtc 	struct _setT *p, *q;
    755   1.3       jtc 
    756   1.3       jtc 	if (setId <= 0) {
    757  1.22  ginsbach 		error("setId's must be greater than zero");
    758   1.3       jtc 		/* NOTREACHED */
    759   1.3       jtc 	}
    760   1.3       jtc 	if (setId > NL_SETMAX) {
    761  1.22  ginsbach 		error("setId exceeds limit");
    762   1.3       jtc 		/* NOTREACHED */
    763   1.3       jtc 	}
    764   1.3       jtc 
    765  1.36       apb 	p = LIST_FIRST(&sethead);
    766   1.3       jtc 	q = NULL;
    767  1.36       apb 	for (; p != NULL && p->setId < setId; q = p, p = LIST_NEXT(p, entries))
    768  1.36       apb 		continue;
    769   1.3       jtc 
    770   1.3       jtc 	if (p && p->setId == setId) {
    771   1.3       jtc 		;
    772   1.3       jtc 	} else {
    773   1.3       jtc 		p = xmalloc(sizeof(struct _setT));
    774   1.3       jtc 		memset(p, '\0', sizeof(struct _setT));
    775   1.3       jtc 		LIST_INIT(&p->msghead);
    776   1.3       jtc 
    777   1.3       jtc 		p->setId = setId;
    778   1.3       jtc 
    779   1.3       jtc 		if (q == NULL) {
    780   1.3       jtc 			LIST_INSERT_HEAD(&sethead, p, entries);
    781   1.3       jtc 		} else {
    782   1.3       jtc 			LIST_INSERT_AFTER(q, p, entries);
    783   1.3       jtc 		}
    784   1.3       jtc 	}
    785   1.3       jtc 
    786   1.3       jtc 	curSet = p;
    787   1.3       jtc }
    788   1.3       jtc 
    789  1.31     joerg static void
    790  1.23  ginsbach MCAddMsg(int msgId, const char *str)
    791   1.3       jtc {
    792   1.3       jtc 	struct _msgT *p, *q;
    793   1.3       jtc 
    794   1.3       jtc 	if (!curSet)
    795  1.22  ginsbach 		error("can't specify a message when no set exists");
    796   1.3       jtc 
    797   1.3       jtc 	if (msgId <= 0) {
    798  1.22  ginsbach 		error("msgId's must be greater than zero");
    799   1.3       jtc 		/* NOTREACHED */
    800   1.3       jtc 	}
    801  1.12      yamt 	if (msgId > NL_MSGMAX) {
    802  1.22  ginsbach 		error("msgID exceeds limit");
    803   1.3       jtc 		/* NOTREACHED */
    804   1.3       jtc 	}
    805   1.3       jtc 
    806  1.36       apb 	p = LIST_FIRST(&curSet->msghead);
    807   1.3       jtc 	q = NULL;
    808  1.36       apb 	for (; p != NULL && p->msgId < msgId; q = p, p = LIST_NEXT(p, entries))
    809  1.36       apb 		continue;
    810   1.3       jtc 
    811   1.3       jtc 	if (p && p->msgId == msgId) {
    812   1.3       jtc 		free(p->str);
    813   1.3       jtc 	} else {
    814   1.3       jtc 		p = xmalloc(sizeof(struct _msgT));
    815   1.3       jtc 		memset(p, '\0', sizeof(struct _msgT));
    816   1.3       jtc 
    817   1.3       jtc 		if (q == NULL) {
    818   1.3       jtc 			LIST_INSERT_HEAD(&curSet->msghead, p, entries);
    819   1.3       jtc 		} else {
    820   1.3       jtc 			LIST_INSERT_AFTER(q, p, entries);
    821   1.3       jtc 		}
    822   1.3       jtc 	}
    823   1.3       jtc 
    824   1.3       jtc 	p->msgId = msgId;
    825   1.3       jtc 	p->str = xstrdup(str);
    826   1.3       jtc }
    827   1.3       jtc 
    828  1.31     joerg static void
    829  1.23  ginsbach MCDelSet(int setId)
    830   1.3       jtc {
    831   1.3       jtc 	struct _setT *set;
    832   1.3       jtc 	struct _msgT *msg;
    833   1.3       jtc 
    834  1.21  ginsbach 	if (setId <= 0) {
    835  1.22  ginsbach 		error("setId's must be greater than zero");
    836  1.21  ginsbach 		/* NOTREACHED */
    837  1.21  ginsbach 	}
    838  1.21  ginsbach 	if (setId > NL_SETMAX) {
    839  1.22  ginsbach 		error("setId exceeds limit");
    840  1.21  ginsbach 		/* NOTREACHED */
    841  1.21  ginsbach 	}
    842  1.21  ginsbach 
    843  1.36       apb 	set = LIST_FIRST(&sethead);
    844  1.36       apb 	for (; set != NULL && set->setId < setId; set = LIST_NEXT(set, entries))
    845  1.36       apb 		continue;
    846   1.3       jtc 
    847   1.3       jtc 	if (set && set->setId == setId) {
    848  1.20       dsl 		LIST_REMOVE(set, entries);
    849  1.36       apb 		while ((msg = LIST_FIRST(&set->msghead)) != NULL) {
    850  1.20       dsl 			LIST_REMOVE(msg, entries);
    851   1.3       jtc 			free(msg->str);
    852  1.20       dsl 			free(msg);
    853   1.3       jtc 		}
    854  1.20       dsl 		free(set);
    855   1.3       jtc 		return;
    856   1.3       jtc 	}
    857   1.3       jtc 	warning(NULL, "specified set doesn't exist");
    858   1.3       jtc }
    859   1.1       jtc 
    860  1.31     joerg static void
    861  1.23  ginsbach MCDelMsg(int msgId)
    862   1.3       jtc {
    863   1.3       jtc 	struct _msgT *msg;
    864   1.1       jtc 
    865   1.3       jtc 	if (!curSet)
    866  1.22  ginsbach 		error("you can't delete a message before defining the set");
    867   1.1       jtc 
    868  1.36       apb 	msg = LIST_FIRST(&curSet->msghead);
    869  1.36       apb 	for (; msg != NULL && msg->msgId < msgId; msg = LIST_NEXT(msg, entries))
    870  1.36       apb 		continue;
    871   1.1       jtc 
    872   1.3       jtc 	if (msg && msg->msgId == msgId) {
    873  1.20       dsl 		LIST_REMOVE(msg, entries);
    874   1.3       jtc 		free(msg->str);
    875  1.20       dsl 		free(msg);
    876   1.3       jtc 		return;
    877   1.3       jtc 	}
    878   1.3       jtc 	warning(NULL, "specified msg doesn't exist");
    879   1.1       jtc }
    880