NetBSD/usr.bin/hexdump/parse.c

521 lines
11 KiB
C
Raw Normal View History

/* $NetBSD: parse.c,v 1.7 1997/10/19 02:34:10 lukem Exp $ */
1997-01-09 23:18:21 +03:00
1993-03-21 12:45:37 +03:00
/*
1997-10-18 17:53:56 +04:00
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
1993-03-21 12:45:37 +03:00
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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>
1993-03-21 12:45:37 +03:00
#ifndef lint
#if 0
1997-10-18 17:53:56 +04:00
static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
#else
__RCSID("$NetBSD: parse.c,v 1.7 1997/10/19 02:34:10 lukem Exp $");
#endif
1993-03-21 12:45:37 +03:00
#endif /* not lint */
#include <sys/types.h>
1993-08-01 21:54:45 +04:00
#include <sys/file.h>
1997-10-18 17:53:56 +04:00
#include <ctype.h>
#include <err.h>
1997-10-18 17:53:56 +04:00
#include <errno.h>
#include <fcntl.h>
1993-03-21 12:45:37 +03:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
1997-10-18 17:53:56 +04:00
1993-03-21 12:45:37 +03:00
#include "hexdump.h"
FU *endfu; /* format at end-of-data */
void
1993-03-21 12:45:37 +03:00
addfile(name)
char *name;
{
char *p;
1993-03-21 12:45:37 +03:00
FILE *fp;
int ch;
char buf[2048 + 1];
1997-10-18 17:53:56 +04:00
if ((fp = fopen(name, "r")) == NULL)
err(1, "fopen %s", name);
1993-03-21 12:45:37 +03:00
while (fgets(buf, sizeof(buf), fp)) {
if (!(p = strchr(buf, '\n'))) {
warnx("line too long.");
1993-03-21 12:45:37 +03:00
while ((ch = getchar()) != '\n' && ch != EOF);
continue;
}
*p = '\0';
for (p = buf; *p && isspace(*p); ++p);
if (!*p || *p == '#')
continue;
add(p);
}
(void)fclose(fp);
}
void
1993-03-21 12:45:37 +03:00
add(fmt)
char *fmt;
{
char *p;
1993-03-21 12:45:37 +03:00
static FS **nextfs;
FS *tfs;
FU *tfu, **nextfu;
char *savep;
1993-03-21 12:45:37 +03:00
/* start new linked list of format units */
1997-10-18 17:53:56 +04:00
tfs = emalloc(sizeof(FS));
1993-03-21 12:45:37 +03:00
if (!fshead)
fshead = tfs;
else
*nextfs = tfs;
nextfs = &tfs->nextfs;
nextfu = &tfs->nextfu;
/* take the format string and break it up into format units */
for (p = fmt;;) {
/* skip leading white space */
for (; isspace(*p); ++p);
if (!*p)
break;
/* allocate a new format unit and link it in */
1997-10-18 17:53:56 +04:00
tfu = emalloc(sizeof(FU));
1993-03-21 12:45:37 +03:00
*nextfu = tfu;
nextfu = &tfu->nextfu;
tfu->reps = 1;
/* if leading digit, repetition count */
if (isdigit(*p)) {
for (savep = p; isdigit(*p); ++p);
if (!isspace(*p) && *p != '/')
badfmt(fmt);
/* may overwrite either white space or slash */
tfu->reps = atoi(savep);
tfu->flags = F_SETREP;
/* skip trailing white space */
for (++p; isspace(*p); ++p);
}
/* skip slash and trailing white space */
if (*p == '/')
while (isspace(*++p));
/* byte count */
if (isdigit(*p)) {
for (savep = p; isdigit(*p); ++p);
if (!isspace(*p))
badfmt(fmt);
tfu->bcnt = atoi(savep);
/* skip trailing white space */
for (++p; isspace(*p); ++p);
}
/* format */
if (*p != '"')
badfmt(fmt);
for (savep = ++p; *p != '"';)
if (*p++ == 0)
badfmt(fmt);
if (!(tfu->fmt = malloc(p - savep + 1)))
nomem();
(void) strncpy(tfu->fmt, savep, p - savep);
tfu->fmt[p - savep] = '\0';
escape(tfu->fmt);
p++;
}
}
static const char *spec = ".#-+ 0123456789";
int
1993-03-21 12:45:37 +03:00
size(fs)
FS *fs;
{
FU *fu;
int bcnt, cursize;
char *fmt;
1993-03-21 12:45:37 +03:00
int prec;
/* figure out the data block size needed for each format unit */
for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
if (fu->bcnt) {
cursize += fu->bcnt * fu->reps;
continue;
}
for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
if (*fmt != '%')
continue;
/*
* skip any special chars -- save precision in
* case it's a %s format.
*/
while (strchr(spec + 1, *++fmt));
1993-03-21 12:45:37 +03:00
if (*fmt == '.' && isdigit(*++fmt)) {
prec = atoi(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;
}
1997-10-18 17:53:56 +04:00
return (cursize);
1993-03-21 12:45:37 +03:00
}
void
1993-03-21 12:45:37 +03:00
rewrite(fs)
FS *fs;
{
enum { NOTOKAY, USEBCNT, USEPREC } sokay;
PR *pr, **nextpr;
FU *fu;
char *p1, *p2;
1997-10-18 17:53:56 +04:00
char savech, *fmtp, cs[3];
1993-03-21 12:45:37 +03:00
int nconv, prec;
nextpr = NULL;
prec = 0;
1993-03-21 12:45:37 +03:00
for (fu = fs->nextfu; fu; fu = fu->nextfu) {
/*
1997-10-18 17:53:56 +04:00
* Break each format unit into print units; each conversion
* character gets its own.
1993-03-21 12:45:37 +03:00
*/
for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
1997-10-18 17:53:56 +04:00
pr = emalloc(sizeof(PR));
1993-03-21 12:45:37 +03:00
if (!fu->nextpr)
fu->nextpr = pr;
else
*nextpr = pr;
1997-10-18 17:53:56 +04:00
/* Skip preceding text and up to the next % sign. */
1993-03-21 12:45:37 +03:00
for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
1997-10-18 17:53:56 +04:00
/* Only text in the string. */
1993-03-21 12:45:37 +03:00
if (!*p1) {
pr->fmt = fmtp;
pr->flags = F_TEXT;
break;
}
/*
1997-10-18 17:53:56 +04:00
* Get precision for %s -- if have a byte count, don't
1993-03-21 12:45:37 +03:00
* need it.
*/
if (fu->bcnt) {
sokay = USEBCNT;
1997-10-18 17:53:56 +04:00
/* Skip to conversion character. */
for (++p1; strchr(spec, *p1); ++p1);
1993-03-21 12:45:37 +03:00
} else {
1997-10-18 17:53:56 +04:00
/* Skip any special chars, field width. */
while (strchr(spec + 1, *++p1));
1993-03-21 12:45:37 +03:00
if (*p1 == '.' && isdigit(*++p1)) {
sokay = USEPREC;
prec = atoi(p1);
while (isdigit(*++p1));
1997-10-18 17:53:56 +04:00
} else
1993-03-21 12:45:37 +03:00
sokay = NOTOKAY;
}
1997-10-18 17:53:56 +04:00
p2 = p1 + 1; /* Set end pointer. */
cs[0] = *p1; /* Set conversion string. */
cs[1] = '\0';
1993-03-21 12:45:37 +03:00
/*
1997-10-18 17:53:56 +04:00
* Figure out the byte count for each conversion;
1993-03-21 12:45:37 +03:00
* rewrite the format as necessary, set up blank-
* padding for end of data.
*/
1997-10-18 17:53:56 +04:00
switch(cs[0]) {
1993-03-21 12:45:37 +03:00
case 'c':
pr->flags = F_CHAR;
switch(fu->bcnt) {
case 0: case 1:
pr->bcnt = 1;
break;
default:
p1[1] = '\0';
badcnt(p1);
}
break;
case 'd': case 'i':
pr->flags = F_INT;
1997-10-18 17:53:56 +04:00
goto isint;
1993-03-21 12:45:37 +03:00
case 'o': case 'u': case 'x': case 'X':
pr->flags = F_UINT;
1997-10-18 17:53:56 +04:00
isint: cs[2] = '\0';
cs[1] = cs[0];
cs[0] = 'q';
switch(fu->bcnt) {
1993-03-21 12:45:37 +03:00
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(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:
p1[1] = '\0';
badcnt(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;
/* FALLTHROUGH */
case 'a':
pr->flags = F_ADDRESS;
++p2;
switch(p1[2]) {
case 'd': case 'o': case'x':
1997-10-18 17:53:56 +04:00
cs[0] = 'q';
cs[1] = p1[2];
cs[2] = '\0';
1993-03-21 12:45:37 +03:00
break;
default:
p1[3] = '\0';
badconv(p1);
}
break;
case 'c':
pr->flags = F_C;
1997-10-18 17:53:56 +04:00
/* cs[0] = 'c'; set in conv_c */
goto isint2;
1993-03-21 12:45:37 +03:00
case 'p':
pr->flags = F_P;
1997-10-18 17:53:56 +04:00
cs[0] = 'c';
goto isint2;
1993-03-21 12:45:37 +03:00
case 'u':
pr->flags = F_U;
1997-10-18 17:53:56 +04:00
/* cs[0] = 'c'; set in conv_u */
isint2: switch(fu->bcnt) {
1993-03-21 12:45:37 +03:00
case 0: case 1:
pr->bcnt = 1;
break;
default:
p1[2] = '\0';
badcnt(p1);
}
break;
default:
p1[2] = '\0';
badconv(p1);
}
break;
default:
p1[1] = '\0';
badconv(p1);
}
/*
1997-10-18 17:53:56 +04:00
* Copy to PR format string, set conversion character
1993-03-21 12:45:37 +03:00
* pointer, update original.
*/
savech = *p2;
1997-10-18 17:53:56 +04:00
p1[0] = '\0';
pr->fmt = emalloc(strlen(fmtp) + 2);
(void)strcpy(pr->fmt, fmtp);
(void)strcat(pr->fmt, cs);
1993-03-21 12:45:37 +03:00
*p2 = savech;
pr->cchar = pr->fmt + (p1 - fmtp);
fmtp = p2;
1997-10-18 17:53:56 +04:00
/* Only one conversion character if byte count. */
if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
errx(1,
"byte count with multiple conversion characters");
1993-03-21 12:45:37 +03:00
}
/*
1997-10-18 17:53:56 +04:00
* If format unit byte count not specified, figure it out
1993-03-21 12:45:37 +03:00
* so can adjust rep count later.
*/
if (!fu->bcnt)
for (pr = fu->nextpr; pr; pr = pr->nextpr)
fu->bcnt += pr->bcnt;
}
/*
1997-10-18 17:53:56 +04:00
* If the format string interprets any data at all, and it's
1993-03-21 12:45:37 +03:00
* not the same as the blocksize, and its last format unit
* interprets any data at all, and has no iteration count,
* repeat it as necessary.
*
1997-10-18 17:53:56 +04:00
* If, rep count is greater than 1, no trailing whitespace
1993-03-21 12:45:37 +03:00
* gets output from the last iteration of the format unit.
*/
for (fu = fs->nextfu;; 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 = pr->fmt, p2 = NULL; *p1; ++p1)
p2 = isspace(*p1) ? p1 : NULL;
if (p2)
pr->nospace = p2;
}
if (!fu->nextfu)
break;
}
1997-10-18 17:53:56 +04:00
#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
1993-03-21 12:45:37 +03:00
}
void
1993-03-21 12:45:37 +03:00
escape(p1)
char *p1;
1993-03-21 12:45:37 +03:00
{
char *p2;
1993-03-21 12:45:37 +03:00
/* alphabetic escape sequences have to be done in place */
for (p2 = p1;; ++p1, ++p2) {
if (!*p1) {
*p2 = *p1;
break;
}
if (*p1 == '\\')
switch(*++p1) {
case 'a':
/* *p2 = '\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
1993-03-21 12:45:37 +03:00
badcnt(s)
char *s;
{
errx(1, "%s: bad byte count", s);
1993-03-21 12:45:37 +03:00
}
void
1993-03-21 12:45:37 +03:00
badsfmt()
{
errx(1, "%%s: requires a precision or a byte count\n");
1993-03-21 12:45:37 +03:00
}
void
1993-03-21 12:45:37 +03:00
badfmt(fmt)
char *fmt;
{
errx(1, "\"%s\": bad format\n", fmt);
1993-03-21 12:45:37 +03:00
}
void
1993-03-21 12:45:37 +03:00
badconv(ch)
char *ch;
{
errx(1, "%%%s: bad conversion character\n", ch);
1993-03-21 12:45:37 +03:00
}