chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / econfig.c
1 /*
2  * Copyright (C) 2004-2008 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 "E.h"
24 #include "econfig.h"
25 #include "emodule.h"
26 #include <ctype.h>
27
28 /*
29  * Braindead flat ASCII config file implementation
30  */
31
32 typedef struct {
33    char               *key;
34    char               *value;
35 } ECfgFileItem;
36
37 typedef struct {
38    FILE               *fs;
39    int                 nitms;
40    ECfgFileItem       *pitms;
41 } ECfgFile;
42
43 static void         CfgItemSetFromString(const CfgItem * ci, const char *str,
44                                          int set_dflt);
45
46 static ECfgFile    *
47 e16_db_open(const char *name)
48 {
49    ECfgFile           *ecf;
50    FILE               *fs;
51
52    fs = fopen(name, "w");
53    if (!fs)
54       return NULL;
55
56    ecf = ECALLOC(ECfgFile, 1);
57    if (!ecf)
58       goto done;
59
60    ecf->fs = fs;
61
62  done:
63    if (!ecf)
64       fclose(fs);
65    return ecf;
66 }
67
68 static ECfgFile    *
69 e16_db_open_read(const char *name)
70 {
71    ECfgFile           *ecf;
72    FILE               *fs;
73    char                buf[4096], key[128], *s;
74    int                 i, len;
75
76    fs = fopen(name, "r");
77    if (!fs)
78       return NULL;
79
80    ecf = ECALLOC(ECfgFile, 1);
81    if (!ecf)
82       goto done;
83
84    for (;;)
85      {
86         s = fgets(buf, sizeof(buf), fs);
87         if (!s)
88            break;
89
90         /* Strip comment and trailing whitespace */
91         i = strcspn(s, "#\r\n");
92         for (; i > 0; i--)
93            if (!isspace(s[i - 1]))
94               break;
95         s[i] = '\0';
96
97         len = 0;
98         i = sscanf(s, "%100s = %n", key, &len);
99         if (i <= 0 || len <= 0)
100            continue;            /* Ignore bad format */
101
102         i = ecf->nitms++;
103         ecf->pitms = EREALLOC(ECfgFileItem, ecf->pitms, ecf->nitms);
104         ecf->pitms[i].key = Estrdup(key);
105         ecf->pitms[i].value = Estrdup(s + len);
106      }
107
108  done:
109    fclose(fs);
110    return ecf;
111 }
112
113 static void
114 e16_db_close(ECfgFile * ecf)
115 {
116    int                 i;
117
118    if (ecf->pitms)
119      {
120         for (i = 0; i < ecf->nitms; i++)
121           {
122              Efree(ecf->pitms[i].key);
123              Efree(ecf->pitms[i].value);
124           }
125         Efree(ecf->pitms);
126      }
127    if (ecf->fs)
128       fclose(ecf->fs);
129    Efree(ecf);
130 }
131
132 static void
133 e16_db_flush(void)
134 {
135 }
136
137 static const char  *
138 ECfgFileFindValue(ECfgFile * ecf, const char *key)
139 {
140    int                 i;
141
142    for (i = 0; i < ecf->nitms; i++)
143       if (!strcmp(key, ecf->pitms[i].key))
144          return ecf->pitms[i].value;
145
146    return NULL;
147 }
148
149 /*
150  * Configuration handling.
151  */
152
153 static void
154 CfgItemLoad(ECfgFile * ecf, const char *prefix, const CfgItem * ci)
155 {
156    char                buf[1024];
157    const char         *name = buf;
158    const char         *value;
159
160    if (prefix)
161       Esnprintf(buf, sizeof(buf), "%s.%s", prefix, ci->name);
162    else
163       name = ci->name;
164
165    if (EDebug(EDBUG_TYPE_CONFIG) > 1)
166       Eprintf("CfgItemLoad %s\n", name);
167
168    if (!ci->ptr)
169       return;
170
171    value = (ecf) ? ECfgFileFindValue(ecf, name) : NULL;
172    CfgItemSetFromString(ci, value, 1);
173 }
174
175 static void
176 CfgItemSave(ECfgFile * ecf, const char *prefix, const CfgItem * ci)
177 {
178    char                buf[1024], buf2[1024];
179    const char         *name = buf;
180
181    if (prefix)
182       Esnprintf(buf, sizeof(buf), "%s.%s", prefix, ci->name);
183    else
184       name = ci->name;
185
186    if (EDebug(EDBUG_TYPE_CONFIG) > 1)
187       Eprintf("CfgItemSave %s\n", name);
188
189    if (!ci->ptr)
190       return;
191
192    CfgItemToString(ci, buf2, sizeof(buf2));
193    fprintf(ecf->fs, "%s = %s\n", name, buf2);
194 }
195
196 static const char  *
197 ConfigurationGetFile(char *buf, int len)
198 {
199    Esnprintf(buf, len, "%s.cfg", EGetSavePrefix());
200    return buf;
201 }
202
203 void
204 ConfigurationLoad(void)
205 {
206    int                 i, nml, j, ncl;
207    const EModule     **pml, *pm;
208    const CfgItem      *pcl;
209    char                buf[4096];
210    ECfgFile           *ecf;
211
212    if (EDebug(EDBUG_TYPE_CONFIG))
213       Eprintf("ConfigurationLoad\n");
214
215    memset(&Conf, 0, sizeof(EConf));
216
217    ecf = e16_db_open_read(ConfigurationGetFile(buf, sizeof(buf)));
218    /* NB! We have to assign the defaults even if it doesn't exist */
219
220    /* Load module configs */
221    pml = ModuleListGet(&nml);
222    for (i = 0; i < nml; i++)
223      {
224         pm = pml[i];
225         ncl = pm->cfg.num;
226         pcl = pm->cfg.lst;
227         for (j = 0; j < ncl; j++)
228            CfgItemLoad(ecf, pm->name, pcl + j);
229      }
230    ModuleListFree(pml);
231
232    if (ecf)
233       e16_db_close(ecf);
234 }
235
236 void
237 ConfigurationSave(void)
238 {
239    int                 i, nml, j, ncl;
240    const EModule     **pml, *pm;
241    const CfgItem      *pcl;
242    char                buf[4096];
243    ECfgFile           *ecf;
244
245    if (EDebug(EDBUG_TYPE_CONFIG))
246       Eprintf("ConfigurationSave\n");
247
248    ecf = e16_db_open(ConfigurationGetFile(buf, sizeof(buf)));
249    if (ecf == NULL)
250       return;
251
252    /* Load module configs */
253    pml = ModuleListGet(&nml);
254    for (i = 0; i < nml; i++)
255      {
256         pm = pml[i];
257         ncl = pm->cfg.num;
258         pcl = pm->cfg.lst;
259         for (j = 0; j < ncl; j++)
260            CfgItemSave(ecf, pm->name, pcl + j);
261      }
262    ModuleListFree(pml);
263
264    e16_db_close(ecf);
265    e16_db_flush();
266 }
267
268 const CfgItem      *
269 CfgItemFind(const CfgItem * pcl, int ncl, const char *name)
270 {
271    int                 i;
272
273    for (i = 0; i < ncl; i++, pcl++)
274       if (!strcmp(name, pcl->name))
275          return pcl;
276    return NULL;
277 }
278
279 static void
280 CfgItemSetFromString(const CfgItem * ci, const char *str, int set_dflt)
281 {
282    int                 ival;
283
284 #ifdef ITEM_TYPE_FLOAT
285    int                 n;
286    float               fval;
287 #endif
288    char               *ptr;
289
290    ptr = (char *)str;
291    switch (ci->type)
292      {
293      case ITEM_TYPE_BOOL:
294         ival = (str) ? strtoul(str, &ptr, 0) : 0;
295         if (ptr <= str)
296           {
297              if (!set_dflt)
298                 break;
299              ival = (ci->dflt) ? 1 : 0;
300           }
301         *((char *)ci->ptr) = ival;
302         break;
303      case ITEM_TYPE_INT:
304      case ITEM_TYPE_HEX:
305         ival = (str) ? strtoul(str, &ptr, 0) : 0;
306         if (ptr <= str)
307           {
308              if (!set_dflt)
309                 break;
310              ival = ci->dflt;
311           }
312         *((int *)ci->ptr) = ival;
313         break;
314 #ifdef ITEM_TYPE_FLOAT
315      case ITEM_TYPE_FLOAT:
316         n = (str) ? sscanf(str, "%f", &fval) : 0;
317         if (n <= 0)
318           {
319              if (!set_dflt)
320                 break;
321              fval = ci->dflt;
322           }
323         *((float *)ci->ptr) = fval;
324         break;
325 #endif
326      case ITEM_TYPE_STRING:
327         Efree(*(char **)ci->ptr);
328         if (str && *str == '\0')
329            str = NULL;
330         *((char **)ci->ptr) = Estrdup(str);
331         break;
332      }
333 }
334
335 void
336 CfgItemToString(const CfgItem * ci, char *buf, int len)
337 {
338    buf[0] = '\0';
339    switch (ci->type)
340      {
341      case ITEM_TYPE_BOOL:
342         Esnprintf(buf, len, "%d", *((char *)ci->ptr));
343         break;
344      case ITEM_TYPE_INT:
345         Esnprintf(buf, len, "%d", *((int *)ci->ptr));
346         break;
347      case ITEM_TYPE_HEX:
348         Esnprintf(buf, len, "%#x", *((unsigned int *)ci->ptr));
349         break;
350 #ifdef ITEM_TYPE_FLOAT
351      case ITEM_TYPE_FLOAT:
352         Esnprintf(buf, len, "%.3f", *((float *)ci->ptr));
353         break;
354 #endif
355      case ITEM_TYPE_STRING:
356         if (*((char **)ci->ptr))
357            Esnprintf(buf, len, "%s", *((char **)ci->ptr));
358         break;
359      }
360 }
361
362 int
363 CfgItemListNamedItemSet(const CfgItem * pcl, int ncl, const char *item,
364                         const char *value)
365 {
366    const CfgItem      *ci;
367
368    ci = CfgItemFind(pcl, ncl, item);
369    if (!ci)
370       return -1;
371
372    if (ci->func)
373       ci->func(ci->ptr, value);
374    else
375       CfgItemSetFromString(ci, value, 0);
376
377    return 0;
378 }
379
380 #if 0                           /* Unused */
381 int
382 CfgItemListNamedItemToString(const CfgItem * pcl, int ncl, const char *item,
383                              char *buf, int len)
384 {
385    const CfgItem      *ci;
386
387    ci = CfgItemFind(pcl, ncl, item);
388    if (!ci)
389       return -1;
390    CfgItemToString(ci, buf, len);
391
392    return 0;
393 }
394 #endif
395
396 /*
397  * Set <module>.<item> <value>
398  */
399 void
400 ConfigurationSet(const char *params)
401 {
402    const char         *p;
403    char                name[1024];
404    char                item[1024];
405    unsigned int        len;
406
407    if (!params)
408       return;
409
410    p = strchr(params, '.');
411    if (!p)
412      {
413         Eprintf("ConfigurationSet - missed: %s\n", params);
414         return;
415      }
416
417    len = p - params;
418    if (len >= sizeof(name))
419       len = sizeof(name) - 1;
420    memcpy(name, params, len);
421    name[len] = '\0';
422    p++;
423    len = 0;
424    sscanf(p, "%1000s %n", item, &len);
425    p += len;
426    ModuleConfigSet(name, item, p);
427
428    /* Save changed configuration */
429    autosave();
430 }
431
432 /*
433  * Show <module>.<item> <value>
434  */
435 void
436 ConfigurationShow(const char *params)
437 {
438    const char         *p;
439    char                name[1024];
440    char                item[1024];
441    unsigned int        len;
442
443    /* No parameters - All */
444    if (!params || params[0] == '\0')
445      {
446         ModulesConfigShow();
447         return;
448      }
449
450    /* No '.' - All for module */
451    p = strchr(params, '.');
452    if (!p)
453      {
454         ModuleConfigShow(params, NULL);
455         return;
456      }
457
458    /* Specific module, specific item. */
459    len = p - params;
460    if (len >= sizeof(name))
461       len = sizeof(name) - 1;
462    memcpy(name, params, len);
463    name[len] = '\0';
464    p++;
465    sscanf(p, "%s", item);
466    ModuleConfigShow(name, item);
467 }