1 /* $NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb 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.36 2013/11/27 17:38:11 apb 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@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 static LIST_HEAD(sethead, _setT) sethead = LIST_HEAD_INITIALIZER(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 __dead static void error(const char *);
120 static char *get_line(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 static void MCParse(int fd);
129 static void MCReadCat(int fd);
130 static void MCWriteCat(int fd);
131 static void MCDelMsg(int msgId);
132 static void MCAddMsg(int msgId, const char *msg);
133 static void MCAddSet(int setId);
134 static void MCDelSet(int setId);
135 __dead static void usage(void);
136
137 #define CORRUPT "corrupt message catalog"
138 #define NOMEMORY "out of memory"
139
140 static void
usage(void)141 usage(void)
142 {
143 fprintf(stderr, "usage: %s catfile [msgfile|- ...]\n", getprogname());
144 exit(1);
145 }
146
147 int
main(int argc,char * argv[])148 main(int argc, char *argv[])
149 {
150 int ofd, ifd;
151 char *catfile = NULL;
152 int c;
153 int updatecat = 0;
154
155 while ((c = getopt(argc, argv, "")) != -1) {
156 switch (c) {
157 case '?':
158 default:
159 usage();
160 /* NOTREACHED */
161 }
162 }
163 argc -= optind;
164 argv += optind;
165
166 if (argc < 1) {
167 usage();
168 /* NOTREACHED */
169 }
170 catfile = *argv++;
171
172 if ((catfile[0] == '-') && (catfile[1] == '\0')) {
173 ofd = STDOUT_FILENO;
174 } else {
175 ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
176 if (ofd < 0) {
177 if (errno == EEXIST) {
178 if ((ofd = open(catfile, O_RDWR)) < 0) {
179 err(1, "Unable to open %s", catfile);
180 /* NOTREACHED */
181 }
182 } else {
183 err(1, "Unable to create new %s", catfile);
184 /* NOTREACHED */
185 }
186 curfile = catfile;
187 updatecat = 1;
188 MCReadCat(ofd);
189 if (lseek(ofd, (off_t)0, SEEK_SET) == (off_t)-1) {
190 err(1, "Unable to seek on %s", catfile);
191 /* NOTREACHED */
192 }
193 }
194 }
195
196 if (argc < 2 || (((*argv)[0] == '-') && ((*argv)[1] == '\0'))) {
197 if (argc > 2)
198 usage();
199 /* NOTREACHED */
200 MCParse(STDIN_FILENO);
201 } else {
202 for (; *argv; argv++) {
203 if ((ifd = open(*argv, O_RDONLY)) < 0)
204 err(1, "Unable to read %s", *argv);
205 curfile = *argv;
206 lineno = 0;
207 MCParse(ifd);
208 close(ifd);
209 }
210 }
211
212 if (updatecat) {
213 if (ftruncate(ofd, 0) != 0) {
214 err(1, "Unable to truncate %s", catfile);
215 /* NOTREACHED */
216 }
217 }
218
219 MCWriteCat(ofd);
220 exit(0);
221 }
222
223 static void
warning(const char * cptr,const char * msg)224 warning(const char *cptr, const char *msg)
225 {
226 if (lineno) {
227 fprintf(stderr, "%s: %s on line %ld, %s\n",
228 getprogname(), msg, lineno, curfile);
229 fprintf(stderr, "%s\n", curline);
230 if (cptr) {
231 char *tptr;
232 for (tptr = curline; tptr < cptr; ++tptr)
233 putc(' ', stderr);
234 fprintf(stderr, "^\n");
235 }
236 } else {
237 fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile);
238 }
239 }
240
241 static void
error(const char * msg)242 error(const char *msg)
243 {
244 warning(NULL, msg);
245 exit(1);
246 }
247
248 static void *
xmalloc(size_t len)249 xmalloc(size_t len)
250 {
251 void *p;
252
253 if ((p = malloc(len)) == NULL)
254 errx(1, NOMEMORY);
255 return (p);
256 }
257
258 static void *
xrealloc(void * ptr,size_t size)259 xrealloc(void *ptr, size_t size)
260 {
261 if ((ptr = realloc(ptr, size)) == NULL)
262 errx(1, NOMEMORY);
263 return (ptr);
264 }
265
266 static char *
xstrdup(const char * str)267 xstrdup(const char *str)
268 {
269 char *nstr;
270
271 if ((nstr = strdup(str)) == NULL)
272 errx(1, NOMEMORY);
273 return (nstr);
274 }
275
276 static char *
get_line(int fd)277 get_line(int fd)
278 {
279 static long curlen = BUFSIZ;
280 static char buf[BUFSIZ], *bptr = buf, *bend = buf;
281 char *cptr, *cend;
282 long buflen;
283
284 if (!curline) {
285 curline = xmalloc(curlen);
286 }
287 ++lineno;
288
289 cptr = curline;
290 cend = curline + curlen;
291 for (;;) {
292 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
293 if (*bptr == '\n') {
294 *cptr = '\0';
295 ++bptr;
296 return (curline);
297 } else
298 *cptr = *bptr;
299 }
300 if (cptr == cend) {
301 cptr = curline = xrealloc(curline, curlen *= 2);
302 cend = curline + curlen;
303 }
304 if (bptr == bend) {
305 buflen = read(fd, buf, BUFSIZ);
306 if (buflen <= 0) {
307 if (cptr > curline) {
308 *cptr = '\0';
309 return (curline);
310 }
311 return (NULL);
312 }
313 bend = buf + buflen;
314 bptr = buf;
315 }
316 }
317 }
318
319 static char *
wskip(char * cptr)320 wskip(char *cptr)
321 {
322 if (!*cptr || !isspace((unsigned char) *cptr)) {
323 warning(cptr, "expected a space");
324 return (cptr);
325 }
326 while (*cptr && isspace((unsigned char) *cptr))
327 ++cptr;
328 return (cptr);
329 }
330
331 static char *
cskip(char * cptr)332 cskip(char *cptr)
333 {
334 if (!*cptr || isspace((unsigned char) *cptr)) {
335 warning(cptr, "wasn't expecting a space");
336 return (cptr);
337 }
338 while (*cptr && !isspace((unsigned char) *cptr))
339 ++cptr;
340 return (cptr);
341 }
342
343 static char *
getmsg(int fd,char * cptr,char quote)344 getmsg(int fd, char *cptr, char quote)
345 {
346 static char *msg = NULL;
347 static size_t msglen = 0;
348 size_t clen, i;
349 int in_quote = 0;
350 char *tptr;
351
352 if (quote && *cptr == quote) {
353 ++cptr;
354 in_quote = 1;
355 }
356
357 clen = strlen(cptr) + 1;
358 if (clen > msglen) {
359 if (msglen)
360 msg = xrealloc(msg, clen);
361 else
362 msg = xmalloc(clen);
363 msglen = clen;
364 }
365 tptr = msg;
366
367 while (*cptr) {
368 if (quote && *cptr == quote) {
369 char *tmp;
370 tmp = cptr + 1;
371 if (!in_quote) {
372 /* XXX hard error? */
373 warning(cptr, "unexpected quote character, ignoring");
374 *tptr++ = *cptr++;
375 } else {
376 cptr++;
377 /* don't use wskip() */
378 while (*cptr && isspace((unsigned char) *cptr))
379 #ifndef _BACKWARDS_COMPAT
380 cptr++;
381 #else
382 *tptr++ = *cptr++;
383 #endif
384 /* XXX hard error? */
385 if (*cptr)
386 warning(tmp, "unexpected extra characters, ignoring");
387 in_quote = 0;
388 #ifndef _BACKWARDS_COMPAT
389 break;
390 #endif
391 }
392 } else {
393 if (*cptr == '\\') {
394 ++cptr;
395 switch (*cptr) {
396 case '\0':
397 cptr = get_line(fd);
398 if (!cptr)
399 error("premature end of file");
400 msglen += strlen(cptr);
401 i = tptr - msg;
402 msg = xrealloc(msg, msglen);
403 tptr = msg + i;
404 break;
405 case 'n':
406 *tptr++ = '\n';
407 ++cptr;
408 break;
409 case 't':
410 *tptr++ = '\t';
411 ++cptr;
412 break;
413 case 'v':
414 *tptr++ = '\v';
415 ++cptr;
416 break;
417 case 'b':
418 *tptr++ = '\b';
419 ++cptr;
420 break;
421 case 'r':
422 *tptr++ = '\r';
423 ++cptr;
424 break;
425 case 'f':
426 *tptr++ = '\f';
427 ++cptr;
428 break;
429 case '\\':
430 *tptr++ = '\\';
431 ++cptr;
432 break;
433 default:
434 if (quote && *cptr == quote) {
435 *tptr++ = *cptr++;
436 } else if (isdigit((unsigned char) *cptr)) {
437 *tptr = 0;
438 for (i = 0; i < 3; ++i) {
439 if (!isdigit((unsigned char) *cptr))
440 break;
441 if (*cptr > '7')
442 warning(cptr, "octal number greater than 7?!");
443 *tptr *= 8;
444 *tptr += (*cptr - '0');
445 ++cptr;
446 }
447 } else {
448 warning(cptr, "unrecognized escape sequence");
449 }
450 break;
451 }
452 } else {
453 *tptr++ = *cptr++;
454 }
455 }
456 }
457
458 if (in_quote)
459 warning(cptr, "unterminated quoted message, ignoring");
460
461 *tptr = '\0';
462 return (msg);
463 }
464
465 static void
MCParse(int fd)466 MCParse(int fd)
467 {
468 char *cptr, *str;
469 int msgid = 0;
470 int setid = 0;
471 char quote = 0;
472
473 while ((cptr = get_line(fd))) {
474 if (*cptr == '$') {
475 ++cptr;
476 if (strncmp(cptr, "set", 3) == 0) {
477 cptr += 3;
478 cptr = wskip(cptr);
479 setid = atoi(cptr);
480 MCAddSet(setid);
481 msgid = 0;
482 } else if (strncmp(cptr, "delset", 6) == 0) {
483 cptr += 6;
484 cptr = wskip(cptr);
485 setid = atoi(cptr);
486 MCDelSet(setid);
487 } else if (strncmp(cptr, "quote", 5) == 0) {
488 cptr += 5;
489 if (!*cptr)
490 quote = 0;
491 else {
492 cptr = wskip(cptr);
493 if (!*cptr)
494 quote = 0;
495 else
496 quote = *cptr;
497 }
498 } else if (isspace((unsigned char) *cptr)) {
499 ;
500 } else {
501 if (*cptr) {
502 cptr = wskip(cptr);
503 if (*cptr)
504 warning(cptr, "unrecognized line");
505 }
506 }
507 } else {
508 /*
509 * First check for (and eat) empty lines....
510 */
511 if (!*cptr)
512 continue;
513 /*
514 * We have a digit? Start of a message. Else,
515 * syntax error.
516 */
517 if (isdigit((unsigned char) *cptr)) {
518 msgid = atoi(cptr);
519 cptr = cskip(cptr);
520 if (*cptr) {
521 cptr = wskip(cptr);
522 if (!*cptr) {
523 MCAddMsg(msgid, "");
524 continue;
525 }
526 }
527 } else {
528 warning(cptr, "neither blank line nor start of a message id");
529 continue;
530 }
531 /*
532 * If no set directive specified, all messages
533 * shall be in default message set NL_SETD.
534 */
535 if (setid == 0) {
536 setid = NL_SETD;
537 MCAddSet(setid);
538 }
539 /*
540 * If we have a message ID, but no message,
541 * then this means "delete this message id
542 * from the catalog".
543 */
544 if (!*cptr) {
545 MCDelMsg(msgid);
546 } else {
547 str = getmsg(fd, cptr, quote);
548 MCAddMsg(msgid, str);
549 }
550 }
551 }
552 }
553
554 static void
MCReadCat(int fd)555 MCReadCat(int fd)
556 {
557 void *msgcat; /* message catalog data */
558 struct _nls_cat_hdr cat_hdr;
559 struct _nls_set_hdr *set_hdr;
560 struct _nls_msg_hdr *msg_hdr;
561 char *strings;
562 ssize_t n;
563 int m, s;
564 int msgno, setno;
565
566 n = read(fd, &cat_hdr, sizeof(cat_hdr));
567 if (n < (ssize_t)sizeof(cat_hdr)) {
568 if (n == 0)
569 return; /* empty file */
570 else if (n == -1)
571 err(1, "header read");
572 else
573 errx(1, CORRUPT);
574 }
575 if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC)
576 errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic);
577
578 cat_hdr.__mem = ntohl(cat_hdr.__mem);
579
580 cat_hdr.__nsets = ntohl(cat_hdr.__nsets);
581 cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset);
582 cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset);
583 if ((cat_hdr.__mem < 0) ||
584 (cat_hdr.__msg_hdr_offset < 0) ||
585 (cat_hdr.__msg_txt_offset < 0) ||
586 (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) ||
587 (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) ||
588 (cat_hdr.__mem < cat_hdr.__msg_txt_offset))
589 errx(1, "%s: catalog header", CORRUPT);
590
591 msgcat = xmalloc(cat_hdr.__mem);
592
593 n = read(fd, msgcat, cat_hdr.__mem);
594 if (n < cat_hdr.__mem) {
595 if (n == -1)
596 err(1, "data read");
597 else
598 errx(1, CORRUPT);
599 }
600
601 set_hdr = (struct _nls_set_hdr *)msgcat;
602 msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat +
603 cat_hdr.__msg_hdr_offset);
604 strings = (char *)msgcat + cat_hdr.__msg_txt_offset;
605
606 setno = 0;
607 for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) {
608 set_hdr->__setno = ntohl(set_hdr->__setno);
609 if (set_hdr->__setno < setno)
610 errx(1, "%s: bad set number (%d)",
611 CORRUPT, set_hdr->__setno);
612 setno = set_hdr->__setno;
613
614 MCAddSet(setno);
615
616 set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs);
617 set_hdr->__index = ntohl(set_hdr->__index);
618 if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0)
619 errx(1, "%s: set header", CORRUPT);
620
621 /* Get the data */
622 msgno = 0;
623 for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) {
624 msg_hdr->__msgno = ntohl(msg_hdr->__msgno);
625 msg_hdr->__offset = ntohl(msg_hdr->__offset);
626 if (msg_hdr->__msgno < msgno)
627 errx(1, "%s: bad message number (%d)",
628 CORRUPT, msg_hdr->__msgno);
629 if ((msg_hdr->__offset < 0) ||
630 ((strings + msg_hdr->__offset) >
631 ((char *)msgcat + cat_hdr.__mem)))
632 errx(1, "%s: message header", CORRUPT);
633
634 msgno = msg_hdr->__msgno;
635 MCAddMsg(msgno, strings + msg_hdr->__offset);
636 }
637 }
638 free(msgcat);
639 }
640
641 /*
642 * Write message catalog.
643 *
644 * The message catalog is first converted from its internal to its
645 * external representation in a chunk of memory allocated for this
646 * purpose. Then the completed catalog is written. This approach
647 * avoids additional housekeeping variables and/or a lot of seeks
648 * that would otherwise be required.
649 */
650 static void
MCWriteCat(int fd)651 MCWriteCat(int fd)
652 {
653 int nsets; /* number of sets */
654 int nmsgs; /* number of msgs */
655 int string_size; /* total size of string pool */
656 int msgcat_size; /* total size of message catalog */
657 void *msgcat; /* message catalog data */
658 struct _nls_cat_hdr *cat_hdr;
659 struct _nls_set_hdr *set_hdr;
660 struct _nls_msg_hdr *msg_hdr;
661 char *strings;
662 struct _setT *set;
663 struct _msgT *msg;
664 int msg_index;
665 int msg_offset;
666
667 /* determine number of sets, number of messages, and size of the
668 * string pool */
669 nsets = 0;
670 nmsgs = 0;
671 string_size = 0;
672
673 LIST_FOREACH(set, &sethead, entries) {
674 nsets++;
675
676 LIST_FOREACH(msg, &set->msghead, entries) {
677 nmsgs++;
678 string_size += strlen(msg->str) + 1;
679 }
680 }
681
682 #ifdef DEBUG
683 printf("number of sets: %d\n", nsets);
684 printf("number of msgs: %d\n", nmsgs);
685 printf("string pool size: %d\n", string_size);
686 #endif
687
688 /* determine size and then allocate buffer for constructing external
689 * message catalog representation */
690 msgcat_size = sizeof(struct _nls_cat_hdr)
691 + (nsets * sizeof(struct _nls_set_hdr))
692 + (nmsgs * sizeof(struct _nls_msg_hdr))
693 + string_size;
694
695 msgcat = xmalloc(msgcat_size);
696 memset(msgcat, '\0', msgcat_size);
697
698 /* fill in msg catalog header */
699 cat_hdr = (struct _nls_cat_hdr *) msgcat;
700 cat_hdr->__magic = htonl(_NLS_MAGIC);
701 cat_hdr->__nsets = htonl(nsets);
702 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
703 cat_hdr->__msg_hdr_offset =
704 htonl(nsets * sizeof(struct _nls_set_hdr));
705 cat_hdr->__msg_txt_offset =
706 htonl(nsets * sizeof(struct _nls_set_hdr) +
707 nmsgs * sizeof(struct _nls_msg_hdr));
708
709 /* compute offsets for set & msg header tables and string pool */
710 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
711 sizeof(struct _nls_cat_hdr));
712 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
713 sizeof(struct _nls_cat_hdr) +
714 nsets * sizeof(struct _nls_set_hdr));
715 strings = (char *) msgcat +
716 sizeof(struct _nls_cat_hdr) +
717 nsets * sizeof(struct _nls_set_hdr) +
718 nmsgs * sizeof(struct _nls_msg_hdr);
719
720 msg_index = 0;
721 msg_offset = 0;
722 LIST_FOREACH(set, &sethead, entries) {
723
724 nmsgs = 0;
725 LIST_FOREACH(msg, &set->msghead, entries) {
726 int32_t msg_len = strlen(msg->str) + 1;
727
728 msg_hdr->__msgno = htonl(msg->msgId);
729 msg_hdr->__msglen = htonl(msg_len);
730 msg_hdr->__offset = htonl(msg_offset);
731
732 memcpy(strings, msg->str, msg_len);
733 strings += msg_len;
734 msg_offset += msg_len;
735
736 nmsgs++;
737 msg_hdr++;
738 }
739
740 set_hdr->__setno = htonl(set->setId);
741 set_hdr->__nmsgs = htonl(nmsgs);
742 set_hdr->__index = htonl(msg_index);
743 msg_index += nmsgs;
744 set_hdr++;
745 }
746
747 /* write out catalog. XXX: should this be done in small chunks? */
748 write(fd, msgcat, msgcat_size);
749 }
750
751 static void
MCAddSet(int setId)752 MCAddSet(int setId)
753 {
754 struct _setT *p, *q;
755
756 if (setId <= 0) {
757 error("setId's must be greater than zero");
758 /* NOTREACHED */
759 }
760 if (setId > NL_SETMAX) {
761 error("setId exceeds limit");
762 /* NOTREACHED */
763 }
764
765 p = LIST_FIRST(&sethead);
766 q = NULL;
767 for (; p != NULL && p->setId < setId; q = p, p = LIST_NEXT(p, entries))
768 continue;
769
770 if (p && p->setId == setId) {
771 ;
772 } else {
773 p = xmalloc(sizeof(struct _setT));
774 memset(p, '\0', sizeof(struct _setT));
775 LIST_INIT(&p->msghead);
776
777 p->setId = setId;
778
779 if (q == NULL) {
780 LIST_INSERT_HEAD(&sethead, p, entries);
781 } else {
782 LIST_INSERT_AFTER(q, p, entries);
783 }
784 }
785
786 curSet = p;
787 }
788
789 static void
MCAddMsg(int msgId,const char * str)790 MCAddMsg(int msgId, const char *str)
791 {
792 struct _msgT *p, *q;
793
794 if (!curSet)
795 error("can't specify a message when no set exists");
796
797 if (msgId <= 0) {
798 error("msgId's must be greater than zero");
799 /* NOTREACHED */
800 }
801 if (msgId > NL_MSGMAX) {
802 error("msgID exceeds limit");
803 /* NOTREACHED */
804 }
805
806 p = LIST_FIRST(&curSet->msghead);
807 q = NULL;
808 for (; p != NULL && p->msgId < msgId; q = p, p = LIST_NEXT(p, entries))
809 continue;
810
811 if (p && p->msgId == msgId) {
812 free(p->str);
813 } else {
814 p = xmalloc(sizeof(struct _msgT));
815 memset(p, '\0', sizeof(struct _msgT));
816
817 if (q == NULL) {
818 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
819 } else {
820 LIST_INSERT_AFTER(q, p, entries);
821 }
822 }
823
824 p->msgId = msgId;
825 p->str = xstrdup(str);
826 }
827
828 static void
MCDelSet(int setId)829 MCDelSet(int setId)
830 {
831 struct _setT *set;
832 struct _msgT *msg;
833
834 if (setId <= 0) {
835 error("setId's must be greater than zero");
836 /* NOTREACHED */
837 }
838 if (setId > NL_SETMAX) {
839 error("setId exceeds limit");
840 /* NOTREACHED */
841 }
842
843 set = LIST_FIRST(&sethead);
844 for (; set != NULL && set->setId < setId; set = LIST_NEXT(set, entries))
845 continue;
846
847 if (set && set->setId == setId) {
848 LIST_REMOVE(set, entries);
849 while ((msg = LIST_FIRST(&set->msghead)) != NULL) {
850 LIST_REMOVE(msg, entries);
851 free(msg->str);
852 free(msg);
853 }
854 free(set);
855 return;
856 }
857 warning(NULL, "specified set doesn't exist");
858 }
859
860 static void
MCDelMsg(int msgId)861 MCDelMsg(int msgId)
862 {
863 struct _msgT *msg;
864
865 if (!curSet)
866 error("you can't delete a message before defining the set");
867
868 msg = LIST_FIRST(&curSet->msghead);
869 for (; msg != NULL && msg->msgId < msgId; msg = LIST_NEXT(msg, entries))
870 continue;
871
872 if (msg && msg->msgId == msgId) {
873 LIST_REMOVE(msg, entries);
874 free(msg->str);
875 free(msg);
876 return;
877 }
878 warning(NULL, "specified msg doesn't exist");
879 }
880