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