save
[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_strvec.h>
36 #include <ecoli_keyval.h>
37 #include <ecoli_log.h>
38 #include <ecoli_node.h>
39 #include <ecoli_parsed.h>
40 #include <ecoli_completed.h>
41
42 struct ec_completed_item {
43         TAILQ_ENTRY(ec_completed_item) next;
44         enum ec_completed_type type;
45         const struct ec_node *node;
46         char *str;
47         char *display;
48         struct ec_keyval *attrs;
49
50         /* reverse order: [0] = last, [len-1] = root */
51         const struct ec_node **path;
52         size_t pathlen;
53 };
54
55 struct ec_completed *ec_completed(void)
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->nodes);
64
65         completed->attrs = ec_keyval();
66         if (completed->attrs == NULL)
67                 goto fail;
68
69         return completed;
70
71  fail:
72         if (completed != NULL)
73                 ec_keyval_free(completed->attrs);
74         ec_free(completed);
75
76         return NULL;
77 }
78
79 /* XXX on error, states are not freed ?
80  * they can be left in a bad state and should not be reused */
81 int
82 ec_node_complete_child(struct ec_node *node,
83                 struct ec_completed *completed,
84                 struct ec_parsed *parsed_state,
85                 const struct ec_strvec *strvec)
86 {
87         struct ec_parsed *child_state = NULL;
88         int ret;
89
90         /* build the node if required */
91         if (node->type->build != NULL) {
92                 if ((node->flags & EC_NODE_F_BUILT) == 0) {
93                         ret = node->type->build(node);
94                         if (ret < 0)
95                                 return ret;
96                 }
97         }
98         node->flags |= EC_NODE_F_BUILT;
99
100         if (node->type->complete == NULL)
101                 return -ENOTSUP;
102
103         child_state = ec_parsed();
104         if (child_state == NULL)
105                 return -ENOMEM;
106         child_state->node = node;
107         ec_parsed_add_child(parsed_state, child_state);
108
109         ret = node->type->complete(node, completed, child_state, strvec);
110         if (ret < 0)
111                 return ret;
112
113 #if 0 // XXX dump
114         printf("----------------------------------------------------------\n");
115         ec_node_dump(stdout, node);
116         ec_strvec_dump(stdout, strvec);
117         ec_completed_dump(stdout, completed);
118         ec_parsed_dump(stdout, parsed_state);
119 #endif
120
121         ec_parsed_del_child(parsed_state, child_state);
122         assert(TAILQ_EMPTY(&child_state->children));
123         ec_parsed_free(child_state);
124
125         return 0;
126 }
127
128 struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
129         const struct ec_strvec *strvec)
130 {
131         struct ec_parsed *parsed_state = NULL;
132         struct ec_completed *completed = NULL;
133         int ret;
134
135         parsed_state = ec_parsed();
136         if (parsed_state == NULL)
137                 goto fail;
138
139         completed = ec_completed();
140         if (completed == NULL)
141                 goto fail;
142
143         ret = ec_node_complete_child(node, completed,
144                                 parsed_state, strvec);
145         if (ret < 0)
146                 goto fail;
147
148         ec_parsed_free(parsed_state);
149
150         return completed;
151
152 fail:
153         ec_parsed_free(parsed_state);
154         ec_completed_free(completed);
155         return NULL;
156 }
157
158 struct ec_completed *ec_node_complete(struct ec_node *node,
159         const char *str)
160 {
161         struct ec_strvec *strvec = NULL;
162         struct ec_completed *completed;
163
164         errno = ENOMEM;
165         strvec = ec_strvec();
166         if (strvec == NULL)
167                 goto fail;
168
169         if (ec_strvec_add(strvec, str) < 0)
170                 goto fail;
171
172         completed = ec_node_complete_strvec(node, strvec);
173         if (completed == NULL)
174                 goto fail;
175
176         ec_strvec_free(strvec);
177         return completed;
178
179  fail:
180         ec_strvec_free(strvec);
181         return NULL;
182 }
183
184 static struct ec_completed_node *
185 ec_completed_node(const struct ec_node *node)
186 {
187         struct ec_completed_node *compnode = NULL;
188
189         compnode = ec_calloc(1, sizeof(*compnode));
190         if (compnode == NULL)
191                 return NULL;
192
193         compnode->node = node;
194         TAILQ_INIT(&compnode->items);
195
196         return compnode;
197 }
198
199 struct ec_completed_item *
200 ec_completed_item(struct ec_parsed *state, const struct ec_node *node)
201 {
202         struct ec_completed_item *item = NULL;
203         struct ec_parsed *p;
204         size_t len;
205
206         item = ec_calloc(1, sizeof(*item));
207         if (item == NULL)
208                 goto fail;
209
210         item->attrs = ec_keyval();
211         if (item->attrs == NULL)
212                 goto fail;
213
214         /* get path len */
215         for (p = state, len = 0; p != NULL;
216              p = ec_parsed_get_parent(p), len++)
217                 ;
218         /* allocate room for path */
219         item->path = ec_calloc(len, sizeof(*item->path));
220         if (item->path == NULL)
221                 goto fail;
222         item->pathlen = len;
223         /* write path in array */
224         for (p = state, len = 0; p != NULL;
225              p = ec_parsed_get_parent(p), len++)
226                 item->path[len] = p->node;
227
228         item->type = EC_NO_MATCH;
229         item->node = node;
230
231         return item;
232
233 fail:
234         if (item != NULL) {
235                 ec_free(item->path);
236                 ec_free(item->str);
237                 ec_free(item->display);
238                 ec_keyval_free(item->attrs);
239         }
240         ec_free(item);
241
242         return NULL;
243 }
244
245 int
246 ec_completed_item_set(struct ec_completed_item *item,
247                 enum ec_completed_type type, const char *str)
248 {
249         char *str_copy = NULL;
250         char *display_copy = NULL;
251         int ret = 0;
252
253         if (item == NULL)
254                 return -EINVAL;
255         if (item->str != NULL)
256                 return -EEXIST;
257
258         switch (type) {
259         case EC_NO_MATCH:
260                 if (str != NULL)
261                         return -EINVAL;
262                 break;
263         case EC_MATCH:
264         case EC_PARTIAL_MATCH:
265                 if (str == NULL)
266                         return -EINVAL;
267                 break;
268         default:
269                 return -EINVAL;
270         }
271
272         if (str != NULL) {
273                 ret = -ENOMEM;
274                 str_copy = ec_strdup(str);
275                 if (str_copy == NULL)
276                         goto fail;
277                 display_copy = ec_strdup(str);
278                 if (display_copy == NULL)
279                         goto fail;
280         }
281
282         item->type = type;
283         item->str = str_copy;
284         item->display = display_copy;
285         return 0;
286
287 fail:
288         ec_free(str_copy);
289         ec_free(display_copy);
290         return ret;
291 }
292
293 int ec_completed_item_set_display(struct ec_completed_item *item,
294                                 const char *display)
295 {
296         char *display_copy = NULL;
297         int ret = 0;
298
299         if (item == NULL || display == NULL ||
300                         item->type == EC_NO_MATCH || item->str == NULL)
301                 return -EINVAL;
302
303         ret = -ENOMEM;
304         display_copy = ec_strdup(display);
305         if (display_copy == NULL)
306                 goto fail;
307
308         ec_free(item->display);
309         item->display = display_copy;
310
311         return 0;
312
313 fail:
314         ec_free(display_copy);
315         return ret;
316 }
317
318 int
319 ec_completed_item_add(struct ec_completed *completed,
320                 struct ec_completed_item *item)
321 {
322         struct ec_completed_node *compnode = NULL;
323
324         if (completed == NULL || item == NULL || item->node == NULL)
325                 return -EINVAL;
326
327         switch (item->type) {
328         case EC_NO_MATCH:
329                 break;
330         case EC_MATCH:
331         case EC_PARTIAL_MATCH:
332                 completed->count_match++; //XXX
333                 break;
334         default:
335                 return -EINVAL;
336         }
337
338         /* find the compnode entry corresponding to this node */
339         TAILQ_FOREACH(compnode, &completed->nodes, next) {
340                 if (compnode->node == item->node)
341                         break;
342         }
343         if (compnode == NULL) {
344                 compnode = ec_completed_node(item->node);
345                 if (compnode == NULL)
346                         return -ENOMEM;
347                 TAILQ_INSERT_TAIL(&completed->nodes, compnode, next);
348         }
349
350         completed->count++;
351         TAILQ_INSERT_TAIL(&compnode->items, item, next);
352
353         return 0;
354 }
355
356 const char *
357 ec_completed_item_get_str(const struct ec_completed_item *item)
358 {
359         return item->str;
360 }
361
362 const char *
363 ec_completed_item_get_display(const struct ec_completed_item *item)
364 {
365         return item->display;
366 }
367
368 enum ec_completed_type
369 ec_completed_item_get_type(const struct ec_completed_item *item)
370 {
371         return item->type;
372 }
373
374 void ec_completed_item_free(struct ec_completed_item *item)
375 {
376         if (item == NULL)
377                 return;
378
379         ec_free(item->str);
380         ec_free(item->display);
381         ec_free(item->path);
382         ec_keyval_free(item->attrs);
383         ec_free(item);
384 }
385
386 /* default completion function: return a no-match element */
387 int
388 ec_node_default_complete(const struct ec_node *gen_node, // XXX rename in nomatch
389                         struct ec_completed *completed,
390                         struct ec_parsed *parsed_state,
391                         const struct ec_strvec *strvec)
392 {
393         struct ec_completed_item *item = NULL;
394         int ret;
395
396         if (ec_strvec_len(strvec) != 1)
397                 return 0;
398
399         item = ec_completed_item(parsed_state, gen_node);
400         if (item == NULL)
401                 return -ENOMEM;
402         ret = ec_completed_item_set(item, EC_NO_MATCH, NULL);
403         if (ret < 0) {
404                 ec_completed_item_free(item);
405                 return ret;
406         }
407         ret = ec_completed_item_add(completed, item);
408         if (ret < 0) {
409                 ec_completed_item_free(item);
410                 return ret;
411         }
412
413         return 0;
414 }
415
416 void ec_completed_free(struct ec_completed *completed)
417 {
418         struct ec_completed_node *compnode;
419         struct ec_completed_item *item;
420
421         if (completed == NULL)
422                 return;
423
424         while (!TAILQ_EMPTY(&completed->nodes)) {
425                 compnode = TAILQ_FIRST(&completed->nodes);
426                 TAILQ_REMOVE(&completed->nodes, compnode, next);
427
428                 while (!TAILQ_EMPTY(&compnode->items)) {
429                         item = TAILQ_FIRST(&compnode->items);
430                         TAILQ_REMOVE(&compnode->items, item, next);
431                         ec_completed_item_free(item);
432                 }
433                 ec_free(compnode);
434         }
435         ec_keyval_free(completed->attrs);
436         ec_free(completed);
437 }
438
439 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
440 {
441         struct ec_completed_node *compnode;
442         struct ec_completed_item *item;
443
444         if (completed == NULL || completed->count == 0) {
445                 fprintf(out, "no completion\n");
446                 return;
447         }
448
449         fprintf(out, "completion: count=%u match=%u\n",
450                 completed->count, completed->count_match);
451
452         TAILQ_FOREACH(compnode, &completed->nodes, next) {
453                 fprintf(out, "node=%p, node_type=%s\n",
454                         compnode->node, compnode->node->type->name);
455                 TAILQ_FOREACH(item, &compnode->items, next) {
456                         const char *typestr;
457
458                         switch (item->type) {
459                         case EC_NO_MATCH: typestr = "no-match"; break;
460                         case EC_MATCH: typestr = "match"; break;
461                         case EC_PARTIAL_MATCH: typestr = "partial-match"; break;
462                         default: typestr = "unknown"; break;
463                         }
464
465                         fprintf(out, "  type=%s str=<%s> disp=<%s>\n",
466                                 typestr, item->str, item->display);
467                 }
468         }
469 }
470
471 unsigned int ec_completed_count(
472         const struct ec_completed *completed,
473         enum ec_completed_type type)
474 {
475         unsigned int count = 0;
476
477         if (completed == NULL)
478                 return count;
479
480         if (type & EC_MATCH)
481                 count += completed->count_match;
482         if (type & EC_NO_MATCH)
483                 count += (completed->count - completed->count_match); //XXX
484
485         return count;
486 }
487
488 struct ec_completed_iter *
489 ec_completed_iter(struct ec_completed *completed,
490         enum ec_completed_type type)
491 {
492         struct ec_completed_iter *iter;
493
494         iter = ec_calloc(1, sizeof(*iter));
495         if (iter == NULL)
496                 return NULL;
497
498         iter->completed = completed;
499         iter->type = type;
500         iter->cur_node = NULL;
501         iter->cur_match = NULL;
502
503         return iter;
504 }
505
506 const struct ec_completed_item *ec_completed_iter_next(
507         struct ec_completed_iter *iter)
508 {
509         const struct ec_completed *completed = iter->completed;
510         const struct ec_completed_node *cur_node;
511         const struct ec_completed_item *cur_match;
512
513         if (completed == NULL)
514                 return NULL;
515
516         cur_node = iter->cur_node;
517         cur_match = iter->cur_match;
518
519         /* first call */
520         if (cur_node == NULL) {
521                 TAILQ_FOREACH(cur_node, &completed->nodes, next) {
522                         TAILQ_FOREACH(cur_match, &cur_node->items, next) {
523                                 if (cur_match != NULL &&
524                                                 cur_match->type & iter->type)
525                                         goto found;
526                         }
527                 }
528                 return NULL;
529         } else {
530                 cur_match = TAILQ_NEXT(cur_match, next);
531                 if (cur_match != NULL &&
532                                 cur_match->type & iter->type)
533                         goto found;
534                 cur_node = TAILQ_NEXT(cur_node, next);
535                 while (cur_node != NULL) {
536                         cur_match = TAILQ_FIRST(&cur_node->items);
537                         if (cur_match != NULL &&
538                                         cur_match->type & iter->type)
539                                 goto found;
540                         cur_node = TAILQ_NEXT(cur_node, next);
541                 }
542                 return NULL;
543         }
544
545 found:
546         iter->cur_node = cur_node;
547         iter->cur_match = cur_match;
548
549         return iter->cur_match;
550 }
551
552 void ec_completed_iter_free(struct ec_completed_iter *iter)
553 {
554         ec_free(iter);
555 }