From ef05ddd2034f31839d5f0be21e372f8313e5d9eb Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 1 Feb 2018 22:32:43 +0100 Subject: [PATCH] store full token and completion in completed_item --- lib/ecoli_completed.c | 278 ++++++++++++++++++++++++++-------------- lib/ecoli_completed.h | 70 +++++----- lib/ecoli_node.c | 8 ++ lib/ecoli_node.h | 4 + lib/ecoli_node_expr.c | 21 ++- lib/ecoli_node_file.c | 28 ++-- lib/ecoli_node_int.c | 1 + lib/ecoli_node_many.c | 2 +- lib/ecoli_node_once.c | 13 +- lib/ecoli_node_seq.c | 2 +- lib/ecoli_node_sh_lex.c | 63 ++++++++- lib/ecoli_node_str.c | 23 ++-- lib/ecoli_node_subset.c | 2 +- lib/ecoli_parsed.c | 7 + lib/main-readline.c | 21 +-- lib/todo.txt | 11 ++ 16 files changed, 360 insertions(+), 194 deletions(-) diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c index 6820971..25ae6cb 100644 --- a/lib/ecoli_completed.c +++ b/lib/ecoli_completed.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -44,12 +45,14 @@ struct ec_completed_item { enum ec_completed_type type; const struct ec_node *node; struct ec_completed_group *grp; - char *str; - char *display; + char *start; /* the initial token */ + char *full; /* the full token after completion */ + char *completion; /* chars that are added, NULL if not applicable */ + char *display; /* what should be displayed by help/completers */ struct ec_keyval *attrs; }; -struct ec_completed *ec_completed(void) +struct ec_completed *ec_completed(struct ec_parsed *state) { struct ec_completed *completed = NULL; @@ -59,23 +62,17 @@ struct ec_completed *ec_completed(void) TAILQ_INIT(&completed->groups); - completed->attrs = ec_keyval(); - if (completed->attrs == NULL) - goto fail; - - completed->cur_state = NULL; + completed->cur_state = state; return completed; fail: - if (completed != NULL) - ec_keyval_free(completed->attrs); ec_free(completed); return NULL; } -struct ec_parsed *ec_completed_cur_parse_state(struct ec_completed *completed) +struct ec_parsed *ec_completed_get_state(struct ec_completed *completed) { return completed->cur_state; } @@ -114,7 +111,7 @@ ec_node_complete_child(struct ec_node *node, struct ec_completed *completed, cur_group = completed->cur_group; completed->cur_group = NULL; - /* complete */ + /* fill the completed struct with items */ ret = node->type->complete(node, completed, strvec); /* restore parent parse state */ @@ -145,7 +142,7 @@ struct ec_completed *ec_node_complete_strvec(struct ec_node *node, struct ec_completed *completed = NULL; int ret; - completed = ec_completed(); + completed = ec_completed(NULL); if (completed == NULL) goto fail; @@ -195,6 +192,10 @@ ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed) if (grp == NULL) return NULL; + grp->attrs = ec_keyval(); + if (grp->attrs == NULL) + goto fail; + grp->state = ec_parsed_dup(parsed); if (grp->state == NULL) goto fail; @@ -205,116 +206,158 @@ ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed) return grp; fail: - if (grp != NULL) + if (grp != NULL) { ec_parsed_free(grp->state); + ec_keyval_free(grp->attrs); + } ec_free(grp); return NULL; } -struct ec_completed_item * -ec_completed_item(const struct ec_node *node) +static struct ec_completed_item * +ec_completed_item(const struct ec_node *node, enum ec_completed_type type, + const char *start, const char *full) { struct ec_completed_item *item = NULL; + struct ec_keyval *attrs = NULL; + char *comp_cp = NULL, *start_cp = NULL; + char *full_cp = NULL, *display_cp = NULL; + + if (type == EC_COMP_UNKNOWN && full != NULL) { + errno = EINVAL; + return NULL; + } + if (type != EC_COMP_UNKNOWN && full == NULL) { + errno = EINVAL; + return NULL; + } item = ec_calloc(1, sizeof(*item)); if (item == NULL) goto fail; - item->attrs = ec_keyval(); - if (item->attrs == NULL) + attrs = ec_keyval(); + if (attrs == NULL) goto fail; - item->type = EC_COMP_UNKNOWN; + if (start != NULL) { + start_cp = ec_strdup(start); + if (start_cp == NULL) + goto fail; + + if (ec_str_startswith(full, start)) { + comp_cp = ec_strdup(&full[strlen(start)]); + if (comp_cp == NULL) + goto fail; + } + } + if (full != NULL) { + full_cp = ec_strdup(full); + if (full_cp == NULL) + goto fail; + display_cp = ec_strdup(full); + if (display_cp == NULL) + goto fail; + } + item->node = node; + item->type = type; + item->start = start_cp; + item->full = full_cp; + item->completion = comp_cp; + item->display = display_cp; + item->attrs = attrs; return item; fail: - if (item != NULL) { - ec_free(item->str); - ec_free(item->display); - ec_keyval_free(item->attrs); - } + ec_keyval_free(item->attrs); + ec_free(comp_cp); + ec_free(start_cp); + ec_free(full_cp); + ec_free(display_cp); ec_free(item); return NULL; } -int -ec_completed_item_set(struct ec_completed_item *item, - enum ec_completed_type type, const char *str) +int ec_completed_item_set_display(struct ec_completed_item *item, + const char *display) { - char *str_copy = NULL; char *display_copy = NULL; int ret = 0; - if (item == NULL) - return -EINVAL; - if (item->str != NULL) - return -EEXIST; - - switch (type) { - case EC_COMP_UNKNOWN: - if (str != NULL) - return -EINVAL; - break; - case EC_COMP_FULL: - case EC_PARTIAL_MATCH: - if (str == NULL) - return -EINVAL; - break; - default: + if (item == NULL || display == NULL || + item->type == EC_COMP_UNKNOWN) return -EINVAL; - } - if (str != NULL) { - ret = -ENOMEM; - str_copy = ec_strdup(str); - if (str_copy == NULL) - goto fail; - display_copy = ec_strdup(str); - if (display_copy == NULL) - goto fail; - } + display_copy = ec_strdup(display); + if (display_copy == NULL) + goto fail; - item->type = type; - item->str = str_copy; + ec_free(item->display); item->display = display_copy; + return 0; fail: - ec_free(str_copy); ec_free(display_copy); return ret; } -int ec_completed_item_set_display(struct ec_completed_item *item, - const char *display) +int +ec_completed_item_set_completion(struct ec_completed_item *item, + const char *completion) { - char *display_copy = NULL; + char *completion_copy = NULL; int ret = 0; - if (item == NULL || display == NULL || - item->type == EC_COMP_UNKNOWN || item->str == NULL) + if (item == NULL || completion == NULL || + item->type == EC_COMP_UNKNOWN) return -EINVAL; ret = -ENOMEM; - display_copy = ec_strdup(display); - if (display_copy == NULL) + completion_copy = ec_strdup(completion); + if (completion_copy == NULL) goto fail; - ec_free(item->display); - item->display = display_copy; + ec_free(item->completion); + item->completion = completion_copy; return 0; fail: - ec_free(display_copy); + ec_free(completion_copy); return ret; } -// XXX refactor ec_completed_item(), ec_completed_item_add(), ec_completed_item_set* int +ec_completed_item_set_str(struct ec_completed_item *item, + const char *str) +{ + char *str_copy = NULL; + int ret = 0; + + if (item == NULL || str == NULL || + item->type == EC_COMP_UNKNOWN) + return -EINVAL; + + ret = -ENOMEM; + str_copy = ec_strdup(str); + if (str_copy == NULL) + goto fail; + + ec_free(item->full); + item->full = str_copy; + + return 0; + +fail: + ec_free(str_copy); + return ret; +} + +static int ec_completed_item_add(struct ec_completed *completed, struct ec_completed_item *item) { @@ -325,7 +368,7 @@ ec_completed_item_add(struct ec_completed *completed, case EC_COMP_UNKNOWN: break; case EC_COMP_FULL: - case EC_PARTIAL_MATCH: + case EC_COMP_PARTIAL: completed->count_match++; //XXX break; default: @@ -352,7 +395,7 @@ ec_completed_item_add(struct ec_completed *completed, const char * ec_completed_item_get_str(const struct ec_completed_item *item) { - return item->str; + return item->full; } const char * @@ -361,6 +404,12 @@ ec_completed_item_get_display(const struct ec_completed_item *item) return item->display; } +const char * +ec_completed_item_get_completion(const struct ec_completed_item *item) +{ + return item->completion; +} + enum ec_completed_type ec_completed_item_get_type(const struct ec_completed_item *item) { @@ -379,42 +428,63 @@ ec_completed_item_get_grp(const struct ec_completed_item *item) return item->grp; } -void ec_completed_item_free(struct ec_completed_item *item) +static void +ec_completed_item_free(struct ec_completed_item *item) { if (item == NULL) return; - ec_free(item->str); + ec_free(item->full); + ec_free(item->start); + ec_free(item->completion); ec_free(item->display); ec_keyval_free(item->attrs); ec_free(item); } +int ec_completed_add_item(struct ec_completed *completed, + const struct ec_node *node, + struct ec_completed_item **p_item, + enum ec_completed_type type, + const char *start, const char *full) +{ + struct ec_completed_item *item = NULL; + int ret; + + item = ec_completed_item(node, type, start, full); + if (item == NULL) + return -1; + + ret = ec_completed_item_add(completed, item); + if (ret < 0) + goto fail; + + if (p_item != NULL) + *p_item = item; + + return 0; + +fail: + ec_completed_item_free(item); + + return -1; +} + /* default completion function: return a no-match element */ int ec_node_default_complete(const struct ec_node *gen_node, // XXX rename in nomatch struct ec_completed *completed, const struct ec_strvec *strvec) { - struct ec_completed_item *item = NULL; int ret; if (ec_strvec_len(strvec) != 1) return 0; - item = ec_completed_item(gen_node); - if (item == NULL) - return -ENOMEM; - ret = ec_completed_item_set(item, EC_COMP_UNKNOWN, NULL); - if (ret < 0) { - ec_completed_item_free(item); - return ret; - } - ret = ec_completed_item_add(completed, item); - if (ret < 0) { - ec_completed_item_free(item); + ret = ec_completed_add_item(completed, gen_node, NULL, + EC_COMP_UNKNOWN, NULL, NULL); + if (ret < 0) return ret; - } return 0; } @@ -432,6 +502,7 @@ static void ec_completed_group_free(struct ec_completed_group *grp) ec_completed_item_free(item); } ec_parsed_free(ec_parsed_get_root(grp->state)); + ec_keyval_free(grp->attrs); ec_free(grp); } @@ -447,7 +518,6 @@ void ec_completed_free(struct ec_completed *completed) TAILQ_REMOVE(&completed->groups, grp, next); ec_completed_group_free(grp); } - ec_keyval_free(completed->attrs); ec_free(completed); } @@ -471,18 +541,36 @@ void ec_completed_dump(FILE *out, const struct ec_completed *completed) const char *typestr; switch (item->type) { - case EC_COMP_UNKNOWN: typestr = "no-match"; break; - case EC_COMP_FULL: typestr = "match"; break; - case EC_PARTIAL_MATCH: typestr = "partial-match"; break; + case EC_COMP_UNKNOWN: typestr = "unknown"; break; + case EC_COMP_FULL: typestr = "full"; break; + case EC_COMP_PARTIAL: typestr = "partial"; break; default: typestr = "unknown"; break; } - fprintf(out, " type=%s str=<%s> disp=<%s>\n", - typestr, item->str, item->display); + fprintf(out, " type=%s str=<%s> comp=<%s> disp=<%s>\n", + typestr, item->full, item->completion, + item->display); } } } +int ec_completed_merge(struct ec_completed *to, + struct ec_completed *from) +{ + struct ec_completed_group *grp; + + while (!TAILQ_EMPTY(&from->groups)) { + grp = TAILQ_FIRST(&from->groups); + TAILQ_REMOVE(&from->groups, grp, next); + TAILQ_INSERT_TAIL(&to->groups, grp, next); + } + to->count += from->count; + to->count_match += from->count_match; + + ec_completed_free(from); + return 0; +} + unsigned int ec_completed_count( const struct ec_completed *completed, enum ec_completed_type type) @@ -518,12 +606,12 @@ ec_completed_iter(struct ec_completed *completed, return iter; } -const struct ec_completed_item *ec_completed_iter_next( +struct ec_completed_item *ec_completed_iter_next( struct ec_completed_iter *iter) { - const struct ec_completed *completed = iter->completed; - const struct ec_completed_group *cur_node; - const struct ec_completed_item *cur_match; + struct ec_completed *completed = iter->completed; + struct ec_completed_group *cur_node; + struct ec_completed_item *cur_match; if (completed == NULL) return NULL; diff --git a/lib/ecoli_completed.h b/lib/ecoli_completed.h index c054994..f616e4b 100644 --- a/lib/ecoli_completed.h +++ b/lib/ecoli_completed.h @@ -44,9 +44,10 @@ struct ec_node; enum ec_completed_type { - EC_COMP_UNKNOWN, - EC_COMP_FULL, - EC_PARTIAL_MATCH, + EC_COMP_UNKNOWN = 0x1, + EC_COMP_FULL = 0x2, + EC_COMP_PARTIAL = 0x4, + EC_COMP_ALL = 0x7, }; struct ec_completed_item; @@ -58,6 +59,7 @@ struct ec_completed_group { const struct ec_node *node; struct ec_completed_item_list items; struct ec_parsed *state; + struct ec_keyval *attrs; }; TAILQ_HEAD(ec_completed_group_list, ec_completed_group); @@ -68,7 +70,6 @@ struct ec_completed { struct ec_parsed *cur_state; struct ec_completed_group *cur_group; struct ec_completed_group_list groups; - struct ec_keyval *attrs; // XXX per node instead? }; /* @@ -90,7 +91,7 @@ int ec_node_complete_child(struct ec_node *node, * * */ -struct ec_completed *ec_completed(void); +struct ec_completed *ec_completed(struct ec_parsed *state); /** * Free a completion object and all its items. @@ -107,32 +108,28 @@ void ec_completed_free(struct ec_completed *completed); void ec_completed_dump(FILE *out, const struct ec_completed *completed); - -struct ec_parsed *ec_completed_cur_parse_state(struct ec_completed *completed); - /** - * Create a completion item. - * + * Merge items contained in 'from' into 'to' * + * The 'from' completed struct is freed. */ -struct ec_completed_item * -ec_completed_item(const struct ec_node *node); +int ec_completed_merge(struct ec_completed *to, + struct ec_completed *from); -/** - * Set type and value of a completion item. - * - * - */ -int ec_completed_item_set(struct ec_completed_item *item, - enum ec_completed_type type, const char *str); +struct ec_parsed *ec_completed_get_state(struct ec_completed *completed); + +/* shortcut for ec_completed_item() + ec_completed_item_add() */ +int ec_completed_add_item(struct ec_completed *completed, + const struct ec_node *node, + struct ec_completed_item **p_item, + enum ec_completed_type type, + const char *start, const char *full); /** - * Add a completion item to a completion list. - * * */ -int ec_completed_item_add(struct ec_completed *completed, - struct ec_completed_item *item); +int ec_completed_item_set_str(struct ec_completed_item *item, + const char *str); /** * Get the string value of a completion item. @@ -150,6 +147,14 @@ ec_completed_item_get_str(const struct ec_completed_item *item); const char * ec_completed_item_get_display(const struct ec_completed_item *item); +/** + * Get the completion string value of a completion item. + * + * + */ +const char * +ec_completed_item_get_completion(const struct ec_completed_item *item); + /** * Get the group of a completion item. * @@ -175,19 +180,20 @@ const struct ec_node * ec_completed_item_get_node(const struct ec_completed_item *item); /** - * + * Set the display value of an item. * * */ -void ec_completed_item_free(struct ec_completed_item *item); +int ec_completed_item_set_display(struct ec_completed_item *item, + const char *display); /** - * Set the display value of an item. + * Set the completion value of an item. * * */ -int ec_completed_item_set_display(struct ec_completed_item *item, - const char *display); +int ec_completed_item_set_completion(struct ec_completed_item *item, + const char *completion); /** * @@ -215,9 +221,9 @@ unsigned int ec_completed_count( */ struct ec_completed_iter { enum ec_completed_type type; - const struct ec_completed *completed; - const struct ec_completed_group *cur_node; - const struct ec_completed_item *cur_match; + struct ec_completed *completed; + struct ec_completed_group *cur_node; + struct ec_completed_item *cur_match; }; /** @@ -234,7 +240,7 @@ ec_completed_iter(struct ec_completed *completed, * * */ -const struct ec_completed_item *ec_completed_iter_next( +struct ec_completed_item *ec_completed_iter_next( struct ec_completed_iter *iter); /** diff --git a/lib/ecoli_node.c b/lib/ecoli_node.c index 6cd95d7..089c0bc 100644 --- a/lib/ecoli_node.c +++ b/lib/ecoli_node.c @@ -243,3 +243,11 @@ const char *ec_node_desc(const struct ec_node *node) return node->desc; } + +int ec_node_check_type(const struct ec_node *node, + const struct ec_node_type *type) +{ + if (strcmp(node->type->name, type->name)) + return -EINVAL; + return 0; +} diff --git a/lib/ecoli_node.h b/lib/ecoli_node.h index 8fe2d76..8afcdbc 100644 --- a/lib/ecoli_node.h +++ b/lib/ecoli_node.h @@ -187,4 +187,8 @@ const char *ec_node_desc(const struct ec_node *node); void ec_node_dump(FILE *out, const struct ec_node *node); struct ec_node *ec_node_find(struct ec_node *node, const char *id); +/* check the type of a node */ +int ec_node_check_type(const struct ec_node *node, + const struct ec_node_type *type); + #endif diff --git a/lib/ecoli_node_expr.c b/lib/ecoli_node_expr.c index 129163e..4c546a7 100644 --- a/lib/ecoli_node_expr.c +++ b/lib/ecoli_node_expr.c @@ -243,6 +243,10 @@ int ec_node_expr_set_val_node(struct ec_node *gen_node, struct ec_node *val_node struct ec_node_expr *node = (struct ec_node_expr *)gen_node; int ret; + ret = ec_node_check_type(gen_node, &ec_node_expr_type); + if (ret < 0) + return ret; + ret = -EINVAL; if (val_node == NULL) goto fail; @@ -270,7 +274,9 @@ int ec_node_expr_add_bin_op(struct ec_node *gen_node, struct ec_node *op) struct ec_node **bin_ops; int ret; - // XXX check node type + ret = ec_node_check_type(gen_node, &ec_node_expr_type); + if (ret < 0) + return ret; ret = -EINVAL; if (node == NULL || op == NULL) @@ -304,7 +310,9 @@ int ec_node_expr_add_pre_op(struct ec_node *gen_node, struct ec_node *op) struct ec_node **pre_ops; int ret; - // XXX check node type + ret = ec_node_check_type(gen_node, &ec_node_expr_type); + if (ret < 0) + return ret; ret = -EINVAL; if (node == NULL || op == NULL) @@ -338,7 +346,9 @@ int ec_node_expr_add_post_op(struct ec_node *gen_node, struct ec_node *op) struct ec_node **post_ops; int ret; - // XXX check node type + ret = ec_node_check_type(gen_node, &ec_node_expr_type); + if (ret < 0) + return ret; ret = -EINVAL; if (node == NULL || op == NULL) @@ -373,7 +383,9 @@ int ec_node_expr_add_parenthesis(struct ec_node *gen_node, struct ec_node **open_ops, **close_ops; int ret; - // XXX check node type + ret = ec_node_check_type(gen_node, &ec_node_expr_type); + if (ret < 0) + return ret; ret = -EINVAL; if (node == NULL || open == NULL || close == NULL) @@ -416,6 +428,7 @@ enum expr_node_type { PAREN_OPEN, PAREN_CLOSE, }; + static enum expr_node_type get_node_type(const struct ec_node *expr_gen_node, const struct ec_node *check) { diff --git a/lib/ecoli_node_file.c b/lib/ecoli_node_file.c index 951c494..8bb7edd 100644 --- a/lib/ecoli_node_file.c +++ b/lib/ecoli_node_file.c @@ -118,13 +118,14 @@ ec_node_file_complete(const struct ec_node *gen_node, struct ec_completed *completed, const struct ec_strvec *strvec) { + char *dname = NULL, *bname = NULL, *effective_dir; struct ec_completed_item *item = NULL; + enum ec_completed_type type; struct stat st; const char *input; size_t bname_len; struct dirent *de = NULL; DIR *dir = NULL; - char *dname = NULL, *bname = NULL, *effective_dir; char *comp_str = NULL; char *disp_str = NULL; int ret; @@ -199,13 +200,8 @@ ec_node_file_complete(const struct ec_node *gen_node, is_dir = 0; } - item = ec_completed_item(gen_node); - if (item == NULL) { - ret = -ENOMEM; - goto out; - } - if (is_dir) { + type = EC_COMP_PARTIAL; if (asprintf(&comp_str, "%s%s/", input, &de->d_name[bname_len]) < 0) { ret = -errno; @@ -215,11 +211,8 @@ ec_node_file_complete(const struct ec_node *gen_node, ret = -errno; goto out; } - ret = ec_completed_item_set(item, EC_PARTIAL_MATCH, - comp_str); - if (ret < 0) - goto out; } else { + type = EC_COMP_FULL; if (asprintf(&comp_str, "%s%s", input, &de->d_name[bname_len]) < 0) { ret = -errno; @@ -229,15 +222,15 @@ ec_node_file_complete(const struct ec_node *gen_node, ret = -errno; goto out; } - ret = ec_completed_item_set(item, EC_COMP_FULL, - comp_str); - if (ret < 0) - goto out; } - ret = ec_completed_item_set_display(item, disp_str); + ret = ec_completed_add_item(completed, gen_node, &item, + type, input, comp_str); if (ret < 0) goto out; - ret = ec_completed_item_add(completed, item); + + /* fix the display string: we don't want to display the full + * path. */ + ret = ec_completed_item_set_display(item, disp_str); if (ret < 0) goto out; @@ -250,7 +243,6 @@ ec_node_file_complete(const struct ec_node *gen_node, ret = 0; out: - ec_completed_item_free(item); free(comp_str); free(disp_str); ec_free(dname); diff --git a/lib/ecoli_node_int.c b/lib/ecoli_node_int.c index cb7f60b..bd262fa 100644 --- a/lib/ecoli_node_int.c +++ b/lib/ecoli_node_int.c @@ -209,6 +209,7 @@ int64_t ec_node_int_getval(struct ec_node *gen_node, const char *str) // XXX check type here // if gen_node->type != int fail + // we may need to change the API (return int + val in a ptr ?) parse_llint(node, str, &val); diff --git a/lib/ecoli_node_many.c b/lib/ecoli_node_many.c index 8395bd1..c718890 100644 --- a/lib/ecoli_node_many.c +++ b/lib/ecoli_node_many.c @@ -108,7 +108,7 @@ __ec_node_many_complete(struct ec_node_many *node, unsigned int max, struct ec_completed *completed, const struct ec_strvec *strvec) { - struct ec_parsed *parsed = ec_completed_cur_parse_state(completed); + struct ec_parsed *parsed = ec_completed_get_state(completed); struct ec_strvec *childvec = NULL; unsigned int i; int ret; diff --git a/lib/ecoli_node_once.c b/lib/ecoli_node_once.c index 2020088..b01640d 100644 --- a/lib/ecoli_node_once.c +++ b/lib/ecoli_node_once.c @@ -93,7 +93,7 @@ ec_node_once_complete(const struct ec_node *gen_node, const struct ec_strvec *strvec) { struct ec_node_once *node = (struct ec_node_once *)gen_node; - struct ec_parsed *parsed = ec_completed_cur_parse_state(completed); + struct ec_parsed *parsed = ec_completed_get_state(completed); unsigned int count; int ret; @@ -131,14 +131,15 @@ EC_NODE_TYPE_REGISTER(ec_node_once_type); int ec_node_once_set(struct ec_node *gen_node, struct ec_node *child) { struct ec_node_once *node = (struct ec_node_once *)gen_node; + int ret; - // XXX check node type - - assert(node != NULL); - - if (child == NULL) + if (gen_node == NULL || child == NULL) return -EINVAL; + ret = ec_node_check_type(gen_node, &ec_node_once_type); + if (ret < 0) + return ret; + gen_node->flags &= ~EC_NODE_F_BUILT; node->child = child; diff --git a/lib/ecoli_node_seq.c b/lib/ecoli_node_seq.c index 6025fd7..9e0440b 100644 --- a/lib/ecoli_node_seq.c +++ b/lib/ecoli_node_seq.c @@ -100,7 +100,7 @@ __ec_node_seq_complete(struct ec_node **table, size_t table_len, struct ec_completed *completed, const struct ec_strvec *strvec) { - struct ec_parsed *parsed = ec_completed_cur_parse_state(completed); + struct ec_parsed *parsed = ec_completed_get_state(completed); struct ec_strvec *childvec = NULL; unsigned int i; int ret; diff --git a/lib/ecoli_node_sh_lex.c b/lib/ecoli_node_sh_lex.c index 1a5e4ab..e3580d7 100644 --- a/lib/ecoli_node_sh_lex.c +++ b/lib/ecoli_node_sh_lex.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#define _GNU_SOURCE /* for asprintf */ #include #include #include @@ -164,6 +165,8 @@ static struct ec_strvec *tokenize(const char *str, int completion, goto fail; while (str[off] != '\0') { + if (missing_quote != NULL) + *missing_quote = '\0'; len = eat_spaces(&str[off]); if (len > 0) last_is_space = 1; @@ -172,6 +175,8 @@ static struct ec_strvec *tokenize(const char *str, int completion, len = 0; suboff = off; while (str[suboff] != '\0') { + if (missing_quote != NULL) + *missing_quote = '\0'; last_is_space = 0; if (str[suboff] == '"' || str[suboff] == '\'') { sublen = eat_quoted_str(&str[suboff]); @@ -211,8 +216,6 @@ static struct ec_strvec *tokenize(const char *str, int completion, concat = NULL; } - /* XXX remove all printf comments */ -// printf("str off=%zd len=%zd\n", off, len); off += len; } @@ -283,7 +286,11 @@ ec_node_sh_lex_complete(const struct ec_node *gen_node, const struct ec_strvec *strvec) { struct ec_node_sh_lex *node = (struct ec_node_sh_lex *)gen_node; + struct ec_completed *tmp_completed = NULL; struct ec_strvec *new_vec = NULL; + struct ec_completed_iter *iter = NULL; + struct ec_completed_item *item = NULL; + char *new_str = NULL; const char *str; char missing_quote; int ret; @@ -292,24 +299,63 @@ ec_node_sh_lex_complete(const struct ec_node *gen_node, 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 - // XXX: if no quotes, replace " " by "\ " - ret = ec_node_complete_child(node->child, completed, new_vec); + /* we will store the completions in a temporary struct, because + * we want to update them (ex: add missing quotes) */ + tmp_completed = ec_completed(ec_completed_get_state(completed)); + if (tmp_completed == NULL) + goto fail; + + ret = ec_node_complete_child(node->child, tmp_completed, new_vec); if (ret < 0) goto fail; + /* add missing quote for full completions */ + if (missing_quote != '\0') { + iter = ec_completed_iter(tmp_completed, EC_COMP_FULL); + if (iter == NULL) + goto fail; + while ((item = ec_completed_iter_next(iter)) != NULL) { + str = ec_completed_item_get_str(item); + if (asprintf(&new_str, "%c%s%c", missing_quote, str, + missing_quote) < 0) { + new_str = NULL; + goto fail; + } + if (ec_completed_item_set_str(item, new_str) < 0) + goto fail; + free(new_str); + new_str = NULL; + + str = ec_completed_item_get_completion(item); + if (asprintf(&new_str, "%s%c", str, + missing_quote) < 0) { + new_str = NULL; + goto fail; + } + if (ec_completed_item_set_completion(item, new_str) < 0) + goto fail; + free(new_str); + new_str = NULL; + } + } + + ec_completed_iter_free(iter); ec_strvec_free(new_vec); + ec_completed_merge(completed, tmp_completed); + return 0; fail: + ec_completed_free(tmp_completed); + ec_completed_iter_free(iter); ec_strvec_free(new_vec); + free(new_str); + return -1; } @@ -425,6 +471,9 @@ static int ec_node_sh_lex_testcase(void) ret |= EC_TEST_CHECK_COMPLETE(node, "foo barx", EC_NODE_ENDLIST, EC_NODE_ENDLIST); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo 'b", EC_NODE_ENDLIST, + "'bar'", EC_NODE_ENDLIST); ec_node_free(node); return ret; diff --git a/lib/ecoli_node_str.c b/lib/ecoli_node_str.c index 6c47980..ab7ff11 100644 --- a/lib/ecoli_node_str.c +++ b/lib/ecoli_node_str.c @@ -72,11 +72,9 @@ ec_node_str_complete(const struct ec_node *gen_node, struct ec_completed *completed, const struct ec_strvec *strvec) { - struct ec_completed_item *item = NULL; struct ec_node_str *node = (struct ec_node_str *)gen_node; const char *str; size_t n = 0; - int ret; if (ec_strvec_len(strvec) != 1) return 0; @@ -91,19 +89,9 @@ ec_node_str_complete(const struct ec_node *gen_node, if (str[n] != '\0') return 0; // XXX add a no_match instead? - item = ec_completed_item(gen_node); - if (item == NULL) - return -ENOMEM; - ret = ec_completed_item_set(item, EC_COMP_FULL, node->string); - if (ret < 0) { - ec_completed_item_free(item); - return ret; - } - ret = ec_completed_item_add(completed, item); - if (ret < 0) { - ec_completed_item_free(item); - return ret; - } + if (ec_completed_add_item(completed, gen_node, NULL, EC_COMP_FULL, + str, node->string) < 0) + return -1; return 0; } @@ -143,6 +131,11 @@ EC_NODE_TYPE_REGISTER(ec_node_str_type); int ec_node_str_set_str(struct ec_node *gen_node, const char *str) { struct ec_node_str *node = (struct ec_node_str *)gen_node; + int ret; + + ret = ec_node_check_type(gen_node, &ec_node_str_type); + if (ret < 0) + return ret; if (str == NULL) return -EINVAL; diff --git a/lib/ecoli_node_subset.c b/lib/ecoli_node_subset.c index b56bf81..8fe4b65 100644 --- a/lib/ecoli_node_subset.c +++ b/lib/ecoli_node_subset.c @@ -181,7 +181,7 @@ __ec_node_subset_complete(struct ec_node **table, size_t table_len, struct ec_completed *completed, const struct ec_strvec *strvec) { - struct ec_parsed *parsed = ec_completed_cur_parse_state(completed); + struct ec_parsed *parsed = ec_completed_get_state(completed); struct ec_strvec *childvec = NULL; struct ec_node *save; size_t i, len; diff --git a/lib/ecoli_parsed.c b/lib/ecoli_parsed.c index 3fb9db2..f7ca38f 100644 --- a/lib/ecoli_parsed.c +++ b/lib/ecoli_parsed.c @@ -42,6 +42,13 @@ TAILQ_HEAD(ec_parsed_list, ec_parsed); +/* XXX idea for parse: maintain a "cursor" ? +struct ec_parsed { + struct ec_parsed_tree *root; + stuct ec_parsed_tree *cursor; +}; +*/ + struct ec_parsed { TAILQ_ENTRY(ec_parsed) next; struct ec_parsed_list children; diff --git a/lib/main-readline.c b/lib/main-readline.c index 141c8cd..9dc0380 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -63,15 +63,15 @@ static char *my_completion_entry(const char *s, int state) (void)s; - /* don't append a quote */ + /* Don't append a quote. Note: there are still some bugs when + * completing a quoted token. */ rl_completion_suppress_quote = 1; - rl_basic_quote_characters = ""; + rl_completer_quote_characters = "\"'"; if (state == 0) { char *line; ec_completed_free(c); - line = strdup(rl_line_buffer); if (line == NULL) return NULL; @@ -83,7 +83,7 @@ static char *my_completion_entry(const char *s, int state) return NULL; ec_completed_iter_free(iter); - iter = ec_completed_iter(c, EC_COMP_FULL | EC_PARTIAL_MATCH); + iter = ec_completed_iter(c, EC_COMP_FULL | EC_COMP_PARTIAL); if (iter == NULL) return NULL; } @@ -134,12 +134,10 @@ static char *get_node_help(const struct ec_completed_item *item) char *help = NULL; const char *node_help = NULL; const char *node_desc = NULL; -// size_t i; - (void)item; -#if 1 grp = ec_completed_item_get_grp(item); state = grp->state; + ec_parsed_dump(stdout, ec_parsed_get_root(state)); for (state = grp->state; state != NULL; state = ec_parsed_get_parent(state)) { node = ec_parsed_get_node(state); @@ -148,11 +146,6 @@ static char *get_node_help(const struct ec_completed_item *item) if (node_desc == NULL) node_desc = ec_node_desc(node); } -#else - node = ec_completed_item_get_node(item); - node_help = ec_keyval_get(ec_node_attrs(node), "help"); - node_desc = ec_node_desc(node); -#endif if (node_help == NULL) node_help = "-"; @@ -199,12 +192,12 @@ static int show_help(int ignore, int invoking_key) if (c == NULL) goto fail; - //ec_completed_dump(stdout, c); + ec_completed_dump(stdout, c); /* let's display one contextual help per node */ count = 0; iter = ec_completed_iter(c, - EC_COMP_UNKNOWN | EC_COMP_FULL | EC_PARTIAL_MATCH); + EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL); if (iter == NULL) goto fail; diff --git a/lib/todo.txt b/lib/todo.txt index 734de6b..7a73930 100644 --- a/lib/todo.txt +++ b/lib/todo.txt @@ -322,6 +322,17 @@ changes: - ec_completed_item_set_display() - ec_completed_item_add() +----- + +sh_lex + or + str(foo) + str(foo2) + str(bar) + +complete(sh_lex, ["'fo"]) + complete(sh_lex, ["fo"]) -> ["foo", "foo2"] + ----- -- 2.39.5