]> mj.ucw.cz Git - libucw.git/commitdiff
lib/shell/config.c rewritten...
authorPavel Charvat <pavel.charvat@netcentrum.cz>
Thu, 11 May 2006 10:49:42 +0000 (12:49 +0200)
committerPavel Charvat <pavel.charvat@netcentrum.cz>
Thu, 11 May 2006 10:49:42 +0000 (12:49 +0200)
- support for int, u64, string, list
- list items are dumped as unstructured bash arrays,
  but this may be changed if necessary...
  alternatively --dumpconfig can be used to
  see everything

lib/shell/config.c

index 7fa161175d339f43b8a5e1845a31b1f7ffa5e033..6efb6201437efaea2702ba9331b2394a0b80de20 100644 (file)
@@ -3,6 +3,7 @@
  *
  *     (c) 2002--2005 Martin Mares <mj@ucw.cz>
  *     (c) 2006 Robert Spalek <robert@ucw.cz>
+ *     (c) 2006 Pavel Charvat <pchar@ucw.cz>
  *
  *     Once we were using this beautiful Shell version, but it turned out
  *     that it doesn't work with nested config files:
 #include "lib/lib.h"
 #include "lib/conf.h"
 #include "lib/getopt.h"
+#include "lib/clists.h"
+#include "lib/mempool.h"
+#include "lib/chartype.h"
+#include "lib/bbuf.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <alloca.h>
 
-enum flag {
-  F_STRING = 0,
-  F_INT = 1,
-  F_DOUBLE = 2,
-  F_INT64 = 3,
-  F_TYPE_MASK = 7,
-  F_ARRAY = 0x80,
-};
-
 static void
 help(void)
 {
   fputs("\n\
-Usage: config [-C<configfile>] [-S<section>.<option>=<value>] <section> [@]<item><type>[=<default>] <item2> ... [*]\n\
+Usage: config [-C<configfile>] [-S<section>.<option>=<value>] <sections>\n\
+\n\
+<sections>\t<section>[,<sections>]\n\
+<section>\t[*]<name>{[<items>]}\n\
+<items>\t\t<item>[,<items>]\n\
+<item>\t\t<static> | @<list>\n\
+<static>\t[-]<type><name>\n\
+<list>\t\t<name>{[<items>]}\n\
 \n\
 Types:\n\
 <empty>\t\tString\n\
@@ -47,132 +50,278 @@ Types:\n\
 $\t\tFloating point number\n\
 \n\
 Modifiers:\n\
-@\t\tMultiple occurences of the item are passed as an array (otherwise the last one wins)\n\
-*\t\tIgnore unknown items instead of reporting them as errors\n\
+*\t\tReport unknown items as errors\n\
+-\t\tDo not dump item's value\n\
 ", stderr);
   exit(1);
 }
 
-static byte *
-report(uns num, byte **pars, struct cf_item *item)
+union value {
+  void *v_ptr;
+  int v_int;
+  u64 v_u64;
+  double v_double;
+  clist list;
+};
+
+#define FLAG_HIDE              0x1
+#define FLAG_NO_UNKNOWN                0x2
+
+struct item {
+  cnode node;
+  uns flags;
+  struct cf_item cf;
+  union value value;
+};
+
+struct section {
+  struct item item;
+  clist list;
+  uns count;
+  uns size;
+};
+
+static struct mempool *pool;
+static clist sections;
+static byte *pos;
+
+static void
+parse_white(void)
 {
-  uns f = item->type;
-  for (uns i=0; i<num; i++)
-  {
-    byte buf[128];
-    byte *err, *value = buf;
-    switch (f & F_TYPE_MASK) {
-      case F_STRING:
-       value = pars[i];
-       break;
-      case F_INT:
-       ; uns val;
-       if (err = cf_parse_int(pars[i], &val))
-         return err;
-       sprintf(buf, "%d", val);
-       break;
-      case F_INT64:
-       ; u64 val64;
-       if (err = cf_parse_u64(pars[i], &val64))
-         return err;
-       sprintf(buf, "%Lu", val64);
-       break;
-      case F_DOUBLE:
-       ; double valf;
-       if (err = cf_parse_double(pars[i], &valf))
-         return err;
-       sprintf(buf, "%g", valf);
-       break;
-      default:
-       ASSERT(0);
-    }
+  while (Cspace(*pos))
+    pos++;
+}
 
-    if (f & F_ARRAY)
-      printf("CF_%s[${#CF_%s[*]}]='", item->name, item->name);
-    else
-      printf("CF_%s='", item->name);
-    while (*value) {
-      if (*value == '\'')
-       die("Apostrophes are not supported in config of scripts");
-      putchar(*value++);
-    }
-    puts("'");
-  }
-  return NULL;
+static void
+parse_char(byte c)
+{
+  if (*pos++ != c)
+    die("Missing '%c'", c);
 }
 
