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.4 2005/08/31 18:11:05 swildner 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) {
168 for (; *argv
; argv
++) {
169 if ((ifd
= open(*argv
, O_RDONLY
)) < 0)
170 err(1, "Unable to read %s", *argv
);
175 if ((ofd
= open(catfile
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0666)) < 0)
176 err(1, "Unable to create a new %s", catfile
);
186 fprintf(stderr
, "%s: %s on line %ld\n", getprogname(), msg
, lineno
);
187 fprintf(stderr
, "%s\n", curline
);
190 for (tptr
= curline
; tptr
< cptr
; ++tptr
)
192 fprintf(stderr
, "^\n");
205 #if 0 /* XXX unused */
209 error(NULL
, "corrupt message catalog");
216 error(NULL
, "out of memory");
225 if ((p
= malloc(len
)) == NULL
)
235 if ((ptr
= realloc(ptr
, size
)) == NULL
)
246 if ((nstr
= strdup(str
)) == NULL
)
255 static long curlen
= BUFSIZ
;
256 static char buf
[BUFSIZ
], *bptr
= buf
, *bend
= buf
;
261 curline
= xmalloc(curlen
);
266 cend
= curline
+ curlen
;
268 for (; bptr
< bend
&& cptr
< cend
; ++cptr
, ++bptr
) {
277 cptr
= curline
= xrealloc(curline
, curlen
*= 2);
278 cend
= curline
+ curlen
;
281 buflen
= read(fd
, buf
, BUFSIZ
);
283 if (cptr
> curline
) {
299 if (!*cptr
|| !isspace((unsigned char) *cptr
)) {
300 warning(cptr
, "expected a space");
303 while (*cptr
&& isspace((unsigned char) *cptr
))
312 if (!*cptr
|| isspace((unsigned char) *cptr
)) {
313 warning(cptr
, "wasn't expecting a space");
316 while (*cptr
&& !isspace((unsigned char) *cptr
))
322 getmsg(fd
, cptr
, quote
)
327 static char *msg
= NULL
;
328 static long msglen
= 0;
332 if (quote
&& *cptr
== quote
) {
336 clen
= strlen(cptr
) + 1;
339 msg
= xrealloc(msg
, clen
);
347 if (quote
&& *cptr
== quote
) {
350 if (*tmp
&& (!isspace((unsigned char) *tmp
) || *wskip(tmp
))) {
351 warning(cptr
, "unexpected quote character, ignoring");
363 error(NULL
, "premature end of file");
364 msglen
+= strlen(cptr
);
366 msg
= xrealloc(msg
, msglen
);
398 if (quote
&& *cptr
== quote
) {
400 } else if (isdigit((unsigned char) *cptr
)) {
402 for (i
= 0; i
< 3; ++i
) {
403 if (!isdigit((unsigned char) *cptr
))
406 warning(cptr
, "octal number greater than 7?!");
408 *tptr
+= (*cptr
- '0');
412 warning(cptr
, "unrecognized escape sequence");
429 int setid
, msgid
= 0;
432 /* XXX: init sethead? */
434 while ((cptr
= getline(fd
))) {
437 if (strncmp(cptr
, "set", 3) == 0) {
443 } else if (strncmp(cptr
, "delset", 6) == 0) {
448 } else if (strncmp(cptr
, "quote", 5) == 0) {
459 } else if (isspace((unsigned char) *cptr
)) {
465 warning(cptr
, "unrecognized line");
470 * First check for (and eat) empty lines....
475 * We have a digit? Start of a message. Else,
478 if (isdigit((unsigned char) *cptr
)) {
482 /* if (*cptr) ++cptr; */
484 warning(cptr
, "neither blank line nor start of a message id");
488 * If we have a message ID, but no message,
489 * then this means "delete this message id
495 str
= getmsg(fd
, cptr
, quote
);
496 MCAddMsg(msgid
, str
);
515 /* XXX init sethead? */
517 if (read(fd
, &mcHead
, sizeof(mcHead
)) != sizeof(mcHead
))
519 if (strncmp(mcHead
.magic
, MCMagic
, MCMagicLen
) != 0)
521 if (mcHead
.majorVer
!= MCMajorVer
)
522 error(NULL
, "unrecognized catalog version");
523 if ((mcHead
.flags
& MCGetByteOrder()) == 0)
524 error(NULL
, "wrong byte order");
526 if (lseek(fd
, mcHead
.firstSet
, SEEK_SET
) == -1)
530 if (read(fd
, &mcSet
, sizeof(mcSet
)) != sizeof(mcSet
))
535 set
= xmalloc(sizeof(setT
));
536 memset(set
, '\0', sizeof(*set
));
538 cat
->last
->next
= set
;
539 set
->prev
= cat
->last
;
542 cat
->first
= cat
->last
= set
;
544 set
->setId
= mcSet
.setId
;
548 data
= xmalloc(mcSet
.dataLen
);
549 if (lseek(fd
, mcSet
.data
.off
, SEEK_SET
) == -1)
551 if (read(fd
, data
, mcSet
.dataLen
) != mcSet
.dataLen
)
553 if (lseek(fd
, mcSet
.u
.firstMsg
, SEEK_SET
) == -1)
556 for (i
= 0; i
< mcSet
.numMsgs
; ++i
) {
557 if (read(fd
, &mcMsg
, sizeof(mcMsg
)) != sizeof(mcMsg
))
563 msg
= xmalloc(sizeof(msgT
));
564 memset(msg
, '\0', sizeof(*msg
));
566 set
->last
->next
= msg
;
567 msg
->prev
= set
->last
;
570 set
->first
= set
->last
= msg
;
572 msg
->msgId
= mcMsg
.msgId
;
573 msg
->str
= xstrdup((char *) (data
+ mcMsg
.msg
.off
));
579 if (lseek(fd
, mcSet
.nextSet
, SEEK_SET
) == -1)
586 * Write message catalog.
588 * The message catalog is first converted from its internal to its
589 * external representation in a chunk of memory allocated for this
590 * purpose. Then the completed catalog is written. This approach
591 * avoids additional housekeeping variables and/or a lot of seeks
592 * that would otherwise be required.
598 int nsets
; /* number of sets */
599 int nmsgs
; /* number of msgs */
600 int string_size
; /* total size of string pool */
601 int msgcat_size
; /* total size of message catalog */
602 void *msgcat
; /* message catalog data */
603 struct _nls_cat_hdr
*cat_hdr
;
604 struct _nls_set_hdr
*set_hdr
;
605 struct _nls_msg_hdr
*msg_hdr
;
612 /* determine number of sets, number of messages, and size of the
618 for (set
= sethead
.lh_first
; set
!= NULL
;
619 set
= set
->entries
.le_next
) {
622 for (msg
= set
->msghead
.lh_first
; msg
!= NULL
;
623 msg
= msg
->entries
.le_next
) {
625 string_size
+= strlen(msg
->str
) + 1;
630 printf("number of sets: %d\n", nsets
);
631 printf("number of msgs: %d\n", nmsgs
);
632 printf("string pool size: %d\n", string_size
);
635 /* determine size and then allocate buffer for constructing external
636 * message catalog representation */
637 msgcat_size
= sizeof(struct _nls_cat_hdr
)
638 + (nsets
* sizeof(struct _nls_set_hdr
))
639 + (nmsgs
* sizeof(struct _nls_msg_hdr
))
642 msgcat
= xmalloc(msgcat_size
);
643 memset(msgcat
, '\0', msgcat_size
);
645 /* fill in msg catalog header */
646 cat_hdr
= (struct _nls_cat_hdr
*) msgcat
;
647 cat_hdr
->__magic
= htonl(_NLS_MAGIC
);
648 cat_hdr
->__nsets
= htonl(nsets
);
649 cat_hdr
->__mem
= htonl(msgcat_size
- sizeof(struct _nls_cat_hdr
));
650 cat_hdr
->__msg_hdr_offset
=
651 htonl(nsets
* sizeof(struct _nls_set_hdr
));
652 cat_hdr
->__msg_txt_offset
=
653 htonl(nsets
* sizeof(struct _nls_set_hdr
) +
654 nmsgs
* sizeof(struct _nls_msg_hdr
));
656 /* compute offsets for set & msg header tables and string pool */
657 set_hdr
= (struct _nls_set_hdr
*) ((char *) msgcat
+
658 sizeof(struct _nls_cat_hdr
));
659 msg_hdr
= (struct _nls_msg_hdr
*) ((char *) msgcat
+
660 sizeof(struct _nls_cat_hdr
) +
661 nsets
* sizeof(struct _nls_set_hdr
));
662 strings
= (char *) msgcat
+
663 sizeof(struct _nls_cat_hdr
) +
664 nsets
* sizeof(struct _nls_set_hdr
) +
665 nmsgs
* sizeof(struct _nls_msg_hdr
);
669 for (set
= sethead
.lh_first
; set
!= NULL
;
670 set
= set
->entries
.le_next
) {
673 for (msg
= set
->msghead
.lh_first
; msg
!= NULL
;
674 msg
= msg
->entries
.le_next
) {
675 int msg_len
= strlen(msg
->str
) + 1;
677 msg_hdr
->__msgno
= htonl(msg
->msgId
);
678 msg_hdr
->__msglen
= htonl(msg_len
);
679 msg_hdr
->__offset
= htonl(msg_offset
);
681 memcpy(strings
, msg
->str
, msg_len
);
683 msg_offset
+= msg_len
;
689 set_hdr
->__setno
= htonl(set
->setId
);
690 set_hdr
->__nmsgs
= htonl(nmsgs
);
691 set_hdr
->__index
= htonl(msg_index
);
696 /* write out catalog. XXX: should this be done in small chunks? */
697 write(fd
, msgcat
, msgcat_size
);
707 error(NULL
, "setId's must be greater than zero");
710 if (setId
> NL_SETMAX
) {
711 error(NULL
, "setId exceeds limit");
715 p
= sethead
.lh_first
;
717 for (; p
!= NULL
&& p
->setId
< setId
; q
= p
, p
= p
->entries
.le_next
);
719 if (p
&& p
->setId
== setId
) {
722 p
= xmalloc(sizeof(struct _setT
));
723 memset(p
, '\0', sizeof(struct _setT
));
724 LIST_INIT(&p
->msghead
);
729 LIST_INSERT_HEAD(&sethead
, p
, entries
);
731 LIST_INSERT_AFTER(q
, p
, entries
);
746 error(NULL
, "can't specify a message when no set exists");
749 error(NULL
, "msgId's must be greater than zero");
752 if (msgId
> NL_MSGMAX
) {
753 error(NULL
, "msgID exceeds limit");
757 p
= curSet
->msghead
.lh_first
;
759 for (; p
!= NULL
&& p
->msgId
< msgId
; q
= p
, p
= p
->entries
.le_next
);
761 if (p
&& p
->msgId
== msgId
) {
764 p
= xmalloc(sizeof(struct _msgT
));
765 memset(p
, '\0', sizeof(struct _msgT
));
768 LIST_INSERT_HEAD(&curSet
->msghead
, p
, entries
);
770 LIST_INSERT_AFTER(q
, p
, entries
);
775 p
->str
= xstrdup(str
);
785 set
= sethead
.lh_first
;
786 for (; set
!= NULL
&& set
->setId
< setId
; set
= set
->entries
.le_next
);
788 if (set
&& set
->setId
== setId
) {
790 msg
= set
->msghead
.lh_first
;
793 LIST_REMOVE(msg
, entries
);
796 LIST_REMOVE(set
, entries
);
799 warning(NULL
, "specified set doesn't exist");
809 error(NULL
, "you can't delete a message before defining the set");
811 msg
= curSet
->msghead
.lh_first
;
812 for (; msg
!= NULL
&& msg
->msgId
< msgId
; msg
= msg
->entries
.le_next
);
814 if (msg
&& msg
->msgId
== msgId
) {
816 LIST_REMOVE(msg
, entries
);
819 warning(NULL
, "specified msg doesn't exist");