blob: 3e017635b45f76b7d6fefaec35fd29e7870a6db0 [file] [log] [blame]
/*
* 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;
}