2 * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
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.
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.
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>
42 struct ec_completed *ec_completed(void)
44 struct ec_completed *completed = NULL;
46 completed = ec_calloc(1, sizeof(*completed));
47 if (completed == NULL)
50 TAILQ_INIT(&completed->nodes);
55 /* XXX on error, states are not freed ?
56 * they can be left in a bad state and should not be reused */
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)
63 struct ec_parsed *child_state = NULL;
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);
74 node->flags |= EC_NODE_F_BUILT;
76 if (node->type->complete == NULL)
79 child_state = ec_parsed();
80 if (child_state == NULL)
82 child_state->node = node;
83 ec_parsed_add_child(parsed_state, child_state);
85 ret = ec_completed_add_node(completed, node);
89 ret = node->type->complete(node, completed,
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);
102 ec_parsed_del_child(parsed_state, child_state);
103 assert(TAILQ_EMPTY(&child_state->children));
104 ec_parsed_free(child_state);
109 struct ec_completed *ec_node_complete_strvec(struct ec_node *node,
110 const struct ec_strvec *strvec)
112 struct ec_parsed *parsed_state = NULL;
113 struct ec_completed *completed = NULL;
116 parsed_state = ec_parsed();
117 if (parsed_state == NULL)
120 completed = ec_completed();
121 if (completed == NULL)
124 ret = ec_node_complete_child(node, completed,
125 parsed_state, strvec);
129 ec_parsed_free(parsed_state);
134 ec_parsed_free(parsed_state);
135 ec_completed_free(completed);
139 struct ec_completed *ec_node_complete(struct ec_node *node,
142 struct ec_strvec *strvec = NULL;
143 struct ec_completed *completed;
146 strvec = ec_strvec();
150 if (ec_strvec_add(strvec, str) < 0)
153 completed = ec_node_complete_strvec(node, strvec);
154 if (completed == NULL)
157 ec_strvec_free(strvec);
161 ec_strvec_free(strvec);
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)
170 while (s1[i] && s2[i] && s1[i] == s2[i])
176 static struct ec_completed_node *
177 ec_completed_node(const struct ec_node *node)
179 struct ec_completed_node *compnode = NULL;
181 compnode = ec_calloc(1, sizeof(*compnode));
182 if (compnode == NULL)
185 compnode->node = node;
186 TAILQ_INIT(&compnode->matches);
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)
195 struct ec_completed_match *item = NULL;
199 item = ec_calloc(1, sizeof(*item));
204 for (p = state, len = 0; p != NULL;
205 p = ec_parsed_get_parent(p), len++)
207 /* allocate room for path */
208 item->path = ec_calloc(len, sizeof(*item->path));
209 if (item->path == NULL)
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;
220 item->add = ec_strdup(add);
221 if (item->add == NULL)
232 ec_completed_match_free(item);
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)
243 struct ec_completed_node *compnode = NULL;
244 struct ec_completed_match *match = NULL;
247 /* find the compnode entry corresponding to this node */
248 TAILQ_FOREACH(compnode, &completed->nodes, next) {
249 if (compnode->node == node)
252 if (compnode == NULL)
255 match = ec_completed_match(type, parsed_state, node, add);
259 if (match->add != NULL)
260 completed->count_match++;
262 TAILQ_INSERT_TAIL(&compnode->matches, match, next);
268 ec_completed_match_free(match);
273 ec_completed_add_match(struct ec_completed *completed,
274 struct ec_parsed *parsed_state,
275 const struct ec_node *node, const char *add)
277 return __ec_completed_add_match(EC_MATCH, completed, parsed_state,
282 ec_completed_add_no_match(struct ec_completed *completed,
283 struct ec_parsed *parsed_state,
284 const struct ec_node *node)
286 return __ec_completed_add_match(EC_NO_MATCH, completed, parsed_state,
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)
295 return __ec_completed_add_match(EC_PARTIAL_MATCH, completed, parsed_state,
300 ec_completed_add_node(struct ec_completed *completed,
301 const struct ec_node *node)
303 struct ec_completed_node *compnode = NULL;
305 compnode = ec_completed_node(node);
306 if (compnode == NULL)
309 TAILQ_INSERT_TAIL(&completed->nodes, compnode, next);
313 void ec_completed_match_free(struct ec_completed_match *match)
316 ec_free(match->path);
320 /* default completion function: return a no-match element */
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)
329 if (ec_strvec_len(strvec) != 1)
332 ret = ec_completed_add_no_match(completed, parsed_state, gen_node);
339 void ec_completed_free(struct ec_completed *completed)
341 struct ec_completed_node *compnode;
342 struct ec_completed_match *item;
344 if (completed == NULL)
347 while (!TAILQ_EMPTY(&completed->nodes)) {
348 compnode = TAILQ_FIRST(&completed->nodes);
349 TAILQ_REMOVE(&completed->nodes, compnode, next);
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);
361 void ec_completed_dump(FILE *out, const struct ec_completed *completed)
363 struct ec_completed_node *compnode;
364 struct ec_completed_match *item;
366 if (completed == NULL || completed->count == 0) {
367 fprintf(out, "no completion\n");
371 fprintf(out, "completion: count=%u match=%u\n",
372 completed->count, completed->count_match);
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) {
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;
387 fprintf(out, " type=%s add=<%s>\n", typestr, item->add);
392 char *ec_completed_smallest_start(const struct ec_completed *completed)
394 struct ec_completed_node *compnode;
395 struct ec_completed_match *item;
396 char *smallest_start = NULL;
399 if (completed == NULL)
402 TAILQ_FOREACH(compnode, &completed->nodes, next) {
403 TAILQ_FOREACH(item, &compnode->matches, next) {
404 if (item->add == NULL)
406 if (smallest_start == NULL) {
407 smallest_start = ec_strdup(item->add);
408 if (smallest_start == NULL)
411 n = strcmp_count(item->add, smallest_start);
412 smallest_start[n] = '\0';
417 return smallest_start;
420 ec_free(smallest_start);
424 unsigned int ec_completed_count(
425 const struct ec_completed *completed,
426 enum ec_completed_type type)
428 unsigned int count = 0;
430 if (completed == NULL)
434 count += completed->count_match;
435 if (type & EC_NO_MATCH)
436 count += (completed->count - completed->count_match); //XXX
441 struct ec_completed_iter *
442 ec_completed_iter(struct ec_completed *completed,
443 enum ec_completed_type type)
445 struct ec_completed_iter *iter;
447 iter = ec_calloc(1, sizeof(*iter));
451 iter->completed = completed;
453 iter->cur_node = NULL;
454 iter->cur_match = NULL;
459 const struct ec_completed_match *ec_completed_iter_next(
460 struct ec_completed_iter *iter)
462 const struct ec_completed *completed = iter->completed;
463 const struct ec_completed_node *cur_node;
464 const struct ec_completed_match *cur_match;
466 if (completed == NULL)
469 cur_node = iter->cur_node;
470 cur_match = iter->cur_match;
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)
483 cur_match = TAILQ_NEXT(cur_match, next);
484 if (cur_match != NULL &&
485 cur_match->type & iter->type)
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)
493 cur_node = TAILQ_NEXT(cur_node, next);
499 iter->cur_node = cur_node;
500 iter->cur_match = cur_match;
502 return iter->cur_match;
505 void ec_completed_iter_free(struct ec_completed_iter *iter)