1 /*
2 * Copyright (C) 2004-2015 Kim Woelders
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include "config.h"
24
25 #include <ctype.h>
26
27 #include "E.h"
28 #include "econfig.h"
29 #include "emodule.h"
30
31 /*
32 * Braindead flat ASCII config file implementation
33 */
34
35 typedef struct {
36 char *key;
37 char *value;
38 } ECfgFileItem;
39
40 typedef struct {
41 FILE *fs;
42 int nitms;
43 ECfgFileItem *pitms;
44 } ECfgFile;
45
46 static void CfgItemSetFromString(const CfgItem * ci, const char *str,
47 int set_dflt);
48
49 static ECfgFile *
e16_db_open(const char * name)50 e16_db_open(const char *name)
51 {
52 ECfgFile *ecf;
53 FILE *fs;
54
55 fs = fopen(name, "w");
56 if (!fs)
57 return NULL;
58
59 ecf = ECALLOC(ECfgFile, 1);
60 if (!ecf)
61 goto done;
62
63 ecf->fs = fs;
64
65 done:
66 if (!ecf)
67 fclose(fs);
68 return ecf;
69 }
70
71 static ECfgFile *
e16_db_open_read(const char * name)72 e16_db_open_read(const char *name)
73 {
74 ECfgFile *ecf;
75 FILE *fs;
76 char buf[4096], key[128], *s;
77 int i, len;
78
79 fs = fopen(name, "r");
80 if (!fs)
81 return NULL;
82
83 ecf = ECALLOC(ECfgFile, 1);
84 if (!ecf)
85 goto done;
86
87 for (;;)
88 {
89 s = fgets(buf, sizeof(buf), fs);
90 if (!s)
91 break;
92
93 /* Strip comment and trailing whitespace */
94 i = strcspn(s, "#\r\n");
95 for (; i > 0; i--)
96 if (!isspace(s[i - 1]))
97 break;
98 s[i] = '\0';
99
100 len = 0;
101 i = sscanf(s, "%100s = %n", key, &len);
102 if (i <= 0 || len <= 0)
103 continue; /* Ignore bad format */
104
105 i = ecf->nitms++;
106 ecf->pitms = EREALLOC(ECfgFileItem, ecf->pitms, ecf->nitms);
107 ecf->pitms[i].key = Estrdup(key);
108 ecf->pitms[i].value = Estrdup(s + len);
109 }
110
111 done:
112 fclose(fs);
113 return ecf;
114 }
115
116 static void
e16_db_close(ECfgFile * ecf)117 e16_db_close(ECfgFile * ecf)
118 {
119 int i;
120
121 if (ecf->pitms)
122 {
123 for (i = 0; i < ecf->nitms; i++)
124 {
125 Efree(ecf->pitms[i].key);
126 Efree(ecf->pitms[i].value);
127 }
128 Efree(ecf->pitms);
129 }
130 if (ecf->fs)
131 fclose(ecf->fs);
132 Efree(ecf);
133 }
134
135 static const char *
ECfgFileFindValue(ECfgFile * ecf,const char * key)136 ECfgFileFindValue(ECfgFile * ecf, const char *key)
137 {
138 int i;
139
140 for (i = 0; i < ecf->nitms; i++)
141 if (!strcmp(key, ecf->pitms[i].key))
142 return ecf->pitms[i].value;
143
144 return NULL;
145 }
146
147 /*
148 * Configuration handling.
149 */
150
151 static void
CfgItemLoad(ECfgFile * ecf,const char * prefix,const CfgItem * ci,int dflt)152 CfgItemLoad(ECfgFile * ecf, const char *prefix, const CfgItem * ci, int dflt)
153 {
154 char buf[1024];
155 const char *name = buf;
156 const char *value;
157
158 if (prefix)
159 Esnprintf(buf, sizeof(buf), "%s.%s", prefix, ci->name);
160 else
161 name = ci->name;
162
163 if (EDebug(EDBUG_TYPE_CONFIG) > 1)
164 Eprintf("%s: %s\n", __func__, name);
165
166 if (!ci->ptr)
167 return;
168
169 value = (ecf) ? ECfgFileFindValue(ecf, name) : NULL;
170 if (value || dflt)
171 CfgItemSetFromString(ci, value, 1);
172 }
173
174 static void
CfgItemSave(ECfgFile * ecf,const char * prefix,const CfgItem * ci)175 CfgItemSave(ECfgFile * ecf, const char *prefix, const CfgItem * ci)
176 {
177 char buf[1024], buf2[1024];
178 const char *name = buf;
179
180 if (prefix)
181 Esnprintf(buf, sizeof(buf), "%s.%s", prefix, ci->name);
182 else
183 name = ci->name;
184
185 if (EDebug(EDBUG_TYPE_CONFIG) > 1)
186 Eprintf("%s: %s\n", __func__, name);
187
188 if (!ci->ptr)
189 return;
190
191 CfgItemToString(ci, buf2, sizeof(buf2));
192 fprintf(ecf->fs, "%s = %s\n", name, buf2);
193 }
194
195 static const char *
ConfigurationGetFile(char * buf,int len)196 ConfigurationGetFile(char *buf, int len)
197 {
198 Esnprintf(buf, len, "%s.cfg", EGetSavePrefix());
199 return buf;
200 }
201
202 static void
_ConfigurationLoad(const char * file,int dflt)203 _ConfigurationLoad(const char *file, int dflt)
204 {
205 int i, nml, j, ncl;
206 const EModule *const *pml, *pm;
207 const CfgItem *pcl;
208 ECfgFile *ecf;
209
210 if (EDebug(EDBUG_TYPE_CONFIG))
211 Eprintf("%s\n", __func__);
212
213 ecf = e16_db_open_read(file);
214 /* NB! We have to assign the defaults even if it doesn't exist */
215 if (!ecf && !dflt)
216 return;
217
218 /* Load module configs */
219 MODULE_LIST_GET(pml, nml);
220 for (i = 0; i < nml; i++)
221 {
222 pm = pml[i];
223 ncl = pm->cfg.num;
224 pcl = pm->cfg.lst;
225 for (j = 0; j < ncl; j++)
226 CfgItemLoad(ecf, pm->name, pcl + j, dflt);
227 }
228 MODULE_LIST_FREE(pml);
229
230 if (ecf)
231 e16_db_close(ecf);
232 }
233
234 void
ConfigurationLoad(void)235 ConfigurationLoad(void)
236 {
237 char buf[4096];
238
239 memset(&Conf, 0, sizeof(EConf));
240
241 _ConfigurationLoad(ConfigurationGetFile(buf, sizeof(buf)), 1);
242 }
243
244 void
ConfigurationSave(void)245 ConfigurationSave(void)
246 {
247 int i, nml, j, ncl;
248 const EModule *const *pml, *pm;
249 const CfgItem *pcl;
250 char buf[4096];
251 ECfgFile *ecf;
252
253 if (EDebug(EDBUG_TYPE_CONFIG))
254 Eprintf("%s\n", __func__);
255
256 ecf = e16_db_open(ConfigurationGetFile(buf, sizeof(buf)));
257 if (!ecf)
258 return;
259
260 /* Load module configs */
261 MODULE_LIST_GET(pml, nml);
262 for (i = 0; i < nml; i++)
263 {
264 pm = pml[i];
265 ncl = pm->cfg.num;
266 pcl = pm->cfg.lst;
267 for (j = 0; j < ncl; j++)
268 CfgItemSave(ecf, pm->name, pcl + j);
269 }
270 MODULE_LIST_FREE(pml);
271
272 e16_db_close(ecf);
273 }
274
275 const CfgItem *
CfgItemFind(const CfgItem * pcl,int ncl,const char * name)276 CfgItemFind(const CfgItem * pcl, int ncl, const char *name)
277 {
278 int i;
279
280 for (i = 0; i < ncl; i++, pcl++)
281 if (!strcmp(name, pcl->name))
282 return pcl;
283 return NULL;
284 }
285
286 static void
CfgItemSetFromString(const CfgItem * ci,const char * str,int set_dflt)287 CfgItemSetFromString(const CfgItem * ci, const char *str, int set_dflt)
288 {
289 int ival;
290
291 #ifdef ITEM_TYPE_FLOAT
292 int n;
293 float fval;
294 #endif
295 char *ptr;
296
297 ptr = (char *)str;
298 switch (ci->type)
299 {
300 case ITEM_TYPE_BOOL:
301 ival = (str) ? strtoul(str, &ptr, 0) : 0;
302 if (ptr <= str)
303 {
304 if (!set_dflt)
305 break;
306 ival = (ci->dflt) ? 1 : 0;
307 }
308 *((char *)ci->ptr) = ival;
309 break;
310 case ITEM_TYPE_INT:
311 case ITEM_TYPE_HEX:
312 ival = (str) ? strtoul(str, &ptr, 0) : 0;
313 if (ptr <= str)
314 {
315 if (!set_dflt)
316 break;
317 ival = ci->dflt;
318 }
319 *((int *)ci->ptr) = ival;
320 break;
321 #ifdef ITEM_TYPE_FLOAT
322 case ITEM_TYPE_FLOAT:
323 n = (str) ? sscanf(str, "%f", &fval) : 0;
324 if (n <= 0)
325 {
326 if (!set_dflt)
327 break;
328 fval = ci->dflt;
329 }
330 *((float *)ci->ptr) = fval;
331 break;
332 #endif
333 case ITEM_TYPE_STRING:
334 Efree(*(char **)ci->ptr);
335 if (str && *str == '\0')
336 str = NULL;
337 *((char **)ci->ptr) = Estrdup(str);
338 break;
339 }
340 }
341
342 void
CfgItemToString(const CfgItem * ci,char * buf,int len)343 CfgItemToString(const CfgItem * ci, char *buf, int len)
344 {
345 buf[0] = '\0';
346 switch (ci->type)
347 {
348 case ITEM_TYPE_BOOL:
349 Esnprintf(buf, len, "%d", *((char *)ci->ptr));
350 break;
351 case ITEM_TYPE_INT:
352 Esnprintf(buf, len, "%d", *((int *)ci->ptr));
353 break;
354 case ITEM_TYPE_HEX:
355 Esnprintf(buf, len, "%#x", *((unsigned int *)ci->ptr));
356 break;
357 #ifdef ITEM_TYPE_FLOAT
358 case ITEM_TYPE_FLOAT:
359 Esnprintf(buf, len, "%.3f", *((float *)ci->ptr));
360 break;
361 #endif
362 case ITEM_TYPE_STRING:
363 if (*((char **)ci->ptr))
364 Esnprintf(buf, len, "%s", *((char **)ci->ptr));
365 break;
366 }
367 }
368
369 int
CfgItemListNamedItemSet(const CfgItem * pcl,int ncl,const char * item,const char * value)370 CfgItemListNamedItemSet(const CfgItem * pcl, int ncl, const char *item,
371 const char *value)
372 {
373 const CfgItem *ci;
374
375 ci = CfgItemFind(pcl, ncl, item);
376 if (!ci)
377 return -1;
378
379 if (ci->func)
380 ci->func(ci->ptr, value);
381 else
382 CfgItemSetFromString(ci, value, 0);
383
384 return 0;
385 }
386
387 #if 0 /* Unused */
388 int
389 CfgItemListNamedItemToString(const CfgItem * pcl, int ncl, const char *item,
390 char *buf, int len)
391 {
392 const CfgItem *ci;
393
394 ci = CfgItemFind(pcl, ncl, item);
395 if (!ci)
396 return -1;
397 CfgItemToString(ci, buf, len);
398
399 return 0;
400 }
401 #endif
402
403 /*
404 * Set <module>.<item> <value>
405 */
406 void
ConfigurationSet(const char * params)407 ConfigurationSet(const char *params)
408 {
409 const char *p;
410 char name[1024];
411 char item[1024];
412 unsigned int len;
413
414 if (!params)
415 return;
416
417 p = strchr(params, '.');
418 if (!p)
419 {
420 Eprintf("%s: missed: %s\n", __func__, params);
421 return;
422 }
423
424 len = p - params;
425 if (len >= sizeof(name))
426 len = sizeof(name) - 1;
427 memcpy(name, params, len);
428 name[len] = '\0';
429 p++;
430 len = 0;
431 sscanf(p, "%1000s %n", item, &len);
432 p += len;
433 ModuleConfigSet(name, item, p);
434
435 /* Save changed configuration */
436 autosave();
437 }
438
439 /*
440 * Show <module>.<item> <value>
441 */
442 void
ConfigurationShow(const char * params)443 ConfigurationShow(const char *params)
444 {
445 const char *p;
446 char name[1024];
447 char item[1024];
448 unsigned int len;
449
450 /* No parameters - All */
451 if (!params || params[0] == '\0')
452 {
453 ModulesConfigShow();
454 return;
455 }
456
457 /* No '.' - All for module */
458 p = strchr(params, '.');
459 if (!p)
460 {
461 ModuleConfigShow(params, NULL);
462 return;
463 }
464
465 /* Specific module, specific item. */
466 len = p - params;
467 if (len >= sizeof(name))
468 len = sizeof(name) - 1;
469 memcpy(name, params, len);
470 name[len] = '\0';
471 p++;
472 sscanf(p, "%s", item);
473 ModuleConfigShow(name, item);
474 }
475