| /* |
| * 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. 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/param.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "hexdump.h" |
| #include "xalloc.h" |
| #include "c.h" |
| #include "nls.h" |
| #include "colors.h" |
| |
| static void doskip(const char *, int, struct hexdump *); |
| static u_char *get(struct hexdump *); |
| |
| enum _vflag vflag = FIRST; |
| |
| static off_t address; /* address/offset in stream */ |
| static off_t eaddress; /* end address */ |
| |
| static const char *color_cond(struct hexdump_pr *pr, unsigned char *bp, int bcnt) |
| { |
| register struct list_head *p; |
| register struct hexdump_clr *clr; |
| off_t offt; |
| int match; |
| |
| list_for_each(p, pr->colorlist) { |
| clr = list_entry(p, struct hexdump_clr, colorlist); |
| offt = clr->offt; |
| match = 0; |
| |
| /* no offset or offset outside this print unit */ |
| if (offt < 0) |
| offt = address; |
| if (offt < address || offt + clr->range > address + bcnt) |
| continue; |
| |
| /* match a string */ |
| if (clr->str) { |
| if (pr->flags == F_ADDRESS) { |
| /* TODO */ |
| } |
| else if (!strncmp(clr->str, (char *)bp + offt |
| - address, clr->range)) |
| match = 1; |
| /* match a value */ |
| } else if (clr->val != -1) { |
| int val = 0; |
| /* addresses are not part of the input, so we can't |
| * compare with the contents of bp */ |
| if (pr->flags == F_ADDRESS) { |
| if (clr->val == address) |
| match = 1; |
| } else { |
| memcpy(&val, bp + offt - address, clr->range); |
| if (val == clr->val) |
| match = 1; |
| } |
| /* no conditions, only a color was specified */ |
| } else |
| return clr->fmt; |
| |
| /* return the format string or check for another */ |
| if (match ^ clr->invert) |
| return clr->fmt; |
| continue; |
| } |
| |
| /* no match */ |
| return NULL; |
| } |
| |
| static inline void |
| print(struct hexdump_pr *pr, unsigned char *bp) { |
| |
| const char *color = NULL; |
| |
| if (pr->colorlist && (color = color_cond(pr, bp, pr->bcnt))) |
| color_enable(color); |
| |
| 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: |
| memmove(&fval, bp, sizeof(fval)); |
| printf(pr->fmt, fval); |
| break; |
| case 8: |
| memmove(&dval, bp, sizeof(dval)); |
| printf(pr->fmt, dval); |
| break; |
| } |
| break; |
| } |
| case F_INT: |
| { |
| short sval; /* int16_t */ |
| int ival; /* int32_t */ |
| long long Lval; /* int64_t, int64_t */ |
| |
| switch(pr->bcnt) { |
| case 1: |
| printf(pr->fmt, (unsigned long long) *bp); |
| break; |
| case 2: |
| memmove(&sval, bp, sizeof(sval)); |
| printf(pr->fmt, (unsigned long long) sval); |
| break; |
| case 4: |
| memmove(&ival, bp, sizeof(ival)); |
| printf(pr->fmt, (unsigned long long) ival); |
| break; |
| case 8: |
| memmove(&Lval, bp, sizeof(Lval)); |
| printf(pr->fmt, Lval); |
| 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("%s", pr->fmt); |
| break; |
| case F_U: |
| conv_u(pr, bp); |
| break; |
| case F_UINT: |
| { |
| unsigned short sval; /* u_int16_t */ |
| unsigned int ival; /* u_int32_t */ |
| unsigned long long Lval;/* u_int64_t, u_int64_t */ |
| |
| switch(pr->bcnt) { |
| case 1: |
| printf(pr->fmt, (unsigned long long) *bp); |
| break; |
| case 2: |
| memmove(&sval, bp, sizeof(sval)); |
| printf(pr->fmt, (unsigned long long) sval); |
| break; |
| case 4: |
| memmove(&ival, bp, sizeof(ival)); |
| printf(pr->fmt, (unsigned long long) ival); |
| break; |
| case 8: |
| memmove(&Lval, bp, sizeof(Lval)); |
| printf(pr->fmt, Lval); |
| break; |
| } |
| break; |
| } |
| } |
| if (color) /* did we colorize something? */ |
| color_disable(); |
| } |
| |
| static void bpad(struct hexdump_pr *pr) |
| { |
| static const char *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; |
| |
| p1 = pr->fmt; |
| while (*p1 != '%') |
| ++p1; |
| |
| p2 = ++p1; |
| while (*p1 && strchr(spec, *p1)) |
| ++p1; |
| |
| while ((*p2++ = *p1++)) |
| ; |
| } |
| |
| void display(struct hexdump *hex) |
| { |
| register struct list_head *fs; |
| register struct hexdump_fs *fss; |
| register struct hexdump_fu *fu; |
| register struct hexdump_pr *pr; |
| register int cnt; |
| register unsigned char *bp; |
| off_t saveaddress; |
| unsigned char savech = 0, *savebp; |
| struct list_head *p, *q, *r; |
| |
| while ((bp = get(hex)) != NULL) { |
| fs = &hex->fshead; savebp = bp; saveaddress = address; |
| |
| list_for_each(p, fs) { |
| fss = list_entry(p, struct hexdump_fs, fslist); |
| |
| list_for_each(q, &fss->fulist) { |
| fu = list_entry(q, struct hexdump_fu, fulist); |
| |
| if (fu->flags&F_IGNORE) |
| break; |
| |
| cnt = fu->reps; |
| |
| while (cnt) { |
| list_for_each(r, &fu->prlist) { |
| pr = list_entry(r, struct hexdump_pr, prlist); |
| |
| 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); |
| *pr->nospace = savech; |
| } else |
| print(pr, bp); |
| |
| address += pr->bcnt; |
| bp += pr->bcnt; |
| } |
| --cnt; |
| } |
| } |
| bp = savebp; |
| address = saveaddress; |
| } |
| } |
| 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; |
| } |
| list_for_each (p, &endfu->prlist) { |
| const char *color = NULL; |
| |
| pr = list_entry(p, struct hexdump_pr, prlist); |
| if (colors_wanted() && pr->colorlist |
| && (color = color_cond(pr, bp, pr->bcnt))) { |
| color_enable(color); |
| } |
| |
| switch(pr->flags) { |
| case F_ADDRESS: |
| printf(pr->fmt, eaddress); |
| break; |
| case F_TEXT: |
| printf("%s", pr->fmt); |
| break; |
| } |
| if (color) /* did we highlight something? */ |
| color_disable(); |
| } |
| } |
| } |
| |
| static char **_argv; |
| |
| static u_char * |
| get(struct hexdump *hex) |
| { |
| static int ateof = 1; |
| static u_char *curp, *savp; |
| ssize_t n, need, nread; |
| u_char *tmpp; |
| |
| if (!curp) { |
| curp = xcalloc(1, hex->blocksize); |
| savp = xcalloc(1, hex->blocksize); |
| } else { |
| tmpp = curp; |
| curp = savp; |
| savp = tmpp; |
| address += hex->blocksize; |
| } |
| need = hex->blocksize, nread = 0; |
| while (TRUE) { |
| /* |
| * 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 (!hex->length || (ateof && !next(NULL, hex))) { |
| if (need == hex->blocksize) |
| goto retnul; |
| if (!need && vflag != ALL && |
| !memcmp(curp, savp, nread)) { |
| if (vflag != DUP) |
| printf("*\n"); |
| goto retnul; |
| } |
| if (need > 0) |
| memset((char *)curp + nread, 0, need); |
| eaddress = address + nread; |
| return(curp); |
| } |
| if (fileno(stdin) == -1) { |
| warnx(_("all input file arguments failed")); |
| goto retnul; |
| } |
| n = fread((char *)curp + nread, sizeof(unsigned char), |
| hex->length == -1 ? need : min(hex->length, need), stdin); |
| if (!n) { |
| if (ferror(stdin)) |
| warn("%s", _argv[-1]); |
| ateof = 1; |
| continue; |
| } |
| ateof = 0; |
| if (hex->length != -1) |
| hex->length -= n; |
| if (!(need -= n)) { |
| if (vflag == ALL || vflag == FIRST || |
| memcmp(curp, savp, hex->blocksize)) { |
| if (vflag == DUP || vflag == FIRST) |
| vflag = WAIT; |
| return(curp); |
| } |
| if (vflag == WAIT) |
| printf("*\n"); |
| vflag = DUP; |
| address += hex->blocksize; |
| need = hex->blocksize; |
| nread = 0; |
| } |
| else |
| nread += n; |
| } |
| retnul: |
| free (curp); |
| free (savp); |
| return NULL; |
| } |
| |
| int next(char **argv, struct hexdump *hex) |
| { |
| static int done; |
| int statok; |
| |
| if (argv) { |
| _argv = argv; |
| return(1); |
| } |
| while (TRUE) { |
| if (*_argv) { |
| if (!(freopen(*_argv, "r", stdin))) { |
| warn("%s", *_argv); |
| hex->exitval = EXIT_FAILURE; |
| ++_argv; |
| continue; |
| } |
| statok = done = 1; |
| } else { |
| if (done++) |
| return(0); |
| statok = 0; |
| } |
| if (hex->skip) |
| doskip(statok ? *_argv : "stdin", statok, hex); |
| if (*_argv) |
| ++_argv; |
| if (!hex->skip) |
| return(1); |
| } |
| /* NOTREACHED */ |
| } |
| |
| static void |
| doskip(const char *fname, int statok, struct hexdump *hex) |
| { |
| struct stat sbuf; |
| |
| if (statok) { |
| if (fstat(fileno(stdin), &sbuf)) |
| err(EXIT_FAILURE, "%s", fname); |
| if (S_ISREG(sbuf.st_mode) && hex->skip > sbuf.st_size) { |
| /* If size valid and skip >= size */ |
| hex->skip -= sbuf.st_size; |
| address += sbuf.st_size; |
| return; |
| } |
| } |
| /* sbuf may be undefined here - do not test it */ |
| if (fseek(stdin, hex->skip, SEEK_SET)) |
| err(EXIT_FAILURE, "%s", fname); |
| address += hex->skip; |
| hex->skip = 0; |
| } |