/* * 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 . */ #include "types.h" #include #include #include #include #include #include #include #include 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 \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; }