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