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