summaryrefslogtreecommitdiff
path: root/usr.bin/gencat
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/gencat')
-rw-r--r--usr.bin/gencat/gencat.1233
-rw-r--r--usr.bin/gencat/gencat.c879
2 files changed, 1112 insertions, 0 deletions
diff --git a/usr.bin/gencat/gencat.1 b/usr.bin/gencat/gencat.1
new file mode 100644
index 0000000..6fcc171
--- /dev/null
+++ b/usr.bin/gencat/gencat.1
@@ -0,0 +1,233 @@
+.\" $NetBSD: gencat.1,v 1.14 2018/07/28 08:03:41 wiz Exp $
+.\"
+.\" Copyright (c) 2007 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Kee Hinckley and Brian Ginsbach.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" Written by Kee Hinckley <nazgul@somewhere.com>
+.\"
+.Dd December 29, 2011
+.Dt GENCAT 1
+.Os
+.Sh NAME
+.Nm gencat
+.Nd generates a Native Language Support (NLS) message catalog file
+.Sh SYNOPSIS
+.Nm
+.Ar catfile
+.Op Ar msgfile|- ...
+.Sh DESCRIPTION
+The
+.Nm
+utility generates a formatted message catalog
+.Ar catfile
+from stdin or one or more message source text files
+.Ar msgfile .
+The file
+.Ar catfile
+is created if it does not already exist.
+If
+.Ar catfile
+does exist, its messages are included in the new
+.Ar catfile .
+The new message text defined in
+.Ar msgfile
+replaces the old message text currently in
+.Ar catfile
+when the set and message numbers match.
+.Pp
+The generated message catalog contains message
+strings that will be retrieved using the
+.Xr catgets 3
+library call.
+These messages are dynamically loaded by the
+Native Language Support (NLS) library at run time.
+Error messages are grouped into sets, and a program can load a
+particular set depending on which type, or language, of messages
+is desired.
+.Ss Message Text Source File Format
+The message text source files are text files in the format described below.
+Note that the fields of a message text source line are separated by
+space or tab characters.
+.\" XXX Required by POSIX; the code must be fixed first. Above line should be
+.\" a single space or tab character;
+.\" any other space or tab characters are considered to be part of the
+.\" field contents.
+.Bl -tag -width 3n
+.It Li $set Ar n comment
+Determines the set identifier to be used for all subsequent messages
+until the next
+.Li $set
+or end-of-file.
+The
+.Ar n
+is the set identifier which is defined as a number in the range
+.Bo 1 ,
+.Dv NL_SETMAX Bc .
+Set identifiers within a single source file need not be contiguous.
+Any string following the set identifier is treated as a comment.
+If no
+.Li $set
+directive is specified in a message text source file,
+all messages will be located in the default message set
+.Dv NL_SETD .
+.It Li $delset Ar n comment
+Removes message set
+.Ar n
+from the catalog.
+The
+.Ar n
+is a set identifier in the range
+.Bo 1 ,
+.Dv NL_SETMAX Bc .
+If a message set was created earlier in the
+current file, or in a file previously read by the
+.Nm
+command, this directive will remove it.
+Any string following the set identifier is treated as a comment.
+.It Li $ Ar comment
+A line beginning with
+.Li $
+followed by a space or tab character is treated as a comment.
+.It Ar m message-text
+A message line consists of a message identifier
+.Ar m
+in the range
+.Bo 1 ,
+.Dv NL_MSGMAX Bc
+and the
+.Ar message-text .
+The
+.Ar message-text
+is read until the end of the line or a quote character
+(if one is specified).
+The
+.Ar message-text
+is stored in the message catalog with
+the set identifier specified by the last
+.Li $set
+directive, and the message identifier
+.Ar m .
+If the
+.Ar message-text
+is empty and there is a space or tab character
+following the message identifier,
+an empty string is stored in the message catalog.
+If no
+.Ar message-text
+is provided,
+and if there is no space or tab character following the message
+identifier,
+the message with the message identifier
+.Ar m
+in the current set is removed from the catalog.
+Message identifiers need not be contiguous within a single set.
+The length of
+.Ar message-text
+must be in the range
+.Bo 0 ,
+.Dv NL_TEXTMAX Bc .
+.It Li $quote Ar c
+Sets an optional quote character to be used around the
+.Ar message-text .
+The quote character
+.Ar c
+may be any character other than white space.
+If this is specified, then messages must begin and end with the
+quote character.
+.\" XXX Remove next sentence when code is fixed for POSIX conformance.
+This is useful when messages must contain leading white space.
+.\" XXX Replacement when above is removed.
+.\" This is useful to make leading and trailing spaces or empty
+.\" messages visible.
+By default no quote character is used.
+If an empty
+.Li $quote
+directive is specified, then the current quote character is unset.
+.El
+.Pp
+Empty lines
+.\" XXX Remove next line when the code is fixed for POSIX conformance.
+and leading blanks
+in a message text source file are ignored.
+Any line beginning with any character other than those
+described above is ignored as a syntax error.
+.Pp
+Text message strings may contain any characters and
+the following special characters and escape sequences.
+.Bl -column -offset indent ".Sy carriage return" ".Sy Symbol" ".Sy Sequence"
+.It Sy Description Ta Sy Symbol Ta Sy Sequence
+.It newline Ta NL(LF) Ta Li \en
+.It horizontal tab Ta HT Ta Li \et
+.It vertical tab Ta VT Ta Li \ev
+.It backspace Ta BS Ta Li \eb
+.It carriage return Ta CR Ta Li \er
+.It form feed Ta FF Ta Li \ef
+.It backslash Ta \e Ta Li \e\e
+.It bit pattern Ta ddd Ta Li \eddd
+.El
+.Pp
+A bit pattern,
+.Li \eddd ,
+consists of a backslash followed by
+one, two, or three octal digits representing the value of the character.
+The current quote character, if defined, may be escaped with a backslash
+to generate the quote character.
+Any character following the backslash ('\e') other than those specified
+is ignored.
+.Pp
+A backslash at the end of the line continues the message onto the next line.
+The following two lines are an example of such a message:
+.Pp
+.Dl 1 This message continues \e
+.D1 on the next line
+.Pp
+Producing the following message:
+.Pp
+.Dl 1 This message continues on the next line
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr catclose 3 ,
+.Xr catgets 3 ,
+.Xr catopen 3 ,
+.Xr nls 7
+.\" XXX Close but not quite; add when code is fixed.
+.\".Sh STANDARDS
+.\"The
+.\".Nm
+.\"utility is compliant with the
+.\".St -p1003.1-2004
+.\"standard.
+.Sh AUTHORS
+.An -nosplit
+The Native Language Support (NLS) message catalog facility was
+contributed by
+.An J.T. Conklin
+.Aq Mt jtc@NetBSD.org .
+This page was originally written by
+.An Kee Hinckley
+.Aq Mt nazgul@somewhere.com .
diff --git a/usr.bin/gencat/gencat.c b/usr.bin/gencat/gencat.c
new file mode 100644
index 0000000..444ce2d
--- /dev/null
+++ b/usr.bin/gencat/gencat.c
@@ -0,0 +1,879 @@
+/* $NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb Exp $ */
+
+/*
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by J.T. Conklin.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: gencat.c,v 1.36 2013/11/27 17:38:11 apb Exp $");
+#endif
+
+/***********************************************************
+Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that Alfalfa's name not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+If you make any modifications, bugfixes or other changes to this software
+we'd appreciate it if you could send a copy to us so we can keep things
+up-to-date. Many thanks.
+ Kee Hinckley
+ Alfalfa Software, Inc.
+ 267 Allston St., #3
+ Cambridge, MA 02139 USA
+ nazgul@alfalfa.com
+
+******************************************************************/
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#define _NLS_PRIVATE
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h> /* Needed by arpa/inet.h on NetBSD */
+#include <arpa/inet.h> /* Needed for htonl() on POSIX systems */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <nl_types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef NL_SETMAX
+#define NL_SETMAX 255
+#endif
+#ifndef NL_MSGMAX
+#define NL_MSGMAX 2048
+#endif
+
+struct _msgT {
+ long msgId;
+ char *str;
+ LIST_ENTRY(_msgT) entries;
+};
+
+struct _setT {
+ long setId;
+ LIST_HEAD(msghead, _msgT) msghead;
+ LIST_ENTRY(_setT) entries;
+};
+
+static LIST_HEAD(sethead, _setT) sethead = LIST_HEAD_INITIALIZER(sethead);
+static struct _setT *curSet;
+
+static const char *curfile;
+static char *curline = NULL;
+static long lineno = 0;
+
+static char *cskip(char *);
+__dead static void error(const char *);
+static char *get_line(int);
+static char *getmsg(int, char *, char);
+static void warning(const char *, const char *);
+static char *wskip(char *);
+static char *xstrdup(const char *);
+static void *xmalloc(size_t);
+static void *xrealloc(void *, size_t);
+
+static void MCParse(int fd);
+static void MCReadCat(int fd);
+static void MCWriteCat(int fd);
+static void MCDelMsg(int msgId);
+static void MCAddMsg(int msgId, const char *msg);
+static void MCAddSet(int setId);
+static void MCDelSet(int setId);
+__dead static void usage(void);
+
+#define CORRUPT "corrupt message catalog"
+#define NOMEMORY "out of memory"
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s catfile [msgfile|- ...]\n", getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ofd, ifd;
+ char *catfile = NULL;
+ int c;
+ int updatecat = 0;
+
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ /* NOTREACHED */
+ }
+ catfile = *argv++;
+
+ if ((catfile[0] == '-') && (catfile[1] == '\0')) {
+ ofd = STDOUT_FILENO;
+ } else {
+ ofd = open(catfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (ofd < 0) {
+ if (errno == EEXIST) {
+ if ((ofd = open(catfile, O_RDWR)) < 0) {
+ err(1, "Unable to open %s", catfile);
+ /* NOTREACHED */
+ }
+ } else {
+ err(1, "Unable to create new %s", catfile);
+ /* NOTREACHED */
+ }
+ curfile = catfile;
+ updatecat = 1;
+ MCReadCat(ofd);
+ if (lseek(ofd, (off_t)0, SEEK_SET) == (off_t)-1) {
+ err(1, "Unable to seek on %s", catfile);
+ /* NOTREACHED */
+ }
+ }
+ }
+
+ if (argc < 2 || (((*argv)[0] == '-') && ((*argv)[1] == '\0'))) {
+ if (argc > 2)
+ usage();
+ /* NOTREACHED */
+ MCParse(STDIN_FILENO);
+ } else {
+ for (; *argv; argv++) {
+ if ((ifd = open(*argv, O_RDONLY)) < 0)
+ err(1, "Unable to read %s", *argv);
+ curfile = *argv;
+ lineno = 0;
+ MCParse(ifd);
+ close(ifd);
+ }
+ }
+
+ if (updatecat) {
+ if (ftruncate(ofd, 0) != 0) {
+ err(1, "Unable to truncate %s", catfile);
+ /* NOTREACHED */
+ }
+ }
+
+ MCWriteCat(ofd);
+ exit(0);
+}
+
+static void
+warning(const char *cptr, const char *msg)
+{
+ if (lineno) {
+ fprintf(stderr, "%s: %s on line %ld, %s\n",
+ getprogname(), msg, lineno, curfile);
+ fprintf(stderr, "%s\n", curline);
+ if (cptr) {
+ char *tptr;
+ for (tptr = curline; tptr < cptr; ++tptr)
+ putc(' ', stderr);
+ fprintf(stderr, "^\n");
+ }
+ } else {
+ fprintf(stderr, "%s: %s, %s\n", getprogname(), msg, curfile);
+ }
+}
+
+static void
+error(const char *msg)
+{
+ warning(NULL, msg);
+ exit(1);
+}
+
+static void *
+xmalloc(size_t len)
+{
+ void *p;
+
+ if ((p = malloc(len)) == NULL)
+ errx(1, NOMEMORY);
+ return (p);
+}
+
+static void *
+xrealloc(void *ptr, size_t size)
+{
+ if ((ptr = realloc(ptr, size)) == NULL)
+ errx(1, NOMEMORY);
+ return (ptr);
+}
+
+static char *
+xstrdup(const char *str)
+{
+ char *nstr;
+
+ if ((nstr = strdup(str)) == NULL)
+ errx(1, NOMEMORY);
+ return (nstr);
+}
+
+static char *
+get_line(int fd)
+{
+ static long curlen = BUFSIZ;
+ static char buf[BUFSIZ], *bptr = buf, *bend = buf;
+ char *cptr, *cend;
+ long buflen;
+
+ if (!curline) {
+ curline = xmalloc(curlen);
+ }
+ ++lineno;
+
+ cptr = curline;
+ cend = curline + curlen;
+ for (;;) {
+ for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
+ if (*bptr == '\n') {
+ *cptr = '\0';
+ ++bptr;
+ return (curline);
+ } else
+ *cptr = *bptr;
+ }
+ if (cptr == cend) {
+ cptr = curline = xrealloc(curline, curlen *= 2);
+ cend = curline + curlen;
+ }
+ if (bptr == bend) {
+ buflen = read(fd, buf, BUFSIZ);
+ if (buflen <= 0) {
+ if (cptr > curline) {
+ *cptr = '\0';
+ return (curline);
+ }
+ return (NULL);
+ }
+ bend = buf + buflen;
+ bptr = buf;
+ }
+ }
+}
+
+static char *
+wskip(char *cptr)
+{
+ if (!*cptr || !isspace((unsigned char) *cptr)) {
+ warning(cptr, "expected a space");
+ return (cptr);
+ }
+ while (*cptr && isspace((unsigned char) *cptr))
+ ++cptr;
+ return (cptr);
+}
+
+static char *
+cskip(char *cptr)
+{
+ if (!*cptr || isspace((unsigned char) *cptr)) {
+ warning(cptr, "wasn't expecting a space");
+ return (cptr);
+ }
+ while (*cptr && !isspace((unsigned char) *cptr))
+ ++cptr;
+ return (cptr);
+}
+
+static char *
+getmsg(int fd, char *cptr, char quote)
+{
+ static char *msg = NULL;
+ static size_t msglen = 0;
+ size_t clen, i;
+ int in_quote = 0;
+ char *tptr;
+
+ if (quote && *cptr == quote) {
+ ++cptr;
+ in_quote = 1;
+ }
+
+ clen = strlen(cptr) + 1;
+ if (clen > msglen) {
+ if (msglen)
+ msg = xrealloc(msg, clen);
+ else
+ msg = xmalloc(clen);
+ msglen = clen;
+ }
+ tptr = msg;
+
+ while (*cptr) {
+ if (quote && *cptr == quote) {
+ char *tmp;
+ tmp = cptr + 1;
+ if (!in_quote) {
+ /* XXX hard error? */
+ warning(cptr, "unexpected quote character, ignoring");
+ *tptr++ = *cptr++;
+ } else {
+ cptr++;
+ /* don't use wskip() */
+ while (*cptr && isspace((unsigned char) *cptr))
+#ifndef _BACKWARDS_COMPAT
+ cptr++;
+#else
+ *tptr++ = *cptr++;
+#endif
+ /* XXX hard error? */
+ if (*cptr)
+ warning(tmp, "unexpected extra characters, ignoring");
+ in_quote = 0;
+#ifndef _BACKWARDS_COMPAT
+ break;
+#endif
+ }
+ } else {
+ if (*cptr == '\\') {
+ ++cptr;
+ switch (*cptr) {
+ case '\0':
+ cptr = get_line(fd);
+ if (!cptr)
+ error("premature end of file");
+ msglen += strlen(cptr);
+ i = tptr - msg;
+ msg = xrealloc(msg, msglen);
+ tptr = msg + i;
+ break;
+ case 'n':
+ *tptr++ = '\n';
+ ++cptr;
+ break;
+ case 't':
+ *tptr++ = '\t';
+ ++cptr;
+ break;
+ case 'v':
+ *tptr++ = '\v';
+ ++cptr;
+ break;
+ case 'b':
+ *tptr++ = '\b';
+ ++cptr;
+ break;
+ case 'r':
+ *tptr++ = '\r';
+ ++cptr;
+ break;
+ case 'f':
+ *tptr++ = '\f';
+ ++cptr;
+ break;
+ case '\\':
+ *tptr++ = '\\';
+ ++cptr;
+ break;
+ default:
+ if (quote && *cptr == quote) {
+ *tptr++ = *cptr++;
+ } else if (isdigit((unsigned char) *cptr)) {
+ *tptr = 0;
+ for (i = 0; i < 3; ++i) {
+ if (!isdigit((unsigned char) *cptr))
+ break;
+ if (*cptr > '7')
+ warning(cptr, "octal number greater than 7?!");
+ *tptr *= 8;
+ *tptr += (*cptr - '0');
+ ++cptr;
+ }
+ } else {
+ warning(cptr, "unrecognized escape sequence");
+ }
+ break;
+ }
+ } else {
+ *tptr++ = *cptr++;
+ }
+ }
+ }
+
+ if (in_quote)
+ warning(cptr, "unterminated quoted message, ignoring");
+
+ *tptr = '\0';
+ return (msg);
+}
+
+static void
+MCParse(int fd)
+{
+ char *cptr, *str;
+ int msgid = 0;
+ int setid = 0;
+ char quote = 0;
+
+ while ((cptr = get_line(fd))) {
+ if (*cptr == '$') {
+ ++cptr;
+ if (strncmp(cptr, "set", 3) == 0) {
+ cptr += 3;
+ cptr = wskip(cptr);
+ setid = atoi(cptr);
+ MCAddSet(setid);
+ msgid = 0;
+ } else if (strncmp(cptr, "delset", 6) == 0) {
+ cptr += 6;
+ cptr = wskip(cptr);
+ setid = atoi(cptr);
+ MCDelSet(setid);
+ } else if (strncmp(cptr, "quote", 5) == 0) {
+ cptr += 5;
+ if (!*cptr)
+ quote = 0;
+ else {
+ cptr = wskip(cptr);
+ if (!*cptr)
+ quote = 0;
+ else
+ quote = *cptr;
+ }
+ } else if (isspace((unsigned char) *cptr)) {
+ ;
+ } else {
+ if (*cptr) {
+ cptr = wskip(cptr);
+ if (*cptr)
+ warning(cptr, "unrecognized line");
+ }
+ }
+ } else {
+ /*
+ * First check for (and eat) empty lines....
+ */
+ if (!*cptr)
+ continue;
+ /*
+ * We have a digit? Start of a message. Else,
+ * syntax error.
+ */
+ if (isdigit((unsigned char) *cptr)) {
+ msgid = atoi(cptr);
+ cptr = cskip(cptr);
+ if (*cptr) {
+ cptr = wskip(cptr);
+ if (!*cptr) {
+ MCAddMsg(msgid, "");
+ continue;
+ }
+ }
+ } else {
+ warning(cptr, "neither blank line nor start of a message id");
+ continue;
+ }
+ /*
+ * If no set directive specified, all messages
+ * shall be in default message set NL_SETD.
+ */
+ if (setid == 0) {
+ setid = NL_SETD;
+ MCAddSet(setid);
+ }
+ /*
+ * If we have a message ID, but no message,
+ * then this means "delete this message id
+ * from the catalog".
+ */
+ if (!*cptr) {
+ MCDelMsg(msgid);
+ } else {
+ str = getmsg(fd, cptr, quote);
+ MCAddMsg(msgid, str);
+ }
+ }
+ }
+}
+
+static void
+MCReadCat(int fd)
+{
+ void *msgcat; /* message catalog data */
+ struct _nls_cat_hdr cat_hdr;
+ struct _nls_set_hdr *set_hdr;
+ struct _nls_msg_hdr *msg_hdr;
+ char *strings;
+ ssize_t n;
+ int m, s;
+ int msgno, setno;
+
+ n = read(fd, &cat_hdr, sizeof(cat_hdr));
+ if (n < (ssize_t)sizeof(cat_hdr)) {
+ if (n == 0)
+ return; /* empty file */
+ else if (n == -1)
+ err(1, "header read");
+ else
+ errx(1, CORRUPT);
+ }
+ if (ntohl((uint32_t)cat_hdr.__magic) != _NLS_MAGIC)
+ errx(1, "%s: bad magic number (%#x)", CORRUPT, cat_hdr.__magic);
+
+ cat_hdr.__mem = ntohl(cat_hdr.__mem);
+
+ cat_hdr.__nsets = ntohl(cat_hdr.__nsets);
+ cat_hdr.__msg_hdr_offset = ntohl(cat_hdr.__msg_hdr_offset);
+ cat_hdr.__msg_txt_offset = ntohl(cat_hdr.__msg_txt_offset);
+ if ((cat_hdr.__mem < 0) ||
+ (cat_hdr.__msg_hdr_offset < 0) ||
+ (cat_hdr.__msg_txt_offset < 0) ||
+ (cat_hdr.__mem < (int32_t)(cat_hdr.__nsets * sizeof(struct _nls_set_hdr))) ||
+ (cat_hdr.__mem < cat_hdr.__msg_hdr_offset) ||
+ (cat_hdr.__mem < cat_hdr.__msg_txt_offset))
+ errx(1, "%s: catalog header", CORRUPT);
+
+ msgcat = xmalloc(cat_hdr.__mem);
+
+ n = read(fd, msgcat, cat_hdr.__mem);
+ if (n < cat_hdr.__mem) {
+ if (n == -1)
+ err(1, "data read");
+ else
+ errx(1, CORRUPT);
+ }
+
+ set_hdr = (struct _nls_set_hdr *)msgcat;
+ msg_hdr = (struct _nls_msg_hdr *)((char *)msgcat +
+ cat_hdr.__msg_hdr_offset);
+ strings = (char *)msgcat + cat_hdr.__msg_txt_offset;
+
+ setno = 0;
+ for (s = 0; s < cat_hdr.__nsets; s++, set_hdr++) {
+ set_hdr->__setno = ntohl(set_hdr->__setno);
+ if (set_hdr->__setno < setno)
+ errx(1, "%s: bad set number (%d)",
+ CORRUPT, set_hdr->__setno);
+ setno = set_hdr->__setno;
+
+ MCAddSet(setno);
+
+ set_hdr->__nmsgs = ntohl(set_hdr->__nmsgs);
+ set_hdr->__index = ntohl(set_hdr->__index);
+ if (set_hdr->__nmsgs < 0 || set_hdr->__index < 0)
+ errx(1, "%s: set header", CORRUPT);
+
+ /* Get the data */
+ msgno = 0;
+ for (m = 0; m < set_hdr->__nmsgs; m++, msg_hdr++) {
+ msg_hdr->__msgno = ntohl(msg_hdr->__msgno);
+ msg_hdr->__offset = ntohl(msg_hdr->__offset);
+ if (msg_hdr->__msgno < msgno)
+ errx(1, "%s: bad message number (%d)",
+ CORRUPT, msg_hdr->__msgno);
+ if ((msg_hdr->__offset < 0) ||
+ ((strings + msg_hdr->__offset) >
+ ((char *)msgcat + cat_hdr.__mem)))
+ errx(1, "%s: message header", CORRUPT);
+
+ msgno = msg_hdr->__msgno;
+ MCAddMsg(msgno, strings + msg_hdr->__offset);
+ }
+ }
+ free(msgcat);
+}
+
+/*
+ * Write message catalog.
+ *
+ * The message catalog is first converted from its internal to its
+ * external representation in a chunk of memory allocated for this
+ * purpose. Then the completed catalog is written. This approach
+ * avoids additional housekeeping variables and/or a lot of seeks
+ * that would otherwise be required.
+ */
+static void
+MCWriteCat(int fd)
+{
+ int nsets; /* number of sets */
+ int nmsgs; /* number of msgs */
+ int string_size; /* total size of string pool */
+ int msgcat_size; /* total size of message catalog */
+ void *msgcat; /* message catalog data */
+ struct _nls_cat_hdr *cat_hdr;
+ struct _nls_set_hdr *set_hdr;
+ struct _nls_msg_hdr *msg_hdr;
+ char *strings;
+ struct _setT *set;
+ struct _msgT *msg;
+ int msg_index;
+ int msg_offset;
+
+ /* determine number of sets, number of messages, and size of the
+ * string pool */
+ nsets = 0;
+ nmsgs = 0;
+ string_size = 0;
+
+ LIST_FOREACH(set, &sethead, entries) {
+ nsets++;
+
+ LIST_FOREACH(msg, &set->msghead, entries) {
+ nmsgs++;
+ string_size += strlen(msg->str) + 1;
+ }
+ }
+
+#ifdef DEBUG
+ printf("number of sets: %d\n", nsets);
+ printf("number of msgs: %d\n", nmsgs);
+ printf("string pool size: %d\n", string_size);
+#endif
+
+ /* determine size and then allocate buffer for constructing external
+ * message catalog representation */
+ msgcat_size = sizeof(struct _nls_cat_hdr)
+ + (nsets * sizeof(struct _nls_set_hdr))
+ + (nmsgs * sizeof(struct _nls_msg_hdr))
+ + string_size;
+
+ msgcat = xmalloc(msgcat_size);
+ memset(msgcat, '\0', msgcat_size);
+
+ /* fill in msg catalog header */
+ cat_hdr = (struct _nls_cat_hdr *) msgcat;
+ cat_hdr->__magic = htonl(_NLS_MAGIC);
+ cat_hdr->__nsets = htonl(nsets);
+ cat_hdr->__mem = htonl(msgcat_size - sizeof(struct _nls_cat_hdr));
+ cat_hdr->__msg_hdr_offset =
+ htonl(nsets * sizeof(struct _nls_set_hdr));
+ cat_hdr->__msg_txt_offset =
+ htonl(nsets * sizeof(struct _nls_set_hdr) +
+ nmsgs * sizeof(struct _nls_msg_hdr));
+
+ /* compute offsets for set & msg header tables and string pool */
+ set_hdr = (struct _nls_set_hdr *) ((char *) msgcat +
+ sizeof(struct _nls_cat_hdr));
+ msg_hdr = (struct _nls_msg_hdr *) ((char *) msgcat +
+ sizeof(struct _nls_cat_hdr) +
+ nsets * sizeof(struct _nls_set_hdr));
+ strings = (char *) msgcat +
+ sizeof(struct _nls_cat_hdr) +
+ nsets * sizeof(struct _nls_set_hdr) +
+ nmsgs * sizeof(struct _nls_msg_hdr);
+
+ msg_index = 0;
+ msg_offset = 0;
+ LIST_FOREACH(set, &sethead, entries) {
+
+ nmsgs = 0;
+ LIST_FOREACH(msg, &set->msghead, entries) {
+ int32_t msg_len = strlen(msg->str) + 1;
+
+ msg_hdr->__msgno = htonl(msg->msgId);
+ msg_hdr->__msglen = htonl(msg_len);
+ msg_hdr->__offset = htonl(msg_offset);
+
+ memcpy(strings, msg->str, msg_len);
+ strings += msg_len;
+ msg_offset += msg_len;
+
+ nmsgs++;
+ msg_hdr++;
+ }
+
+ set_hdr->__setno = htonl(set->setId);
+ set_hdr->__nmsgs = htonl(nmsgs);
+ set_hdr->__index = htonl(msg_index);
+ msg_index += nmsgs;
+ set_hdr++;
+ }
+
+ /* write out catalog. XXX: should this be done in small chunks? */
+ write(fd, msgcat, msgcat_size);
+}
+
+static void
+MCAddSet(int setId)
+{
+ struct _setT *p, *q;
+
+ if (setId <= 0) {
+ error("setId's must be greater than zero");
+ /* NOTREACHED */
+ }
+ if (setId > NL_SETMAX) {
+ error("setId exceeds limit");
+ /* NOTREACHED */
+ }
+
+ p = LIST_FIRST(&sethead);
+ q = NULL;
+ for (; p != NULL && p->setId < setId; q = p, p = LIST_NEXT(p, entries))
+ continue;
+
+ if (p && p->setId == setId) {
+ ;
+ } else {
+ p = xmalloc(sizeof(struct _setT));
+ memset(p, '\0', sizeof(struct _setT));
+ LIST_INIT(&p->msghead);
+
+ p->setId = setId;
+
+ if (q == NULL) {
+ LIST_INSERT_HEAD(&sethead, p, entries);
+ } else {
+ LIST_INSERT_AFTER(q, p, entries);
+ }
+ }
+
+ curSet = p;
+}
+
+static void
+MCAddMsg(int msgId, const char *str)
+{
+ struct _msgT *p, *q;
+
+ if (!curSet)
+ error("can't specify a message when no set exists");
+
+ if (msgId <= 0) {
+ error("msgId's must be greater than zero");
+ /* NOTREACHED */
+ }
+ if (msgId > NL_MSGMAX) {
+ error("msgID exceeds limit");
+ /* NOTREACHED */
+ }
+
+ p = LIST_FIRST(&curSet->msghead);
+ q = NULL;
+ for (; p != NULL && p->msgId < msgId; q = p, p = LIST_NEXT(p, entries))
+ continue;
+
+ if (p && p->msgId == msgId) {
+ free(p->str);
+ } else {
+ p = xmalloc(sizeof(struct _msgT));
+ memset(p, '\0', sizeof(struct _msgT));
+
+ if (q == NULL) {
+ LIST_INSERT_HEAD(&curSet->msghead, p, entries);
+ } else {
+ LIST_INSERT_AFTER(q, p, entries);
+ }
+ }
+
+ p->msgId = msgId;
+ p->str = xstrdup(str);
+}
+
+static void
+MCDelSet(int setId)
+{
+ struct _setT *set;
+ struct _msgT *msg;
+
+ if (setId <= 0) {
+ error("setId's must be greater than zero");
+ /* NOTREACHED */
+ }
+ if (setId > NL_SETMAX) {
+ error("setId exceeds limit");
+ /* NOTREACHED */
+ }
+
+ set = LIST_FIRST(&sethead);
+ for (; set != NULL && set->setId < setId; set = LIST_NEXT(set, entries))
+ continue;
+
+ if (set && set->setId == setId) {
+ LIST_REMOVE(set, entries);
+ while ((msg = LIST_FIRST(&set->msghead)) != NULL) {
+ LIST_REMOVE(msg, entries);
+ free(msg->str);
+ free(msg);
+ }
+ free(set);
+ return;
+ }
+ warning(NULL, "specified set doesn't exist");
+}
+
+static void
+MCDelMsg(int msgId)
+{
+ struct _msgT *msg;
+
+ if (!curSet)
+ error("you can't delete a message before defining the set");
+
+ msg = LIST_FIRST(&curSet->msghead);
+ for (; msg != NULL && msg->msgId < msgId; msg = LIST_NEXT(msg, entries))
+ continue;
+
+ if (msg && msg->msgId == msgId) {
+ LIST_REMOVE(msg, entries);
+ free(msg->str);
+ free(msg);
+ return;
+ }
+ warning(NULL, "specified msg doesn't exist");
+}