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