diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2025-03-06 16:37:12 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-11-07 09:55:58 +0100 |
| commit | 0086ccf0272fa195dca36845fde9c54149c72f0e (patch) | |
| tree | dc025e30b7e0a6830e44c9558f3b800295cc79df /main.c | |
| download | nwc-0086ccf0272fa195dca36845fde9c54149c72f0e.tar.gz | |
First commit
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 1228 |
1 files changed, 1228 insertions, 0 deletions
@@ -0,0 +1,1228 @@ +/* + * nwc, a NanoWasm compiler + * Copyright (C) 2025 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "types.h" +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +static void free_parse(struct parse *const p) +{ + if (!p) + return; + + free(p->to.offsets); + free(p->fti.offsets); + free(p->fbo.offsets); + free(p->iti.offsets); + + if (p->lo) + for (size_t i = 0; i < p->n_lo; i++) + free(p->lo[i].entries); + + free(p->lo); +} + +static char *mktmppath(const char *const out) +{ + char *path = NULL; + static const char fmt[] = "%s.nwc.tmp"; + const int n = snprintf(NULL, 0, fmt, out); + + if (n < 0) + { + fprintf(stderr, "%s: snprintf(3) failed\n", __func__); + goto failure; + } + + const size_t sz = n +1; + + if (!(path = malloc(sz))) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + const int nn = snprintf(path, sz, fmt, out); + + if (nn < 0 || nn >= sz) + { + fprintf(stderr, "%s: snprintf(3) failed with %d\n", __func__, nn); + goto failure; + } + + return path; + +failure: + free(path); + return NULL; +} + +static int dump(FILE *const in, FILE *const out) +{ + do + { + char buf[BUFSIZ]; + const size_t r = fread(buf, 1, sizeof buf, in); + + if (ferror(in)) + { + fprintf(stderr, "%s: fread(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const size_t w = fwrite(buf, r, 1, out); + + if (ferror(out)) + { + fprintf(stderr, "%s: fwrite(3): %s\n", __func__, strerror(errno)); + return -1; + } + } while (!feof(in)); + + return 0; +} + +static int dump_n(const char *const name, const struct offsets *const o, + FILE *const f) +{ + const varuint32 name_len = strlen(name), + payload_len = len_varuint32(name_len) + name_len + + (o->n * sizeof *o->offsets); + + if (write_varuint7(f, SECTION_CUSTOM)) + { + fprintf(stderr, "%s: write_varuint7 failed\n", __func__); + return -1; + } + else if (write_varuint32(f, payload_len)) + { + fprintf(stderr, "%s: write_varuint32 failed\n", __func__); + return -1; + } + else if (write_varuint32(f, name_len)) + { + fprintf(stderr, "%s: write_varuint32 failed\n", __func__); + return -1; + } + else if (fprintf(f, "%s", name) < 0) + { + fprintf(stderr, "%s: fprintf(3) failed\n", __func__); + return -1; + } + + for (size_t i = 0; i < o->n; i++) + { + const uint32_t off = o->offsets[i]; + const uint8_t v[] = {off, off >> 8, off >> 16, off >> 24}; + + if (!fwrite(&v, sizeof v, 1, f)) + { + fprintf(stderr, "%s: fwrite(3) failed, ferror(3)=%d, feof(3)=%d\n", + __func__, ferror(f), feof(f)); + return -1; + } + } + + return 0; +} + +static int dump_lo_fn(const struct lo *const lo, const size_t n_lo, + FILE *const f) +{ + long off = ftell(f); + + if (off < 0) + { + fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + off += n_lo * sizeof (uint32_t); + + for (size_t i = 0; i < n_lo; i++) + { + const struct lo *const o = &lo[i]; + const uint8_t v[] = {off, off >> 8, off >> 16, off >> 25}; + + if (!fwrite(v, sizeof v, 1, f)) + { + fprintf(stderr, "%s: fwrite(3) failed, ferror=%d, feof=%d\n", + __func__, ferror(f), feof(f)); + return -1; + } + + const struct lo_entry *const e = o->entries; + static const size_t sz = sizeof e->pc + sizeof e->dst; + + off += sizeof (uint32_t) + o->n * sz; + } + + return 0; +} + +static int dump_lo_entry(const struct lo_entry *const e, FILE *const f) +{ + const uint32_t p = e->pc; + const uint8_t pv[] = {p, p >> 8, p >> 16, p >> 24}; + + if (!fwrite(pv, sizeof pv, 1, f)) + { + fprintf(stderr, "%s: fwrite(3) failed, ferror(3)=%d, " + "feof(3)=%d\n", __func__, ferror(f), feof(f)); + return -1; + } + + const uint32_t d = e->dst; + const uint8_t dv[] = {d, d >> 8, d >> 16, d >> 24}; + + if (!fwrite(dv, sizeof dv, 1, f)) + { + fprintf(stderr, "%s: fwrite(3) failed, ferror(3)=%d, " + "feof(3)=%d\n", __func__, ferror(f), feof(f)); + return -1; + } + + return 0; +} + +static int dump_lo_off(const struct lo *const lo, const size_t n_lo, + FILE *const f) +{ + for (size_t i = 0; i < n_lo; i++) + { + const struct lo *const o = &lo[i]; + const uint8_t n[] = {o->n, o->n >> 8, o->n >> 16, o->n >> 24}; + + if (!fwrite(n, sizeof n, 1, f)) + { + fprintf(stderr, "%s: fwrite(3) failed, ferror(3)=%d, feof(3)=%d\n", + __func__, ferror(f), feof(f)); + return -1; + } + + for (size_t i = 0; i < o->n; i++) + if (dump_lo_entry(&o->entries[i], f)) + { + fprintf(stderr, "%s: dump_lo_entry %zu failed\n", __func__, i); + return -1; + } + } + + return 0; +} + +static int dump_lo(const struct lo *const lo, const size_t n_lo, + FILE *const f) +{ + static const char name[] = "nw_lo"; + varuint32 name_len = strlen(name), + payload_len = len_varuint32(name_len) + name_len + + n_lo * sizeof (uint32_t); + + for (size_t i = 0; i < n_lo; i++) + { + const struct lo *const o = &lo[i]; + const size_t entrysz = sizeof lo->entries->dst + sizeof lo->entries->pc; + + payload_len += sizeof (uint32_t) + o->n * entrysz; + } + + if (write_varuint7(f, SECTION_CUSTOM)) + { + fprintf(stderr, "%s: write_varuint7 failed\n", __func__); + return -1; + } + else if (write_varuint32(f, payload_len)) + { + fprintf(stderr, "%s: write_varuint32 failed\n", __func__); + return -1; + } + else if (write_varuint32(f, name_len)) + { + fprintf(stderr, "%s: write_varuint32 failed\n", __func__); + return -1; + } + else if (fprintf(f, "%s", name) < 0) + { + fprintf(stderr, "%s: fprintf(3) failed\n", __func__); + return -1; + } + else if (dump_lo_fn(lo, n_lo, f)) + { + fprintf(stderr, "%s: dump_lo_fn failed\n", __func__); + return -1; + } + else if (dump_lo_off(lo, n_lo, f)) + { + fprintf(stderr, "%s: dump_lo_off failed\n", __func__); + return -1; + } + + return 0; +} + +static int dump_parse(const struct parse *const p, FILE *const f) +{ + const struct n + { + const char *name; + const struct offsets *o; + } ns[] = + { + {.name = "nw_to", .o = &p->to}, + {.name = "nw_fti", .o = &p->fti}, + {.name = "nw_fbo", .o = &p->fbo}, + {.name = "nw_iti", .o = &p->iti} + }; + + for (size_t i = 0; i < sizeof ns / sizeof *ns; i++) + { + const struct n *const n = &ns[i]; + + if (dump_n(n->name, n->o, f)) + { + fprintf(stderr, "%s: dump_n %s failed\n", __func__, n->name); + return -1; + } + } + + if (dump_lo(p->lo, p->n_lo, f)) + { + fprintf(stderr, "%s: dump_lo failed\n", __func__); + return -1; + } + + return 0; +} + +static char *gentmp(FILE *const in, const char *out, + const struct parse *const p) +{ + FILE *f = NULL; + char *ret = NULL; + char *const path = mktmppath(out); + + if (!path) + { + fprintf(stderr, "%s: mkttmppath failed\n", __func__); + goto end; + } + else if (!(f = fopen(path, "wb"))) + { + fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, path, + strerror(errno)); + goto end; + } + else if (fseek(in, 0, SEEK_SET)) + { + fprintf(stderr, "%s: fseek(3): %s\n", __func__, strerror(errno)); + goto end; + } + else if (dump(in, f)) + { + fprintf(stderr, "%s: dump failed\n", __func__); + goto end; + } + else if (dump_parse(p, f)) + { + fprintf(stderr, "%s: dump_parse failed\n", __func__); + goto end; + } + + ret = path; + +end: + + if (f && fclose(f)) + { + fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, path, + strerror(errno)); + ret = NULL; + } + + if (!ret && remove(path)) + { + fprintf(stderr, "%s: remove(3) %s: %s\n", __func__, path, + strerror(errno)); + } + + if (!ret) + free(path); + + return ret; +} + +static int check_header(FILE *const f) +{ + uint8_t magic[sizeof "asm"], version[sizeof (uint32_t)]; + static const uint8_t exp[sizeof version] = {1, 0, 0, 0}; + + if (!fread(magic, sizeof magic, 1, f)) + { + fprintf(stderr, "%s: fread(3) magic failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + else if (memcmp(magic, "\0asm", sizeof magic)) + { + fprintf(stderr, "%s: invalid magic number\n", __func__); + return -1; + } + else if (!fread(version, sizeof version, 1, f)) + { + fprintf(stderr, "%s: fread(3) version failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + else if (memcmp(version, exp, sizeof version)) + { + fprintf(stderr, "%s: invalid version number\n", __func__); + return -1; + } + + return 0; +} + +struct section +{ + varuint7 code; + varuint32 len; + char *name; +}; + +static char *read_name(FILE *const f) +{ + varuint32 name_len; + char *name = malloc(1); + + if (!name) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + else if (read_varuint32(f, &name_len)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + goto failure; + } + + for (varuint32 i = 0; i < name_len; i++) + { + uint8_t c; + const size_t r = fread(&c, sizeof c, 1, f); + + if (!r) + { + fprintf(stderr, "%s: fread(3) failed, ferror(3)=%d, feof(3)=%d\n", + __func__, ferror(f), feof(f)); + goto failure; + } + + char *const newname = realloc(name, i + 2); + + if (!newname) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + name = newname; + name[i] = c; + } + + name[name_len] = '\0'; + return name; + +failure: + free(name); + return NULL; +} + +static int parse_header(FILE *const f, struct section *const s) +{ + varuint7 code; + varuint32 payload_len; + char *name = NULL; + long before, after; + + if (read_varuint7(f, &code)) + { + /* Exceptionally, this might not be a fatal error. */ + if (!feof(f)) + fprintf(stderr, "%s: read_varuint7 failed\n", __func__); + + goto failure; + } + else if (read_varuint32(f, &payload_len)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + goto failure; + } + else if ((before = ftell(f)) < 0) + { + fprintf(stderr, "%s: ftell(3) before: %s\n", __func__, strerror(errno)); + goto failure; + } + else if (!code && !(name = read_name(f))) + { + fprintf(stderr, "%s: read_name failed\n", __func__); + goto failure; + } + else if ((after = ftell(f)) < 0) + { + fprintf(stderr, "%s: ftell(3) after: %s\n", __func__, strerror(errno)); + goto failure; + } + + const unsigned long sz = after - before; + + if (payload_len < sz) + { + fprintf(stderr, "%s: expected payload_len >= %lu, got %lu\n", + __func__, sz, (unsigned long)payload_len); + goto failure; + } + + payload_len -= sz; + + *s = (const struct section) + { + .code = code, + .len = payload_len, + .name = name + }; + + return 0; + +failure: + free(name); + return -1; +} + +static int append(const varuint32 value, struct offsets *const o) +{ + const size_t n = o->n + 1; + uint32_t *const v = realloc(o->offsets, n * sizeof *o->offsets); + + if (!v) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + v[o->n++] = value; + o->offsets = v; + return 0; +} + +static int read_type(FILE *const f, struct parse *const p) +{ + const long offset = ftell(f); + + if (offset < 0) + { + fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (read_varint7(f, &(varint7){0})) + { + fprintf(stderr, "%s: read_varint7 failed\n", __func__); + return -1; + } + + varuint32 param_count; + + if (read_varuint32(f, ¶m_count)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < param_count; i++) + if (read_varint7(f, &(value_type){0})) + { + fprintf(stderr, "%s: read_varint7 failed\n", __func__); + return -1; + } + + varuint1 return_count; + value_type return_type; + + if (read_varuint1(f, &return_count)) + { + fprintf(stderr, "%s: read_varuint1 failed\n", __func__); + return -1; + } + else if (return_count && read_varint7(f, &return_type)) + { + fprintf(stderr, "%s: read_varint7 failed\n", __func__); + return -1; + } + else if (append(offset, &p->to)) + { + fprintf(stderr, "%s: append failed\n", __func__); + return -1; + } + + return 0; +} + +static int readstr(FILE *const f, struct parse *const p) +{ + varuint32 len; + + if (read_varuint32(f, &len)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < len; i++) + { + uint8_t b; + + if (!fread(&b, sizeof b, 1, f)) + { + fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + } + + return 0; +} + +static int read_resizable_limits(FILE *const f) +{ + varuint1 flags; + + if (read_varuint1(f, &flags)) + { + fprintf(stderr, "%s: read_varuint1 failed\n", __func__); + return -1; + } + else if (read_varuint32(f, &(varuint32){0})) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + else if (flags && read_varuint32(f, &(varuint32){0})) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + return 0; +} + +static int kind_function(FILE *const f) +{ + if (read_varuint32(f, &(varuint32){0})) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + return 0; +} + +static int kind_table(FILE *const f) +{ + enum {ANYFUNC = 0x70}; + value_type elem_type; + + if (read_varint7(f, &elem_type)) + { + fprintf(stderr, "%s: read_varint7 failed\n", __func__); + return -1; + } + else if (elem_type != ANYFUNC) + { + fprintf(stderr, "%s: invalid elem_type, expected=%#x, got=%#hhx\n", + __func__, ANYFUNC, (unsigned char)elem_type); + return -1; + } + else if (read_resizable_limits(f)) + { + fprintf(stderr, "%s: read_resizable_limits failed\n", __func__); + return -1; + } + + return 0; +} + +static int kind_memory(FILE *const f) +{ + return read_resizable_limits(f); +} + +static int kind_global(FILE *const f) +{ + if (read_varint7(f, &(varint7){0})) + { + fprintf(stderr, "%s: read_varint7 failed\n", __func__); + return -1; + } + else if (read_varuint1(f, &(varuint1){0})) + { + fprintf(stderr, "%s: read_varuint1 failed\n", __func__); + return -1; + } + + return 0; +} + +static int read_external_kind(FILE *const f, struct parse *const p) +{ + enum + { + FUNCTION, + TABLE, + MEMORY, + GLOBAL, + + TYPES + }; + + uint8_t external_kind; + + if (!fread(&external_kind, sizeof external_kind, 1, f)) + { + fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + + static int (*const fn[])(FILE *) = + { + [FUNCTION] = kind_function, + [TABLE] = kind_table, + [MEMORY] = kind_memory, + [GLOBAL] = kind_global + }; + + if (external_kind >= sizeof fn / sizeof *fn) + { + fprintf(stderr, "%s: invalid external_kind: %" PRIu8 "\n", __func__, + external_kind); + return -1; + } + else if (fn[external_kind](f)) + { + fprintf(stderr, "%s: callback failed, external_kind: %" PRIu8 "\n", + __func__, external_kind); + return -1; + } + + return 0; +} + +static int read_import(FILE *const f, struct parse *const p) +{ + if (readstr(f, p) || readstr(f, p)) + { + fprintf(stderr, "%s: readstr failed\n", __func__); + return -1; + } + + const long offset = ftell(f); + + if (offset < 0) + { + fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (read_external_kind(f, p)) + { + fprintf(stderr, "%s: read_external_kind failed\n", __func__); + return -1; + } + else if (append(offset, &p->iti)) + { + fprintf(stderr, "%s: append failed\n", __func__); + return -1; + } + + return 0; +} + +static int parse_import(FILE *const f, struct parse *const p) +{ + varuint32 count; + + if (read_varuint32(f, &count)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (read_import(f, p)) + { + fprintf(stderr, "%s: read_import %lu failed\n", __func__, + (unsigned long)i); + return -1; + } + + return 0; +} + +static int parse_type(FILE *const f, struct parse *const p) +{ + varuint32 count; + + if (read_varuint32(f, &count)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (read_type(f, p)) + { + fprintf(stderr, "%s: read_type %lu failed\n", __func__, + (unsigned long)i); + return -1; + } + + return 0; +} + +static int read_function(FILE *const f, struct parse *const p) +{ + varuint32 index; + + if (read_varuint32(f, &index)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + else if (append(index, &p->fti)) + { + fprintf(stderr, "%s: append failed\n", __func__); + return -1; + } + + return 0; +} + +static int parse_function(FILE *const f, struct parse *const p) +{ + varuint32 count; + + if (read_varuint32(f, &count)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (read_function(f, p)) + { + fprintf(stderr, "%s: read_function %lu failed\n", __func__, + (unsigned long)i); + return -1; + } + + return 0; +} + +static int skip(FILE *const f, const struct section *const s) +{ + const int ret = fseek(f, s->len, SEEK_CUR); + + if (ret) + fprintf(stderr, "%s: fseek(3): %s\n", __func__, strerror(errno)); + + return ret; +} + +static int read_local(FILE *const f) +{ + if (read_varuint32(f, &(varuint32){0})) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + else if (read_varint7(f, &(value_type){0})) + { + fprintf(stderr, "%s: read_varint7 failed\n", __func__); + return -1; + } + + return 0; +} + +static int read_code(FILE *const f, struct parse *const p, + const unsigned long len) +{ + long after; + const long before = ftell(f); + + if (before < 0) + { + fprintf(stderr, "%s: ftell(3) before: %s\n", __func__, strerror(errno)); + return -1; + } + else if (read_instr(f, p)) + { + fprintf(stderr, "%s: read_instr failed\n", __func__); + return -1; + } + else if ((after = ftell(f)) < 0) + { + fprintf(stderr, "%s: ftell(3) after: %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long read = after - before; + + if (read != len) + { + fprintf(stderr, "%s: expected function body length: %lu, got %lu\n", + __func__, len, read); + return -1; + } + + return 0; +} + +static int read_body(FILE *const f, struct parse *const p) +{ + varuint32 body_size, local_count; + long before, after; + const long start = ftell(f); + + if (start < 0) + { + fprintf(stderr, "%s: ftell(3) start: %s\n", __func__, strerror(errno)); + return -1; + } + else if (read_varuint32(f, &body_size)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + else if ((before = ftell(f)) < 0) + { + fprintf(stderr, "%s: ftell(3) before: %s\n", __func__, strerror(errno)); + return -1; + } + else if (read_varuint32(f, &local_count)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < local_count; i++) + if (read_local(f)) + { + fprintf(stderr, "%s: read_local %lu failed\n", __func__, + (unsigned long)i); + return -1; + } + + if ((after = ftell(f)) < 0) + { + fprintf(stderr, "%s: ftell(3): after %s\n", __func__, strerror(errno)); + return -1; + } + else if (read_code(f, p, body_size - (after - before))) + { + fprintf(stderr, "%s: read_code failed\n", __func__); + return -1; + } + else if (append(start, &p->fbo)) + { + fprintf(stderr, "%s: append failed\n", __func__); + return -1; + } + + return 0; +} + +static int parse_code(FILE *const f, struct parse *const p) +{ + varuint32 count; + + if (read_varuint32(f, &count)) + { + fprintf(stderr, "%s: read_varuint32 failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (read_body(f, p)) + { + fprintf(stderr, "%s: read_body %lu failed\n", __func__, + (unsigned long)i); + return -1; + } + + return 0; +} + +static int parse_payload(FILE *const f, struct parse *const p, + const struct section *const s) +{ + static const struct fn + { + varuint7 code; + int (*fn)(FILE *, struct parse *); + } fns[] = + { + {.code = SECTION_IMPORT, parse_import}, + {.code = SECTION_TYPE, parse_type}, + {.code = SECTION_FUNCTION, parse_function}, + {.code = SECTION_CODE, parse_code} + }; + + for (size_t i = 0; i < sizeof fns / sizeof *fns; i++) + { + const struct fn *const fn = &fns[i]; + + if (s->code == fn->code) + return fn->fn(f, p); + } + + fprintf(stderr, "%s: unsupported section %hhu\n", __func__, + (unsigned char)s->code); + return -1; +} + +static bool relevant(const varuint7 code) +{ + switch (code) + { + case SECTION_CODE: + case SECTION_FUNCTION: + case SECTION_TYPE: + case SECTION_IMPORT: + return true; + + default: + break; + } + + return false; +} + +static bool repeated(const char *const name) +{ + static const char *const s[] = + { + "nw_lo", + "nw_fbo", + "nw_to", + "nw_iti" + }; + + for (size_t i = 0; i < sizeof s / sizeof *s; i++) + if (!strcmp(name, s[i])) + return true; + + return false; +} + +static int read_section(FILE *const f, struct parse *const p) +{ + int ret = -1; + struct section s = {0}; + + if (parse_header(f, &s)) + { + /* Exceptionally, this might not be a fatal error. */ + if (!feof(f)) + fprintf(stderr, "%s: parse_header failed\n", __func__); + + goto end; + } + else if (!relevant(s.code)) + { + if (s.code == SECTION_CUSTOM && repeated(s.name)) + { + fprintf(stderr, "%s: nw extension %s already present in file\n", + __func__, s.name); + goto end; + } + else if (skip(f, &s)) + { + fprintf(stderr, "%s: skip failed\n", __func__); + goto end; + } + } + else if (parse_payload(f, p, &s)) + { + fprintf(stderr, "%s: parse_payload failed\n", __func__); + goto end; + } + + ret = 0; + +end: + free(s.name); + return ret; +} + +static int parse(FILE *const f, struct parse *const p) +{ + if (check_header(f)) + { + fprintf(stderr, "%s: check_magic failed\n", __func__); + return -1; + } + + for (;;) + if (read_section(f, p)) + { + if (feof(f)) + break; + + fprintf(stderr, "%s: read_section failed\n", __func__); + return -1; + } + + return 0; +} + +static int copy(const char *const old, const char *const new) +{ + int ret = -1; + FILE *const in = fopen(old, "rb"), *const out = fopen(new, "wb"); + + if (!in) + { + fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, old, + strerror(errno)); + goto end; + } + else if (!out) + { + fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, new, + strerror(errno)); + goto end; + } + else if (dump(in, out)) + { + fprintf(stderr, "%s: dump failed\n", __func__); + goto end; + } + + ret = 0; + +end: + + if (in && fclose(in)) + { + fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, old, + strerror(errno)); + ret = -1; + } + + if (out && fclose(out)) + { + fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, new, + strerror(errno)); + ret = -1; + } + + return ret; +} + +static int move(const char *const old, const char *const new) +{ + if (rename(old, new)) + { + if (errno != EXDEV) + { + fprintf(stderr, "%s: rename(3) %s->%s: %s\n", __func__, old, new, + strerror(errno)); + return -1; + } + else if (copy(old, new)) + { + fprintf(stderr, "%s: copy %s->%s failed\n", __func__, old, new); + return -1; + } + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + char *tmp = NULL; + FILE *f = NULL; + struct parse p = {0}; + + if (argc != 3) + { + fprintf(stderr, "%s <infile> <outfile>\n", *argv); + goto end; + } + + const char *const in = argv[1], *const out = argv[2]; + + if (!(f = fopen(in, "rb"))) + { + fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, in, + strerror(errno)); + goto end; + } + else if (parse(f, &p)) + { + fprintf(stderr, "%s: parse failed\n", __func__); + goto end; + } + else if (!(tmp = gentmp(f, out, &p))) + { + fprintf(stderr, "%s: gettmp failed\n", __func__); + goto end; + } + else if (move(tmp, out)) + { + fprintf(stderr, "%s: move %s->%s failed\n", __func__, tmp, out); + goto end; + } + + ret = EXIT_SUCCESS; + +end: + + if (f && fclose(f)) + { + fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, in, + strerror(errno)); + ret = EXIT_FAILURE; + } + + if (ret != EXIT_SUCCESS && tmp && remove(tmp)) + fprintf(stderr, "%s: remove(3) %s: %s\n", __func__, tmp, + strerror(errno)); + + free(tmp); + free_parse(&p); + return ret; +} |