-int main(int argc, char **argv)
+static byte *
+parse_name(void)
 {
-  int i = 1;
-  int start;
+  byte *name = pos;
+  while (Cword(*pos))
+    pos++;
+  uns len = pos - name;
+  if (!len)
+    die("Expected item/section name");
+  byte *buf = mp_alloc(pool, len + 1);
+  memcpy(buf, name, len);
+  buf[len] = 0;
+  return buf;
+}
 
-  log_init("config");
-  while (i < argc && argv[i][0] == '-')
-    i++;
-  if (i + 1 >= argc)
-    help();
-  start = i;
+static void
+parse_section(struct section *section)
+{
+  for (uns sep = 0; ; sep = 1)
+    {
+      parse_white();
+      if (!*pos || *pos == '}')
+       break;
+      if (sep)
+       parse_char(',');
+      parse_white();
 
-  byte *sec_name = argv[i++];
-  uns allow_unknown = 0;
-  struct cf_section *sec = xmalloc_zero(sizeof(struct cf_section));
-  struct cf_item *c = sec->cfg = xmalloc_zero(sizeof(struct cf_item) * (argc-i+1));
+      struct item *item;
 
-  for (; i<argc; i++)
-    {
-      byte *arg = xstrdup(argv[i]);
-      if (!strcmp(arg, "*"))
-       allow_unknown = 1;
+      if (*pos == '@')
+        {
+         pos++;
+         struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
+         sec->size = sizeof(cnode);
+         clist_init(&sec->list);
+         item = &sec->item;
+         item->cf.name = parse_name();
+         item->cf.cls = CC_LIST;
+         item->cf.number = 1;
+         parse_white();
+         parse_char('{');
+         parse_section(sec);
+         parse_char('}');
+       }
       else
-       {
-         byte *e = strchr(arg, '=');
-         if (e)
-           *e++ = 0;
-
-         byte *t = arg + strlen(arg) - 1;
-         if (t > arg)
+        {
+         item = mp_alloc_zero(pool, sizeof(*item));
+         if (*pos == '-')
            {
-             if (*t == '#')
-               {
-                 *t-- = 0;
-                 if (t > arg && *t == '#')
-                   {
-                     *t-- = 0;
-                     c->type |= F_INT64;
-                   }
-                 else
-                   c->type |= F_INT;
-               }
-             else if (*t == '$')
-               {
-                 *t-- = 0;
-                 c->type |= F_DOUBLE;
-               }
+             item->flags |= FLAG_HIDE;
+             pos++;
            }
-
-         if (*arg == '@')
+         item->cf.cls = CC_STATIC;
+         item->cf.number = 1;
+         switch (*pos)
            {
-             arg++;
-             printf("declare -a CF_%s ; CF_%s=()\n", arg, arg);
-             c->type |= F_ARRAY;
+             case '#':
+               if (*++pos == '#')
+                 {
+                   pos++;
+                   item->cf.type = CT_U64;
+                 }
+               else
+                 item->cf.type = CT_INT;
+               break;
+             case '$':
+               pos++;
+               item->cf.type = CT_DOUBLE;
+               break;
+             default:
+               if (!Cword(*pos))
+                 die("Invalid type syntax");
+               item->cf.type = CT_STRING;
+               break;
            }
+         item->cf.name = parse_name();
+       }
+      if (section->item.cf.cls == CC_LIST)
+        {
+          item->cf.ptr = (void *)section->size;
+          section->size += sizeof(union value);
+        }
+      else
+        item->cf.ptr = &item->value;
+      clist_add_tail(&section->list, &item->node);
+      section->count++;
+    }
+}
 
-         c->cls = CC_PARSER;
-         c->name = arg;
-         c->number = (c->type & F_ARRAY) ? CF_ANY_NUM : 1;
-         c->ptr = c;
-         c->u.par = (cf_parser*) report;
-         if (e) {
-           byte *err = report(1, &e, c);
-           if (err)
-             die("Cannot parse the default value #%d: %s", c-sec->cfg, err);
-         }
-         c++;
+static void
+parse_outer(void)
+{
+  for (uns sep = 0; ; sep = 1)
+    {
+      parse_white();
+      if (!*pos)
+       break;
+      if (sep)
+       parse_char(',');
+      parse_white();
+      struct section *sec = mp_alloc_zero(pool, sizeof(*sec));
+      if (*pos == '*')
+        {
+         pos++;
+         sec->item.flags |= FLAG_NO_UNKNOWN;
        }
+      sec->item.cf.name = parse_name();
+      parse_white();
+      parse_char('{');
+      clist_add_tail(&sections, &sec->item.node);
+      clist_init(&sec->list);
+      parse_section(sec);
+      parse_char('}');
+    }
+}
+
+static struct cf_section *
+generate_section(struct section *section)
+{
+  struct cf_section *sec = mp_alloc_zero(pool, sizeof(*sec));
+  if (section->item.cf.cls == CC_LIST)
+    sec->size = section->size;
+  struct cf_item *c = sec->cfg = mp_alloc_zero(pool, sizeof(struct cf_item) * (section->count + 1));
+  CLIST_FOR_EACH(struct item *, item, section->list)
+    {
+      *c = item->cf;
+      if (c->cls == CC_LIST)
+       c->u.sec = generate_section((struct section *)item);
+      c++;
     }
   c->cls = CC_END;
-  cf_declare_section(sec_name, sec, allow_unknown);
-  if (cf_getopt(start, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) != -1)
+  return sec;
+}
+
+static bb_t path;
+
+static void
+dump_item(struct item *item, void *ptr, uns path_len)
+{
+  if (item->flags & FLAG_HIDE)
+    return;
+  union value *val = (union value *)((addr_int_t)ptr + (addr_int_t)item->cf.ptr);
+  if (item->cf.cls == CC_LIST)
+    {
+      uns len = strlen(item->cf.name);
+      bb_grow(&path, path_len + len + 1);
+      path.ptr[path_len] = '_';
+      memcpy(path.ptr + path_len + 1, item->cf.name, len);
+      CLIST_FOR_EACH(cnode *, ptr2, val->list)
+        CLIST_FOR_EACH(struct item *, item2, ((struct section *)item)->list)
+          dump_item(item2, ptr2, path_len + len + 1);
+    }
+  else
+    {
+      byte *name = item->cf.name;
+      byte buf[128], *value = buf;
+      bb_grow(&path, path_len + 1)[path_len] = 0;
+      if (!ptr)
+        printf("CF_%s_%s='", path.ptr, name);
+      else
+        printf("CF_%s_%s[${#CF_%s_%s[*]}]='", path.ptr, name, path.ptr, name);
+      switch (item->cf.type)
+        {
+          case CT_INT:
+           sprintf(buf, "%d", val->v_int);
+            break;
+          case CT_U64:
+           sprintf(buf, "%Lu", val->v_u64);
+           break;
+         case CT_DOUBLE:
+           sprintf(buf, "%g", val->v_double);
+           break;
+         case CT_STRING:
+           if (val->v_ptr)
+             value = val->v_ptr;
+           else
+             *value = 0;
+           break;
+         default:
+           ASSERT(0);
+       }
+          while (*value) {
+#if 0
+            if (*value == '\'')
+             die("Apostrophes are not supported in config of scripts");
+#endif
+            if (*value == '\'')
+             printf("'\\''");
+           else
+              putchar(*value);
+           value++;
+          }
+         printf("'\n");
+    }
+}
+
+int main(int argc, char **argv)
+{
+  log_init("config");
+  if (argc < 2)
     help();
+  pos = argv[argc - 1];
+  argv[argc - 1] = NULL;
+
+  pool = mp_new(0x1000);
+  clist_init(&sections);
+  parse_outer();
+  CLIST_FOR_EACH(struct section *, sec, sections)
+    cf_declare_section(sec->item.cf.name, generate_section(sec), !(sec->item.flags & FLAG_NO_UNKNOWN));
+
+  if (cf_getopt(argc - 1, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) != -1)
+    help();
+
+  bb_init(&path);
+  CLIST_FOR_EACH(struct section *, section, sections)
+    {
+      uns len = strlen(section->item.cf.name);
+      memcpy(bb_grow(&path, len), section->item.cf.name, len);
+      CLIST_FOR_EACH(struct item *, item, section->list)
+        dump_item(item, NULL, len);
+    }
+  bb_done(&path);
+
   return 0;
 }
+