1 /* $NetBSD: src/usr.bin/gencat/gencat.c,v 1.19 2004/01/05 23:23:34 jmmv Exp $ */
2 /* $DragonFly: src/usr.bin/gencat/gencat.c,v 1.3 2005/04/21 16:36:35 joerg Exp $ */
5 * Copyright (c) 1996 The NetBSD Foundation, Inc.
8 * This code is derived from software contributed to The NetBSD Foundation
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
40 /***********************************************************
41 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
45 Permission to use, copy, modify, and distribute this software and its
46 documentation for any purpose and without fee is hereby granted,
47 provided that the above copyright notice appear in all copies and that
48 both that copyright notice and this permission notice appear in
49 supporting documentation, and that Alfalfa's name not be used in
50 advertising or publicity pertaining to distribution of the software
51 without specific, written prior permission.
53 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
54 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
55 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
56 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
57 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
58 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
61 If you make any modifications, bugfixes or other changes to this software
62 we'd appreciate it if you could send a copy to us so we can keep things
63 up-to-date. Many thanks.
65 Alfalfa Software, Inc.
67 Cambridge, MA 02139 USA
70 ******************************************************************/
72 #include <sys/types.h>
73 #include <sys/queue.h>
75 #include <arpa/inet.h>
93 #define NL_MSGMAX 2048
99 LIST_ENTRY(_msgT) entries;
104 LIST_HEAD(msghead, _msgT) msghead;
105 LIST_ENTRY(_setT) entries;
108 LIST_HEAD(sethead, _setT) sethead;
109 static struct _setT *curSet;
111 static char *curline = NULL;
112 static long lineno = 0;
114 static char *cskip(char *);
115 static void error(char *, char *);
116 static void nomem(void);
117 static char *getline(int);
118 static char *getmsg(int, char *, char);
119 static void warning(char *, char *);
120 static char *wskip(char *);
121 static char *xstrdup(const char *);
122 static void *xmalloc(size_t);
123 static void *xrealloc(void *, size_t);
125 void MCParse __P((int fd));
126 void MCReadCat __P((int fd));
127 void MCWriteCat __P((int fd));
128 void MCDelMsg __P((int msgId));
129 void MCAddMsg __P((int msgId, const char *msg));
130 void MCAddSet __P((int setId));
131 void MCDelSet __P((int setId));
132 int main __P((int, char **));
133 void usage __P((void));
139 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
149 char *catfile = NULL;
152 while ((c = getopt(argc, argv, "")) != -1) {
169 for (; *argv; argv++) {
170 if ((ifd = open(*argv, O_RDONLY)) < 0)
171 err(1, "Unable to read %s", *argv);
176 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
177 err(1, "Unable to create a new %s", catfile);
187 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
188 fprintf(stderr, "%s\n", curline);
191 for (tptr = curline; tptr < cptr; ++tptr)
193 fprintf(stderr, "^\n");
206 #if 0 /* XXX unused */
210 error(NULL, "corrupt message catalog");
217 error(NULL, "out of memory");
226 if ((p = malloc(len)) == NULL)
236 if ((ptr = realloc(ptr, size)) == NULL)
247 if ((nstr = strdup(str)) == NULL)
256 static long curlen = BUFSIZ;
257 static char buf[BUFSIZ], *bptr = buf, *bend = buf;
262 curline = xmalloc(curlen);
267 cend = curline + curlen;
269 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
278 cptr = curline = xrealloc(curline, curlen *= 2);
279 cend = curline + curlen;
282 buflen = read(fd, buf, BUFSIZ);
284 if (cptr > curline) {
300 if (!*cptr || !isspace((unsigned char) *cptr)) {
301 warning(cptr, "expected a space");
304 while (*cptr && isspace((unsigned char) *cptr))
313 if (!*cptr || isspace((unsigned char) *cptr)) {
314 warning(cptr, "wasn't expecting a space");
317 while (*cptr && !isspace((unsigned char) *cptr))
323 getmsg(fd, cptr, quote)
328 static char *msg = NULL;
329 static long msglen = 0;
333 if (quote && *cptr == quote) {
337 clen = strlen(cptr) + 1;
340 msg = xrealloc(msg, clen);
348 if (quote && *cptr == quote) {
351 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
352 warning(cptr, "unexpected quote character, ignoring");
364 error(NULL, "premature end of file");
365 msglen += strlen(cptr);
367 msg = xrealloc(msg, msglen);
399 if (quote && *cptr == quote) {
401 } else if (isdigit((unsigned char) *cptr)) {
403 for (i = 0; i < 3; ++i) {
404 if (!isdigit((unsigned char) *cptr))
407 warning(cptr, "octal number greater than 7?!");
409 *tptr += (*cptr - '0');
413 warning(cptr, "unrecognized escape sequence");
430 int setid, msgid = 0;
433 /* XXX: init sethead? */
435 while ((cptr = getline(fd))) {
438 if (strncmp(cptr, "set", 3) == 0) {
444 } else if (strncmp(cptr, "delset", 6) == 0) {
449 } else if (strncmp(cptr, "quote", 5) == 0) {
460 } else if (isspace((unsigned char) *cptr)) {
466 warning(cptr, "unrecognized line");
471 * First check for (and eat) empty lines....
476 * We have a digit? Start of a message. Else,
479 if (isdigit((unsigned char) *cptr)) {
483 /* if (*cptr) ++cptr; */
485 warning(cptr, "neither blank line nor start of a message id");
489 * If we have a message ID, but no message,
490 * then this means "delete this message id
496 str = getmsg(fd, cptr, quote);
497 MCAddMsg(msgid, str);
516 /* XXX init sethead? */
518 if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
520 if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
522 if (mcHead.majorVer != MCMajorVer)
523 error(NULL, "unrecognized catalog version");
524 if ((mcHead.flags & MCGetByteOrder()) == 0)
525 error(NULL, "wrong byte order");
527 if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
531 if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
536 set = xmalloc(sizeof(setT));
537 memset(set, '\0', sizeof(*set));
539 cat->last->next = set;
540 set->prev = cat->last;
543 cat->first = cat->last = set;
545 set->setId = mcSet.setId;
549 data = xmalloc(mcSet.dataLen);
550 if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
552 if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
554 if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
557 for (i = 0; i < mcSet.numMsgs; ++i) {
558 if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
564 msg = xmalloc(sizeof(msgT));
565 memset(msg, '\0', sizeof(*msg));
567 set->last->next = msg;
568 msg->prev = set->last;
571 set->first = set->last = msg;
573 msg->msgId = mcMsg.msgId;
574 msg->str = xstrdup((char *) (data + mcMsg.msg.off));
580 if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
587 * Write message catalog.
589 * The message catalog is first converted from its internal to its
590 * external representation in a chunk of memory allocated for this
591 * purpose. Then the completed catalog is written. This approach
592 * avoids additional housekeeping variables and/or a lot of seeks
593 * that would otherwise be required.
599 int nsets; /* number of sets */
600 int nmsgs; /* number of msgs */
601 int string_size; /* total size of string pool */
602 int msgcat_size; /* total size of message catalog */
603 void *msgcat; /* message catalog data */
604 struct _nls_cat_hdr *cat_hdr;
605 struct _nls_set_hdr *set_hdr;
606 struct _nls_msg_hdr *msg_hdr;
613 /* determine number of sets, number of messages, and size of the
619 for (set = sethead.lh_first; set != NULL;
620 set = set->entries.le_next) {
623 for (msg = set->msghead.lh_first; msg != NULL;
624 msg = msg->entries.le_next) {
626 string_size += strlen(msg->str) + 1;
631 printf("number of sets: %d\n", nsets);
632 printf("number of msgs: %d\n", nmsgs);
633 printf("string pool size: %d\n", string_size);
636 /* determine size and then allocate buffer for constructing external
637 * message catalog representation */
638 msgcat_size = sizeof(struct _nls_cat_hdr)
639 + (nsets * sizeof(struct _nls_set_hdr))
640 + (nmsgs * sizeof(struct _nls_msg_hdr))
643 msgcat = xmalloc(msgcat_size);
644 memset(msgcat, '\0', msgcat_size);
646 /* fill in msg catalog header */
647 cat_hdr = (struct _nls_cat_hdr *) msgcat;
648 cat_hdr->__magic = htonl(_NLS_MAGIC);
649 cat_hdr->__nsets = htonl(nsets);
650 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
651 cat_hdr->__msg_hdr_offset =
652 htonl(nsets * sizeof(struct _nls_set_hdr));
653 cat_hdr->__msg_txt_offset =
654 htonl(nsets * sizeof(struct _nls_set_hdr) +
655 nmsgs * sizeof(struct _nls_msg_hdr));
657 /* compute offsets for set & msg header tables and string pool */
658 set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
659 sizeof(struct _nls_cat_hdr));
660 msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
661 sizeof(struct _nls_cat_hdr) +
662 nsets * sizeof(struct _nls_set_hdr));
663 strings = (char *) msgcat +
664 sizeof(struct _nls_cat_hdr) +
665 nsets * sizeof(struct _nls_set_hdr) +
666 nmsgs * sizeof(struct _nls_msg_hdr);
670 for (set = sethead.lh_first; set != NULL;
671 set = set->entries.le_next) {
674 for (msg = set->msghead.lh_first; msg != NULL;
675 msg = msg->entries.le_next) {
676 int msg_len = strlen(msg->str) + 1;
678 msg_hdr->__msgno = htonl(msg->msgId);
679 msg_hdr->__msglen = htonl(msg_len);
680 msg_hdr->__offset = htonl(msg_offset);
682 memcpy(strings, msg->str, msg_len);
684 msg_offset += msg_len;
690 set_hdr->__setno = htonl(set->setId);
691 set_hdr->__nmsgs = htonl(nmsgs);
692 set_hdr->__index = htonl(msg_index);
697 /* write out catalog. XXX: should this be done in small chunks? */
698 write(fd, msgcat, msgcat_size);
708 error(NULL, "setId's must be greater than zero");
711 if (setId > NL_SETMAX) {
712 error(NULL, "setId exceeds limit");
716 p = sethead.lh_first;
718 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
720 if (p && p->setId == setId) {
723 p = xmalloc(sizeof(struct _setT));
724 memset(p, '\0', sizeof(struct _setT));
725 LIST_INIT(&p->msghead);
730 LIST_INSERT_HEAD(&sethead, p, entries);
732 LIST_INSERT_AFTER(q, p, entries);
747 error(NULL, "can't specify a message when no set exists");
750 error(NULL, "msgId's must be greater than zero");
753 if (msgId > NL_MSGMAX) {
754 error(NULL, "msgID exceeds limit");
758 p = curSet->msghead.lh_first;
760 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
762 if (p && p->msgId == msgId) {
765 p = xmalloc(sizeof(struct _msgT));
766 memset(p, '\0', sizeof(struct _msgT));
769 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
771 LIST_INSERT_AFTER(q, p, entries);
776 p->str = xstrdup(str);
786 set = sethead.lh_first;
787 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
789 if (set && set->setId == setId) {
791 msg = set->msghead.lh_first;
794 LIST_REMOVE(msg, entries);
797 LIST_REMOVE(set, entries);
800 warning(NULL, "specified set doesn't exist");
810 error(NULL, "you can't delete a message before defining the set");
812 msg = curSet->msghead.lh_first;
813 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
815 if (msg && msg->msgId == msgId) {
817 LIST_REMOVE(msg, entries);
820 warning(NULL, "specified msg doesn't exist");