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