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