gencat.c revision 1.6 1 /* $NetBSD: gencat.c,v 1.6 1997/10/08 22:28:15 jtc 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 /***********************************************************
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 (at) alfalfa.com
68
69 ******************************************************************/
70
71 #define _NLS_PRIVATE
72
73 #include <sys/queue.h>
74 #include <ctype.h>
75 #include <stdio.h>
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 };
101
102 LIST_HEAD(sethead, _setT) sethead;
103 static struct _setT *curSet;
104
105 static char *curline = NULL;
106 static long lineno = 0;
107
108 void
109 usage()
110 {
111 fprintf(stderr, "Use: gencat catfile msgfile ...\n");
112 exit(1);
113 }
114
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;
134
135 if (argc < 2) {
136 usage();
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);
144 exit(1);
145 }
146 MCParse(ifd);
147 close(ifd);
148 }
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 }
155 MCWriteCat(ofd);
156 exit(0);
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);
180 exit(1);
181 }
182
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 }
536 #endif
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;
551 {
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 }
761
762 void
763 MCDelMsg(msgId)
764 int msgId;
765 {
766 struct _msgT *msg;
767
768 if (!curSet)
769 error(NULL, "you can't delete a message before defining the set");
770
771 msg = curSet->msghead.lh_first;
772 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
773
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");
780 }
781