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