api documentation
[protos/libecoli.git] / src / ecoli_complete.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <errno.h>
10
11 #include <ecoli_malloc.h>
12 #include <ecoli_string.h>
13 #include <ecoli_strvec.h>
14 #include <ecoli_dict.h>
15 #include <ecoli_log.h>
16 #include <ecoli_test.h>
17 #include <ecoli_node.h>
18 #include <ecoli_parse.h>
19 #include <ecoli_node_sh_lex.h>
20 #include <ecoli_node_str.h>
21 #include <ecoli_node_or.h>
22 #include <ecoli_complete.h>
23
24 EC_LOG_TYPE_REGISTER(comp);
25
26 struct ec_comp_item {
27         TAILQ_ENTRY(ec_comp_item) next;
28         enum ec_comp_type type;
29         struct ec_comp_group *grp;
30         char *start;      /* the initial token */
31         char *full;       /* the full token after completion */
32         char *completion; /* chars that are added, NULL if not applicable */
33         char *display;    /* what should be displayed by help/completers */
34         struct ec_dict *attrs;
35 };
36
37 TAILQ_HEAD(ec_comp_item_list, ec_comp_item);
38
39 struct ec_comp_group {
40         /* XXX counts ? */
41         TAILQ_ENTRY(ec_comp_group) next;
42         const struct ec_node *node;
43         struct ec_comp_item_list items;
44         struct ec_parse *state;
45         struct ec_dict *attrs;
46 };
47
48 TAILQ_HEAD(ec_comp_group_list, ec_comp_group);
49
50 struct ec_comp {
51         unsigned count;
52         unsigned count_full;
53         unsigned count_partial;
54         unsigned count_unknown;
55         struct ec_parse *cur_state;
56         struct ec_comp_group *cur_group;
57         struct ec_comp_group_list groups;
58         struct ec_dict *attrs;
59 };
60
61 struct ec_comp *ec_comp(struct ec_parse *state)
62 {
63         struct ec_comp *comp = NULL;
64
65         comp = ec_calloc(1, sizeof(*comp));
66         if (comp == NULL)
67                 goto fail;
68
69         comp->attrs = ec_dict();
70         if (comp->attrs == NULL)
71                 goto fail;
72
73         TAILQ_INIT(&comp->groups);
74
75         comp->cur_state = state;
76
77         return comp;
78
79  fail:
80         if (comp != NULL)
81                 ec_dict_free(comp->attrs);
82         ec_free(comp);
83
84         return NULL;
85 }
86
87 struct ec_parse *ec_comp_get_state(const struct ec_comp *comp)
88 {
89         return comp->cur_state;
90 }
91
92 struct ec_comp_group *ec_comp_get_group(const struct ec_comp *comp)
93 {
94         return comp->cur_group;
95 }
96
97 struct ec_dict *ec_comp_get_attrs(const struct ec_comp *comp)
98 {
99         return comp->attrs;
100 }
101
102 int
103 ec_node_complete_child(const struct ec_node *node,
104                 struct ec_comp *comp,
105                 const struct ec_strvec *strvec)
106 {
107         struct ec_parse *child_state, *cur_state;
108         struct ec_comp_group *cur_group;
109         int ret;
110
111         if (ec_node_type(node)->complete == NULL) {
112                 errno = ENOTSUP;
113                 return -1;
114         }
115
116         /* save previous parse state, prepare child state */
117         cur_state = comp->cur_state;
118         child_state = ec_parse(node);
119         if (child_state == NULL)
120                 return -1;
121
122         if (cur_state != NULL)
123                 ec_parse_link_child(cur_state, child_state);
124         comp->cur_state = child_state;
125         cur_group = comp->cur_group;
126         comp->cur_group = NULL;
127
128         /* fill the comp struct with items */
129         ret = ec_node_type(node)->complete(node, comp, strvec);
130
131         /* restore parent parse state */
132         if (cur_state != NULL) {
133                 ec_parse_unlink_child(cur_state, child_state);
134                 assert(!ec_parse_has_child(child_state));
135         }
136         ec_parse_free(child_state);
137         comp->cur_state = cur_state;
138         comp->cur_group = cur_group;
139
140         if (ret < 0)
141                 return -1;
142
143         return 0;
144 }
145
146 struct ec_comp *ec_node_complete_strvec(const struct ec_node *node,
147         const struct ec_strvec *strvec)
148 {
149         struct ec_comp *comp = NULL;
150         int ret;
151
152         comp = ec_comp(NULL);
153         if (comp == NULL)
154                 goto fail;
155
156         ret = ec_node_complete_child(node, comp, strvec);
157         if (ret < 0)
158                 goto fail;
159
160         return comp;
161
162 fail:
163         ec_comp_free(comp);
164         return NULL;
165 }
166
167 struct ec_comp *ec_node_complete(const struct ec_node *node,
168         const char *str)
169 {
170         struct ec_strvec *strvec = NULL;
171         struct ec_comp *comp;
172
173         errno = ENOMEM;
174         strvec = ec_strvec();
175         if (strvec == NULL)
176                 goto fail;
177
178         if (ec_strvec_add(strvec, str) < 0)
179                 goto fail;
180
181         comp = ec_node_complete_strvec(node, strvec);
182         if (comp == NULL)
183                 goto fail;
184
185         ec_strvec_free(strvec);
186         return comp;
187
188  fail:
189         ec_strvec_free(strvec);
190         return NULL;
191 }
192
193 static struct ec_comp_group *
194 ec_comp_group(const struct ec_node *node, struct ec_parse *parse)
195 {
196         struct ec_comp_group *grp = NULL;
197
198         grp = ec_calloc(1, sizeof(*grp));
199         if (grp == NULL)
200                 return NULL;
201
202         grp->attrs = ec_dict();
203         if (grp->attrs == NULL)
204                 goto fail;
205
206         grp->state = ec_parse_dup(parse);
207         if (grp->state == NULL)
208                 goto fail;
209
210         grp->node = node;
211         TAILQ_INIT(&grp->items);
212
213         return grp;
214
215 fail:
216         if (grp != NULL) {
217                 ec_parse_free(grp->state);
218                 ec_dict_free(grp->attrs);
219         }
220         ec_free(grp);
221         return NULL;
222 }
223
224 static struct ec_comp_item *
225 ec_comp_item(enum ec_comp_type type,
226         const char *start, const char *full)
227 {
228         struct ec_comp_item *item = NULL;
229         struct ec_dict *attrs = NULL;
230         char *comp_cp = NULL, *start_cp = NULL;
231         char *full_cp = NULL, *display_cp = NULL;
232
233         if (type == EC_COMP_UNKNOWN && full != NULL) {
234                 errno = EINVAL;
235                 return NULL;
236         }
237         if (type != EC_COMP_UNKNOWN && full == NULL) {
238                 errno = EINVAL;
239                 return NULL;
240         }
241
242         item = ec_calloc(1, sizeof(*item));
243         if (item == NULL)
244                 goto fail;
245
246         attrs = ec_dict();
247         if (attrs == NULL)
248                 goto fail;
249
250         if (start != NULL) {
251                 start_cp = ec_strdup(start);
252                 if (start_cp == NULL)
253                         goto fail;
254
255                 if (ec_str_startswith(full, start)) {
256                         comp_cp = ec_strdup(&full[strlen(start)]);
257                         if (comp_cp == NULL)
258                                 goto fail;
259                 }
260         }
261         if (full != NULL) {
262                 full_cp = ec_strdup(full);
263                 if (full_cp == NULL)
264                         goto fail;
265                 display_cp = ec_strdup(full);
266                 if (display_cp == NULL)
267                         goto fail;
268         }
269
270         item->type = type;
271         item->start = start_cp;
272         item->full = full_cp;
273         item->completion = comp_cp;
274         item->display = display_cp;
275         item->attrs = attrs;
276
277         return item;
278
279 fail:
280         ec_dict_free(attrs);
281         ec_free(comp_cp);
282         ec_free(start_cp);
283         ec_free(full_cp);
284         ec_free(display_cp);
285         ec_free(item);
286
287         return NULL;
288 }
289
290 int ec_comp_item_set_display(struct ec_comp_item *item,
291                                 const char *display)
292 {
293         char *display_copy = NULL;
294
295         if (item == NULL || display == NULL ||
296                         item->type == EC_COMP_UNKNOWN) {
297                 errno = EINVAL;
298                 return -1;
299         }
300
301         display_copy = ec_strdup(display);
302         if (display_copy == NULL)
303                 goto fail;
304
305         ec_free(item->display);
306         item->display = display_copy;
307
308         return 0;
309
310 fail:
311         ec_free(display_copy);
312         return -1;
313 }
314
315 int
316 ec_comp_item_set_completion(struct ec_comp_item *item,
317                                 const char *completion)
318 {
319         char *completion_copy = NULL;
320
321         if (item == NULL || completion == NULL ||
322                         item->type == EC_COMP_UNKNOWN) {
323                 errno = EINVAL;
324                 return -1;
325         }
326
327         completion_copy = ec_strdup(completion);
328         if (completion_copy == NULL)
329                 goto fail;
330
331         ec_free(item->completion);
332         item->completion = completion_copy;
333
334         return 0;
335
336 fail:
337         ec_free(completion_copy);
338         return -1;
339 }
340
341 int
342 ec_comp_item_set_str(struct ec_comp_item *item,
343                         const char *str)
344 {
345         char *str_copy = NULL;
346
347         if (item == NULL || str == NULL ||
348                         item->type == EC_COMP_UNKNOWN) {
349                 errno = EINVAL;
350                 return -1;
351         }
352
353         str_copy = ec_strdup(str);
354         if (str_copy == NULL)
355                 goto fail;
356
357         ec_free(item->full);
358         item->full = str_copy;
359
360         return 0;
361
362 fail:
363         ec_free(str_copy);
364         return -1;
365 }
366
367 static int
368 ec_comp_item_add(struct ec_comp *comp, const struct ec_node *node,
369                 struct ec_comp_item *item)
370 {
371         if (comp == NULL || item == NULL) {
372                 errno = EINVAL;
373                 return -1;
374         }
375
376         switch (item->type) {
377         case EC_COMP_UNKNOWN:
378                 comp->count_unknown++;
379                 break;
380         case EC_COMP_FULL:
381                 comp->count_full++;
382                 break;
383         case EC_COMP_PARTIAL:
384                 comp->count_partial++;
385                 break;
386         default:
387                 errno = EINVAL;
388                 return -1;
389         }
390
391         if (comp->cur_group == NULL) {
392                 struct ec_comp_group *grp;
393
394                 grp = ec_comp_group(node, comp->cur_state);
395                 if (grp == NULL)
396                         return -1;
397                 TAILQ_INSERT_TAIL(&comp->groups, grp, next);
398                 comp->cur_group = grp;
399         }
400
401         comp->count++;
402         TAILQ_INSERT_TAIL(&comp->cur_group->items, item, next);
403         item->grp = comp->cur_group;
404
405         return 0;
406 }
407
408 const char *
409 ec_comp_item_get_str(const struct ec_comp_item *item)
410 {
411         return item->full;
412 }
413
414 const char *
415 ec_comp_item_get_display(const struct ec_comp_item *item)
416 {
417         return item->display;
418 }
419
420 const char *
421 ec_comp_item_get_completion(const struct ec_comp_item *item)
422 {
423         return item->completion;
424 }
425
426 enum ec_comp_type
427 ec_comp_item_get_type(const struct ec_comp_item *item)
428 {
429         return item->type;
430 }
431
432 const struct ec_comp_group *
433 ec_comp_item_get_grp(const struct ec_comp_item *item)
434 {
435         return item->grp;
436 }
437
438 const struct ec_node *
439 ec_comp_item_get_node(const struct ec_comp_item *item)
440 {
441         return ec_comp_item_get_grp(item)->node;
442 }
443
444 static void
445 ec_comp_item_free(struct ec_comp_item *item)
446 {
447         if (item == NULL)
448                 return;
449
450         ec_free(item->full);
451         ec_free(item->start);
452         ec_free(item->completion);
453         ec_free(item->display);
454         ec_dict_free(item->attrs);
455         ec_free(item);
456 }
457
458 int ec_comp_add_item(struct ec_comp *comp,
459                         const struct ec_node *node,
460                         struct ec_comp_item **p_item,
461                         enum ec_comp_type type,
462                         const char *start, const char *full)
463 {
464         struct ec_comp_item *item = NULL;
465         int ret;
466
467         item = ec_comp_item(type, start, full);
468         if (item == NULL)
469                 return -1;
470
471         ret = ec_comp_item_add(comp, node, item);
472         if (ret < 0)
473                 goto fail;
474
475         if (p_item != NULL)
476                 *p_item = item;
477
478         return 0;
479
480 fail:
481         ec_comp_item_free(item);
482
483         return -1;
484 }
485
486 /* return a completion item of type "unknown" */
487 int
488 ec_node_complete_unknown(const struct ec_node *gen_node,
489                         struct ec_comp *comp,
490                         const struct ec_strvec *strvec)
491 {
492         int ret;
493
494         if (ec_strvec_len(strvec) != 1)
495                 return 0;
496
497         ret = ec_comp_add_item(comp, gen_node, NULL,
498                                 EC_COMP_UNKNOWN, NULL, NULL);
499         if (ret < 0)
500                 return ret;
501
502         return 0;
503 }
504
505 static void ec_comp_group_free(struct ec_comp_group *grp)
506 {
507         struct ec_comp_item *item;
508
509         if (grp == NULL)
510                 return;
511
512         while (!TAILQ_EMPTY(&grp->items)) {
513                 item = TAILQ_FIRST(&grp->items);
514                 TAILQ_REMOVE(&grp->items, item, next);
515                 ec_comp_item_free(item);
516         }
517         ec_parse_free(ec_parse_get_root(grp->state));
518         ec_dict_free(grp->attrs);
519         ec_free(grp);
520 }
521
522 const struct ec_node *
523 ec_comp_group_get_node(const struct ec_comp_group *grp)
524 {
525         return grp->node;
526 }
527
528 const struct ec_parse *
529 ec_comp_group_get_state(const struct ec_comp_group *grp)
530 {
531         return grp->state;
532 }
533
534 const struct ec_dict *
535 ec_comp_group_get_attrs(const struct ec_comp_group *grp)
536 {
537         return grp->attrs;
538 }
539
540 void ec_comp_free(struct ec_comp *comp)
541 {
542         struct ec_comp_group *grp;
543
544         if (comp == NULL)
545                 return;
546
547         while (!TAILQ_EMPTY(&comp->groups)) {
548                 grp = TAILQ_FIRST(&comp->groups);
549                 TAILQ_REMOVE(&comp->groups, grp, next);
550                 ec_comp_group_free(grp);
551         }
552         ec_dict_free(comp->attrs);
553         ec_free(comp);
554 }
555
556 void ec_comp_dump(FILE *out, const struct ec_comp *comp)
557 {
558         struct ec_comp_group *grp;
559         struct ec_comp_item *item;
560
561         if (comp == NULL || comp->count == 0) {
562                 fprintf(out, "no completion\n");
563                 return;
564         }
565
566         fprintf(out, "completion: count=%u full=%u partial=%u unknown=%u\n",
567                 comp->count, comp->count_full,
568                 comp->count_partial,  comp->count_unknown);
569
570         TAILQ_FOREACH(grp, &comp->groups, next) {
571                 fprintf(out, "node=%p, node_type=%s\n",
572                         grp->node, ec_node_type(grp->node)->name);
573                 TAILQ_FOREACH(item, &grp->items, next) {
574                         const char *typestr;
575
576                         switch (item->type) {
577                         case EC_COMP_UNKNOWN: typestr = "unknown"; break;
578                         case EC_COMP_FULL: typestr = "full"; break;
579                         case EC_COMP_PARTIAL: typestr = "partial"; break;
580                         default: typestr = "unknown"; break;
581                         }
582
583                         fprintf(out, "  type=%s str=<%s> comp=<%s> disp=<%s>\n",
584                                 typestr, item->full, item->completion,
585                                 item->display);
586                 }
587         }
588 }
589
590 int ec_comp_merge(struct ec_comp *to,
591                 struct ec_comp *from)
592 {
593         struct ec_comp_group *grp;
594
595         while (!TAILQ_EMPTY(&from->groups)) {
596                 grp = TAILQ_FIRST(&from->groups);
597                 TAILQ_REMOVE(&from->groups, grp, next);
598                 TAILQ_INSERT_TAIL(&to->groups, grp, next);
599         }
600         to->count += from->count;
601         to->count_full += from->count_full;
602         to->count_partial += from->count_partial;
603         to->count_unknown += from->count_unknown;
604
605         ec_comp_free(from);
606         return 0;
607 }
608
609 unsigned int ec_comp_count(
610         const struct ec_comp *comp,
611         enum ec_comp_type type)
612 {
613         unsigned int count = 0;
614
615         if (comp == NULL)
616                 return count;
617
618         if (type & EC_COMP_FULL)
619                 count += comp->count_full;
620         if (type & EC_COMP_PARTIAL)
621                 count += comp->count_partial;
622         if (type & EC_COMP_UNKNOWN)
623                 count += comp->count_unknown;
624
625         return count;
626 }
627
628 struct ec_comp_iter *
629 ec_comp_iter(const struct ec_comp *comp,
630         enum ec_comp_type type)
631 {
632         struct ec_comp_iter *iter;
633
634         iter = ec_calloc(1, sizeof(*iter));
635         if (iter == NULL)
636                 return NULL;
637
638         iter->comp = comp;
639         iter->type = type;
640         iter->cur_node = NULL;
641         iter->cur_match = NULL;
642
643         return iter;
644 }
645
646 struct ec_comp_item *ec_comp_iter_next(
647         struct ec_comp_iter *iter)
648 {
649         const struct ec_comp *comp;
650         struct ec_comp_group *cur_node;
651         struct ec_comp_item *cur_match;
652
653         if (iter == NULL)
654                 return NULL;
655         comp = iter->comp;
656         if (comp == NULL)
657                 return NULL;
658
659         cur_node = iter->cur_node;
660         cur_match = iter->cur_match;
661
662         /* first call */
663         if (cur_node == NULL) {
664                 TAILQ_FOREACH(cur_node, &comp->groups, next) {
665                         TAILQ_FOREACH(cur_match, &cur_node->items, next) {
666                                 if (cur_match != NULL &&
667                                                 cur_match->type & iter->type)
668                                         goto found;
669                         }
670                 }
671                 return NULL;
672         } else {
673                 cur_match = TAILQ_NEXT(cur_match, next);
674                 if (cur_match != NULL &&
675                                 cur_match->type & iter->type)
676                         goto found;
677                 cur_node = TAILQ_NEXT(cur_node, next);
678                 while (cur_node != NULL) {
679                         cur_match = TAILQ_FIRST(&cur_node->items);
680                         if (cur_match != NULL &&
681                                         cur_match->type & iter->type)
682                                 goto found;
683                         cur_node = TAILQ_NEXT(cur_node, next);
684                 }
685                 return NULL;
686         }
687
688 found:
689         iter->cur_node = cur_node;
690         iter->cur_match = cur_match;
691
692         return iter->cur_match;
693 }
694
695 void ec_comp_iter_free(struct ec_comp_iter *iter)
696 {
697         ec_free(iter);
698 }
699
700 /* LCOV_EXCL_START */
701 static int ec_comp_testcase(void)
702 {
703         struct ec_node *node = NULL;
704         struct ec_comp *c = NULL;
705         struct ec_comp_iter *iter = NULL;
706         struct ec_comp_item *item;
707         FILE *f = NULL;
708         char *buf = NULL;
709         size_t buflen = 0;
710         int testres = 0;
711
712         node = ec_node_sh_lex(EC_NO_ID,
713                         EC_NODE_OR(EC_NO_ID,
714                                 ec_node_str("id_x", "xx"),
715                                 ec_node_str("id_y", "yy")));
716         if (node == NULL)
717                 goto fail;
718
719         c = ec_node_complete(node, "xcdscds");
720         testres |= EC_TEST_CHECK(
721                 c != NULL && ec_comp_count(c, EC_COMP_ALL) == 0,
722                 "complete count should is not 0\n");
723         ec_comp_free(c);
724
725         c = ec_node_complete(node, "x");
726         testres |= EC_TEST_CHECK(
727                 c != NULL && ec_comp_count(c, EC_COMP_ALL) == 1,
728                 "complete count should is not 1\n");
729         ec_comp_free(c);
730
731         c = ec_node_complete(node, "");
732         testres |= EC_TEST_CHECK(
733                 c != NULL && ec_comp_count(c, EC_COMP_ALL) == 2,
734                 "complete count should is not 2\n");
735
736         f = open_memstream(&buf, &buflen);
737         if (f == NULL)
738                 goto fail;
739         ec_comp_dump(f, NULL);
740         fclose(f);
741         f = NULL;
742
743         testres |= EC_TEST_CHECK(
744                 strstr(buf, "no completion"), "bad dump\n");
745         free(buf);
746         buf = NULL;
747
748         f = open_memstream(&buf, &buflen);
749         if (f == NULL)
750                 goto fail;
751         ec_comp_dump(f, c);
752         fclose(f);
753         f = NULL;
754
755         testres |= EC_TEST_CHECK(
756                 strstr(buf, "comp=<xx>"), "bad dump\n");
757         testres |= EC_TEST_CHECK(
758                 strstr(buf, "comp=<yy>"), "bad dump\n");
759         free(buf);
760         buf = NULL;
761
762         iter = ec_comp_iter(c, EC_COMP_ALL);
763         item = ec_comp_iter_next(iter);
764         if (item == NULL)
765                 goto fail;
766
767         testres |= EC_TEST_CHECK(
768                 !strcmp(ec_comp_item_get_display(item), "xx"),
769                 "bad item display\n");
770         testres |= EC_TEST_CHECK(
771                 ec_comp_item_get_type(item) == EC_COMP_FULL,
772                 "bad item type\n");
773         testres |= EC_TEST_CHECK(
774                 !strcmp(ec_node_id(ec_comp_item_get_node(item)), "id_x"),
775                 "bad item node\n");
776
777         item = ec_comp_iter_next(iter);
778         if (item == NULL)
779                 goto fail;
780
781         testres |= EC_TEST_CHECK(
782                 !strcmp(ec_comp_item_get_display(item), "yy"),
783                 "bad item display\n");
784         testres |= EC_TEST_CHECK(
785                 ec_comp_item_get_type(item) == EC_COMP_FULL,
786                 "bad item type\n");
787         testres |= EC_TEST_CHECK(
788                 !strcmp(ec_node_id(ec_comp_item_get_node(item)), "id_y"),
789                 "bad item node\n");
790
791         item = ec_comp_iter_next(iter);
792         testres |= EC_TEST_CHECK(item == NULL, "should be the last item\n");
793
794         ec_comp_iter_free(iter);
795         ec_comp_free(c);
796         ec_node_free(node);
797
798         return testres;
799
800 fail:
801         ec_comp_iter_free(iter);
802         ec_comp_free(c);
803         ec_node_free(node);
804         if (f != NULL)
805                 fclose(f);
806         free(buf);
807
808         return -1;
809 }
810 /* LCOV_EXCL_STOP */
811
812 static struct ec_test ec_comp_test = {
813         .name = "comp",
814         .test = ec_comp_testcase,
815 };
816
817 EC_TEST_REGISTER(ec_comp_test);