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