save
authorOlivier Matz <zer0@droids-corp.org>
Tue, 25 Jul 2017 16:55:19 +0000 (18:55 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Tue, 25 Jul 2017 16:55:19 +0000 (18:55 +0200)
26 files changed:
lib/Makefile
lib/ecoli_completed.c
lib/ecoli_completed.h
lib/ecoli_keyval.c
lib/ecoli_node_cmd.c
lib/ecoli_node_empty.c
lib/ecoli_node_expr_test.c
lib/ecoli_node_int.c
lib/ecoli_node_many.c
lib/ecoli_node_once.c
lib/ecoli_node_option.c
lib/ecoli_node_or.c
lib/ecoli_node_re.c
lib/ecoli_node_re_lex.c
lib/ecoli_node_seq.c
lib/ecoli_node_sh_lex.c
lib/ecoli_node_space.c
lib/ecoli_node_str.c
lib/ecoli_node_subset.c
lib/ecoli_node_weakref.c
lib/ecoli_parsed.c
lib/ecoli_parsed.h
lib/ecoli_strvec.h
lib/main-readline.c
lib/main.c
lib/todo.txt

index e9f7f5e..8d44796 100644 (file)
@@ -30,9 +30,17 @@ include $(ECOLI)/mk/ecoli-pre.mk
 # output path with trailing slash
 O ?= build/
 
+# XXX -O0
 CFLAGS  = -g -O0 -Wall -Werror -W -Wextra -fPIC -Wmissing-prototypes
 CFLAGS += -I.
 
+# XXX coverage
+CFLAGS += --coverage -fprofile-arcs -ftest-coverage
+LDFLAGS += --coverage
+#  rm -rf build; rm -rf result; make && ./build/test
+#  lcov -d build -c -t build/test -o test.info && genhtml -o result test.info
+
+
 srcs :=
 srcs += ecoli_completed.c
 srcs += ecoli_keyval.c
@@ -45,6 +53,7 @@ srcs += ecoli_node_cmd.c
 srcs += ecoli_node_empty.c
 srcs += ecoli_node_expr.c
 srcs += ecoli_node_expr_test.c
+srcs += ecoli_node_file.c
 srcs += ecoli_node_int.c
 srcs += ecoli_node_many.c
 srcs += ecoli_node_once.c
@@ -59,6 +68,7 @@ srcs += ecoli_node_str.c
 srcs += ecoli_node_subset.c
 srcs += ecoli_node_weakref.c
 srcs += ecoli_parsed.c
+srcs += ecoli_vec.c
 
 shlib-y-$(O)libecoli.so := $(srcs)
 
index 18e92ed..5e79ac2 100644 (file)
@@ -53,27 +53,6 @@ struct ec_completed *ec_completed(void)
        return completed;
 }
 
