#include <sys/cdefs.h>
#include "fbsdcompat.h"
__FBSDID("$FreeBSD: src/usr.bin/gencat/genlib.c,v 1.13 2002/12/24 07:40:10 davidxu Exp $");
#include <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <nl_types.h>
#include "msgcat.h"
#include "gencat.h"
#include <machine/endian.h>
#include <libkern/OSByteOrder.h>
#ifndef htonll
#define htonll(x) OSSwapHostToBigInt64(x)
#define ntohll(x) OSSwapBigToHostInt64(x)
#endif
static char *curline = NULL;
static long lineno = 0;
static void
warning(char *cptr, const char *msg)
{
warnx("%s on line %ld\n%s", msg, lineno, (curline == NULL ? "" : curline) );
if (cptr) {
char *tptr;
for (tptr = curline; tptr < cptr; ++tptr) putc(' ', stderr);
fprintf(stderr, "^\n");
}
}
static void
error(char *cptr, const char *msg)
{
warning(cptr, msg);
exit(1);
}
static void
corrupt(void) {
error(NULL, "corrupt message catalog");
}
static void
nomem(void) {
error(NULL, "out of memory");
}
static char *
getline(int fd)
{
static size_t curlen = BUFSIZ;
static char buf[BUFSIZ], *bptr = buf, *bend = buf;
char *cptr, *cend;
long buflen;
if (!curline) {
curline = (char *) malloc(curlen);
if (!curline) nomem();
}
++lineno;
cptr = curline;
cend = curline + curlen;
while (TRUE) {
for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
if (*bptr == '\n') {
*cptr = '\0';
++bptr;
return(curline);
} else *cptr = *bptr;
}
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;
}
if (cptr == cend) {
cptr = curline = (char *) realloc(curline, curlen *= 2);
if (!curline) nomem();
cend = curline + curlen;
}
}
}
static char *
token(char *cptr)
{
static char tok[MAXTOKEN+1];
char *tptr = tok;
while (*cptr && isspace((unsigned char)*cptr)) ++cptr;
while (*cptr && !isspace((unsigned char)*cptr)) *tptr++ = *cptr++;
*tptr = '\0';
return(tok);
}
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;
char *tptr;
int needq;
if (quote && *cptr == quote) {
needq = TRUE;
++cptr;
} else needq = FALSE;
clen = strlen(cptr) + 1;
if (clen > msglen) {
if (msglen) msg = (char *) realloc(msg, clen);
else msg = (char *) malloc(clen);
if (!msg) nomem();
msglen = clen;
}
tptr = msg;
while (*cptr) {
if (quote && *cptr == quote) {
char *tmp;
tmp = cptr+1;
if (*tmp && (!isspace((unsigned char)*tmp) || *wskip(tmp))) {
warning(cptr, "unexpected quote character, ignoring");
*tptr++ = *cptr++;
} else {
*cptr = '\0';
}
} else if (*cptr == '\\') {
++cptr;
switch (*cptr) {
case '\0':
cptr = getline(fd);
if (!cptr) error(NULL, "premature end of file");
msglen += strlen(cptr);
i = tptr - msg;
msg = (char *) realloc(msg, msglen);
if (!msg) nomem();
tptr = msg + i;
break;
#define CASEOF(CS, CH) \
case CS: \
*tptr++ = CH; \
++cptr; \
break;
CASEOF('n', '\n')
CASEOF('t', '\t')
CASEOF('v', '\v')
CASEOF('b', '\b')
CASEOF('r', '\r')
CASEOF('f', '\f')
CASEOF('"', '"')
CASEOF('\'', '\'')
CASEOF('\\', '\\')
default:
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;
}
++tptr;
} else {
warning(cptr, "unrecognized escape sequence");
}
}
} else {
*tptr++ = *cptr++;
}
}
*tptr = '\0';
return(msg);
}
static char *
dupstr(const char *ostr)
{
char *nstr;
nstr = strdup(ostr);
if (!nstr) error(NULL, "unable to allocate storage");
return(nstr);
}
typedef struct _msgT {
long msgId;
char *str;
char *hconst;
long offset;
struct _msgT *prev, *next;
} msgT;
typedef struct _setT {
long setId;
char *hconst;
msgT *first, *last;
struct _setT *prev, *next;
} setT;
typedef struct {
setT *first, *last;
} catT;
static setT *curSet;
static catT *cat;
long
MCGetByteOrder(void) {
long l = 0x00010203;
char *cptr = (char *) &l;
if (cptr[0] == 0 && cptr[1] == 1 && cptr[2] == 2 && cptr[3] == 3)
return MC68KByteOrder;
else return MCn86ByteOrder;
}
void
MCParse(int fd)
{
char *cptr, *str;
int setid = NL_SETD, msgid = 0;
char hconst[MAXTOKEN+1];
char quote = 0;
if (!cat) {
cat = (catT *) malloc(sizeof(catT));
if (!cat) nomem();
bzero(cat, sizeof(catT));
}
hconst[0] = '\0';
while ((cptr = getline(fd)) != NULL) {
if (*cptr == '$') {
++cptr;
if (strncmp(cptr, "set", 3) == 0) {
cptr += 3;
cptr = wskip(cptr);
setid = atoi(cptr);
cptr = cskip(cptr);
if (*cptr) cptr = wskip(cptr);
if (*cptr == '#') {
++cptr;
MCAddSet(setid, token(cptr));
} else MCAddSet(setid, NULL);
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)) {
cptr = wskip(cptr);
if (*cptr == '#') {
++cptr;
strcpy(hconst, token(cptr));
}
} else {
if (*cptr) {
cptr = wskip(cptr);
if (*cptr) warning(cptr, "unrecognized line");
}
}
} else {
if (!curSet) MCAddSet(setid, NULL);
if (isdigit((unsigned char)*cptr) || *cptr == '#') {
if (*cptr == '#') {
++msgid;
++cptr;
if (!*cptr) {
MCAddMsg(msgid, "", hconst);
hconst[0] = '\0';
continue;
}
if (!isspace((unsigned char)*cptr)) warning(cptr, "expected a space");
++cptr;
if (!*cptr) {
MCAddMsg(msgid, "", hconst);
hconst[0] = '\0';
continue;
}
} else {
msgid = atoi(cptr);
cptr = cskip(cptr);
if (isspace(*cptr))
cptr++;
}
if (!*cptr) {
if (isspace(cptr[-1])) {
MCAddMsg(msgid, "", hconst);
hconst[0] = '\0';
} else {
MCDelMsg(msgid);
}
} else {
str = getmsg(fd, cptr, quote);
MCAddMsg(msgid, str, hconst);
hconst[0] = '\0';
}
}
}
}
}
void
MCReadCat(int fd)
{
MCHeaderT mcHead;
MCMsgT mcMsg;
MCSetT mcSet;
msgT *msg;
setT *set;
int i;
char *data;
cat = (catT *) malloc(sizeof(catT));
if (!cat) nomem();
bzero(cat, sizeof(catT));
if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead)) corrupt();
if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0) corrupt();
if (ntohl(mcHead.majorVer) != MCMajorVer) error(NULL, "unrecognized catalog version");
if ((ntohl(mcHead.flags) & MC68KByteOrder) == 0) error(NULL, "wrong byte order");
if (lseek(fd, ntohll(mcHead.firstSet), L_SET) == -1) corrupt();
while (TRUE) {
if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet)) corrupt();
if (mcSet.invalid) continue;
set = (setT *) malloc(sizeof(setT));
if (!set) nomem();
bzero(set, sizeof(*set));
if (cat->first) {
cat->last->next = set;
set->prev = cat->last;
cat->last = set;
} else cat->first = cat->last = set;
set->setId = ntohl(mcSet.setId);
if (mcSet.dataLen) {
data = (char *) malloc((size_t)ntohl(mcSet.dataLen));
if (!data) nomem();
if (lseek(fd, ntohll(mcSet.data.off), L_SET) == -1) corrupt();
if (read(fd, data, (size_t)ntohl(mcSet.dataLen)) != ntohl(mcSet.dataLen)) corrupt();
if (lseek(fd, ntohll(mcSet.u.firstMsg), L_SET) == -1) corrupt();
for (i = 0; i < ntohl(mcSet.numMsgs); ++i) {
if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg)) corrupt();
if (mcMsg.invalid) {
--i;
continue;
}
msg = (msgT *) malloc(sizeof(msgT));
if (!msg) nomem();
bzero(msg, sizeof(*msg));
if (set->first) {
set->last->next = msg;
msg->prev = set->last;
set->last = msg;
} else set->first = set->last = msg;
msg->msgId = ntohl(mcMsg.msgId);
msg->str = dupstr((char *) (data + ntohll(mcMsg.msg.off)));
}
free(data);
}
if (!mcSet.nextSet) break;
if (lseek(fd, ntohll(mcSet.nextSet), L_SET) == -1) corrupt();
}
}
static void
printS(int fd, const char *str)
{
if (str)
write(fd, str, strlen(str));
}
static void
printL(int fd, long l)
{
char buf[32];
sprintf(buf, "%ld", l);
write(fd, buf, strlen(buf));
}
static void
printLX(int fd, long l)
{
char buf[32];
sprintf(buf, "%lx", l);
write(fd, buf, strlen(buf));
}
static void
genconst(int fd, int type, char *setConst, char *msgConst, long val)
{
switch (type) {
case MCLangC:
if (!msgConst) {
printS(fd, "\n#define ");
printS(fd, setConst);
printS(fd, "Set");
} else {
printS(fd, "#define ");
printS(fd, setConst);
printS(fd, msgConst);
}
printS(fd, "\t0x");
printLX(fd, val);
printS(fd, "\n");
break;
case MCLangCPlusPlus:
case MCLangANSIC:
if (!msgConst) {
printS(fd, "\nconst long ");
printS(fd, setConst);
printS(fd, "Set");
} else {
printS(fd, "const long ");
printS(fd, setConst);
printS(fd, msgConst);
}
printS(fd, "\t= ");
printL(fd, val);
printS(fd, ";\n");
break;
default:
error(NULL, "not a recognized (programming) language type");
}
}
void
MCWriteConst(int fd, int type, int orConsts)
{
msgT *msg;
setT *set;
long id;
if (orConsts && (type == MCLangC || type == MCLangCPlusPlus || type == MCLangANSIC)) {
printS(fd, "/* Use these Macros to compose and decompose setId's and msgId's */\n");
printS(fd, "#ifndef MCMakeId\n");
printS(fd, "# define MCMakeId(s,m)\t(unsigned long)(((unsigned short)s<<(sizeof(short)*8))\\\n");
printS(fd, "\t\t\t\t\t|(unsigned short)m)\n");
printS(fd, "# define MCSetId(id)\t(unsigned int) (id >> (sizeof(short) * 8))\n");
printS(fd, "# define MCMsgId(id)\t(unsigned int) ((id << (sizeof(short) * 8))\\\n");
printS(fd, "\t\t\t\t\t>> (sizeof(short) * 8))\n");
printS(fd, "#endif\n");
}
for (set = cat->first; set; set = set->next) {
if (set->hconst) genconst(fd, type, set->hconst, NULL, set->setId);
for (msg = set->first; msg; msg = msg->next) {
if (msg->hconst) {
if (orConsts) id = MCMakeId(set->setId, msg->msgId);
else id = msg->msgId;
genconst(fd, type, set->hconst, msg->hconst, id);
free(msg->hconst);
msg->hconst = NULL;
}
}
if (set->hconst) {
free(set->hconst);
set->hconst = NULL;
}
}
}
void
MCWriteCat(int fd)
{
MCHeaderT mcHead;
int cnt;
setT *set;
msgT *msg;
MCSetT mcSet;
MCMsgT mcMsg;
off_t pos;
bcopy(MCMagic, mcHead.magic, MCMagicLen);
mcHead.majorVer = htonl(MCMajorVer);
mcHead.minorVer = htonl(MCMinorVer);
mcHead.flags = htonl(MC68KByteOrder);
mcHead.firstSet = 0;
if (cat == NULL)
error(NULL, "cannot write empty catalog set");
for (cnt = 0, set = cat->first; set; set = set->next) ++cnt;
mcHead.numSets = htonl(cnt);
lseek(fd, (off_t)0L, L_SET);
write(fd, &mcHead, sizeof(mcHead));
mcHead.firstSet = htonll(lseek(fd, (off_t)0L, L_INCR));
lseek(fd, (off_t)0L, L_SET);
write(fd, &mcHead, sizeof(mcHead));
for (set = cat->first; set; set = set->next) {
bzero(&mcSet, sizeof(mcSet));
mcSet.setId = htonl(set->setId);
mcSet.invalid = FALSE;
pos = lseek(fd, (off_t)0L, L_INCR);
write(fd, &mcSet, sizeof(mcSet));
mcSet.data.off = htonll(lseek(fd, (off_t)0L, L_INCR));
cnt = 0;
for (msg = set->first; msg; msg = msg->next) {
msg->offset = lseek(fd, (off_t)0L, L_INCR) - ntohll(mcSet.data.off);
mcSet.dataLen += write(fd, msg->str, strlen(msg->str) + 1);
++cnt;
}
mcSet.u.firstMsg = htonll(lseek(fd, (off_t)0L, L_INCR));
mcSet.numMsgs = htonl(cnt);
mcSet.dataLen = htonl(mcSet.dataLen);
for (msg = set->first; msg; msg = msg->next) {
mcMsg.msgId = htonl(msg->msgId);
mcMsg.msg.off = htonll(msg->offset);
mcMsg.invalid = FALSE;
write(fd, &mcMsg, sizeof(mcMsg));
}
if (set == cat->last) {
mcSet.nextSet = 0;
lseek(fd, pos, L_SET);
write(fd, &mcSet, sizeof(mcSet));
} else {
mcSet.nextSet = htonll(lseek(fd, (off_t)0L, L_INCR));
lseek(fd, pos, L_SET);
write(fd, &mcSet, sizeof(mcSet));
lseek(fd, ntohll(mcSet.nextSet), L_SET);
}
}
}
void
MCAddSet(int setId, char *hconst)
{
setT *set;
if (setId <= 0) {
error(NULL, "setId's must be greater than zero");
return;
}
if (hconst && !*hconst) hconst = NULL;
for (set = cat->first; set; set = set->next) {
if (set->setId == setId) {
if (set->hconst && hconst) free(set->hconst);
set->hconst = NULL;
break;
} else if (set->setId > setId) {
setT *newSet;
newSet = (setT *) malloc(sizeof(setT));
if (!newSet) nomem();
bzero(newSet, sizeof(setT));
newSet->prev = set->prev;
newSet->next = set;
if (set->prev) set->prev->next = newSet;
else cat->first = newSet;
set->prev = newSet;
set = newSet;
break;
}
}
if (!set) {
set = (setT *) malloc(sizeof(setT));
if (!set) nomem();
bzero(set, sizeof(setT));
if (cat->first) {
set->prev = cat->last;
set->next = NULL;
cat->last->next = set;
cat->last = set;
} else {
set->prev = set->next = NULL;
cat->first = cat->last = set;
}
}
set->setId = setId;
if (hconst) set->hconst = dupstr(hconst);
curSet = set;
}
void
MCAddMsg(int msgId, const char *str, char *hconst)
{
msgT *msg;
if (!curSet)
error(NULL, "can't specify a message when no set exists");
if (msgId <= 0) {
error(NULL, "msgId's must be greater than zero");
return;
}
if (hconst && !*hconst) hconst = NULL;
for (msg = curSet->first; msg; msg = msg->next) {
if (msg->msgId == msgId) {
if (msg->hconst && hconst) free(msg->hconst);
if (msg->str) free(msg->str);
msg->hconst = msg->str = NULL;
break;
} else if (msg->msgId > msgId) {
msgT *newMsg;
newMsg = (msgT *) malloc(sizeof(msgT));
if (!newMsg) nomem();
bzero(newMsg, sizeof(msgT));
newMsg->prev = msg->prev;
newMsg->next = msg;
if (msg->prev) msg->prev->next = newMsg;
else curSet->first = newMsg;
msg->prev = newMsg;
msg = newMsg;
break;
}
}
if (!msg) {
msg = (msgT *) malloc(sizeof(msgT));
if (!msg) nomem();
bzero(msg, sizeof(msgT));
if (curSet->first) {
msg->prev = curSet->last;
msg->next = NULL;
curSet->last->next = msg;
curSet->last = msg;
} else {
msg->prev = msg->next = NULL;
curSet->first = curSet->last = msg;
}
}
msg->msgId = msgId;
if (hconst) msg->hconst = dupstr(hconst);
msg->str = dupstr(str);
}
void
MCDelSet(int setId)
{
setT *set;
msgT *msg;
for (set = cat->first; set; set = set->next) {
if (set->setId == setId) {
for (msg = set->first; msg; msg = msg->next) {
if (msg->hconst) free(msg->hconst);
if (msg->str) free(msg->str);
free(msg);
}
if (set->hconst) free(set->hconst);
if (set->prev) set->prev->next = set->next;
else cat->first = set->next;
if (set->next) set->next->prev = set->prev;
else cat->last = set->prev;
free(set);
return;
} else if (set->setId > setId) break;
}
warning(NULL, "specified set doesn't exist");
}
void
MCDelMsg(int msgId)
{
msgT *msg;
if (!curSet)
error(NULL, "you can't delete a message before defining the set");
for (msg = curSet->first; msg; msg = msg->next) {
if (msg->msgId == msgId) {
if (msg->hconst) free(msg->hconst);
if (msg->str) free(msg->str);
if (msg->prev) msg->prev->next = msg->next;
else curSet->first = msg->next;
if (msg->next) msg->next->prev = msg->prev;
else curSet->last = msg->prev;
free(msg);
return;
} else if (msg->msgId > msgId) break;
}
warning(NULL, "specified msg doesn't exist");
}
#if 0
void
MCDumpcat(fp)
FILE *fp;
{
msgT *msg;
setT *set;
if (!cat)
errx(1, "no catalog open");
for (set = cat->first; set; set = set->next) {
fprintf(fp, "$set %ld", set->setId);
if (set->hconst)
fprintf(fp, " # %s", set->hconst);
fprintf(fp, "\n\n");
for (msg = set->first; msg; msg = msg->next) {
if (msg->hconst)
fprintf(fp, "# %s\n", msg->hconst);
fprintf(fp, "%ld\t%s\n", msg->msgId, msg->str);
}
fprintf(fp, "\n");
}
}
#endif