cont
[protos/libecoli.git] / lib / ecoli_tk.c
index 7c5ac21..e21df55 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
 
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
 #include <ecoli_tk.h>
 
 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
@@ -39,12 +42,12 @@ struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
 
        assert(size >= sizeof(*tk));
 
-       tk = calloc(1, size);
+       tk = ec_calloc(1, size);
        if (tk == NULL)
                goto fail;
 
        if (id != NULL) {
-               tk->id = strdup(id);
+               tk->id = ec_strdup(id);
                if (tk->id == NULL)
                        goto fail;
        }
@@ -65,30 +68,59 @@ void ec_tk_free(struct ec_tk *tk)
 
        if (tk->ops != NULL && tk->ops->free_priv != NULL)
                tk->ops->free_priv(tk);
-       free(tk->id);
-       free(tk);
+       ec_free(tk->id);
+       ec_free(tk);
 }
 
-struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *token, const char *str,
-       size_t off)
+struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
 {
+       struct ec_strvec *strvec = NULL;
        struct ec_parsed_tk *parsed_tk;
 
-       parsed_tk = token->ops->parse(token, str, off);
+       errno = ENOMEM;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       if (ec_strvec_add(strvec, str) < 0)
+               goto fail;
+
+       parsed_tk = ec_tk_parse_tokens(tk, strvec);
+       if (parsed_tk == NULL)
+               goto fail;
 
+       ec_strvec_free(strvec);
        return parsed_tk;
-}
 
+ fail:
+       ec_strvec_free(strvec);
+       return NULL;
+}
 
-struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk)
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
+       const struct ec_strvec *strvec)
 {
        struct ec_parsed_tk *parsed_tk;
 
-       parsed_tk = calloc(1, sizeof(*parsed_tk));
+       if (tk->ops->parse == NULL) {
+               errno = ENOTSUP;
+               return NULL;
+       }
+
+       parsed_tk = tk->ops->parse(tk, strvec);
+
+       return parsed_tk;
+}
+
+
+struct ec_parsed_tk *ec_parsed_tk_new(void)
+{
+       struct ec_parsed_tk *parsed_tk = NULL;
+
+       parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
        if (parsed_tk == NULL)
                goto fail;
 
-       parsed_tk->tk = tk;
        TAILQ_INIT(&parsed_tk->children);
 
        return parsed_tk;
@@ -97,7 +129,14 @@ struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk)
        return NULL;
 }
 
-void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
+void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
+       const struct ec_tk *tk, struct ec_strvec *strvec)
+{
+       parsed_tk->tk = tk;
+       parsed_tk->strvec = strvec;
+}
+
+void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk)
 {
        struct ec_parsed_tk *child;
 
@@ -109,35 +148,54 @@ void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
                TAILQ_REMOVE(&parsed_tk->children, child, next);
                ec_parsed_tk_free(child);
        }
-       free(parsed_tk->str);
-       free(parsed_tk);
 }
 
-static void __ec_parsed_tk_dump(const struct ec_parsed_tk *parsed_tk,
-       size_t indent)
+void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
+{
+       if (parsed_tk == NULL)
+               return;
+
+       ec_parsed_tk_free_children(parsed_tk);
+       ec_strvec_free(parsed_tk->strvec);
+       ec_free(parsed_tk);
+}
+
+static void __ec_parsed_tk_dump(FILE *out,
+       const struct ec_parsed_tk *parsed_tk, size_t indent)
 {
        struct ec_parsed_tk *child;
        size_t i;
-       const char *s;
+       const char *s, *id = "None", *typename = "None";
 
        /* XXX enhance */
        for (i = 0; i < indent; i++)
-               printf(" ");
+               fprintf(out, " ");
+
        s = ec_parsed_tk_to_string(parsed_tk);
-       printf("id=%s, s=<%s>\n", parsed_tk->tk->id, s);
+       if (parsed_tk->tk != NULL) {
+               if (parsed_tk->tk->id != NULL)
+                       id = parsed_tk->tk->id;
+               typename = parsed_tk->tk->ops->typename;
+       }
+
+       fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
 
        TAILQ_FOREACH(child, &parsed_tk->children, next)
-               __ec_parsed_tk_dump(child, indent + 2);
+               __ec_parsed_tk_dump(out, child, indent + 2);
 }
 
-void ec_parsed_tk_dump(const struct ec_parsed_tk *parsed_tk)
+void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
 {
        if (parsed_tk == NULL) {
-               printf("no match\n");
+               fprintf(out, "parsed_tk is NULL, error in parse\n");
+               return;
+       }
+       if (!ec_parsed_tk_matches(parsed_tk)) {
+               fprintf(out, "no match\n");
                return;
        }
 
-       __ec_parsed_tk_dump(parsed_tk, 0);
+       __ec_parsed_tk_dump(out, parsed_tk, 0);
 }
 
 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
