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.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the NetBSD
24 * Foundation, Inc. and its contributors.
25 * 4. Neither the name of The NetBSD Foundation nor the names of its
26 * contributors may be used to endorse or promote products derived
27 * from this software without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
42 /***********************************************************
43 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
47 Permission to use, copy, modify, and distribute this software and its
48 documentation for any purpose and without fee is hereby granted,
49 provided that the above copyright notice appear in all copies and that
50 both that copyright notice and this permission notice appear in
51 supporting documentation, and that Alfalfa's name not be used in
52 advertising or publicity pertaining to distribution of the software
53 without specific, written prior permission.
55 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
56 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
57 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
58 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
59 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
60 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
63 If you make any modifications, bugfixes or other changes to this software
64 we'd appreciate it if you could send a copy to us so we can keep things
65 up-to-date. Many thanks.
67 Alfalfa Software, Inc.
69 Cambridge, MA 02139 USA
72 ******************************************************************/
74 #include <sys/cdefs.h>
75 __FBSDID("$FreeBSD$");
79 #include <sys/types.h>
80 #include <sys/queue.h>
82 #include <arpa/inet.h> /* for htonl() */
97 LIST_ENTRY(_msgT) entries;
102 LIST_HEAD(msghead, _msgT) msghead;
103 LIST_ENTRY(_setT) entries;
106 LIST_HEAD(sethead, _setT) sethead;
107 static struct _setT *curSet;
109 static char *curline = NULL;
110 static long lineno = 0;
112 static char *cskip(char *);
113 static void error(const char *);
114 static char *getline(int);
115 static char *getmsg(int, char *, char);
116 static void warning(const char *, const char *);
117 static char *wskip(char *);
118 static char *xstrdup(const char *);
119 static void *xmalloc(size_t);
120 static void *xrealloc(void *, size_t);
124 void MCWriteCat(int);
126 void MCAddMsg(int, const char *);
130 int main(int, char **);
135 fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname());
140 main(int argc, char **argv)
143 char *catfile = NULL;
146 #define DEPRECATEDMSG 1
149 while ((c = getopt(argc, argv, "new")) != -1) {
151 while ((c = getopt(argc, argv, "")) != -1) {
156 fprintf(stderr, "WARNING: Usage of \"-new\" argument is deprecated.\n");
176 for (; *argv; argv++) {
177 if ((ifd = open(*argv, O_RDONLY)) < 0)
178 err(1, "Unable to read %s", *argv);
183 if ((ofd = open(catfile, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
184 err(1, "Unable to create a new %s", catfile);
190 warning(const char *cptr, const char *msg)
192 fprintf(stderr, "%s: %s on line %ld\n", getprogname(), msg, lineno);
193 fprintf(stderr, "%s\n", curline);
196 for (tptr = curline; tptr < cptr; ++tptr)
198 fprintf(stderr, "^\n");
202 #define CORRUPT() { error("corrupt message catalog"); }
203 #define NOMEM() { error("out of memory"); }
206 error(const char *msg)
217 if ((p = malloc(len)) == NULL)
223 xrealloc(void *ptr, size_t size)
225 if ((ptr = realloc(ptr, size)) == NULL)
231 xstrdup(const char *str)
235 if ((nstr = strdup(str)) == NULL)
243 static long curlen = BUFSIZ;
244 static char buf[BUFSIZ], *bptr = buf, *bend = buf;
249 curline = xmalloc(curlen);
254 cend = curline + curlen;
256 for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
265 cptr = curline = xrealloc(curline, curlen *= 2);
266 cend = curline + curlen;
269 buflen = read(fd, buf, BUFSIZ);
271 if (cptr > curline) {
286 if (!*cptr || !isspace((unsigned char) *cptr)) {
287 warning(cptr, "expected a space");
290 while (*cptr && isspace((unsigned char) *cptr))
298 if (!*cptr || isspace((unsigned char) *cptr)) {
299 warning(cptr, "wasn't expecting a space");
302 while (*cptr && !isspace((unsigned char) *cptr))
308 getmsg(int fd, char *cptr, char quote)
310 static char *msg = NULL;
311 static long msglen = 0;
315 if (quote && *cptr == quote) {
319 clen = strlen(cptr) + 1;
322 msg = xrealloc(msg, clen);
330 if (quote && *cptr == quote) {
333 if (*tmp && (!isspace((unsigned char) *tmp) || *wskip(tmp))) {
334 warning(cptr, "unexpected quote character, ignoring");
346 error("premature end of file");
347 msglen += strlen(cptr);
349 msg = xrealloc(msg, msglen);
353 #define CASEOF(CS, CH) \
369 if (quote && *cptr == quote) {
371 } else if (isdigit((unsigned char) *cptr)) {
373 for (i = 0; i < 3; ++i) {
374 if (!isdigit((unsigned char) *cptr))
377 warning(cptr, "octal number greater than 7?!");
379 *tptr += (*cptr - '0');
383 warning(cptr, "unrecognized escape sequence");
399 int setid, msgid = 0;
402 /* XXX: init sethead? */
404 while ((cptr = getline(fd))) {
407 if (strncmp(cptr, "set", 3) == 0) {
413 } else if (strncmp(cptr, "delset", 6) == 0) {
418 } else if (strncmp(cptr, "quote", 5) == 0) {
429 } else if (isspace((unsigned char) *cptr)) {
435 warning(cptr, "unrecognized line");
440 * First check for (and eat) empty lines....
445 * We have a digit? Start of a message. Else,
448 if (isdigit((unsigned char) *cptr)) {
452 /* if (*cptr) ++cptr; */
454 warning(cptr, "neither blank line nor start of a message id");
458 * If we have a message ID, but no message,
459 * then this means "delete this message id
465 str = getmsg(fd, cptr, quote);
466 MCAddMsg(msgid, str);
485 /* XXX init sethead? */
487 if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead))
489 if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0)
491 if (mcHead.majorVer != MCMajorVer)
492 error("unrecognized catalog version");
493 if ((mcHead.flags & MCGetByteOrder()) == 0)
494 error("wrong byte order");
496 if (lseek(fd, mcHead.firstSet, SEEK_SET) == -1)
500 if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet))
505 set = xmalloc(sizeof(setT));
506 memset(set, '\0', sizeof(*set));
508 cat->last->next = set;
509 set->prev = cat->last;
512 cat->first = cat->last = set;
514 set->setId = mcSet.setId;
518 data = xmalloc(mcSet.dataLen);
519 if (lseek(fd, mcSet.data.off, SEEK_SET) == -1)
521 if (read(fd, data, mcSet.dataLen) != mcSet.dataLen)
523 if (lseek(fd, mcSet.u.firstMsg, SEEK_SET) == -1)
526 for (i = 0; i < mcSet.numMsgs; ++i) {
527 if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg))
533 msg = xmalloc(sizeof(msgT));
534 memset(msg, '\0', sizeof(*msg));
536 set->last->next = msg;
537 msg->prev = set->last;
540 set->first = set->last = msg;
542 msg->msgId = mcMsg.msgId;
543 msg->str = xstrdup((char *) (data + mcMsg.msg.off));
549 if (lseek(fd, mcSet.nextSet, SEEK_SET) == -1)
556 * Write message catalog.
558 * The message catalog is first converted from its internal to its
559 * external representation in a chunk of memory allocated for this
560 * purpose. Then the completed catalog is written. This approach
561 * avoids additional housekeeping variables and/or a lot of seeks
562 * that would otherwise be required.
567 int nsets; /* number of sets */
568 int nmsgs; /* number of msgs */
569 int string_size; /* total size of string pool */
570 int msgcat_size; /* total size of message catalog */
571 void *msgcat; /* message catalog data */
572 struct _nls_cat_hdr *cat_hdr;
573 struct _nls_set_hdr *set_hdr;
574 struct _nls_msg_hdr *msg_hdr;
581 /* determine number of sets, number of messages, and size of the
587 for (set = sethead.lh_first; set != NULL;
588 set = set->entries.le_next) {
591 for (msg = set->msghead.lh_first; msg != NULL;
592 msg = msg->entries.le_next) {
594 string_size += strlen(msg->str) + 1;
599 printf("number of sets: %d\n", nsets);
600 printf("number of msgs: %d\n", nmsgs);
601 printf("string pool size: %d\n", string_size);
604 /* determine size and then allocate buffer for constructing external
605 * message catalog representation */
606 msgcat_size = sizeof(struct _nls_cat_hdr)
607 + (nsets * sizeof(struct _nls_set_hdr))
608 + (nmsgs * sizeof(struct _nls_msg_hdr))
611 msgcat = xmalloc(msgcat_size);
612 memset(msgcat, '\0', msgcat_size);
614 /* fill in msg catalog header */
615 cat_hdr = (struct _nls_cat_hdr *) msgcat;
616 cat_hdr->__magic = htonl(_NLS_MAGIC);
617 cat_hdr->__nsets = htonl(nsets);
618 cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
619 cat_hdr->__msg_hdr_offset =
620 htonl(nsets * sizeof(struct _nls_set_hdr));
621 cat_hdr->__msg_txt_offset =
622 htonl(nsets * sizeof(struct _nls_set_hdr) +
623 nmsgs * sizeof(struct _nls_msg_hdr));
625 /* compute offsets for set & msg header tables and string pool */
626 set_hdr = (struct _nls_set_hdr *)(void *)((char *)msgcat +
627 sizeof(struct _nls_cat_hdr));
628 msg_hdr = (struct _nls_msg_hdr *)(void *)((char *)msgcat +
629 sizeof(struct _nls_cat_hdr) +
630 nsets * sizeof(struct _nls_set_hdr));
631 strings = (char *) msgcat +
632 sizeof(struct _nls_cat_hdr) +
633 nsets * sizeof(struct _nls_set_hdr) +
634 nmsgs * sizeof(struct _nls_msg_hdr);
638 for (set = sethead.lh_first; set != NULL;
639 set = set->entries.le_next) {
642 for (msg = set->msghead.lh_first; msg != NULL;
643 msg = msg->entries.le_next) {
644 int msg_len = strlen(msg->str) + 1;
646 msg_hdr->__msgno = htonl(msg->msgId);
647 msg_hdr->__msglen = htonl(msg_len);
648 msg_hdr->__offset = htonl(msg_offset);
650 memcpy(strings, msg->str, msg_len);
652 msg_offset += msg_len;
658 set_hdr->__setno = htonl(set->setId);
659 set_hdr->__nmsgs = htonl(nmsgs);
660 set_hdr->__index = htonl(msg_index);
665 /* write out catalog. XXX: should this be done in small chunks? */
666 write(fd, msgcat, msgcat_size);
675 error("setId's must be greater than zero");
678 if (setId > NL_SETMAX) {
679 error("setId exceeds limit");
683 p = sethead.lh_first;
685 for (; p != NULL && p->setId < setId; q = p, p = p->entries.le_next);
687 if (p && p->setId == setId) {
690 p = xmalloc(sizeof(struct _setT));
691 memset(p, '\0', sizeof(struct _setT));
692 LIST_INIT(&p->msghead);
697 LIST_INSERT_HEAD(&sethead, p, entries);
699 LIST_INSERT_AFTER(q, p, entries);
707 MCAddMsg(int msgId, const char *str)
712 error("can't specify a message when no set exists");
715 error("msgId's must be greater than zero");
718 if (msgId > NL_MSGMAX) {
719 error("msgID exceeds limit");
723 p = curSet->msghead.lh_first;
725 for (; p != NULL && p->msgId < msgId; q = p, p = p->entries.le_next);
727 if (p && p->msgId == msgId) {
730 p = xmalloc(sizeof(struct _msgT));
731 memset(p, '\0', sizeof(struct _msgT));
734 LIST_INSERT_HEAD(&curSet->msghead, p, entries);
736 LIST_INSERT_AFTER(q, p, entries);
741 p->str = xstrdup(str);
750 set = sethead.lh_first;
751 for (; set != NULL && set->setId < setId; set = set->entries.le_next);
753 if (set && set->setId == setId) {
755 msg = set->msghead.lh_first;
758 LIST_REMOVE(msg, entries);
761 LIST_REMOVE(set, entries);
764 warning(NULL, "specified set doesn't exist");
773 error("you can't delete a message before defining the set");
775 msg = curSet->msghead.lh_first;
776 for (; msg != NULL && msg->msgId < msgId; msg = msg->entries.le_next);
778 if (msg && msg->msgId == msgId) {
780 LIST_REMOVE(msg, entries);
783 warning(NULL, "specified msg doesn't exist");