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.5 2008/10/16 01:52:32 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(int fd
);
126 void MCReadCat(int fd
);
127 void MCWriteCat(int fd
);
128 void MCDelMsg(int msgId
);
129 void MCAddMsg(int msgId
, const char *msg
);
130 void MCAddSet(int setId
);
131 void MCDelSet(int setId
);
132 int main(int, char **);
139 fprintf(stderr
, "usage: %s catfile msgfile ...\n", getprogname());
144 main(int argc
, char *argv
[])
147 char *catfile
= NULL
;
150 while ((c
= getopt(argc
, argv
, "")) != -1) {
166 for (; *argv
; argv
++) {
167 if ((ifd
= open(*argv
, O_RDONLY
)) < 0)
168 err(1, "Unable to read %s", *argv
);
173 if ((ofd
= open(catfile
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0666)) < 0)
174 err(1, "Unable to create a new %s", catfile
);
180 warning(char *cptr
, char *msg
)
182 fprintf(stderr
, "%s: %s on line %ld\n", getprogname(), msg
, lineno
);
183 fprintf(stderr
, "%s\n", curline
);
186 for (tptr
= curline
; tptr
< cptr
; ++tptr
)
188 fprintf(stderr
, "^\n");
193 error(char *cptr
, char *msg
)
199 #if 0 /* XXX unused */
203 error(NULL
, "corrupt message catalog");
210 error(NULL
, "out of memory");
218 if ((p
= malloc(len
)) == NULL
)
224 xrealloc(void *ptr
, size_t size
)
226 if ((ptr
= realloc(ptr
, size
)) == NULL
)
232 xstrdup(const char *str
)
236 if ((nstr
= strdup(str
)) == NULL
)
244 static long curlen
= BUFSIZ
;
245 static char buf
[BUFSIZ
], *bptr
= buf
, *bend
= buf
;
250 curline
= xmalloc(curlen
);
255 cend
= curline
+ curlen
;
257 for (; bptr
< bend
&& cptr
< cend
; ++cptr
, ++bptr
) {
266 cptr
= curline
= xrealloc(curline
, curlen
*= 2);
267 cend
= curline
+ curlen
;
270 buflen
= read(fd
, buf
, BUFSIZ
);
272 if (cptr
> curline
) {
287 if (!*cptr
|| !isspace((unsigned char) *cptr
)) {
288 warning(cptr
, "expected a space");
291 while (*cptr
&& isspace((unsigned char) *cptr
))
299 if (!*cptr
|| isspace((unsigned char) *cptr
)) {
300 warning(cptr
, "wasn't expecting a space");
303 while (*cptr
&& !isspace((unsigned char) *cptr
))
309 getmsg(int fd
, char *cptr
, char quote
)
311 static char *msg
= NULL
;
312 static long msglen
= 0;
316 if (quote
&& *cptr
== quote
) {
320 clen
= strlen(cptr
) + 1;
323 msg
= xrealloc(msg
, clen
);
331 if (quote
&& *cptr
== quote
) {
334 if (*tmp
&& (!isspace((unsigned char) *tmp
) || *wskip(tmp
))) {
335 warning(cptr
, "unexpected quote character, ignoring");
347 error(NULL
, "premature end of file");
348 msglen
+= strlen(cptr
);
350 msg
= xrealloc(msg
, msglen
);
382 if (quote
&& *cptr
== quote
) {
384 } else if (isdigit((unsigned char) *cptr
)) {
386 for (i
= 0; i
< 3; ++i
) {
387 if (!isdigit((unsigned char) *cptr
))
390 warning(cptr
, "octal number greater than 7?!");
392 *tptr
+= (*cptr
- '0');
396 warning(cptr
, "unrecognized escape sequence");
412 int setid
, msgid
= 0;
415 /* XXX: init sethead? */
417 while ((cptr
= getline(fd
))) {
420 if (strncmp(cptr
, "set", 3) == 0) {
426 } else if (strncmp(cptr
, "delset", 6) == 0) {
431 } else if (strncmp(cptr
, "quote", 5) == 0) {
442 } else if (isspace((unsigned char) *cptr
)) {
448 warning(cptr
, "unrecognized line");
453 * First check for (and eat) empty lines....
458 * We have a digit? Start of a message. Else,
461 if (isdigit((unsigned char) *cptr
)) {
465 /* if (*cptr) ++cptr; */
467 warning(cptr
, "neither blank line nor start of a message id");
471 * If we have a message ID, but no message,
472 * then this means "delete this message id
478 str
= getmsg(fd
, cptr
, quote
);
479 MCAddMsg(msgid
, str
);
497 /* XXX init sethead? */
499 if (read(fd
, &mcHead
, sizeof(mcHead
)) != sizeof(mcHead
))
501 if (strncmp(mcHead
.magic
, MCMagic
, MCMagicLen
) != 0)
503 if (mcHead
.majorVer
!= MCMajorVer
)
504 error(NULL
, "unrecognized catalog version");
505 if ((mcHead
.flags
& MCGetByteOrder()) == 0)
506 error(NULL
, "wrong byte order");
508 if (lseek(fd
, mcHead
.firstSet
, SEEK_SET
) == -1)
512 if (read(fd
, &mcSet
, sizeof(mcSet
)) != sizeof(mcSet
))
517 set
= xmalloc(sizeof(setT
));
518 memset(set
, '\0', sizeof(*set
));
520 cat
->last
->next
= set
;
521 set
->prev
= cat
->last
;
524 cat
->first
= cat
->last
= set
;
526 set
->setId
= mcSet
.setId
;
530 data
= xmalloc(mcSet
.dataLen
);
531 if (lseek(fd
, mcSet
.data
.off
, SEEK_SET
) == -1)
533 if (read(fd
, data
, mcSet
.dataLen
) != mcSet
.dataLen
)
535 if (lseek(fd
, mcSet
.u
.firstMsg
, SEEK_SET
) == -1)
538 for (i
= 0; i
< mcSet
.numMsgs
; ++i
) {
539 if (read(fd
, &mcMsg
, sizeof(mcMsg
)) != sizeof(mcMsg
))
545 msg
= xmalloc(sizeof(msgT
));
546 memset(msg
, '\0', sizeof(*msg
));
548 set
->last
->next
= msg
;
549 msg
->prev
= set
->last
;
552 set
->first
= set
->last
= msg
;
554 msg
->msgId
= mcMsg
.msgId
;
555 msg
->str
= xstrdup((char *) (data
+ mcMsg
.msg
.off
));
561 if (lseek(fd
, mcSet
.nextSet
, SEEK_SET
) == -1)
568 * Write message catalog.
570 * The message catalog is first converted from its internal to its
571 * external representation in a chunk of memory allocated for this
572 * purpose. Then the completed catalog is written. This approach
573 * avoids additional housekeeping variables and/or a lot of seeks
574 * that would otherwise be required.
579 int nsets
; /* number of sets */
580 int nmsgs
; /* number of msgs */
581 int string_size
; /* total size of string pool */
582 int msgcat_size
; /* total size of message catalog */
583 void *msgcat
; /* message catalog data */
584 struct _nls_cat_hdr
*cat_hdr
;
585 struct _nls_set_hdr
*set_hdr
;
586 struct _nls_msg_hdr
*msg_hdr
;
593 /* determine number of sets, number of messages, and size of the
599 for (set
= sethead
.lh_first
; set
!= NULL
;
600 set
= set
->entries
.le_next
) {
603 for (msg
= set
->msghead
.lh_first
; msg
!= NULL
;
604 msg
= msg
->entries
.le_next
) {
606 string_size
+= strlen(msg
->str
) + 1;
611 printf("number of sets: %d\n", nsets
);
612 printf("number of msgs: %d\n", nmsgs
);
613 printf("string pool size: %d\n", string_size
);
616 /* determine size and then allocate buffer for constructing external
617 * message catalog representation */
618 msgcat_size
= sizeof(struct _nls_cat_hdr
)
619 + (nsets
* sizeof(struct _nls_set_hdr
))
620 + (nmsgs
* sizeof(struct _nls_msg_hdr
))
623 msgcat
= xmalloc(msgcat_size
);
624 memset(msgcat
, '\0', msgcat_size
);
626 /* fill in msg catalog header */
627 cat_hdr
= (struct _nls_cat_hdr
*) msgcat
;
628 cat_hdr
->__magic
= htonl(_NLS_MAGIC
);
629 cat_hdr
->__nsets
= htonl(nsets
);
630 cat_hdr
->__mem
= htonl(msgcat_size
- sizeof(struct _nls_cat_hdr
));
631 cat_hdr
->__msg_hdr_offset
=
632 htonl(nsets
* sizeof(struct _nls_set_hdr
));
633 cat_hdr
->__msg_txt_offset
=
634 htonl(nsets
* sizeof(struct _nls_set_hdr
) +
635 nmsgs
* sizeof(struct _nls_msg_hdr
));
637 /* compute offsets for set & msg header tables and string pool */
638 set_hdr
= (struct _nls_set_hdr
*) ((char *) msgcat
+
639 sizeof(struct _nls_cat_hdr
));
640 msg_hdr
= (struct _nls_msg_hdr
*) ((char *) msgcat
+
641 sizeof(struct _nls_cat_hdr
) +
642 nsets
* sizeof(struct _nls_set_hdr
));
643 strings
= (char *) msgcat
+
644 sizeof(struct _nls_cat_hdr
) +
645 nsets
* sizeof(struct _nls_set_hdr
) +
646 nmsgs
* sizeof(struct _nls_msg_hdr
);
650 for (set
= sethead
.lh_first
; set
!= NULL
;
651 set
= set
->entries
.le_next
) {
654 for (msg
= set
->msghead
.lh_first
; msg
!= NULL
;
655 msg
= msg
->entries
.le_next
) {
656 int msg_len
= strlen(msg
->str
) + 1;
658 msg_hdr
->__msgno
= htonl(msg
->msgId
);
659 msg_hdr
->__msglen
= htonl(msg_len
);
660 msg_hdr
->__offset
= htonl(msg_offset
);
662 memcpy(strings
, msg
->str
, msg_len
);
664 msg_offset
+= msg_len
;
670 set_hdr
->__setno
= htonl(set
->setId
);
671 set_hdr
->__nmsgs
= htonl(nmsgs
);
672 set_hdr
->__index
= htonl(msg_index
);
677 /* write out catalog. XXX: should this be done in small chunks? */
678 write(fd
, msgcat
, msgcat_size
);
687 error(NULL
, "setId's must be greater than zero");
690 if (setId
> NL_SETMAX
) {
691 error(NULL
, "setId exceeds limit");
695 p
= sethead
.lh_first
;
697 for (; p
!= NULL
&& p
->setId
< setId
; q
= p
, p
= p
->entries
.le_next
);
699 if (p
&& p
->setId
== setId
) {
702 p
= xmalloc(sizeof(struct _setT
));
703 memset(p
, '\0', sizeof(struct _setT
));
704 LIST_INIT(&p
->msghead
);
709 LIST_INSERT_HEAD(&sethead
, p
, entries
);
711 LIST_INSERT_AFTER(q
, p
, entries
);
719 MCAddMsg(int msgId
, const char *str
)
724 error(NULL
, "can't specify a message when no set exists");
727 error(NULL
, "msgId's must be greater than zero");
730 if (msgId
> NL_MSGMAX
) {
731 error(NULL
, "msgID exceeds limit");
735 p
= curSet
->msghead
.lh_first
;
737 for (; p
!= NULL
&& p
->msgId
< msgId
; q
= p
, p
= p
->entries
.le_next
);
739 if (p
&& p
->msgId
== msgId
) {
742 p
= xmalloc(sizeof(struct _msgT
));
743 memset(p
, '\0', sizeof(struct _msgT
));
746 LIST_INSERT_HEAD(&curSet
->msghead
, p
, entries
);
748 LIST_INSERT_AFTER(q
, p
, entries
);
753 p
->str
= xstrdup(str
);
762 set
= sethead
.lh_first
;
763 for (; set
!= NULL
&& set
->setId
< setId
; set
= set
->entries
.le_next
);
765 if (set
&& set
->setId
== setId
) {
767 msg
= set
->msghead
.lh_first
;
770 LIST_REMOVE(msg
, entries
);
773 LIST_REMOVE(set
, entries
);
776 warning(NULL
, "specified set doesn't exist");
785 error(NULL
, "you can't delete a message before defining the set");
787 msg
= curSet
->msghead
.lh_first
;
788 for (; msg
!= NULL
&& msg
->msgId
< msgId
; msg
= msg
->entries
.le_next
);
790 if (msg
&& msg
->msgId
== msgId
) {
792 LIST_REMOVE(msg
, entries
);
795 warning(NULL
, "specified msg doesn't exist");