From 785bc33192d92becd1389414305d433c8ee9b053 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 11 May 2019 18:22:01 +1000 Subject: [PATCH] [common] added config file loading capability --- VERSION | 2 +- common/include/common/option.h | 3 + common/src/option.c | 221 +++++++++++++++++++++++++++++---- 3 files changed, 203 insertions(+), 23 deletions(-) diff --git a/VERSION b/VERSION index 4a4d8b2a..7bcfa0e9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -a12-177-gcf030f6f0c+1 \ No newline at end of file +a12-178-g522bacb1f0+1 \ No newline at end of file diff --git a/common/include/common/option.h b/common/include/common/option.h index 57af4623..96c08218 100644 --- a/common/include/common/option.h +++ b/common/include/common/option.h @@ -68,6 +68,9 @@ bool option_get_bool (const char * module, const char * name); // called by the main application to parse the command line arguments bool option_parse(int argc, char * argv[]); +// called by the main application to load configuration from a file +bool option_load(const char * filename); + // called by the main application to validate the option values bool option_validate(); diff --git a/common/src/option.c b/common/src/option.c index 9cb341a6..3e53f433 100644 --- a/common/src/option.c +++ b/common/src/option.c @@ -22,6 +22,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include +#include #include #include @@ -132,6 +133,35 @@ void option_free() state.gCount = 0; } +static bool option_set(struct OptionValue * v, const char * value) +{ + switch(v->type) + { + case OPTION_TYPE_INT: + v->v.x_int = atol(value); + break; + + case OPTION_TYPE_STRING: + free(v->v.x_string); + v->v.x_string = strdup(value); + break; + + case OPTION_TYPE_BOOL: + v->v.x_bool = + strcmp(value, "1" ) == 0 || + strcmp(value, "yes" ) == 0 || + strcmp(value, "true") == 0 || + strcmp(value, "on" ) == 0; + break; + + default: + DEBUG_ERROR("BUG: Invalid option type, this should never happen"); + return false; + } + + return true; +} + bool option_parse(int argc, char * argv[]) { for(int a = 1; a < argc; ++a) @@ -173,29 +203,11 @@ bool option_parse(int argc, char * argv[]) continue; } - switch(o->value.type) + if (!option_set(&o->value, value)) { - case OPTION_TYPE_INT: - o->value.v.x_int = atol(value); - break; - - case OPTION_TYPE_STRING: - free(o->value.v.x_string); - o->value.v.x_string = strdup(value); - break; - - case OPTION_TYPE_BOOL: - o->value.v.x_bool = - strcmp(value, "1" ) == 0 || - strcmp(value, "yes" ) == 0 || - strcmp(value, "true") == 0 || - strcmp(value, "on" ) == 0; - break; - - default: - DEBUG_ERROR("BUG: Invalid option type, this should never happen"); - assert(false); - break; + DEBUG_ERROR("Failed to set the option value"); + free(arg); + continue; } free(arg); @@ -204,6 +216,171 @@ bool option_parse(int argc, char * argv[]) return true; } +static char * file_parse_module(FILE * fp) +{ + char * module = NULL; + int len = 0; + + for(int c = fgetc(fp); !feof(fp); c = fgetc(fp)) + { + switch(c) + { + case ']': + if (module) + module[len] = '\0'; + return module; + + case '\r': + case '\n': + free(module); + return NULL; + + default: + if (len % 32 == 0) + module = realloc(module, len + 32 + 1); + module[len++] = c; + } + } + + if (module) + free(module); + + return NULL; +} + +bool option_load(const char * filename) +{ + FILE * fp = fopen(filename, "r"); + if (!fp) + return false; + + bool result = true; + int lineno = 1; + char * module = NULL; + bool line = true; + bool expectLine = false; + bool expectValue = false; + char * name = NULL; + int nameLen = 0; + char * value = NULL; + int valueLen = 0; + + char ** p = &name; + int * len = &nameLen; + + for(int c = fgetc(fp); !feof(fp); c = fgetc(fp)) + { + switch(c) + { + case '[': + if (expectLine) + { + DEBUG_ERROR("Syntax error on line %d, expected new line", lineno); + result = false; + goto exit; + } + + if (line) + { + free(module); + module = file_parse_module(fp); + if (!module) + { + DEBUG_ERROR("Syntax error on line %d, failed to parse the module", lineno); + result = false; + goto exit; + } + line = false; + expectLine = true; + continue; + } + + if (*len % 32 == 0) + *p = realloc(*p, *len + 32 + 1); + (*p)[(*len)++] = c; + break; + + case '\r': + continue; + + case '\n': + if (name) + { + struct OptionValue * o = option_get(module, name); + if (!o) + DEBUG_WARN("Ignored unknown option %s:%s", module, name); + else + { + if (!option_set(o, value)) + DEBUG_ERROR("Failed to set the option value"); + } + } + + line = true; + expectLine = false; + expectValue = false; + ++lineno; + + p = &name; + len = &nameLen; + + free(name); + name = NULL; + nameLen = 0; + free(value); + value = NULL; + valueLen = 0; + break; + + case '=': + if (!expectValue) + { + if (!name) + { + DEBUG_ERROR("Syntax error on line %d, expected option name", lineno); + result = false; + goto exit; + } + + name[nameLen] = '\0'; + expectValue = true; + + p = &value; + len = &valueLen; + continue; + } + + if (*len % 32 == 0) + *p = realloc(*p, *len + 32 + 1); + (*p)[(*len)++] = c; + break; + + default: + if (expectLine) + { + DEBUG_ERROR("Syntax error on line %d, expected new line", lineno); + result = false; + goto exit; + } + line = false; + + if (*len % 32 == 0) + *p = realloc(*p, *len + 32 + 1); + (*p)[(*len)++] = c; + break; + } + } + +exit: + fclose(fp); + + free(module); + free(name ); + free(value ); + + return result; +} + bool option_validate() { if (state.doHelp)