exhaustively check for errors
This commit is contained in:
parent
66f196db6b
commit
0ee4adcb09
1 changed files with 79 additions and 67 deletions
142
compressor.c
142
compressor.c
|
@ -1,7 +1,8 @@
|
||||||
|
//#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
//#include <assert.h>
|
|
||||||
|
|
||||||
typedef unsigned char u8;
|
typedef unsigned char u8;
|
||||||
typedef unsigned int u32;
|
typedef unsigned int u32;
|
||||||
|
@ -10,6 +11,21 @@ typedef unsigned int u32;
|
||||||
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
||||||
#define PMOD(a, b) (((a) + (b)) % (b))
|
#define PMOD(a, b) (((a) + (b)) % (b))
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define FMT_SIZE "%Iu"
|
||||||
|
#else
|
||||||
|
#define FMT_SIZE "%zu"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define lament(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
#define error_when(cond, ...) do { \
|
||||||
|
if ((cond) || errno) { \
|
||||||
|
lament(__VA_ARGS__); \
|
||||||
|
lament(": %s\n", strerror(errno)); \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define BUF_LEN 1024
|
#define BUF_LEN 1024
|
||||||
#define MIN_LEN 3
|
#define MIN_LEN 3
|
||||||
#define MAX_LEN 66
|
#define MAX_LEN 66
|
||||||
|
@ -19,9 +35,9 @@ typedef unsigned int u32;
|
||||||
#define RW_OVERLAP 1
|
#define RW_OVERLAP 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
long compress(const u8 *bufi, long size, u8 *bufo) {
|
long compress(const u8 *bufi, long size, u8 *bufo) {
|
||||||
|
// this function cannot fail.
|
||||||
|
// just ensure bufo points to enough memory to hold the compressed data.
|
||||||
u8 buf[BUF_LEN] = {0};
|
u8 buf[BUF_LEN] = {0};
|
||||||
int buf_i = BUF_START;
|
int buf_i = BUF_START;
|
||||||
|
|
||||||
|
@ -48,21 +64,21 @@ long compress(const u8 *bufi, long size, u8 *bufo) {
|
||||||
int buf_off = (match_i + match_len) % BUF_LEN;
|
int buf_off = (match_i + match_len) % BUF_LEN;
|
||||||
#if RW_OVERLAP
|
#if RW_OVERLAP
|
||||||
if (overlap > 0 || (buf_off == buf_i && j != 0)) {
|
if (overlap > 0 || (buf_off == buf_i && j != 0)) {
|
||||||
if (sub[overlap % j] != sub[match_len]) { break; }
|
if (sub[overlap % j] != sub[match_len]) break;
|
||||||
overlap++;
|
overlap++;
|
||||||
} else {
|
} else {
|
||||||
if (buf[buf_off] != sub[match_len]) { break; }
|
if (buf[buf_off] != sub[match_len]) break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (buf_off == buf_i && j != 0) { break; }
|
if (buf_off == buf_i && j != 0) break;
|
||||||
if (buf[buf_off] != sub[match_len]) { break; }
|
if (buf[buf_off] != sub[match_len]) break;
|
||||||
#endif
|
#endif
|
||||||
match_len++;
|
match_len++;
|
||||||
if (match_len == MAX_LEN) { break; }
|
if (match_len == MAX_LEN) break;
|
||||||
if (match_len == sub_len) { break; }
|
if (match_len == sub_len) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match_len < MIN_LEN) { continue; }
|
if (match_len < MIN_LEN) continue;
|
||||||
if (match_len > best_len) {
|
if (match_len > best_len) {
|
||||||
best_i = match_i;
|
best_i = match_i;
|
||||||
best_len = match_len;
|
best_len = match_len;
|
||||||
|
@ -121,87 +137,83 @@ long compress(const u8 *bufi, long size, u8 *bufo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int compress_file(const char *fp) {
|
int compress_file(const char *fp) {
|
||||||
// TODO: better error handling.
|
FILE *f = NULL;
|
||||||
// functions here that can fail:
|
u8 *bufi = NULL;
|
||||||
// fclose
|
u8 *bufo = NULL;
|
||||||
// fopen
|
char fs_buf[4] = {0};
|
||||||
// fseek
|
long size = 0;
|
||||||
// ftell
|
long new_size = 0;
|
||||||
// fwrite
|
|
||||||
// malloc
|
|
||||||
// calloc
|
|
||||||
// free?
|
|
||||||
|
|
||||||
FILE *f = fopen(fp, "rb");
|
errno = 0;
|
||||||
if (f == NULL) {
|
|
||||||
perror(program_name);
|
f = fopen(fp, "rb");
|
||||||
return 1;
|
error_when(f == NULL, "Error opening file: %s", fp);
|
||||||
|
|
||||||
|
error_when(fseek(f, 0, SEEK_END) != 0, "Error seeking in file: %s", fp);
|
||||||
|
size = ftell(f);
|
||||||
|
error_when(size < 0, "Error telling in file: %s", fp);
|
||||||
|
error_when(fseek(f, 0, SEEK_SET) != 0, "Error seeking in file: %s", fp);
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
bufi = (u8 *)malloc(size);
|
||||||
|
error_when(bufi == NULL, "Error allocating %li bytes", size);
|
||||||
|
error_when(fread(bufi, 1, size, f) != (size_t)size, "Error reading %li bytes from file: %s", size, fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
error_when(fclose(f) != 0, "Error closing file: %s", fp);
|
||||||
long size = ftell(f);
|
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 *bufi = (u8 *)malloc(size);
|
|
||||||
if (bufi == NULL) {
|
|
||||||
fprintf(stderr, "failed to malloc %li bytes\n", size);
|
|
||||||
free(bufi);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
fread(bufi, 1, size, f);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
// allocate enough for the worst case scenario.
|
// allocate enough for the worst case scenario.
|
||||||
size_t bufo_size = size * 9 / 8 + 8;
|
size_t bufo_size = size * 9 / 8 + 8;
|
||||||
u8 *bufo = (u8 *)malloc(bufo_size);
|
bufo = (u8 *)malloc(bufo_size);
|
||||||
|
error_when(bufo == NULL, "Error allocating " FMT_SIZE " bytes", bufo_size);
|
||||||
|
|
||||||
long new_size = compress(bufi, size, bufo);
|
new_size = compress(bufi, size, bufo);
|
||||||
free(bufi);
|
|
||||||
//assert(new_size > 0 && (size_t)new_size < bufo_size);
|
//assert(new_size > 0 && (size_t)new_size < bufo_size);
|
||||||
|
|
||||||
f = fopen(fp, "wb");
|
|
||||||
if (f == NULL) {
|
|
||||||
perror(program_name);
|
|
||||||
free(bufo);
|
|
||||||
return 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char fs_buf[4] = {0};
|
f = fopen(fp, "wb");
|
||||||
|
error_when(f == NULL, "Error opening file: %s", fp);
|
||||||
|
|
||||||
fs_buf[0] = (size >> 24) & 0xFF;
|
fs_buf[0] = (size >> 24) & 0xFF;
|
||||||
fs_buf[1] = (size >> 16) & 0xFF;
|
fs_buf[1] = (size >> 16) & 0xFF;
|
||||||
fs_buf[2] = (size >> 8) & 0xFF;
|
fs_buf[2] = (size >> 8) & 0xFF;
|
||||||
fs_buf[3] = size & 0xFF;
|
fs_buf[3] = size & 0xFF;
|
||||||
fwrite(fs_buf, 1, 4, f);
|
error_when(fwrite(fs_buf, 1, 4, f) != 4, "Error writing %i bytes to file: %s", 4, fp);
|
||||||
|
|
||||||
fwrite(bufo, 1, new_size, f);
|
if (new_size > 0) {
|
||||||
fclose(f);
|
error_when(fwrite(bufo, 1, new_size, f) != (size_t)new_size, "Error writing %i bytes to file: %s", 4, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_when(fclose(f) != 0, "Error closing file: %s", fp);
|
||||||
|
|
||||||
|
free(bufi);
|
||||||
free(bufo);
|
free(bufo);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(bufi);
|
||||||
|
free(bufo);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
if (argc <= 0 || argv == NULL || argv[0] == NULL) {
|
if (argc <= 0 || argv == NULL || argv[0] == NULL) {
|
||||||
fprintf(stderr, "You've met with a terrible fate.\n");
|
lament("You've met with a terrible fate.\n");
|
||||||
exit(1);
|
return 1;
|
||||||
}
|
}
|
||||||
program_name = argv[0];
|
const char *name = argv[0];
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fprintf(stderr, "usage: %s {file}\n", program_name);
|
lament("compressor: compress files in-place for Bomberman 64.\n");
|
||||||
exit(2);
|
lament("usage: %s {file}\n", name);
|
||||||
|
return 1;
|
||||||
} else if (argc == 2) {
|
} else if (argc == 2) {
|
||||||
const char *fp = argv[1];
|
const char *fp = argv[1];
|
||||||
int ret = compress_file(fp);
|
int ret = compress_file(fp);
|
||||||
if (ret != 0) {
|
return ret;
|
||||||
exit(ret + 3);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "too many arguments\n");
|
lament("Error: too many arguments\n");
|
||||||
exit(3);
|
lament("usage: %s {file}\n", name);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue