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