gencat.c revision 1.26 1 /* $NetBSD: gencat.c,v 1.26 2008/11/04 03:14:46 ginsbach 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.26 2008/11/04 03:14:46 ginsbach 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 long 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 msgcat = xmalloc(cat_hdr.__mem);
563
564 cat_hdr.__nsets = ntohl(cat_hdr.__nsets);
565 cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset);
566 cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset);
567 if ((cat_hdr.__mem < 0) ||
568 (cat_hdr.__msg_hdr_offset < 0) ||
569 (cat_hdr.__msg_txt_offset < 0) ||
570 (cat_hdr.__mem < (cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) ||
571 (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) ||
572 (cat_hdr.__mem < cat_hdr.__msg_txt_offset))
573 errx(1, "%s: catalog header", CORRUPT);
574
575 n = read(fd, msgcat, cat_hdr.__mem);
576 if (n < cat_hdr.__mem) {
577 if (n == -1)
578 err(1, "data read");
579 else
580 errx(1, CORRUPT);
581 }
582
583 set_hdr = (struct _nls_set_hdr *)msgcat;
584 msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat +
585 cat_hdr.__msg_hdr_offset);
586 strings = (char *)msgcat + cat_hdr.__msg_txt_offset;
587
588 setno = 0;
589 for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) {
590 set_hdr->__setno = ntohl(set_hdr->__setno);
591 if (set_hdr->__setno < setno)
592 errx(1, "%s: bad set number (%d)",
593 CORRUPT, set_hdr->__setno);
594 setno = set_hdr->__setno;
595
596 MCAddSet(setno);
597
598 set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs);
599 set_hdr->__index = ntohl(set_hdr->__index);
600 if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0)
601 errx(1, "%s: set header", CORRUPT);
602
603 /* Get the data */
604 msgno = 0;
605 for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) {
606 msg_hdr->__msgno = ntohl(msg_hdr->__msgno);
607 msg_hdr->__offset = ntohl(msg_hdr->__offset);
608 if (msg_hdr->__msgno < msgno)
609 errx(1, "%s: bad message number (%d)",
610 CORRUPT, msg_hdr->__msgno);
611 if ((msg_hdr->__offset < 0) ||
612 ((strings + msg_hdr->__offset) >
613 ((char *)msgcat + cat_hdr.__mem)))
614 errx(1, "%s: message header", CORRUPT);
615
616 msgno = msg_hdr->__msgno;
617 MCAddMsg(msgno, strings + msg_hdr->__offset);
618 }
619 }
620 free(msgcat);
621 }
622
623 /*
624 * Write message catalog.
625 *
626 * The message catalog is first converted from its internal to its
627 * external representation in a chunk of memory allocated for this
628 * purpose. Then the completed catalog is written. This approach
629 * avoids additional housekeeping variables and/or a lot of seeks
630 * that would otherwise be required.
631 */
632 void
633 MCWriteCat(int fd)
634 {
635 int nsets; /* number of sets */
636 int nmsgs; /* number of msgs */
637 int string_size; /* total size of string pool */
638 int msgcat_size; /* total size of message catalog */
639 void *msgcat; /* message catalog data */
640 struct _nls_cat_hdr *cat_hdr;
641 struct _nls_set_hdr *set_hdr;
642 struct _nls_msg_hdr *msg_hdr;
643 char *strings;
644 struct _setT *set;
645 struct _msgT *msg;
646 int msg_index;
647 int msg_offset;
648
649 /* determine number of sets, number of messages, and size of the
650 * string pool */
651 nsets = 0;
652 nmsgs = 0;
653 string_size = 0;
654
655 for (set = sethead.lh_first; set != NULL;
656 set = set->entries.le_next) {
657 nsets++;
658
659 for (msg = set->msghead.lh_first; msg != NULL;
660 msg = msg->entries.le_next) {
661 nmsgs++;
662 string_size += strlen(msg->str) + 1;
663 }
664 }
665
666 #ifdef DEBUG
667 printf("number of sets: %d\n", nsets);
668 printf("number of msgs: %d\n", nmsgs);
669 printf("string pool size: %d\n", string_size);
670 #endif
671
672 /* determine size and then allocate buffer for constructing external
673 * message catalog representation */
674 msgcat_size = sizeof(struct _nls_cat_hdr)
675 + (nsets * sizeof(struct _nls_set_hdr))
676 + (nmsgs * sizeof(struct _nls_msg_hdr))
677 + string_size;
678
679 msgcat = xmalloc(msgcat_size);
680 memset(msgcat, '\0', msgcat_size);
681
682 /* fill in msg catalog header */
683 cat_hdr = (struct _nls_cat_hdr *) msgcat;
684 cat_hdr->__magic = htonl(_NLS_MAGIC);
685 cat_hdr->__nsets = htonl(nsets);
686 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
687 cat_hdr->__msg_hdr_offset =
688 htonl(nsets * sizeof(struct _nls_set_hdr));
689 cat_hdr->__msg_txt_offset =
690 htonl(nsets * sizeof(struct _nls_set_hdr) +
691 nmsgs * sizeof(struct _nls_msg_hdr));
692
693 /* compute offsets for set & msg header tables and string pool */
694 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
695 sizeof(struct _nls_cat_hdr));
696 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
697 sizeof(struct _nls_cat_hdr) +
698 nsets * sizeof(struct _nls_set_hdr));
699 strings = (char *) msgcat +
700 sizeof(struct _nls_cat_hdr) +
701 nsets * sizeof(struct _nls_set_hdr) +
702 nmsgs * sizeof(struct _nls_msg_hdr);
703
704 msg_index = 0;
705 msg_offset = 0;
706 for (set = sethead.lh_first; set != NULL;
707 set = set->entries.le_next) {
708
709 nmsgs = 0;
710 for (msg = set->msghead.lh_first; msg != NULL;
711 msg = msg->entries.le_next) {
712 int msg_len = strlen(msg->str) + 1;
713
714 msg_hdr->__msgno = htonl(msg->msgId);
715 msg_hdr->__msglen = htonl(msg_len);
716 msg_hdr->__offset = htonl(msg_offset);
717
718 memcpy(strings, msg->str, msg_len);
719 strings += msg_len;
720 msg_offset += msg_len;
721
722 nmsgs++;
723 msg_hdr++;
724 }
725
726 set_hdr->__setno = htonl(set->setId);
727 set_hdr->__nmsgs = htonl(nmsgs);
728 set_hdr->__index = htonl(msg_index);
729 msg_index += nmsgs;
730 set_hdr++;
731 }
732
733 /* write out catalog. XXX: should this be done in small chunks? */
734 write(fd, msgcat, msgcat_size);
735 }
736
737 void
738 MCAddSet(int setId)
739 {
740 struct _setT *p, *q;
741
742 if (setId <= 0) {
743 error("setId's must be greater than zero");
744 /* NOTREACHED */
745 }
746 if (setId > NL_SETMAX) {
747 error("setId exceeds limit");
748 /* NOTREACHED */
749 }
750
751 p = sethead.lh_first;
752 q = NULL;
753 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
754
755 if (p && p->setId == setId) {
756 ;
757 } else {
758 p = xmalloc(sizeof(struct _setT));
759 memset(p, '\0', sizeof(struct _setT));
760 LIST_INIT(&p->msghead);
761
762 p->setId = setId;
763
764 if (q == NULL) {
765 LIST_INSERT_HEAD(&sethead, p, entries);
766 } else {
767 LIST_INSERT_AFTER(q, p, entries);
768 }
769 }
770
771 curSet = p;
772 }
773
774 void
775 MCAddMsg(int msgId, const char *str)
776 {
777 struct _msgT *p, *q;
778
779 if (!curSet)
780 error("can't specify a message when no set exists");
781
782 if (msgId <= 0) {
783 error("msgId's must be greater than zero");
784 /* NOTREACHED */
785 }
786 if (msgId > NL_MSGMAX) {
787 error("msgID exceeds limit");
788 /* NOTREACHED */
789 }
790
791 p = curSet->msghead.lh_first;
792 q = NULL;
793 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
794
795 if (p && p->msgId == msgId) {
796 free(p->str);
797 } else {
798 p = xmalloc(sizeof(struct _msgT));
799 memset(p, '\0', sizeof(struct _msgT));
800
801 if (q == NULL) {
802 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
803 } else {
804 LIST_INSERT_AFTER(q, p, entries);
805 }
806 }
807
808 p->msgId = msgId;
809 p->str = xstrdup(str);
810 }
811
812 void
813 MCDelSet(int setId)
814 {
815 struct _setT *set;
816 struct _msgT *msg;
817
818 if (setId <= 0) {
819 error("setId's must be greater than zero");
820 /* NOTREACHED */
821 }
822 if (setId > NL_SETMAX) {
823 error("setId exceeds limit");
824 /* NOTREACHED */
825 }
826
827 set = sethead.lh_first;
828 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
829
830 if (set && set->setId == setId) {
831 LIST_REMOVE(set, entries);
832 while ((msg = set->msghead.lh_first) != NULL) {
833 LIST_REMOVE(msg, entries);
834 free(msg->str);
835 free(msg);
836 }
837 free(set);
838 return;
839 }
840 warning(NULL, "specified set doesn't exist");
841 }
842
843 void
844 MCDelMsg(int msgId)
845 {
846 struct _msgT *msg;
847
848 if (!curSet)
849 error("you can't delete a message before defining the set");
850
851 msg = curSet->msghead.lh_first;
852 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
853
854 if (msg && msg->msgId == msgId) {
855 LIST_REMOVE(msg, entries);
856 free(msg->str);
857 free(msg);
858 return;
859 }
860 warning(NULL, "specified msg doesn't exist");
861 }
862