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>
38 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
41 struct ec_tk *tk = NULL;
43 assert(size >= sizeof(*tk));
45 tk = ec_calloc(1, size);
50 tk->id = ec_strdup(id);
64 void ec_tk_free(struct ec_tk *tk)
69 if (tk->ops != NULL && tk->ops->free_priv != NULL)
70 tk->ops->free_priv(tk);
75 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
77 struct ec_strvec *strvec = NULL;
78 struct ec_parsed_tk *parsed_tk;
81 strvec = ec_strvec_new();
85 if (ec_strvec_add(strvec, str) < 0)
88 parsed_tk = ec_tk_parse_tokens(tk, strvec);
89 if (parsed_tk == NULL)
92 ec_strvec_free(strvec);
96 ec_strvec_free(strvec);
100 struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
101 const struct ec_strvec *strvec)
103 struct ec_parsed_tk *parsed_tk;
105 if (tk->ops->parse == NULL) {
110 parsed_tk = tk->ops->parse(tk, strvec);
116 struct ec_parsed_tk *ec_parsed_tk_new(void)
118 struct ec_parsed_tk *parsed_tk = NULL;
120 parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
121 if (parsed_tk == NULL)
124 TAILQ_INIT(&parsed_tk->children);
132 void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
133 const struct ec_tk *tk, struct ec_strvec *strvec)
136 parsed_tk->strvec = strvec;
139 void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk)
141 struct ec_parsed_tk *child;
143 if (parsed_tk == NULL)
146 while (!TAILQ_EMPTY(&parsed_tk->children)) {
147 child = TAILQ_FIRST(&parsed_tk->children);
148 TAILQ_REMOVE(&parsed_tk->children, child, next);
149 ec_parsed_tk_free(child);
153 void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
155 if (parsed_tk == NULL)
158 ec_parsed_tk_free_children(parsed_tk);
159 ec_strvec_free(parsed_tk->strvec);
163 static void __ec_parsed_tk_dump(FILE *out,
164 const struct ec_parsed_tk *parsed_tk, size_t indent)
166 struct ec_parsed_tk *child;
168 const char *s, *id = "None", *typename = "None";
171 for (i = 0; i < indent; i++)
174 s = ec_parsed_tk_to_string(parsed_tk);
175 if (parsed_tk->tk != NULL) {
176 if (parsed_tk->tk->id != NULL)
177 id = parsed_tk->tk->id;
178 typename = parsed_tk->tk->ops->typename;
181 fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
183 TAILQ_FOREACH(child, &parsed_tk->children, next)
184 __ec_parsed_tk_dump(out, child, indent + 2);
187 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
189 if (parsed_tk == NULL) {
190 fprintf(out, "parsed_tk is NULL, error in parse\n");
193 if (!ec_parsed_tk_matches(parsed_tk)) {
194 fprintf(out, "no match\n");
198 __ec_parsed_tk_dump(out, parsed_tk, 0);
201 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
202 struct ec_parsed_tk *child)
204 TAILQ_INSERT_TAIL(&parsed_tk->children, child, next);
207 struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
210 struct ec_parsed_tk *child, *ret;
212 if (parsed_tk == NULL)
215 if (parsed_tk->tk != NULL &&
216 parsed_tk->tk->id != NULL &&
217 !strcmp(parsed_tk->tk->id, id))
220 TAILQ_FOREACH(child, &parsed_tk->children, next) {
221 ret = ec_parsed_tk_find_first(child, id);
229 /* XXX return NUL if it matches several tokens?
230 or add a parameter to join() the tokens ? */
231 const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
233 if (parsed_tk == NULL || parsed_tk->strvec == NULL)
236 return ec_strvec_val(parsed_tk->strvec, 0);
239 /* number of parsed tokens */
240 size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk)
242 if (parsed_tk == NULL || parsed_tk->strvec == NULL)
245 return ec_strvec_len(parsed_tk->strvec);
248 size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk)
250 if (parsed_tk == NULL)
253 if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children))
259 struct ec_completed_tk *ec_completed_tk_new(void)
261 struct ec_completed_tk *completed_tk = NULL;
263 completed_tk = ec_calloc(1, sizeof(*completed_tk));
264 if (completed_tk == NULL)
267 TAILQ_INIT(&completed_tk->elts);
268 completed_tk->count_match = 0;
273 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
276 struct ec_completed_tk_elt *elt = NULL;
278 elt = ec_calloc(1, sizeof(*elt));
284 elt->add = ec_strdup(add);
285 if (elt->add == NULL) {
286 ec_completed_tk_elt_free(elt);
294 /* XXX define when to use ec_tk_complete() or tk->complete()
296 * suggestion: tk->op() is internal, user calls the function
297 * other idea: have 2 functions
299 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
302 struct ec_strvec *strvec = NULL;
303 struct ec_completed_tk *completed_tk;
306 strvec = ec_strvec_new();
310 if (ec_strvec_add(strvec, str) < 0)
313 completed_tk = ec_tk_complete_tokens(tk, strvec);
314 if (completed_tk == NULL)
317 ec_strvec_free(strvec);
321 ec_strvec_free(strvec);
325 /* default completion function: return a no-match element */
326 struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
327 const struct ec_strvec *strvec)
329 struct ec_completed_tk *completed_tk;
330 struct ec_completed_tk_elt *completed_tk_elt;
334 completed_tk = ec_completed_tk_new();
335 if (completed_tk == NULL)
338 completed_tk_elt = ec_completed_tk_elt_new(gen_tk, NULL);
339 if (completed_tk_elt == NULL) {
340 ec_completed_tk_free(completed_tk);
344 ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
349 struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
350 const struct ec_strvec *strvec)
352 return tk->ops->complete(tk, strvec);
355 /* count the number of identical chars at the beginning of 2 strings */
356 static size_t strcmp_count(const char *s1, const char *s2)
360 while (s1[i] && s2[i] && s1[i] == s2[i])
366 void ec_completed_tk_add_elt(
367 struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt)
371 TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next);
372 completed_tk->count++;
373 if (elt->add != NULL)
374 completed_tk->count_match++;
375 if (elt->add != NULL) {
376 if (completed_tk->smallest_start == NULL) {
377 completed_tk->smallest_start = ec_strdup(elt->add);
379 n = strcmp_count(elt->add,
380 completed_tk->smallest_start);
381 completed_tk->smallest_start[n] = '\0';
386 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
392 void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
393 struct ec_completed_tk *completed_tk2)
395 struct ec_completed_tk_elt *elt;
397 assert(completed_tk1 != NULL);
398 assert(completed_tk2 != NULL);
400 while (!TAILQ_EMPTY(&completed_tk2->elts)) {
401 elt = TAILQ_FIRST(&completed_tk2->elts);
402 TAILQ_REMOVE(&completed_tk2->elts, elt, next);
403 ec_completed_tk_add_elt(completed_tk1, elt);
406 ec_completed_tk_free(completed_tk2);
409 void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
411 struct ec_completed_tk_elt *elt;
413 if (completed_tk == NULL)
416 while (!TAILQ_EMPTY(&completed_tk->elts)) {
417 elt = TAILQ_FIRST(&completed_tk->elts);
418 TAILQ_REMOVE(&completed_tk->elts, elt, next);
419 ec_completed_tk_elt_free(elt);
421 ec_free(completed_tk->smallest_start);
422 ec_free(completed_tk);
425 void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
427 struct ec_completed_tk_elt *elt;
429 if (completed_tk == NULL || completed_tk->count == 0) {
430 fprintf(out, "no completion\n");
434 fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
435 completed_tk->count, completed_tk->count_match,
436 completed_tk->smallest_start);
438 TAILQ_FOREACH(elt, &completed_tk->elts, next) {
439 fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n",
440 elt->add, elt->tk, elt->tk->ops->typename);
444 const char *ec_completed_tk_smallest_start(
445 const struct ec_completed_tk *completed_tk)
447 if (completed_tk == NULL || completed_tk->smallest_start == NULL)
450 return completed_tk->smallest_start;
453 unsigned int ec_completed_tk_count_match(
454 const struct ec_completed_tk *completed_tk)
456 if (completed_tk == NULL)
459 return completed_tk->count_match;
462 struct ec_completed_tk_iter *
463 ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
464 enum ec_completed_tk_filter_flags flags)
466 struct ec_completed_tk_iter *iter;
468 iter = ec_calloc(1, sizeof(*iter));
472 iter->completed_tk = completed_tk;
479 const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
480 struct ec_completed_tk_iter *iter)
482 if (iter->completed_tk == NULL)
486 if (iter->cur == NULL) {
487 iter->cur = TAILQ_FIRST(&iter->completed_tk->elts);
489 iter->cur = TAILQ_NEXT(iter->cur, next);
492 if (iter->cur == NULL)
495 if (iter->cur->add == NULL &&
496 (iter->flags & ITER_NO_MATCH))
499 if (iter->cur->add != NULL &&
500 (iter->flags & ITER_MATCH))
503 } while (iter->cur != NULL);
508 void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)