completion type
[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->elts);
51         completed->count_match = 0;
52
53         return completed;
54 }
55
56 struct ec_completed *
57 ec_node_complete_child(struct ec_node *node,
58                 struct ec_parsed *state,
59                 const struct ec_strvec *strvec)
60 {
61         struct ec_completed *completed;
62         struct ec_parsed *child;
63         int ret;
64
65         /* build the node if required */
66         if (node->type->build != NULL) {
67                 if ((node->flags & EC_NODE_F_BUILT) == 0) {
68                         ret = node->type->build(node);
69                         if (ret < 0) {
70                                 errno = -ret;
71                                 return NULL;
72                         }
73                 }
74         }
75         node->flags |= EC_NODE_F_BUILT;
76
77         if (node->type->complete == NULL) {
78                 errno = ENOTSUP;
79                 return NULL;
80         }
81
82         child = ec_parsed();
83         if (child == NULL)
84                 return NULL;
85
86         child->node = node;
87         ec_parsed_add_child(state, child);
88         completed = node->type->complete(node, child, strvec);
89
90 #if 0 // XXX dump
91         printf("----------------------------------------------------------\n");
92         ec_node_dump(stdout, node);
93         ec_strvec_dump(stdout, strvec);
94         ec_completed_dump(stdout, completed);
95         ec_parsed_dump(stdout, state);
96 #endif
97
98         ec_parsed_del_child(state, child);
99         assert(TAILQ_EMPTY(&child->children));
100         ec_parsed_free(child);
101
102         return completed;
103 }
104
105 struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
106         const struct ec_strvec *strvec)
107 {
108         struct ec_parsed *state = ec_parsed();
109         struct ec_completed *completed;
110
111         if (state == NULL)
112                 return NULL;
113
114         completed = ec_node_complete_child(node, state, strvec);
115         ec_parsed_free(state);
116
117         return completed;
118 }
119
120 struct ec_completed *ec_node_complete(struct ec_node *node,
121         const char *str)
122 {
123         struct ec_strvec *strvec = NULL;
124         struct ec_completed *completed;
125
126         errno = ENOMEM;
127         strvec = ec_strvec();
128         if (strvec == NULL)
129                 goto fail;
130
131         if (ec_strvec_add(strvec, str) < 0)
132                 goto fail;
133
134         completed = ec_node_complete_strvec(node, strvec);
135         if (completed == NULL)
136                 goto fail;
137
138         ec_strvec_free(strvec);
139         return completed;
140
141  fail:
142         ec_strvec_free(strvec);
143         return NULL;
144 }
145
146 /* count the number of identical chars at the beginning of 2 strings */
147 static size_t strcmp_count(const char *s1, const char *s2)
148 {
149         size_t i = 0;
150
151         while (s1[i] && s2[i] && s1[i] == s2[i])
152                 i++;
153
154         return i;
155 }
156
157 static struct ec_completed_elt *
158 ec_completed_elt(enum ec_completed_type type, struct ec_parsed *state,
159                 const struct ec_node *node, const char *add)
160 {
161         struct ec_completed_elt *elt = NULL;
162
163         elt = ec_calloc(1, sizeof(*elt));
164         if (elt == NULL)
165                 return NULL;
166
167         /* XXX can state be NULL? */
168         if (state != NULL) {
169                 struct ec_parsed *p;
170                 size_t len;
171
172                 /* get path len */
173                 for (p = state, len = 0; p != NULL;
174                      p = ec_parsed_get_parent(p), len++)
175                         ;
176
177                 elt->path = ec_calloc(len, sizeof(*elt->path));
178                 if (elt->path == NULL)
179                         goto fail;
180
181                 elt->pathlen = len;
182
183                 /* write path in array */
184                 for (p = state, len = 0; p != NULL;
185                      p = ec_parsed_get_parent(p), len++)
186                         elt->path[len] = p->node;
187         }
188
189         elt->type = type;
190         elt->node = node;
191         if (add != NULL) {
192                 elt->add = ec_strdup(add);
193                 if (elt->add == NULL)
194                         goto fail;
195         }
196
197         return elt;
198
199 fail:
200         if (elt != NULL) {
201                 ec_free(elt->path);
202                 ec_free(elt->add);
203         }
204         ec_completed_elt_free(elt);
205
206         return NULL;
207 }
208
209 static int ec_completed_add_elt(struct ec_completed *completed,
210                                 struct ec_completed_elt *elt)
211 {
212         size_t n;
213
214         if (elt->add != NULL) {
215                 if (completed->smallest_start == NULL) {
216                         completed->smallest_start = ec_strdup(elt->add);
217                         if (completed->smallest_start == NULL)
218                                 return -ENOMEM;
219                 } else {
220                         n = strcmp_count(elt->add,
221                                 completed->smallest_start);
222                         completed->smallest_start[n] = '\0';
223                 }
224                 completed->count_match++;
225         }
226         TAILQ_INSERT_TAIL(&completed->elts, elt, next);
227         completed->count++;
228
229         return 0;
230 }
231
232 int ec_completed_add_match(struct ec_completed *completed,
233                         struct ec_parsed *state,
234                         const struct ec_node *node, const char *add)
235 {
236         struct ec_completed_elt *elt;
237         int ret;
238
239         elt = ec_completed_elt(EC_MATCH, state, node, add);
240         if (elt == NULL)
241                 return -ENOMEM;
242
243         ret = ec_completed_add_elt(completed, elt);
244         if (ret < 0) {
245                 ec_completed_elt_free(elt);
246                 return ret;
247         }
248
249         return 0;
250 }
251
252 int ec_completed_add_no_match(struct ec_completed *completed,
253                         struct ec_parsed *state, const struct ec_node *node)
254 {
255         struct ec_completed_elt *elt;
256         int ret;
257
258         elt = ec_completed_elt(EC_NO_MATCH, state, node, NULL);
259         if (elt == NULL)
260                 return -ENOMEM;
261
262         ret = ec_completed_add_elt(completed, elt);
263         if (ret < 0) {
264                 ec_completed_elt_free(elt);
265                 return ret;
266         }
267
268         return 0;
269 }
270
271 void ec_completed_elt_free(struct ec_completed_elt *elt)
272 {
273         ec_free(elt->add);
274         ec_free(elt->path);
275         ec_free(elt);
276 }
277
278 /* default completion function: return a no-match element */
279 struct ec_completed *ec_node_default_complete(const struct ec_node *gen_node,
280                                         struct ec_parsed *state,
281                                         const struct ec_strvec *strvec)
282 {
283         struct ec_completed *completed;
284
285         (void)strvec;
286         (void)state;
287
288         completed = ec_completed();
289         if (completed == NULL)
290                 return NULL;
291
292         if (ec_strvec_len(strvec) != 1)
293                 return completed;
294
295         if (ec_completed_add_no_match(completed, state, gen_node) < 0) {
296                 ec_completed_free(completed);
297                 return NULL;
298         }
299
300         return completed;
301 }
302
303 void ec_completed_merge(struct ec_completed *completed1,
304         struct ec_completed *completed2)
305 {
306         struct ec_completed_elt *elt;
307
308         assert(completed1 != NULL);
309         assert(completed2 != NULL);
310
311         while (!TAILQ_EMPTY(&completed2->elts)) {
312                 elt = TAILQ_FIRST(&completed2->elts);
313                 TAILQ_REMOVE(&completed2->elts, elt, next);
314                 ec_completed_add_elt(completed1, elt);
315         }
316
317         ec_completed_free(completed2);
318 }
319
320 void ec_completed_free(struct ec_completed *completed)
321 {
322         struct ec_completed_elt *elt;
323
324         if (completed == NULL)
325                 return;
326
327         while (!TAILQ_EMPTY(&completed->elts)) {
328                 elt = TAILQ_FIRST(&completed->elts);
329                 TAILQ_REMOVE(&completed->elts, elt, next);
330                 ec_completed_elt_free(elt);
331         }
332         ec_free(completed->smallest_start);
333         ec_free(completed);
334 }
335
336 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
337 {
338         struct ec_completed_elt *elt;
339
340         if (completed == NULL || completed->count == 0) {
341                 fprintf(out, "no completion\n");
342                 return;
343         }
344
345         fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
346                 completed->count, completed->count_match,
347                 completed->smallest_start);
348
349         TAILQ_FOREACH(elt, &completed->elts, next) {
350                 fprintf(out, "add=<%s>, node=%p, node_type=%s\n",
351                         elt->add, elt->node, elt->node->type->name);
352         }
353 }
354
355 const char *ec_completed_smallest_start(
356         const struct ec_completed *completed)
357 {
358         if (completed == NULL || completed->smallest_start == NULL)
359                 return "";
360
361         return completed->smallest_start;
362 }
363
364 unsigned int ec_completed_count(
365         const struct ec_completed *completed,
366         enum ec_completed_type type)
367 {
368         unsigned int count = 0;
369
370         if (completed == NULL)
371                 return count;
372
373         if (type & EC_MATCH)
374                 count += completed->count_match;
375         if (type & EC_NO_MATCH)
376                 count += (completed->count - completed->count_match); //XXX
377
378         return count;
379 }
380
381 struct ec_completed_iter *
382 ec_completed_iter(struct ec_completed *completed,
383         enum ec_completed_type type)
384 {
385         struct ec_completed_iter *iter;
386
387         iter = ec_calloc(1, sizeof(*iter));
388         if (iter == NULL)
389                 return NULL;
390
391         iter->completed = completed;
392         iter->type = type;
393         iter->cur = NULL;
394
395         return iter;
396 }
397
398 const struct ec_completed_elt *ec_completed_iter_next(
399         struct ec_completed_iter *iter)
400 {
401         if (iter->completed == NULL)
402                 return NULL;
403
404         do {
405                 if (iter->cur == NULL)
406                         iter->cur = TAILQ_FIRST(&iter->completed->elts);
407                 else
408                         iter->cur = TAILQ_NEXT(iter->cur, next);
409
410                 if (iter->cur == NULL)
411                         break;
412
413                 if (iter->cur->add == NULL &&
414                                 (iter->type & EC_NO_MATCH))
415                         break;
416
417                 if (iter->cur->add != NULL &&
418                                 (iter->type & EC_MATCH))
419                         break;
420
421         } while (iter->cur != NULL);
422
423         return iter->cur;
424 }
425
426 void ec_completed_iter_free(struct ec_completed_iter *iter)
427 {
428         ec_free(iter);
429 }