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