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