1/***********************************************************
2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3
4                        All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that Alfalfa's name not be used in
11advertising or publicity pertaining to distribution of the software
12without specific, written prior permission.
13
14ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22If you make any modifications, bugfixes or other changes to this software
23we'd appreciate it if you could send a copy to us so we can keep things
24up-to-date.  Many thanks.
25				Kee Hinckley
26				Alfalfa Software, Inc.
27				267 Allston St., #3
28				Cambridge, MA 02139  USA
29				nazgul@alfalfa.com
30
31******************************************************************/
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: src/usr.bin/gencat/genlib.c,v 1.13 2002/12/24 07:40:10 davidxu Exp $");
35
36#include <ctype.h>
37#include <err.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include "msgcat.h"
43#include "gencat.h"
44#include <machine/endian.h>
45/* libkern/OSByteOrder is needed for the 64 bit byte swap */
46#include <libkern/OSByteOrder.h>
47
48#ifndef htonll
49#define htonll(x) OSSwapHostToBigInt64(x)
50#define ntohll(x) OSSwapBigToHostInt64(x)
51#endif
52
53static char *curline = NULL;
54static long lineno = 0;
55
56static void
57warning(char *cptr, const char *msg)
58{
59    warnx("%s on line %ld\n%s", msg, lineno, (curline == NULL ? "" : curline) );
60    if (cptr) {
61	char	*tptr;
62	for (tptr = curline; tptr < cptr; ++tptr) putc(' ', stderr);
63	fprintf(stderr, "^\n");
64    }
65}
66
67static void
68error(char *cptr, const char *msg)
69{
70    warning(cptr, msg);
71    exit(1);
72}
73
74static void
75corrupt(void) {
76    error(NULL, "corrupt message catalog");
77}
78
79static void
80nomem(void) {
81    error(NULL, "out of memory");
82}
83
84static char *
85gencat_getline(int fd)
86{
87    static size_t curlen = BUFSIZ;
88    static char	buf[BUFSIZ], *bptr = buf, *bend = buf;
89    char	*cptr, *cend;
90    long	buflen;
91
92    if (!curline) {
93	curline = (char *) malloc(curlen);
94	if (!curline) nomem();
95    }
96    ++lineno;
97
98    cptr = curline;
99    cend = curline + curlen;
100    while (TRUE) {
101	for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
102	    if (*bptr == '\n') {
103		*cptr = '\0';
104		++bptr;
105		return(curline);
106	    } else *cptr = *bptr;
107	}
108	if (bptr == bend) {
109	    buflen = read(fd, buf, BUFSIZ);
110	    if (buflen <= 0) {
111		if (cptr > curline) {
112		    *cptr = '\0';
113		    return(curline);
114		}
115		return(NULL);
116	    }
117	    bend = buf + buflen;
118	    bptr = buf;
119	}
120	if (cptr == cend) {
121	    cptr = curline = (char *) realloc(curline, curlen *= 2);
122	    if (!curline) nomem();
123	    cend = curline + curlen;
124	}
125    }
126}
127
128static char *
129token(char *cptr)
130{
131    static char	tok[MAXTOKEN+1];
132    char	*tptr = tok;
133
134    while (*cptr && isspace((unsigned char)*cptr)) ++cptr;
135    while (*cptr && !isspace((unsigned char)*cptr)) *tptr++ = *cptr++;
136    *tptr = '\0';
137    return(tok);
138}
139
140static char *
141wskip(char *cptr)
142{
143    if (!*cptr || !isspace((unsigned char)*cptr)) {
144	warning(cptr, "expected a space");
145	return(cptr);
146    }
147    while (*cptr && isspace((unsigned char)*cptr)) ++cptr;
148    return(cptr);
149}
150
151static char *
152cskip(char *cptr)
153{
154    if (!*cptr || isspace((unsigned char)*cptr)) {
155	warning(cptr, "wasn't expecting a space");
156	return(cptr);
157    }
158    while (*cptr && !isspace((unsigned char)*cptr)) ++cptr;
159    return(cptr);
160}
161
162static char *
163getmsg(int fd, char *cptr, char quote)
164{
165    static char		*msg = NULL;
166    static size_t	msglen = 0;
167    size_t		clen, i;
168    char		*tptr;
169    int			needq;
170
171    if (quote && *cptr == quote) {
172	needq = TRUE;
173	++cptr;
174    } else needq = FALSE;
175
176    clen = strlen(cptr) + 1;
177    if (clen > msglen) {
178	if (msglen) msg = (char *) realloc(msg, clen);
179	else msg = (char *) malloc(clen);
180	if (!msg) nomem();
181	msglen = clen;
182    }
183    tptr = msg;
184
185    while (*cptr) {
186	if (quote && *cptr == quote) {
187	    char	*tmp;
188	    tmp = cptr+1;
189	    if (*tmp && (!isspace((unsigned char)*tmp) || *wskip(tmp))) {
190		warning(cptr, "unexpected quote character, ignoring");
191		*tptr++ = *cptr++;
192	    } else {
193		*cptr = '\0';
194	    }
195	} else if (*cptr == '\\') {
196	    ++cptr;
197	    switch (*cptr) {
198	      case '\0':
199		cptr = gencat_getline(fd);
200		if (!cptr) error(NULL, "premature end of file");
201		msglen += strlen(cptr);
202		i = tptr - msg;
203		msg = (char *) realloc(msg, msglen);
204		if (!msg) nomem();
205		tptr = msg + i;
206		break;
207
208#define CASEOF(CS, CH)			\
209              case CS:			\
210		*tptr++ = CH;		\
211		++cptr;			\
212		break;
213
214		CASEOF('n', '\n')
215		CASEOF('t', '\t')
216		CASEOF('v', '\v')
217		CASEOF('b', '\b')
218		CASEOF('r', '\r')
219		CASEOF('f', '\f')
220		CASEOF('"', '"')
221		CASEOF('\'', '\'')
222		CASEOF('\\', '\\')
223
224	      default:
225		if (isdigit((unsigned char)*cptr)) {
226		    *tptr = 0;
227		    for (i = 0; i < 3; ++i) {
228			if (!isdigit((unsigned char)*cptr)) break;
229			if (*cptr > '7') warning(cptr, "octal number greater than 7?!");
230			*tptr *= 8;
231			*tptr += (*cptr - '0');
232			++cptr;
233		    }
234		    ++tptr;
235		} else {
236		    warning(cptr, "unrecognized escape sequence");
237		}
238	    }
239	} else {
240	    *tptr++ = *cptr++;
241	}
242    }
243    *tptr = '\0';
244    return(msg);
245}
246
247static char *
248dupstr(const char *ostr)
249{
250    char	*nstr;
251
252    nstr = strdup(ostr);
253    if (!nstr) error(NULL, "unable to allocate storage");
254    return(nstr);
255}
256
257/*
258 * The Global Stuff
259 */
260
261typedef struct _msgT {
262    long	msgId;
263    char	*str;
264    char	*hconst;
265    long	offset;
266    struct _msgT	*prev, *next;
267} msgT;
268
269typedef struct _setT {
270    long	setId;
271    char	*hconst;
272    msgT	*first, *last;
273    struct _setT	*prev, *next;
274} setT;
275
276typedef struct {
277    setT	*first, *last;
278} catT;
279
280static setT	*curSet;
281static catT	*cat;
282
283/*
284 * Find the current byte order.  There are of course some others, but
285 * this will do for now.  Note that all we care about is "long".
286 */
287long
288MCGetByteOrder(void) {
289    long	l = 0x00010203;
290    char	*cptr = (char *) &l;
291
292    if (cptr[0] == 0 && cptr[1] == 1 && cptr[2] == 2 && cptr[3] == 3)
293      return MC68KByteOrder;
294    else return MCn86ByteOrder;
295}
296
297void
298MCParse(int fd)
299{
300    char	*cptr, *str;
301    int		setid = 1, msgid = 0;
302    char	hconst[MAXTOKEN+1];
303    char	quote = 0;
304
305    if (!cat) {
306	cat = (catT *) malloc(sizeof(catT));
307	if (!cat) nomem();
308	bzero(cat, sizeof(catT));
309    }
310
311    hconst[0] = '\0';
312
313    while ((cptr = gencat_getline(fd)) != NULL) {
314	if (*cptr == '$') {
315	    ++cptr;
316	    if (strncmp(cptr, "set", 3) == 0) {
317		cptr += 3;
318		cptr = wskip(cptr);
319		setid = atoi(cptr);
320		cptr = cskip(cptr);
321		if (*cptr) cptr = wskip(cptr);
322		if (*cptr == '#') {
323		    ++cptr;
324		    MCAddSet(setid, token(cptr));
325		} else MCAddSet(setid, NULL);
326		msgid = 0;
327	    } else if (strncmp(cptr, "delset", 6) == 0) {
328		cptr += 6;
329		cptr = wskip(cptr);
330		setid = atoi(cptr);
331		MCDelSet(setid);
332	    } else if (strncmp(cptr, "quote", 5) == 0) {
333		cptr += 5;
334		if (!*cptr) quote = 0;
335		else {
336		    cptr = wskip(cptr);
337		    if (!*cptr) quote = 0;
338		    else quote = *cptr;
339		}
340	    } else if (isspace((unsigned char)*cptr)) {
341		cptr = wskip(cptr);
342		if (*cptr == '#') {
343		    ++cptr;
344		    strcpy(hconst, token(cptr));
345		}
346	    } else {
347		if (*cptr) {
348		    cptr = wskip(cptr);
349		    if (*cptr) warning(cptr, "unrecognized line");
350		}
351	    }
352	} else {
353	    if (!curSet) MCAddSet(setid, NULL);
354	    if (isdigit((unsigned char)*cptr) || *cptr == '#') {
355		if (*cptr == '#') {
356		    ++msgid;
357		    ++cptr;
358		    if (!*cptr) {
359			MCAddMsg(msgid, "", hconst);
360			hconst[0] = '\0';
361			continue;
362		    }
363		    if (!isspace((unsigned char)*cptr)) warning(cptr, "expected a space");
364		    ++cptr;
365		    if (!*cptr) {
366			MCAddMsg(msgid, "", hconst);
367			hconst[0] = '\0';
368			continue;
369		    }
370		} else {
371		    msgid = atoi(cptr);
372		    cptr = cskip(cptr);
373		    if (isspace(*cptr))
374			    cptr++;
375		    /* if (*cptr) ++cptr; */
376		}
377		if (!*cptr) {
378			if (isspace(cptr[-1])) {
379				MCAddMsg(msgid, "", hconst);
380				hconst[0] = '\0';
381			} else {
382				MCDelMsg(msgid);
383			}
384		} else {
385		    str = getmsg(fd, cptr, quote);
386		    MCAddMsg(msgid, str, hconst);
387		    hconst[0] = '\0';
388		}
389	    }
390	}
391    }
392}
393
394void
395MCReadCat(int fd)
396{
397    MCHeaderT	mcHead;
398    MCMsgT	mcMsg;
399    MCSetT	mcSet;
400    msgT	*msg;
401    setT	*set;
402    int		i;
403    char	*data;
404
405    cat = (catT *) malloc(sizeof(catT));
406    if (!cat) nomem();
407    bzero(cat, sizeof(catT));
408
409    /* While we deal with read/write this in network byte order we do NOT
410      deal with struct member padding issues, or even sizeof(long) issues,
411      those are left for a future genneration to curse either me, or the
412      original author for */
413
414    if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead)) corrupt();
415    if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0) corrupt();
416    if (ntohl(mcHead.majorVer) != MCMajorVer) error(NULL, "unrecognized catalog version");
417    if ((ntohl(mcHead.flags) & MC68KByteOrder) == 0) error(NULL, "wrong byte order");
418
419    if (lseek(fd, ntohll(mcHead.firstSet), L_SET) == -1) corrupt();
420
421    while (TRUE) {
422	if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet)) corrupt();
423	if (mcSet.invalid) continue;
424
425	set = (setT *) malloc(sizeof(setT));
426	if (!set) nomem();
427	bzero(set, sizeof(*set));
428	if (cat->first) {
429	    cat->last->next = set;
430	    set->prev = cat->last;
431	    cat->last = set;
432	} else cat->first = cat->last = set;
433
434	set->setId = ntohl(mcSet.setId);
435
436	/* Get the data */
437	if (mcSet.dataLen) {
438	    data = (char *) malloc((size_t)ntohl(mcSet.dataLen));
439	    if (!data) nomem();
440	    if (lseek(fd, ntohll(mcSet.data.off), L_SET) == -1) corrupt();
441	    if (read(fd, data, (size_t)ntohl(mcSet.dataLen)) != ntohl(mcSet.dataLen)) corrupt();
442	    if (lseek(fd, ntohll(mcSet.u.firstMsg), L_SET) == -1) corrupt();
443
444	    for (i = 0; i < ntohl(mcSet.numMsgs); ++i) {
445		if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg)) corrupt();
446		if (mcMsg.invalid) {
447		    --i;
448		    continue;
449		}
450
451		msg = (msgT *) malloc(sizeof(msgT));
452		if (!msg) nomem();
453		bzero(msg, sizeof(*msg));
454		if (set->first) {
455		    set->last->next = msg;
456		    msg->prev = set->last;
457		    set->last = msg;
458		} else set->first = set->last = msg;
459
460		msg->msgId = ntohl(mcMsg.msgId);
461		msg->str = dupstr((char *) (data + ntohll(mcMsg.msg.off)));
462	    }
463	    free(data);
464	}
465	if (!mcSet.nextSet) break;
466	if (lseek(fd, ntohll(mcSet.nextSet), L_SET) == -1) corrupt();
467    }
468}
469
470
471static void
472printS(int fd, const char *str)
473{
474    if (str)
475	write(fd, str, strlen(str));
476}
477
478static void
479printL(int fd, long l)
480{
481    char	buf[32];
482    sprintf(buf, "%ld", l);
483    write(fd, buf, strlen(buf));
484}
485
486static void
487printLX(int fd, long l)
488{
489    char	buf[32];
490    sprintf(buf, "%lx", l);
491    write(fd, buf, strlen(buf));
492}
493
494static void
495genconst(int fd, int type, char *setConst, char *msgConst, long val)
496{
497    switch (type) {
498      case MCLangC:
499	if (!msgConst) {
500	    printS(fd, "\n#define ");
501	    printS(fd, setConst);
502	    printS(fd, "Set");
503	} else {
504	    printS(fd, "#define ");
505	    printS(fd, setConst);
506	    printS(fd, msgConst);
507	}
508	printS(fd, "\t0x");
509	printLX(fd, val);
510	printS(fd, "\n");
511	break;
512      case MCLangCPlusPlus:
513      case MCLangANSIC:
514	if (!msgConst) {
515	    printS(fd, "\nconst long ");
516	    printS(fd, setConst);
517	    printS(fd, "Set");
518	} else {
519	    printS(fd, "const long ");
520	    printS(fd, setConst);
521	    printS(fd, msgConst);
522	}
523	printS(fd, "\t= ");
524	printL(fd, val);
525	printS(fd, ";\n");
526	break;
527      default:
528	error(NULL, "not a recognized (programming) language type");
529    }
530}
531
532void
533MCWriteConst(int fd, int type, int orConsts)
534{
535    msgT	*msg;
536    setT	*set;
537    long	id;
538
539    if (orConsts && (type == MCLangC || type == MCLangCPlusPlus || type == MCLangANSIC)) {
540	printS(fd, "/* Use these Macros to compose and decompose setId's and msgId's */\n");
541	printS(fd, "#ifndef MCMakeId\n");
542        printS(fd, "# define MCMakeId(s,m)\t(unsigned long)(((unsigned short)s<<(sizeof(short)*8))\\\n");
543        printS(fd, "\t\t\t\t\t|(unsigned short)m)\n");
544        printS(fd, "# define MCSetId(id)\t(unsigned int) (id >> (sizeof(short) * 8))\n");
545        printS(fd, "# define MCMsgId(id)\t(unsigned int) ((id << (sizeof(short) * 8))\\\n");
546        printS(fd, "\t\t\t\t\t>> (sizeof(short) * 8))\n");
547	printS(fd, "#endif\n");
548    }
549
550    for (set = cat->first; set; set = set->next) {
551	if (set->hconst) genconst(fd, type, set->hconst, NULL, set->setId);
552
553	for (msg = set->first; msg; msg = msg->next) {
554	    if (msg->hconst) {
555		if (orConsts) id = MCMakeId(set->setId, msg->msgId);
556		else id = msg->msgId;
557		genconst(fd, type, set->hconst, msg->hconst, id);
558		free(msg->hconst);
559		msg->hconst = NULL;
560	    }
561	}
562	if (set->hconst) {
563	    free(set->hconst);
564	    set->hconst = NULL;
565	}
566    }
567}
568
569void
570MCWriteCat(int fd)
571{
572    MCHeaderT	mcHead;
573    int		cnt;
574    setT	*set;
575    msgT	*msg;
576    MCSetT	mcSet;
577    MCMsgT	mcMsg;
578    off_t	pos;
579
580    bcopy(MCMagic, mcHead.magic, MCMagicLen);
581    mcHead.majorVer = htonl(MCMajorVer);
582    mcHead.minorVer = htonl(MCMinorVer);
583    mcHead.flags = htonl(MC68KByteOrder);
584    mcHead.firstSet = 0;	/* We'll be back to set this in a minute */
585
586    if (cat == NULL)
587	error(NULL, "cannot write empty catalog set");
588
589    for (cnt = 0, set = cat->first; set; set = set->next) ++cnt;
590    mcHead.numSets = htonl(cnt);
591
592    /* I'm not inclined to mess with it, but it looks odd that we write
593      the header twice...and that we get the firstSet value from another
594      lseek rather then just 'sizeof(mcHead)' */
595
596    /* Also, this code doesn't seem to check returns from write! */
597
598    lseek(fd, (off_t)0L, L_SET);
599    write(fd, &mcHead, sizeof(mcHead));
600    mcHead.firstSet = htonll(lseek(fd, (off_t)0L, L_INCR));
601    lseek(fd, (off_t)0L, L_SET);
602    write(fd, &mcHead, sizeof(mcHead));
603
604    for (set = cat->first; set; set = set->next) {
605	bzero(&mcSet, sizeof(mcSet));
606
607	mcSet.setId = htonl(set->setId);
608	mcSet.invalid = FALSE;
609
610	/* The rest we'll have to come back and change in a moment */
611	pos = lseek(fd, (off_t)0L, L_INCR);
612	write(fd, &mcSet, sizeof(mcSet));
613
614	/* Now write all the string data */
615	mcSet.data.off = htonll(lseek(fd, (off_t)0L, L_INCR));
616	cnt = 0;
617	for (msg = set->first; msg; msg = msg->next) {
618	    msg->offset = lseek(fd, (off_t)0L, L_INCR) - ntohll(mcSet.data.off);
619	    mcSet.dataLen += write(fd, msg->str, strlen(msg->str) + 1);
620	    ++cnt;
621	}
622	mcSet.u.firstMsg = htonll(lseek(fd, (off_t)0L, L_INCR));
623	mcSet.numMsgs = htonl(cnt);
624	mcSet.dataLen = htonl(mcSet.dataLen);
625
626	/* Now write the message headers */
627	for (msg = set->first; msg; msg = msg->next) {
628	    mcMsg.msgId = htonl(msg->msgId);
629	    mcMsg.msg.off = htonll(msg->offset);
630	    mcMsg.invalid = FALSE;
631	    write(fd, &mcMsg, sizeof(mcMsg));
632	}
633
634	/* Go back and fix things up */
635
636	if (set == cat->last) {
637	    mcSet.nextSet = 0;
638	    lseek(fd, pos, L_SET);
639	    write(fd, &mcSet, sizeof(mcSet));
640	} else {
641	    mcSet.nextSet = htonll(lseek(fd, (off_t)0L, L_INCR));
642	    lseek(fd, pos, L_SET);
643	    write(fd, &mcSet, sizeof(mcSet));
644	    lseek(fd, ntohll(mcSet.nextSet), L_SET);
645	}
646    }
647}
648
649void
650MCAddSet(int setId, char *hconst)
651{
652    setT	*set;
653
654    if (setId <= 0) {
655	error(NULL, "setId's must be greater than zero");
656	return;
657    }
658
659    if (hconst && !*hconst) hconst = NULL;
660    for (set = cat->first; set; set = set->next) {
661	if (set->setId == setId) {
662	    if (set->hconst && hconst) free(set->hconst);
663	    set->hconst = NULL;
664	    break;
665	} else if (set->setId > setId) {
666	    setT	*newSet;
667
668	    newSet = (setT *) malloc(sizeof(setT));
669	    if (!newSet) nomem();
670	    bzero(newSet, sizeof(setT));
671	    newSet->prev = set->prev;
672	    newSet->next = set;
673	    if (set->prev) set->prev->next = newSet;
674	    else cat->first = newSet;
675	    set->prev = newSet;
676	    set = newSet;
677	    break;
678	}
679    }
680    if (!set) {
681	set = (setT *) malloc(sizeof(setT));
682	if (!set) nomem();
683	bzero(set, sizeof(setT));
684
685	if (cat->first) {
686	    set->prev = cat->last;
687	    set->next = NULL;
688	    cat->last->next = set;
689	    cat->last = set;
690	} else {
691	    set->prev = set->next = NULL;
692	    cat->first = cat->last = set;
693	}
694    }
695    set->setId = setId;
696    if (hconst) set->hconst = dupstr(hconst);
697    curSet = set;
698}
699
700void
701MCAddMsg(int msgId, const char *str, char *hconst)
702{
703    msgT	*msg;
704
705    if (!curSet)
706	error(NULL, "can't specify a message when no set exists");
707
708    if (msgId <= 0) {
709	error(NULL, "msgId's must be greater than zero");
710	return;
711    }
712
713    if (hconst && !*hconst) hconst = NULL;
714    for (msg = curSet->first; msg; msg = msg->next) {
715	if (msg->msgId == msgId) {
716	    if (msg->hconst && hconst) free(msg->hconst);
717	    if (msg->str) free(msg->str);
718	    msg->hconst = msg->str = NULL;
719	    break;
720	} else if (msg->msgId > msgId) {
721	    msgT	*newMsg;
722
723	    newMsg = (msgT *) malloc(sizeof(msgT));
724	    if (!newMsg) nomem();
725	    bzero(newMsg, sizeof(msgT));
726	    newMsg->prev = msg->prev;
727	    newMsg->next = msg;
728	    if (msg->prev) msg->prev->next = newMsg;
729	    else curSet->first = newMsg;
730	    msg->prev = newMsg;
731	    msg = newMsg;
732	    break;
733	}
734    }
735    if (!msg) {
736	msg = (msgT *) malloc(sizeof(msgT));
737	if (!msg) nomem();
738	bzero(msg, sizeof(msgT));
739
740	if (curSet->first) {
741	    msg->prev = curSet->last;
742	    msg->next = NULL;
743	    curSet->last->next = msg;
744	    curSet->last = msg;
745	} else {
746	    msg->prev = msg->next = NULL;
747	    curSet->first = curSet->last = msg;
748	}
749    }
750    msg->msgId = msgId;
751    if (hconst) msg->hconst = dupstr(hconst);
752    msg->str = dupstr(str);
753}
754
755void
756MCDelSet(int setId)
757{
758    setT	*set;
759    msgT	*msg;
760
761    for (set = cat->first; set; set = set->next) {
762	if (set->setId == setId) {
763	    for (msg = set->first; msg; msg = msg->next) {
764		if (msg->hconst) free(msg->hconst);
765		if (msg->str) free(msg->str);
766		free(msg);
767	    }
768	    if (set->hconst) free(set->hconst);
769
770	    if (set->prev) set->prev->next = set->next;
771	    else cat->first = set->next;
772
773	    if (set->next) set->next->prev = set->prev;
774	    else cat->last = set->prev;
775
776	    free(set);
777	    return;
778	} else if (set->setId > setId) break;
779    }
780    warning(NULL, "specified set doesn't exist");
781}
782
783void
784MCDelMsg(int msgId)
785{
786    msgT	*msg;
787
788    if (!curSet)
789	error(NULL, "you can't delete a message before defining the set");
790
791    for (msg = curSet->first; msg; msg = msg->next) {
792	if (msg->msgId == msgId) {
793	    if (msg->hconst) free(msg->hconst);
794	    if (msg->str) free(msg->str);
795
796	    if (msg->prev) msg->prev->next = msg->next;
797	    else curSet->first = msg->next;
798
799	    if (msg->next) msg->next->prev = msg->prev;
800	    else curSet->last = msg->prev;
801
802	    free(msg);
803	    return;
804	} else if (msg->msgId > msgId) break;
805    }
806    warning(NULL, "specified msg doesn't exist");
807}
808
809#if 0 /* this function is unsed and looks like debug thing */
810
811void
812MCDumpcat(fp)
813FILE  *fp;
814{
815    msgT	*msg;
816    setT	*set;
817
818    if (!cat)
819	errx(1, "no catalog open");
820
821    for (set = cat->first; set; set = set->next) {
822	fprintf(fp, "$set %ld", set->setId);
823	if (set->hconst)
824	    fprintf(fp, " # %s", set->hconst);
825	fprintf(fp, "\n\n");
826
827	for (msg = set->first; msg; msg = msg->next) {
828	    if (msg->hconst)
829		fprintf(fp, "# %s\n", msg->hconst);
830	    fprintf(fp, "%ld\t%s\n", msg->msgId, msg->str);
831	}
832	fprintf(fp, "\n");
833    }
834
835}
836#endif /* 0 */
837