@@ -154,7 +212,9 @@ struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        if (parsed_tk == NULL)
                return NULL;
 
-       if (parsed_tk->tk->id != NULL && !strcmp(parsed_tk->tk->id, id))
+       if (parsed_tk->tk != NULL &&
+                       parsed_tk->tk->id != NULL &&
+                       !strcmp(parsed_tk->tk->id, id))
                return parsed_tk;
 
        TAILQ_FOREACH(child, &parsed_tk->children, next) {
@@ -166,58 +226,132 @@ struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
        return NULL;
 }
 
+/* XXX return NUL if it matches several tokens?
+   or add a parameter to join() the tokens ? */
 const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
 {
-       if (parsed_tk == NULL)
+       if (parsed_tk == NULL || parsed_tk->strvec == NULL)
                return NULL;
 
-       return parsed_tk->str;
+       return ec_strvec_val(parsed_tk->strvec, 0);
 }
 
-struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
-       const char *str, size_t off)
+/* number of parsed tokens */
+size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk)
 {
-       struct ec_completed_tk *completed_tk;
+       if (parsed_tk == NULL || parsed_tk->strvec == NULL)
+               return 0;
 
-       completed_tk = token->ops->complete(token, str, off);
+       return ec_strvec_len(parsed_tk->strvec);
+}
 
-       return completed_tk;
+size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk)
+{
+       if (parsed_tk == NULL)
+               return 0;
+
+       if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children))
+               return 0;
+
+       return 1;
 }
 
 struct ec_completed_tk *ec_completed_tk_new(void)
 {
        struct ec_completed_tk *completed_tk = NULL;
 
-       completed_tk = calloc(1, sizeof(*completed_tk));
+       completed_tk = ec_calloc(1, sizeof(*completed_tk));
        if (completed_tk == NULL)
                return NULL;
 
        TAILQ_INIT(&completed_tk->elts);
-       completed_tk->count = 0;
+       completed_tk->count_match = 0;
 
        return completed_tk;
 }
 
 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
