]> mj.ucw.cz Git - libucw.git/blob - lib/shell/config.c
2395458408e5e36d466ba8f7f7dccd5324c50dc7
[libucw.git] / lib / shell / config.c
1 /*
2  *      UCW Library -- Shell Interface to Configuration Files
3  *
4  *      (c) 2002--2005 Martin Mares <mj@ucw.cz>
5  *      (c) 2006 Robert Spalek <robert@ucw.cz>
6  *      (c) 2006 Pavel Charvat <pchar@ucw.cz>
7  *
8  *      Once we were using this beautiful Shell version, but it turned out
9  *      that it doesn't work with nested config files:
10  *
11  *              eval `sed <cf/sherlock '/^#/d;/^ *$/d;s/ \+$//;
12  *              h;s@[^  ]*@@;x;s@[      ].*@@;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;G;s/\n//;
13  *              /^\[SECTION\]/,/^\[/ {; /^[A-Z]/ { s/^\([^      ]\+\)[  ]*\(.*\)$/SH_\1="\2"/; p; }; };
14  *              d;'`
15  *
16  *      This software may be freely distributed and used according to the terms
17  *      of the GNU Lesser General Public License.
18  */
19
20 #include "lib/lib.h"
21 #include "lib/conf.h"
22 #include "lib/getopt.h"
23 #include "lib/clists.h"
24 #include "lib/mempool.h"
25 #include "lib/chartype.h"
26 #include "lib/bbuf.h"
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <alloca.h>
32
33 static void
34 help(void)
35 {
36   fputs("\n\
37 Usage: config [-C<configfile>] [-S<section>.<option>=<value>] <sections>\n\
38 \n\
39 <sections>\t<section>[;<sections>]\n\
40 <section>\t[*]<name>{[<items>]}\n\
41 <items>\t\t[-]<item>[;<items>]\n\
42 <item>\t\t<static> | <array> | <list>\n\
43 <static>\t<type><name>[=<value>]\n\
44 <list>\t\t@<name>{[<items>]}\n\
45 <array>\t\t<type><name><left-bracket>[<number>]<right-bracket>\n\
46 <value>\t\t[a-zA-Z0-9.-/]* | 'string without single quotes'<value> | \"c-like string\"<value>\n\
47 \n\
48 Types:\n\
49 :\t\tString\n\
50 #\t\t32-bit integer\n\
51 ##\t\t64-bit integer\n\
52 $\t\tFloating point number\n\
53 \n\
54 Modifiers:\n\
55 !\t\tReport unknown items as errors\n\
56 -\t\tDo not dump item's value\n\
57 ", stderr);
58   exit(1);
59 }
60
61 union value {
62   void *v_ptr;
63   int v_int;
64   u64 v_u64;
65   double v_double;
66   clist list;
67 };
68
69 #define FLAG_HIDE               0x1
70 #define FLAG_NO_UNKNOWN         0x2
71
72 struct item {
73   cnode node;
74   uns flags;
75   struct cf_item cf;
76   union value value;
77   uns index;
78 };
79
80 struct section {
81   struct item item;
82   clist list;
83   uns count;
84   uns size;
85 };
86
87 static struct mempool *pool;
88 static clist sections;
89 static byte *pos;
90
91 static void
92 parse_white(void)
93 {
94   while (Cspace(*pos))
95     pos++;
96 }
97
98 static void
99 parse_char(byte c)
100 {
101   if (*pos++ != c)
102     die("Missing '%c'", c);
103 }
104
105 static byte *
106 parse_name(void)
107 {
108   byte *name = pos;
109   while (Cword(*pos))
110     pos++;
111   uns len = pos - name;
112   if (!len)
113     die("Expected item/section name");
114   byte *buf = mp_alloc(pool, len + 1);
115   memcpy(buf, name, len);
116   buf[len] = 0;
117   return buf;
118 }
119
120 static void
121 parse_section(struct section *section)
122 {
123 #define TRY(x) do{byte *_err=(x); if (_err) die(_err); }while(0)
124   for (uns sep = 0; ; sep = 1)
125     {
126       parse_white();
127       if (!*pos || *pos == '}')
128         break;
129       if (sep)
130         parse_char(';');
131       parse_white();
132
133       struct item *item;
134
135       if (*pos == '@')
136         {
137           pos++;
138           struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
139           sec->size = sizeof(cnode);
140           clist_init(&sec->list);
141           item = &sec->item;
142           item->cf.name = parse_name();
143           item->cf.cls = CC_LIST;
144           item->cf.number = 1;
145           parse_white();
146           parse_char('{');
147           parse_section(sec);
148           parse_char('}');
149         }
150       else
151         {
152           item = mp_alloc_zero(pool, sizeof(*item));
153           if (*pos == '-')
154             {
155               item->flags |= FLAG_HIDE;
156               pos++;
157             }
158           item->cf.cls = CC_STATIC;
159           item->cf.number = 1;
160           switch (*pos++)
161             {
162               case '#':
163                 if (*pos == '#')
164                   {
165                     pos++;
166                     item->cf.type = CT_U64;
167                   }
168                 else
169                   item->cf.type = CT_INT;
170                 break;
171               case '$':
172                 item->cf.type = CT_DOUBLE;
173                 break;
174               case ':':
175                 item->cf.type = CT_STRING;
176                 break;
177               default:
178                 die("Invalid type syntax");
179             }
180           parse_white();
181           item->cf.name = parse_name();
182           parse_white();
183           if (*pos == '[')
184             {
185               pos++;
186               parse_white();
187               item->cf.cls = CC_DYNAMIC;
188               byte *num = pos;
189               while (*pos && *pos != ']')
190                 pos++;
191               if (!*pos)
192                 die("Missing ']'");
193               *pos++ = 0;
194               if (!*num)
195                 item->cf.number = CF_ANY_NUM;
196               else
197                 {
198                   int inum;
199                   TRY(cf_parse_int(num, &inum));
200                   if (!inum)
201                     die("Invalid array length");
202                   item->cf.number = inum;
203                 }
204               parse_white();
205             }
206           if (*pos == '=')
207             {
208               pos++;
209               parse_white();
210               if (section->item.cf.cls == CC_LIST)
211                 die("List items can not have default values");
212               if (item->cf.cls == CC_DYNAMIC)
213                 die("Arrays can not have default values");
214               byte *def = pos, *d = def;
215               while (*pos != ';' && *pos != '}' && !Cspace(*pos))
216                 {
217                   if (*pos == '\'')
218                     {
219                       pos++;
220                       while (*pos != '\'')
221                         {
222                           if (!*pos)
223                             die("Unterminated string");
224                           *d++ = *pos++;
225                         }
226                       pos++;
227                     }
228                   else if (*pos == '"')
229                     {
230                       pos++;
231                       byte *start = d;
232                       uns esc = 0;
233                       while (*pos != '"' || esc)
234                         {
235                           if (!*pos)
236                             die("Unterminated string");
237                           if (*pos == '\\')
238                             esc ^= 1;
239                           else
240                             esc = 0;
241                           *d++ = *pos++;
242                         }
243                       pos++;
244                       *d = 0;
245                       d = str_unesc(start, start);
246                     }
247                   else
248                     *d++ = *pos++;
249                 }
250               uns len = d - def;
251               byte *buf = mp_alloc(pool, len + 1);
252               memcpy(buf, def, len);
253               buf[len] = 0;
254               switch (item->cf.type)
255                 {
256                   case CT_STRING:
257                     item->value.v_ptr = buf;
258                     break;
259                   case CT_INT:
260                     TRY(cf_parse_int(buf, &item->value.v_int));
261                     break;
262                   case CT_U64:
263                     TRY(cf_parse_u64(buf, &item->value.v_u64));
264                     break;
265                   case CT_DOUBLE:
266                     TRY(cf_parse_double(buf, &item->value.v_double));
267                     break;
268                   default:
269                     ASSERT(0);
270                 }
271             }
272         }
273       if (section->item.cf.cls == CC_LIST)
274         {
275           item->cf.ptr = (void *)section->size;
276           section->size += sizeof(union value);
277         }
278       else
279         item->cf.ptr = &item->value;
280       clist_add_tail(&section->list, &item->node);
281       section->count++;
282     }
283 #undef TRY
284 }
285
286 static void
287 parse_outer(void)
288 {
289   for (uns sep = 0; ; sep = 1)
290     {
291       parse_white();
292       if (!*pos)
293         break;
294       if (sep)
295         parse_char(';');
296       parse_white();
297       struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
298       if (*pos == '!')
299         {
300           pos++;
301           sec->item.flags |= FLAG_NO_UNKNOWN;
302         }
303       sec->item.cf.name = parse_name();
304       parse_white();
305       parse_char('{');
306       clist_add_tail(&sections, &sec->item.node);
307       clist_init(&sec->list);
308       parse_section(sec);
309       parse_char('}');
310     }
311 }
312
313 static struct cf_section *
314 generate_section(struct section *section)
315 {
316   struct cf_section *sec = mp_alloc_zero(pool, sizeof(*sec));
317   if (section->item.cf.cls == CC_LIST)
318     sec->size = section->size;
319   struct cf_item *c = sec->cfg = mp_alloc_zero(pool, sizeof(struct cf_item) * (section->count + 1));
320   CLIST_FOR_EACH(struct item *, item, section->list)
321     {
322       *c = item->cf;
323       if (c->cls == CC_LIST)
324         c->u.sec = generate_section((struct section *)item);
325       c++;
326     }
327   c->cls = CC_END;
328   return sec;
329 }
330
331 static bb_t path;
332
333 static uns
334 type_size(enum cf_type type)
335 {
336   switch (type)
337     {
338       case CT_INT: return sizeof(int);
339       case CT_U64: return sizeof(u64);
340       case CT_DOUBLE: return sizeof(double);
341       case CT_STRING: return sizeof(byte *);
342       default: ASSERT(0);
343     }
344 }
345
346 static void
347 dump_value(uns array, struct item *item, void *v)
348 {
349   byte buf[128], *value = buf;
350   if (!array)
351     printf("CF_%s_%s='", path.ptr, item->cf.name);
352   else
353     printf("CF_%s_%s[%u]='", path.ptr, item->cf.name, ++item->index);
354   switch (item->cf.type)
355     {
356       case CT_INT:
357         sprintf(buf, "%d", *(int *)v);
358         break;
359       case CT_U64:
360         sprintf(buf, "%Lu", *(u64 *)v);
361         break;
362       case CT_DOUBLE:
363         sprintf(buf, "%g", *(double *)v);
364         break;
365       case CT_STRING:
366         if (*(byte **)v)
367           value = *(byte **)v;
368         else
369           *value = 0;
370         break;
371       default:
372         ASSERT(0);
373     }
374   while (*value) {
375     if (*value == '\'')
376       printf("'\\''");
377     else
378       putchar(*value);
379     value++;
380   }
381   printf("'\n");
382 }
383
384 static void
385 dump_item(struct item *item, void *ptr, uns path_len)
386 {
387   if (item->flags & FLAG_HIDE)
388     return;
389   byte *val = (byte *)((addr_int_t)ptr + (addr_int_t)item->cf.ptr);
390   if (item->cf.cls == CC_LIST)
391     {
392       uns len = strlen(item->cf.name);
393       bb_grow(&path, path_len + len + 1);
394       path.ptr[path_len] = '_';
395       memcpy(path.ptr + path_len + 1, item->cf.name, len);
396       CLIST_FOR_EACH(cnode *, ptr2, *(clist *)val)
397         CLIST_FOR_EACH(struct item *, item2, ((struct section *)item)->list)
398           dump_item(item2, ptr2, path_len + len + 1);
399     }
400   else
401     {
402       bb_grow(&path, path_len + 1)[path_len] = 0;
403       if (item->cf.cls == CC_STATIC)
404         dump_value(!!ptr, item, val);
405       else
406         {
407           val = *(void **)val;
408           uns len = DARY_LEN(val);
409           uns size = type_size(item->cf.type);
410           for (uns i = 0; i < len; i++, val += size)
411             dump_value(1, item, val);
412         }
413     }
414 }
415
416 int main(int argc, char **argv)
417 {
418   log_init("config");
419   if (argc < 2)
420     help();
421   pos = argv[argc - 1];
422   argv[argc - 1] = NULL;
423
424   pool = mp_new(0x1000);
425   clist_init(&sections);
426   parse_outer();
427   CLIST_FOR_EACH(struct section *, sec, sections)
428     cf_declare_section(sec->item.cf.name, generate_section(sec), !(sec->item.flags & FLAG_NO_UNKNOWN));
429
430   if (cf_getopt(argc - 1, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) != -1)
431     help();
432
433   bb_init(&path);
434   CLIST_FOR_EACH(struct section *, section, sections)
435     {
436       uns len = strlen(section->item.cf.name);
437       memcpy(bb_grow(&path, len), section->item.cf.name, len);
438       CLIST_FOR_EACH(struct item *, item, section->list)
439         dump_item(item, NULL, len);
440     }
441   bb_done(&path);
442
443   return 0;
444 }
445