/* * option.c * * This file contains the functions which support our CLI options; you * are both able to parse options and retrieve information from that * option parsing. */ #include #include #include #include #include "option.h" #include "log.h" #include "vm_di.h" /* * These are the file inputs we may have to the system. What their * contents are will vary based on emulation context, and these values * may change through the course of the program's execution. */ static FILE *input1 = NULL; static FILE *input2 = NULL; static FILE *disasm_log = NULL; /* * The size of our error buffer for anything we want to record while our * option parsing goes on. */ #define ERRBUF_SIZE 2048 /* * The alluded-to error buffer. */ static char error_buffer[ERRBUF_SIZE] = ""; /* * The default width and height of the window. This is "roughly" * 640x480, but because the Apple II has a 4.375:3 aspect ratio, we had * to bump up the width to a number that respects that ratio. */ static int width = 840; static int height = 576; /* * These are all of the options we allow in our long-form options. It's * a bit faster to identify them by integer symbols than to do string * comparisons. */ enum options { DISK1, DISK2, HELP, DISASSEMBLE, }; /* * Here are the options we support for program execution. */ static struct option long_options[] = { { "disassemble", 1, NULL, DISASSEMBLE }, { "disk1", 1, NULL, DISK1 }, { "disk2", 1, NULL, DISK2 }, { "help", 0, NULL, HELP }, }; /* * This simply returns the pointer of our error buffer, with the * qualification that the caller cannot modify the error buffer once * they get the pointer back. */ const char * option_get_error() { return error_buffer; } /* * Directly set the error buffer with something (that has to be less * than ERRBUF_SIZE). This function is not itself hugely useful, but * does allow for some better testing on option_error(). */ void option_set_error(const char *str) { // Use `- 1` so that we can ensure error_buffer is NUL-terminated. // Otherwise, strncpy will copy all of src but leave the dst // unterminated. Not that we're _likely_ to so self-injure // ourselves, but, ya know. strncpy(error_buffer, str, ERRBUF_SIZE - 1); } /* * Parse our command-line arguments from the given argc and argv. This * may or may not be what the kernel passes into the main() function! * We return 1 if we can continue beyond the option parse phase, and 0 * if something came up to cause us to exit early. But whether you * actually exit early is left up to the caller. */ int option_parse(int argc, char **argv) { int index; int opt = -1; // To begin with, let's (effectively) NUL-out the error buffer. error_buffer[0] = '\0'; vm_di_set(VM_WIDTH, &width); vm_di_set(VM_HEIGHT, &height); do { opt = getopt_long_only(argc, argv, "", long_options, &index); switch (opt) { case DISASSEMBLE: if (!option_open_file(&disasm_log, optarg, "w")) { return 0; } vm_di_set(VM_DISASM_LOG, disasm_log); break; case DISK1: if (!option_open_file(&input1, optarg, "r+")) { return 0; } vm_di_set(VM_DISK1, input1); break; case DISK2: if (!option_open_file(&input2, optarg, "r+")) { return 0; } vm_di_set(VM_DISK2, input2); break; case HELP: option_print_help(); // The help option should terminate normal execution return 0; } } while (opt != -1); return 1; } /* * Given a file path, we open a FILE stream and set our given input * source to that stream. If we cannot do so, we will set an error * string in the buffer. Assuming all goes well, we will return 1, and * 0 if not. */ int option_open_file(FILE **stream, const char *file, const char *opts) { if (!file) { snprintf(error_buffer, ERRBUF_SIZE, "No file given\n"); return 0; } *stream = fopen(file, opts); if (*stream == NULL) { snprintf(error_buffer, ERRBUF_SIZE, "open file for %s: %s", file, strerror(errno)); return 0; } return 1; } /* * Print out a help message. You'll note this is not automatically * generated; it must be manually updated as we add other options. */ void option_print_help() { fprintf(stderr, "Usage: erc [options...]\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, "\ --disassemble=FILE Write assembly notation into FILE\n\ --disk1=FILE Load FILE into disk drive 1\n\ --disk2=FILE Load FILE into disk drive 2\n\ --help Print this help message\n\ --size=WIDTHxHEIGHT Use WIDTH and HEIGHT for window size\n\ (only 700x480 and 875x600 are supported)\n"); }