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