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