4 /* $NetBSD: gencat.c,v 1.18 2003/10/27 00:12:43 lukem Exp $ */
7 * Copyright (c) 1996 The NetBSD Foundation, Inc.
10 * This code is derived from software contributed to The NetBSD Foundation
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
35 /***********************************************************
36 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
40 Permission to use, copy, modify, and distribute this software and its
41 documentation for any purpose and without fee is hereby granted,
42 provided that the above copyright notice appear in all copies and that
43 both that copyright notice and this permission notice appear in
44 supporting documentation, and that Alfalfa's name not be used in
45 advertising or publicity pertaining to distribution of the software
46 without specific, written prior permission.
48 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
49 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
50 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
51 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
52 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
53 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56 If you make any modifications, bugfixes or other changes to this software
57 we'd appreciate it if you could send a copy to us so we can keep things
58 up-to-date. Many thanks.
60 Alfalfa Software, Inc.
62 Cambridge, MA 02139 USA
65 ******************************************************************/
67 #include <sys/cdefs.h>
68 __FBSDID("$FreeBSD$");
72 #include <sys/types.h>
73 #include <sys/queue.h>
75 #include <arpa/inet.h> /* for htonl() */
90 LIST_ENTRY(_msgT) entries;
95 LIST_HEAD(msghead, _msgT) msghead;
96 LIST_ENTRY(_setT) entries;
99 LIST_HEAD(sethead, _setT) sethead;
100 static struct _setT *curSet;
102 static char *curline = NULL;
103 static long lineno = 0;
105 static char *cskip(char *);
106 static void error(const char *);
107 static char *getline(int);
108 static char *getmsg(int, char *, char);
109 static void warning(const char *, const char *);
110 static char *wskip(char *);
111 static char *xstrdup(const char *);
112 static void *xmalloc(size_t);
113 static void *xrealloc(void *, size_t);
117 void MCWriteCat(int);
119 void MCAddMsg(int, const char *);
123 int main(int, char **);
128 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
133 main(int argc, char **argv)
136 char *catfile = NULL;
139 #define DEPRECATEDMSG 1
142 while ((c = getopt(argc, argv, "new")) != -1) {
144 while ((c = getopt(argc, argv, "")) != -1) {
149 fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
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);
183 warning(const char *cptr, const char *msg)
185 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
186 fprintf(stderr, "%s\n", curline);
189 for (tptr = curline; tptr < cptr; ++tptr)
191 fprintf(stderr, "^\n");
195 #define CORRUPT() { error("corrupt message catalog"); }
196 #define NOMEM() { error("out of memory"); }
199 error(const char *msg)
210 if ((p = malloc(len)) == NULL)
216 xrealloc(void *ptr, size_t size)
218 if ((ptr = realloc(ptr, size)) == NULL)
224 xstrdup(const char *str)
228 if ((nstr = strdup(str)) == NULL)
236 static long curlen = BUFSIZ;
237 static char buf[BUFSIZ], *bptr = buf, *bend = buf;
242 curline = xmalloc(curlen);
247 cend = curline + curlen;
249 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
258 cptr = curline = xrealloc(curline, curlen *= 2);
259 cend = curline + curlen;
262 buflen = read(fd, buf, BUFSIZ);
264 if (cptr > curline) {
279 if (!*cptr || !isspace((unsigned char) *cptr)) {
280 warning(cptr, "expected a space");
283 while (*cptr && isspace((unsigned char) *cptr))
291 if (!*cptr || isspace((unsigned char) *cptr)) {
292 warning(cptr, "wasn't expecting a space");
295 while (*cptr && !isspace((unsigned char) *cptr))
301 getmsg(int fd, char *cptr, char quote)
303 static char *msg = NULL;
304 static long msglen = 0;
308 if (quote && *cptr == quote) {
312 clen = strlen(cptr) + 1;
315 msg = xrealloc(msg, clen);
323 if (quote && *cptr == quote) {
326 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
327 warning(cptr, "unexpected quote character, ignoring");
339 error("premature end of file");
340 msglen += strlen(cptr);
342 msg = xrealloc(msg, msglen);
346 #define CASEOF(CS, CH) \
362 if (quote && *cptr == quote) {
364 } else if (isdigit((unsigned char) *cptr)) {
366 for (i = 0; i < 3; ++i) {
367 if (!isdigit((unsigned char) *cptr))
370 warning(cptr, "octal number greater than 7?!");
372 *tptr += (*cptr - '0');
376 warning(cptr, "unrecognized escape sequence");
392 int setid, msgid = 0;
395 /* XXX: init sethead? */
397 while ((cptr = getline(fd))) {
400 if (strncmp(cptr, "set", 3) == 0) {
406 } else if (strncmp(cptr, "delset", 6) == 0) {
411 } else if (strncmp(cptr, "quote", 5) == 0) {
422 } else if (isspace((unsigned char) *cptr)) {
428 warning(cptr, "unrecognized line");
433 * First check for (and eat) empty lines....
438 * We have a digit? Start of a message. Else,
441 if (isdigit((unsigned char) *cptr)) {
445 /* if (*cptr) ++cptr; */
447 warning(cptr, "neither blank line nor start of a message id");
451 * If we have a message ID, but no message,
452 * then this means "delete this message id
458 str = getmsg(fd, cptr, quote);
459 MCAddMsg(msgid, str);
478 /* XXX init sethead? */
480 if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
482 if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
484 if (mcHead.majorVer != MCMajorVer)
485 error("unrecognized catalog version");
486 if ((mcHead.flags & MCGetByteOrder()) == 0)
487 error("wrong byte order");
489 if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
493 if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
498 set = xmalloc(sizeof(setT));
499 memset(set, '\0', sizeof(*set));
501 cat->last->next = set;
502 set->prev = cat->last;
505 cat->first = cat->last = set;
507 set->setId = mcSet.setId;
511 data = xmalloc(mcSet.dataLen);
512 if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
514 if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
516 if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
519 for (i = 0; i < mcSet.numMsgs; ++i) {
520 if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
526 msg = xmalloc(sizeof(msgT));
527 memset(msg, '\0', sizeof(*msg));
529 set->last->next = msg;
530 msg->prev = set->last;
533 set->first = set->last = msg;
535 msg->msgId = mcMsg.msgId;
536 msg->str = xstrdup((char *) (data + mcMsg.msg.off));
542 if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
549 * Write message catalog.
551 * The message catalog is first converted from its internal to its
552 * external representation in a chunk of memory allocated for this
553 * purpose. Then the completed catalog is written. This approach
554 * avoids additional housekeeping variables and/or a lot of seeks
555 * that would otherwise be required.
560 int nsets; /* number of sets */
561 int nmsgs; /* number of msgs */
562 int string_size; /* total size of string pool */
563 int msgcat_size; /* total size of message catalog */
564 void *msgcat; /* message catalog data */
565 struct _nls_cat_hdr *cat_hdr;
566 struct _nls_set_hdr *set_hdr;
567 struct _nls_msg_hdr *msg_hdr;
574 /* determine number of sets, number of messages, and size of the
580 for (set = sethead.lh_first; set != NULL;
581 set = set->entries.le_next) {
584 for (msg = set->msghead.lh_first; msg != NULL;
585 msg = msg->entries.le_next) {
587 string_size += strlen(msg->str) + 1;
592 printf("number of sets: %d\n", nsets);
593 printf("number of msgs: %d\n", nmsgs);
594 printf("string pool size: %d\n", string_size);
597 /* determine size and then allocate buffer for constructing external
598 * message catalog representation */
599 msgcat_size = sizeof(struct _nls_cat_hdr)
600 + (nsets * sizeof(struct _nls_set_hdr))
601 + (nmsgs * sizeof(struct _nls_msg_hdr))
604 msgcat = xmalloc(msgcat_size);
605 memset(msgcat, '\0', msgcat_size);
607 /* fill in msg catalog header */
608 cat_hdr = (struct _nls_cat_hdr *) msgcat;
609 cat_hdr->__magic = htonl(_NLS_MAGIC);
610 cat_hdr->__nsets = htonl(nsets);
611 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
612 cat_hdr->__msg_hdr_offset =
613 htonl(nsets * sizeof(struct _nls_set_hdr));
614 cat_hdr->__msg_txt_offset =
615 htonl(nsets * sizeof(struct _nls_set_hdr) +
616 nmsgs * sizeof(struct _nls_msg_hdr));
618 /* compute offsets for set & msg header tables and string pool */
619 set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
620 sizeof(struct _nls_cat_hdr));
621 msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
622 sizeof(struct _nls_cat_hdr) +
623 nsets * sizeof(struct _nls_set_hdr));
624 strings = (char *) msgcat +
625 sizeof(struct _nls_cat_hdr) +
626 nsets * sizeof(struct _nls_set_hdr) +
627 nmsgs * sizeof(struct _nls_msg_hdr);
631 for (set = sethead.lh_first; set != NULL;
632 set = set->entries.le_next) {
635 for (msg = set->msghead.lh_first; msg != NULL;
636 msg = msg->entries.le_next) {
637 int msg_len = strlen(msg->str) + 1;
639 msg_hdr->__msgno = htonl(msg->msgId);
640 msg_hdr->__msglen = htonl(msg_len);
641 msg_hdr->__offset = htonl(msg_offset);
643 memcpy(strings, msg->str, msg_len);
645 msg_offset += msg_len;
651 set_hdr->__setno = htonl(set->setId);
652 set_hdr->__nmsgs = htonl(nmsgs);
653 set_hdr->__index = htonl(msg_index);
658 /* write out catalog. XXX: should this be done in small chunks? */
659 write(fd, msgcat, msgcat_size);
668 error("setId's must be greater than zero");
671 if (setId > NL_SETMAX) {
672 error("setId exceeds limit");
676 p = sethead.lh_first;
678 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
680 if (p && p->setId == setId) {
683 p = xmalloc(sizeof(struct _setT));
684 memset(p, '\0', sizeof(struct _setT));
685 LIST_INIT(&p->msghead);
690 LIST_INSERT_HEAD(&sethead, p, entries);
692 LIST_INSERT_AFTER(q, p, entries);
700 MCAddMsg(int msgId, const char *str)
705 error("can't specify a message when no set exists");
708 error("msgId's must be greater than zero");
711 if (msgId > NL_MSGMAX) {
712 error("msgID exceeds limit");
716 p = curSet->msghead.lh_first;
718 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
720 if (p && p->msgId == msgId) {
723 p = xmalloc(sizeof(struct _msgT));
724 memset(p, '\0', sizeof(struct _msgT));
727 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
729 LIST_INSERT_AFTER(q, p, entries);
734 p->str = xstrdup(str);
743 set = sethead.lh_first;
744 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
746 if (set && set->setId == setId) {
748 msg = set->msghead.lh_first;
751 LIST_REMOVE(msg, entries);
754 LIST_REMOVE(set, entries);
757 warning(NULL, "specified set doesn't exist");
766 error("you can't delete a message before defining the set");
768 msg = curSet->msghead.lh_first;
769 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
771 if (msg && msg->msgId == msgId) {
773 LIST_REMOVE(msg, entries);
776 warning(NULL, "specified msg doesn't exist");