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