#include "io.h" #include #include #include #ifdef __unix__ #include #endif static const char* outfile_name; static FILE* infile; static FILE* outfile; // HACK: "here" tracking should be handled by the assembler, not IO. uint32_t here = 0; void open_files(const char* infile_name, const char* outfile_name_) { outfile_name = outfile_name_; infile = fopen(infile_name, "rb"); if (infile == NULL) { fprintf(stderr, "failed to open source file: %s\n", strerror(errno)); exit(1); } outfile = fopen(outfile_name, "wb"); if (outfile == NULL) { fprintf(stderr, "failed to open output file: %s\n", strerror(errno)); exit(1); } } void close_files(void) { fclose(infile); if (fclose(outfile) != 0) { fprintf(stderr, "failed to close output file: %s\n", strerror(errno)); // NOTE: ideally we'd do this on any dirty exit if (remove(outfile_name) != 0) { fprintf(stderr, "failed to remove output file; if it exists, it is corrupt: %s\n", strerror(errno)); } exit(1); } #ifdef __unix__ chmod(outfile_name, 0777); #endif } void reserve(size_t len) { if (fseek(outfile, len, SEEK_CUR) != 0) { fprintf(stderr, "failed to reserve space in in output file: %s\n", strerror(errno)); exit(1); } here += len; } void emit(const void* restrict ptr, size_t count) { fwrite(ptr, 1, count, outfile); if (ferror(outfile)) { fprintf(stderr, "failed to write to output file\n"); exit(1); } here += count; } void emit_u8(uint8_t x) { emit(&x, sizeof(uint8_t)); } void emit_u32(uint32_t x) { emit(&x, sizeof(uint32_t)); } void emit_u64(uint64_t x) { emit(&x, sizeof(uint64_t)); } void patch(size_t off, const void* ptr, size_t count) { fpos_t save; if (fgetpos(outfile, &save) != 0) { fprintf(stderr, "failed to save file position before patch: %s\n", strerror(errno)); exit(1); } if (fseek(outfile, (long) off, SEEK_SET) != 0) { fprintf(stderr, "failed to set file position for patch: %s\n", strerror(errno)); exit(1); } fwrite(ptr, 1, count, outfile); if (ferror(outfile) != 0) { fprintf(stderr, "failed to patch output file: %s\n", strerror(errno)); exit(1); } if (fsetpos(outfile, &save) != 0) { fprintf(stderr, "failed to restore file position after patch: %s\n", strerror(errno)); exit(1); } } void patch_u32(size_t off, uint32_t x) { patch(off, &x, sizeof(uint32_t)); } void patch_i32(size_t off, int32_t x) { patch_u32(off, (uint32_t) x); } #define MAX_LOOKAHEAD 4 static size_t read_buf_len = 0; static char read_buf[MAX_LOOKAHEAD]; char* peek(size_t* len) { if (*len >= MAX_LOOKAHEAD) { fprintf(stderr, "syntax error: maximum lookahead exceeded\n"); exit(1); } if (*len >= read_buf_len) { size_t inc = fread(read_buf + read_buf_len, 1, *len - read_buf_len, infile); if (ferror(infile)) { fprintf(stderr, "failed to read source file: %s\n", strerror(errno)); exit(1); } read_buf_len += inc; *len = read_buf_len; } return read_buf; } void skip(size_t off) { if (read_buf_len > off) { memmove(read_buf, &read_buf[off], read_buf_len - off); read_buf_len -= off; } else { if (read_buf_len < off) { if (fseek(infile, off - read_buf_len, SEEK_CUR) != 0) { fprintf(stderr, "failed to seek in source file: %s\n", strerror(errno)); exit(1); } } read_buf_len = 0; } }