version 1.8, 2001/11/19 19:02:14 |
version 1.9, 2001/12/30 08:17:32 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
|
/* $NetBSD: parse.c,v 1.12 2001/12/07 13:37:39 bjh21 Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 1989 The Regents of the University of California. |
* Copyright (c) 1989, 1993 |
* All rights reserved. |
* The Regents of the University of California. All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/file.h> |
#include <sys/file.h> |
|
|
|
#include <ctype.h> |
|
#include <err.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <ctype.h> |
|
#include <string.h> |
#include <string.h> |
#include <err.h> |
|
#include "hexdump.h" |
#include "hexdump.h" |
|
|
void addfile __P((char *)); |
|
void add __P((char *)); |
|
int size __P((FS *)); |
|
void rewrite __P((FS *)); |
|
void escape __P((char *)); |
|
|
|
FU *endfu; /* format at end-of-data */ |
FU *endfu; /* format at end-of-data */ |
|
|
void |
void |
|
|
{ |
{ |
char *p; |
char *p; |
FILE *fp; |
FILE *fp; |
size_t len; |
int ch; |
|
char buf[2048 + 1]; |
|
|
if (!(fp = fopen(name, "r"))) |
if ((fp = fopen(name, "r")) == NULL) |
err(1, "%s", name); |
err(1, "fopen %s", name); |
while ((p = fgetln(fp, &len))) { |
while (fgets(buf, sizeof(buf), fp)) { |
if (*(p + len - 1) == '\n') |
if (!(p = strchr(buf, '\n'))) { |
*(p + len - 1) = '\0'; |
warnx("line too long."); |
else { |
while ((ch = getchar()) != '\n' && ch != EOF); |
warnx("incomplete line"); |
|
continue; |
continue; |
} |
} |
for (; *p && isspace(*p); ++p); |
*p = '\0'; |
|
for (p = buf; *p && isspace((unsigned char)*p); ++p); |
if (!*p || *p == '#') |
if (!*p || *p == '#') |
continue; |
continue; |
add(p); |
add(p); |
|
|
|
|
void |
void |
add(fmt) |
add(fmt) |
char *fmt; |
const char *fmt; |
{ |
{ |
char *p; |
const char *p; |
static FS **nextfs; |
static FS **nextfs; |
FS *tfs; |
FS *tfs; |
FU *tfu, **nextfu; |
FU *tfu, **nextfu; |
char *savep, *emalloc(); |
const char *savep; |
|
|
/* start new linked list of format units */ |
/* start new linked list of format units */ |
/* NOSTRICT */ |
tfs = emalloc(sizeof(FS)); |
tfs = (FS *)emalloc(sizeof(FS)); |
|
if (!fshead) |
if (!fshead) |
fshead = tfs; |
fshead = tfs; |
else |
else |
|
|
/* take the format string and break it up into format units */ |
/* take the format string and break it up into format units */ |
for (p = fmt;;) { |
for (p = fmt;;) { |
/* skip leading white space */ |
/* skip leading white space */ |
for (; isspace(*p); ++p); |
for (; isspace((unsigned char)*p); ++p); |
if (!*p) |
if (!*p) |
break; |
break; |
|
|
/* allocate a new format unit and link it in */ |
/* allocate a new format unit and link it in */ |
/* NOSTRICT */ |
tfu = emalloc(sizeof(FU)); |
tfu = (FU *)emalloc(sizeof(FU)); |
|
*nextfu = tfu; |
*nextfu = tfu; |
nextfu = &tfu->nextfu; |
nextfu = &tfu->nextfu; |
tfu->reps = 1; |
tfu->reps = 1; |
|
|
/* if leading digit, repetition count */ |
/* if leading digit, repetition count */ |
if (isdigit(*p)) { |
if (isdigit((unsigned char)*p)) { |
for (savep = p; isdigit(*p); ++p); |
for (savep = p; isdigit((unsigned char)*p); ++p); |
if (!isspace(*p) && *p != '/') |
if (!isspace((unsigned char)*p) && *p != '/') |
errx(1, "bad format {%s}", fmt); |
badfmt(fmt); |
/* may overwrite either white space or slash */ |
/* may overwrite either white space or slash */ |
tfu->reps = atoi(savep); |
tfu->reps = atoi(savep); |
tfu->flags = F_SETREP; |
tfu->flags = F_SETREP; |
/* skip trailing white space */ |
/* skip trailing white space */ |
for (++p; isspace(*p); ++p); |
for (++p; isspace((unsigned char)*p); ++p); |
} |
} |
|
|
/* skip slash and trailing white space */ |
/* skip slash and trailing white space */ |
if (*p == '/') |
if (*p == '/') |
while (isspace(*++p)); |
while (isspace((unsigned char)*++p)); |
|
|
/* byte count */ |
/* byte count */ |
if (isdigit(*p)) { |
if (isdigit((unsigned char)*p)) { |
for (savep = p; isdigit(*p); ++p); |
for (savep = p; isdigit((unsigned char)*p); ++p); |
if (!isspace(*p)) |
if (!isspace((unsigned char)*p)) |
errx(1, "bad format {%s}", fmt); |
badfmt(fmt); |
tfu->bcnt = atoi(savep); |
tfu->bcnt = atoi(savep); |
/* skip trailing white space */ |
/* skip trailing white space */ |
for (++p; isspace(*p); ++p); |
for (++p; isspace((unsigned char)*p); ++p); |
} |
} |
|
|
/* format */ |
/* format */ |
if (*p != '"') |
if (*p != '"') |
errx(1, "bad format {%s}", fmt); |
badfmt(fmt); |
for (savep = ++p; *p != '"';) |
for (savep = ++p; *p != '"';) |
if (*p++ == 0) |
if (*p++ == 0) |
errx(1, "bad format {%s}", fmt); |
badfmt(fmt); |
if (!(tfu->fmt = malloc(p - savep + 1))) |
if (!(tfu->fmt = malloc(p - savep + 1))) |
err(1, "malloc"); |
nomem(); |
(void) strncpy(tfu->fmt, savep, p - savep); |
(void) strncpy(tfu->fmt, savep, p - savep); |
tfu->fmt[p - savep] = '\0'; |
tfu->fmt[p - savep] = '\0'; |
escape(tfu->fmt); |
escape(tfu->fmt); |
p++; |
p++; |
} |
} |
/* no single fu in fmt */ |
|
if (tfs->nextfu == NULL) |
|
errx(1, "bad format {%s}", fmt); |
|
} |
} |
|
|
static const char *spec = ".#-+ 0123456789"; |
static const char *spec = ".#-+ 0123456789"; |
|
|
int |
int |
size(fs) |
size(fs) |
FS *fs; |
FS *fs; |
|
|
* case it's a %s format. |
* case it's a %s format. |
*/ |
*/ |
while (strchr(spec + 1, *++fmt)); |
while (strchr(spec + 1, *++fmt)); |
if (*fmt == '.' && isdigit(*++fmt)) { |
if (*fmt == '.' && isdigit((unsigned char)*++fmt)) { |
prec = atoi(fmt); |
prec = atoi(fmt); |
while (isdigit(*++fmt)); |
while (isdigit((unsigned char)*++fmt)); |
} |
} |
switch(*fmt) { |
switch(*fmt) { |
case 'c': |
case 'c': |
|
|
} |
} |
cursize += bcnt * fu->reps; |
cursize += bcnt * fu->reps; |
} |
} |
return(cursize); |
return (cursize); |
} |
} |
|
|
void |
void |
|
|
PR *pr, **nextpr; |
PR *pr, **nextpr; |
FU *fu; |
FU *fu; |
char *p1, *p2; |
char *p1, *p2; |
char savech, *fmtp; |
char savech, *fmtp, cs[3]; |
int nconv, prec; |
int nconv, prec; |
|
|
|
nextpr = NULL; |
|
prec = 0; |
for (fu = fs->nextfu; fu; fu = fu->nextfu) { |
for (fu = fs->nextfu; fu; fu = fu->nextfu) { |
/* |
/* |
* break each format unit into print units; each |
* Break each format unit into print units; each conversion |
* conversion character gets its own. |
* character gets its own. |
*/ |
*/ |
for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { |
for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { |
/* NOSTRICT */ |
pr = emalloc(sizeof(PR)); |
pr = (PR *)emalloc(sizeof(PR)); |
|
if (!fu->nextpr) |
if (!fu->nextpr) |
fu->nextpr = pr; |
fu->nextpr = pr; |
else |
else |
*nextpr = pr; |
*nextpr = pr; |
|
|
/* skip preceding text and up to the next % sign */ |
/* Skip preceding text and up to the next % sign. */ |
for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); |
for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); |
|
|
/* only text in the string */ |
/* Only text in the string. */ |
if (!*p1) { |
if (!*p1) { |
pr->fmt = fmtp; |
pr->fmt = fmtp; |
pr->flags = F_TEXT; |
pr->flags = F_TEXT; |
|
|
} |
} |
|
|
/* |
/* |
* get precision for %s -- if have a byte count, don't |
* Get precision for %s -- if have a byte count, don't |
* need it. |
* need it. |
*/ |
*/ |
if (fu->bcnt) { |
if (fu->bcnt) { |
sokay = USEBCNT; |
sokay = USEBCNT; |
/* skip to conversion character */ |
/* Skip to conversion character. */ |
for (++p1; strchr(spec, *p1); ++p1); |
for (++p1; strchr(spec, *p1); ++p1); |
} else { |
} else { |
/* skip any special chars, field width */ |
/* Skip any special chars, field width. */ |
while (strchr(spec + 1, *++p1)); |
while (strchr(spec + 1, *++p1)); |
if (*p1 == '.' && isdigit(*++p1)) { |
if (*p1 == '.' && |
|
isdigit((unsigned char)*++p1)) { |
sokay = USEPREC; |
sokay = USEPREC; |
prec = atoi(p1); |
prec = atoi(p1); |
while (isdigit(*++p1)); |
while (isdigit((unsigned char)*++p1)) |
} |
continue; |
else |
} else |
sokay = NOTOKAY; |
sokay = NOTOKAY; |
} |
} |
|
|
p2 = p1 + 1; /* set end pointer */ |
p2 = p1 + 1; /* Set end pointer. */ |
|
cs[0] = *p1; /* Set conversion string. */ |
|
cs[1] = '\0'; |
|
|
/* |
/* |
* figure out the byte count for each conversion; |
* Figure out the byte count for each conversion; |
* rewrite the format as necessary, set up blank- |
* rewrite the format as necessary, set up blank- |
* padding for end of data. |
* padding for end of data. |
*/ |
*/ |
switch(*p1) { |
switch(cs[0]) { |
case 'c': |
case 'c': |
pr->flags = F_CHAR; |
pr->flags = F_CHAR; |
switch(fu->bcnt) { |
switch(fu->bcnt) { |
|
|
pr->bcnt = 1; |
pr->bcnt = 1; |
break; |
break; |
default: |
default: |
errx(1, "bad byte count for conversion character \'%c\'", *p1); |
p1[1] = '\0'; |
|
badcnt(p1); |
} |
} |
break; |
break; |
case 'd': case 'i': |
case 'd': case 'i': |
pr->flags = F_INT; |
pr->flags = F_INT; |
goto sw1; |
goto isint; |
case 'l': |
|
++p2; |
|
switch(p1[1]) { |
|
case 'd': case 'i': |
|
++p1; |
|
pr->flags = F_INT; |
|
goto sw1; |
|
case 'o': case 'u': case 'x': case 'X': |
|
++p1; |
|
pr->flags = F_UINT; |
|
goto sw1; |
|
default: |
|
p1[2] = '\0'; |
|
errx(1, "bad conversion character %%%s", p1); |
|
} |
|
/* NOTREACHED */ |
|
case 'o': case 'u': case 'x': case 'X': |
case 'o': case 'u': case 'x': case 'X': |
pr->flags = F_UINT; |
pr->flags = F_UINT; |
sw1: switch(fu->bcnt) { |
isint: cs[2] = '\0'; |
|
cs[1] = cs[0]; |
|
cs[0] = 'q'; |
|
switch(fu->bcnt) { |
case 0: case 4: |
case 0: case 4: |
pr->bcnt = 4; |
pr->bcnt = 4; |
break; |
break; |
|
|
case 2: |
case 2: |
pr->bcnt = 2; |
pr->bcnt = 2; |
break; |
break; |
|
case 8: |
|
pr->bcnt = 8; |
|
break; |
default: |
default: |
errx(1, "bad byte count for conversion character \'%c\'", *p1); |
p1[1] = '\0'; |
|
badcnt(p1); |
} |
} |
break; |
break; |
case 'e': case 'E': case 'f': case 'g': case 'G': |
case 'e': case 'E': case 'f': case 'g': case 'G': |
|
|
pr->bcnt = 4; |
pr->bcnt = 4; |
break; |
break; |
default: |
default: |
errx(1, "bad byte count for conversion character \'%c\'", *p1); |
p1[1] = '\0'; |
|
badcnt(p1); |
} |
} |
break; |
break; |
case 's': |
case 's': |
pr->flags = F_STR; |
pr->flags = F_STR; |
switch(sokay) { |
switch(sokay) { |
case NOTOKAY: |
case NOTOKAY: |
errx(1, "%%s requires a precision or a byte count"); |
badsfmt(); |
case USEBCNT: |
case USEBCNT: |
pr->bcnt = fu->bcnt; |
pr->bcnt = fu->bcnt; |
break; |
break; |
|
|
++p2; |
++p2; |
switch(p1[2]) { |
switch(p1[2]) { |
case 'd': case 'o': case'x': |
case 'd': case 'o': case'x': |
*p1 = 'q'; |
cs[0] = 'q'; |
p1[1] = p1[2]; |
cs[1] = p1[2]; |
|
cs[2] = '\0'; |
break; |
break; |
default: |
default: |
p1[3] = '\0'; |
p1[3] = '\0'; |
errx(1, "bad conversion character %%%s", p1); |
badconv(p1); |
} |
} |
break; |
break; |
case 'c': |
case 'c': |
pr->flags = F_C; |
pr->flags = F_C; |
/* *p1 = 'c'; set in conv_c */ |
/* cs[0] = 'c'; set in conv_c */ |
goto sw2; |
goto isint2; |
case 'p': |
case 'p': |
pr->flags = F_P; |
pr->flags = F_P; |
*p1 = 'c'; |
cs[0] = 'c'; |
goto sw2; |
goto isint2; |
case 'u': |
case 'u': |
pr->flags = F_U; |
pr->flags = F_U; |
/* *p1 = 'c'; set in conv_u */ |
/* cs[0] = 'c'; set in conv_u */ |
sw2: switch(fu->bcnt) { |
isint2: switch(fu->bcnt) { |
case 0: case 1: |
case 0: case 1: |
pr->bcnt = 1; |
pr->bcnt = 1; |
break; |
break; |
default: |
default: |
p1[2] = '\0'; |
p1[2] = '\0'; |
errx(1, "bad byte count for conversion character \'%s\'", p1); |
badcnt(p1); |
} |
} |
break; |
break; |
default: |
default: |
p1[2] = '\0'; |
p1[2] = '\0'; |
errx(1, "bad conversion character %%%s", p1); |
badconv(p1); |
} |
} |
break; |
break; |
default: |
default: |
errx(1, "bad conversion character %%%c", *p1); |
p1[1] = '\0'; |
|
badconv(p1); |
} |
} |
|
|
/* |
/* |
* copy to PR format string, set conversion character |
* Copy to PR format string, set conversion character |
* pointer, update original. |
* pointer, update original. |
*/ |
*/ |
savech = *p2; |
savech = *p2; |
p1[(pr->flags&F_ADDRESS)?2:1] = '\0'; |
p1[0] = '\0'; |
if (!(pr->fmt = strdup(fmtp))) |
pr->fmt = emalloc(strlen(fmtp) + strlen(cs) + 1); |
err(1, "malloc"); |
(void)strcpy(pr->fmt, fmtp); |
|
(void)strcat(pr->fmt, cs); |
*p2 = savech; |
*p2 = savech; |
pr->cchar = pr->fmt + (p1 - fmtp); |
pr->cchar = pr->fmt + (p1 - fmtp); |
fmtp = p2; |
fmtp = p2; |
|
|
/* only one conversion character if byte count */ |
/* Only one conversion character if byte count. */ |
if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) |
if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) |
errx(1, |
errx(1, |
"byte count with multiple conversion characters"); |
"byte count with multiple conversion characters"); |
} |
} |
/* |
/* |
* if format unit byte count not specified, figure it out |
* If format unit byte count not specified, figure it out |
* so can adjust rep count later. |
* so can adjust rep count later. |
*/ |
*/ |
if (!fu->bcnt) |
if (!fu->bcnt) |
|
|
fu->bcnt += pr->bcnt; |
fu->bcnt += pr->bcnt; |
} |
} |
/* |
/* |
* if the format string interprets any data at all, and it's |
* If the format string interprets any data at all, and it's |
* not the same as the blocksize, and its last format unit |
* not the same as the blocksize, and its last format unit |
* interprets any data at all, and has no iteration count, |
* interprets any data at all, and has no iteration count, |
* repeat it as necessary. |
* repeat it as necessary. |
* |
* |
* if, rep count is greater than 1, no trailing whitespace |
* If, rep count is greater than 1, no trailing whitespace |
* gets output from the last iteration of the format unit. |
* gets output from the last iteration of the format unit. |
*/ |
*/ |
for (fu = fs->nextfu;; fu = fu->nextfu) { |
for (fu = fs->nextfu; fu; fu = fu->nextfu) { |
if (!fu->nextfu && fs->bcnt < blocksize && |
if (!fu->nextfu && fs->bcnt < blocksize && |
!(fu->flags&F_SETREP) && fu->bcnt) |
!(fu->flags&F_SETREP) && fu->bcnt) |
fu->reps += (blocksize - fs->bcnt) / fu->bcnt; |
fu->reps += (blocksize - fs->bcnt) / fu->bcnt; |
|
|
if (!pr->nextpr) |
if (!pr->nextpr) |
break; |
break; |
for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) |
for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) |
p2 = isspace(*p1) ? p1 : NULL; |
p2 = isspace((unsigned char)*p1) ? p1 : NULL; |
if (p2) |
if (p2) |
pr->nospace = p2; |
pr->nospace = p2; |
} |
} |
if (!fu->nextfu) |
|
break; |
|
} |
} |
|
#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 |
void |
|
|
break; |
break; |
} |
} |
} |
} |
|
} |
|
|
|
void |
|
badcnt(s) |
|
char *s; |
|
{ |
|
errx(1, "%s: bad byte count", s); |
|
} |
|
|
|
void |
|
badsfmt() |
|
{ |
|
errx(1, "%%s: requires a precision or a byte count\n"); |
|
} |
|
|
|
void |
|
badfmt(fmt) |
|
const char *fmt; |
|
{ |
|
errx(1, "\"%s\": bad format\n", fmt); |
|
} |
|
|
|
void |
|
badconv(ch) |
|
char *ch; |
|
{ |
|
errx(1, "%%%s: bad conversion character\n", ch); |
} |
} |