X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fecoli_completed.c;h=a342105096df4571eee674399e12634be0885d00;hb=7cbb8a1000b85db2a487afd4d17e688b8c0aa756;hp=5e79ac2957a08bacdd07a3cd3153f9996b93a151;hpb=5510aae15debf26e0e0b07732f6fd041fd53acb6;p=protos%2Flibecoli.git diff --git a/lib/ecoli_completed.c b/lib/ecoli_completed.c index 5e79ac2..a342105 100644 --- a/lib/ecoli_completed.c +++ b/lib/ecoli_completed.c @@ -1,28 +1,5 @@ -/* +/* SPDX-License-Identifier: BSD-3-Clause * Copyright (c) 2016, Olivier MATZ - * - * 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. */ #include @@ -32,6 +9,7 @@ #include #include +#include #include #include #include @@ -39,85 +17,113 @@ #include #include -struct ec_completed *ec_completed(void) +struct ec_completed_item { + TAILQ_ENTRY(ec_completed_item) next; + enum ec_completed_type type; + const struct ec_node *node; + struct ec_completed_group *grp; + 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(struct ec_parsed *state) { struct ec_completed *completed = NULL; completed = ec_calloc(1, sizeof(*completed)); if (completed == NULL) - return NULL; + goto fail; + + completed->attrs = ec_keyval(); + if (completed->attrs == NULL) + goto fail; + + TAILQ_INIT(&completed->groups); - TAILQ_INIT(&completed->elts); - completed->count_match = 0; + completed->cur_state = state; return completed; + + fail: + if (completed != NULL) + ec_keyval_free(completed->attrs); + ec_free(completed); + + return NULL; } -struct ec_completed * -ec_node_complete_child(struct ec_node *node, - struct ec_parsed *state, +struct ec_parsed *ec_completed_get_state(struct ec_completed *completed) +{ + return completed->cur_state; +} + +int +ec_node_complete_child(const struct ec_node *node, + struct ec_completed *completed, const struct ec_strvec *strvec) { - struct ec_completed *completed; - struct ec_parsed *child; + struct ec_parsed *child_state, *cur_state; + struct ec_completed_group *cur_group; int ret; - /* build the node if required */ - if (node->type->build != NULL) { - if ((node->flags & EC_NODE_F_BUILT) == 0) { - ret = node->type->build(node); - if (ret < 0) { - errno = -ret; - return NULL; - } - } - } - node->flags |= EC_NODE_F_BUILT; + if (node->type->complete == NULL) + return -ENOTSUP; - if (node->type->complete == NULL) { - errno = ENOTSUP; - return NULL; - } + /* save previous parse state, prepare child state */ + cur_state = completed->cur_state; + child_state = ec_parsed(node); + if (child_state == NULL) + return -ENOMEM; - child = ec_parsed(); - if (child == NULL) - return NULL; + if (cur_state != NULL) + ec_parsed_add_child(cur_state, child_state); + completed->cur_state = child_state; + cur_group = completed->cur_group; + completed->cur_group = NULL; - child->node = node; - ec_parsed_add_child(state, child); - completed = node->type->complete(node, child, strvec); + /* fill the completed struct with items */ + ret = node->type->complete(node, completed, strvec); -#if 0 // XXX dump - printf("----------------------------------------------------------\n"); - ec_node_dump(stdout, node); - ec_strvec_dump(stdout, strvec); - ec_completed_dump(stdout, completed); - ec_parsed_dump(stdout, state); -#endif + /* restore parent parse state */ + if (cur_state != NULL) { + ec_parsed_del_child(cur_state, child_state); + assert(!ec_parsed_has_child(child_state)); + } + ec_parsed_free(child_state); + completed->cur_state = cur_state; + completed->cur_group = cur_group; - ec_parsed_del_child(state, child); - assert(TAILQ_EMPTY(&child->children)); - ec_parsed_free(child); + if (ret < 0) + return ret; - return completed; + return 0; } -struct ec_completed *ec_node_complete_strvec(struct ec_node *node, +struct ec_completed *ec_node_complete_strvec(const struct ec_node *node, const struct ec_strvec *strvec) { - struct ec_parsed *state = ec_parsed(); - struct ec_completed *completed; + struct ec_completed *completed = NULL; + int ret; - if (state == NULL) - return NULL; + completed = ec_completed(NULL); + if (completed == NULL) + goto fail; - completed = ec_node_complete_child(node, state, strvec); - ec_parsed_free(state); + ret = ec_node_complete_child(node, completed, strvec); + if (ret < 0) + goto fail; return completed; + +fail: + ec_completed_free(completed); + return NULL; } -struct ec_completed *ec_node_complete(struct ec_node *node, +struct ec_completed *ec_node_complete(const struct ec_node *node, const char *str) { struct ec_strvec *strvec = NULL; @@ -143,214 +149,423 @@ struct ec_completed *ec_node_complete(struct ec_node *node, return NULL; } -/* default completion function: return a no-match element */ -struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node, - struct ec_parsed *state, - const struct ec_strvec *strvec) +static struct ec_completed_group * +ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed) { - struct ec_completed *completed; - - (void)strvec; - (void)state; + struct ec_completed_group *grp = NULL; - completed = ec_completed(); - if (completed == NULL) + grp = ec_calloc(1, sizeof(*grp)); + if (grp == NULL) return NULL; - if (ec_strvec_len(strvec) != 1) - return completed; - - if (ec_completed_add_elt(completed, state, gen_node, NULL) < 0) { - ec_completed_free(completed); - return NULL; - } + grp->attrs = ec_keyval(); + if (grp->attrs == NULL) + goto fail; - return completed; -} + grp->state = ec_parsed_dup(parsed); + if (grp->state == NULL) + goto fail; -/* count the number of identical chars at the beginning of 2 strings */ -static size_t strcmp_count(const char *s1, const char *s2) -{ - size_t i = 0; + grp->node = node; + TAILQ_INIT(&grp->items); - while (s1[i] && s2[i] && s1[i] == s2[i]) - i++; + return grp; - return i; +fail: + if (grp != NULL) { + ec_parsed_free(grp->state); + ec_keyval_free(grp->attrs); + } + ec_free(grp); + return NULL; } -static struct ec_completed_elt * -ec_completed_elt(struct ec_parsed *parsed, - const struct ec_node *node, const char *add) +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_elt *elt = NULL; + 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; - elt = ec_calloc(1, sizeof(*elt)); - if (elt == NULL) + if (type == EC_COMP_UNKNOWN && full != NULL) { + errno = EINVAL; return NULL; + } + if (type != EC_COMP_UNKNOWN && full == NULL) { + errno = EINVAL; + return NULL; + } - if (parsed != NULL) { - struct ec_parsed *p; - size_t len; + item = ec_calloc(1, sizeof(*item)); + if (item == NULL) + goto fail; - /* get path len */ - for (p = parsed, len = 0; p != NULL; - p = ec_parsed_get_parent(p), len++) - ; + attrs = ec_keyval(); + if (attrs == NULL) + goto fail; - elt->path = ec_calloc(len, sizeof(*elt->path)); - if (elt->path == NULL) + if (start != NULL) { + start_cp = ec_strdup(start); + if (start_cp == 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; + if (ec_str_startswith(full, start)) { + comp_cp = ec_strdup(&full[strlen(start)]); + if (comp_cp == NULL) + goto fail; + } } - - elt->node = node; - if (add != NULL) { - elt->add = ec_strdup(add); - if (elt->add == NULL) + 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; } - return elt; + 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 (elt != NULL) { - ec_free(elt->path); - ec_free(elt->add); - } - ec_completed_elt_free(elt); + ec_keyval_free(attrs); + ec_free(comp_cp); + ec_free(start_cp); + ec_free(full_cp); + ec_free(display_cp); + ec_free(item); return NULL; } -static int __ec_completed_add_elt(struct ec_completed *completed, - struct ec_completed_elt *elt) +int ec_completed_item_set_display(struct ec_completed_item *item, + const char *display) { - size_t n; + char *display_copy = NULL; + int ret = 0; - TAILQ_INSERT_TAIL(&completed->elts, elt, next); - completed->count++; - if (elt->add != NULL) { - completed->count_match++; - if (completed->smallest_start == NULL) { - completed->smallest_start = ec_strdup(elt->add); - } else { - n = strcmp_count(elt->add, - completed->smallest_start); - completed->smallest_start[n] = '\0'; - } - } + if (item == NULL || display == NULL || + item->type == EC_COMP_UNKNOWN) + return -EINVAL; + + display_copy = ec_strdup(display); + if (display_copy == NULL) + goto fail; + + ec_free(item->display); + item->display = display_copy; return 0; + +fail: + ec_free(display_copy); + return ret; } -int ec_completed_add_elt(struct ec_completed *completed, - struct ec_parsed *parsed, - const struct ec_node *node, const char *add) +int +ec_completed_item_set_completion(struct ec_completed_item *item, + const char *completion) { - struct ec_completed_elt *elt; + char *completion_copy = NULL; + int ret = 0; - elt = ec_completed_elt(parsed, node, add); - if (elt == NULL) - return -ENOMEM; + if (item == NULL || completion == NULL || + item->type == EC_COMP_UNKNOWN) + return -EINVAL; - return __ec_completed_add_elt(completed, elt); + ret = -ENOMEM; + completion_copy = ec_strdup(completion); + if (completion_copy == NULL) + goto fail; + + ec_free(item->completion); + item->completion = completion_copy; + + return 0; + +fail: + ec_free(completion_copy); + return ret; } -void ec_completed_elt_free(struct ec_completed_elt *elt) +int +ec_completed_item_set_str(struct ec_completed_item *item, + const char *str) { - ec_free(elt->add); - ec_free(elt->path); - ec_free(elt); + 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; } -void ec_completed_merge(struct ec_completed *completed1, - struct ec_completed *completed2) +static int +ec_completed_item_add(struct ec_completed *completed, + struct ec_completed_item *item) { - struct ec_completed_elt *elt; + if (completed == NULL || item == NULL || item->node == NULL) + return -EINVAL; + + switch (item->type) { + case EC_COMP_UNKNOWN: + completed->count_unknown++; + break; + case EC_COMP_FULL: + completed->count_full++; + break; + case EC_COMP_PARTIAL: + completed->count_partial++; + break; + default: + return -EINVAL; + } - assert(completed1 != NULL); - assert(completed2 != NULL); + if (completed->cur_group == NULL) { + struct ec_completed_group *grp; - while (!TAILQ_EMPTY(&completed2->elts)) { - elt = TAILQ_FIRST(&completed2->elts); - TAILQ_REMOVE(&completed2->elts, elt, next); - __ec_completed_add_elt(completed1, elt); + grp = ec_completed_group(item->node, completed->cur_state); + if (grp == NULL) + return -ENOMEM; + TAILQ_INSERT_TAIL(&completed->groups, grp, next); + completed->cur_group = grp; } - ec_completed_free(completed2); + completed->count++; + TAILQ_INSERT_TAIL(&completed->cur_group->items, item, next); + item->grp = completed->cur_group; + + return 0; +} + +const char * +ec_completed_item_get_str(const struct ec_completed_item *item) +{ + return item->full; +} + +const char * +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) +{ + return item->type; +} + +const struct ec_node * +ec_completed_item_get_node(const struct ec_completed_item *item) +{ + return item->node; +} + +const struct ec_completed_group * +ec_completed_item_get_grp(const struct ec_completed_item *item) +{ + return item->grp; +} + +static void +ec_completed_item_free(struct ec_completed_item *item) +{ + if (item == NULL) + return; + + 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) +{ + int ret; + + if (ec_strvec_len(strvec) != 1) + return 0; + + ret = ec_completed_add_item(completed, gen_node, NULL, + EC_COMP_UNKNOWN, NULL, NULL); + if (ret < 0) + return ret; + + return 0; +} + +static void ec_completed_group_free(struct ec_completed_group *grp) +{ + struct ec_completed_item *item; + + if (grp == NULL) + return; + + while (!TAILQ_EMPTY(&grp->items)) { + item = TAILQ_FIRST(&grp->items); + TAILQ_REMOVE(&grp->items, item, next); + ec_completed_item_free(item); + } + ec_parsed_free(ec_parsed_get_root(grp->state)); + ec_keyval_free(grp->attrs); + ec_free(grp); } void ec_completed_free(struct ec_completed *completed) { - struct ec_completed_elt *elt; + struct ec_completed_group *grp; if (completed == NULL) return; - while (!TAILQ_EMPTY(&completed->elts)) { - elt = TAILQ_FIRST(&completed->elts); - TAILQ_REMOVE(&completed->elts, elt, next); - ec_completed_elt_free(elt); + while (!TAILQ_EMPTY(&completed->groups)) { + grp = TAILQ_FIRST(&completed->groups); + TAILQ_REMOVE(&completed->groups, grp, next); + ec_completed_group_free(grp); } - ec_free(completed->smallest_start); + ec_keyval_free(completed->attrs); ec_free(completed); } void ec_completed_dump(FILE *out, const struct ec_completed *completed) { - struct ec_completed_elt *elt; + struct ec_completed_group *grp; + struct ec_completed_item *item; if (completed == NULL || completed->count == 0) { fprintf(out, "no completion\n"); return; } - fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n", - completed->count, completed->count_match, - completed->smallest_start); + fprintf(out, "completion: count=%u full=%u partial=%u unknown=%u\n", + completed->count, completed->count_full, + completed->count_partial, completed->count_unknown); + + TAILQ_FOREACH(grp, &completed->groups, next) { + fprintf(out, "node=%p, node_type=%s\n", + grp->node, grp->node->type->name); + TAILQ_FOREACH(item, &grp->items, next) { + const char *typestr; + + switch (item->type) { + 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; + } - TAILQ_FOREACH(elt, &completed->elts, next) { - fprintf(out, "add=<%s>, node=%p, node_type=%s\n", - elt->add, elt->node, elt->node->type->name); + fprintf(out, " type=%s str=<%s> comp=<%s> disp=<%s>\n", + typestr, item->full, item->completion, + item->display); + } } } -const char *ec_completed_smallest_start( - const struct ec_completed *completed) +int ec_completed_merge(struct ec_completed *to, + struct ec_completed *from) { - if (completed == NULL || completed->smallest_start == NULL) - return ""; + 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_full += from->count_full; + to->count_partial += from->count_partial; + to->count_unknown += from->count_unknown; - return completed->smallest_start; + ec_completed_free(from); + return 0; } unsigned int ec_completed_count( const struct ec_completed *completed, - enum ec_completed_filter_flags flags) + enum ec_completed_type type) { unsigned int count = 0; if (completed == NULL) return count; - if (flags & EC_MATCH) - count += completed->count_match; - if (flags & EC_NO_MATCH) - count += (completed->count - completed->count_match); //XXX + if (type & EC_COMP_FULL) + count += completed->count_full; + if (type & EC_COMP_PARTIAL) + count += completed->count_partial; + if (type & EC_COMP_UNKNOWN) + count += completed->count_unknown; return count; } struct ec_completed_iter * ec_completed_iter(struct ec_completed *completed, - enum ec_completed_filter_flags flags) + enum ec_completed_type type) { struct ec_completed_iter *iter; @@ -359,38 +574,60 @@ ec_completed_iter(struct ec_completed *completed, return NULL; iter->completed = completed; - iter->flags = flags; - iter->cur = NULL; + iter->type = type; + iter->cur_node = NULL; + iter->cur_match = NULL; return iter; } -const struct ec_completed_elt *ec_completed_iter_next( +struct ec_completed_item *ec_completed_iter_next( struct ec_completed_iter *iter) { - if (iter->completed == NULL) - return NULL; - - do { - if (iter->cur == NULL) - iter->cur = TAILQ_FIRST(&iter->completed->elts); - else - iter->cur = TAILQ_NEXT(iter->cur, next); + struct ec_completed *completed; + struct ec_completed_group *cur_node; + struct ec_completed_item *cur_match; - if (iter->cur == NULL) - break; + if (iter == NULL) + return NULL; + completed = iter->completed; + if (completed == NULL) + return NULL; - if (iter->cur->add == NULL && - (iter->flags & EC_NO_MATCH)) - break; + cur_node = iter->cur_node; + cur_match = iter->cur_match; - if (iter->cur->add != NULL && - (iter->flags & EC_MATCH)) - break; + /* first call */ + if (cur_node == NULL) { + TAILQ_FOREACH(cur_node, &completed->groups, next) { + TAILQ_FOREACH(cur_match, &cur_node->items, next) { + 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 && + cur_match->type & iter->type) + goto found; + cur_node = TAILQ_NEXT(cur_node, next); + while (cur_node != NULL) { + cur_match = TAILQ_FIRST(&cur_node->items); + if (cur_match != NULL && + cur_match->type & iter->type) + goto found; + cur_node = TAILQ_NEXT(cur_node, next); + } + return NULL; + } - } while (iter->cur != NULL); +found: + iter->cur_node = cur_node; + iter->cur_match = cur_match; - return iter->cur; + return iter->cur_match; } void ec_completed_iter_free(struct ec_completed_iter *iter)