+ 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);