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