diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2ba7f25 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +LFLAGS = +CFLAGS = + +all: gif + +gif: LFLAGS += -lgif +gif: CFLAGS += -DGIF_SUPPORT +gif: gameoflife + +gameoflife: src/main.o src/field.o src/misc.o src/cli.o src/gif.o + gcc $(LFLAGS) -o $@ $^ + +src/main.o: src/field.h src/misc.h src/cli.h +src/field.o: src/field.h src/misc.h +src/misc.o: src/misc.h +src/cli.o: src/field.h + +%.o: %.c + gcc $(CFLAGS) -c -o $@ $< + +clean: + @rm src/*.o + @rm gameoflife diff --git a/demos/diehard.gol b/demos/diehard.gol new file mode 100644 index 0000000..00e9ff2 --- /dev/null +++ b/demos/diehard.gol @@ -0,0 +1,5 @@ +#50,30; + +27,6; +21,7;22,7; +22,8;26,8;27,8;28,8; diff --git a/demos/glider-gun.gol b/demos/glider-gun.gol new file mode 100644 index 0000000..f45d7ec --- /dev/null +++ b/demos/glider-gun.gol @@ -0,0 +1,23 @@ +#200,200; + +1,6;2,6; +1,7;2,7; + +13,4;14,4; +12,5;16,5; +11,6;17,6; +11,7;15,7;17,7;18,7; +11,8;17,8; +12,9;16,9; +13,10;14,10; + +25,2; +23,3;25,3; +21,4;22,4; +21,5;22,5; +21,6;22,6; +23,7;25,7; +25,8; + +35,4;36,4; +35,5;36,4; diff --git a/demos/glider.gol b/demos/glider.gol new file mode 100644 index 0000000..f91e8e5 --- /dev/null +++ b/demos/glider.gol @@ -0,0 +1,5 @@ +#30,30; + +1,0; +2,1; +0,2;1,2;2,2; diff --git a/demos/lwss.gol b/demos/lwss.gol new file mode 100644 index 0000000..6d34493 --- /dev/null +++ b/demos/lwss.gol @@ -0,0 +1,6 @@ +#70,7; + +0,1;4,1; +5,2; +1,3;5,3; +2,4;3,4;4,4;5,4; diff --git a/demos/pentadecathlon.gol b/demos/pentadecathlon.gol new file mode 100644 index 0000000..f6e9dc3 --- /dev/null +++ b/demos/pentadecathlon.gol @@ -0,0 +1,14 @@ +#11,18; + +4,3;5,3;6,3; +5,4; +5,5; +4,6;5,6;6,6; + +4,8;5,8;6,8; +4,9;5,9;6,9; + +4,11;5,11;6,11; +5,12; +5,13; +4,14;5,14;6,14; diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 0000000..1896d84 --- /dev/null +++ b/src/cli.c @@ -0,0 +1,43 @@ +#include +#include + +#include "cli.h" +#include "field.h" + +cli_settings_t cli_settings; + +void cli_init(cli_settings_t _settings) { + cli_settings = _settings; +} + +int height; +void cli_output(field_t field) { + height = field.height; + if (cli_settings.border) + for (int x = 0; x < field.width + 2; x++) { + printf("#"); + } + printf("\n"); + for (int y = 0; y < field.height; y++) { + if (cli_settings.border) + printf("#"); + for (int x = 0; x < field.width; x++) { + cell_t cell = field.cells[x][y]; + if (cell == UNDEFINED) + cell = DEAD; + printf("%c", cli_settings.charset[cell]); + } + if (cli_settings.border) + printf("#"); + printf("\n"); + } + if (cli_settings.border) + for (int x = 0; x < field.width + 2; x++) { + printf("#"); + } + printf("\n\033[%iA", height + (cli_settings.border ? 2 : 0)); +} + +void cli_end() { + printf("\33[%iB", height + (cli_settings.border ? 2 : 0)); +} diff --git a/src/cli.h b/src/cli.h new file mode 100644 index 0000000..2fd39a4 --- /dev/null +++ b/src/cli.h @@ -0,0 +1,17 @@ +#ifndef CLI_H +#define CLI_H + +#include + +#include "field.h" + +typedef struct { + bool border; + const char* charset; +} cli_settings_t; + +void cli_init(cli_settings_t); +void cli_output(field_t); +void cli_end(); + +#endif diff --git a/src/field.c b/src/field.c new file mode 100644 index 0000000..f46a013 --- /dev/null +++ b/src/field.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +#include "misc.h" +#include "field.h" + +settings_t settings; + +int generationCount; +cell_t*** generations = NULL; + +void initField(settings_t _settings) { + settings = _settings; + + generationCount = settings.periodThreshold; + if (generationCount < 1) + generationCount = 2; + generations = malloc(generationCount * sizeof(cell_t**)); + if (generations == NULL) + error(EXIT_PANIC, "field_setup", NULL); + + for (int g = 0; g < generationCount; g++) { + generations[g] = malloc(settings.dimensions.width * sizeof(cell_t*)); + if (generations[g] == NULL) + error(EXIT_PANIC, "field_setup", NULL); + + for (int x = 0; x < settings.dimensions.width; x++) { + generations[g][x] = malloc(settings.dimensions.height * sizeof(cell_t)); + if (generations[g][x] == NULL) + error(EXIT_PANIC, "field_setup", NULL); + for (int y = 0; y < settings.dimensions.height; y++) { + generations[g][x][y] = g == 0 ? DEAD : UNDEFINED; + } + } + } +} + +inline cell_t normalize(cell_t cell) { + if (cell == NEW) + return ALIVE; + if (cell == DIED) + return DEAD; + if (cell == UNDEFINED) + return DEAD; + return cell; +} + +static inline cell_t normalizeForPeriodCheck(cell_t cell) { + if (cell == UNDEFINED) + return cell; + return normalize(cell); +} + +static inline int getBoundarySafeState(int x, int y, cell_t** field) { + if (x < 0) + return 0; + if (y < 0) + return 0; + if (x >= settings.dimensions.width) + return 0; + if (y >= settings.dimensions.height) + return 0; + return normalize(field[x][y]) == ALIVE ? 1 : 0; +} +static inline int countNeighbors(int x, int y, cell_t** field) { + int count = 0; + + count += getBoundarySafeState(x + 1, y - 1, field); + count += getBoundarySafeState(x + 1, y, field); + count += getBoundarySafeState(x + 1, y + 1, field); + count += getBoundarySafeState(x, y + 1, field); + count += getBoundarySafeState(x - 1, y + 1, field); + count += getBoundarySafeState(x - 1, y, field); + count += getBoundarySafeState(x - 1, y - 1, field); + count += getBoundarySafeState(x, y - 1, field); + + return count; +} + +static inline cell_t nextState(int count, cell_t current) { + current = normalize(current); + + if (current == ALIVE) { + switch (settings.rules.neighbors[count]) { + case DIE: + return DIED; + case CREATE: + return ALIVE; + case KEEP: + return ALIVE; + case TOGGLE: + return DIED; + default: + error(EXIT_PANIC, "field_nextState", "unknown case"); + } + } else { + switch (settings.rules.neighbors[count]) { + case DIE: + return DEAD; + case CREATE: + return NEW; + case KEEP: + return DEAD; + case TOGGLE: + return NEW; + default: + error(EXIT_PANIC, "field_nextState", "unknown case"); + } + } +} + +static inline int getPeriod() { + if (settings.periodThreshold < 0) + return -1; + + for (int g = 1; g < generationCount; g++) { + bool diff = false; + for (int x = 0; x < settings.dimensions.width; x++) { + for (int y = 0; y < settings.dimensions.height; y++) { + //printf("x: %i, y: %i, g: %i, %i, %i\n", x, y, g, normalizeForPeriodCheck(generations[0][x][y]), normalizeForPeriodCheck(generations[g][x][y])); + if (normalizeForPeriodCheck(generations[0][x][y]) != normalizeForPeriodCheck(generations[g][x][y])) { + diff = true; + break; + } + } + if (diff) + break; + } + //printf("diff: %i\n", diff); + if (!diff) + return g; + } + + return -1; +} + +int nextGeneration() { + for (int x = 0; x < settings.dimensions.width; x++) + free(generations[generationCount - 1][x]); + for (int g = generationCount - 2; g >= 0; g--) { + memcpy(generations[g + 1], generations[g], settings.dimensions.width * sizeof(cell_t*)); + } + for (int x = 0; x < settings.dimensions.width; x++) { + generations[0][x] = malloc(settings.dimensions.height * sizeof(cell_t)); + if (generations[0][x] == NULL) + error(EXIT_PANIC, "field_nextGeneration", NULL); + + for (int y = 0; y < settings.dimensions.height; y++) { + generations[0][x][y] = nextState(countNeighbors(x, y, generations[1]), generations[1][x][y]); + } + } + + return getPeriod(); +} + +field_t getField() { + return (field_t) { + .height = settings.dimensions.height, + .width = settings.dimensions.width, + .cells = generations[0] + }; +} diff --git a/src/field.h b/src/field.h new file mode 100644 index 0000000..73d05a9 --- /dev/null +++ b/src/field.h @@ -0,0 +1,43 @@ +#ifndef FIELD_H +#define FIELD_H + +#include + +typedef enum { + DEAD = 0, DIED = 1, NEW = 2, ALIVE = 3, UNDEFINED = -1 +} cell_t; + +typedef enum { + DIE = 0, CREATE = 1, KEEP = 2, TOGGLE = 3 +} behaviour_t; + +typedef struct { + behaviour_t neighbors[9]; +} rules_t; + +typedef struct { + int height; + int width; +} dimensions_t; + +typedef struct { + rules_t rules; + dimensions_t dimensions; + int periodThreshold; +} settings_t; + +typedef struct { + int height; + int width; + cell_t** cells; +} field_t; + +#define DEFAULT_RULES ((rules_t) { .neighbors = {DIE, DIE, KEEP, CREATE, DIE, DIE, DIE, DIE, DIE}}) +#define DEFAULT_PERIOD_THRESHOLD (30) + +void initField(settings_t); +int nextGeneration(); +field_t getField(); +cell_t normalize(cell_t); + +#endif diff --git a/src/gif.c b/src/gif.c new file mode 100644 index 0000000..2c2f2ce --- /dev/null +++ b/src/gif.c @@ -0,0 +1,134 @@ +#ifdef GIF_SUPPORT + +#include +#include +#include + +#include + +#include "gif.h" +#include "field.h" +#include "misc.h" + +gif_settings_t gif_settings; + +GifFileType* image; + +void putGraficsControlBlock() { + int delay = gif_settings.sleep; + + static unsigned char + ExtStr[4] = { 0x04, 0x00, 0x00, 0xff }; + + + ExtStr[0] = (false) ? 0x06 : 0x04; + ExtStr[1] = delay % 256; + ExtStr[2] = delay / 256; + + EGifPutExtension(image, GRAPHICS_EXT_FUNC_CODE, 4, ExtStr); +} + +void enableLoop() { + char nsle[12] = "NETSCAPE2.0"; + char subblock[3]; + if (EGifPutExtensionLeader(image, APPLICATION_EXT_FUNC_CODE) != GIF_OK) { + error(EXIT_PANIC, "gif_init", "put extension leader"); + } + if (EGifPutExtensionBlock(image, 11, nsle) != GIF_OK) { + error(EXIT_PANIC, "gif_init", "put extension"); + } + subblock[0] = 1; + subblock[2] = 0; // loop count lo + subblock[1] = 0; // loop count hi + if (EGifPutExtensionBlock(image, 3, subblock) != GIF_OK) { + error(EXIT_PANIC, "gif_init", "put extension"); + } + if (EGifPutExtensionTrailer(image) != GIF_OK) { + error(EXIT_PANIC, "gif_init", "put extension trailer"); + } +} + +void gif_init(gif_settings_t _settings) { + gif_settings = _settings; + + int errorCode = 0; + image = EGifOpenFileHandle(gif_settings.fh, &errorCode); + + if (errorCode != E_GIF_SUCCEEDED) { + error(EXIT_PANIC, "gif_init", "open file handler"); + } + + GifColorType colors[4]; + for (int i = 0; i < 4; i++) { + colors[i].Red = gif_settings.colors[i][0]; + colors[i].Green = gif_settings.colors[i][1]; + colors[i].Blue = gif_settings.colors[i][2]; + } + + ColorMapObject* colorMap = GifMakeMapObject(4, &(colors[0])); + + EGifSetGifVersion(image, true); + + if (EGifPutScreenDesc(image, gif_settings.dimensions.width, gif_settings.dimensions.height, 4, 0, colorMap) != GIF_OK) { + error(EXIT_PANIC, "gif_init", "put screen desc"); + } + + enableLoop(); +} + +void gif_output(field_t field) { + putGraficsControlBlock(); + + if (EGifPutImageDesc(image, 0, 0, field.width, field.height, false, NULL) != GIF_OK) { + error(EXIT_PANIC, "git_output", "put image desc"); + } + + for (int y = 0; y < field.height; y++) { + for(int x = 0; x < field.width; x++) { + cell_t cell = field.cells[x][y]; + if (cell == UNDEFINED) + cell = DEAD; + if (EGifPutPixel(image, (GifPixelType) cell) != GIF_OK) { + error(EXIT_PANIC, "gif_output", "put pixel"); + } + } + } +} + +void gif_end() { + int errorCode; + EGifCloseFile(image, &errorCode); + + if (errorCode != E_GIF_SUCCEEDED) { + fprintf(stderr, "%d\n", errorCode); + error(EXIT_PANIC, "gif_end", "close file"); + } +} + +unsigned char hex2byte(const char* string, int offset) { + char tmp[3]; + memcpy(&(tmp[0]), string + offset, 2); + tmp[3] = '\0'; + + char* endptr; + unsigned char result = strtol(tmp, &endptr, 16); + + return result; +} + +void hex2color(unsigned char* color, const char* string) { + if (strlen(string) == 7) { + color[0] = hex2byte(string, 1); + color[1] = hex2byte(string, 3); + color[2] = hex2byte(string, 5); + } else { + } +} + +void gif_setColor(gif_settings_t* settings, const char* dead, const char* died, const char* new, const char* alive) { + hex2color(&(settings->colors[DEAD][0]), dead); + hex2color(&(settings->colors[DIED][0]), died); + hex2color(&(settings->colors[NEW][0]), new); + hex2color(&(settings->colors[ALIVE][0]), alive); +} +#endif diff --git a/src/gif.h b/src/gif.h new file mode 100644 index 0000000..95b65fe --- /dev/null +++ b/src/gif.h @@ -0,0 +1,19 @@ +#ifndef GIF_H +#define GIF_H + +#include "field.h" + +typedef struct { + int fh; + dimensions_t dimensions; + unsigned char colors[4][3]; + int sleep; +} gif_settings_t; + +void gif_init(gif_settings_t _settings); +void gif_output(field_t field); +void gif_setColor(gif_settings_t* settings, const char* dead, const char* died, const char* new, const char* alive); +void hex2color(unsigned char* color, const char* string); +void gif_end(); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4311966 --- /dev/null +++ b/src/main.c @@ -0,0 +1,386 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "field.h" +#include "cli.h" +#include "gif.h" + +char behaviourMap[4] = { + 'd', 'c', 'k', 't' +}; + +void optionError(const char* string) { + fprintf(stderr, "Error: %s\n", string); + fprintf(stderr, "User -y for help.\n"); + exit(EXIT_USER_ERROR); +} + +void rules2string(char* string, rules_t rules) { + for (int i = 0; i < 9; i++) { + string[i] = behaviourMap[rules.neighbors[i]]; + } + string[10] = '\0'; +} + +rules_t string2rules(const char* string) { + if (strlen(string) != 9) { + optionError("Rules string malformed"); + } + + rules_t rules; + + for(int i = 0; i < strlen(string); i++) { + bool okay = false; + for (int j = 0; j < 4; j++) { + if (string[i] == behaviourMap[j]) { + rules.neighbors[i] = j; + okay = true; + break; + } + } + if (!okay) { + optionError("Rules string malformed"); + } + } + + return rules; +} + +void help() { + printf("SYNOPSIS: %s [-y] [-c [-lbn] [-s TIME] [-t CHARSET]]\n" + "%*s%s[-p LIMIT] [-r RULES] [-h HEIGHT -w WIDTH] FILE\n\n", getProgName(), strlen(getProgName()), "", + #ifdef GIF_SUPPORT + "[-g [-s TIME] [{-o COLOR}]] " + #else + "" + #endif + ); + printf("OPTIONS:\n"); + printf(" -y show help\n"); + printf(" -c use CLI output (default)\n"); + printf(" -g use GIF output\n"); + printf(" -p LIMIT the search limit for periodes (default: %i)\n", DEFAULT_PERIOD_THRESHOLD); + char tmp[11]; + rules2string(tmp, DEFAULT_RULES); + printf(" -r RULES set custom rules (default: %s)\n", tmp); + printf(" -h HEIGHT override the height of the output\n"); + printf(" -w WIDTH override the width of the output\n"); + printf(" FILE the input file (- for stdin)\n"); + printf("\n"); + + printf("CLI OPTIONS:\n"); + printf(" -l loop the output even if periodes occure\n"); + printf(" -b draw border (defaul)\n"); + printf(" -n don't draw border\n"); + printf(" -s TIME sleep in ms (default: 200)\n"); + printf(" -t CHARSET charset for drawing (default: \" **\")\n"); + printf("\n"); + + #ifdef GIF_SUPPORT + printf("GIF OPTIONS:\n"); + printf(" -s TIME sleep in 1/10 s (default: 20)\n"); + printf(" -o COLOR colors to use\n"); + printf(" COLOR := DEAD|DIED|NEW|ALIVE:HEX_COLOR\n"); + printf(" HEX_COLOR := #[a-f0-9]{6}\n"); + printf(" (defaults: DEAD/DIED = #000000; NEW/ALIVE = #ffffff)\n"); + printf("\n"); + #endif + + printf("INPUT FORMAT:\n"); + printf("[#WIDTH,HEIGHT;]{X,Y;}\n\n"); + printf(" WDITH the width of the output\n"); + printf(" HEIGHT the width of the output\n"); + printf(" X the x coordinate of a cell\n"); + printf(" Y the y coordinate of a cell\n\n"); + printf("Example:\n"); + printf("#3,3;1,0;1,2;1,3;\n"); + exit(EXIT_SUCCESS); +} + +int readCoordinates(FILE* file, int* x, int* y) { + int c; + char tmp[10]; + char* endptr; + int position = 0; + *x = -1; + *y = -1; + bool has = false; + while((c = getc(file)) != EOF) { + switch(c) { + case ',': + tmp[position] = '\0'; + position = 0; + *x = strtol(tmp, &endptr, 10); + if (*endptr != '\0') { + return -1; + } + break; + case ';': + tmp[position] = '\0'; + position = 0; + *y = strtol(tmp, &endptr, 10); + if (*endptr != '\0') { + return -1; + } + if ((*x < 0) || (*y < 0)) + return -1; + return 0; + case ' ': + case '\n': + case '\t': + break; + default: + has = true; + if (position > 9) { + return -1; + } + tmp[position++] = c; + break; + } + } + if (!has) + return 1; + return -1; +} + +void setSize(FILE* file, int* width, int* height) { + int c = getc(file); + if (c != '#') { + ungetc(c, file); + return; + } + + c = readCoordinates(file, width, height); + + if (c != 0) { + error(EXIT_USER_ERROR, "setSize", "malformed file format"); + } +} + +void setField(FILE* file, field_t field) { + int r, x, y; + + while((r = readCoordinates(file, &x, &y)) == 0) { + if ((x < 0) || (y < 0) || (x >= field.width) || (y >= field.height)) { + error(EXIT_USER_ERROR, "setField", "coordinates out of bounds"); + } + field.cells[x][y] = ALIVE; + } + + if (r < 0) { + error(EXIT_USER_ERROR, "setField", "malformed input format"); + } +}; + +void parseColor(gif_settings_t* settings, char* optarg) { + char* color = NULL; + int celltype = -1; + for (int i = 0; i < strlen(optarg); i++) { + if (optarg[i] == ':') { + color = optarg + i + 1; + optarg[i] = '\0'; + break; + } + } + + if (color == NULL) { + optionError("color format malformed"); + } + + if (strcmp(optarg, "DEAD") == 0) { + celltype = DEAD; + } else if (strcmp(optarg, "DIED") == 0) { + celltype = DIED; + } else if (strcmp(optarg, "NEW") == 0) { + celltype = NEW; + } else if (strcmp(optarg, "ALIVE") == 0) { + celltype = ALIVE; + } else { + optionError("unknown cell type"); + } + + hex2color(&(settings->colors[celltype][0]), color); +} + +int main(int argc, char** argv) { + initMisc(argv[0]); + + bool gif = false; + bool loop = false; + int width = -1; + int height = -1; + + const char* filename; + FILE* file; + + long sleep = -1; + + settings_t settings; + settings.dimensions.width = -1; + settings.dimensions.height = -1; + settings.rules = DEFAULT_RULES; + settings.periodThreshold = DEFAULT_PERIOD_THRESHOLD; + + cli_settings_t cli_settings; + cli_settings.border = true; + cli_settings.charset = " **"; + + gif_settings_t gif_settings; + const char* gif_filename = "-"; + gif_setColor(&gif_settings, "#000000", "#000000", "#ffffff", "#ffffff"); + + int opt; + char* endptr; + + while((opt = getopt(argc, argv, "ycgp:r:h:w:lbns:t:o:")) != -1) { + switch(opt) { + case 'y': + help(); + break; + case 'c': + gif = false; + break; + case 'g': + gif = true; + break; + case 'p': + settings.periodThreshold = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + optionError("Argument has to be a number."); + } + break; + case 'r': + settings.rules = string2rules(optarg); + break; + case 'h': + settings.dimensions.height = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + optionError("Argument has to be a number."); + } + break; + case 'w': + settings.dimensions.width = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + optionError("Argument has to be a number."); + } + break; + + case 'l': + loop = true; + break; + case 'b': + cli_settings.border = true; + break; + case 'n': + cli_settings.border = false; + break; + case 's': + sleep = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + optionError("Argument has to be a number."); + } + break; + case 't': + if (strlen(optarg) != 4) { + optionError("Argument needs to be length 4"); + } + cli_settings.charset = optarg; + break; + case 'o': + parseColor(&gif_settings, optarg); + break; + default: + optionError("unknown option"); + break; + } + } + + if (optind >= argc) { + optionError("No file given."); + } + + filename = argv[optind]; + + if (strcmp("-", filename) == 0) { + file = stdin; + } else { + file = fopen(filename, "r"); + if (file == NULL) + error(EXIT_USER_ERROR, "main", NULL); + } + + setSize(file, &width, &height); + + if ((width < 0) || (height < 0)) { + optionError("Width or height missing"); + } + + settings.dimensions.width = width; + settings.dimensions.height = height; + + initField(settings); + + setField(file, getField()); + + if (gif) { + #ifndef GIF_SUPPORT + error(EXIT_COMPILE_TIME_ERROR, "main", "no GIF support"); + #else + + if (sleep < 0) + sleep = 20; + + if (strcmp(gif_filename, "-") == 0) { + freopen(NULL, "wb", stdout); + + gif_settings.fh = fileno(stdout); + } else { + gif_settings.fh = open(gif_filename, O_WRONLY | O_CREAT | O_TRUNC); + + if (gif_settings.fh < 0) { + error(EXIT_USER_ERROR, gif_filename, NULL); + } + } + + gif_settings.dimensions = settings.dimensions; + gif_settings.sleep = sleep; + + gif_init(gif_settings); + + gif_output(getField()); + + int period; + while(((period = nextGeneration()) < 0)) { + gif_output(getField()); + } + + gif_end(); + + #endif + } else { + if (sleep < 0) + sleep = 200; + + cli_init(cli_settings); + + cli_output(getField()); + + int period; + while(((period = nextGeneration()) < 0) || loop) { + usleep(sleep * 1000); + cli_output(getField()); + } + + cli_output(getField()); + + cli_end(); + } + + return EXIT_SUCCESS; +} diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..0b63a4c --- /dev/null +++ b/src/misc.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +#include "misc.h" + +const char* progName; + +void initMisc(const char* _progName) { + progName = _progName; +} + +const char* getProgName() { + return progName; +} + +void error(int exitcode, const char* module, const char* error) { + (void) fprintf(stderr, "%s: %s", progName, module); + if (error != NULL) { + (void) fprintf(stderr, ": %s", error); + } + if (errno != 0) { + (void) fprintf(stderr, ": %s", strerror(errno)); + } + (void) fprintf(stderr, "\n"); + exit(exitcode); +} diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..e8765e7 --- /dev/null +++ b/src/misc.h @@ -0,0 +1,13 @@ +#ifndef MISC_H +#define MISC_H + +#define EXIT_PANIC 42 +#define EXIT_SUCCESS 0 +#define EXIT_USER_ERROR 1 +#define EXIT_COMPILE_TIME_ERROR 5 + +void initMisc(const char*); +const char* getProgName(); +void error(int, const char*, const char*); + +#endif