/*
* 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;
}