[net.sources] Hex dump program

dlnash@ut-ngp.UUCP (01/29/87)

Here's a handy little program for producing hex dumps of files.  It
runs on generic UN!X machines and MS-DOS machines.  The dump produced
is right to left on little-endian machines and left to right on
big-endian machines, but you have to compile it properly for your
machine, see hd.h

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
-----cut here-----cut here-----cut here-----cut here-----
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	hd.1
#	hd.c
#	hd.h
# This archive created: Wed Jan 28 17:36:05 1987
echo shar: extracting hd.1 '(2190 characters)'
sed 's/^x//' << \SHAR_EOF > hd.1
x.TH HD 1 "January 28, 1986"
x.UC
x.SH NAME
xhd \- dump the contents of a file in hex
x.SH SYNOPSIS
x.B hd
x[ name... ]
x.br
x.SH DESCRIPTION
xFor each argument,
x.I hd
xdumps to standard output the contents of the file in hex
xand ASCII, and the offsets into the file for each byte.
xIf no file is specified, the standard input is used.
xIf one of the files cannot be opened, a message is printed
xto that effect and the next file is tried.
x.PP
xDumps from
x.I hd
xare meant to go to a printer, so form feed characters
xare inserted to cause page ejects at the end of the
xpage.  Pages are formatted as follows:  At the top of
xthe page is the name of the file being dumped and the
xcolumn headings.  The column headings label the offset
xof the byte in that column from the address given at
xthe beginning of the line containing the byte.
x.PP
xAfter the column headings, the dump of the file is presented.
xEach line contains 16 bytes of data from the file.  The first
xitem on the line is the address within the file of the
xfirst byte of the line.  If the address is preceded by a "+",
xthen it is the beginning of a 512 byte block.
x.PP
xThe next item on the line is the hexadecimal representation of the bytes
xin the file.  For machines with the so\-called "little\-endian"
xarchitecture (eg. VAXes, IBM PCs), the bytes are arranged right to
xleft, that is, the byte whose address is given at the beginning of the
xline is at the rightmost part of the hex dump.  For machines with
x"big\-endian" architectures (eg. Sun Workstations), the bytes are
xarranged left to right. 
x.PP
xFollowing the hex dump is the ASCII dump.  It is always arranged
xleft to right.  Characters which are not printable (eg. control
xcharacters) are represented by a ".".
x.PP
xWhen the end of file is reached,
x.I hd
xstarts a new page.
x.SH BUGS
xOn MS-DOS machines, most C standard I/O libraries do newline conversion,
xthat is, a \\r\\n sequence is turned into a single \\n.  This is
xcorrected when reading from files which are given on the command line,
xbut not when reading from stdin.
x.PP
xThis manual page uses the term ASCII when it really shouldn't.  Hd will
xcompile and run properly on an EBCDIC machine, since <ctype.h> and
xisprint() are used.
SHAR_EOF
if test 2190 -ne "`wc -c hd.1`"
then
echo shar: error transmitting hd.1 '(should have been 2190 characters)'
fi
echo shar: extracting hd.c '(9234 characters)'
sed 's/^x//' << \SHAR_EOF > hd.c
x/***    hd - hex dump
x *
x *      Revision history:
x *
x *          Version 2.0:  Modified by Donald L. Nash to run on IBM PCs,
x *                        generic MS-DOS machines, generic UN!X machines
x *                        with big-endian architectures, and non-ASCII
x *                        machines which support the UN!X standard I/O
x *                        library.  Also modified to make dumps fit on
x *                        an 80 column screen and printer for machines
x *                        like an IBM PC which have 80 column printers.
x *
x *          Version 1.0:  Written by Kenneth J. Montgomery for the DEC
x *                        VAX running UN!X 4.2 BSD.  Originally made
x *                        dumps wider than 80 columns, indended for
x *                        line printers only.
x *
x */
x
x#include <stdio.h>
x#include "hd.h"
x
xCHAR *titlep;                       /* pointer to name of current file */
xlong addr = 0l;                     /* current address in file */
xint linel;                          /* current line on page */
xCHAR encode[]="0123456789ABCDEF";   /* to convert int to hex number */
x
xmain(argc, argv)
xint argc;
xCHAR *argv[];
x
x{
x
x    FILE *fp, *nextfile();
x
x    while ( (fp = nextfile(argc, argv)) != NULL )   /* while more files */
x        while (dumpline(fp))        /* while more data */
x            if (!--linel) page();   /* if at end of page, page eject */
x    exit(0);
x
x} /* main */
x
x
xaddress()
x
x/*
x    This function prints out the current offset into the file.  If the
x    offset is a multiple of 512 (0x200), then a '+' precedes the address.
x    Otherwise a ' ' precedes it.
x*/
x
x{
x
x    CHAR buf[10];                   /* to put address string in */
x    register CHAR *bp;              /* to step through buf */
x    register long a;                /* to manipulate current address */
x
x    a = addr;                       /* init a to current address */
x    bp = buf + 9;                   /* init bp to end of buff */
x    *bp-- = '\0';                   /* null terminate buf */
x
x    if (a % 0x200 == 0)             /* if on a 512 byte boundary */
x         buf[0] = '+';              /* precede address by a '+' */
x    else
x         buf[0] = ' ';              /* else by a ' ' */
x
x    while (bp >= buf + 1) {         /* while not at buf[1] */
x        *bp-- = encode[a & 0xf];    /* convert first nibble in a to hex */
x        a >>= 4;                    /* shift to get next nibble */
x    } /* while */
x
x    printf(" %s", buf);             /* print address */
x
x} /* address */
x
x
xdumpline(fp)
xFILE *fp;
x
x/*
x    This function dumps a CBSIZE byte line to stdout.  Each line contains
x    the current offset in the file for the start of the line, the hex digits
x    for the CBSIZE bytes, and the ASCII representation of the CBSIZE bytes.
x*/
x
x{
x
x    CHAR cbuf[CBSIZE];              /* input buffer (data from file) */
x    CHAR fbuf[FBSIZE];              /* formatted data buffer */
x    register int ccount;
x
x    ccount = getbuf(cbuf, fp);      /* get CBSIZE bytes from fp to cbuf */
x    if (!ccount) return(0);         /* if ccount == 0 no bytes read, so done */
x    address();                      /* print address */
x    format(cbuf, ccount, fbuf);     /* format ccount bytes from cbuf to fbuf */
x    printf("%s    ", fbuf);         /* print formatted output */
x    xlate(cbuf, ccount, fbuf);      /* xlate ccount bytes from cbuf to fbuf */
x    printf("%s\n", fbuf);           /* print translated output */
x    addr += ccount;                 /* update current address in file */
x    return(ccount == CBSIZE);       /* if we read < CBSIZE bytes, return 0 */
x
x} /* dumpline */
x
x
xformat(cbuf, ccount, fbuf)
xCHAR *cbuf;
xint ccount;
xCHAR *fbuf;
x
x/*
x    This function reads ccount bytes from cbuf and turns them into
x    hexadecimal digit pairs in fbuf.
x*/
x
x{
x
x    register CHAR c, *src, *dst, *lmt;
x
x    for (dst = fbuf, lmt = dst + FBSIZE; dst < lmt;)
x        *dst++ = ' ';               /* blank fill output string */
x    lmt = cbuf + ccount;
x
x#if BIGENDIAN
x
x/*
x    If this is a big-endian machine, then dump from left to right, so start
x    at the beginning of the output buffer.
x*/
x
x    dst = fbuf;
x    dst[FBSIZE - 1] = '\0';         /* terminate string */
x#else
x
x/*
x    If this is a little-endian machine, then dump from right to left, so
x    start at the end of the output buffer.
x*/
x
x    dst = fbuf + FBSIZE - 1;
x    *dst-- = '\0';                  /* terminate string */
x#endif
x
x    for (src = cbuf; src < lmt; src++) {    /* step through source buffer */
x        c = *src;
x
x#if BIGENDIAN
x        *dst++ = ' ';                       /* put a space before hex number */
x        *dst++ = encode[(c >> 4) & 0xf];    /* put in upper nibble */
x        *dst++ = encode[c & 0xf];           /* put in lower nibble */
x#else
x        *dst-- = encode[c & 0xf];           /* put in lower nibble */
x        *dst-- = encode[(c >> 4) & 0xf];    /* put in upper nibble */
x        *dst-- = ' ';                       /* put a space after hex number */
x#endif
x
x    } /* for */
x
x
x} /* format */
x
x
xint getbuf(bp, fp)
xCHAR *bp;
xFILE *fp;
x
x/*
x    This function gets the next CBSIZE bytes from fp.  The bytes read are
x    put in the buffer pointed to by bp.  The actual number of bytes read
x    is returned.
x*/
x
x{
x
x    register CHAR *abp;
x    register int i, x;
x
x    for (i = 0, abp = bp; i < CBSIZE; i++) {    /* read at most CBSIZE bytes */
x        if ((x = getc(fp)) == EOF) break;       /* if EOF then done */
x        *abp++ = x;                             /* put byte in buffer */
x    } /* for */
x
x    return(i);                      /* i counts # of bytes read */
x
x} /* getbuf */
x
x
xFILE *nextfile(argc, argv)
xint argc;
xCHAR *argv[];
x
x/*
x    This function steps through the files on the command line opening
x    each file in turn.  It returns the FILE * for the file it opens.
x*/
x
x{
x
x    static int fn = 0;              /* keeps track of which argv[] is in use */
x    FILE *filep;
x
x    addr = 0l;                      /* reset addr to zero */
x    if (argc < 2) {                 /* if no args, use stdin */
x        if (++fn == 1) {            /* only do this part once */
x            titlep = (CHAR *)NULL;  /* set titlep */
x            page();                 /* start a new page */
x            return(stdin);          /* return stdin */
x        } /* if */
x        else
x            return(NULL);           /* no more, so return NULL */
x    } /* if */
x
xloop:
x
x    if (++fn < argc) {              /* if still more args to go */
x        if (fn == 1) {              /* on the first file */
x
x#if MSDOS
x            filep = fopen(argv[fn], "rb");  /* open next file */
x#else
x            filep = fopen(argv[fn], "r");   /* open next file */
x#endif
x
x        } /* if */
x        else {                      /* already have a file open */
x
x#if MSDOS
x            filep = freopen(argv[fn], "rb", filep);
x#else
x            filep = freopen(argv[fn], "r", filep);
x#endif
x        } /* else */
x
x        titlep = argv[fn];          /* set titlep */
x        page();                     /* start a new page */
x
x        if (filep == NULL) {        /* check for bad filename */
x            printf("Cannot open file %s\n", argv[fn]);
x            goto loop;
x        } /* if */
x
x        return(filep);              /* return file pointer */
x    } /* if */
x
x    return(NULL);                   /* no more files, return NULL */
x
x} /* nextfile */
x
x
xpage()
x
x/*
x    This function starts a new page, prints the name of the input file or
x    "Standard Input" if there are no files on the command line, and prints
x    out the byte indecies for the line.
x*/
x
x{
x
x    putc('\014', stdout);           /* page eject */
x
x    if (titlep)
x        printf("File:  %s", titlep); /* print filename */
x    else
x        printf("Standard Input");
x
x/*
x    Print index for hex part of dump.  Go left to right for big-endian,
x    right to left for little-endian.
x*/
x
x#if BIGENDIAN
x    printf("\n\n           +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F");
x#else
x    printf("\n\n           +F +E +D +C +B +A +9 +8 +7 +6 +5 +4 +3 +2 +1 +0");
x#endif
x
x/*
x    Print index for ASCII part of dump.  It is always left to right.
x*/
x
x    printf("    0123456789ABCDEF\n\n");
x    linel = 55;                     /* set # of lines left on this page */
x
x} /* page */
x
x
xxlate(cbuf, ccount, fbuf)
xCHAR *cbuf;
xint ccount;
xCHAR *fbuf;
x
x/*
x    This function reads ccount bytes from cbuf and puts them in fbuf.  A
x    byte is copied into fbuf only if it is a printable character.  If it
x    is not a printable character, a '.' is put in its place in fbuf.
x*/
x
x{
x
x    register CHAR c, *src, *dst, *lmt;
x
x    src = cbuf;                     /* pointer to source buffer */
x    dst = fbuf;                     /* pointer to destination buffer */
x    lmt = src + ccount;             /* limit of source buffer */
x
x    while (src < lmt) {             /* while more bytes in source buffer */
x        c = *src++;                 /* get next byte */
x        if ( isprint(c) )           /* if it is printable */
x            *dst++ = c;             /* then put it in destination buffer */
x        else
x            *dst++ = '.';           /* else put a '.' in destination buffer */
x    } /* while */
x
x    *dst++ = '\0';                  /* terminate the string */
x
x} /* xlate */
SHAR_EOF
if test 9234 -ne "`wc -c hd.c`"
then
echo shar: error transmitting hd.c '(should have been 9234 characters)'
fi
echo shar: extracting hd.h '(1279 characters)'
sed 's/^x//' << \SHAR_EOF > hd.h
x#define BIGENDIAN   0               /* make this 1 for left to right dump */
x#define ASCII       1               /* make this 1 for ASCII machines */
x#define MSDOS       0               /* make this 1 for MSDOS machines */
x#define IBMPC_CHAR  0               /* make this 1 for IBM PC character set */
x#define CBSIZE      16              /* bytes per line to dump */
x#define FBSIZE (CBSIZE*3+1)         /* output array size */
x
x#if IBMPC_CHAR
xtypedef unsigned char CHAR;         /* chars are unsigned for IBMPC */
x#else
xtypedef char CHAR;                  /* they are signed on others */
x#endif
x
x/*
x    I make my own version of isprint() here so I don't have to #include
x    all the other stuff in <ctype.h>, like the tables for isascii(),
x    isalpha(), islower(), etc.  Since I don't know of an easy way to make
x    this macro work on non-ASCII machines, I just use <ctype.h> for them.
x
x    I special-case IBMPC_CHAR since all characters above ' ' are printable
x    on an IBM PC screen and printer.  If your machine or printer does not
x    support this feature, set IBMPC_CHAR to 0.
x*/
x
x#if ASCII & ( IBMPC_CHAR == 0 )
x#define isprint(c)  ( c >= ' ' && c <= '~' )
x#endif
x
x#if ASCII & IBMPC_CHAR
x#define isprint(c)  ( c >= ' ' )
x#endif
x
x#if ASCII == 0
x#include <ctype.h>
x#endif
SHAR_EOF
if test 1279 -ne "`wc -c hd.h`"
then
echo shar: error transmitting hd.h '(should have been 1279 characters)'
fi
#	End of shell archive
exit 0