#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/debug.h>
#include <sys/ilstr.h>
#include <sys/hexdump.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/stdbool.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#define DATADIR "/opt/os-tests/tests/hexdump/data"
const char *
_umem_debug_init(void)
{
return ("default,verbose");
}
const char *
_umem_logging_init(void)
{
return ("transaction,contents,fail");
}
typedef struct test {
const char *name;
hexdump_flag_t flags;
uint8_t width;
uint8_t grouping;
uint64_t addr;
uint8_t indent;
uint8_t marker;
} test_t;
test_t tests[] = {
{
.name = "basic",
}, {
.name = "header",
.flags = HDF_HEADER,
}, {
.name = "address",
.flags = HDF_ADDRESS,
}, {
.name = "ascii",
.flags = HDF_ASCII,
}, {
.name = "dedup",
.flags = HDF_DEDUP,
}, {
.name = "doublespace",
.flags = HDF_DOUBLESPACE,
}, {
.name = "address+header",
.flags = HDF_ADDRESS | HDF_HEADER,
}, {
.name = "default",
.flags = HDF_DEFAULT,
}, {
.name = "marker1",
.flags = HDF_DEFAULT | HDF_DEDUP,
.marker = 5
}, {
.name = "addr1",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0x876543
}, {
.name = "addr2",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0xffff8
}, {
.name = "align1",
.flags = HDF_DEFAULT | HDF_DEDUP | HDF_ALIGN,
.addr = 0x876543
}, {
.name = "indent",
.flags = HDF_DEFAULT | HDF_DEDUP,
.indent = 3
}, {
.name = "group2",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0x876543,
.grouping = 2,
}, {
.name = "group4",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0x876543,
.grouping = 4,
}, {
.name = "group8",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0x876543,
.grouping = 8,
}, {
.name = "width12",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0x876543,
.width = 12,
.grouping = 4
}, {
.name = "wide1",
.flags = HDF_ADDRESS | HDF_HEADER,
.addr = 0x876543,
.width = 32,
.grouping = 8
}, {
.name = "narrow1",
.flags = HDF_DEFAULT | HDF_DOUBLESPACE,
.addr = 0x876543,
.width = 4,
.grouping = 2
}, {
.name = "narrow2",
.flags = HDF_DEFAULT | HDF_DEDUP,
.addr = 0x876543,
.width = 1,
.grouping = 1
}
};
static char *flagdescr[] = {
"HEADER",
"ADDRESS",
"ASCII",
"ALIGN",
"DEDUP",
"DOUBLESPACE",
};
static void
descr(test_t *t, ilstr_t *i)
{
ilstr_append_str(i, "=============================================\n");
ilstr_aprintf(i, "[%s] w=%u g=%u a=0x%x - ",
t->name, t->width, t->grouping, t->addr);
int flags = t->flags;
bool first = true;
while (flags != 0) {
int b = fls(flags);
if (b == 0)
break;
b--;
VERIFY3S(b, <, ARRAY_SIZE(flagdescr));
if (first)
first = false;
else
ilstr_append_char(i, ' ');
ilstr_aprintf(i, "%s", flagdescr[b]);
flags &= ~(1<< b);
}
ilstr_append_char(i, '\n');
ilstr_append_str(i, "=============================================\n");
}
static int
cb(void *arg, uint64_t addr __unused, const char *str, size_t l)
{
ilstr_t *i = arg;
ilstr_append_str(i, str);
ilstr_append_char(i, '\n');
return (0);
}
static void
run(test_t *t, uint8_t *data, size_t len, ilstr_t *i)
{
hexdump_t hd;
descr(t, i);
hexdump_init(&hd);
if (t->width != 0)
hexdump_set_width(&hd, t->width);
if (t->grouping != 0)
hexdump_set_grouping(&hd, t->grouping);
if (t->addr != 0)
hexdump_set_addr(&hd, t->addr);
if (t->indent != 0)
hexdump_set_indent(&hd, t->indent);
if (t->marker != 0)
hexdump_set_marker(&hd, t->marker);
VERIFY0(hexdumph(&hd, data, len, t->flags, cb, (void *)i));
hexdump_fini(&hd);
VERIFY3U(ilstr_errno(i), ==, ILSTR_ERROR_OK);
}
static uint8_t *
mapfile(const char *filename, size_t *lenp)
{
uint8_t *p;
struct stat st;
int fd;
if ((fd = open(filename, O_RDONLY)) == -1)
err(EXIT_FAILURE, "could not open '%s'", filename);
if (fstat(fd, &st) == -1)
err(EXIT_FAILURE, "failed to stat '%s'", filename);
p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (p == MAP_FAILED)
err(EXIT_FAILURE, "failed to mmap 0x%lx bytes from '%s'",
st.st_size, filename);
VERIFY0(close(fd));
*lenp = st.st_size;
return (p);
}
static void __PRINTFLIKE(2) __NORETURN
usage(int ec, const char *fmt, ...)
{
va_list ap;
if (fmt != NULL) {
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
(void) fprintf(stderr, "\n");
}
(void) fprintf(stderr,
"Usage:\n"
" -d <directory> specify data directory\n"
" (default: %s)\n"
" -g generate baseline files\n"
" -h show usage\n"
" -t <test> run just the test named <test>\n"
" -v send test output to stdout\n",
DATADIR);
exit(ec);
}
int
main(int argc, char **argv)
{
uint8_t *data;
const char *datadir = DATADIR;
char buf[MAXPATHLEN + 1];
const char *test = NULL;
ilstr_t testout;
uint_t failures = 0;
size_t maplen;
int c;
enum {
MODE_TEST,
MODE_GENERATE,
MODE_DUMP
} testmode = MODE_TEST;
while ((c = getopt(argc, argv, ":d:ght:v")) != -1) {
switch (c) {
case 'd':
datadir = optarg;
break;
case 'g':
testmode = MODE_GENERATE;
break;
case 'h':
usage(0, NULL);
case 't':
test = optarg;
break;
case 'v':
testmode = MODE_DUMP;
break;
case ':':
usage(EXIT_FAILURE,
"Option -%c requires an operand\n", optopt);
case '?':
usage(EXIT_FAILURE, "Unknown option: -%c", optopt);
}
}
if (snprintf(buf, sizeof (buf), "%s/_input", datadir) >= sizeof (buf))
errx(EXIT_FAILURE, "Overflow building data dir path");
data = mapfile(buf, &maplen);
ilstr_init(&testout, 0);
for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
if (test != NULL && strcmp(test, tests[i].name) != 0)
continue;
if (snprintf(buf, sizeof (buf), "%s/%s", datadir,
tests[i].name) >= sizeof (buf)) {
errx(EXIT_FAILURE, "Overflow building output path");
}
run(&tests[i], data, maplen, &testout);
switch (testmode) {
case MODE_TEST: {
uint8_t *refdata;
size_t reflen;
refdata = mapfile(buf, &reflen);
if (ilstr_len(&testout) != reflen ||
memcmp(ilstr_cstr(&testout), refdata,
reflen) != 0) {
failures++;
(void) fprintf(stderr,
"Hexdump '%s' output mismatch",
tests[i].name);
(void) fprintf(stderr, "== Expected:\n%s\n",
refdata);
(void) fprintf(stderr, "== Got:\n%s\n",
ilstr_cstr(&testout));
}
VERIFY0(munmap(refdata, reflen));
break;
}
case MODE_GENERATE: {
FILE *fp;
fp = fopen(buf, "w");
if (fp == NULL)
err(EXIT_FAILURE, "Failed to create %s", buf);
(void) fprintf(fp, "%s", ilstr_cstr(&testout));
VERIFY0(fclose(fp));
break;
}
case MODE_DUMP:
(void) fprintf(stdout, "%s\n", ilstr_cstr(&testout));
break;
}
ilstr_reset(&testout);
}
ilstr_fini(&testout);
VERIFY0(munmap(data, maplen));
if (testmode == MODE_TEST && failures == 0)
(void) printf("All hexdump tests have passed.\n");
return (failures > 0 ? EXIT_FAILURE : 0);
}