-static struct ec_completed_elt *
-ec_completed_elt(const struct ec_node *node, const char *add)
-{
-       struct ec_completed_elt *elt = NULL;
-
-       elt = ec_calloc(1, sizeof(*elt));
-       if (elt == NULL)
-               return NULL;
-
-       elt->node = node;
-       if (add != NULL) {
-               elt->add = ec_strdup(add);
-               if (elt->add == NULL) {
-                       ec_completed_elt_free(elt);
-                       return NULL;
-               }
-       }
-
-       return elt;
-}
-
 struct ec_completed *
 ec_node_complete_child(struct ec_node *node,
                struct ec_parsed *state,
@@ -181,7 +160,7 @@ struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node,
        if (ec_strvec_len(strvec) != 1)
                return completed;
 
-       if (ec_completed_add_elt(completed, gen_node, NULL) < 0) {
+       if (ec_completed_add_elt(completed, state, gen_node, NULL) < 0) {
                ec_completed_free(completed);
                return NULL;
        }
@@ -200,6 +179,56 @@ static size_t strcmp_count(const char *s1, const char *s2)
        return i;
 }
 
+static struct ec_completed_elt *
+ec_completed_elt(struct ec_parsed *parsed,
+               const struct ec_node *node, const char *add)
+{
+       struct ec_completed_elt *elt = NULL;
+
+       elt = ec_calloc(1, sizeof(*elt));
+       if (elt == NULL)
+               return NULL;
+
+       if (parsed != NULL) {
+               struct ec_parsed *p;
+               size_t len;
+
+               /* get path len */
+               for (p = parsed, len = 0; p != NULL;
+                    p = ec_parsed_get_parent(p), len++)
+                       ;
+
+               elt->path = ec_calloc(len, sizeof(*elt->path));
+               if (elt->path == NULL)
+                       goto fail;
+
+               elt->pathlen = len;
+
+               /* write path in array */
+               for (p = parsed, len = 0; p != NULL;
+                    p = ec_parsed_get_parent(p), len++)
+                       elt->path[len] = p->node;
+       }
+
+       elt->node = node;
+       if (add != NULL) {
+               elt->add = ec_strdup(add);
+               if (elt->add == NULL)
+                       goto fail;
+       }
+
+       return elt;
+
+fail:
+       if (elt != NULL) {
+               ec_free(elt->path);
+               ec_free(elt->add);
+       }
+       ec_completed_elt_free(elt);
+
+       return NULL;
+}
+
 static int __ec_completed_add_elt(struct ec_completed *completed,
                                struct ec_completed_elt *elt)
 {
@@ -222,11 +251,12 @@ static int __ec_completed_add_elt(struct ec_completed *completed,
 }
 
 int ec_completed_add_elt(struct ec_completed *completed,
+                       struct ec_parsed *parsed,
                        const struct ec_node *node, const char *add)
 {
        struct ec_completed_elt *elt;
 
-       elt = ec_completed_elt(node, add);
+       elt = ec_completed_elt(parsed, node, add);
        if (elt == NULL)
                return -ENOMEM;
 
@@ -236,6 +266,7 @@ int ec_completed_add_elt(struct ec_completed *completed,
 void ec_completed_elt_free(struct ec_completed_elt *elt)
 {
        ec_free(elt->add);
+       ec_free(elt->path);
        ec_free(elt);
 }
 
@@ -341,11 +372,10 @@ const struct ec_completed_elt *ec_completed_iter_next(
                return NULL;
 
        do {
-               if (iter->cur == NULL) {
+               if (iter->cur == NULL)
                        iter->cur = TAILQ_FIRST(&iter->completed->elts);
-               } else {
+               else
                        iter->cur = TAILQ_NEXT(iter->cur, next);
-               }
 
                if (iter->cur == NULL)
                        break;
index 993db7f..26ac77a 100644 (file)
@@ -38,6 +38,10 @@ struct ec_completed_elt {
        TAILQ_ENTRY(ec_completed_elt) next;
        const struct ec_node *node;
        char *add;
+
+       /* reverse order: [0] = last parsed, [len-1] = root */
+       const struct ec_node **path;
+       size_t pathlen;
 };
 
 TAILQ_HEAD(ec_completed_elt_list, ec_completed_elt);
@@ -67,6 +71,7 @@ struct ec_completed *ec_completed(void);
 
 /* XXX add completion type: full, partial, none */
 int ec_completed_add_elt(struct ec_completed *completed,
+                       struct ec_parsed *parsed,
                        const struct ec_node *node, const char *add);
 void ec_completed_elt_free(struct ec_completed_elt *elt);
 void ec_completed_merge(struct ec_completed *completed1,
index 2572ba0..c1d881f 100644 (file)
@@ -187,6 +187,7 @@ void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
        }
 }
 
+/* LCOV_EXCL_START */
 static int ec_keyval_testcase(void)
 {
        struct ec_keyval *keyval;
@@ -226,6 +227,7 @@ static int ec_keyval_testcase(void)
 
        return 0;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_keyval_test = {
        .name = "keyval",
index 2082bdd..7d11dfa 100644 (file)
@@ -491,6 +491,7 @@ fail:
        return NULL;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_cmd_testcase(void)
 {
        struct ec_node *node;
@@ -525,6 +526,7 @@ static int ec_node_cmd_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_cmd_test = {
        .name = "node_cmd",
index 6a6ed61..cc29779 100644 (file)
@@ -62,6 +62,7 @@ static struct ec_node_type ec_node_empty_type = {
 
 EC_NODE_TYPE_REGISTER(ec_node_empty_type);
 
+/* LCOV_EXCL_START */
 static int ec_node_empty_testcase(void)
 {
        struct ec_node *node;
@@ -95,6 +96,7 @@ static int ec_node_empty_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_empty_test = {
        .name = "node_empty",
index 1e78212..c0e149a 100644 (file)
@@ -216,6 +216,7 @@ static int ec_node_expr_test_eval(struct ec_node *lex_node,
        return ret;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_expr_testcase(void)
 {
        struct ec_node *node = NULL, *lex_node = NULL;
@@ -298,6 +299,7 @@ fail:
        ec_node_free(node);
        return -1;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_expr_test = {
        .name = "expr",
index 2ba1b28..0635a16 100644 (file)
@@ -139,6 +139,7 @@ long long ec_node_int_getval(struct ec_node *gen_node, const char *str)
        return val;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_int_testcase(void)
 {
        struct ec_parsed *p;
@@ -220,6 +221,7 @@ static int ec_node_int_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_int_test = {
        .name = "node_int",
index 87534a3..7e1582c 100644 (file)
@@ -224,6 +224,7 @@ struct ec_node *ec_node_many(const char *id, struct ec_node *child,
        return &node->gen;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_many_testcase(void)
 {
        struct ec_node *node;
@@ -302,6 +303,7 @@ static int ec_node_many_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_many_test = {
        .name = "node_many",
index deb9baa..b89b80e 100644 (file)
@@ -171,6 +171,7 @@ struct ec_node *ec_node_once(const char *id, struct ec_node *child)
        return gen_node;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_once_testcase(void)
 {
        struct ec_node *node;
@@ -223,6 +224,7 @@ static int ec_node_once_testcase(void)
 #endif
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_once_test = {
        .name = "node_once",
index 0c72f98..4b52814 100644 (file)
@@ -113,6 +113,7 @@ struct ec_node *ec_node_option(const char *id, struct ec_node *child)
        return &node->gen;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_option_testcase(void)
 {
        struct ec_node *node;
@@ -151,6 +152,7 @@ static int ec_node_option_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_option_test = {
        .name = "node_option",
index 8925cc7..a8d653c 100644 (file)
@@ -180,6 +180,7 @@ fail:
        return NULL;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_or_testcase(void)
 {
        struct ec_node *node;
@@ -246,6 +247,7 @@ static int ec_node_or_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_or_test = {
        .name = "node_or",
index 63e2d3e..984e686 100644 (file)
@@ -131,6 +131,7 @@ fail:
        return NULL;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_re_testcase(void)
 {
        struct ec_node *node;
@@ -151,6 +152,7 @@ static int ec_node_re_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_re_test = {
        .name = "node_re",
index c4d5104..2de1c64 100644 (file)
@@ -216,7 +216,7 @@ struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child)
        return &node->gen;
 }
 
-
+/* LCOV_EXCL_START */
 static int ec_node_re_lex_testcase(void)
 {
        struct ec_node *node;
@@ -258,6 +258,7 @@ static int ec_node_re_lex_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_re_lex_test = {
        .name = "node_re_lex",
index 8b7b2ee..2d6363e 100644 (file)
@@ -271,6 +271,7 @@ fail:
        return NULL;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_seq_testcase(void)
 {
        struct ec_node *node;
@@ -343,6 +344,7 @@ static int ec_node_seq_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_seq_test = {
        .name = "node_seq",
index ec15fc7..0ce54fe 100644 (file)
@@ -356,6 +356,7 @@ struct ec_node *ec_node_sh_lex(const char *id, struct ec_node *child)
        return &node->gen;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_sh_lex_testcase(void)
 {
        struct ec_node *node;
@@ -447,6 +448,7 @@ static int ec_node_sh_lex_testcase(void)
        ec_node_free(node);
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_sh_lex_test = {
        .name = "node_sh_lex",
index 265cc9b..fd526e6 100644 (file)
@@ -75,6 +75,7 @@ static struct ec_node_type ec_node_space_type = {
 
 EC_NODE_TYPE_REGISTER(ec_node_space_type);
 
+/* LCOV_EXCL_START */
 static int ec_node_space_testcase(void)
 {
        struct ec_node *node;
@@ -114,6 +115,7 @@ static int ec_node_space_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_space_test = {
        .name = "space",
index 011a86c..62ec788 100644 (file)
@@ -95,7 +95,7 @@ ec_node_str_complete(const struct ec_node *gen_node,
        else
                add = node->string + n;
 
-       if (ec_completed_add_elt(completed, gen_node, add) < 0) {
+       if (ec_completed_add_elt(completed, state, gen_node, add) < 0) {
                ec_completed_free(completed);
                return NULL;
        }
@@ -164,6 +164,7 @@ fail:
        return NULL;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_str_testcase(void)
 {
        struct ec_node *node;
@@ -235,6 +236,7 @@ static int ec_node_str_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_str_test = {
        .name = "node_str",
index 6b5fee0..599c761 100644 (file)
@@ -358,6 +358,7 @@ fail:
        return NULL;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_subset_testcase(void)
 {
        struct ec_node *node;
@@ -438,6 +439,7 @@ static int ec_node_subset_testcase(void)
 
        return ret;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_subset_test = {
        .name = "node_subset",
index 9ad52fb..735f831 100644 (file)
@@ -114,11 +114,13 @@ struct ec_node *ec_node_weakref(const char *id, struct ec_node *child)
        return gen_node;
 }
 
+/* LCOV_EXCL_START */
 static int ec_node_weakref_testcase(void)
 {
        //XXX weakref testcase
        return 0;
 }
+/* LCOV_EXCL_STOP */
 
 static struct ec_test ec_node_weakref_test = {
        .name = "node_weakref",
index 140edea..e3a05e9 100644 (file)
@@ -274,6 +274,14 @@ struct ec_parsed *ec_parsed_get_root(struct ec_parsed *parsed)
        return parsed;
 }
 
+struct ec_parsed *ec_parsed_get_parent(struct ec_parsed *parsed)
+{
+       if (parsed == NULL)
+               return NULL;
+
+       return parsed->parent;
+}
+
 struct ec_parsed *ec_parsed_find_first(struct ec_parsed *parsed,
        const char *id)
 {
index 1ebea9e..df24f05 100644 (file)
@@ -89,9 +89,12 @@ void ec_parsed_add_child(struct ec_parsed *parsed,
                        struct ec_parsed *child);
 void ec_parsed_del_child(struct ec_parsed *parsed,
                        struct ec_parsed *child);
+
 struct ec_parsed *ec_parsed_get_root(struct ec_parsed *parsed);
+struct ec_parsed *ec_parsed_get_parent(struct ec_parsed *parsed);
 struct ec_parsed *ec_parsed_get_last_child(struct ec_parsed *parsed);
 void ec_parsed_del_last_child(struct ec_parsed *parsed);
+int ec_parsed_get_path(struct ec_parsed *parsed, struct ec_node **path);
 
 void ec_parsed_dump(FILE *out, const struct ec_parsed *parsed);
 
index af1db57..ee3313e 100644 (file)
@@ -28,7 +28,6 @@
 #ifndef ECOLI_STRVEC_
 #define ECOLI_STRVEC_
 
-#include <sys/types.h>
 #include <stdio.h>
 
 struct ec_strvec *ec_strvec(void);
index 9d898ff..7aef334 100644 (file)
@@ -101,21 +101,28 @@ static char **my_attempted_completion(const char *text, int start, int end)
 }
 
 /* this function builds the help string */
-static char *get_tk_help(const struct ec_node *node)
+static char *get_node_help(const struct ec_completed_elt *elt)
 {
-       const struct ec_node *node2;
+       const struct ec_node *node;
        char *help = NULL;
-       char *tk_help = NULL;
-
-       for (node2 = node;
-            node2 != NULL && tk_help == NULL;
-            node2 = ec_node_parent(node2))
-               tk_help = ec_keyval_get(ec_node_attrs(node2), "help");
+       const char *node_help = NULL;
+       const char *node_desc = NULL;
+       size_t i;
+
+       for (i = 0; i < elt->pathlen; i++) {
+               node = elt->path[i];
+               if (node_help == NULL)
+                       node_help = ec_keyval_get(ec_node_attrs(node), "help");
+               if (node_desc == NULL)
+                       node_desc = ec_node_desc(node);
+       }
 
-       if (tk_help == NULL)
-               tk_help = "";
+       if (node_help == NULL)
+               node_help = "";
+       if (node_desc == NULL)
+               return NULL;
 
-       if (asprintf(&help, "%-20s %s", ec_node_desc(node), tk_help) < 0)
+       if (asprintf(&help, "%-20s %s", node_desc, node_help) < 0)
                return NULL;
 
        return help;
@@ -169,7 +176,7 @@ static int show_help(int ignore, int invoking_key)
        for (i = match + 1, elt = ec_completed_iter_next(iter);
             i < count + match + 1 && elt != NULL;
             i++, elt = ec_completed_iter_next(iter)) {
-               helps[i] = get_tk_help(elt->node);
+               helps[i] = get_node_help(elt);
        }
 
        ec_completed_free(c);
@@ -198,7 +205,7 @@ static int create_commands(void)
        cmd = EC_NODE_SEQ(NULL,
                ec_node_str(NULL, "hello"),
                EC_NODE_OR("name",
-                       ec_node_str(NULL, "john"),
+                       ec_node_str("john", "john"),
                        ec_node_str(NULL, "johnny"),
                        ec_node_str(NULL, "mike")
                ),
@@ -208,6 +215,8 @@ static int create_commands(void)
                goto fail;
        ec_keyval_set(ec_node_attrs(cmd), "help",
                "say hello to someone several times", NULL);
+       ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "john")),
+               "help", "specific help for john", NULL);
        ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")),
                "help", "the name of the person", NULL);
        ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "int")),
@@ -216,12 +225,17 @@ static int create_commands(void)
                goto fail;
 
 
-       cmd = EC_NODE_CMD(NULL, "good morning bob|bobby|michael [count]",
+       cmd = EC_NODE_CMD(NULL, "good morning name [count]",
+                       EC_NODE_CMD("name", "bob|bobby|michael"),
                        ec_node_int("count", 0, 10, 10));
        if (cmd == NULL)
                goto fail;
        ec_keyval_set(ec_node_attrs(cmd), "help",
                "say good morning to someone several times", NULL);
+       ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "name")), "help",
+               "the person to greet", NULL);
+       ec_keyval_set(ec_node_attrs(ec_node_find(cmd, "count")), "help",
+               "how many times to greet", NULL);
        if (ec_node_or_add(cmdlist, cmd) < 0)
                goto fail;
 
@@ -236,11 +250,10 @@ static int create_commands(void)
                goto fail;
 
 
-       cmd = EC_NODE_CMD(NULL, "sell vegetable",
-                       ec_node_many("vegetable",
+       cmd = EC_NODE_CMD(NULL, "eat vegetables",
+                       ec_node_many("vegetables",
                                EC_NODE_OR(NULL,
-                                       ec_node_once(NULL,
-                                               ec_node_str(NULL, "potatoes")),
+                                       ec_node_str(NULL, "potatoes"),
                                        ec_node_once(NULL,
                                                ec_node_str(NULL, "carrots")),
                                        ec_node_once(NULL,
@@ -249,7 +262,7 @@ static int create_commands(void)
        if (cmd == NULL)
                goto fail;
        ec_keyval_set(ec_node_attrs(cmd), "help",
-               "sell vegetables", NULL);
+               "eat vegetables (take some more potatoes)", NULL);
        if (ec_node_or_add(cmdlist, cmd) < 0)
                goto fail;
 
@@ -257,7 +270,7 @@ static int create_commands(void)
        cmd = EC_NODE_SEQ(NULL,
                ec_node_str(NULL, "bye")
        );
-       ec_keyval_set(ec_node_attrs(cmd), "help", "say bye to someone", NULL);
+       ec_keyval_set(ec_node_attrs(cmd), "help", "say bye", NULL);
        if (ec_node_or_add(cmdlist, cmd) < 0)
                goto fail;
 
index 1f36b69..25cc4ca 100644 (file)
@@ -65,8 +65,7 @@ static const struct option ec_long_options[] = {
 
 static void usage(const char *prgname)
 {
-       /* XXX add a parameter to test only one testcase */
-       printf("%s [options]\n"
+       printf("%s [options] [test1 test2 test3...]\n"
                "  -h\n"
                "  --"EC_OPT_HELP"\n"
                "      Show this help.\n"
index c34e104..0597ecb 100644 (file)
@@ -2,15 +2,16 @@ tk_cmd
 ======
 
 X evaluate expression tree in ec_tk_expr
-- cmd token
+X cmd token
 - example
 X tk_re
 
-cleanup
-=======
+cleanup / rework
+================
 
 - check XXX in code
 X remove the _new() functions
+- iterate children nodes without chaining them
 - add a tk vector type: will be used in several nodes (ex: or, seq, ...)
 - check allocation model everywhere
 - checkpatch?
@@ -25,17 +26,21 @@ X split ecoli_tk.h
 - cache results when appropriate?
 - size_t or unsigned int?
 X rename:
-  - ec_tk -> ec_node
-  - ec_parsed_tk -> ec_parsed
-  - ec_completed_tk -> ec_completed
-  - tk, gen_tk, token, ... -> node
-  - tokens -> input_str / input_strvec ?
+  X ec_tk -> ec_node
+  X ec_parsed_tk -> ec_parsed
+  X ec_completed_tk -> ec_completed
+  X tk, gen_tk, token, ... -> node
+  X tokens -> input_str / input_strvec ?
 - use is_err() or errno for funcs returning ptrs
+- save node path in completion to fix help string
+- code coverage
+- try to hide structures
 
 dependencies
 ============
 
-- pass the current parsed state when parsing/completing
+X pass the current parsed state when parsing/completing
+X new node "once"
 - new node "condition"
 
 logs