]> mj.ucw.cz Git - libucw.git/blob - lib/shell/config.c
8a89e4a4dddcdf29beb1fac3c11a5f6a3bf7dd70
[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> | @<list>\n\
43 <static>\t[-]<type><name>\n\
44 <list>\t\t<name>{[<items>]}\n\
45 \n\
46 Types:\n\
47 <empty>\t\tString\n\
48 #\t\t32-bit integer\n\
49 ##\t\t64-bit integer\n\
50 $\t\tFloating point number\n\
51 \n\
52 Modifiers:\n\
53 !\t\tReport unknown items as errors\n\
54 -\t\tDo not dump item's value\n\
55 ", stderr);
56   exit(1);
57 }
58
59 union value {
60   void *v_ptr;
61   int v_int;
62   u64 v_u64;
63   double v_double;
64   clist list;
65 };
66
67 #define FLAG_HIDE               0x1
68 #define FLAG_NO_UNKNOWN         0x2
69
70 struct item {
71   cnode node;
72   uns flags;
73   struct cf_item cf;
74   union value value;
75 };
76
77 struct section {
78   struct item item;
79   clist list;
80   uns count;
81   uns size;
82 };
83
84 static struct mempool *pool;
85 static clist sections;
86 static byte *pos;
87
88 static void
89 parse_white(void)
90 {
91   while (Cspace(*pos))
92     pos++;
93 }
94
95 static void
96 parse_char(byte c)
97 {
98   if (*pos++ != c)
99     die("Missing '%c'", c);
100 }
101
102 static byte *
103 parse_name(void)
104 {
105   byte *name = pos;
106   while (Cword(*pos))
107     pos++;
108   uns len = pos - name;
109   if (!len)
110     die("Expected item/section name");
111   byte *buf = mp_alloc(pool, len + 1);
112   memcpy(buf, name, len);
113   buf[len] = 0;
114   return buf;
115 }
116
117 static void
118 parse_section(struct section *section)
119 {
120   for (uns sep = 0; ; sep = 1)
121     {
122       parse_white();
123       if (!*pos || *pos == '}')
124         break;
125       if (sep)
126         parse_char(';');
127       parse_white();
128
129       struct item *item;
130
131       if (*pos == '@')
132         {
133           pos++;
134           struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
135           sec->size = sizeof(cnode);
136           clist_init(&sec->list);
137           item = &sec->item;
138           item->cf.name = parse_name();
139           item->cf.cls = CC_LIST;
140           item->cf.number = 1;
141           parse_white();
142           parse_char('{');
143           parse_section(sec);
144           parse_char('}');
145         }
146       else
147         {
148           item = mp_alloc_zero(pool, sizeof(*item));
149           if (*pos == '-')
150             {
151               item->flags |= FLAG_HIDE;
152               pos++;
153             }
154           item->cf.cls = CC_STATIC;
155           item->cf.number = 1;
156           switch (*pos)
157             {
158               case '#':
159                 if (*++pos == '#')
160                   {
161                     pos++;
162                     item->cf.type = CT_U64;
163                   }
164                 else
165                   item->cf.type = CT_INT;
166                 break;
167               case '$':
168                 pos++;
169                 item->cf.type = CT_DOUBLE;
170                 break;
171               default:
172                 if (!Cword(*pos))
173                   die("Invalid type syntax");
174                 item->cf.type = CT_STRING;
175                 break;
176             }
177           item->cf.name = parse_name();
178           parse_white();
179           if (*pos == '=')
180             {
181               pos++;
182               parse_white();
183               if (section->item.cf.cls == CC_LIST)
184                 die("List items can not have default values");
185               byte *def = pos, *d = def;
186               while (*pos != ';' && *pos != '}' && !Cspace(*pos))
187                 {
188                   if (*pos == '\'')
189                     {
190                       pos++;
191                       while (*pos != '\'')
192                         {
193                           if (!*pos)
194                             die("Unterminated string");
195                           *d++ = *pos++;
196                         }
197                       pos++;
198                     }
199                   else if (*pos == '"')
200                     {
201                       pos++;
202                       uns esc = 0;
203                       while (*pos != '"' || esc)
204                         {
205                           if (!*pos)
206                             die("Unterminated string");
207                           if (*pos == '\\')
208                             esc ^= 1;
209                           *d++ = *pos++;
210                         }
211                       pos++;
212                     }
213                   else
214                     *d++ = *pos++;
215                 }
216               uns len = d - def;
217               byte *buf = mp_alloc(pool, len + 1);
218               memcpy(buf, def, len);
219               buf[len] = 0;
220               str_unesc(buf, buf);
221               switch (item->cf.type)
222 #define TRY(x) do{byte *_err=(x); if (_err) die(_err); }while(0)
223                 {
224                   case CT_STRING:
225                     item->value.v_ptr = buf;
226                     break;
227                   case CT_INT:
228                     TRY(cf_parse_int(buf, &item->value.v_int));
229                     break;
230                   case CT_U64:
231                     TRY(cf_parse_u64(buf, &item->value.v_u64));
232                     break;
233                   case CT_DOUBLE:
234                     TRY(cf_parse_double(buf, &item->value.v_double));
235                     break;
236                   default:
237                     ASSERT(0);
238 #undef TRY                  
239                 }
240             }
241         }
242       if (section->item.cf.cls == CC_LIST)
243         {
244           item->cf.ptr = (void *)section->size;
245           section->size += sizeof(union value);
246         }
247       else
248         item->cf.ptr = &item->value;
249       clist_add_tail(&section->list, &item->node);
250       section->count++;
251     }
252 }
253
254 static void
255 parse_outer(void)
256 {
257   for (uns sep = 0; ; sep = 1)
258     {
259       parse_white();
260       if (!*pos)
261         break;
262       if (sep)
263         parse_char(';');
264       parse_white();
265       struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
266       if (*pos == '!')
267         {
268           pos++;
269           sec->item.flags |= FLAG_NO_UNKNOWN;
270         }
271       sec->item.cf.name = parse_name();
272       parse_white();
273       parse_char('{');
274       clist_add_tail(&sections, &sec->item.node);
275       clist_init(&sec->list);
276       parse_section(sec);
277       parse_char('}');
278     }
279 }
280
281 static struct cf_section *
282 generate_section(struct section *section)
283 {
284   struct cf_section *sec = mp_alloc_zero(pool, sizeof(*sec));
285   if (section->item.cf.cls == CC_LIST)
286     sec->size = section->size;
287   struct cf_item *c = sec->cfg = mp_alloc_zero(pool, sizeof(struct cf_item) * (section->count + 1));
288   CLIST_FOR_EACH(struct item *, item, section->list)
289     {
290       *c = item->cf;
291       if (c->cls == CC_LIST)
292         c->u.sec = generate_section((struct section *)item);
293       c++;
294     }
295   c->cls = CC_END;
296   return sec;
297 }
298
299 static bb_t path;
300
301 static void
302 dump_item(struct item *item, void *ptr, uns path_len)
303 {
304   if (item->flags & FLAG_HIDE)
305     return;
306   union value *val = (union value *)((addr_int_t)ptr + (addr_int_t)item->cf.ptr);
307   if (item->cf.cls == CC_LIST)
308     {
309       uns len = strlen(item->cf.name);
310       bb_grow(&path, path_len + len + 1);
311       path.ptr[path_len] = '_';
312       memcpy(path.ptr + path_len + 1, item->cf.name, len);
313       CLIST_FOR_EACH(cnode *, ptr2, val->list)
314         CLIST_FOR_EACH(struct item *, item2, ((struct section *)item)->list)
315           dump_item(item2, ptr2, path_len + len + 1);
316     }
317   else
318     {
319       byte *name = item->cf.name;
320       byte buf[128], *value = buf;
321       bb_grow(&path, path_len + 1)[path_len] = 0;
322       if (!ptr)
323         printf("CF_%s_%s='", path.ptr, name);
324       else
325         printf("CF_%s_%s[${#CF_%s_%s[*]}]='", path.ptr, name, path.ptr, name);
326       switch (item->cf.type)
327         {
328           case CT_INT:
329             sprintf(buf, "%d", val->v_int);
330             break;
331           case CT_U64:
332             sprintf(buf, "%Lu", val->v_u64);
333             break;
334           case CT_DOUBLE:
335             sprintf(buf, "%g", val->v_double);
336             break;
337           case CT_STRING:
338             if (val->v_ptr)
339               value = val->v_ptr;
340             else
341               *value = 0;
342             break;
343           default:
344             ASSERT(0);
345         }
346           while (*value) {
347 #if 0
348             if (*value == '\'')
349               die("Apostrophes are not supported in config of scripts");
350 #endif
351             if (*value == '\'')
352               printf("'\\''");
353             else
354               putchar(*value);
355             value++;
356           }
357           printf("'\n");
358     }
359 }
360
361 int main(int argc, char **argv)
362 {
363   log_init("config");
364   if (argc < 2)
365     help();
366   pos = argv[argc - 1];
367   argv[argc - 1] = NULL;
368
369   pool = mp_new(0x1000);
370   clist_init(&sections);
371   parse_outer();
372   CLIST_FOR_EACH(struct section *, sec, sections)
373     cf_declare_section(sec->item.cf.name, generate_section(sec), !(sec->item.flags & FLAG_NO_UNKNOWN));
374
375   if (cf_getopt(argc - 1, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) != -1)
376     help();
377
378   bb_init(&path);
379   CLIST_FOR_EACH(struct section *, section, sections)
380     {
381       uns len = strlen(section->item.cf.name);
382       memcpy(bb_grow(&path, len), section->item.cf.name, len);
383       CLIST_FOR_EACH(struct item *, item, section->list)
384         dump_item(item, NULL, len);
385     }
386   bb_done(&path);
387
388   return 0;
389 }
390