#ifndef lint
#if 0
static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
#endif
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/usr.bin/hexdump/parse.c,v 1.12 2002/09/04 23:29:01 dwmalone Exp $");
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "hexdump.h"
FU *endfu;
void
addfile(char *name)
{
unsigned char *p;
FILE *fp;
int ch;
char buf[2048 + 1];
if ((fp = fopen(name, "r")) == NULL)
err(1, "%s", name);
while (fgets(buf, sizeof(buf), fp)) {
if (!(p = (unsigned char *)index(buf, '\n'))) {
warnx("line too long");
while ((ch = getchar()) != '\n' && ch != EOF);
continue;
}
*p = '\0';
for (p = (unsigned char *)buf; *p && isspace(*p); ++p);
if (!*p || *p == '#')
continue;
add((const char *)p);
}
(void)fclose(fp);
}
void
add(const char *fmt)
{
unsigned const char *p, *savep;
static FS **nextfs;
FS *tfs;
FU *tfu, **nextfu;
if ((tfs = calloc(1, sizeof(FS))) == NULL)
err(1, NULL);
if (!fshead)
fshead = tfs;
else
*nextfs = tfs;
nextfs = &tfs->nextfs;
nextfu = &tfs->nextfu;
for (p = (unsigned const char *)fmt;;) {
for (; isspace(*p); ++p);
if (!*p)
break;
if ((tfu = calloc(1, sizeof(FU))) == NULL)
err(1, NULL);
*nextfu = tfu;
nextfu = &tfu->nextfu;
tfu->reps = 1;
if (isdigit(*p)) {
for (savep = p; isdigit(*p); ++p);
if (!isspace(*p) && *p != '/')
badfmt(fmt);
tfu->reps = atoi((const char *)savep);
tfu->flags = F_SETREP;
for (++p; isspace(*p); ++p);
}
if (*p == '/')
while (isspace(*++p));
if (isdigit(*p)) {
for (savep = p; isdigit(*p); ++p);
if (!isspace(*p))
badfmt(fmt);
tfu->bcnt = atoi((const char *)savep);
for (++p; isspace(*p); ++p);
}
if (*p != '"')
badfmt(fmt);
for (savep = ++p; *p != '"';)
if (*p++ == 0)
badfmt(fmt);
if (!(tfu->fmt = malloc(p - savep + 1)))
err(1, NULL);
(void) strncpy(tfu->fmt, (const char *)savep, p - savep);
tfu->fmt[p - savep] = '\0';
escape(tfu->fmt);
p++;
}
}
static const char *spec = ".#-+ 0123456789";
int
size(FS *fs)
{
FU *fu;
int bcnt, cursize;
unsigned char *fmt;
int prec;
for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
if (fu->bcnt) {
cursize += fu->bcnt * fu->reps;
continue;
}
for (bcnt = prec = 0, fmt = (unsigned char *)fu->fmt; *fmt; ++fmt) {
if (*fmt != '%')
continue;
while (index(spec + 1, *++fmt));
if (*fmt == '.' && isdigit(*++fmt)) {
prec = atoi((const char *)fmt);
while (isdigit(*++fmt));
}
switch(*fmt) {
case 'c':
bcnt += 1;
break;
case 'd': case 'i': case 'o': case 'u':
case 'x': case 'X':
bcnt += 4;
break;
case 'e': case 'E': case 'f': case 'g': case 'G':
bcnt += 8;
break;
case 's':
bcnt += prec;
break;
case '_':
switch(*++fmt) {
case 'c': case 'p': case 'u':
bcnt += 1;
break;
}
}
}
cursize += bcnt * fu->reps;
}
return (cursize);
}
void
rewrite(FS *fs)
{
enum { NOTOKAY, USEBCNT, USEPREC } sokay;
PR *pr, **nextpr = NULL;
FU *fu;
unsigned char *p1, *p2, *fmtp;
char savech, cs[3];
int nconv, prec = 0;
for (fu = fs->nextfu; fu; fu = fu->nextfu) {
for (nconv = 0, fmtp = (unsigned char *)fu->fmt; *fmtp; nextpr = &pr->nextpr) {
if ((pr = calloc(1, sizeof(PR))) == NULL)
err(1, NULL);
if (!fu->nextpr)
fu->nextpr = pr;
else
*nextpr = pr;
for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
if (!*p1) {
pr->fmt = (char *)fmtp;
pr->flags = F_TEXT;
break;
}
if (fu->bcnt) {
sokay = USEBCNT;
for (++p1; index(spec, *p1); ++p1);
} else {
while (index(spec + 1, *++p1));
if (*p1 == '.' && isdigit(*++p1)) {
sokay = USEPREC;
prec = atoi((const char *)p1);
while (isdigit(*++p1));
} else
sokay = NOTOKAY;
}
p2 = p1 + 1;
cs[0] = *p1;
cs[1] = '\0';
switch(cs[0]) {
case 'c':
pr->flags = F_CHAR;
switch(fu->bcnt) {
case 0: case 1:
pr->bcnt = 1;
break;
default:
p1[1] = '\0';
badcnt((char *)p1);
}
break;
case 'd': case 'i':
pr->flags = F_INT;
goto isint;
case 'o': case 'u': case 'x': case 'X':
pr->flags = F_UINT;
isint: cs[2] = '\0';
cs[1] = cs[0];
cs[0] = 'q';
switch(fu->bcnt) {
case 0: case 4:
pr->bcnt = 4;
break;
case 1:
pr->bcnt = 1;
break;
case 2:
pr->bcnt = 2;
break;
default:
p1[1] = '\0';
badcnt((char *)p1);
}
break;
case 'e': case 'E': case 'f': case 'g': case 'G':
pr->flags = F_DBL;
switch(fu->bcnt) {
case 0: case 8:
pr->bcnt = 8;
break;
case 4:
pr->bcnt = 4;
break;
default:
if (fu->bcnt == sizeof(long double)) {
cs[2] = '\0';
cs[1] = cs[0];
cs[0] = 'L';
pr->bcnt = sizeof(long double);
} else {
p1[1] = '\0';
badcnt((char *)p1);
}
}
break;
case 's':
pr->flags = F_STR;
switch(sokay) {
case NOTOKAY:
badsfmt();
case USEBCNT:
pr->bcnt = fu->bcnt;
break;
case USEPREC:
pr->bcnt = prec;
break;
}
break;
case '_':
++p2;
switch(p1[1]) {
case 'A':
endfu = fu;
fu->flags |= F_IGNORE;
case 'a':
pr->flags = F_ADDRESS;
++p2;
switch(p1[2]) {
case 'd': case 'o': case'x':
cs[0] = 'q';
cs[1] = p1[2];
cs[2] = '\0';
break;
default:
p1[3] = '\0';
badconv((char *)p1);
}
break;
case 'c':
pr->flags = F_C;
goto isint2;
case 'p':
pr->flags = F_P;
cs[0] = 'c';
goto isint2;
case 'u':
pr->flags = F_U;
isint2: switch(fu->bcnt) {
case 0: case 1:
pr->bcnt = 1;
break;
default:
p1[2] = '\0';
badcnt((char *)p1);
}
break;
case 'n':
endfu = fu;
fu->flags = F_IGNORE;
pr->flags = F_TEXT;
fmtp = (unsigned char *)"\n";
cs[0] = '\0';
break;
default:
p1[2] = '\0';
badconv((char *)p1);
}
break;
default:
p1[1] = '\0';
badconv((char *)p1);
}
savech = *p2;
p1[0] = '\0';
if ((pr->fmt = calloc(1, strlen((const char *)fmtp) + 2)) == NULL)
err(1, NULL);
(void)strcpy(pr->fmt, (const char *)fmtp);
(void)strcat(pr->fmt, cs);
*p2 = savech;
pr->cchar = pr->fmt + (p1 - fmtp);
fmtp = p2;
if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
errx(1, "byte count with multiple conversion characters");
}
if (!fu->bcnt)
for (pr = fu->nextpr; pr; pr = pr->nextpr)
fu->bcnt += pr->bcnt;
}
for (fu = fs->nextfu; fu; fu = fu->nextfu) {
if (!fu->nextfu && fs->bcnt < blocksize &&
!(fu->flags&F_SETREP) && fu->bcnt)
fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
if (fu->reps > 1) {
for (pr = fu->nextpr;; pr = pr->nextpr)
if (!pr->nextpr)
break;
for (p1 = (unsigned char *)pr->fmt, p2 = NULL; *p1; ++p1)
p2 = isspace(*p1) ? p1 : NULL;
if (p2)
pr->nospace = (char *)p2;
}
}
#ifdef DEBUG
for (fu = fs->nextfu; fu; fu = fu->nextfu) {
(void)printf("fmt:");
for (pr = fu->nextpr; pr; pr = pr->nextpr)
(void)printf(" {%s}", pr->fmt);
(void)printf("\n");
}
#endif
}
void
escape(char *p1)
{
char *p2;
for (p2 = p1;; ++p1, ++p2) {
if (!*p1) {
*p2 = *p1;
break;
}
if (*p1 == '\\')
switch(*++p1) {
case 'a':
*p2 = '\007';
break;
case 'b':
*p2 = '\b';
break;
case 'f':
*p2 = '\f';
break;
case 'n':
*p2 = '\n';
break;
case 'r':
*p2 = '\r';
break;
case 't':
*p2 = '\t';
break;
case 'v':
*p2 = '\v';
break;
default:
*p2 = *p1;
break;
}
}
}
void
badcnt(char *s)
{
errx(1, "%s: bad byte count", s);
}
void
badsfmt(void)
{
errx(1, "%%s: requires a precision or a byte count");
}
void
badfmt(const char *fmt)
{
errx(1, "\"%s\": bad format", fmt);
}
void
badconv(char *ch)
{
errx(1, "%%%s: bad conversion character", ch);
}