-       const char *add, const char *full)
+       const char *add)
 {
        struct ec_completed_tk_elt *elt = NULL;
 
-       elt = calloc(1, sizeof(*elt));
+       elt = ec_calloc(1, sizeof(*elt));
        if (elt == NULL)
                return NULL;
 
        elt->tk = tk;
-       elt->add = strdup(add);
-       elt->full = strdup(full);
-       if (elt->add == NULL || elt->full == NULL) {
-               ec_completed_tk_elt_free(elt);
-               return NULL;
+       if (add != NULL) {
+               elt->add = ec_strdup(add);
+               if (elt->add == NULL) {
+                       ec_completed_tk_elt_free(elt);
+                       return NULL;
+               }
        }
 
        return elt;
 }
 
+/* XXX define when to use ec_tk_complete() or tk->complete()
+ * (same for parse)
+ * suggestion: tk->op() is internal, user calls the function
+ * other idea: have 2 functions
+ */
+struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
+       const char *str)
+{
+       struct ec_strvec *strvec = NULL;
+       struct ec_completed_tk *completed_tk;
+
+       errno = ENOMEM;
+       strvec = ec_strvec_new();
+       if (strvec == NULL)
+               goto fail;
+
+       if (ec_strvec_add(strvec, str) < 0)
+               goto fail;
+
+       completed_tk = ec_tk_complete_tokens(tk, strvec);
+       if (completed_tk == NULL)
+               goto fail;
+
+       ec_strvec_free(strvec);
+       return completed_tk;
+
+ fail:
+       ec_strvec_free(strvec);
+       return NULL;
+}
+
+/* default completion function: return a no-match element */
+struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
+       const struct ec_strvec *strvec)
+{
+       struct ec_completed_tk *completed_tk;
+       struct ec_completed_tk_elt *completed_tk_elt;
+
+       (void)strvec;
+
+       completed_tk = ec_completed_tk_new();
+       if (completed_tk == NULL)
+               return NULL;
+
+       completed_tk_elt = ec_completed_tk_elt_new(gen_tk, NULL);
+       if (completed_tk_elt == NULL) {
+               ec_completed_tk_free(completed_tk);
+               return NULL;
+       }
+
+       ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
+
+       return completed_tk;
+}
+
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
+       const struct ec_strvec *strvec)
+{
+       return tk->ops->complete(tk, strvec);
+}
+
 /* count the number of identical chars at the beginning of 2 strings */
 static size_t strcmp_count(const char *s1, const char *s2)
 {
@@ -236,9 +370,11 @@ void ec_completed_tk_add_elt(
 
        TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next);
        completed_tk->count++;
+       if (elt->add != NULL)
+               completed_tk->count_match++;
        if (elt->add != NULL) {
                if (completed_tk->smallest_start == NULL) {
-                       completed_tk->smallest_start = strdup(elt->add);
+                       completed_tk->smallest_start = ec_strdup(elt->add);
                } else {
                        n = strcmp_count(elt->add,
                                completed_tk->smallest_start);
@@ -249,22 +385,17 @@ void ec_completed_tk_add_elt(
 
 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
 {
-       free(elt->add);
-       free(elt->full);
-       free(elt);
+       ec_free(elt->add);
+       ec_free(elt);
 }
 
-struct ec_completed_tk *ec_completed_tk_merge(
-       struct ec_completed_tk *completed_tk1,
+void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
        struct ec_completed_tk *completed_tk2)
 {
        struct ec_completed_tk_elt *elt;
 
-       if (completed_tk2 == NULL)
-               return completed_tk1;
-
-       if (completed_tk1 == NULL)
-               return completed_tk2;
+       assert(completed_tk1 != NULL);
+       assert(completed_tk2 != NULL);
 
        while (!TAILQ_EMPTY(&completed_tk2->elts)) {
                elt = TAILQ_FIRST(&completed_tk2->elts);
@@ -273,8 +404,6 @@ struct ec_completed_tk *ec_completed_tk_merge(
        }
 
        ec_completed_tk_free(completed_tk2);
-
-       return completed_tk1;
 }
 
 void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
@@ -289,45 +418,94 @@ void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
                TAILQ_REMOVE(&completed_tk->elts, elt, next);
                ec_completed_tk_elt_free(elt);
        }
-       free(completed_tk->smallest_start);
-       free(completed_tk);
+       ec_free(completed_tk->smallest_start);
+       ec_free(completed_tk);
 }
 
-void ec_completed_tk_dump(const struct ec_completed_tk *completed_tk)
+void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
 {
        struct ec_completed_tk_elt *elt;
 
        if (completed_tk == NULL || completed_tk->count == 0) {
-               printf("no completion\n");
+               fprintf(out, "no completion\n");
                return;
        }
 
-       printf("completion: count=%u smallest_start=<%s>\n",
-               completed_tk->count, completed_tk->smallest_start);
+       fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
+               completed_tk->count, completed_tk->count_match,
+               completed_tk->smallest_start);
 
-       TAILQ_FOREACH(elt, &completed_tk->elts, next)
-               printf("add=<%s> full=<%s>\n", elt->add, elt->full);
+       TAILQ_FOREACH(elt, &completed_tk->elts, next) {
+               fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n",
+                       elt->add, elt->tk, elt->tk->ops->typename);
+       }
 }
 
 const char *ec_completed_tk_smallest_start(
        const struct ec_completed_tk *completed_tk)
 {
-       if (completed_tk == NULL)
-               return NULL;
+       if (completed_tk == NULL || completed_tk->smallest_start == NULL)
+               return "";
 
        return completed_tk->smallest_start;
 }
 
-unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk)
+unsigned int ec_completed_tk_count_match(
+       const struct ec_completed_tk *completed_tk)
 {
-       struct ec_completed_tk_elt *elt;
-       unsigned int count = 0;
-
        if (completed_tk == NULL)
                return 0;
 
-       TAILQ_FOREACH(elt, &completed_tk->elts, next)
-               count++;
+       return completed_tk->count_match;
+}
+
+struct ec_completed_tk_iter *
+ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
+       enum ec_completed_tk_filter_flags flags)
+{
+       struct ec_completed_tk_iter *iter;
+
+       iter = ec_calloc(1, sizeof(*iter));
+       if (iter == NULL)
+               return NULL;
+
+       iter->completed_tk = completed_tk;
+       iter->flags = flags;
+       iter->cur = NULL;
+
+       return iter;
+}
+
+const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
+       struct ec_completed_tk_iter *iter)
+{
+       if (iter->completed_tk == NULL)
+               return NULL;
+
+       do {
+               if (iter->cur == NULL) {
+                       iter->cur = TAILQ_FIRST(&iter->completed_tk->elts);
+               } else {
+                       iter->cur = TAILQ_NEXT(iter->cur, next);
+               }
 
-       return count;
+               if (iter->cur == NULL)
+                       break;
+
+               if (iter->cur->add == NULL &&
+                               (iter->flags & ITER_NO_MATCH))
+                       break;
+
+               if (iter->cur->add != NULL &&
+                               (iter->flags & ITER_MATCH))
+                       break;
+
+       } while (iter->cur != NULL);
+
+       return iter->cur;
+}
+
+void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)
+{
+       ec_free(iter);
 }