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