From: Timo Kokkonen Date: Sun, 29 Dec 2013 11:41:37 +0000 (+0200) Subject: config.c: Add support for replacing variables names from strings X-Git-Url: http://git.itanic.dy.fi/?p=log-plotter;a=commitdiff_plain;h=6100946ec13ca780c199b5202995c03c963dede1 config.c: Add support for replacing variables names from strings Read all unknown config options from the config file and store them in the config variable array. Implement a new function that can be used to replace variable names from a string with the contents of the variable name in question. Signed-off-by: Timo Kokkonen --- diff --git a/config.c b/config.c index 54f672e..7caa63d 100644 --- a/config.c +++ b/config.c @@ -7,12 +7,12 @@ #include "config.h" #include "trace.h" -static int parse_config_line(char *line, char **variable, char **value) -{ #define SKIP_UNTIL(_str, _cond) \ while (*_str && (_cond)) \ _str++; +static int parse_config_line(char *line, char **variable, char **value) +{ SKIP_UNTIL(line, isspace(*line)); /* Skip comments */ @@ -57,6 +57,54 @@ value_start: return 0; } +static void init_variable_value(struct variable_value *v) +{ + bzero(v, sizeof(*v)); +} + +static int store_str_variable_value_to_array(const char *variable, + const char *value, + struct plotter_config *cfg) +{ + struct variable_value *v; + int i; + + if (!cfg->variables) { + cfg->variables = + realloc(cfg->variables, sizeof(*cfg->variables)); + + init_variable_value(cfg->variables); + } + + v = cfg->variables; + for (i = 0; v[i].name; i++) { + if (strcmp(variable, v[i].name)) + continue; + + free(v[i].name); + free(v[i].ptr); + goto out_store; + } + + /* + * Failed to find an existing variable with same type, create + * a new one. Resize the variable array so that there is room + * for the new entry and one terminator entry (with all fields + * zeroed). + */ + cfg->variables = realloc(cfg->variables, + sizeof(*cfg->variables) * (i + 2)); + v = cfg->variables; + init_variable_value(&cfg->variables[i + 1]); + +out_store: + v[i].name = strdup(variable); + v[i].type = TYPE_STRING; + v[i].ptr = strdup(value); + + return 0; +} + static int store_variable_value_to_plotter_config(const char *variable, const char *value, struct plotter_config *cfg) @@ -80,7 +128,7 @@ static int store_variable_value_to_plotter_config(const char *variable, TEST_STR_STORE_STR_RETURN(cfg, variable, log_path, value); TEST_STR_STORE_INT_RETURN(cfg, variable, baudrate, value); - return -1; + return store_str_variable_value_to_array(variable, value, cfg); } static int read_config_file(FILE *file, struct plotter_config *cfg) @@ -133,3 +181,160 @@ int populate_config_data_from_file(const char *path, return read_config_file(file, cfg); } + +static char *get_value_for_variable(char *variable, + const struct plotter_config *cfg) +{ + static char result[256]; + struct variable_value *values = cfg->variables; + int i; + int *ip; + char *cp; + + + for (i = 0; values[i].name; i++) { + if (strcmp(values[i].name, variable)) + continue; + + switch (values[i].type) { + case TYPE_INT: + ip = values[i].ptr; + snprintf(result, sizeof(result), "%d", *ip); + break; + case TYPE_STRING: + cp = values[i].ptr; + snprintf(result, sizeof(result), "%s", cp); + break; + default: + pr_err("BUG: Unknown variable type %d\n", + values[i].type); + return NULL; + } + + return result; + } + + return NULL; +} + +static int replace_variable_with_value(char *str, size_t len, + char *variable_start, char *variable_end, + const char *value) +{ + int value_len = strlen(value); + char *from, *to; + int overflow = 0; + + if (variable_end - variable_start >= value_len) { + /* + * The replacement string is shorter than the variable + * name, this is the easy case, no need to worry about + * overflows. + */ + + strcpy(variable_start, value); + + from = variable_end + 1; + to = variable_start + value_len; + + while (*from) { + *to = *from; + from++; + to++; + } + + *to = 0; + } else { + /* + * The replacement string is longer than the space + * occupied by the variable name. We need to first + * make room for the value, without overflowing the + * buffer. Make an overlapping scring copy, start from + * the end. + */ + + /* + * The closing parenthesis was replaced with null byte + * earlier, put a non-zero char there to avoid + * confusing strlen(). + */ + *variable_end = '.'; + + from = str + strlen(str); + to = from + (value_len - (variable_end - variable_start)) - 1; + if (to >= str + len - 1) { + to = str + len - 1; + overflow = 1; + } + + while (from >= variable_end) { + *to = *from; + from--; + to--; + } + + /* + * use memcpy to avoid NULL terminating prematurely + * the target string + */ + memcpy(variable_start, value, value_len); + } + + return overflow; +} + +/* + * Replace variable names from a string with the variable + * contents. Variable names are in format: $(name) + */ +int replace_variables_with_values(char *str, size_t len, + const struct plotter_config *cfg) +{ + char *ptr = str; + int overflow = 0; + + while (*ptr && ptr < str + len) { + char *replace_start, *variable_start, *variable_end, *value; + + SKIP_UNTIL(ptr, *ptr != '$'); + + if (!*ptr) + break; + + replace_start = ptr; + ptr++; + + /* Skip if not in correct format */ + if (*ptr != '(') + continue; + + ptr++; + variable_start = ptr; + + SKIP_UNTIL(ptr, *ptr != ')'); + + variable_end = ptr; + *ptr = 0; + ptr++; + + value = get_value_for_variable(variable_start, cfg); + if (!value) { + pr_debug("Could not substitute unknown variable name %s\n", + variable_start); + + /* + * Put back the closing parenthesis so that + * the line remains as it was + */ + *variable_end = ')'; + continue; + } + overflow = replace_variable_with_value(str, len, replace_start, + variable_end, value); + } + + if (overflow) + pr_err("String buffer overflow\n"); + + return overflow; +} diff --git a/config.h b/config.h index 37437ea..851c7d4 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,17 @@ #ifndef _CONFIG_H_ #define _CONFIG_H_ +struct variable_value { + char *name; + int type; + void *ptr; +}; + +enum { + TYPE_INT, + TYPE_STRING, +}; + struct plotter_config { char *charger_name; @@ -11,10 +22,15 @@ struct plotter_config { int baudrate; char *log_path; char *config_file_path; + + struct variable_value *variables; }; int read_args(int argc, char *argv[], struct plotter_config *cfg); int populate_config_data_from_file(const char *path, struct plotter_config *cfg); +int replace_variables_with_values(char *str, size_t len, + const struct plotter_config *cfg); + #endif