8f8dc22584987f0ecfb639c4b09e3784dcc76a2d
[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
247         /* find the compnode entry corresponding to this node */
248         TAILQ_FOREACH(compnode, &completed->nodes, next) {
249                 if (compnode->node == node)
250                         break;
251         }
252         if (compnode == NULL)
253                 return -ENOENT;
254
255         match = ec_completed_match(type, parsed_state, node, add);
256         if (match == NULL)
257                 goto fail;
258
259         if (match->add != NULL)
260                 completed->count_match++;
261
262         TAILQ_INSERT_TAIL(&compnode->matches, match, next);
263         completed->count++;
264
265         return 0;
266
267 fail:
268         ec_completed_match_free(match);
269         return ret;
270 }
271
272 int
273 ec_completed_add_match(struct ec_completed *completed,
274                 struct ec_parsed *parsed_state,
275                 const struct ec_node *node, const char *add)
276 {
277         return __ec_completed_add_match(EC_MATCH, completed, parsed_state,
278                                         node, add);
279 }
280
281 int
282 ec_completed_add_no_match(struct ec_completed *completed,
283                 struct ec_parsed *parsed_state,
284                 const struct ec_node *node)
285 {
286         return __ec_completed_add_match(EC_NO_MATCH, completed, parsed_state,
287                                         node, NULL);
288 }
289
290 int
291 ec_completed_add_partial_match(struct ec_completed *completed,
292                 struct ec_parsed *parsed_state,
293                 const struct ec_node *node, const char *add)
294 {
295         return __ec_completed_add_match(EC_PARTIAL_MATCH, completed, parsed_state,
296                                         node, add);
297 }
298
299 int
300 ec_completed_add_node(struct ec_completed *completed,
301                 const struct ec_node *node)
302 {
303         struct ec_completed_node *compnode = NULL;
304
305         compnode = ec_completed_node(node);
306         if (compnode == NULL)
307                 return -ENOMEM;
308
309         TAILQ_INSERT_TAIL(&completed->nodes, compnode, next);
310         return 0;
311 }
312
313 void ec_completed_match_free(struct ec_completed_match *match)
314 {
315         ec_free(match->add);
316         ec_free(match->path);
317         ec_free(match);
318 }
319
320 /* default completion function: return a no-match element */
321 int
322 ec_node_default_complete(const struct ec_node *gen_node,
323                         struct ec_completed *completed,
324                         struct ec_parsed *parsed_state,
325                         const struct ec_strvec *strvec)
326 {
327         int ret;
328
329         if (ec_strvec_len(strvec) != 1)
330                 return 0;
331
332         ret = ec_completed_add_no_match(completed, parsed_state, gen_node);
333         if (ret < 0)
334                 return ret;
335
336         return 0;
337 }
338
339 void ec_completed_free(struct ec_completed *completed)
340 {
341         struct ec_completed_node *compnode;
342         struct ec_completed_match *item;
343
344         if (completed == NULL)
345                 return;
346
347         while (!TAILQ_EMPTY(&completed->nodes)) {
348                 compnode = TAILQ_FIRST(&completed->nodes);
349                 TAILQ_REMOVE(&completed->nodes, compnode, next);
350
351                 while (!TAILQ_EMPTY(&compnode->matches)) {
352                         item = TAILQ_FIRST(&compnode->matches);
353                         TAILQ_REMOVE(&compnode->matches, item, next);
354                         ec_completed_match_free(item);
355                 }
356                 ec_free(compnode);
357         }
358         ec_free(completed);
359 }
360
361 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
362 {
363         struct ec_completed_node *compnode;
364         struct ec_completed_match *item;
365
366         if (completed == NULL || completed->count == 0) {
367                 fprintf(out, "no completion\n");
368                 return;
369         }
370
371         fprintf(out, "completion: count=%u match=%u\n",
372                 completed->count, completed->count_match);
373
374         TAILQ_FOREACH(compnode, &completed->nodes, next) {
375                 fprintf(out, "node=%p, node_type=%s\n",
376                         compnode->node, compnode->node->type->name);
377                 TAILQ_FOREACH(item, &compnode->matches, next) {
378                         const char *typestr;
379
380                         switch (item->type) {
381                         case EC_NO_MATCH: typestr = "no-match"; break;
382                         case EC_MATCH: typestr = "match"; break;
383                         case EC_PARTIAL_MATCH: typestr = "partial-match"; break;
384                         default: typestr = "unknown"; break;
385                         }
386
387                         fprintf(out, "  type=%s add=<%s>\n", typestr, item->add);
388                 }
389         }
390 }
391
392 char *ec_completed_smallest_start(const struct ec_completed *completed)
393 {
394         struct ec_completed_node *compnode;
395         struct ec_completed_match *item;
396         char *smallest_start = NULL;
397         size_t n;
398
399         if (completed == NULL)
400                 goto fail;
401
402         TAILQ_FOREACH(compnode, &completed->nodes, next) {
403                 TAILQ_FOREACH(item, &compnode->matches, next) {
404                         if (item->add == NULL)
405                                 continue;
406                         if (smallest_start == NULL) {
407                                 smallest_start = ec_strdup(item->add);
408                                 if (smallest_start == NULL)
409                                         goto fail;
410                         } else {
411                                 n = strcmp_count(item->add, smallest_start);
412                                 smallest_start[n] = '\0';
413                         }
414                 }
415         }
416
417         return smallest_start;
418
419 fail:
420         ec_free(smallest_start);
421         return NULL;
422 }
423
424 unsigned int ec_completed_count(
425         const struct ec_completed *completed,
426         enum ec_completed_type type)
427 {
428         unsigned int count = 0;
429
430         if (completed == NULL)
431                 return count;
432
433         if (type & EC_MATCH)
434                 count += completed->count_match;
435         if (type & EC_NO_MATCH)
436                 count += (completed->count - completed->count_match); //XXX
437
438         return count;
439 }
440
441 struct ec_completed_iter *
442 ec_completed_iter(struct ec_completed *completed,
443         enum ec_completed_type type)
444 {
445         struct ec_completed_iter *iter;
446
447         iter = ec_calloc(1, sizeof(*iter));
448         if (iter == NULL)
449                 return NULL;
450
451         iter->completed = completed;
452         iter->type = type;
453         iter->cur_node = NULL;
454         iter->cur_match = NULL;
455
456         return iter;
457 }
458
459 const struct ec_completed_match *ec_completed_iter_next(
460         struct ec_completed_iter *iter)
461 {
462         const struct ec_completed *completed = iter->completed;
463         const struct ec_completed_node *cur_node;
464         const struct ec_completed_match *cur_match;
465
466         if (completed == NULL)
467                 return NULL;
468
469         cur_node = iter->cur_node;
470         cur_match = iter->cur_match;
471
472         /* first call */
473         if (cur_node == NULL) {
474                 TAILQ_FOREACH(cur_node, &completed->nodes, next) {
475                         TAILQ_FOREACH(cur_match, &cur_node->matches, next) {
476                                 if (cur_match != NULL &&
477                                                 cur_match->type & iter->type)
478                                         goto found;
479                         }
480                 }
481                 return NULL;
482         } else {
483                 cur_match = TAILQ_NEXT(cur_match, next);
484                 if (cur_match != NULL &&
485                                 cur_match->type & iter->type)
486                         goto found;
487                 cur_node = TAILQ_NEXT(cur_node, next);
488                 while (cur_node != NULL) {
489                         cur_match = TAILQ_FIRST(&cur_node->matches);
490                         if (cur_match != NULL &&
491                                         cur_match->type & iter->type)
492                                 goto found;
493                         cur_node = TAILQ_NEXT(cur_node, next);
494                 }
495                 return NULL;
496         }
497
498 found:
499         iter->cur_node = cur_node;
500         iter->cur_match = cur_match;
501
502         return iter->cur_match;
503 }
504
505 void ec_completed_iter_free(struct ec_completed_iter *iter)
506 {
507         ec_free(iter);
508 }