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