/* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char sccsid[] = "@(#)parse.c 5.4 (Berkeley) 6/1/90"; #endif /* not lint */ #include #include #include #include #include #include "hexdump.h" FU *endfu; /* format at end-of-data */ addfile(name) char *name; { register char *p; FILE *fp; int ch; char buf[2048 + 1]; if (!(fp = fopen(name, "r"))) { (void)fprintf(stderr, "hexdump: can't read %s.\n", name); exit(1); } while (fgets(buf, sizeof(buf), fp)) { if (!(p = index(buf, '\n'))) { (void)fprintf(stderr, "hexdump: line too long.\n"); 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); } add(fmt) char *fmt; { register char *p; static FS **nextfs; FS *tfs; FU *tfu, **nextfu; char savech, *savep, *emalloc(), *strdup(); /* start new linked list of format units */ /* NOSTRICT */ tfs = (FS *)emalloc(sizeof(FS)); 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 */ /* NOSTRICT */ tfu = (FU *)emalloc(sizeof(FU)); *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 */ savech = *p; *p = '\0'; tfu->reps = atoi(savep); tfu->flags = F_SETREP; *p = savech; /* 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); savech = *p; *p = '\0'; tfu->bcnt = atoi(savep); *p = savech; /* skip trailing white space */ for (++p; isspace(*p); ++p); } /* format */ if (*p != '"') badfmt(fmt); for (savep = ++p; *p != '"'; ++p); if (*p != '"') badfmt(fmt); savech = *p; *p = '\0'; if (!(tfu->fmt = strdup(savep))) nomem(); escape(tfu->fmt); *p++ = savech; } } static char *spec = ".#-+ 0123456789"; size(fs) FS *fs; { register FU *fu; register int bcnt, cursize; register char *fmt; 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 (index(spec + 1, *++fmt)); 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; } return(cursize); } rewrite(fs) FS *fs; { enum { NOTOKAY, USEBCNT, USEPREC } sokay; register PR *pr, **nextpr; register FU *fu; register char *p1, *p2; char savech, *fmtp; int nconv, prec; for (fu = fs->nextfu; fu; fu = fu->nextfu) { /* * break each format unit into print units; each * conversion character gets its own. */ for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { /* NOSTRICT */ pr = (PR *)emalloc(sizeof(PR)); if (!fu->nextpr) fu->nextpr = pr; else *nextpr = pr; /* skip preceding text and up to the next % sign */ for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); /* only text in the string */ if (!*p1) { pr->fmt = fmtp; pr->flags = F_TEXT; break; } /* * get precision for %s -- if have a byte count, don't * need it. */ if (fu->bcnt) { sokay = USEBCNT; /* skip to conversion character */ for (++p1; index(spec, *p1); ++p1); } else { /* skip any special chars, field width */ while (index(spec + 1, *++p1)); if (*p1 == '.' && isdigit(*++p1)) { sokay = USEPREC; prec = atoi(p1); while (isdigit(*++p1)); } else sokay = NOTOKAY; } p2 = p1 + 1; /* set end pointer */ /* * figure out the byte count for each conversion; * rewrite the format as necessary, set up blank- * padding for end of data. */ switch(*p1) { 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; goto sw1; 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'; badconv(p1); } /* NOTREACHED */ case 'o': case 'u': case 'x': case 'X': pr->flags = F_UINT; sw1: 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(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': *p1 = p1[2]; break; default: p1[3] = '\0'; badconv(p1); } break; case 'c': pr->flags = F_C; /* *p1 = 'c'; set in conv_c */ goto sw2; case 'p': pr->flags = F_P; *p1 = 'c'; goto sw2; case 'u': pr->flags = F_U; /* *p1 = 'c'; set in conv_u */ sw2: switch(fu->bcnt) { 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); } /* * copy to PR format string, set conversion character * pointer, update original. */ savech = *p2; p1[1] = '\0'; if (!(pr->fmt = strdup(fmtp))) nomem(); *p2 = savech; pr->cchar = pr->fmt + (p1 - fmtp); fmtp = p2; /* only one conversion character if byte count */ if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) { (void)fprintf(stderr, "hexdump: byte count with multiple conversion characters.\n"); exit(1); } } /* * if format unit byte count not specified, figure it out * so can adjust rep count later. */ if (!fu->bcnt) for (pr = fu->nextpr; pr; pr = pr->nextpr) fu->bcnt += pr->bcnt; } /* * if the format string interprets any data at all, and it's * 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. * * if, rep count is greater than 1, no trailing whitespace * 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; } } escape(p1) register char *p1; { register char *p2; /* 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; } } } badcnt(s) char *s; { (void)fprintf(stderr, "hexdump: bad byte count for conversion character %s.\n", s); exit(1); } badsfmt() { (void)fprintf(stderr, "hexdump: %%s requires a precision or a byte count.\n"); exit(1); } badfmt(fmt) char *fmt; { (void)fprintf(stderr, "hexdump: bad format {%s}\n", fmt); exit(1); } badconv(ch) char *ch; { (void)fprintf(stderr, "hexdump: bad conversion character %%%s.\n", ch); exit(1); }