srcs += ecoli_node_empty.c
srcs += ecoli_node_expr.c
srcs += ecoli_node_expr_test.c
-#srcs += ecoli_node_file.c
+srcs += ecoli_node_file.c
srcs += ecoli_node_int.c
srcs += ecoli_node_many.c
srcs += ecoli_node_once.c
exe-y-$(O)test = $(srcs) main.c
ldflags-$(O)readline = -lreadline -ltermcap
-#exe-y-$(O)readline = $(srcs) main-readline.c
+exe-y-$(O)readline = $(srcs) main-readline.c
include $(ECOLI)/mk/ecoli-post.mk
const struct ec_node *node, const char *add)
{
struct ec_completed_match *item = NULL;
+ struct ec_parsed *p;
+ size_t len;
item = ec_calloc(1, sizeof(*item));
if (item == NULL)
return NULL;
- /* XXX can state be NULL? */
- if (state != NULL) {
- struct ec_parsed *p;
- size_t len;
-
- /* get path len */
- for (p = state, len = 0; p != NULL;
- p = ec_parsed_get_parent(p), len++)
- ;
-
- item->path = ec_calloc(len, sizeof(*item->path));
- if (item->path == NULL)
- goto fail;
-
- item->pathlen = len;
-
- /* write path in array */
- for (p = state, len = 0; p != NULL;
- p = ec_parsed_get_parent(p), len++)
- item->path[len] = p->node;
- }
+ /* get path len */
+ for (p = state, len = 0; p != NULL;
+ p = ec_parsed_get_parent(p), len++)
+ ;
+ /* allocate room for path */
+ item->path = ec_calloc(len, sizeof(*item->path));
+ if (item->path == NULL)
+ goto fail;
+ item->pathlen = len;
+ /* write path in array */
+ for (p = state, len = 0; p != NULL;
+ p = ec_parsed_get_parent(p), len++)
+ item->path[len] = p->node;
item->type = type;
item->node = node;
return NULL;
}
-int
-ec_completed_add_match(struct ec_completed *completed,
- struct ec_parsed *parsed_state,
- const struct ec_node *node, const char *add)
+static int
+__ec_completed_add_match(enum ec_completed_type type,
+ struct ec_completed *completed,
+ struct ec_parsed *parsed_state,
+ const struct ec_node *node, const char *add)
{
struct ec_completed_node *compnode = NULL;
- struct ec_completed_match *item = NULL;
+ struct ec_completed_match *match = NULL;
int ret = -ENOMEM;
size_t n;
+ /* find the compnode entry corresponding to this node */
TAILQ_FOREACH(compnode, &completed->nodes, next) {
if (compnode->node == node)
break;
if (compnode == NULL)
return -ENOENT;
- item = ec_completed_match(EC_MATCH, parsed_state, node, add);
- if (item == NULL)
+ match = ec_completed_match(type, parsed_state, node, add);
+ if (match == NULL)
goto fail;
- if (item->add != NULL) {
+ if (match->add != NULL) {
if (completed->smallest_start == NULL) {
- completed->smallest_start = ec_strdup(item->add);
+ completed->smallest_start = ec_strdup(match->add);
if (completed->smallest_start == NULL)
goto fail;
} else {
- n = strcmp_count(item->add,
+ n = strcmp_count(match->add,
completed->smallest_start);
completed->smallest_start[n] = '\0';
}
completed->count_match++;
}
- TAILQ_INSERT_TAIL(&compnode->matches, item, next);
+ TAILQ_INSERT_TAIL(&compnode->matches, match, next);
completed->count++;
return 0;
fail:
- ec_completed_match_free(item);
+ ec_completed_match_free(match);
return ret;
}
+int
+ec_completed_add_match(struct ec_completed *completed,
+ struct ec_parsed *parsed_state,
+ const struct ec_node *node, const char *add)
+{
+ return __ec_completed_add_match(EC_MATCH, completed, parsed_state,
+ node, add);
+}
+
+int
+ec_completed_add_no_match(struct ec_completed *completed,
+ struct ec_parsed *parsed_state,
+ const struct ec_node *node)
+{
+ return __ec_completed_add_match(EC_NO_MATCH, completed, parsed_state,
+ node, NULL);
+}
+
+int
+ec_completed_add_partial_match(struct ec_completed *completed,
+ struct ec_parsed *parsed_state,
+ const struct ec_node *node, const char *add)
+{
+ return __ec_completed_add_match(EC_PARTIAL_MATCH, completed, parsed_state,
+ node, add);
+}
+
int
ec_completed_add_node(struct ec_completed *completed,
const struct ec_node *node)
{
- struct ec_completed_node *item = NULL;
+ struct ec_completed_node *compnode = NULL;
- item = ec_completed_node(node);
- if (item == NULL)
+ compnode = ec_completed_node(node);
+ if (compnode == NULL)
return -ENOMEM;
- TAILQ_INSERT_TAIL(&completed->nodes, item, next);
+ TAILQ_INSERT_TAIL(&completed->nodes, compnode, next);
return 0;
}
-void ec_completed_match_free(struct ec_completed_match *item)
+void ec_completed_match_free(struct ec_completed_match *match)
{
- ec_free(item->add);
- ec_free(item->path);
- ec_free(item);
+ ec_free(match->add);
+ ec_free(match->path);
+ ec_free(match);
}
-/* default completion function: return a no-item element */
+/* default completion function: return a no-match element */
int
ec_node_default_complete(const struct ec_node *gen_node,
struct ec_completed *completed,
- struct ec_parsed *parsed,
+ struct ec_parsed *parsed_state,
const struct ec_strvec *strvec)
{
- (void)gen_node;
- (void)completed;
- (void)parsed;
+ int ret;
- if (ec_strvec_len(strvec) != 1) //XXX needed?
+ if (ec_strvec_len(strvec) != 1)
return 0;
+ ret = ec_completed_add_no_match(completed, parsed_state, gen_node);
+ if (ret < 0)
+ return ret;
+
return 0;
}
fprintf(out, "node=%p, node_type=%s\n",
compnode->node, compnode->node->type->name);
TAILQ_FOREACH(item, &compnode->matches, next) {
- fprintf(out, "add=<%s>\n", item->add); /* XXX comp type */
+ const char *typestr;
+
+ switch (item->type) {
+ case EC_NO_MATCH: typestr = "no-match"; break;
+ case EC_MATCH: typestr = "match"; break;
+ case EC_PARTIAL_MATCH: typestr = "partial-match"; break;
+ default: typestr = "unknown"; break;
+ }
+
+ fprintf(out, " type=%s add=<%s>\n", typestr, item->add);
}
}
}
if (cur_node == NULL) {
TAILQ_FOREACH(cur_node, &completed->nodes, next) {
TAILQ_FOREACH(cur_match, &cur_node->matches, next) {
- if (cur_match != NULL)
+ if (cur_match != NULL &&
+ cur_match->type & iter->type)
goto found;
}
}
return NULL;
} else {
cur_match = TAILQ_NEXT(cur_match, next);
- if (cur_match != NULL)
+ if (cur_match != NULL &&
+ cur_match->type & iter->type)
goto found;
cur_node = TAILQ_NEXT(cur_node, next);
while (cur_node != NULL) {
cur_match = TAILQ_FIRST(&cur_node->matches);
- if (cur_match != NULL)
+ if (cur_match != NULL &&
+ cur_match->type & iter->type)
goto found;
cur_node = TAILQ_NEXT(cur_node, next);
}
enum ec_completed_type {
EC_NO_MATCH,
EC_MATCH,
- EC_PARTIAL,
+ EC_PARTIAL_MATCH,
};
struct ec_completed_match {
const struct ec_node *node;
char *add;
- /* reverse order: [0] = last parsed, [len-1] = root */
+ /* reverse order: [0] = last, [len-1] = root */
const struct ec_node **path;
size_t pathlen;
};
int ec_completed_add_match(struct ec_completed *completed,
struct ec_parsed *state,
const struct ec_node *node, const char *add);
-int ec_completed_add_node(struct ec_completed *completed,
+int ec_completed_add_no_match(struct ec_completed *completed,
+ struct ec_parsed *parsed_state,
const struct ec_node *node);
-int ec_completed_add_partial(struct ec_completed *completed,
+int ec_completed_add_partial_match(struct ec_completed *completed,
struct ec_parsed *state, const struct ec_node *node,
const char *add);
+int ec_completed_add_node(struct ec_completed *completed,
+ const struct ec_node *node);
+
void ec_completed_match_free(struct ec_completed_match *item);
void ec_completed_free(struct ec_completed *completed);
void ec_completed_dump(FILE *out,
ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
ec_node_free(node);
- node = EC_NODE_CMD(NULL, "good morning bob|bobby|michael [count]",
+ node = EC_NODE_CMD(NULL, "good morning [count] bob|bobby|michael",
ec_node_int("count", 0, 10, 10));
if (node == NULL) {
ec_log(EC_LOG_ERR, "cannot create node\n");
return -1;
}
- ret |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "bob", "1");
- ec_node_free(node);
+ ret |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob");
+
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "", EC_NODE_ENDLIST,
+ "good", EC_NODE_ENDLIST,
+ "good");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "g", EC_NODE_ENDLIST,
+ "ood", EC_NODE_ENDLIST,
+ "ood");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "good", "morning", "", EC_NODE_ENDLIST,
+ "bob", "bobby", "michael", EC_NODE_ENDLIST,
+ "");
- // XXX completion
+ ec_node_free(node);
return ret;
}
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE /* for asprintf */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <ecoli_log.h>
+#include <ecoli_malloc.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_parsed.h>
+#include <ecoli_completed.h>
+#include <ecoli_node_file.h>
+
+struct ec_node_file {
+ struct ec_node gen;
+};
+
+static int
+ec_node_file_parse(const struct ec_node *gen_node,
+ struct ec_parsed *state,
+ const struct ec_strvec *strvec)
+{
+ (void)gen_node;
+ (void)state;
+
+ if (ec_strvec_len(strvec) == 0)
+ return EC_PARSED_NOMATCH;
+
+ return 1;
+}
+
+/*
+ * Almost the same than dirname (3) and basename (3) except that
+ * it always returns a substring of the given path, which can
+ * be empty.
+ * - the behavior is different when the path finishes with a '/'
+ * - the argument is not modified
+ * - the output is allocated and must be freed with ec_free().
+ *
+ * path dirname basename split_path
+ * /usr/lib /usr lib /usr/ lib
+ * /usr/ / usr /usr/
+ * usr . usr usr
+ * / / / /
+ * . . . .
+ * .. . .. ..
+ */
+static int split_path(const char *path, char **dname_p, char **bname_p)
+{
+ char *last_slash;
+ size_t dirlen;
+ char *dname, *bname;
+
+ *dname_p = NULL;
+ *bname_p = NULL;
+
+ last_slash = strrchr(path, '/');
+ if (last_slash == NULL)
+ dirlen = 0;
+ else
+ dirlen = last_slash - path + 1;
+
+ dname = ec_strdup(path);
+ if (dname == NULL)
+ return -ENOMEM;
+ dname[dirlen] = '\0';
+
+ bname = ec_strdup(path + dirlen);
+ if (bname == NULL) {
+ ec_free(dname);
+ return -ENOMEM;
+ }
+
+ *dname_p = dname;
+ *bname_p = bname;
+
+ return 0;
+}
+
+static int
+ec_node_file_complete(const struct ec_node *gen_node,
+ struct ec_completed *completed,
+ struct ec_parsed *state,
+ const struct ec_strvec *strvec)
+{
+ struct stat st;
+ const char *path;
+ size_t bname_len;
+ struct dirent *de = NULL;
+ DIR *dir = NULL;
+ char *dname = NULL, *bname = NULL, *effective_dir;
+ char *add = NULL;
+ int ret;
+ int is_dir = 0;
+
+ /*
+ * Example with this file tree:
+ * /
+ * ├── dir1
+ * │ ├── file1
+ * │ ├── file2
+ * │ └── subdir
+ * │ └── file3
+ * ├── dir2
+ * │ └── file4
+ * └── file5
+ *
+ * Input Output completions
+ * / [dir1/, dir2/, file5]
+ * /d [dir1/, dir2/]
+ * /f [file5]
+ * /dir1/ [file1, file2, subdir/]
+ *
+ *
+ *
+ */
+
+ if (ec_strvec_len(strvec) != 1)
+ goto out;
+
+ path = ec_strvec_val(strvec, 0);
+ ret = split_path(path, &dname, &bname);
+ if (ret < 0) {
+ ec_completed_free(completed);
+ completed = NULL;
+ goto out;
+ }
+
+ if (strcmp(dname, "") == 0)
+ effective_dir = ".";
+ else
+ effective_dir = dname;
+
+ ret = lstat(effective_dir, &st);
+ if (ret != 0) {
+ ret = -errno;
+ goto out;
+ }
+ if (!S_ISDIR(st.st_mode))
+ goto out;
+
+ dir = opendir(effective_dir);
+ if (dir == NULL)
+ goto out;
+
+ bname_len = strlen(bname);
+ while (1) {
+ de = readdir(dir);
+ if (de == NULL)
+ goto out;
+
+ if (strncmp(bname, de->d_name, bname_len))
+ continue;
+ if (bname[0] != '.' && de->d_name[0] == '.')
+ continue;
+
+ /* add '/' if it's a dir */
+ if (de->d_type == DT_DIR) {
+ is_dir = 1;
+ } else if (de->d_type == DT_UNKNOWN) { // XXX todo
+ } else {
+ is_dir = 0;
+ }
+
+ if (is_dir) {
+ if (asprintf(&add, "%s/", &de->d_name[bname_len]) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ if (ec_completed_add_partial_match(
+ completed, state, gen_node, add) < 0) {
+ ec_completed_free(completed);
+ completed = NULL;
+ goto out;
+ }
+ } else {
+ if (asprintf(&add, "%s", &de->d_name[bname_len]) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ if (ec_completed_add_match(completed, state, gen_node,
+ add) < 0) {
+ ec_completed_free(completed);
+ completed = NULL;
+ goto out;
+ }
+ }
+ }
+ ret = 0;
+
+out:
+ free(add);
+ ec_free(dname);
+ ec_free(bname);
+ if (dir != NULL)
+ closedir(dir);
+
+ return ret;
+}
+
+static struct ec_node_type ec_node_file_type = {
+ .name = "file",
+ .parse = ec_node_file_parse,
+ .complete = ec_node_file_complete,
+ .size = sizeof(struct ec_node_file),
+};
+
+EC_NODE_TYPE_REGISTER(ec_node_file_type);
+
+/* LCOV_EXCL_START */
+static int ec_node_file_testcase(void)
+{
+ struct ec_node *node;
+ int ret = 0;
+
+ node = ec_node("file", NULL);
+ if (node == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create node\n");
+ return -1;
+ }
+ /* any string matches */
+ ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
+ ret |= EC_TEST_CHECK_PARSE(node, 1, "/tmp/bar");
+ ret |= EC_TEST_CHECK_PARSE(node, -1);
+
+ /* test completion */
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ EC_NODE_ENDLIST,
+ EC_NODE_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "", EC_NODE_ENDLIST,
+ EC_NODE_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "/", EC_NODE_ENDLIST,
+ EC_NODE_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "/tmp", EC_NODE_ENDLIST,
+ EC_NODE_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "/tmp/", EC_NODE_ENDLIST,
+ EC_NODE_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_COMPLETE(node,
+ "/tmp/.", EC_NODE_ENDLIST,
+ EC_NODE_ENDLIST,
+ "");
+ ec_node_free(node);
+
+ return ret;
+}
+/* LCOV_EXCL_STOP */
+
+static struct ec_test ec_node_file_test = {
+ .name = "node_file",
+ .test = ec_node_file_testcase,
+};
+
+EC_TEST_REGISTER(ec_node_file_test);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ECOLI_NODE_FILE_
+#define ECOLI_NODE_FILE_
+
+#include <ecoli_node.h>
+
+struct ec_node *ec_node_file(const char *id, const char *file);
+
+/* file is duplicated */
+int ec_node_file_set_str(struct ec_node *node, const char *file);
+
+#endif
{
size_t i = 0;
- /* skip spaces */
- while (!isblank(str[i]) && str[i] != '\0')
+ /* eat chars until we find a quote, space, or end of string */
+ while (!isblank(str[i]) && str[i] != '\0' &&
+ str[i] != '"' && str[i] != '\'')
i++;
return i;
return 0;
str = ec_strvec_val(strvec, 0);
+// printf("\nold:%s\n", str);
new_vec = tokenize(str, 1, 1, &missing_quote);
if (new_vec == NULL)
goto fail;
+// printf("new:%s\n", ec_strvec_val(new_vec, 0));
+ // XXX: complete should add the quotes for !EC_PARTIAL: use another
+ // completed object
ret = ec_node_complete_child(node->child, completed, parsed, new_vec);
if (ret < 0)
goto fail;
ret |= EC_TEST_CHECK_PARSE(node, 1, " foo bar");
ret |= EC_TEST_CHECK_PARSE(node, 1, " 'foo' \"bar\"");
ret |= EC_TEST_CHECK_PARSE(node, 1, " 'f'oo 'toto' bar");
+ ret |= EC_TEST_CHECK_PARSE(node, -1, " foo toto bar'");
ec_node_free(node);
/* test completion */
count, ec_completed_count(c, EC_MATCH));
ec_completed_dump(stdout, c);
ret = -1;
- }
+ } else
+ ec_completed_dump(stdout, c); //XXX
/* check the expected smallest start */
expected = va_arg(ap, const char *);
#include <ecoli_node_cmd.h>
#include <ecoli_node_many.h>
#include <ecoli_node_once.h>
+#include <ecoli_node_file.h>
static struct ec_node *commands;
{
static struct ec_completed *c;
static struct ec_completed_iter *iter;
- static const struct ec_completed_item *item;
+ static const struct ec_completed_match *item;
char *out_string;
+ /* don't append a quote */
+ rl_completion_suppress_quote = 1;
+ rl_basic_quote_characters = "";
if (state == 0) {
char *line;
return NULL;
ec_completed_iter_free(iter);
- iter = ec_completed_iter(c, EC_MATCH);
+ iter = ec_completed_iter(c, EC_MATCH | EC_PARTIAL_MATCH);
if (iter == NULL)
return NULL;
}
- elt = ec_completed_iter_next(iter);
- if (elt == NULL)
+ item = ec_completed_iter_next(iter);
+ if (item == NULL)
return NULL;
- if (asprintf(&out_string, "%s%s", s, elt->add) < 0)
+ /* don't add the trailing space for partial completions */
+ if (state == 0) {
+ if (item->type == EC_MATCH)
+ rl_completion_suppress_append = 0;
+ else
+ rl_completion_suppress_append = 1;
+ }
+
+ //XXX see printable_part() and rl_display_match_list()
+ //XXX or: if match count > 1, strip beginning (in node_file)
+
+ if (asprintf(&out_string, "%s%s", s, item->add) < 0) // XXX ec_asprintf (check all in code)
return NULL;
return out_string;
}
/* this function builds the help string */
-static char *get_node_help(const struct ec_completed_elt *elt)
+static char *get_node_help(const struct ec_completed_match *elt)
{
const struct ec_node *node;
char *help = NULL;
static int show_help(int ignore, int invoking_key)
{
- const struct ec_completed_item *item;
- struct ec_completed_iter *iter;
+ const struct ec_completed_node *compnode;
+// const struct ec_completed_match *item;
+// struct ec_completed_iter *iter;
struct ec_completed *c;
struct ec_parsed *p;
char *line;
if (c == NULL)
return 1;
- count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH);
+#if 0 // old method
+ count = ec_completed_count(c, EC_MATCH | EC_NO_MATCH |
+ EC_PARTIAL_MATCH);
helps = calloc(count + match + 1, sizeof(char *));
if (helps == NULL)
return 1;
if (match)
helps[1] = "<return>";
- iter = ec_completed_iter(c, EC_MATCH | EC_NO_MATCH);
+ iter = ec_completed_iter(c, EC_MATCH | EC_NO_MATCH |
+ EC_PARTIAL_MATCH);
if (iter == NULL)
goto fail;
i++, item = ec_completed_iter_next(iter)) {
helps[i] = get_node_help(item);
}
+#else
+ count = 0;
+ TAILQ_FOREACH(compnode, &c->nodes, next) {
+ if (TAILQ_EMPTY(&compnode->matches))
+ continue;
+ count++;
+ }
+ helps = calloc(count + match + 1, sizeof(char *));
+ if (helps == NULL)
+ return 1;
+
+ if (match)
+ helps[1] = "<return>";
+
+ i = match + 1;
+ TAILQ_FOREACH(compnode, &c->nodes, next) {
+ if (TAILQ_EMPTY(&compnode->matches))
+ continue;
+ // we should pass compnode instead
+ helps[i++] = get_node_help(TAILQ_FIRST(&compnode->matches)); //XXX
+ }
+#endif
+
+ ec_completed_dump(stdout, c);
ec_completed_free(c);
rl_display_match_list(helps, count + match, 1000); /* XXX 1000 */
return 0;
-fail:
+//fail:
free(helps);
// free helps[n] XXX
return 1;
goto fail;
+ cmd = EC_NODE_SEQ(NULL,
+ ec_node_str(NULL, "load"),
+ ec_node("file", NULL)
+ );
+ ec_keyval_set(ec_node_attrs(cmd), "help", "load a file", NULL);
+ if (ec_node_or_add(cmdlist, cmd) < 0)
+ goto fail;
+
+
commands = ec_node_sh_lex(NULL, cmdlist);
if (commands == NULL)
goto fail;
int main(void)
{
struct ec_parsed *p;
-// const char *name;
char *line;
-
if (create_commands() < 0)
return 1;
cleanup / rework
================
+- add_no_match
+- add_partial_match
- check XXX in code
+- properly manage quotes in shlex
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, ...)
===============
- demonstration example that parses yang file and generate cli
+
+
+
+-----------------------
+
+readline:
+
+[tab] list possible completions (matches only)
+[?] list what is expected, example:
+
+"command [foo] toto|titi|<int>"
+
+help("command f") ->
+ foo (help of foo)
+ toto (help of toto)
+ titi (help of titi)
+ <int> (help of int)
+