cont
[protos/libecoli.git] / lib / ecoli_tk.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_tk.h>
37
38 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
39         size_t size)
40 {
41         struct ec_tk *tk = NULL;
42
43         assert(size >= sizeof(*tk));
44
45         tk = ec_calloc(1, size);
46         if (tk == NULL)
47                 goto fail;
48
49         if (id != NULL) {
50                 tk->id = ec_strdup(id);
51                 if (tk->id == NULL)
52                         goto fail;
53         }
54
55         tk->ops = ops;
56
57         return tk;
58
59  fail:
60         ec_tk_free(tk);
61         return NULL;
62 }
63
64 void ec_tk_free(struct ec_tk *tk)
65 {
66         if (tk == NULL)
67                 return;
68
69         if (tk->ops != NULL && tk->ops->free_priv != NULL)
70                 tk->ops->free_priv(tk);
71         ec_free(tk->id);
72         ec_free(tk);
73 }
74
75 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
76 {
77         struct ec_strvec *strvec = NULL;
78         struct ec_parsed_tk *parsed_tk;
79
80         errno = ENOMEM;
81         strvec = ec_strvec_new();
82         if (strvec == NULL)
83                 goto fail;
84
85         if (ec_strvec_add(strvec, str) < 0)
86                 goto fail;
87
88         parsed_tk = ec_tk_parse_tokens(tk, strvec);
89         if (parsed_tk == NULL)
90                 goto fail;
91
92         ec_strvec_free(strvec);
93         return parsed_tk;
94
95  fail:
96         ec_strvec_free(strvec);
97         return NULL;
98 }
99
100 struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
101         const struct ec_strvec *strvec)
102 {
103         struct ec_parsed_tk *parsed_tk;
104
105         if (tk->ops->parse == NULL) {
106                 errno = ENOTSUP;
107                 return NULL;
108         }
109
110         parsed_tk = tk->ops->parse(tk, strvec);
111
112         return parsed_tk;
113 }
114
115
116 struct ec_parsed_tk *ec_parsed_tk_new(void)
117 {
118         struct ec_parsed_tk *parsed_tk = NULL;
119
120         parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
121         if (parsed_tk == NULL)
122                 goto fail;
123
124         TAILQ_INIT(&parsed_tk->children);
125
126         return parsed_tk;
127
128  fail:
129         return NULL;
130 }
131
132 void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
133         const struct ec_tk *tk, struct ec_strvec *strvec)
134 {
135         parsed_tk->tk = tk;
136         parsed_tk->strvec = strvec;
137 }
138
139 void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk)
140 {
141         struct ec_parsed_tk *child;
142
143         if (parsed_tk == NULL)
144                 return;
145
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);
150         }
151 }
152
153 void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
154 {
155         if (parsed_tk == NULL)
156                 return;
157
158         ec_parsed_tk_free_children(parsed_tk);
159         ec_strvec_free(parsed_tk->strvec);
160         ec_free(parsed_tk);
161 }
162
163 static void __ec_parsed_tk_dump(FILE *out,
164         const struct ec_parsed_tk *parsed_tk, size_t indent)
165 {
166         struct ec_parsed_tk *child;
167         size_t i;
168         const char *s, *id = "None", *typename = "None";
169
170         /* XXX enhance */
171         for (i = 0; i < indent; i++)
172                 fprintf(out, " ");
173
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;
179         }
180
181         fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
182
183         TAILQ_FOREACH(child, &parsed_tk->children, next)
184                 __ec_parsed_tk_dump(out, child, indent + 2);
185 }
186
187 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
188 {
189         if (parsed_tk == NULL) {
190                 fprintf(out, "parsed_tk is NULL, error in parse\n");
191                 return;
192         }
193         if (!ec_parsed_tk_matches(parsed_tk)) {
194                 fprintf(out, "no match\n");
195                 return;
196         }
197
198         __ec_parsed_tk_dump(out, parsed_tk, 0);
199 }
200
201 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
202         struct ec_parsed_tk *child)
203 {
204         TAILQ_INSERT_TAIL(&parsed_tk->children, child, next);
205 }
206
207 struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
208         const char *id)
209 {
210         struct ec_parsed_tk *child, *ret;
211
212         if (parsed_tk == NULL)
213                 return NULL;
214
215         if (parsed_tk->tk != NULL &&
216                         parsed_tk->tk->id != NULL &&
217                         !strcmp(parsed_tk->tk->id, id))
218                 return parsed_tk;
219
220         TAILQ_FOREACH(child, &parsed_tk->children, next) {
221                 ret = ec_parsed_tk_find_first(child, id);
222                 if (ret != NULL)
223                         return ret;
224         }
225
226         return NULL;
227 }
228
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)
232 {
233         if (parsed_tk == NULL || parsed_tk->strvec == NULL)
234                 return NULL;
235
236         return ec_strvec_val(parsed_tk->strvec, 0);
237 }
238
239 /* number of parsed tokens */
240 size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk)
241 {
242         if (parsed_tk == NULL || parsed_tk->strvec == NULL)
243                 return 0;
244
245         return ec_strvec_len(parsed_tk->strvec);
246 }
247
248 size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk)
249 {
250         if (parsed_tk == NULL)
251                 return 0;
252
253         if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children))
254                 return 0;
255
256         return 1;
257 }
258
259 struct ec_completed_tk *ec_completed_tk_new(void)
260 {
261         struct ec_completed_tk *completed_tk = NULL;
262
263         completed_tk = ec_calloc(1, sizeof(*completed_tk));
264         if (completed_tk == NULL)
265                 return NULL;
266
267         TAILQ_INIT(&completed_tk->elts);
268         completed_tk->count_match = 0;
269
270         return completed_tk;
271 }
272
273 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
274         const char *add)
275 {
276         struct ec_completed_tk_elt *elt = NULL;
277
278         elt = ec_calloc(1, sizeof(*elt));
279         if (elt == NULL)
280                 return NULL;
281
282         elt->tk = tk;
283         if (add != NULL) {
284                 elt->add = ec_strdup(add);
285                 if (elt->add == NULL) {
286                         ec_completed_tk_elt_free(elt);
287                         return NULL;
288                 }
289         }
290
291         return elt;
292 }
293
294 /* XXX define when to use ec_tk_complete() or tk->complete()
295  * (same for parse)
296  * suggestion: tk->op() is internal, user calls the function
297  * other idea: have 2 functions
298  */
299 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
300         const char *str)
301 {
302         struct ec_strvec *strvec = NULL;
303         struct ec_completed_tk *completed_tk;
304
305         errno = ENOMEM;
306         strvec = ec_strvec_new();
307         if (strvec == NULL)
308                 goto fail;
309
310         if (ec_strvec_add(strvec, str) < 0)
311                 goto fail;
312
313         completed_tk = ec_tk_complete_tokens(tk, strvec);
314         if (completed_tk == NULL)
315                 goto fail;
316
317         ec_strvec_free(strvec);
318         return completed_tk;
319
320  fail:
321         ec_strvec_free(strvec);
322         return NULL;
323 }
324
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)
328 {
329         struct ec_completed_tk *completed_tk;
330         struct ec_completed_tk_elt *completed_tk_elt;
331
332         (void)strvec;
333
334         completed_tk = ec_completed_tk_new();
335         if (completed_tk == NULL)
336                 return NULL;
337
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);
341                 return NULL;
342         }
343
344         ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
345
346         return completed_tk;
347 }
348
349 struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
350         const struct ec_strvec *strvec)
351 {
352         return tk->ops->complete(tk, strvec);
353 }
354
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)
357 {
358         size_t i = 0;
359
360         while (s1[i] && s2[i] && s1[i] == s2[i])
361                 i++;
362
363         return i;
364 }
365
366 void ec_completed_tk_add_elt(
367         struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt)
368 {
369         size_t n;
370
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);
378                 } else {
379                         n = strcmp_count(elt->add,
380                                 completed_tk->smallest_start);
381                         completed_tk->smallest_start[n] = '\0';
382                 }
383         }
384 }
385
386 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
387 {
388         ec_free(elt->add);
389         ec_free(elt);
390 }
391
392 void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
393         struct ec_completed_tk *completed_tk2)
394 {
395         struct ec_completed_tk_elt *elt;
396
397         assert(completed_tk1 != NULL);
398         assert(completed_tk2 != NULL);
399
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);
404         }
405
406         ec_completed_tk_free(completed_tk2);
407 }
408
409 void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
410 {
411         struct ec_completed_tk_elt *elt;
412
413         if (completed_tk == NULL)
414                 return;
415
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);
420         }
421         ec_free(completed_tk->smallest_start);
422         ec_free(completed_tk);
423 }
424
425 void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
426 {
427         struct ec_completed_tk_elt *elt;
428
429         if (completed_tk == NULL || completed_tk->count == 0) {
430                 fprintf(out, "no completion\n");
431                 return;
432         }
433
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);
437
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);
441         }
442 }
443
444 const char *ec_completed_tk_smallest_start(
445         const struct ec_completed_tk *completed_tk)
446 {
447         if (completed_tk == NULL || completed_tk->smallest_start == NULL)
448                 return "";
449
450         return completed_tk->smallest_start;
451 }
452
453 unsigned int ec_completed_tk_count_match(
454         const struct ec_completed_tk *completed_tk)
455 {
456         if (completed_tk == NULL)
457                 return 0;
458
459         return completed_tk->count_match;
460 }
461
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)
465 {
466         struct ec_completed_tk_iter *iter;
467
468         iter = ec_calloc(1, sizeof(*iter));
469         if (iter == NULL)
470                 return NULL;
471
472         iter->completed_tk = completed_tk;
473         iter->flags = flags;
474         iter->cur = NULL;
475
476         return iter;
477 }
478
479 const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
480         struct ec_completed_tk_iter *iter)
481 {
482         if (iter->completed_tk == NULL)
483                 return NULL;
484
485         do {
486                 if (iter->cur == NULL) {
487                         iter->cur = TAILQ_FIRST(&iter->completed_tk->elts);
488                 } else {
489                         iter->cur = TAILQ_NEXT(iter->cur, next);
490                 }
491
492                 if (iter->cur == NULL)
493                         break;
494
495                 if (iter->cur->add == NULL &&
496                                 (iter->flags & ITER_NO_MATCH))
497                         break;
498
499                 if (iter->cur->add != NULL &&
500                                 (iter->flags & ITER_MATCH))
501                         break;
502
503         } while (iter->cur != NULL);
504
505         return iter->cur;
506 }
507
508 void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)
509 {
510         ec_free(iter);
511 }