diff --git a/usr.bin/hexdump/display.c b/usr.bin/hexdump/display.c index 5e5b30b73fd0..cad548e1ec41 100644 --- a/usr.bin/hexdump/display.c +++ b/usr.bin/hexdump/display.c @@ -1,441 +1,441 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hexdump.h" enum _vflag vflag = FIRST; static off_t address; /* address/offset in stream */ static off_t eaddress; /* end address */ static void print(PR *, u_char *); static void noseek(void); void display(void) { FS *fs; FU *fu; PR *pr; int cnt; u_char *bp; off_t saveaddress; u_char savech, *savebp; savech = 0; while ((bp = get())) for (fs = fshead, savebp = bp, saveaddress = address; fs; fs = fs->nextfs, bp = savebp, address = saveaddress) for (fu = fs->nextfu; fu; fu = fu->nextfu) { if (fu->flags&F_IGNORE) break; for (cnt = fu->reps; cnt; --cnt) for (pr = fu->nextpr; pr; address += pr->bcnt, bp += pr->bcnt, pr = pr->nextpr) { if (eaddress && address >= eaddress && !(pr->flags & (F_TEXT|F_BPAD))) bpad(pr); if (cnt == 1 && pr->nospace) { savech = *pr->nospace; *pr->nospace = '\0'; } print(pr, bp); if (cnt == 1 && pr->nospace) *pr->nospace = savech; } } if (endfu) { /* * If eaddress not set, error or file size was multiple of * blocksize, and no partial block ever found. */ if (!eaddress) { if (!address) return; eaddress = address; } for (pr = endfu->nextpr; pr; pr = pr->nextpr) switch(pr->flags) { case F_ADDRESS: (void)printf(pr->fmt, (quad_t)eaddress); break; case F_TEXT: (void)printf("%s", pr->fmt); break; } } } static void print(PR *pr, u_char *bp) { long double ldbl; double f8; float f4; int16_t s2; - int8_t s8; int32_t s4; + int64_t s8; u_int16_t u2; u_int32_t u4; u_int64_t u8; switch(pr->flags) { case F_ADDRESS: (void)printf(pr->fmt, (quad_t)address); break; case F_BPAD: (void)printf(pr->fmt, ""); break; case F_C: conv_c(pr, bp, eaddress ? eaddress - address : blocksize - address % blocksize); break; case F_CHAR: (void)printf(pr->fmt, *bp); break; case F_DBL: switch(pr->bcnt) { case 4: bcopy(bp, &f4, sizeof(f4)); (void)printf(pr->fmt, f4); break; case 8: bcopy(bp, &f8, sizeof(f8)); (void)printf(pr->fmt, f8); break; default: if (pr->bcnt == sizeof(long double)) { bcopy(bp, &ldbl, sizeof(ldbl)); (void)printf(pr->fmt, ldbl); } break; } break; case F_INT: switch(pr->bcnt) { case 1: (void)printf(pr->fmt, (quad_t)(signed char)*bp); break; case 2: bcopy(bp, &s2, sizeof(s2)); (void)printf(pr->fmt, (quad_t)s2); break; case 4: bcopy(bp, &s4, sizeof(s4)); (void)printf(pr->fmt, (quad_t)s4); break; case 8: bcopy(bp, &s8, sizeof(s8)); (void)printf(pr->fmt, s8); break; } break; case F_P: (void)printf(pr->fmt, isprint(*bp) ? *bp : '.'); break; case F_STR: (void)printf(pr->fmt, (char *)bp); break; case F_TEXT: (void)printf("%s", pr->fmt); break; case F_U: conv_u(pr, bp); break; case F_UINT: switch(pr->bcnt) { case 1: (void)printf(pr->fmt, (u_quad_t)*bp); break; case 2: bcopy(bp, &u2, sizeof(u2)); (void)printf(pr->fmt, (u_quad_t)u2); break; case 4: bcopy(bp, &u4, sizeof(u4)); (void)printf(pr->fmt, (u_quad_t)u4); break; case 8: bcopy(bp, &u8, sizeof(u8)); (void)printf(pr->fmt, u8); break; } break; } } void bpad(PR *pr) { static char const *spec = " -0+#"; char *p1, *p2; /* * Remove all conversion flags; '-' is the only one valid * with %s, and it's not useful here. */ pr->flags = F_BPAD; pr->cchar[0] = 's'; pr->cchar[1] = '\0'; for (p1 = pr->fmt; *p1 != '%'; ++p1); for (p2 = ++p1; *p1 && strchr(spec, *p1); ++p1); while ((*p2++ = *p1++)); } static char **_argv; u_char * get(void) { static int ateof = 1; static u_char *curp, *savp; int n; int need, nread; int valid_save = 0; u_char *tmpp; if (!curp) { if ((curp = calloc(1, blocksize)) == NULL) err(1, NULL); if ((savp = calloc(1, blocksize)) == NULL) err(1, NULL); } else { tmpp = curp; curp = savp; savp = tmpp; address += blocksize; valid_save = 1; } for (need = blocksize, nread = 0;;) { /* * if read the right number of bytes, or at EOF for one file, * and no other files are available, zero-pad the rest of the * block and set the end flag. */ if (!length || (ateof && !next((char **)NULL))) { if (odmode && address < skip) errx(1, "cannot skip past end of input"); if (need == blocksize) return((u_char *)NULL); /* * XXX bcmp() is not quite right in the presence * of multibyte characters. */ if (vflag != ALL && valid_save && bcmp(curp, savp, nread) == 0) { if (vflag != DUP) { (void)printf("*\n"); (void)fflush(stdout); } return((u_char *)NULL); } bzero((char *)curp + nread, need); eaddress = address + nread; return(curp); } n = fread((char *)curp + nread, sizeof(u_char), length == -1 ? need : MIN(length, need), stdin); if (!n) { if (ferror(stdin)) warn("%s", _argv[-1]); ateof = 1; continue; } ateof = 0; if (length != -1) length -= n; if (!(need -= n)) { /* * XXX bcmp() is not quite right in the presence * of multibyte characters. */ if (vflag == ALL || vflag == FIRST || valid_save == 0 || bcmp(curp, savp, blocksize) != 0) { if (vflag == DUP || vflag == FIRST) vflag = WAIT; return(curp); } if (vflag == WAIT) { (void)printf("*\n"); (void)fflush(stdout); } vflag = DUP; address += blocksize; need = blocksize; nread = 0; } else nread += n; } } size_t peek(u_char *buf, size_t nbytes) { size_t n, nread; int c; if (length != -1 && nbytes > (unsigned int)length) nbytes = length; nread = 0; while (nread < nbytes && (c = getchar()) != EOF) { *buf++ = c; nread++; } n = nread; while (n-- > 0) { c = *--buf; ungetc(c, stdin); } return (nread); } int next(char **argv) { static int done; int statok; if (argv) { _argv = argv; return(1); } for (;;) { if (*_argv) { done = 1; if (!(freopen(*_argv, "r", stdin))) { warn("%s", *_argv); exitval = 1; ++_argv; continue; } statok = 1; } else { if (done++) return(0); statok = 0; } if (caph_limit_stream(fileno(stdin), CAPH_READ) < 0) err(1, "unable to restrict %s", statok ? *_argv : "stdin"); /* * We've opened our last input file; enter capsicum sandbox. */ if (statok == 0 || *(_argv + 1) == NULL) { if (caph_enter() < 0) err(1, "unable to enter capability mode"); } if (skip) doskip(statok ? *_argv : "stdin", statok); if (*_argv) ++_argv; if (!skip) return(1); } /* NOTREACHED */ } void doskip(const char *fname, int statok) { int type; struct stat sb; if (statok) { if (fstat(fileno(stdin), &sb)) err(1, "%s", fname); if (S_ISREG(sb.st_mode) && skip > sb.st_size) { address += sb.st_size; skip -= sb.st_size; return; } } if (!statok || S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode)) { noseek(); return; } if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { if (ioctl(fileno(stdin), FIODTYPE, &type)) err(1, "%s", fname); /* * Most tape drives don't support seeking, * yet fseek() would succeed. */ if (type & D_TAPE) { noseek(); return; } } if (fseeko(stdin, skip, SEEK_SET)) { noseek(); return; } address += skip; skip = 0; } static void noseek(void) { int count; for (count = 0; count < skip; ++count) if (getchar() == EOF) break; address += count; skip -= count; } diff --git a/usr.bin/hexdump/parse.c b/usr.bin/hexdump/parse.c index f59f0d9b6356..c6616fdd24d7 100644 --- a/usr.bin/hexdump/parse.c +++ b/usr.bin/hexdump/parse.c @@ -1,529 +1,532 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "hexdump.h" FU *endfu; /* format at end-of-data */ void addfile(const 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 = strchr(buf, '\n'))) { warnx("line too long"); 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 add(const char *fmt) { unsigned const char *p, *savep; static FS **nextfs; FS *tfs; FU *tfu, **nextfu; /* start new linked list of format units */ if ((tfs = calloc(1, sizeof(FS))) == NULL) err(1, NULL); 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 */ if ((tfu = calloc(1, sizeof(FU))) == NULL) err(1, NULL); *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))) err(1, NULL); (void) strlcpy(tfu->fmt, savep, p - savep + 1); escape(tfu->fmt); p++; } } static const char *spec = ".#-+ 0123456789"; int size(FS *fs) { FU *fu; int bcnt, cursize; unsigned 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 (*++fmt != 0 && strchr(spec + 1, *fmt) != NULL) ; if (*fmt == 0) badnoconv(); 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); } void rewrite(FS *fs) { enum { NOTOKAY, USEBCNT, USEPREC } sokay; PR *pr, **nextpr; FU *fu; unsigned char *p1, *p2, *fmtp; char savech, cs[3]; int nconv, prec; prec = 0; for (fu = fs->nextfu; fu; fu = fu->nextfu) { /* * Break each format unit into print units; each conversion * character gets its own. */ nextpr = &fu->nextpr; for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { if ((pr = calloc(1, sizeof(PR))) == NULL) err(1, NULL); *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. */ while (*++p1 != 0 && strchr(spec, *p1) != NULL) ; if (*p1 == 0) badnoconv(); } else { /* Skip any special chars, field width. */ while (*++p1 != 0 && strchr(spec + 1, *p1) != NULL) ; if (*p1 == 0) badnoconv(); if (*p1 == '.' && isdigit(*++p1)) { sokay = USEPREC; prec = atoi(p1); while (isdigit(*++p1)); } else sokay = NOTOKAY; } p2 = *p1 ? p1 + 1 : p1; /* Set end pointer -- make sure * that it's non-NUL/-NULL first * though. */ cs[0] = *p1; /* Set conversion string. */ cs[1] = '\0'; /* * Figure out the byte count for each conversion; * rewrite the format as necessary, set up blank- * padding for end of data. */ 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(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; + case 8: + pr->bcnt = 8; + 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: 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(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': cs[0] = 'q'; cs[1] = p1[2]; cs[2] = '\0'; break; default: p1[3] = '\0'; badconv(p1); } break; case 'c': pr->flags = F_C; /* cs[0] = 'c'; set in conv_c */ goto isint2; case 'p': pr->flags = F_P; cs[0] = 'c'; goto isint2; case 'u': pr->flags = F_U; /* cs[0] = 'c'; set in conv_u */ isint2: 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[0] = '\0'; if (asprintf(&pr->fmt, "%s%s", fmtp, cs) == -1) err(1, NULL); *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++) errx(1, "byte count with multiple conversion characters"); } /* * 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 = 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; } } #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; /* alphabetic escape sequences have to be done in place */ for (p2 = p1;; p1++, p2++) { if (*p1 == '\\') { p1++; switch(*p1) { case '\0': *p2 = '\\'; *++p2 = '\0'; return; 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; } } else { *p2 = *p1; if (*p1 == '\0') return; } } } void badcnt(const 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(const char *ch) { errx(1, "%%%s: bad conversion character", ch); } void badnoconv(void) { errx(1, "missing conversion character"); }