save
[protos/libecoli.git] / lib / ecoli_tk_or.c
index 4c782dd..55f3e69 100644 (file)
 
 #include <ecoli_malloc.h>
 #include <ecoli_log.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 #include <ecoli_tk_or.h>
 #include <ecoli_tk_str.h>
 #include <ecoli_test.h>
 
+struct ec_tk_or {
+       struct ec_tk gen;
+       struct ec_tk **table;
+       unsigned int len;
+};
+
 static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
-       struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+       struct ec_parsed_tk *parsed_tk, *child_parsed_tk = NULL;
+       struct ec_strvec *match_strvec;
        unsigned int i;
 
-       parsed_tk = ec_parsed_tk_new(gen_tk);
+       parsed_tk = ec_parsed_tk_new();
        if (parsed_tk == NULL)
-               return NULL;
+               goto fail;
 
        for (i = 0; i < tk->len; i++) {
-               child_parsed_tk = ec_tk_parse(tk->table[i], str);
-               if (child_parsed_tk != NULL)
+               child_parsed_tk = ec_tk_parse_tokens(tk->table[i], strvec);
+               if (child_parsed_tk == NULL)
+                       goto fail;
+               if (ec_parsed_tk_matches(child_parsed_tk))
                        break;
+               ec_parsed_tk_free(child_parsed_tk);
+               child_parsed_tk = NULL;
        }
 
-       if (child_parsed_tk == NULL)
+       /* no match */
+       if (i == tk->len)
+               return parsed_tk;
+
+       match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
+       if (match_strvec == NULL)
                goto fail;
 
+       ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
        ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
 
-       parsed_tk->str = ec_strndup(child_parsed_tk->str,
-               strlen(child_parsed_tk->str));
-
        return parsed_tk;
 
  fail:
+       ec_parsed_tk_free(child_parsed_tk);
        ec_parsed_tk_free(parsed_tk);
        return NULL;
 }
 
 static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
-       const char *str)
+       const struct ec_strvec *strvec)
 {
        struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
-       struct ec_completed_tk *completed_tk = NULL, *child_completed_tk;
+       struct ec_completed_tk *completed_tk, *child_completed_tk;
        size_t n;
 
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
        for (n = 0; n < tk->len; n++) {
-               child_completed_tk = ec_tk_complete(tk->table[n], str);
+               child_completed_tk = ec_tk_complete_tokens(tk->table[n],
+                       strvec);
 
-               if (child_completed_tk == NULL)
+               if (child_completed_tk == NULL) // XXX fail instead?
                        continue;
 
-               completed_tk = ec_completed_tk_merge(completed_tk,
-                       child_completed_tk);
+               ec_completed_tk_merge(completed_tk, child_completed_tk);
        }
 
        return completed_tk;
@@ -101,6 +121,7 @@ static void ec_tk_or_free_priv(struct ec_tk *gen_tk)
 }
 
 static struct ec_tk_ops ec_tk_or_ops = {
+       .typename = "or",
        .parse = ec_tk_or_parse,
        .complete = ec_tk_or_complete,
        .free_priv = ec_tk_or_free_priv,
@@ -125,6 +146,7 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        struct ec_tk_or *tk = NULL;
        struct ec_tk *child;
        va_list ap;
+       int fail = 0;
 
        va_start(ap, id);
 
@@ -135,12 +157,18 @@ struct ec_tk *ec_tk_or_new_list(const char *id, ...)
        for (child = va_arg(ap, struct ec_tk *);
             child != EC_TK_ENDLIST;
             child = va_arg(ap, struct ec_tk *)) {
-               if (child == NULL)
-                       goto fail;
 
-               ec_tk_or_add(&tk->gen, child);
+               /* on error, don't quit the loop to avoid leaks */
+               if (fail == 1 || child == NULL ||
+                               ec_tk_or_add(&tk->gen, child) < 0) {
+                       fail = 1;
+                       ec_tk_free(child);
+               }
        }
 
+       if (fail == 1)
+               goto fail;
+
        va_end(ap);
        return &tk->gen;
 
@@ -166,6 +194,9 @@ int ec_tk_or_add(struct ec_tk *gen_tk, struct ec_tk *child)
        table[tk->len] = child;
        tk->len ++;
 
+       child->parent = gen_tk;
+       TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
        return 0;
 }
 
@@ -174,7 +205,6 @@ static int ec_tk_or_testcase(void)
        struct ec_tk *tk;
        int ret = 0;
 
-       /* all inputs starting with foo should match */
        tk = ec_tk_or_new_list(NULL,
                ec_tk_str_new(NULL, "foo"),
                ec_tk_str_new(NULL, "bar"),
@@ -183,10 +213,13 @@ static int ec_tk_or_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "fooxxx", "foo");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "bar");
-       ret |= EC_TEST_CHECK_TK_PARSE(tk, "oo", NULL);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " ", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "toto", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
        ec_tk_free(tk);
 
        /* test completion */
@@ -201,20 +234,34 @@ static int ec_tk_or_testcase(void)
                ec_log(EC_LOG_ERR, "cannot create tk\n");
                return -1;
        }
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "ar");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "t", "");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "to", "to");
-       ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
-       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
-               "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST);
-       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "f",
-               "oo", EC_TK_ENDLIST);
-       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "b",
-               "ar", "ar2", EC_TK_ENDLIST);
-       ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "t",
-               "oto", "iti", EC_TK_ENDLIST);
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "", EC_TK_ENDLIST,
+               "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "f", EC_TK_ENDLIST,
+               "oo", EC_TK_ENDLIST,
+               "oo");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "b", EC_TK_ENDLIST,
+               "ar", "ar2", EC_TK_ENDLIST,
+               "ar");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "bar", EC_TK_ENDLIST,
+               "", "2", EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "t", EC_TK_ENDLIST,
+               "oto", "iti", EC_TK_ENDLIST,
+               "");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "to", EC_TK_ENDLIST,
+               "to", EC_TK_ENDLIST,
+               "to");
+       ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+               "x", EC_TK_ENDLIST,
+               EC_TK_ENDLIST,
+               "");
        ec_tk_free(tk);
 
        return ret;