From 29086b1d12a2c28cffdbfbf0b3990a7bd75506b9 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 20 Apr 2017 16:49:35 +0300 Subject: work in progress --- src/dump.c | 834 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 665 insertions(+), 169 deletions(-) (limited to 'src/dump.c') diff --git a/src/dump.c b/src/dump.c index 641bb55..8ff1466 100644 --- a/src/dump.c +++ b/src/dump.c @@ -6,13 +6,19 @@ #include #include +#include #include #include +#include -#include "ragel/fspec.h" +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) static size_t -to_hex(const char *buf, const size_t buf_sz, char *out, const size_t out_sz, const bool reverse) +to_hex(const uint8_t *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' }; @@ -35,234 +41,688 @@ to_hex(const char *buf, const size_t buf_sz, char *out, const size_t out_sz, con } static void -print_decimal(const char *buf, const bool is_signed, const size_t size, const size_t nmemb) +print_dec(const uint8_t *buf, const size_t size, const bool is_signed) { - if (nmemb > 1) - printf("{ "); + char hex[2 * sizeof(fspec_num) + 1]; + to_hex(buf, size, hex, sizeof(hex), true); - 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); - } + static_assert(sizeof(fspec_num) <= sizeof(uint64_t), "fspec_num is larger than uint64_t"); + + if (is_signed) { + printf("%ld", (int64_t)strtoll(hex, NULL, 16)); + } else { + printf("%lu", (uint64_t)strtoull(hex, NULL, 16)); } +} + +static void +print_udec(const uint8_t *buf, const size_t size) +{ + print_dec(buf, size, false); +} + +static void +print_sdec(const uint8_t *buf, const size_t size) +{ + print_dec(buf, size, true); +} - printf("%s\n", (nmemb > 1 ? " }" : "")); +static void +print_hex(const uint8_t *buf, const size_t size) +{ + char hex[2 * sizeof(fspec_num) + 1]; + to_hex(buf, size, hex, sizeof(hex), false); + printf("%s", hex); } static void -print_hex(const char *buf, const size_t size, const size_t nmemb) +print_array(const uint8_t *buf, const size_t size, const size_t nmemb, void (*fun)(const uint8_t *buf, const size_t size)) { - if (nmemb > 1) + const int indent = 4; + if (nmemb > 8) { + printf("{\n%*s", indent, ""); + } else 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 ? ", " : "")); + fun(buf + n * size, size); + printf("%s", (nmemb > 1 && n + 1 < nmemb ? ", " : "")); + + if (!((n + 1) % 8)) + printf("\n%*s", indent, ""); } - printf("%s\n", (nmemb > 1 ? " }" : "")); + printf("%s\n", (nmemb > 8 ? "\n}" : (nmemb > 1 ? " }" : ""))); } static void -print_chars(const char *buf, const size_t size, const size_t nmemb) +print_str(const char *buf, const size_t size, const size_t nmemb) { - assert(size == sizeof(char)); + const bool has_nl = memchr(buf, '\n', size * nmemb); + if (has_nl) + puts("```"); - for (size_t n = 0; n < nmemb && buf[n] != 0; ++n) + for (size_t n = 0; n < size * nmemb && buf[n] != 0; ++n) printf("%c", buf[n]); + + puts((has_nl ? "```" : "")); } +struct code { + const enum fspec_op *start, *end, *data; +}; + static void -print_encoded(const char *buf, const char *from, const char *to, const size_t size, const size_t nmemb) +dump_ops(const struct code *code) { - assert(from && size == sizeof(char)); + for (const enum fspec_op *op = code->start; op; op = fspec_op_next(op, code->end, false)) { + printf("%*s- ", (*op == FSPEC_OP_ARG ? 2 : 0), ""); + switch (*op) { + case FSPEC_OP_HEADER: + printf("header\n"); + break; + + case FSPEC_OP_DECLARATION: + printf("declaration\n"); + break; + + case FSPEC_OP_READ: + printf("read\n"); + break; + + case FSPEC_OP_GOTO: + printf("goto\n"); + break; + + case FSPEC_OP_FILTER: + printf("filter\n"); + break; + + case FSPEC_OP_VISUAL: + printf("visual\n"); + break; + + case FSPEC_OP_ARG: + { + const enum fspec_arg *arg = (void*)(op + 1); + printf("arg "); + switch (*arg) { + case FSPEC_ARG_STR: + printf("str %s\n", fspec_arg_get_cstr(arg, code->data)); + break; + + case FSPEC_ARG_VAR: + printf("var %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg)); + break; + + case FSPEC_ARG_NUM: + printf("num %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg)); + break; + + case FSPEC_ARG_OFF: + printf("off %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg)); + break; + + case FSPEC_ARG_DAT: + printf("dat %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg)); + break; + + case FSPEC_ARG_EOF: + printf("eof\n"); + break; + + case FSPEC_ARG_LAST: + break; + } + } + break; - if (!to) { - static const char *sys_encoding; - if (!sys_encoding) { - setlocale(LC_ALL, ""); - sys_encoding = nl_langinfo(CODESET); + case FSPEC_OP_LAST: + break; } + } +} - to = sys_encoding; +static const enum fspec_op* +get_last_struct(const struct code *code) +{ + const enum fspec_op *last = NULL; + for (const enum fspec_op *op = code->start; op; op = fspec_op_next(op, code->end, true)) { + const enum fspec_arg *arg; + if (*op == FSPEC_OP_DECLARATION && + (arg = fspec_op_get_arg(op, code->end, 1, 1<data = realloc(buf->data, size))) + err(EXIT_FAILURE, "realloc(%zu)", size); - if (iconv(iv, (char**)&in, &in_left, &out, &out_left) == (size_t)-1) - err(EXIT_FAILURE, "iconv(%s, %s)", to, from); + buf->len = size; +} - print_chars(enc, 1, sizeof(enc) - out_left); - } while (in_left > 0); +static inline void +dynbuf_resize_if_needed(struct dynbuf *buf, const size_t size) +{ + if (buf->len >= size) + return; - iconv_close(iv); - puts(""); + dynbuf_resize(buf, size); } -struct container; -struct field { - struct fspec_field f; - struct container *c, *link; - uint64_t value; -}; +static inline void +dynbuf_grow_if_needed(struct dynbuf *buf, const size_t nmemb) +{ + assert(buf); + if (buf->len >= nmemb && buf->written <= buf->len - nmemb) + return; -struct container { - struct fspec_container c; - struct field fields[255]; - size_t num_fields; -}; + dynbuf_resize(buf, buf->written + nmemb); +} -static size_t -field_get_buffer(const struct field *field, FILE *f, char **buf) +static inline void +dynbuf_append(struct dynbuf *buf, const void *data, const size_t data_sz) { - 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); + dynbuf_grow_if_needed(buf, data_sz); + memcpy((char*)buf->data + buf->written, data, data_sz); + buf->written += data_sz; + assert(buf->written <= buf->len); +} - if (fread(*buf, field->f.type.size, field->f.array.nmemb, f) != field->f.array.nmemb) - return 0; +static inline void +dynbuf_reset(struct dynbuf *buf) +{ + assert(buf); + buf->written = 0; +} - return field->f.array.nmemb; +static inline void +dynbuf_release(struct dynbuf *buf) +{ + assert(buf); + free(buf->data); + *buf = (struct dynbuf){0}; +} - 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); +static void +display(const void *buf, const size_t size, const size_t nmemb, const bool is_signed, const enum fspec_visual visual) +{ + switch (visual) { + case FSPEC_VISUAL_NUL: + puts("..."); + break; - assert(off < len); - if (fread(*buf + off, 1, 1, f) != 1) - return 0; + case FSPEC_VISUAL_STR: + print_str(buf, size, nmemb); + break; - if (off >= msz && !memcmp(field->f.array.match.data, *buf + off - msz, msz)) - break; - } + case FSPEC_VISUAL_HEX: + print_array(buf, size, nmemb, print_hex); + break; - (*buf)[off] = 0; - return off; - } + case FSPEC_VISUAL_DEC: + print_array(buf, size, nmemb, (is_signed ? print_sdec : print_udec)); 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; - } + case FSPEC_VISUAL_LAST: break; } - - return 0; } +struct decl { + struct dynbuf buf; + const char *name; + const void *start, *end; + size_t nmemb; + uint8_t size; + enum fspec_visual visual; + enum fspec_declaration declaration; +}; + static void -container_process(struct container *container, FILE *f); +decl_display(const struct decl *decl) +{ + assert(decl); + assert(decl->size * decl->nmemb <= decl->buf.len); + printf("%s: ", decl->name); + display(decl->buf.data, decl->size, decl->nmemb, false, decl->visual); +} + +static fspec_num +decl_get_num(const struct decl *decl) +{ + assert(decl); + assert(decl->nmemb == 1); + assert(decl->size * decl->nmemb <= decl->buf.len); + char hex[2 * sizeof(fspec_num) + 1]; + to_hex(decl->buf.data, decl->size, hex, sizeof(hex), true); + static_assert(sizeof(fspec_num) <= sizeof(uint64_t), "fspec_num is larger than uint64_t"); + return (fspec_num)strtoull(hex, NULL, 16); +} + +static const char* +decl_get_cstr(const struct decl *decl) +{ + assert(decl); + return decl->buf.data; +} + +struct context { + struct code code; + struct decl *decl; + fspec_num decl_count; +}; + +static fspec_num +var_get_num(const struct context *context, const enum fspec_arg *arg) +{ + assert(context && arg); + return decl_get_num(&context->decl[fspec_arg_get_num(arg)]); +} + +static const char* +var_get_cstr(const struct context *context, const enum fspec_arg *arg) +{ + assert(context && arg); + return decl_get_cstr(&context->decl[fspec_arg_get_num(arg)]); +} + +enum type { + TYPE_NUM, + TYPE_STR, +}; + +static enum type +var_get_type(const struct context *context, const enum fspec_arg *arg) +{ + assert(context && arg); + const struct decl *decl = &context->decl[fspec_arg_get_num(arg)]; + switch (decl->visual) { + case FSPEC_VISUAL_DEC: + case FSPEC_VISUAL_HEX: + case FSPEC_VISUAL_NUL: + return TYPE_NUM; + + case FSPEC_VISUAL_STR: + return TYPE_STR; + + case FSPEC_VISUAL_LAST: + break; + } + return ~0; +} static void -field_process(struct field *field, FILE *f) +filter_decompress(const struct context *context, struct decl *decl) { - assert(field && f); + assert(decl); - char *buf = NULL; - const size_t nmemb = field_get_buffer(field, f, &buf); + const enum fspec_arg *arg; + if (!(arg = fspec_op_get_arg(context->code.start, context->code.end, 2, 1<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); + SquashCodec *codec; + const char *algo = fspec_arg_get_cstr(arg, context->code.data); + if (!(codec = squash_get_codec(algo))) + errx(EXIT_FAILURE, "unknown compression '%s'", algo); - 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); + SquashOptions *opts; + if (!(opts = squash_options_new(codec, NULL))) + errx(EXIT_FAILURE, "squash_options_new"); + + size_t dsize = squash_codec_get_uncompressed_size(codec, decl->buf.len, decl->buf.data); + dsize = (dsize ? dsize : decl->buf.len * 2); + + { + const enum fspec_arg *var = arg; + if ((arg = fspec_arg_next(arg, context->code.end, 1, 1<f.type.size, hex, sizeof(hex), true); - field->value = strtoull(hex, NULL, 16); + for (; (var = fspec_arg_next(var, context->code.end, 1, 1<code.data); + if (!(var = fspec_arg_next(var, context->code.end, 1, ~0))) + errx(EXIT_FAILURE, "expected argument for key '%s'", key); + + switch (*var) { + case FSPEC_ARG_STR: + squash_options_set_string(opts, key, fspec_arg_get_cstr(var, context->code.data)); + break; + + case FSPEC_ARG_NUM: + squash_options_set_int(opts, key, fspec_arg_get_num(var)); + break; + + case FSPEC_ARG_VAR: + if (var_get_type(context, var) == TYPE_STR) { + squash_options_set_string(opts, key, var_get_cstr(context, var)); + } else { + squash_options_set_int(opts, key, var_get_num(context, var)); + } + break; + + default: + break; + } } } - free(buf); + // what a horrible api + squash_object_ref(opts); + + SquashStatus r; + struct dynbuf buf = {0}; + dynbuf_resize(&buf, dsize); + while ((r = squash_codec_decompress_with_options(codec, &buf.len, buf.data, decl->buf.len, decl->buf.data, opts)) == SQUASH_BUFFER_FULL) + dynbuf_resize(&buf, dsize *= 2); + + dynbuf_resize_if_needed(&buf, (buf.written = buf.len)); + squash_object_unref(opts); + + if (r != SQUASH_OK) + errx(EXIT_FAILURE, "squash_codec_decompress(%zu, %zu) = %d: %s", dsize, decl->buf.len, r, squash_status_to_string(r)); + + dynbuf_release(&decl->buf); + decl->buf = buf; + decl->nmemb = buf.len / decl->size; } static void -container_process(struct container *container, FILE *f) +filter_decode(const struct context *context, struct decl *decl) { - assert(container && f); + assert(decl); - for (size_t i = 0; i < container->num_fields; ++i) - field_process(&container->fields[i], f); -} + const enum fspec_arg *arg; + if (!(arg = fspec_op_get_arg(context->code.start, context->code.end, 2, 1<member) - offsetof(type, member))) + const char *encoding = fspec_arg_get_cstr(arg, context->code.data); -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 const char *sys_encoding; + if (!sys_encoding) { + setlocale(LC_ALL, ""); + sys_encoding = nl_langinfo(CODESET); + } + + iconv_t iv; + if ((iv = iconv_open(sys_encoding, encoding)) == (iconv_t)-1) + err(EXIT_FAILURE, "iconv_open(%s, %s)", sys_encoding, encoding); + + struct dynbuf buf = {0}; + const uint8_t *in = decl->buf.data; + size_t in_left = decl->buf.written; + do { + char enc[1024], *out = enc; + size_t out_left = sizeof(enc); + + errno = 0; + if (iconv(iv, (char**)&in, &in_left, &out, &out_left) == (size_t)-1 && errno != E2BIG) + err(EXIT_FAILURE, "iconv(%s, %s)", sys_encoding, encoding); + + dynbuf_append(&buf, enc, sizeof(enc) - out_left); + } while (in_left > 0); + + iconv_close(iv); + + dynbuf_release(&decl->buf); + decl->buf = buf; + decl->nmemb = buf.len / decl->size; +} static void -fspec_field(struct fspec *fspec, const struct fspec_container *container, const struct fspec_field *field) +call(const struct context *context, FILE *f) { - assert(fspec && container); - struct fspec_file *f = container_of(fspec, struct fspec_file, fspec); + assert(context && f); - if (!f->num_containers || memcmp(container, &f->containers[f->num_containers - 1].c, sizeof(*container))) - f->containers[f->num_containers++].c = *container; + struct decl *decl = NULL; + for (const enum fspec_op *op = context->code.start; op; op = fspec_op_next(op, context->code.end, true)) { + if (decl && op == decl->end) { + decl_display(decl); + decl = NULL; + } - struct container *c = &f->containers[f->num_containers - 1]; + switch (*op) { + case FSPEC_OP_DECLARATION: + { + const enum fspec_arg *arg; + arg = fspec_op_get_arg(op, context->code.end, 2, 1<decl[fspec_arg_get_num(arg)]; + dynbuf_reset(&decl->buf); + } + break; + + case FSPEC_OP_READ: + { + assert(decl); + const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<size = fspec_arg_get_num(arg) / 8; + decl->nmemb = 0; + + for (const enum fspec_arg *var = arg; (var = fspec_arg_next(var, context->code.end, 1, ~0));) { + switch (*var) { + case FSPEC_ARG_NUM: + case FSPEC_ARG_VAR: + { + const fspec_num v = (*var == FSPEC_ARG_NUM ? fspec_arg_get_num(var) : var_get_num(context, var)); + if (v == 0) { + goto noop; + } else if (v > 1) { + const size_t nmemb = (decl->nmemb ? decl->nmemb : 1) * v; + dynbuf_grow_if_needed(&decl->buf, decl->size * nmemb); + const size_t read = fread((char*)decl->buf.data + decl->buf.written, decl->size, nmemb, f); + decl->buf.written += decl->size * read; + decl->nmemb += read; + } + } + break; + + case FSPEC_ARG_STR: + break; + + case FSPEC_ARG_EOF: + { + const size_t nmemb = (decl->nmemb ? decl->nmemb : 1); + size_t read = 0, r = nmemb; + while (r == nmemb) { + dynbuf_grow_if_needed(&decl->buf, decl->size * nmemb); + read += (r = fread((char*)decl->buf.data + decl->buf.written, decl->size, nmemb, f)); + decl->buf.written += decl->size * r; + }; + decl->nmemb += read; + } + break; + + default: + break; + } + } +noop: + + if (!fspec_arg_next(arg, context->code.end, 1, ~0)) { + dynbuf_grow_if_needed(&decl->buf, decl->size * 1); + const size_t read = fread((char*)decl->buf.data + decl->buf.written, decl->size, 1, f); + decl->buf.written += decl->size * read; + decl->nmemb = read; + } + } + break; + + case FSPEC_OP_GOTO: + { + decl = NULL; + const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<decl[fspec_arg_get_num(arg)]; + struct context c = *context; + c.code.start = d->start; + c.code.end = d->end; + + for (const enum fspec_arg *var = arg; (var = fspec_arg_next(var, context->code.end, 1, ~0));) { + switch (*var) { + case FSPEC_ARG_NUM: + case FSPEC_ARG_VAR: + { + const fspec_num v = (*var == FSPEC_ARG_NUM ? fspec_arg_get_num(var) : var_get_num(context, var)); + for (fspec_num i = 0; i < v; ++i) + call(&c, f); + } + break; + + // XXX: How to handle STR with stdin? + // With fseek would be easy. + case FSPEC_ARG_STR: + break; + + case FSPEC_ARG_EOF: + while (!feof(f)) + call(&c, f); + break; + + default: + break; + } + } + + if (!fspec_arg_next(arg, context->code.end, 1, ~0)) + call(&c, f); + } + break; + + case FSPEC_OP_FILTER: + { + assert(decl); + const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<code.data); + for (size_t i = 0; i < ARRAY_SIZE(map); ++i) { + if (!strcmp(filter, map[i].name)) { + struct context c = *context; + c.code.start = op; + map[i].fun(&c, decl); + break; + } + + if (i == ARRAY_SIZE(map) - 1) + warnx("unknown filter '%s'", filter); + } + } + break; - 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; + case FSPEC_OP_VISUAL: + { + assert(decl); + const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<visual = fspec_arg_get_num(arg); + } + break; - c->fields[c->num_fields].link = &f->containers[i]; - break; + case FSPEC_OP_ARG: + case FSPEC_OP_HEADER: + case FSPEC_OP_LAST: + break; } } - c->fields[c->num_fields].c = c; - c->fields[c->num_fields++].f = *field; + if (decl && context->code.end == decl->end) + decl_display(decl); } -static size_t -fspec_read(struct fspec *fspec, char *buf, const size_t size, const size_t nmemb) +static void +setup(const struct context *context) { - assert(fspec && buf); - struct fspec_file *f = container_of(fspec, struct fspec_file, fspec); - return fread(buf, size, nmemb, f->handle); + assert(context); + + for (const enum fspec_op *op = context->code.start; op; op = fspec_op_next(op, context->code.end, true)) { + switch (*op) { + case FSPEC_OP_DECLARATION: + { + const enum fspec_arg *arg[4]; + arg[0] = fspec_op_get_arg(op, context->code.end, 1, 1<code.end, 1, 1<code.end, 1, 1<code.end, 1, 1<decl[id]; + decl->declaration = fspec_arg_get_num(arg[0]); + decl->name = fspec_arg_get_cstr(arg[3], context->code.data); + decl->visual = FSPEC_VISUAL_DEC; + decl->start = op; + decl->end = (char*)op + fspec_arg_get_num(arg[2]); + assert(!decl->buf.data); + } + break; + + default: + break; + } + } +} + +static void +execute(const struct fspec_mem *mem) +{ + assert(mem); + + struct context context = { + .code.start = mem->data, + .code.end = (void*)((char*)mem->data + mem->len), + .code.data = mem->data + }; + + printf("output: %zu bytes\n", mem->len); + dump_ops(&context.code); + + const enum fspec_arg *arg = fspec_op_get_arg(context.code.data, context.code.end, 2, 1<member) - offsetof(type, member))) + +struct lexer { + struct fspec_lexer lexer; + FILE *file; +}; + +static size_t +fspec_lexer_read(struct fspec_lexer *lexer, void *ptr, const size_t size, const size_t nmemb) +{ + assert(lexer && ptr); + struct lexer *l = container_of(lexer, struct lexer, lexer); + return fread(ptr, size, nmemb, l->file); +} + +static size_t +fspec_validator_read(struct fspec_validator *validator, void *ptr, const size_t size, const size_t nmemb) +{ + assert(validator && ptr); + assert(ptr == validator->mem.input.data); + const size_t read = validator->mem.input.len / size; + assert((validator->mem.input.len && read == nmemb) || (!validator->mem.input.len && !read)); + validator->mem.input.len -= read * size; + assert(validator->mem.input.len == 0); + return read; +} + int main(int argc, const char *argv[]) { if (argc < 2) - errx(EXIT_FAILURE, "usage: %s file.spec < data\n", argv[0]); + errx(EXIT_FAILURE, "usage: %s file.spec < data", argv[0]); + + char output[4096]; + struct fspec_mem bcode = {0}; + + { + char input[4096]; + struct lexer l = { + .lexer = { + .ops.read = fspec_lexer_read, + .mem.input = { .data = input, sizeof(input) }, + .mem.output = { .data = output, sizeof(output) }, + }, + .file = fopen_or_die(argv[1], "rb"), + }; - uint8_t data[4096] = {0}; + if (!fspec_lexer_parse(&l.lexer, argv[1])) + exit(EXIT_FAILURE); - 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"), - }; + fclose(l.file); + bcode = l.lexer.mem.output; + } - fspec_parse(&file.fspec); + { + struct fspec_validator validator = { + .ops.read = fspec_validator_read, + .mem.input = bcode, + }; - if (!file.num_containers) - errx(EXIT_FAILURE, "'%s' contains no containers", argv[1]); + if (!fspec_validator_parse(&validator, argv[1])) + exit(EXIT_FAILURE); + } - container_process(&file.containers[file.num_containers - 1], stdin); - fclose(file.handle); + execute(&bcode); return EXIT_SUCCESS; } -- cgit v1.2.3