]> git.itanic.dy.fi Git - log-plotter/blob - config.c
data.c: Implement initial support for handling multiple data channels
[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)
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                 while (*from) {
269                         *to = *from;
270                         from++;
271                         to++;
272                 }
273
274                 *to = 0;
275         } else {
276                 /*
277                  * The replacement string is longer than the space
278                  * occupied by the variable name. We need to first
279                  * make room for the value, without overflowing the
280                  * buffer. Make an overlapping scring copy, start from
281                  * the end.
282                  */
283
284                 /*
285                  * The closing parenthesis was replaced with null byte
286                  * earlier, put a non-zero char there to avoid
287                  * confusing strlen().
288                  */
289                 *variable_end = '.';
290
291                 from = str + strlen(str);
292                 to = from + (value_len - (variable_end - variable_start)) - 1;
293                 if (to >= str + len - 1) {
294                         to = str + len - 1;
295                         overflow = 1;
296                 }
297
298                 while (from >= variable_end) {
299                         *to = *from;
300                         from--;
301                         to--;
302                 }
303
304                 /*
305                  * use memcpy to avoid NULL terminating prematurely
306                  * the target string
307                  */
308                 memcpy(variable_start, value, value_len);
309         }
310
311         return overflow;
312 }
313
314 /*
315  * Replace variable names from a string with the variable
316  * contents. Variable names are in format: $(name)
317  */
318 int replace_variables_with_values(char *str, size_t len,
319                                 const struct plotter_config *cfg)
320 {
321         char *ptr = str;
322         int overflow = 0;
323
324         while (*ptr && ptr < str + len) {
325                 char *replace_start, *variable_start, *variable_end, *value;
326
327                 SKIP_UNTIL(ptr, *ptr != '$');
328
329                 if (!*ptr)
330                         break;
331
332                 replace_start = ptr;
333                 ptr++;
334
335                 /* Skip if not in correct format */
336                 if (*ptr != '(')
337                         continue;
338
339                 ptr++;
340                 variable_start = ptr;
341
342                 SKIP_UNTIL(ptr, *ptr != ')');
343
344                 variable_end = ptr;
345                 *ptr = 0;
346                 ptr++;
347
348                 value = get_value_for_variable(variable_start, cfg);
349                 if (!value) {
350                         pr_debug("Could not substitute unknown variable name %s\n",
351                                 variable_start);
352
353                         /*
354                          * Put back the closing parenthesis so that
355                          * the line remains as it was
356                          */
357                         *variable_end = ')';
358                         continue;
359                 }
360                 overflow = replace_variable_with_value(str, len, replace_start,
361                                         variable_end, value);
362         }
363
364         if (overflow)
365                 pr_err("String buffer overflow\n");
366
367         return overflow;
368 }