a600902ae04196db109a711ed2760051d69a55ea
[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
197         item = ec_calloc(1, sizeof(*item));
198         if (item == NULL)
199                 return NULL;
200
201         /* XXX can state be NULL? */
202         if (state != NULL) {
203                 struct ec_parsed *p;
204                 size_t len;
205
206                 /* get path len */
207                 for (p = state, len = 0; p != NULL;
208                      p = ec_parsed_get_parent(p), len++)
209                         ;
210
211                 item->path = ec_calloc(len, sizeof(*item->path));
212                 if (item->path == NULL)
213                         goto fail;
214
215                 item->pathlen = len;
216
217                 /* write path in array */
218                 for (p = state, len = 0; p != NULL;
219                      p = ec_parsed_get_parent(p), len++)
220                         item->path[len] = p->node;
221         }
222
223         item->type = type;
224         item->node = node;
225         if (add != NULL) {
226                 item->add = ec_strdup(add);
227                 if (item->add == NULL)
228                         goto fail;
229         }
230
231         return item;
232
233 fail:
234         if (item != NULL) {
235                 ec_free(item->path);
236                 ec_free(item->add);
237         }
238         ec_completed_match_free(item);
239
240         return NULL;
241 }
242
243 int
244 ec_completed_add_match(struct ec_completed *completed,
245                 struct ec_parsed *parsed_state,
246                 const struct ec_node *node, const char *add)
247 {
248         struct ec_completed_node *compnode = NULL;
249         struct ec_completed_match *item = NULL;
250         int ret = -ENOMEM;
251         size_t n;
252
253         TAILQ_FOREACH(compnode, &completed->nodes, next) {
254                 if (compnode->node == node)
255                         break;
256         }
257         if (compnode == NULL)
258                 return -ENOENT;
259
260         item = ec_completed_match(EC_MATCH, parsed_state, node, add);
261         if (item == NULL)
262                 goto fail;
263
264         if (item->add != NULL) {
265                 if (completed->smallest_start == NULL) {
266                         completed->smallest_start = ec_strdup(item->add);
267                         if (completed->smallest_start == NULL)
268                                 goto fail;
269                 } else {
270                         n = strcmp_count(item->add,
271                                 completed->smallest_start);
272                         completed->smallest_start[n] = '\0';
273                 }
274                 completed->count_match++;
275         }
276
277         TAILQ_INSERT_TAIL(&compnode->matches, item, next);
278         completed->count++;
279
280         return 0;
281
282 fail:
283         ec_completed_match_free(item);
284         return ret;
285 }
286
287 int
288 ec_completed_add_node(struct ec_completed *completed,
289                 const struct ec_node *node)
290 {
291         struct ec_completed_node *item = NULL;
292
293         item = ec_completed_node(node);
294         if (item == NULL)
295                 return -ENOMEM;
296
297         TAILQ_INSERT_TAIL(&completed->nodes, item, next);
298         return 0;
299 }
300
301 void ec_completed_match_free(struct ec_completed_match *item)
302 {
303         ec_free(item->add);
304         ec_free(item->path);
305         ec_free(item);
306 }
307
308 /* default completion function: return a no-item element */
309 int
310 ec_node_default_complete(const struct ec_node *gen_node,
311                         struct ec_completed *completed,
312                         struct ec_parsed *parsed,
313                         const struct ec_strvec *strvec)
314 {
315         (void)gen_node;
316         (void)completed;
317         (void)parsed;
318
319         if (ec_strvec_len(strvec) != 1) //XXX needed?
320                 return 0;
321
322         return 0;
323 }
324
325 void ec_completed_free(struct ec_completed *completed)
326 {
327         struct ec_completed_node *compnode;
328         struct ec_completed_match *item;
329
330         if (completed == NULL)
331                 return;
332
333         while (!TAILQ_EMPTY(&completed->nodes)) {
334                 compnode = TAILQ_FIRST(&completed->nodes);
335                 TAILQ_REMOVE(&completed->nodes, compnode, next);
336
337                 while (!TAILQ_EMPTY(&compnode->matches)) {
338                         item = TAILQ_FIRST(&compnode->matches);
339                         TAILQ_REMOVE(&compnode->matches, item, next);
340                         ec_completed_match_free(item);
341                 }
342                 ec_free(compnode);
343         }
344         ec_free(completed->smallest_start);
345         ec_free(completed);
346 }
347
348 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
349 {
350         struct ec_completed_node *compnode;
351         struct ec_completed_match *item;
352
353         if (completed == NULL || completed->count == 0) {
354                 fprintf(out, "no completion\n");
355                 return;
356         }
357
358         fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
359                 completed->count, completed->count_match,
360                 completed->smallest_start);
361
362         TAILQ_FOREACH(compnode, &completed->nodes, next) {
363                 fprintf(out, "node=%p, node_type=%s\n",
364                         compnode->node, compnode->node->type->name);
365                 TAILQ_FOREACH(item, &compnode->matches, next) {
366                         fprintf(out, "add=<%s>\n", item->add); /* XXX comp type */
367                 }
368         }
369 }
370
371 const char *ec_completed_smallest_start(
372         const struct ec_completed *completed)
373 {
374         if (completed == NULL || completed->smallest_start == NULL)
375                 return "";
376
377         return completed->smallest_start;
378 }
379
380 unsigned int ec_completed_count(
381         const struct ec_completed *completed,
382         enum ec_completed_type type)
383 {
384         unsigned int count = 0;
385
386         if (completed == NULL)
387                 return count;
388
389         if (type & EC_MATCH)
390                 count += completed->count_match;
391         if (type & EC_NO_MATCH)
392                 count += (completed->count - completed->count_match); //XXX
393
394         return count;
395 }
396
397 struct ec_completed_iter *
398 ec_completed_iter(struct ec_completed *completed,
399         enum ec_completed_type type)
400 {
401         struct ec_completed_iter *iter;
402
403         iter = ec_calloc(1, sizeof(*iter));
404         if (iter == NULL)
405                 return NULL;
406
407         iter->completed = completed;
408         iter->type = type;
409         iter->cur_node = NULL;
410         iter->cur_match = NULL;
411
412         return iter;
413 }
414
415 const struct ec_completed_match *ec_completed_iter_next(
416         struct ec_completed_iter *iter)
417 {
418         const struct ec_completed *completed = iter->completed;
419         const struct ec_completed_node *cur_node;
420         const struct ec_completed_match *cur_match;
421
422         if (completed == NULL)
423                 return NULL;
424
425         cur_node = iter->cur_node;
426         cur_match = iter->cur_match;
427
428         /* first call */
429         if (cur_node == NULL) {
430                 TAILQ_FOREACH(cur_node, &completed->nodes, next) {
431                         TAILQ_FOREACH(cur_match, &cur_node->matches, next) {
432                                 if (cur_match != NULL)
433                                         goto found;
434                         }
435                 }
436                 return NULL;
437         } else {
438                 cur_match = TAILQ_NEXT(cur_match, next);
439                 if (cur_match != NULL)
440                         goto found;
441                 cur_node = TAILQ_NEXT(cur_node, next);
442                 while (cur_node != NULL) {
443                         cur_match = TAILQ_FIRST(&cur_node->matches);
444                         if (cur_match != NULL)
445                                 goto found;
446                         cur_node = TAILQ_NEXT(cur_node, next);
447                 }
448                 return NULL;
449         }
450
451 found:
452         iter->cur_node = cur_node;
453         iter->cur_match = cur_match;
454
455         return iter->cur_match;
456 }
457
458 void ec_completed_iter_free(struct ec_completed_iter *iter)
459 {
460         ec_free(iter);
461 }