config: allow grouping config settings by key prefix.
authorKrisztian Litkey <krisztian.litkey@intel.com>
Fri, 30 May 2014 15:34:51 +0000 (18:34 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Wed, 4 Jun 2014 13:01:31 +0000 (16:01 +0300)
You can now group configuration settings together with an
object-like notation. For instance, the following snippet
of configuration

    foo.blah = foobar
    foo.xyz  = zy
    foo.bar.foobar = xyzzy
    foo.bar.barfoo = yaddayadda

can be replaced with the equivalent snippet

    foo = {
        blah = foobar
        xyz  = zy
        bar = {
            foobar = xyzzy
            barfoo = yaddayadda
        }
    }

Internally both will be parsed to an identical representation.
The configuration parser is still the original primitive one.
Grouping has been bolted on top of it in a similarly primitive
manner as syntactic sugar. As a side-effect, if you make any
syntactic errors when using the grouping syntax (IOW nesting or
balancing errors), the resulting error messages probably will
not be very intuitive and in some cases might not help too much
in locating the actual error. Apologies...

src/daemon/config.c

index a606b09..fbb701b 100644 (file)
 #endif
 #define MAX_ARGS 64
 
+#define MAX_DEPTH   16
+#define MAX_BLOCK   64
+#define MAX_PREFIX 128
+
 static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
                      int saved_argc, char **saved_argv, char **envp);
 
 static srs_cfg_t *find_config(srs_cfg_t *settings, const char *key);
 
 
+static int  nblock = 0;
+static int  prflen = 0;
+static char blocks[MAX_DEPTH][MAX_BLOCK];
+static char prefix[MAX_PREFIX];
+
+
 /*
  * command line processing
  */
@@ -255,6 +265,68 @@ static void config_load_plugins(srs_context_t *srs, char *plugins)
 }
 
 
+static void push_block(const char *block, int blen)
+{
+    int plen;
+
+    if (nblock >= MAX_DEPTH) {
+        mrp_log_error("Too deeply nested configuration block: %s.%s",
+                      prefix, block);
+        exit(1);
+    }
+
+    if (blen >= MAX_BLOCK - 1) {
+        mrp_log_error("Too long block name '%s'.", block);
+        exit(1);
+    }
+
+    if (prflen + 1 + blen + 1 >= sizeof(prefix)) {
+        mrp_log_error("Too long nested block name '%s.%s'.", prefix, block);
+        exit(1);
+    }
+
+    strncpy(blocks[nblock], block, blen);
+    blocks[nblock][blen] = '\0';
+    if (nblock > 0)
+        prefix[prflen++] = '.';
+    strncpy(prefix + prflen, block, blen);
+    prefix[prflen + blen] = '\0';
+    nblock++;
+    prflen += blen;
+
+    mrp_debug("pushed block '%*.*s', prefix now '%s'", blen, blen, block,
+              prefix);
+}
+
+
+static void pop_block(void)
+{
+    char *block;
+    int   blen;
+
+    if (nblock <= 0) {
+        mrp_log_error("Unbalanced block open ({) and close (}).");
+        exit(1);
+    }
+
+    block = blocks[--nblock];
+    blen  = strlen(block);
+
+    if (nblock > 0 && prflen < blen + 1) {
+        mrp_log_error("Internal error in nested block book-keeping.");
+        exit(1);
+    }
+
+    if (nblock > 0)
+        prflen -= blen + 1;
+    else
+        prflen = 0;
+    prefix[prflen] = '\0';
+
+    mrp_debug("popped block '%s', prefix now '%s'", block, prefix);
+}
+
+
 static void config_parse_settings(srs_context_t *srs, char *settings)
 {
     char   *key, *val, *next;
@@ -271,6 +343,21 @@ static void config_parse_settings(srs_context_t *srs, char *settings)
         return;
     }
 
+    if (*key == '}') {
+        key++;
+
+        while (*key == ' ' || *key == '\t')
+            key++;
+
+        if (*key != '\0') {
+            mrp_log_error("Invalid block closing '%s'.", settings);
+            exit(1);
+        }
+
+        pop_block();
+        return;
+    }
+
     while (key && *key) {
         val  = strchr(key, '=');
         next = strchr(key, ';');
@@ -298,15 +385,23 @@ static void config_parse_settings(srs_context_t *srs, char *settings)
         while (vlen > 0 && val[vlen - 1] == ' ')
             vlen--;
 
-        if (klen >= sizeof(keybuf) || vlen >= sizeof(valbuf)) {
+        if (klen + prflen >= sizeof(keybuf) || vlen >= sizeof(valbuf)) {
             mrp_log_error("Configuration setting %*.*s = %*.*s too long.",
                           (int)klen, (int)klen, key,
                           (int)vlen, (int)vlen, val);
             exit(1);
         }
 
-        strncpy(keybuf, key, klen);
-        keybuf[klen] = '\0';
+        if (vlen == 1 && val[0] == '{') {
+            push_block(key, klen);
+            return;
+        }
+
+        if (nblock > 0)
+            snprintf(keybuf, sizeof(keybuf), "%s.%*.*s", prefix,
+                     klen, klen, key);
+        else
+            snprintf(keybuf, sizeof(keybuf), "%*.*s", klen, klen, key);
         strncpy(valbuf, val, vlen);
         valbuf[vlen] = '\0';
 
@@ -330,6 +425,9 @@ static void config_parse_file(srs_context_t *srs, char *path)
         exit(1);
     }
 
+    nblock = 0;
+    prflen = 0;
+
     while ((p = fgets(line, sizeof(line), fp)) != NULL) {
         while (*p == ' ' || *p == '\t')
             p++;
@@ -343,6 +441,9 @@ static void config_parse_file(srs_context_t *srs, char *path)
         config_parse_settings(srs, p);
     }
 
+    nblock = 0;
+    prflen = 0;
+
     fclose(fp);
 }
 
@@ -436,7 +537,11 @@ void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
 
         case 's':
             SAVE_OPTARG("-s", optarg);
+            nblock = 0;
+            prflen = 0;
             config_parse_settings(srs, optarg);
+            nblock = 0;
+            prflen = 0;
             break;
 
         case 'd':