]> git.itanic.dy.fi Git - log-plotter/blob - config.c
config.c: Export store_str_variable_value_to_array()
[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 int store_str_variable_value_to_array(const char *variable, const char *value,
106                                 struct plotter_config *cfg)
107 {
108         struct variable_value *v;
109
110         v = prepare_slot_for_insertion(cfg, variable);
111
112         v->name = strdup(variable);
113         v->type = TYPE_STRING;
114         v->ptr = strdup(value);
115
116         return 0;
117 }
118
119 int store_int_variable_value_to_array(const char *variable, int value,
120                                 struct plotter_config *cfg)
121 {
122         struct variable_value *v;
123         int *p;
124
125         v = prepare_slot_for_insertion(cfg, variable);
126
127         v->name = strdup(variable);
128         v->type = TYPE_INT;
129         p = v->ptr = malloc(sizeof(value));
130         *p = value;
131
132         return 0;
133 }
134
135 static int store_variable_value_to_plotter_config(const char *variable,
136                                                 const char *value,
137                                                 struct plotter_config *cfg)
138 {
139 #define TEST_STR_STORE_STR_RETURN(_cfg, _variable, _str, _value)        \
140         if (!strcmp(_variable, #_str) && !cfg->_str) {                  \
141                 cfg->_str = strdup(_value);                             \
142                 return 0;                                               \
143         }
144
145 #define TEST_STR_STORE_INT_RETURN(_cfg, _variable, _str, _value)        \
146         if (!strcmp(_variable, #_str)) {                                \
147                 cfg->_str = atoi(_value);                               \
148                 return 0;                                               \
149         }
150
151         TEST_STR_STORE_STR_RETURN(cfg, variable, charger_name, value);
152         TEST_STR_STORE_STR_RETURN(cfg, variable, plotter_scripts_dir, value);
153         TEST_STR_STORE_STR_RETURN(cfg, variable, images_output_dir, value);
154         TEST_STR_STORE_STR_RETURN(cfg, variable, device_path, value);
155         TEST_STR_STORE_STR_RETURN(cfg, variable, log_path, value);
156         TEST_STR_STORE_INT_RETURN(cfg, variable, baudrate, value);
157
158         return store_str_variable_value_to_array(variable, value, cfg);
159 }
160
161 static int read_config_file(FILE *file, struct plotter_config *cfg)
162 {
163         char line[1024];
164         char *variable, *value;
165
166         while (fgets(line, sizeof(line), file)) {
167                 if (parse_config_line(line, &variable, &value))
168                         continue;
169
170                 if (!store_variable_value_to_plotter_config(variable, value,
171                                                                 cfg))
172                         continue;
173
174                 pr_debug("Discarding unsupported config variable %s\n",
175                         variable);
176         }
177
178         return 0;
179 }
180
181 int populate_config_data_from_file(const char *path,
182                                 struct plotter_config *cfg)
183 {
184         glob_t globbuf;
185         FILE *file;
186         int ret;
187
188         ret = glob(path, GLOB_TILDE, NULL, &globbuf);
189         if (ret)
190                 return -1;
191
192         if (globbuf.gl_pathc == 0) {
193                 pr_debug("No config file foud from path %s\n", path);
194                 return -1;
195         }
196
197         if (globbuf.gl_pathc > 1) {
198                 pr_warn("Found multiple config files from path %s, using the first one\n",
199                         path);
200         }
201
202         file = fopen(globbuf.gl_pathv[0], "r");
203         if (!file) {
204                 pr_err("Failed to open config file %s: %m\n",
205                         globbuf.gl_pathv[0]);
206                 return -1;
207         }
208
209         return read_config_file(file, cfg);
210 }
211
212 static char *get_value_for_variable(char *variable,
213                         const struct plotter_config *cfg)
214 {
215         static char result[256];
216         struct variable_value *values = cfg->variables;
217         int i;
218         int *ip;
219         char *cp;
220
221
222         for (i = 0; values[i].name; i++) {
223                 if (strcmp(values[i].name, variable))
224                         continue;
225
226                 switch (values[i].type) {
227                 case TYPE_INT:
228                         ip = values[i].ptr;
229                         snprintf(result, sizeof(result), "%d", *ip);
230                         break;
231                 case TYPE_STRING:
232                         cp = values[i].ptr;
233                         snprintf(result, sizeof(result), "%s", cp);
234                         break;
235                 default:
236                         pr_err("BUG: Unknown variable type %d\n",
237                                 values[i].type);
238                         return NULL;
239                 }
240
241                 return result;
242         }
243
244         return NULL;
245 }
246
247 static int replace_variable_with_value(char *str, size_t len,
248                                 char *variable_start, char *variable_end,
249                                 const char *value, char **endptr)
250 {
251         int value_len = strlen(value);
252         char *from, *to;
253         int overflow = 0;
254
255         if (variable_end - variable_start >= value_len) {
256                 /*
257                  * The replacement string is shorter than the variable
258                  * name, this is the easy case, no need to worry about
259                  * overflows.
260                  */
261
262                 strcpy(variable_start, value);
263
264                 from = variable_end + 1;
265                 to = variable_start + value_len;
266
267                 *endptr = to;
268
269                 while (*from) {
270                         *to = *from;
271                         from++;
272                         to++;
273                 }
274
275                 *to = 0;
276         } else {
277                 /*
278                  * The replacement string is longer than the space
279                  * occupied by the variable name. We need to first
280                  * make room for the value, without overflowing the
281                  * buffer. Make an overlapping scring copy, start from
282                  * the end.
283                  */
284
285                 /*
286                  * The closing parenthesis was replaced with null byte
287                  * earlier, put a non-zero char there to avoid
288                  * confusing strlen().
289                  */
290                 *variable_end = '.';
291
292                 from = str + strlen(str);
293                 to = from + (value_len - (variable_end - variable_start)) - 1;
294                 if (to >= str + len - 1) {
295                         to = str + len - 1;
296                         overflow = 1;
297                 }
298
299                 while (from >= variable_end) {
300                         *to = *from;
301                         from--;
302                         to--;
303                 }
304
305                 *endptr = from;
306
307                 /*
308                  * use memcpy to avoid NULL terminating prematurely
309                  * the target string
310                  */
311                 memcpy(variable_start, value, value_len);
312         }
313
314         return overflow;
315 }
316
317 /*
318  * Replace variable names from a string with the variable
319  * contents. Variable names are in format: $(name)
320  */
321 int replace_variables_with_values(char *str, size_t len,
322                                 const struct plotter_config *cfg)
323 {
324         char *ptr = str;
325         int overflow = 0;
326
327         while (*ptr && ptr < str + len) {
328                 char *replace_start, *variable_start, *variable_end, *value;
329
330                 SKIP_UNTIL(ptr, *ptr != '$');
331
332                 if (!*ptr)
333                         break;
334
335                 replace_start = ptr;
336                 ptr++;
337
338                 /* Skip if not in correct format */
339                 if (*ptr != '(')
340                         continue;
341
342                 ptr++;
343                 variable_start = ptr;
344
345                 SKIP_UNTIL(ptr, *ptr != ')');
346
347                 variable_end = ptr;
348                 *ptr = 0;
349
350                 value = get_value_for_variable(variable_start, cfg);
351                 if (!value) {
352                         pr_debug("Could not substitute unknown variable name %s\n",
353                                 variable_start);
354
355                         /*
356                          * Put back the closing parenthesis so that
357                          * the line remains as it was
358                          */
359                         *variable_end = ')';
360                         continue;
361                 }
362                 overflow = replace_variable_with_value(str, len, replace_start,
363                                         variable_end, value, &ptr);
364         }
365
366         if (overflow)
367                 pr_err("String buffer overflow\n");
368
369         return overflow;
370 }