/* * Support code for the hexdump and od applets, * based on code from util-linux v 2.11l * * Copyright (c) 1989 * The Regents of the University of California. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Original copyright notice is retained at the end of this file. */ #include #include #include /* for isdigit() */ #include "dump.h" #include "libbb.h" enum _vflag vflag = FIRST; FS *fshead; /* head of format strings */ extern FS *fshead; /* head of format strings */ extern int blocksize; static FU *endfu; static char **_argv; static off_t savaddress; /* saved address/offset in stream */ static off_t eaddress; /* end address */ static off_t address; /* address/offset in stream */ off_t skip; /* bytes to skip */ off_t saveaddress; int exitval; /* final exit value */ int blocksize; /* data block size */ int length = -1; /* max bytes to read */ int size(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(".#-+ 0123456789" + 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); } void rewrite(FS * fs) { enum { NOTOKAY, USEBCNT, USEPREC } sokay; register PR *pr, **nextpr = NULL; register FU *fu; register char *p1, *p2; char savech, *fmtp; int nconv, prec = 0; 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 *) xmalloc(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(".#-+ 0123456789", *p1); ++p1); } else { /* skip any special chars, field width */ while (index(".#-+ 0123456789" + 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'; error_msg_and_die ("bad byte count for conversion character %s.", 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'; error_msg_and_die ("hexdump: bad conversion character %%%s.\n", 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'; error_msg_and_die ("bad byte count for conversion character %s.", 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'; error_msg_and_die ("bad byte count for conversion character %s.", p1); } break; case 's': pr->flags = F_STR; switch (sokay) { case NOTOKAY: error_msg_and_die ("%%s requires a precision or a byte count."); 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'; error_msg_and_die ("hexdump: bad conversion character %%%s.\n", 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'; error_msg_and_die ("bad byte count for conversion character %s.", p1); } break; default: p1[2] = '\0'; error_msg_and_die ("hexdump: bad conversion character %%%s.\n", p1); } break; default: p1[1] = '\0'; error_msg_and_die("hexdump: bad conversion character %%%s.\n", p1); } /* * copy to PR format string, set conversion character * pointer, update original. */ savech = *p2; p1[1] = '\0'; if (!(pr->fmt = strdup(fmtp))) perror_msg_and_die("hexdump"); *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++) { error_msg_and_die ("hexdump: byte count with multiple conversion characters.\n"); } } /* * 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; } } static void doskip(char *fname, int statok) { struct stat sbuf; if (statok) { if (fstat(fileno(stdin), &sbuf)) { perror_msg_and_die("hexdump: %s", fname); } if ((!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))) && skip >= sbuf.st_size) { /* If size valid and skip >= size */ skip -= sbuf.st_size; address += sbuf.st_size; return; } } if (fseek(stdin, skip, SEEK_SET)) { perror_msg_and_die("hexdump: %s", fname); } savaddress = address += skip; skip = 0; } int next(char **argv) { static int done; int statok; if (argv) { _argv = argv; return (1); } for (;;) { if (*_argv) { if (!(freopen(*_argv, "r", stdin))) { perror_msg("%s", *_argv); exitval = 1; ++_argv; continue; } statok = done = 1; } else { if (done++) return (0); statok = 0; } if (skip) doskip(statok ? *_argv : "stdin", statok); if (*_argv) ++_argv; if (!skip) return (1); } /* NOTREACHED */ } static u_char *get(void) { static int ateof = 1; static u_char *curp, *savp; register int n; int need, nread; u_char *tmpp; if (!curp) { curp = (u_char *) xmalloc(blocksize); savp = (u_char *) xmalloc(blocksize); } else { tmpp = curp; curp = savp; savp = tmpp; address = savaddress += blocksize; } 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 (need == blocksize) { return ((u_char *) NULL); } if (vflag != ALL && !bcmp(curp, savp, nread)) { if (vflag != DUP) { printf("*\n"); } 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)) { perror_msg("%s", _argv[-1]); } ateof = 1; continue; } ateof = 0; if (length != -1) { length -= n; } if (!(need -= n)) { if (vflag == ALL || vflag == FIRST || bcmp(curp, savp, blocksize)) { if (vflag == DUP || vflag == FIRST) { vflag = WAIT; } return (curp); } if (vflag == WAIT) { printf("*\n"); } vflag = DUP; address = savaddress += blocksize; need = blocksize; nread = 0; } else { nread += n; } } } static void bpad(PR * pr) { register 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 = 's'; for (p1 = pr->fmt; *p1 != '%'; ++p1); for (p2 = ++p1; *p1 && index(" -0+#", *p1); ++p1); while ((*p2++ = *p1++) != 0); } void conv_c(PR * pr, u_char * p) { char buf[10], *str; switch (*p) { case '\0': str = "\\0"; goto strpr; /* case '\a': */ case '\007': str = "\\a"; goto strpr; case '\b': str = "\\b"; goto strpr; case '\f': str = "\\f"; goto strpr; case '\n': str = "\\n"; goto strpr; case '\r': str = "\\r"; goto strpr; case '\t': str = "\\t"; goto strpr; case '\v': str = "\\v"; goto strpr; default: break; } if (isprint(*p)) { *pr->cchar = 'c'; (void) printf(pr->fmt, *p); } else { sprintf(str = buf, "%03o", (int) *p); strpr: *pr->cchar = 's'; printf(pr->fmt, str); } } void conv_u(PR * pr, u_char * p) { static char *list[] = { "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "lf", "vt", "ff", "cr", "so", "si", "dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em", "sub", "esc", "fs", "gs", "rs", "us", }; /* od used nl, not lf */ if (*p <= 0x1f) { *pr->cchar = 's'; printf(pr->fmt, list[*p]); } else if (*p == 0x7f) { *pr->cchar = 's'; printf(pr->fmt, "del"); } else if (isprint(*p)) { *pr->cchar = 'c'; printf(pr->fmt, *p); } else { *pr->cchar = 'x'; printf(pr->fmt, (int) *p); } } void display(void) { /* extern FU *endfu; */ register FS *fs; register FU *fu; register PR *pr; register int cnt; register u_char *bp; /* off_t saveaddress; */ u_char savech = 0, *savebp; while ((bp = get()) != NULL) { 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; */ switch (pr->flags) { case F_ADDRESS: printf(pr->fmt, address); break; case F_BPAD: printf(pr->fmt, ""); break; case F_C: conv_c(pr, bp); break; case F_CHAR: printf(pr->fmt, *bp); break; case F_DBL:{ double dval; float fval; switch (pr->bcnt) { case 4: bcopy((char *) bp, (char *) &fval, sizeof(fval)); printf(pr->fmt, fval); break; case 8: bcopy((char *) bp, (char *) &dval, sizeof(dval)); printf(pr->fmt, dval); break; } break; } case F_INT:{ int ival; short sval; switch (pr->bcnt) { case 1: printf(pr->fmt, (int) *bp); break; case 2: bcopy((char *) bp, (char *) &sval, sizeof(sval)); printf(pr->fmt, (int) sval); break; case 4: bcopy((char *) bp, (char *) &ival, sizeof(ival)); printf(pr->fmt, ival); break; } break; } case F_P: printf(pr->fmt, isprint(*bp) ? *bp : '.'); break; case F_STR: printf(pr->fmt, (char *) bp); break; case F_TEXT: printf(pr->fmt); break; case F_U: conv_u(pr, bp); break; case F_UINT:{ u_int ival; u_short sval; switch (pr->bcnt) { case 1: printf(pr->fmt, (u_int) * bp); break; case 2: bcopy((char *) bp, (char *) &sval, sizeof(sval)); printf(pr->fmt, (u_int) sval); break; case 4: bcopy((char *) bp, (char *) &ival, sizeof(ival)); printf(pr->fmt, ival); break; } break; } } 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, eaddress); break; case F_TEXT: (void) printf(pr->fmt); break; } } } } int dump(char **argv) { register FS *tfs; /* figure out the data block size */ for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) { tfs->bcnt = size(tfs); if (blocksize < tfs->bcnt) { blocksize = tfs->bcnt; } } /* rewrite the rules, do syntax checking */ for (tfs = fshead; tfs; tfs = tfs->nextfs) { rewrite(tfs); } next(argv); display(); return (exitval); } void add(char *fmt) { register char *p; register char *p1; register char *p2; static FS **nextfs; FS *tfs; FU *tfu, **nextfu; char *savep; /* start new linked list of format units */ /* NOSTRICT */ tfs = (FS *) xmalloc(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 *) xmalloc(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 != '/') { error_msg_and_die("hexdump: bad format {%s}", 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)) { error_msg_and_die("hexdump: bad format {%s}", fmt); } tfu->bcnt = atoi(savep); /* skip trailing white space */ for (++p; isspace(*p); ++p); } /* format */ if (*p != '"') { error_msg_and_die("hexdump: bad format {%s}", fmt); } for (savep = ++p; *p != '"';) { if (*p++ == 0) { error_msg_and_die("hexdump: bad format {%s}", fmt); } } if (!(tfu->fmt = malloc(p - savep + 1))) { perror_msg_and_die("hexdump"); } strncpy(tfu->fmt, savep, p - savep); tfu->fmt[p - savep] = '\0'; /* escape(tfu->fmt); */ p1 = tfu->fmt; /* 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; } } } p++; } } /* * Copyright (c) 1989 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. */