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