1 /* $NetBSD: gencat.c,v 1.29 2009/04/16 13:52:52 tron Exp $ */
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #if defined(__RCSID) && !defined(lint)
34 __RCSID("$NetBSD: gencat.c,v 1.29 2009/04/16 13:52:52 tron Exp $");
37 /***********************************************************
38 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
42 Permission to use, copy, modify, and distribute this software and its
43 documentation for any purpose and without fee is hereby granted,
44 provided that the above copyright notice appear in all copies and that
45 both that copyright notice and this permission notice appear in
46 supporting documentation, and that Alfalfa's name not be used in
47 advertising or publicity pertaining to distribution of the software
48 without specific, written prior permission.
50 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
51 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
52 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
53 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
54 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
55 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
58 If you make any modifications, bugfixes or other changes to this software
59 we'd appreciate it if you could send a copy to us so we can keep things
60 up-to-date. Many thanks.
62 Alfalfa Software, Inc.
64 Cambridge, MA 02139 USA
67 ******************************************************************/
69 #if HAVE_NBTOOL_CONFIG_H
70 #include "nbtool_config.h"
75 #include <sys/types.h>
76 #include <sys/queue.h>
78 #include <netinet/in.h> /* Needed by arpa/inet.h on NetBSD */
79 #include <arpa/inet.h> /* Needed for htonl() on POSIX systems */
96 #define NL_MSGMAX 2048
102 LIST_ENTRY(_msgT
) entries
;
107 LIST_HEAD(msghead
, _msgT
) msghead
;
108 LIST_ENTRY(_setT
) entries
;
111 LIST_HEAD(sethead
, _setT
) sethead
;
112 static struct _setT
*curSet
;
114 static const char *curfile
;
115 static char *curline
= NULL
;
116 static long lineno
= 0;
118 static char *cskip(char *);
119 static void error(const char *);
120 static char *get_line(int);
121 static char *getmsg(int, char *, char);
122 static void warning(const char *, const char *);
123 static char *wskip(char *);
124 static char *xstrdup(const char *);
125 static void *xmalloc(size_t);
126 static void *xrealloc(void *, size_t);
128 void MCParse(int fd
);
129 void MCReadCat(int fd
);
130 void MCWriteCat(int fd
);
131 void MCDelMsg(int msgId
);
132 void MCAddMsg(int msgId
, const char *msg
);
133 void MCAddSet(int setId
);
134 void MCDelSet(int setId
);
135 int main(int, char **);
138 #define CORRUPT "corrupt message catalog"
139 #define NOMEMORY "out of memory"
144 fprintf(stderr
, "usage: %s catfile msgfile ...\n", getprogname());
149 main(int argc
, char *argv
[])
152 char *catfile
= NULL
;
156 while ((c
= getopt(argc
, argv
, "")) != -1) {
173 if ((catfile
[0] == '-') && (catfile
[1] == '\0')) {
176 ofd
= open(catfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
178 if (errno
== EEXIST
) {
179 if ((ofd
= open(catfile
, O_RDWR
)) < 0) {
180 err(1, "Unable to open %s", catfile
);
184 err(1, "Unable to create new %s", catfile
);
190 if (lseek(ofd
, SEEK_SET
, 0) < 0) {
191 err(1, "Unable to seek on %s", catfile
);
197 if (((*argv
)[0] == '-') && ((*argv
)[1] == '\0')) {
201 MCParse(STDIN_FILENO
);
203 for (; *argv
; argv
++) {
204 if ((ifd
= open(*argv
, O_RDONLY
)) < 0)
205 err(1, "Unable to read %s", *argv
);
214 if (ftruncate(ofd
, 0) != 0) {
215 err(1, "Unable to truncate %s", catfile
);
225 warning(const char *cptr
, const char *msg
)
228 fprintf(stderr
, "%s: %s on line %ld, %s\n",
229 getprogname(), msg
, lineno
, curfile
);
230 fprintf(stderr
, "%s\n", curline
);
233 for (tptr
= curline
; tptr
< cptr
; ++tptr
)
235 fprintf(stderr
, "^\n");
238 fprintf(stderr
, "%s: %s, %s\n", getprogname(), msg
, curfile
);
243 error(const char *msg
)
254 if ((p
= malloc(len
)) == NULL
)
260 xrealloc(void *ptr
, size_t size
)
262 if ((ptr
= realloc(ptr
, size
)) == NULL
)
268 xstrdup(const char *str
)
272 if ((nstr
= strdup(str
)) == NULL
)
280 static long curlen
= BUFSIZ
;
281 static char buf
[BUFSIZ
], *bptr
= buf
, *bend
= buf
;
286 curline
= xmalloc(curlen
);
291 cend
= curline
+ curlen
;
293 for (; bptr
< bend
&& cptr
< cend
; ++cptr
, ++bptr
) {
302 cptr
= curline
= xrealloc(curline
, curlen
*= 2);
303 cend
= curline
+ curlen
;
306 buflen
= read(fd
, buf
, BUFSIZ
);
308 if (cptr
> curline
) {
323 if (!*cptr
|| !isspace((unsigned char) *cptr
)) {
324 warning(cptr
, "expected a space");
327 while (*cptr
&& isspace((unsigned char) *cptr
))
335 if (!*cptr
|| isspace((unsigned char) *cptr
)) {
336 warning(cptr
, "wasn't expecting a space");
339 while (*cptr
&& !isspace((unsigned char) *cptr
))
345 getmsg(int fd
, char *cptr
, char quote
)
347 static char *msg
= NULL
;
348 static size_t msglen
= 0;
352 if (quote
&& *cptr
== quote
) {
356 clen
= strlen(cptr
) + 1;
359 msg
= xrealloc(msg
, clen
);
367 if (quote
&& *cptr
== quote
) {
370 if (*tmp
&& (!isspace((unsigned char) *tmp
) || *wskip(tmp
))) {
371 warning(cptr
, "unexpected quote character, ignoring");
383 error("premature end of file");
384 msglen
+= strlen(cptr
);
386 msg
= xrealloc(msg
, msglen
);
418 if (quote
&& *cptr
== quote
) {
420 } else if (isdigit((unsigned char) *cptr
)) {
422 for (i
= 0; i
< 3; ++i
) {
423 if (!isdigit((unsigned char) *cptr
))
426 warning(cptr
, "octal number greater than 7?!");
428 *tptr
+= (*cptr
- '0');
432 warning(cptr
, "unrecognized escape sequence");
453 /* XXX: init sethead? */
455 while ((cptr
= get_line(fd
))) {
458 if (strncmp(cptr
, "set", 3) == 0) {
464 } else if (strncmp(cptr
, "delset", 6) == 0) {
469 } else if (strncmp(cptr
, "quote", 5) == 0) {
480 } else if (isspace((unsigned char) *cptr
)) {
486 warning(cptr
, "unrecognized line");
491 * First check for (and eat) empty lines....
496 * We have a digit? Start of a message. Else,
499 if (isdigit((unsigned char) *cptr
)) {
510 warning(cptr
, "neither blank line nor start of a message id");
514 * If no set directive specified, all messages
515 * shall be in default message set NL_SETD.
522 * If we have a message ID, but no message,
523 * then this means "delete this message id
529 str
= getmsg(fd
, cptr
, quote
);
530 MCAddMsg(msgid
, str
);
539 void *msgcat
; /* message catalog data */
540 struct _nls_cat_hdr cat_hdr
;
541 struct _nls_set_hdr
*set_hdr
;
542 struct _nls_msg_hdr
*msg_hdr
;
548 /* XXX init sethead? */
550 n
= read(fd
, &cat_hdr
, sizeof(cat_hdr
));
551 if (n
< (ssize_t
)sizeof(cat_hdr
)) {
553 return; /* empty file */
555 err(1, "header read");
559 if (ntohl((uint32_t)cat_hdr
.__magic
) != _NLS_MAGIC
)
560 errx(1, "%s: bad magic number (%#x)", CORRUPT
, cat_hdr
.__magic
);
562 cat_hdr
.__mem
= ntohl(cat_hdr
.__mem
);
564 cat_hdr
.__nsets
= ntohl(cat_hdr
.__nsets
);
565 cat_hdr
.__msg_hdr_offset
= ntohl(cat_hdr
.__msg_hdr_offset
);
566 cat_hdr
.__msg_txt_offset
= ntohl(cat_hdr
.__msg_txt_offset
);
567 if ((cat_hdr
.__mem
< 0) ||
568 (cat_hdr
.__msg_hdr_offset
< 0) ||
569 (cat_hdr
.__msg_txt_offset
< 0) ||
570 (cat_hdr
.__mem
< (int32_t)(cat_hdr
.__nsets
* sizeof(struct _nls_set_hdr
))) ||
571 (cat_hdr
.__mem
< cat_hdr
.__msg_hdr_offset
) ||
572 (cat_hdr
.__mem
< cat_hdr
.__msg_txt_offset
))
573 errx(1, "%s: catalog header", CORRUPT
);
575 msgcat
= xmalloc(cat_hdr
.__mem
);
577 n
= read(fd
, msgcat
, cat_hdr
.__mem
);
578 if (n
< cat_hdr
.__mem
) {
585 set_hdr
= (struct _nls_set_hdr
*)msgcat
;
586 msg_hdr
= (struct _nls_msg_hdr
*)((char *)msgcat
+
587 cat_hdr
.__msg_hdr_offset
);
588 strings
= (char *)msgcat
+ cat_hdr
.__msg_txt_offset
;
591 for (s
= 0; s
< cat_hdr
.__nsets
; s
++, set_hdr
++) {
592 set_hdr
->__setno
= ntohl(set_hdr
->__setno
);
593 if (set_hdr
->__setno
< setno
)
594 errx(1, "%s: bad set number (%d)",
595 CORRUPT
, set_hdr
->__setno
);
596 setno
= set_hdr
->__setno
;
600 set_hdr
->__nmsgs
= ntohl(set_hdr
->__nmsgs
);
601 set_hdr
->__index
= ntohl(set_hdr
->__index
);
602 if (set_hdr
->__nmsgs
< 0 || set_hdr
->__index
< 0)
603 errx(1, "%s: set header", CORRUPT
);
607 for (m
= 0; m
< set_hdr
->__nmsgs
; m
++, msg_hdr
++) {
608 msg_hdr
->__msgno
= ntohl(msg_hdr
->__msgno
);
609 msg_hdr
->__offset
= ntohl(msg_hdr
->__offset
);
610 if (msg_hdr
->__msgno
< msgno
)
611 errx(1, "%s: bad message number (%d)",
612 CORRUPT
, msg_hdr
->__msgno
);
613 if ((msg_hdr
->__offset
< 0) ||
614 ((strings
+ msg_hdr
->__offset
) >
615 ((char *)msgcat
+ cat_hdr
.__mem
)))
616 errx(1, "%s: message header", CORRUPT
);
618 msgno
= msg_hdr
->__msgno
;
619 MCAddMsg(msgno
, strings
+ msg_hdr
->__offset
);
626 * Write message catalog.
628 * The message catalog is first converted from its internal to its
629 * external representation in a chunk of memory allocated for this
630 * purpose. Then the completed catalog is written. This approach
631 * avoids additional housekeeping variables and/or a lot of seeks
632 * that would otherwise be required.
637 int nsets
; /* number of sets */
638 int nmsgs
; /* number of msgs */
639 int string_size
; /* total size of string pool */
640 int msgcat_size
; /* total size of message catalog */
641 void *msgcat
; /* message catalog data */
642 struct _nls_cat_hdr
*cat_hdr
;
643 struct _nls_set_hdr
*set_hdr
;
644 struct _nls_msg_hdr
*msg_hdr
;
651 /* determine number of sets, number of messages, and size of the
657 for (set
= sethead
.lh_first
; set
!= NULL
;
658 set
= set
->entries
.le_next
) {
661 for (msg
= set
->msghead
.lh_first
; msg
!= NULL
;
662 msg
= msg
->entries
.le_next
) {
664 string_size
+= strlen(msg
->str
) + 1;
669 printf("number of sets: %d\n", nsets
);
670 printf("number of msgs: %d\n", nmsgs
);
671 printf("string pool size: %d\n", string_size
);
674 /* determine size and then allocate buffer for constructing external
675 * message catalog representation */
676 msgcat_size
= sizeof(struct _nls_cat_hdr
)
677 + (nsets
* sizeof(struct _nls_set_hdr
))
678 + (nmsgs
* sizeof(struct _nls_msg_hdr
))
681 msgcat
= xmalloc(msgcat_size
);
682 memset(msgcat
, '\0', msgcat_size
);
684 /* fill in msg catalog header */
685 cat_hdr
= (struct _nls_cat_hdr
*) msgcat
;
686 cat_hdr
->__magic
= htonl(_NLS_MAGIC
);
687 cat_hdr
->__nsets
= htonl(nsets
);
688 cat_hdr
->__mem
= htonl(msgcat_size
- sizeof(struct _nls_cat_hdr
));
689 cat_hdr
->__msg_hdr_offset
=
690 htonl(nsets
* sizeof(struct _nls_set_hdr
));
691 cat_hdr
->__msg_txt_offset
=
692 htonl(nsets
* sizeof(struct _nls_set_hdr
) +
693 nmsgs
* sizeof(struct _nls_msg_hdr
));
695 /* compute offsets for set & msg header tables and string pool */
696 set_hdr
= (struct _nls_set_hdr
*) ((char *) msgcat
+
697 sizeof(struct _nls_cat_hdr
));
698 msg_hdr
= (struct _nls_msg_hdr
*) ((char *) msgcat
+
699 sizeof(struct _nls_cat_hdr
) +
700 nsets
* sizeof(struct _nls_set_hdr
));
701 strings
= (char *) msgcat
+
702 sizeof(struct _nls_cat_hdr
) +
703 nsets
* sizeof(struct _nls_set_hdr
) +
704 nmsgs
* sizeof(struct _nls_msg_hdr
);
708 for (set
= sethead
.lh_first
; set
!= NULL
;
709 set
= set
->entries
.le_next
) {
712 for (msg
= set
->msghead
.lh_first
; msg
!= NULL
;
713 msg
= msg
->entries
.le_next
) {
714 int32_t msg_len
= strlen(msg
->str
) + 1;
716 msg_hdr
->__msgno
= htonl(msg
->msgId
);
717 msg_hdr
->__msglen
= htonl(msg_len
);
718 msg_hdr
->__offset
= htonl(msg_offset
);
720 memcpy(strings
, msg
->str
, msg_len
);
722 msg_offset
+= msg_len
;
728 set_hdr
->__setno
= htonl(set
->setId
);
729 set_hdr
->__nmsgs
= htonl(nmsgs
);
730 set_hdr
->__index
= htonl(msg_index
);
735 /* write out catalog. XXX: should this be done in small chunks? */
736 write(fd
, msgcat
, msgcat_size
);
745 error("setId's must be greater than zero");
748 if (setId
> NL_SETMAX
) {
749 error("setId exceeds limit");
753 p
= sethead
.lh_first
;
755 for (; p
!= NULL
&& p
->setId
< setId
; q
= p
, p
= p
->entries
.le_next
);
757 if (p
&& p
->setId
== setId
) {
760 p
= xmalloc(sizeof(struct _setT
));
761 memset(p
, '\0', sizeof(struct _setT
));
762 LIST_INIT(&p
->msghead
);
767 LIST_INSERT_HEAD(&sethead
, p
, entries
);
769 LIST_INSERT_AFTER(q
, p
, entries
);
777 MCAddMsg(int msgId
, const char *str
)
782 error("can't specify a message when no set exists");
785 error("msgId's must be greater than zero");
788 if (msgId
> NL_MSGMAX
) {
789 error("msgID exceeds limit");
793 p
= curSet
->msghead
.lh_first
;
795 for (; p
!= NULL
&& p
->msgId
< msgId
; q
= p
, p
= p
->entries
.le_next
);
797 if (p
&& p
->msgId
== msgId
) {
800 p
= xmalloc(sizeof(struct _msgT
));
801 memset(p
, '\0', sizeof(struct _msgT
));
804 LIST_INSERT_HEAD(&curSet
->msghead
, p
, entries
);
806 LIST_INSERT_AFTER(q
, p
, entries
);
811 p
->str
= xstrdup(str
);
821 error("setId's must be greater than zero");
824 if (setId
> NL_SETMAX
) {
825 error("setId exceeds limit");
829 set
= sethead
.lh_first
;
830 for (; set
!= NULL
&& set
->setId
< setId
; set
= set
->entries
.le_next
);
832 if (set
&& set
->setId
== setId
) {
833 LIST_REMOVE(set
, entries
);
834 while ((msg
= set
->msghead
.lh_first
) != NULL
) {
835 LIST_REMOVE(msg
, entries
);
842 warning(NULL
, "specified set doesn't exist");
851 error("you can't delete a message before defining the set");
853 msg
= curSet
->msghead
.lh_first
;
854 for (; msg
!= NULL
&& msg
->msgId
< msgId
; msg
= msg
->entries
.le_next
);
856 if (msg
&& msg
->msgId
== msgId
) {
857 LIST_REMOVE(msg
, entries
);
862 warning(NULL
, "specified msg doesn't exist");