2 * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the University of California, Berkeley nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <ecoli_malloc.h>
35 #include <ecoli_string.h>
36 #include <ecoli_strvec.h>
37 #include <ecoli_keyval.h>
38 #include <ecoli_log.h>
39 #include <ecoli_node.h>
40 #include <ecoli_parsed.h>
41 #include <ecoli_completed.h>
43 struct ec_completed_item {
44 TAILQ_ENTRY(ec_completed_item) next;
45 enum ec_completed_type type;
46 const struct ec_node *node;
47 struct ec_completed_group *grp;
48 char *start; /* the initial token */
49 char *full; /* the full token after completion */
50 char *completion; /* chars that are added, NULL if not applicable */
51 char *display; /* what should be displayed by help/completers */
52 struct ec_keyval *attrs;
55 struct ec_completed *ec_completed(struct ec_parsed *state)
57 struct ec_completed *completed = NULL;
59 completed = ec_calloc(1, sizeof(*completed));
60 if (completed == NULL)
63 completed->attrs = ec_keyval();
64 if (completed->attrs == NULL)
67 TAILQ_INIT(&completed->groups);
69 completed->cur_state = state;
74 if (completed != NULL)
75 ec_keyval_free(completed->attrs);
81 struct ec_parsed *ec_completed_get_state(struct ec_completed *completed)
83 return completed->cur_state;
87 ec_node_complete_child(struct ec_node *node, struct ec_completed *completed,
88 const struct ec_strvec *strvec)
90 struct ec_parsed *child_state, *cur_state;
91 struct ec_completed_group *cur_group;
94 /* build the node if required */
95 if (node->type->build != NULL) {
96 if ((node->flags & EC_NODE_F_BUILT) == 0) {
97 ret = node->type->build(node);
102 node->flags |= EC_NODE_F_BUILT;
104 if (node->type->complete == NULL)
107 /* save previous parse state, prepare child state */
108 cur_state = completed->cur_state;
109 child_state = ec_parsed(node);
110 if (child_state == NULL)
113 if (cur_state != NULL)
114 ec_parsed_add_child(cur_state, child_state);
115 completed->cur_state = child_state;
116 cur_group = completed->cur_group;
117 completed->cur_group = NULL;
119 /* fill the completed struct with items */
120 ret = node->type->complete(node, completed, strvec);
122 /* restore parent parse state */
123 if (cur_state != NULL) {
124 ec_parsed_del_child(cur_state, child_state);
125 assert(!ec_parsed_has_child(child_state));
127 ec_parsed_free(child_state);
128 completed->cur_state = cur_state;
129 completed->cur_group = cur_group;
135 printf("----------------------------------------------------------\n");
136 ec_node_dump(stdout, node);
137 ec_strvec_dump(stdout, strvec);
138 ec_completed_dump(stdout, completed);
144 struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
145 const struct ec_strvec *strvec)
147 struct ec_completed *completed = NULL;
150 completed = ec_completed(NULL);
151 if (completed == NULL)
154 ret = ec_node_complete_child(node, completed, strvec);
161 ec_completed_free(completed);
165 struct ec_completed *ec_node_complete(struct ec_node *node,
168 struct ec_strvec *strvec = NULL;
169 struct ec_completed *completed;
172 strvec = ec_strvec();
176 if (ec_strvec_add(strvec, str) < 0)
179 completed = ec_node_complete_strvec(node, strvec);
180 if (completed == NULL)
183 ec_strvec_free(strvec);
187 ec_strvec_free(strvec);
191 static struct ec_completed_group *
192 ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed)
194 struct ec_completed_group *grp = NULL;
196 grp = ec_calloc(1, sizeof(*grp));
200 grp->attrs = ec_keyval();
201 if (grp->attrs == NULL)
204 grp->state = ec_parsed_dup(parsed);
205 if (grp->state == NULL)
209 TAILQ_INIT(&grp->items);
215 ec_parsed_free(grp->state);
216 ec_keyval_free(grp->attrs);
222 static struct ec_completed_item *
223 ec_completed_item(const struct ec_node *node, enum ec_completed_type type,
224 const char *start, const char *full)
226 struct ec_completed_item *item = NULL;
227 struct ec_keyval *attrs = NULL;
228 char *comp_cp = NULL, *start_cp = NULL;
229 char *full_cp = NULL, *display_cp = NULL;
231 if (type == EC_COMP_UNKNOWN && full != NULL) {
235 if (type != EC_COMP_UNKNOWN && full == NULL) {
240 item = ec_calloc(1, sizeof(*item));
249 start_cp = ec_strdup(start);
250 if (start_cp == NULL)
253 if (ec_str_startswith(full, start)) {
254 comp_cp = ec_strdup(&full[strlen(start)]);
260 full_cp = ec_strdup(full);
263 display_cp = ec_strdup(full);
264 if (display_cp == NULL)
270 item->start = start_cp;
271 item->full = full_cp;
272 item->completion = comp_cp;
273 item->display = display_cp;
279 ec_keyval_free(item->attrs);
289 int ec_completed_item_set_display(struct ec_completed_item *item,
292 char *display_copy = NULL;
295 if (item == NULL || display == NULL ||
296 item->type == EC_COMP_UNKNOWN)
299 display_copy = ec_strdup(display);
300 if (display_copy == NULL)
303 ec_free(item->display);
304 item->display = display_copy;
309 ec_free(display_copy);
314 ec_completed_item_set_completion(struct ec_completed_item *item,
315 const char *completion)
317 char *completion_copy = NULL;
320 if (item == NULL || completion == NULL ||
321 item->type == EC_COMP_UNKNOWN)
325 completion_copy = ec_strdup(completion);
326 if (completion_copy == NULL)
329 ec_free(item->completion);
330 item->completion = completion_copy;
335 ec_free(completion_copy);
340 ec_completed_item_set_str(struct ec_completed_item *item,
343 char *str_copy = NULL;
346 if (item == NULL || str == NULL ||
347 item->type == EC_COMP_UNKNOWN)
351 str_copy = ec_strdup(str);
352 if (str_copy == NULL)
356 item->full = str_copy;
366 ec_completed_item_add(struct ec_completed *completed,
367 struct ec_completed_item *item)
369 if (completed == NULL || item == NULL || item->node == NULL)
372 switch (item->type) {
373 case EC_COMP_UNKNOWN:
376 case EC_COMP_PARTIAL:
377 completed->count_match++; //XXX
383 if (completed->cur_group == NULL) {
384 struct ec_completed_group *grp;
386 grp = ec_completed_group(item->node, completed->cur_state);
389 TAILQ_INSERT_TAIL(&completed->groups, grp, next);
390 completed->cur_group = grp;
394 TAILQ_INSERT_TAIL(&completed->cur_group->items, item, next);
395 item->grp = completed->cur_group;
401 ec_completed_item_get_str(const struct ec_completed_item *item)
407 ec_completed_item_get_display(const struct ec_completed_item *item)
409 return item->display;
413 ec_completed_item_get_completion(const struct ec_completed_item *item)
415 return item->completion;
418 enum ec_completed_type
419 ec_completed_item_get_type(const struct ec_completed_item *item)
424 const struct ec_node *
425 ec_completed_item_get_node(const struct ec_completed_item *item)
430 const struct ec_completed_group *
431 ec_completed_item_get_grp(const struct ec_completed_item *item)
437 ec_completed_item_free(struct ec_completed_item *item)
443 ec_free(item->start);
444 ec_free(item->completion);
445 ec_free(item->display);
446 ec_keyval_free(item->attrs);
450 int ec_completed_add_item(struct ec_completed *completed,
451 const struct ec_node *node,
452 struct ec_completed_item **p_item,
453 enum ec_completed_type type,
454 const char *start, const char *full)
456 struct ec_completed_item *item = NULL;
459 item = ec_completed_item(node, type, start, full);
463 ret = ec_completed_item_add(completed, item);
473 ec_completed_item_free(item);
478 /* default completion function: return a no-match element */
480 ec_node_default_complete(const struct ec_node *gen_node, // XXX rename in nomatch
481 struct ec_completed *completed,
482 const struct ec_strvec *strvec)
486 if (ec_strvec_len(strvec) != 1)
489 ret = ec_completed_add_item(completed, gen_node, NULL,
490 EC_COMP_UNKNOWN, NULL, NULL);
497 static void ec_completed_group_free(struct ec_completed_group *grp)
499 struct ec_completed_item *item;
504 while (!TAILQ_EMPTY(&grp->items)) {
505 item = TAILQ_FIRST(&grp->items);
506 TAILQ_REMOVE(&grp->items, item, next);
507 ec_completed_item_free(item);
509 ec_parsed_free(ec_parsed_get_root(grp->state));
510 ec_keyval_free(grp->attrs);
514 void ec_completed_free(struct ec_completed *completed)
516 struct ec_completed_group *grp;
518 if (completed == NULL)
521 while (!TAILQ_EMPTY(&completed->groups)) {
522 grp = TAILQ_FIRST(&completed->groups);
523 TAILQ_REMOVE(&completed->groups, grp, next);
524 ec_completed_group_free(grp);
526 ec_keyval_free(completed->attrs);
530 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
532 struct ec_completed_group *grp;
533 struct ec_completed_item *item;
535 if (completed == NULL || completed->count == 0) {
536 fprintf(out, "no completion\n");
540 fprintf(out, "completion: count=%u match=%u\n",
541 completed->count, completed->count_match);
543 TAILQ_FOREACH(grp, &completed->groups, next) {
544 fprintf(out, "node=%p, node_type=%s\n",
545 grp->node, grp->node->type->name);
546 TAILQ_FOREACH(item, &grp->items, next) {
549 switch (item->type) {
550 case EC_COMP_UNKNOWN: typestr = "unknown"; break;
551 case EC_COMP_FULL: typestr = "full"; break;
552 case EC_COMP_PARTIAL: typestr = "partial"; break;
553 default: typestr = "unknown"; break;
556 fprintf(out, " type=%s str=<%s> comp=<%s> disp=<%s>\n",
557 typestr, item->full, item->completion,
563 int ec_completed_merge(struct ec_completed *to,
564 struct ec_completed *from)
566 struct ec_completed_group *grp;
568 while (!TAILQ_EMPTY(&from->groups)) {
569 grp = TAILQ_FIRST(&from->groups);
570 TAILQ_REMOVE(&from->groups, grp, next);
571 TAILQ_INSERT_TAIL(&to->groups, grp, next);
573 to->count += from->count;
574 to->count_match += from->count_match;
576 ec_completed_free(from);
580 unsigned int ec_completed_count(
581 const struct ec_completed *completed,
582 enum ec_completed_type type)
584 unsigned int count = 0;
586 if (completed == NULL)
589 if (type & EC_COMP_FULL)
590 count += completed->count_match;
591 if (type & EC_COMP_UNKNOWN)
592 count += (completed->count - completed->count_match); //XXX
597 struct ec_completed_iter *
598 ec_completed_iter(struct ec_completed *completed,
599 enum ec_completed_type type)
601 struct ec_completed_iter *iter;
603 iter = ec_calloc(1, sizeof(*iter));
607 iter->completed = completed;
609 iter->cur_node = NULL;
610 iter->cur_match = NULL;
615 struct ec_completed_item *ec_completed_iter_next(
616 struct ec_completed_iter *iter)
618 struct ec_completed *completed = iter->completed;
619 struct ec_completed_group *cur_node;
620 struct ec_completed_item *cur_match;
622 if (completed == NULL)
625 cur_node = iter->cur_node;
626 cur_match = iter->cur_match;
629 if (cur_node == NULL) {
630 TAILQ_FOREACH(cur_node, &completed->groups, next) {
631 TAILQ_FOREACH(cur_match, &cur_node->items, next) {
632 if (cur_match != NULL &&
633 cur_match->type & iter->type)
639 cur_match = TAILQ_NEXT(cur_match, next);
640 if (cur_match != NULL &&
641 cur_match->type & iter->type)
643 cur_node = TAILQ_NEXT(cur_node, next);
644 while (cur_node != NULL) {
645 cur_match = TAILQ_FIRST(&cur_node->items);
646 if (cur_match != NULL &&
647 cur_match->type & iter->type)
649 cur_node = TAILQ_NEXT(cur_node, next);
655 iter->cur_node = cur_node;
656 iter->cur_match = cur_match;
658 return iter->cur_match;
661 void ec_completed_iter_free(struct ec_completed_iter *iter)