1 /* $NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb 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.36 2013/11/27 17:38:11 apb 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 static LIST_HEAD(sethead
, _setT
) sethead
= LIST_HEAD_INITIALIZER(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 __dead
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 static void MCParse(int fd
);
129 static void MCReadCat(int fd
);
130 static void MCWriteCat(int fd
);
131 static void MCDelMsg(int msgId
);
132 static void MCAddMsg(int msgId
, const char *msg
);
133 static void MCAddSet(int setId
);
134 static void MCDelSet(int setId
);
135 __dead
static void usage(void);
137 #define CORRUPT "corrupt message catalog"
138 #define NOMEMORY "out of memory"
143 fprintf(stderr
, "usage: %s catfile [msgfile|- ...]\n", getprogname());
148 main(int argc
, char *argv
[])
151 char *catfile
= NULL
;
155 while ((c
= getopt(argc
, argv
, "")) != -1) {
172 if ((catfile
[0] == '-') && (catfile
[1] == '\0')) {
175 ofd
= open(catfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
177 if (errno
== EEXIST
) {
178 if ((ofd
= open(catfile
, O_RDWR
)) < 0) {
179 err(1, "Unable to open %s", catfile
);
183 err(1, "Unable to create new %s", catfile
);
189 if (lseek(ofd
, (off_t
)0, SEEK_SET
) == (off_t
)-1) {
190 err(1, "Unable to seek on %s", catfile
);
196 if (argc
< 2 || (((*argv
)[0] == '-') && ((*argv
)[1] == '\0'))) {
200 MCParse(STDIN_FILENO
);
202 for (; *argv
; argv
++) {
203 if ((ifd
= open(*argv
, O_RDONLY
)) < 0)
204 err(1, "Unable to read %s", *argv
);
213 if (ftruncate(ofd
, 0) != 0) {
214 err(1, "Unable to truncate %s", catfile
);
224 warning(const char *cptr
, const char *msg
)
227 fprintf(stderr
, "%s: %s on line %ld, %s\n",
228 getprogname(), msg
, lineno
, curfile
);
229 fprintf(stderr
, "%s\n", curline
);
232 for (tptr
= curline
; tptr
< cptr
; ++tptr
)
234 fprintf(stderr
, "^\n");
237 fprintf(stderr
, "%s: %s, %s\n", getprogname(), msg
, curfile
);
242 error(const char *msg
)
253 if ((p
= malloc(len
)) == NULL
)
259 xrealloc(void *ptr
, size_t size
)
261 if ((ptr
= realloc(ptr
, size
)) == NULL
)
267 xstrdup(const char *str
)
271 if ((nstr
= strdup(str
)) == NULL
)
279 static long curlen
= BUFSIZ
;
280 static char buf
[BUFSIZ
], *bptr
= buf
, *bend
= buf
;
285 curline
= xmalloc(curlen
);
290 cend
= curline
+ curlen
;
292 for (; bptr
< bend
&& cptr
< cend
; ++cptr
, ++bptr
) {
301 cptr
= curline
= xrealloc(curline
, curlen
*= 2);
302 cend
= curline
+ curlen
;
305 buflen
= read(fd
, buf
, BUFSIZ
);
307 if (cptr
> curline
) {
322 if (!*cptr
|| !isspace((unsigned char) *cptr
)) {
323 warning(cptr
, "expected a space");
326 while (*cptr
&& isspace((unsigned char) *cptr
))
334 if (!*cptr
|| isspace((unsigned char) *cptr
)) {
335 warning(cptr
, "wasn't expecting a space");
338 while (*cptr
&& !isspace((unsigned char) *cptr
))
344 getmsg(int fd
, char *cptr
, char quote
)
346 static char *msg
= NULL
;
347 static size_t msglen
= 0;
352 if (quote
&& *cptr
== quote
) {
357 clen
= strlen(cptr
) + 1;
360 msg
= xrealloc(msg
, clen
);
368 if (quote
&& *cptr
== quote
) {
372 /* XXX hard error? */
373 warning(cptr
, "unexpected quote character, ignoring");
377 /* don't use wskip() */
378 while (*cptr
&& isspace((unsigned char) *cptr
))
379 #ifndef _BACKWARDS_COMPAT
384 /* XXX hard error? */
386 warning(tmp
, "unexpected extra characters, ignoring");
388 #ifndef _BACKWARDS_COMPAT
399 error("premature end of file");
400 msglen
+= strlen(cptr
);
402 msg
= xrealloc(msg
, msglen
);
434 if (quote
&& *cptr
== quote
) {
436 } else if (isdigit((unsigned char) *cptr
)) {
438 for (i
= 0; i
< 3; ++i
) {
439 if (!isdigit((unsigned char) *cptr
))
442 warning(cptr
, "octal number greater than 7?!");
444 *tptr
+= (*cptr
- '0');
448 warning(cptr
, "unrecognized escape sequence");
459 warning(cptr
, "unterminated quoted message, ignoring");
473 while ((cptr
= get_line(fd
))) {
476 if (strncmp(cptr
, "set", 3) == 0) {
482 } else if (strncmp(cptr
, "delset", 6) == 0) {
487 } else if (strncmp(cptr
, "quote", 5) == 0) {
498 } else if (isspace((unsigned char) *cptr
)) {
504 warning(cptr
, "unrecognized line");
509 * First check for (and eat) empty lines....
514 * We have a digit? Start of a message. Else,
517 if (isdigit((unsigned char) *cptr
)) {
528 warning(cptr
, "neither blank line nor start of a message id");
532 * If no set directive specified, all messages
533 * shall be in default message set NL_SETD.
540 * If we have a message ID, but no message,
541 * then this means "delete this message id
547 str
= getmsg(fd
, cptr
, quote
);
548 MCAddMsg(msgid
, str
);
557 void *msgcat
; /* message catalog data */
558 struct _nls_cat_hdr cat_hdr
;
559 struct _nls_set_hdr
*set_hdr
;
560 struct _nls_msg_hdr
*msg_hdr
;
566 n
= read(fd
, &cat_hdr
, sizeof(cat_hdr
));
567 if (n
< (ssize_t
)sizeof(cat_hdr
)) {
569 return; /* empty file */
571 err(1, "header read");
575 if (ntohl((uint32_t)cat_hdr
.__magic
) != _NLS_MAGIC
)
576 errx(1, "%s: bad magic number (%#x)", CORRUPT
, cat_hdr
.__magic
);
578 cat_hdr
.__mem
= ntohl(cat_hdr
.__mem
);
580 cat_hdr
.__nsets
= ntohl(cat_hdr
.__nsets
);
581 cat_hdr
.__msg_hdr_offset
= ntohl(cat_hdr
.__msg_hdr_offset
);
582 cat_hdr
.__msg_txt_offset
= ntohl(cat_hdr
.__msg_txt_offset
);
583 if ((cat_hdr
.__mem
< 0) ||
584 (cat_hdr
.__msg_hdr_offset
< 0) ||
585 (cat_hdr
.__msg_txt_offset
< 0) ||
586 (cat_hdr
.__mem
< (int32_t)(cat_hdr
.__nsets
* sizeof(struct _nls_set_hdr
))) ||
587 (cat_hdr
.__mem
< cat_hdr
.__msg_hdr_offset
) ||
588 (cat_hdr
.__mem
< cat_hdr
.__msg_txt_offset
))
589 errx(1, "%s: catalog header", CORRUPT
);
591 msgcat
= xmalloc(cat_hdr
.__mem
);
593 n
= read(fd
, msgcat
, cat_hdr
.__mem
);
594 if (n
< cat_hdr
.__mem
) {
601 set_hdr
= (struct _nls_set_hdr
*)msgcat
;
602 msg_hdr
= (struct _nls_msg_hdr
*)((char *)msgcat
+
603 cat_hdr
.__msg_hdr_offset
);
604 strings
= (char *)msgcat
+ cat_hdr
.__msg_txt_offset
;
607 for (s
= 0; s
< cat_hdr
.__nsets
; s
++, set_hdr
++) {
608 set_hdr
->__setno
= ntohl(set_hdr
->__setno
);
609 if (set_hdr
->__setno
< setno
)
610 errx(1, "%s: bad set number (%d)",
611 CORRUPT
, set_hdr
->__setno
);
612 setno
= set_hdr
->__setno
;
616 set_hdr
->__nmsgs
= ntohl(set_hdr
->__nmsgs
);
617 set_hdr
->__index
= ntohl(set_hdr
->__index
);
618 if (set_hdr
->__nmsgs
< 0 || set_hdr
->__index
< 0)
619 errx(1, "%s: set header", CORRUPT
);
623 for (m
= 0; m
< set_hdr
->__nmsgs
; m
++, msg_hdr
++) {
624 msg_hdr
->__msgno
= ntohl(msg_hdr
->__msgno
);
625 msg_hdr
->__offset
= ntohl(msg_hdr
->__offset
);
626 if (msg_hdr
->__msgno
< msgno
)
627 errx(1, "%s: bad message number (%d)",
628 CORRUPT
, msg_hdr
->__msgno
);
629 if ((msg_hdr
->__offset
< 0) ||
630 ((strings
+ msg_hdr
->__offset
) >
631 ((char *)msgcat
+ cat_hdr
.__mem
)))
632 errx(1, "%s: message header", CORRUPT
);
634 msgno
= msg_hdr
->__msgno
;
635 MCAddMsg(msgno
, strings
+ msg_hdr
->__offset
);
642 * Write message catalog.
644 * The message catalog is first converted from its internal to its
645 * external representation in a chunk of memory allocated for this
646 * purpose. Then the completed catalog is written. This approach
647 * avoids additional housekeeping variables and/or a lot of seeks
648 * that would otherwise be required.
653 int nsets
; /* number of sets */
654 int nmsgs
; /* number of msgs */
655 int string_size
; /* total size of string pool */
656 int msgcat_size
; /* total size of message catalog */
657 void *msgcat
; /* message catalog data */
658 struct _nls_cat_hdr
*cat_hdr
;
659 struct _nls_set_hdr
*set_hdr
;
660 struct _nls_msg_hdr
*msg_hdr
;
667 /* determine number of sets, number of messages, and size of the
673 LIST_FOREACH(set
, &sethead
, entries
) {
676 LIST_FOREACH(msg
, &set
->msghead
, entries
) {
678 string_size
+= strlen(msg
->str
) + 1;
683 printf("number of sets: %d\n", nsets
);
684 printf("number of msgs: %d\n", nmsgs
);
685 printf("string pool size: %d\n", string_size
);
688 /* determine size and then allocate buffer for constructing external
689 * message catalog representation */
690 msgcat_size
= sizeof(struct _nls_cat_hdr
)
691 + (nsets
* sizeof(struct _nls_set_hdr
))
692 + (nmsgs
* sizeof(struct _nls_msg_hdr
))
695 msgcat
= xmalloc(msgcat_size
);
696 memset(msgcat
, '\0', msgcat_size
);
698 /* fill in msg catalog header */
699 cat_hdr
= (struct _nls_cat_hdr
*) msgcat
;
700 cat_hdr
->__magic
= htonl(_NLS_MAGIC
);
701 cat_hdr
->__nsets
= htonl(nsets
);
702 cat_hdr
->__mem
= htonl(msgcat_size
- sizeof(struct _nls_cat_hdr
));
703 cat_hdr
->__msg_hdr_offset
=
704 htonl(nsets
* sizeof(struct _nls_set_hdr
));
705 cat_hdr
->__msg_txt_offset
=
706 htonl(nsets
* sizeof(struct _nls_set_hdr
) +
707 nmsgs
* sizeof(struct _nls_msg_hdr
));
709 /* compute offsets for set & msg header tables and string pool */
710 set_hdr
= (struct _nls_set_hdr
*) ((char *) msgcat
+
711 sizeof(struct _nls_cat_hdr
));
712 msg_hdr
= (struct _nls_msg_hdr
*) ((char *) msgcat
+
713 sizeof(struct _nls_cat_hdr
) +
714 nsets
* sizeof(struct _nls_set_hdr
));
715 strings
= (char *) msgcat
+
716 sizeof(struct _nls_cat_hdr
) +
717 nsets
* sizeof(struct _nls_set_hdr
) +
718 nmsgs
* sizeof(struct _nls_msg_hdr
);
722 LIST_FOREACH(set
, &sethead
, entries
) {
725 LIST_FOREACH(msg
, &set
->msghead
, entries
) {
726 int32_t msg_len
= strlen(msg
->str
) + 1;
728 msg_hdr
->__msgno
= htonl(msg
->msgId
);
729 msg_hdr
->__msglen
= htonl(msg_len
);
730 msg_hdr
->__offset
= htonl(msg_offset
);
732 memcpy(strings
, msg
->str
, msg_len
);
734 msg_offset
+= msg_len
;
740 set_hdr
->__setno
= htonl(set
->setId
);
741 set_hdr
->__nmsgs
= htonl(nmsgs
);
742 set_hdr
->__index
= htonl(msg_index
);
747 /* write out catalog. XXX: should this be done in small chunks? */
748 write(fd
, msgcat
, msgcat_size
);
757 error("setId's must be greater than zero");
760 if (setId
> NL_SETMAX
) {
761 error("setId exceeds limit");
765 p
= LIST_FIRST(&sethead
);
767 for (; p
!= NULL
&& p
->setId
< setId
; q
= p
, p
= LIST_NEXT(p
, entries
))
770 if (p
&& p
->setId
== setId
) {
773 p
= xmalloc(sizeof(struct _setT
));
774 memset(p
, '\0', sizeof(struct _setT
));
775 LIST_INIT(&p
->msghead
);
780 LIST_INSERT_HEAD(&sethead
, p
, entries
);
782 LIST_INSERT_AFTER(q
, p
, entries
);
790 MCAddMsg(int msgId
, const char *str
)
795 error("can't specify a message when no set exists");
798 error("msgId's must be greater than zero");
801 if (msgId
> NL_MSGMAX
) {
802 error("msgID exceeds limit");
806 p
= LIST_FIRST(&curSet
->msghead
);
808 for (; p
!= NULL
&& p
->msgId
< msgId
; q
= p
, p
= LIST_NEXT(p
, entries
))
811 if (p
&& p
->msgId
== msgId
) {
814 p
= xmalloc(sizeof(struct _msgT
));
815 memset(p
, '\0', sizeof(struct _msgT
));
818 LIST_INSERT_HEAD(&curSet
->msghead
, p
, entries
);
820 LIST_INSERT_AFTER(q
, p
, entries
);
825 p
->str
= xstrdup(str
);
835 error("setId's must be greater than zero");
838 if (setId
> NL_SETMAX
) {
839 error("setId exceeds limit");
843 set
= LIST_FIRST(&sethead
);
844 for (; set
!= NULL
&& set
->setId
< setId
; set
= LIST_NEXT(set
, entries
))
847 if (set
&& set
->setId
== setId
) {
848 LIST_REMOVE(set
, entries
);
849 while ((msg
= LIST_FIRST(&set
->msghead
)) != NULL
) {
850 LIST_REMOVE(msg
, entries
);
857 warning(NULL
, "specified set doesn't exist");
866 error("you can't delete a message before defining the set");
868 msg
= LIST_FIRST(&curSet
->msghead
);
869 for (; msg
!= NULL
&& msg
->msgId
< msgId
; msg
= LIST_NEXT(msg
, entries
))
872 if (msg
&& msg
->msgId
== msgId
) {
873 LIST_REMOVE(msg
, entries
);
878 warning(NULL
, "specified msg doesn't exist");