From 76b8c9e03c97b16d9ff97f3b79c0ecbff0f5e7f2 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 30 Mar 2017 17:31:44 +0300 Subject: Initial commit --- src/dump.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 src/dump.c (limited to 'src/dump.c') diff --git a/src/dump.c b/src/dump.c new file mode 100644 index 0000000..641bb55 --- /dev/null +++ b/src/dump.c @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ragel/fspec.h" + +static size_t +to_hex(const char *buf, const size_t buf_sz, char *out, const size_t out_sz, const bool reverse) +{ + assert(out); + const char nibble[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + const uint8_t nbs = sizeof(nibble) - 1; + + size_t w = 0; + for (size_t i = 0; i < buf_sz && out_sz > 2 && w < out_sz - 2; ++i) { + for (uint8_t c = 0; c < CHAR_BIT / 8 && w < out_sz; ++c) { + const size_t idx = (reverse ? (buf_sz - 1) - i : i); + const uint8_t hi = (buf[idx] >> (4 * (c + 1))) & nbs; + const uint8_t lo = (buf[idx] >> (8 * c)) & nbs; + out[w++] = nibble[hi]; + out[w++] = nibble[lo]; + } + } + + assert(w < out_sz); + out[w] = 0; + return w; +} + +static void +print_decimal(const char *buf, const bool is_signed, const size_t size, const size_t nmemb) +{ + if (nmemb > 1) + printf("{ "); + + for (size_t n = 0; n < nmemb; ++n) { + char hex[2 * sizeof(uint64_t) + 1]; + to_hex(buf + size * n, size, hex, sizeof(hex), true); + const char *delim = (nmemb > 1 && n + 1 < nmemb ? ", " : ""); + + if (is_signed) { + printf("%ld%s", (int64_t)strtoll(hex, NULL, 16), delim); + } else { + printf("%lu%s", (uint64_t)strtoull(hex, NULL, 16), delim); + } + } + + printf("%s\n", (nmemb > 1 ? " }" : "")); +} + +static void +print_hex(const char *buf, const size_t size, const size_t nmemb) +{ + if (nmemb > 1) + printf("{ "); + + for (size_t n = 0; n < nmemb; ++n) { + char hex[2 * sizeof(uint64_t) + 1]; + to_hex(buf + size * n, size, hex, sizeof(hex), false); + printf("%s%s", hex, (nmemb > 1 && n + 1 < nmemb ? ", " : "")); + } + + printf("%s\n", (nmemb > 1 ? " }" : "")); +} + +static void +print_chars(const char *buf, const size_t size, const size_t nmemb) +{ + assert(size == sizeof(char)); + + for (size_t n = 0; n < nmemb && buf[n] != 0; ++n) + printf("%c", buf[n]); +} + +static void +print_encoded(const char *buf, const char *from, const char *to, const size_t size, const size_t nmemb) +{ + assert(from && size == sizeof(char)); + + if (!to) { + static const char *sys_encoding; + if (!sys_encoding) { + setlocale(LC_ALL, ""); + sys_encoding = nl_langinfo(CODESET); + } + + to = sys_encoding; + } + + iconv_t iv; + if ((iv = iconv_open(to, from)) == (iconv_t)-1) + err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); + + const char *in = buf; + size_t in_left = nmemb; + do { + char enc[1024], *out = enc; + size_t out_left = sizeof(enc); + + if (iconv(iv, (char**)&in, &in_left, &out, &out_left) == (size_t)-1) + err(EXIT_FAILURE, "iconv(%s, %s)", to, from); + + print_chars(enc, 1, sizeof(enc) - out_left); + } while (in_left > 0); + + iconv_close(iv); + puts(""); +} + +struct container; +struct field { + struct fspec_field f; + struct container *c, *link; + uint64_t value; +}; + +struct container { + struct fspec_container c; + struct field fields[255]; + size_t num_fields; +}; + +static size_t +field_get_buffer(const struct field *field, FILE *f, char **buf) +{ + assert(field && f && buf); + + switch (field->f.array.type) { + case FSPEC_ARRAY_FIXED: + if (!(*buf = calloc(field->f.array.nmemb, field->f.type.size))) + err(EXIT_FAILURE, "calloc(%zu, %zu)", field->f.array.nmemb, field->f.type.size); + + if (fread(*buf, field->f.type.size, field->f.array.nmemb, f) != field->f.array.nmemb) + return 0; + + return field->f.array.nmemb; + + case FSPEC_ARRAY_MATCH: + { + size_t off = 0; + const size_t msz = field->f.array.match.size; + for (size_t len = 0;; ++off) { + if (off >= (len ? len - 1 : len) && !(*buf = realloc(*buf, (len += 1024)))) + err(EXIT_FAILURE, "realloc(%zu)", len); + + assert(off < len); + if (fread(*buf + off, 1, 1, f) != 1) + return 0; + + if (off >= msz && !memcmp(field->f.array.match.data, *buf + off - msz, msz)) + break; + } + + (*buf)[off] = 0; + return off; + } + break; + + case FSPEC_ARRAY_VAR: + for (size_t i = 0; i < field->c->num_fields; ++i) { + if (!strcmp(field->c->fields[i].f.name, field->f.array.var)) + return field->c->fields[i].value; + } + break; + } + + return 0; +} + +static void +container_process(struct container *container, FILE *f); + +static void +field_process(struct field *field, FILE *f) +{ + assert(field && f); + + char *buf = NULL; + const size_t nmemb = field_get_buffer(field, f, &buf); + + if (field->link) { + for (size_t i = 0; i < nmemb; ++i) + container_process(field->link, f); + } else { + printf("%s(%zu) %s[%zu] = ", field->f.type.name, field->f.type.size, field->f.name, nmemb); + + if (field->f.kind.flags & FSPEC_KIND_IGNORE) { + puts("..."); + } else if (field->f.kind.flags & FSPEC_KIND_ENCODING) { + print_encoded(buf, field->f.kind.name, NULL, field->f.type.size, nmemb); + } else if (field->f.kind.flags & FSPEC_KIND_HEXADECIMAL) { + print_hex(buf, field->f.type.size, nmemb); + } else { + print_decimal(buf, (field->f.type.flags & FSPEC_TYPE_SIGNED), field->f.type.size, nmemb); + } + + if (nmemb == 1) { + char hex[2 * sizeof(uint64_t) + 1]; + to_hex(buf, field->f.type.size, hex, sizeof(hex), true); + field->value = strtoull(hex, NULL, 16); + } + } + + free(buf); +} + +static void +container_process(struct container *container, FILE *f) +{ + assert(container && f); + + for (size_t i = 0; i < container->num_fields; ++i) + field_process(&container->fields[i], f); +} + +#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))) + +struct fspec_file { + // TODO: Rethink container/field + // I think I want just flat structure of key / value pairs in the end + // Especially if I want to express members of struct members (e.g. struct a { struct b b; u8 c[b.x]; };) + struct container containers[32]; + struct fspec fspec; + FILE *handle; + size_t num_containers; +}; + +static void +fspec_field(struct fspec *fspec, const struct fspec_container *container, const struct fspec_field *field) +{ + assert(fspec && container); + struct fspec_file *f = container_of(fspec, struct fspec_file, fspec); + + if (!f->num_containers || memcmp(container, &f->containers[f->num_containers - 1].c, sizeof(*container))) + f->containers[f->num_containers++].c = *container; + + struct container *c = &f->containers[f->num_containers - 1]; + + if (field->type.flags & FSPEC_TYPE_CONTAINER) { + for (size_t i = 0; i < f->num_containers - 1; ++i) { + if (strcmp(field->type.name, f->containers[i].c.name)) + continue; + + c->fields[c->num_fields].link = &f->containers[i]; + break; + } + } + + c->fields[c->num_fields].c = c; + c->fields[c->num_fields++].f = *field; +} + +static size_t +fspec_read(struct fspec *fspec, char *buf, const size_t size, const size_t nmemb) +{ + assert(fspec && buf); + struct fspec_file *f = container_of(fspec, struct fspec_file, fspec); + return fread(buf, size, nmemb, f->handle); +} + +static FILE* +fopen_or_die(const char *path, const char *mode) +{ + assert(path && mode); + + FILE *f; + if (!(f = fopen(path, mode))) + err(EXIT_FAILURE, "fopen(%s, %s)", path, mode); + + return f; +} + +int +main(int argc, const char *argv[]) +{ + if (argc < 2) + errx(EXIT_FAILURE, "usage: %s file.spec < data\n", argv[0]); + + uint8_t data[4096] = {0}; + + struct fspec_file file = { + .fspec = { + .ops = { + .read = fspec_read, + .field = fspec_field, + }, + .mem = { + .data = data, + .size = sizeof(data), + }, + }, + .handle = fopen_or_die(argv[1], "rb"), + }; + + fspec_parse(&file.fspec); + + if (!file.num_containers) + errx(EXIT_FAILURE, "'%s' contains no containers", argv[1]); + + container_process(&file.containers[file.num_containers - 1], stdin); + fclose(file.handle); + return EXIT_SUCCESS; +} -- cgit v1.2.3