dynamic node
[protos/libecoli.git] / lib / ecoli_completed.c
1 /*
2  * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
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.
15  *
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.
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <errno.h>
33
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>
42
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;
53 };
54
55 struct ec_completed *ec_completed(struct ec_parsed *state)
56 {
57         struct ec_completed *completed = NULL;
58
59         completed = ec_calloc(1, sizeof(*completed));
60         if (completed == NULL)
61                 goto fail;
62
63         completed->attrs = ec_keyval();
64         if (completed->attrs == NULL)
65                 goto fail;
66
67         TAILQ_INIT(&completed->groups);
68
69         completed->cur_state = state;
70
71         return completed;
72
73  fail:
74         if (completed != NULL)
75                 ec_keyval_free(completed->attrs);
76         ec_free(completed);
77
78         return NULL;
79 }
80
81 struct ec_parsed *ec_completed_get_state(struct ec_completed *completed)
82 {
83         return completed->cur_state;
84 }
85
86 int
87 ec_node_complete_child(struct ec_node *node, struct ec_completed *completed,
88                         const struct ec_strvec *strvec)
89 {
90         struct ec_parsed *child_state, *cur_state;
91         struct ec_completed_group *cur_group;
92         int ret;
93
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);
98                         if (ret < 0)
99                                 return ret;
100                 }
101         }
102         node->flags |= EC_NODE_F_BUILT;
103
104         if (node->type->complete == NULL)
105                 return -ENOTSUP;
106
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)
111                 return -ENOMEM;
112
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;
118
119         /* fill the completed struct with items */
120         ret = node->type->complete(node, completed, strvec);
121
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));
126         }
127         ec_parsed_free(child_state);
128         completed->cur_state = cur_state;
129         completed->cur_group = cur_group;
130
131         if (ret < 0)
132                 return ret;
133
134 #if 0 // XXX dump
135         printf("----------------------------------------------------------\n");
136         ec_node_dump(stdout, node);
137         ec_strvec_dump(stdout, strvec);
138         ec_completed_dump(stdout, completed);
139 #endif
140
141         return 0;
142 }
143
144 struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
145         const struct ec_strvec *strvec)
146 {
147         struct ec_completed *completed = NULL;
148         int ret;
149
150         completed = ec_completed(NULL);
151         if (completed == NULL)
152                 goto fail;
153
154         ret = ec_node_complete_child(node, completed, strvec);
155         if (ret < 0)
156                 goto fail;
157
158         return completed;
159
160 fail:
161         ec_completed_free(completed);
162         return NULL;
163 }
164
165 struct ec_completed *ec_node_complete(struct ec_node *node,
166         const char *str)
167 {
168         struct ec_strvec *strvec = NULL;
169         struct ec_completed *completed;
170
171         errno = ENOMEM;
172         strvec = ec_strvec();
173         if (strvec == NULL)
174                 goto fail;
175
176         if (ec_strvec_add(strvec, str) < 0)
177                 goto fail;
178
179         completed = ec_node_complete_strvec(node, strvec);
180         if (completed == NULL)
181                 goto fail;
182
183         ec_strvec_free(strvec);
184         return completed;
185
186  fail:
187         ec_strvec_free(strvec);
188         return NULL;
189 }
190
191 static struct ec_completed_group *
192 ec_completed_group(const struct ec_node *node, struct ec_parsed *parsed)
193 {
194         struct ec_completed_group *grp = NULL;
195
196         grp = ec_calloc(1, sizeof(*grp));
197         if (grp == NULL)
198                 return NULL;
199
200         grp->attrs = ec_keyval();
201         if (grp->attrs == NULL)
202                 goto fail;
203
204         grp->state = ec_parsed_dup(parsed);
205         if (grp->state == NULL)
206                 goto fail;
207
208         grp->node = node;
209         TAILQ_INIT(&grp->items);
210
211         return grp;
212
213 fail:
214         if (grp != NULL) {
215                 ec_parsed_free(grp->state);
216                 ec_keyval_free(grp->attrs);
217         }
218         ec_free(grp);
219         return NULL;
220 }
221
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)
225 {
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;
230
231         if (type == EC_COMP_UNKNOWN && full != NULL) {
232                 errno = EINVAL;
233                 return NULL;
234         }
235         if (type != EC_COMP_UNKNOWN && full == NULL) {
236                 errno = EINVAL;
237                 return NULL;
238         }
239
240         item = ec_calloc(1, sizeof(*item));
241         if (item == NULL)
242                 goto fail;
243
244         attrs = ec_keyval();
245         if (attrs == NULL)
246                 goto fail;
247
248         if (start != NULL) {
249                 start_cp = ec_strdup(start);
250                 if (start_cp == NULL)
251                         goto fail;
252
253                 if (ec_str_startswith(full, start)) {
254                         comp_cp = ec_strdup(&full[strlen(start)]);
255                         if (comp_cp == NULL)
256                                 goto fail;
257                 }
258         }
259         if (full != NULL) {
260                 full_cp = ec_strdup(full);
261                 if (full_cp == NULL)
262                         goto fail;
263                 display_cp = ec_strdup(full);
264                 if (display_cp == NULL)
265                         goto fail;
266         }
267
268         item->node = node;
269         item->type = type;
270         item->start = start_cp;
271         item->full = full_cp;
272         item->completion = comp_cp;
273         item->display = display_cp;
274         item->attrs = attrs;
275
276         return item;
277
278 fail:
279         ec_keyval_free(item->attrs);
280         ec_free(comp_cp);
281         ec_free(start_cp);
282         ec_free(full_cp);
283         ec_free(display_cp);
284         ec_free(item);
285
286         return NULL;
287 }
288
289 int ec_completed_item_set_display(struct ec_completed_item *item,
290                                 const char *display)
291 {
292         char *display_copy = NULL;
293         int ret = 0;
294
295         if (item == NULL || display == NULL ||
296                         item->type == EC_COMP_UNKNOWN)
297                 return -EINVAL;
298
299         display_copy = ec_strdup(display);
300         if (display_copy == NULL)
301                 goto fail;
302
303         ec_free(item->display);
304         item->display = display_copy;
305
306         return 0;
307
308 fail:
309         ec_free(display_copy);
310         return ret;
311 }
312
313 int
314 ec_completed_item_set_completion(struct ec_completed_item *item,
315                                 const char *completion)
316 {
317         char *completion_copy = NULL;
318         int ret = 0;
319
320         if (item == NULL || completion == NULL ||
321                         item->type == EC_COMP_UNKNOWN)
322                 return -EINVAL;
323
324         ret = -ENOMEM;
325         completion_copy = ec_strdup(completion);
326         if (completion_copy == NULL)
327                 goto fail;
328
329         ec_free(item->completion);
330         item->completion = completion_copy;
331
332         return 0;
333
334 fail:
335         ec_free(completion_copy);
336         return ret;
337 }
338
339 int
340 ec_completed_item_set_str(struct ec_completed_item *item,
341                         const char *str)
342 {
343         char *str_copy = NULL;
344         int ret = 0;
345
346         if (item == NULL || str == NULL ||
347                         item->type == EC_COMP_UNKNOWN)
348                 return -EINVAL;
349
350         ret = -ENOMEM;
351         str_copy = ec_strdup(str);
352         if (str_copy == NULL)
353                 goto fail;
354
355         ec_free(item->full);
356         item->full = str_copy;
357
358         return 0;
359
360 fail:
361         ec_free(str_copy);
362         return ret;
363 }
364
365 static int
366 ec_completed_item_add(struct ec_completed *completed,
367                 struct ec_completed_item *item)
368 {
369         if (completed == NULL || item == NULL || item->node == NULL)
370                 return -EINVAL;
371
372         switch (item->type) {
373         case EC_COMP_UNKNOWN:
374                 break;
375         case EC_COMP_FULL:
376         case EC_COMP_PARTIAL:
377                 completed->count_match++; //XXX
378                 break;
379         default:
380                 return -EINVAL;
381         }
382
383         if (completed->cur_group == NULL) {
384                 struct ec_completed_group *grp;
385
386                 grp = ec_completed_group(item->node, completed->cur_state);
387                 if (grp == NULL)
388                         return -ENOMEM;
389                 TAILQ_INSERT_TAIL(&completed->groups, grp, next);
390                 completed->cur_group = grp;
391         }
392
393         completed->count++;
394         TAILQ_INSERT_TAIL(&completed->cur_group->items, item, next);
395         item->grp = completed->cur_group;
396
397         return 0;
398 }
399
400 const char *
401 ec_completed_item_get_str(const struct ec_completed_item *item)
402 {
403         return item->full;
404 }
405
406 const char *
407 ec_completed_item_get_display(const struct ec_completed_item *item)
408 {
409         return item->display;
410 }
411
412 const char *
413 ec_completed_item_get_completion(const struct ec_completed_item *item)
414 {
415         return item->completion;
416 }
417
418 enum ec_completed_type
419 ec_completed_item_get_type(const struct ec_completed_item *item)
420 {
421         return item->type;
422 }
423
424 const struct ec_node *
425 ec_completed_item_get_node(const struct ec_completed_item *item)
426 {
427         return item->node;
428 }
429
430 const struct ec_completed_group *
431 ec_completed_item_get_grp(const struct ec_completed_item *item)
432 {
433         return item->grp;
434 }
435
436 static void
437 ec_completed_item_free(struct ec_completed_item *item)
438 {
439         if (item == NULL)
440                 return;
441
442         ec_free(item->full);
443         ec_free(item->start);
444         ec_free(item->completion);
445         ec_free(item->display);
446         ec_keyval_free(item->attrs);
447         ec_free(item);
448 }
449
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)
455 {
456         struct ec_completed_item *item = NULL;
457         int ret;
458
459         item = ec_completed_item(node, type, start, full);
460         if (item == NULL)
461                 return -1;
462
463         ret = ec_completed_item_add(completed, item);
464         if (ret < 0)
465                 goto fail;
466
467         if (p_item != NULL)
468                 *p_item = item;
469
470         return 0;
471
472 fail:
473         ec_completed_item_free(item);
474
475         return -1;
476 }
477
478 /* default completion function: return a no-match element */
479 int
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)
483 {
484         int ret;
485
486         if (ec_strvec_len(strvec) != 1)
487                 return 0;
488
489         ret = ec_completed_add_item(completed, gen_node, NULL,
490                                 EC_COMP_UNKNOWN, NULL, NULL);
491         if (ret < 0)
492                 return ret;
493
494         return 0;
495 }
496
497 static void ec_completed_group_free(struct ec_completed_group *grp)
498 {
499         struct ec_completed_item *item;
500
501         if (grp == NULL)
502                 return;
503
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);
508         }
509         ec_parsed_free(ec_parsed_get_root(grp->state));
510         ec_keyval_free(grp->attrs);
511         ec_free(grp);
512 }
513
514 void ec_completed_free(struct ec_completed *completed)
515 {
516         struct ec_completed_group *grp;
517
518         if (completed == NULL)
519                 return;
520
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);
525         }
526         ec_keyval_free(completed->attrs);
527         ec_free(completed);
528 }
529
530 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
531 {
532         struct ec_completed_group *grp;
533         struct ec_completed_item *item;
534
535         if (completed == NULL || completed->count == 0) {
536                 fprintf(out, "no completion\n");
537                 return;
538         }
539
540         fprintf(out, "completion: count=%u match=%u\n",
541                 completed->count, completed->count_match);
542
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) {
547                         const char *typestr;
548
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;
554                         }
555
556                         fprintf(out, "  type=%s str=<%s> comp=<%s> disp=<%s>\n",
557                                 typestr, item->full, item->completion,
558                                 item->display);
559                 }
560         }
561 }
562
563 int ec_completed_merge(struct ec_completed *to,
564                 struct ec_completed *from)
565 {
566         struct ec_completed_group *grp;
567
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);
572         }
573         to->count += from->count;
574         to->count_match += from->count_match;
575
576         ec_completed_free(from);
577         return 0;
578 }
579
580 unsigned int ec_completed_count(
581         const struct ec_completed *completed,
582         enum ec_completed_type type)
583 {
584         unsigned int count = 0;
585
586         if (completed == NULL)
587                 return count;
588
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
593
594         return count;
595 }
596
597 struct ec_completed_iter *
598 ec_completed_iter(struct ec_completed *completed,
599         enum ec_completed_type type)
600 {
601         struct ec_completed_iter *iter;
602
603         iter = ec_calloc(1, sizeof(*iter));
604         if (iter == NULL)
605                 return NULL;
606
607         iter->completed = completed;
608         iter->type = type;
609         iter->cur_node = NULL;
610         iter->cur_match = NULL;
611
612         return iter;
613 }
614
615 struct ec_completed_item *ec_completed_iter_next(
616         struct ec_completed_iter *iter)
617 {
618         struct ec_completed *completed = iter->completed;
619         struct ec_completed_group *cur_node;
620         struct ec_completed_item *cur_match;
621
622         if (completed == NULL)
623                 return NULL;
624
625         cur_node = iter->cur_node;
626         cur_match = iter->cur_match;
627
628         /* first call */
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)
634                                         goto found;
635                         }
636                 }
637                 return NULL;
638         } else {
639                 cur_match = TAILQ_NEXT(cur_match, next);
640                 if (cur_match != NULL &&
641                                 cur_match->type & iter->type)
642                         goto found;
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)
648                                 goto found;
649                         cur_node = TAILQ_NEXT(cur_node, next);
650                 }
651                 return NULL;
652         }
653
654 found:
655         iter->cur_node = cur_node;
656         iter->cur_match = cur_match;
657
658         return iter->cur_match;
659 }
660
661 void ec_completed_iter_free(struct ec_completed_iter *iter)
662 {
663         ec_free(iter);
664 }