]> git.itanic.dy.fi Git - log-plotter/blob - config.c
config.c: Handle multiple variables correcly
[log-plotter] / config.c
1 #include <stdio.h>
2 #include <glob.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include <stdlib.h>
6
7 #include "config.h"
8 #include "trace.h"
9
10 #define SKIP_UNTIL(_str, _cond)         \
11         while (*_str && (_cond))        \
12                 _str++;
13
14 static int parse_config_line(char *line, char **variable, char **value)
15 {
16         SKIP_UNTIL(line, isspace(*line));
17
18         /* Skip comments */
19         if (*line == '#')
20                 return -1;
21
22         /* The actual variable name begins here */
23         *variable = line;
24
25         /* Find where the variable name ends */
26         SKIP_UNTIL(line, !isspace(*line) && *line != '=');
27
28         if (*line == '=') {
29                 *line = '\0';
30                 line++;
31                 goto value_start;
32         }
33
34         /* If we encounter line end already, the line can't be valid */
35         if (!*line)
36                 return -1;
37
38         /* Terminate the variable name */
39         *line = '\0';
40         line++;
41          
42         /* Find the equal sign */
43         SKIP_UNTIL(line, *line != '=');
44
45         if (*line != '=')
46                 return -1;
47         line++;
48
49         SKIP_UNTIL(line, isspace(*line));
50
51 value_start:
52         *value = line;
53
54         SKIP_UNTIL(line, *line != '\n' && *line != '#');
55         *line = 0;
56
57         return 0;
58 }
59
60 static void init_variable_value(struct variable_value *v)
61 {
62         bzero(v, sizeof(*v));
63 }
64
65 static struct variable_value *prepare_slot_for_insertion(
66         struct plotter_config *cfg, const char *variable)
67 {
68         struct variable_value *v;
69         int i;
70
71         if (!cfg->variables) {
72                 cfg->variables =
73                         realloc(cfg->variables, sizeof(*cfg->variables));
74
75                 init_variable_value(cfg->variables);
76         }
77
78         v = cfg->variables;
79         for (i = 0; v[i].name; i++) {
80                 if (strcmp(variable, v[i].name))
81                         continue;
82
83                 free(v[i].name);
84                 v[i].name = NULL;
85                 free(v[i].ptr);
86                 v[i].ptr = NULL;
87
88                 return &v[i];
89         }
90
91         /*
92          * Failed to find an existing variable with same name, create
93          * a new one. Resize the variable array so that there is room
94          * for the new entry and one terminator entry (with all fields
95          * zeroed).
96          */
97         cfg->variables = realloc(cfg->variables,
98                                 sizeof(*cfg->variables) * (i + 2));
99         v = cfg->variables;
100         init_variable_value(&cfg->variables[i + 1]);
101
102         return &v[i];
103 }
104
105 static int store_str_variable_value_to_array(const char *variable,
106                                         const char *value,
107                                         struct plotter_config *cfg)
108 {
109         struct variable_value *v;
110
111         v = prepare_slot_for_insertion(cfg, variable);
112
113         v->name = strdup(variable);
114         v->type = TYPE_STRING;
115         v->ptr = strdup(value);
116
117         return 0;
118 }
119
120 int store_int_variable_value_to_array(const char *variable, int value,
121                                 struct plotter_config *cfg)
122 {
123         struct variable_value *v;
124         int *p;
125
126         v = prepare_slot_for_insertion(cfg, variable);
127
128         v->name = strdup(variable);
129         v->type = TYPE_INT;
130         p = v->ptr = malloc(sizeof(value));
131         *p = value;
132
133         return 0;
134 }
135
136 static int store_variable_value_to_plotter_config(const char *variable,
137                                                 const char *value,
138                                                 struct plotter_config *cfg)
139 {
140 #define TEST_STR_STORE_STR_RETURN(_cfg, _variable, _str, _value)        \
141         if (!strcmp(_variable, #_str) && !cfg->_str) {                  \
142                 cfg->_str = strdup(_value);                             \
143                 return 0;                                               \
144         }
145
146 #define TEST_STR_STORE_INT_RETURN(_cfg, _variable, _str, _value)        \
147         if (!strcmp(_variable, #_str)) {                                \
148                 cfg->_str = atoi(_value);                               \
149                 return 0;                                               \
150         }
151
152         TEST_STR_STORE_STR_RETURN(cfg, variable, charger_name, value);
153         TEST_STR_STORE_STR_RETURN(cfg, variable, plotter_scripts_dir, value);
154         TEST_STR_STORE_STR_RETURN(cfg, variable, images_output_dir, value);
155         TEST_STR_STORE_STR_RETURN(cfg, variable, device_path, value);
156         TEST_STR_STORE_STR_RETURN(cfg, variable, log_path, value);
157         TEST_STR_STORE_INT_RETURN(cfg, variable, baudrate, value);
158
159         return store_str_variable_value_to_array(variable, value, cfg);
160 }
161
162 static int read_config_file(FILE *file, struct plotter_config *cfg)
163 {
164         char line[1024];
165         char *variable, *value;
166
167         while (fgets(line, sizeof(line), file)) {
168                 if (parse_config_line(line, &variable, &value))
169                         continue;
170
171                 if (!store_variable_value_to_plotter_config(variable, value,
172                                                                 cfg))
173                         continue;
174
175                 pr_debug("Discarding unsupported config variable %s\n",
176                         variable);
177         }
178
179         return 0;
180 }
181
182 int populate_config_data_from_file(const char *path,
183                                 struct plotter_config *cfg)
184 {
185         glob_t globbuf;
186         FILE *file;
187         int ret;
188
189         ret = glob(path, GLOB_TILDE, NULL, &globbuf);
190         if (ret)
191                 return -1;
192
193         if (globbuf.gl_pathc == 0) {
194                 pr_debug("No config file foud from path %s\n", path);
195                 return -1;
196         }
197
198         if (globbuf.gl_pathc > 1) {
199                 pr_warn("Found multiple config files from path %s, using the first one\n",
200                         path);
201         }
202
203         file = fopen(globbuf.gl_pathv[0], "r");
204         if (!file) {
205                 pr_err("Failed to open config file %s: %m\n",
206                         globbuf.gl_pathv[0]);
207                 return -1;
208         }
209
210         return read_config_file(file, cfg);
211 }
212
213 static char *get_value_for_variable(char *variable,
214                         const struct plotter_config *cfg)
215 {
216         static char result[256];
217         struct variable_value *values = cfg->variables;
218         int i;
219         int *ip;
220         char *cp;
221
222
223         for (i = 0; values[i].name; i++) {
224                 if (strcmp(values[i].name, variable))
225                         continue;
226
227                 switch (values[i].type) {
228                 case TYPE_INT:
229                         ip = values[i].ptr;
230                         snprintf(result, sizeof(result), "%d", *ip);
231                         break;
232                 case TYPE_STRING:
233                         cp = values[i].ptr;
234                         snprintf(result, sizeof(result), "%s", cp);
235                         break;
236                 default:
237                         pr_err("BUG: Unknown variable type %d\n",
238                                 values[i].type);
239                         return NULL;
240                 }
241
242                 return result;
243         }
244
245         return NULL;
246 }
247
248 static int replace_variable_with_value(char *str, size_t len,
249                                 char *variable_start, char *variable_end,
250                                 const char *value, char **endptr)
251 {
252         int value_len = strlen(value);
253         char *from, *to;
254         int overflow = 0;
255
256         if (variable_end - variable_start >= value_len) {
257                 /*
258                  * The replacement string is shorter than the variable
259                  * name, this is the easy case, no need to worry about
260                  * overflows.
261                  */
262
263                 strcpy(variable_start, value);
264
265                 from = variable_end + 1;
266                 to = variable_start + value_len;
267
268                 *endptr = to;
269
270                 while (*from) {
271                         *to = *from;
272                         from++;
273                         to++;
274                 }
275
276                 *to = 0;
277         } else {
278                 /*
279                  * The replacement string is longer than the space
280                  * occupied by the variable name. We need to first
281                  * make room for the value, without overflowing the
282                  * buffer. Make an overlapping scring copy, start from
283                  * the end.
284                  */
285
286                 /*
287                  * The closing parenthesis was replaced with null byte
288                  * earlier, put a non-zero char there to avoid
289                  * confusing strlen().
290                  */
291                 *variable_end = '.';
292
293                 from = str + strlen(str);
294                 to = from + (value_len - (variable_end - variable_start)) - 1;
295                 if (to >= str + len - 1) {
296                         to = str + len - 1;
297                         overflow = 1;
298                 }
299
300                 while (from >= variable_end) {
301                         *to = *from;
302                         from--;
303                         to--;
304                 }
305
306                 *endptr = from;
307
308                 /*
309                  * use memcpy to avoid NULL terminating prematurely
310                  * the target string
311                  */
312                 memcpy(variable_start, value, value_len);
313         }
314
315         return overflow;
316 }
317
318 /*
319  * Replace variable names from a string with the variable
320  * contents. Variable names are in format: $(name)
321  */
322 int replace_variables_with_values(char *str, size_t len,
323                                 const struct plotter_config *cfg)
324 {
325         char *ptr = str;
326         int overflow = 0;
327
328         while (*ptr && ptr < str + len) {
329                 char *replace_start, *variable_start, *variable_end, *value;
330
331                 SKIP_UNTIL(ptr, *ptr != '$');
332
333                 if (!*ptr)
334                         break;
335
336                 replace_start = ptr;
337                 ptr++;
338
339                 /* Skip if not in correct format */
340                 if (*ptr != '(')
341                         continue;
342
343                 ptr++;
344                 variable_start = ptr;
345
346                 SKIP_UNTIL(ptr, *ptr != ')');
347
348                 variable_end = ptr;
349                 *ptr = 0;
350
351                 value = get_value_for_variable(variable_start, cfg);
352                 if (!value) {
353                         pr_debug("Could not substitute unknown variable name %s\n",
354                                 variable_start);
355
356                         /*
357                          * Put back the closing parenthesis so that
358                          * the line remains as it was
359                          */
360                         *variable_end = ')';
361                         continue;
362                 }
363                 overflow = replace_variable_with_value(str, len, replace_start,
364                                         variable_end, value, &ptr);
365         }
366
367         if (overflow)
368                 pr_err("String buffer overflow\n");
369
370         return overflow;
371 }