#include #include #include #include #include #include "config.h" #include "trace.h" #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 */ if (*line == '#') return -1; /* The actual variable name begins here */ *variable = line; /* Find where the variable name ends */ SKIP_UNTIL(line, !isspace(*line) && *line != '='); if (*line == '=') { *line = '\0'; line++; goto value_start; } /* If we encounter line end already, the line can't be valid */ if (!*line) return -1; /* Terminate the variable name */ *line = '\0'; line++; /* Find the equal sign */ SKIP_UNTIL(line, *line != '='); if (*line != '=') return -1; line++; SKIP_UNTIL(line, isspace(*line)); value_start: *value = line; SKIP_UNTIL(line, *line != '\n' && *line != '#'); *line = 0; return 0; } static void init_variable_value(struct variable_value *v) { bzero(v, sizeof(*v)); } static struct variable_value *prepare_slot_for_insertion( struct plotter_config *cfg, const char *variable) { 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); v[i].name = NULL; free(v[i].ptr); v[i].ptr = NULL; return &v[i]; } /* * Failed to find an existing variable with same name, 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]); return &v[i]; } static int store_str_variable_value_to_array(const char *variable, const char *value, struct plotter_config *cfg) { struct variable_value *v; v = prepare_slot_for_insertion(cfg, variable); v->name = strdup(variable); v->type = TYPE_STRING; v->ptr = strdup(value); return 0; } int store_int_variable_value_to_array(const char *variable, int value, struct plotter_config *cfg) { struct variable_value *v; int *p; v = prepare_slot_for_insertion(cfg, variable); v->name = strdup(variable); v->type = TYPE_INT; p = v->ptr = malloc(sizeof(value)); *p = value; return 0; } static int store_variable_value_to_plotter_config(const char *variable, const char *value, struct plotter_config *cfg) { #define TEST_STR_STORE_STR_RETURN(_cfg, _variable, _str, _value) \ if (!strcmp(_variable, #_str) && !cfg->_str) { \ cfg->_str = strdup(_value); \ return 0; \ } #define TEST_STR_STORE_INT_RETURN(_cfg, _variable, _str, _value) \ if (!strcmp(_variable, #_str)) { \ cfg->_str = atoi(_value); \ return 0; \ } TEST_STR_STORE_STR_RETURN(cfg, variable, charger_name, value); TEST_STR_STORE_STR_RETURN(cfg, variable, plotter_scripts_dir, value); TEST_STR_STORE_STR_RETURN(cfg, variable, images_output_dir, value); TEST_STR_STORE_STR_RETURN(cfg, variable, device_path, value); TEST_STR_STORE_STR_RETURN(cfg, variable, log_path, value); TEST_STR_STORE_INT_RETURN(cfg, variable, baudrate, value); return store_str_variable_value_to_array(variable, value, cfg); } static int read_config_file(FILE *file, struct plotter_config *cfg) { char line[1024]; char *variable, *value; while (fgets(line, sizeof(line), file)) { if (parse_config_line(line, &variable, &value)) continue; if (!store_variable_value_to_plotter_config(variable, value, cfg)) continue; pr_debug("Discarding unsupported config variable %s\n", variable); } return 0; } int populate_config_data_from_file(const char *path, struct plotter_config *cfg) { glob_t globbuf; FILE *file; int ret; ret = glob(path, GLOB_TILDE, NULL, &globbuf); if (ret) return -1; if (globbuf.gl_pathc == 0) { pr_debug("No config file foud from path %s\n", path); return -1; } if (globbuf.gl_pathc > 1) { pr_warn("Found multiple config files from path %s, using the first one\n", path); } file = fopen(globbuf.gl_pathv[0], "r"); if (!file) { pr_err("Failed to open config file %s: %m\n", globbuf.gl_pathv[0]); return -1; } 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, char **endptr) { 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; *endptr = to; 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--; } *endptr = from; /* * 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; 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, &ptr); } if (overflow) pr_err("String buffer overflow\n"); return overflow; }