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