ex_hexdump.c
#include "ex_utils.h"
#define PRNT_NONE 0
#define PRNT_SPAC 1
#define PRNT_HIGH 2
#define EX_HEXDUMP_CHRS_PER_LINE 16
static unsigned int prnt_high_chars = PRNT_NONE;
#if 0
# define EX_HEXDUMP_X8(s1, num) \
vstr_add_fmt(s1, (s1)->len, "0x%08X:", (num))
# define EX_HEXDUMP_X2X2(s1, num1, num2) \
vstr_add_fmt(s1, (s1)->len, " %02X%02X", (num1), (num2))
# define EX_HEXDUMP_X2__(s1, num1) \
vstr_add_fmt(s1, (s1)->len, " %02X ", (num1))
#else
static const char *hexnums = "0123456789ABCDEF";
# define EX_HEXDUMP_BYTE(buf, b) do { \
(buf)[1] = hexnums[((b) >> 0) & 0xf]; \
(buf)[0] = hexnums[((b) >> 4) & 0xf]; \
} while (FALSE)
# define EX_HEXDUMP_UINT(buf, i) do { \
EX_HEXDUMP_BYTE((buf) + 6, (i) >> 0); \
EX_HEXDUMP_BYTE((buf) + 4, (i) >> 8); \
EX_HEXDUMP_BYTE((buf) + 2, (i) >> 16); \
EX_HEXDUMP_BYTE((buf) + 0, (i) >> 24); \
} while (FALSE)
# define EX_HEXDUMP_X8(s1, num) do { unsigned char xbuf[9]; \
xbuf[8] = ':'; \
EX_HEXDUMP_UINT(xbuf, num); \
vstr_add_buf(s1, (s1)->len, xbuf, sizeof(xbuf)); } while (FALSE)
# define EX_HEXDUMP_X2X2(s1, num1, num2) do { unsigned char xbuf[5]; \
xbuf[0] = ' '; \
EX_HEXDUMP_BYTE(xbuf + 3, num2); \
EX_HEXDUMP_BYTE(xbuf + 1, num1); \
vstr_add_buf(s1, (s1)->len, xbuf, sizeof(xbuf)); } while (FALSE)
# define EX_HEXDUMP_X2__(s1, num1) do { unsigned char xbuf[5]; \
xbuf[4] = ' '; \
xbuf[3] = ' '; \
EX_HEXDUMP_BYTE(xbuf + 1, num1); \
xbuf[0] = ' '; \
vstr_add_buf(s1, (s1)->len, xbuf, sizeof(xbuf)); } while (FALSE)
#endif
static int ex_hexdump_process(Vstr_base *s1, Vstr_base *s2, int last)
{
static unsigned int addr = 0;
unsigned int flags = VSTR_FLAG02(CONV_UNPRINTABLE_ALLOW, COMMA, DOT);
unsigned int flags_sp = VSTR_FLAG04(CONV_UNPRINTABLE_ALLOW,
COMMA, DOT, _, SP);
unsigned int flags_hsp = VSTR_FLAG06(CONV_UNPRINTABLE_ALLOW,
COMMA, DOT, _, SP, HSP, HIGH);
unsigned char buf[EX_HEXDUMP_CHRS_PER_LINE];
switch (prnt_high_chars)
{
case PRNT_HIGH: flags = flags_hsp; break;
case PRNT_SPAC: flags = flags_sp; break;
case PRNT_NONE: break;
default: ASSERT(FALSE); break;
}
if (s1->len > EX_MAX_W_DATA_INCORE)
return (FALSE);
while (s2->len >= EX_HEXDUMP_CHRS_PER_LINE)
{
unsigned int count = 0;
vstr_export_buf(s2, 1, EX_HEXDUMP_CHRS_PER_LINE, buf, sizeof(buf));
EX_HEXDUMP_X8(s1, addr);
while (count < EX_HEXDUMP_CHRS_PER_LINE)
{
EX_HEXDUMP_X2X2(s1, buf[count], buf[count + 1]);
count += 2;
}
vstr_add_rep_chr(s1, s1->len, ' ', 2);
vstr_conv_unprintable_chr(s2, 1, EX_HEXDUMP_CHRS_PER_LINE, flags, '.');
vstr_add_vstr(s1, s1->len, s2, 1, EX_HEXDUMP_CHRS_PER_LINE,
VSTR_TYPE_ADD_ALL_BUF);
vstr_add_rep_chr(s1, s1->len, '\n', 1);
addr += EX_HEXDUMP_CHRS_PER_LINE;
vstr_del(s2, 1, EX_HEXDUMP_CHRS_PER_LINE);
if (s1->conf->malloc_bad)
errno = ENOMEM, err(EXIT_FAILURE, "adding data");
if (s1->len > EX_MAX_W_DATA_INCORE)
return (TRUE);
}
if (last && s2->len)
{
size_t got = s2->len;
size_t missing = EX_HEXDUMP_CHRS_PER_LINE - s2->len;
const char *ptr = buf;
missing -= (missing % 2);
vstr_export_buf(s2, 1, s2->len, buf, sizeof(buf));
EX_HEXDUMP_X8(s1, addr);
while (got >= 2)
{
EX_HEXDUMP_X2X2(s1, ptr[0], ptr[1]);
got -= 2;
ptr += 2;
}
if (got)
{
EX_HEXDUMP_X2__(s1, ptr[0]);
got -= 2;
}
vstr_add_rep_chr(s1, s1->len, ' ', (missing * 2) + (missing / 2) + 2);
vstr_conv_unprintable_chr(s2, 1, s2->len, flags, '.');
vstr_add_vstr(s1, s1->len, s2, 1, s2->len, VSTR_TYPE_ADD_ALL_BUF);
vstr_add_cstr_buf(s1, s1->len, "\n");
addr += s2->len;
vstr_del(s2, 1, s2->len);
if (s1->conf->malloc_bad)
errno = ENOMEM, err(EXIT_FAILURE, "adding data");
return (TRUE);
}
return (FALSE);
}
static void ex_hexdump_process_limit(Vstr_base *s1, Vstr_base *s2,
unsigned int lim)
{
while (s2->len > lim)
{
int proc_data = ex_hexdump_process(s1, s2, !lim);
if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
io_block(-1, STDOUT_FILENO);
}
}
static void ex_hexdump_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2,
int fd)
{
while (TRUE)
{
int io_w_state = IO_OK;
int io_r_state = io_get(s2, fd);
if (io_r_state == IO_EOF)
break;
ex_hexdump_process(s1, s2, FALSE);
io_w_state = io_put(s1, 1);
io_limit(io_r_state, fd, io_w_state, 1, s1);
}
ex_hexdump_process_limit(s1, s2, 0);
}
int main(int argc, char *argv[])
{
Vstr_base *s2 = NULL;
Vstr_base *s1 = ex_init(&s2);
int count = 1;
unsigned int use_mmap = FALSE;
while (count < argc)
{
if (!strcmp("--", argv[count]))
{
++count;
break;
}
else if (!strcmp("--mmap", argv[count]))
use_mmap = !use_mmap;
else if (!strcmp("--none", argv[count]))
prnt_high_chars = PRNT_NONE;
else if (!strcmp("--space", argv[count]))
prnt_high_chars = PRNT_SPAC;
else if (!strcmp("--high", argv[count]))
prnt_high_chars = PRNT_HIGH;
else if (!strcmp("--version", argv[count]))
{
vstr_add_fmt(s1, 0, "%s", "\
jhexdump 1.0.0\n\
Written by James Antill\n\
\n\
Uses Vstr string library.\n\
");
goto out;
}
else if (!strcmp("--help", argv[count]))
{
vstr_add_fmt(s1, 0, "%s", "\
Usage: jhexdump [FILENAME]...\n\
or: jhexdump OPTION\n\
Output filenames in human hexdump format.\n\
\n\
--help Display this help and exit\n\
--version Output version information and exit\n\
--high Allow space and high characters in ASCII output\n\
--none Allow only small amount of characters ASCII output (default)\n\
--space Allow space characters in ASCII output\n\
--mmap Toggle use of mmap() to load input files\n\
-- Treat rest of cmd line as input filenames\n\
\n\
Report bugs to James Antill <james@and.org>.\n\
");
goto out;
}
else
break;
++count;
}
if (count >= argc)
{
io_fd_set_o_nonblock(STDIN_FILENO);
ex_hexdump_read_fd_write_stdout(s1, s2, STDIN_FILENO);
}
while (count < argc)
{
unsigned int ern = 0;
ASSERT(!s2->len);
if (use_mmap)
vstr_sc_mmap_file(s2, s2->len, argv[count], 0, 0, &ern);
if (!use_mmap ||
(ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
(ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
(ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
{
int fd = io_open(argv[count]);
ex_hexdump_read_fd_write_stdout(s1, s2, fd);
if (close(fd) == -1)
warn("close(%s)", argv[count]);
}
else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
err(EXIT_FAILURE, "add");
else
ex_hexdump_process_limit(s1, s2, 0);
++count;
}
ASSERT(!s2->len);
out:
io_put_all(s1, STDOUT_FILENO);
exit (ex_exit(s1, s2));
}