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