option, shlex
[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
33 #include <ecoli_malloc.h>
34 #include <ecoli_tk.h>
35
36 struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
37         size_t size)
38 {
39         struct ec_tk *tk = NULL;
40
41         assert(size >= sizeof(*tk));
42
43         tk = ec_calloc(1, size);
44         if (tk == NULL)
45                 goto fail;
46
47         if (id != NULL) {
48                 tk->id = ec_strdup(id);
49                 if (tk->id == NULL)
50                         goto fail;
51         }
52
53         tk->ops = ops;
54
55         return tk;
56
57  fail:
58         ec_tk_free(tk);
59         return NULL;
60 }
61
62 void ec_tk_free(struct ec_tk *tk)
63 {
64         if (tk == NULL)
65                 return;
66
67         if (tk->ops != NULL && tk->ops->free_priv != NULL)
68                 tk->ops->free_priv(tk);
69         ec_free(tk->id);
70         ec_free(tk);
71 }
72
73 struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
74 {
75         struct ec_parsed_tk *parsed_tk;
76
77         /* by default, it does not match anything */
78         if (tk->ops->parse == NULL)
79                 return NULL;
80
81         parsed_tk = tk->ops->parse(tk, str);
82
83         return parsed_tk;
84 }
85
86
87 struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk)
88 {
89         struct ec_parsed_tk *parsed_tk;
90
91         parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
92         if (parsed_tk == NULL)
93                 goto fail;
94
95         parsed_tk->tk = tk;
96         TAILQ_INIT(&parsed_tk->children);
97
98         return parsed_tk;
99
100  fail:
101         return NULL;
102 }
103
104 void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
105 {
106         struct ec_parsed_tk *child;
107
108         if (parsed_tk == NULL)
109                 return;
110
111         while (!TAILQ_EMPTY(&parsed_tk->children)) {
112                 child = TAILQ_FIRST(&parsed_tk->children);
113                 TAILQ_REMOVE(&parsed_tk->children, child, next);
114                 ec_parsed_tk_free(child);
115         }
116         ec_free(parsed_tk->str);
117         ec_free(parsed_tk);
118 }
119
120 static void __ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk,
121         size_t indent)
122 {
123         struct ec_parsed_tk *child;
124         size_t i;
125         const char *s;
126
127         /* XXX enhance */
128         for (i = 0; i < indent; i++)
129                 fprintf(out, " ");
130         s = ec_parsed_tk_to_string(parsed_tk);
131         fprintf(out, "id=%s, s=<%s>\n", parsed_tk->tk->id, s);
132
133         TAILQ_FOREACH(child, &parsed_tk->children, next)
134                 __ec_parsed_tk_dump(out, child, indent + 2);
135 }
136
137 void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
138 {
139         if (parsed_tk == NULL) {
140                 fprintf(out, "no match\n");
141                 return;
142         }
143
144         __ec_parsed_tk_dump(out, parsed_tk, 0);
145 }
146
147 void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
148         struct ec_parsed_tk *child)
149 {
150         TAILQ_INSERT_TAIL(&parsed_tk->children, child, next);
151 }
152
153 struct ec_parsed_tk *ec_parsed_tk_find_first(struct ec_parsed_tk *parsed_tk,
154         const char *id)
155 {
156         struct ec_parsed_tk *child, *ret;
157
158         if (parsed_tk == NULL)
159                 return NULL;
160
161         if (parsed_tk->tk->id != NULL && !strcmp(parsed_tk->tk->id, id))
162                 return parsed_tk;
163
164         TAILQ_FOREACH(child, &parsed_tk->children, next) {
165                 ret = ec_parsed_tk_find_first(child, id);
166                 if (ret != NULL)
167                         return ret;
168         }
169
170         return NULL;
171 }
172
173 const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
174 {
175         if (parsed_tk == NULL)
176                 return NULL;
177
178         return parsed_tk->str;
179 }
180
181 struct ec_completed_tk *ec_completed_tk_new(void)
182 {
183         struct ec_completed_tk *completed_tk = NULL;
184
185         completed_tk = ec_calloc(1, sizeof(*completed_tk));
186         if (completed_tk == NULL)
187                 return NULL;
188
189         TAILQ_INIT(&completed_tk->elts);
190         completed_tk->count = 0;
191
192         return completed_tk;
193 }
194
195 struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
196         const char *add, const char *full)
197 {
198         struct ec_completed_tk_elt *elt = NULL;
199
200         elt = ec_calloc(1, sizeof(*elt));
201         if (elt == NULL)
202                 return NULL;
203
204         elt->tk = tk;
205         if (add != NULL) {
206                 elt->add = ec_strdup(add);
207                 if (elt->add == NULL) {
208                         ec_completed_tk_elt_free(elt);
209                         return NULL;
210                 }
211         }
212         if (full != NULL) {
213                 elt->full = ec_strdup(full);
214                 if (elt->full == NULL) {
215                         ec_completed_tk_elt_free(elt);
216                         return NULL;
217                 }
218         }
219
220         return elt;
221 }
222
223 /* XXX define when to use ec_tk_complete() or tk->complete()
224  * (same for parse)
225  * suggestion: tk->op() is internal, user calls the function
226  * other idea: have 2 functions
227  */
228 struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
229         const char *str)
230 {
231         struct ec_completed_tk *completed_tk;
232
233         if (tk->ops->complete == NULL)
234                 return ec_completed_tk_new();
235
236         completed_tk = tk->ops->complete(tk, str);
237
238         return completed_tk;
239 }
240
241 /* count the number of identical chars at the beginning of 2 strings */
242 static size_t strcmp_count(const char *s1, const char *s2)
243 {
244         size_t i = 0;
245
246         while (s1[i] && s2[i] && s1[i] == s2[i])
247                 i++;
248
249         return i;
250 }
251
252 void ec_completed_tk_add_elt(
253         struct ec_completed_tk *completed_tk, struct ec_completed_tk_elt *elt)
254 {
255         size_t n;
256
257         TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next);
258         completed_tk->count++;
259         if (elt->add != NULL) {
260                 if (completed_tk->smallest_start == NULL) {
261                         completed_tk->smallest_start = ec_strdup(elt->add);
262                 } else {
263                         n = strcmp_count(elt->add,
264                                 completed_tk->smallest_start);
265                         completed_tk->smallest_start[n] = '\0';
266                 }
267         }
268 }
269
270 void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
271 {
272         ec_free(elt->add);
273         ec_free(elt->full);
274         ec_free(elt);
275 }
276
277 void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
278         struct ec_completed_tk *completed_tk2)
279 {
280         struct ec_completed_tk_elt *elt;
281
282         assert(completed_tk1 != NULL);
283         assert(completed_tk2 != NULL);
284
285         while (!TAILQ_EMPTY(&completed_tk2->elts)) {
286                 elt = TAILQ_FIRST(&completed_tk2->elts);
287                 TAILQ_REMOVE(&completed_tk2->elts, elt, next);
288                 ec_completed_tk_add_elt(completed_tk1, elt);
289         }
290
291         ec_completed_tk_free(completed_tk2);
292 }
293
294 void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
295 {
296         struct ec_completed_tk_elt *elt;
297
298         if (completed_tk == NULL)
299                 return;
300
301         while (!TAILQ_EMPTY(&completed_tk->elts)) {
302                 elt = TAILQ_FIRST(&completed_tk->elts);
303                 TAILQ_REMOVE(&completed_tk->elts, elt, next);
304                 ec_completed_tk_elt_free(elt);
305         }
306         ec_free(completed_tk->smallest_start);
307         ec_free(completed_tk);
308 }
309
310 void ec_completed_tk_dump(FILE *out, const struct ec_completed_tk *completed_tk)
311 {
312         struct ec_completed_tk_elt *elt;
313
314         if (completed_tk == NULL || completed_tk->count == 0) {
315                 fprintf(out, "no completion\n");
316                 return;
317         }
318
319         fprintf(out, "completion: count=%u smallest_start=<%s>\n",
320                 completed_tk->count, completed_tk->smallest_start);
321
322         TAILQ_FOREACH(elt, &completed_tk->elts, next) {
323                 fprintf(out, "add=<%s>, full=<%s>, tk=%p\n",
324                         elt->add, elt->full, elt->tk);
325         }
326 }
327
328 const char *ec_completed_tk_smallest_start(
329         const struct ec_completed_tk *completed_tk)
330 {
331         if (completed_tk == NULL || completed_tk->smallest_start == NULL)
332                 return "";
333
334         return completed_tk->smallest_start;
335 }
336
337 unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk)
338 {
339         if (completed_tk == NULL)
340                 return 0;
341
342         return completed_tk->count;
343 }
344
345 void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk)
346 {
347         if (completed_tk == NULL)
348                 return;
349
350         completed_tk->cur = NULL;
351 }
352
353 const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
354         struct ec_completed_tk *completed_tk)
355 {
356         if (completed_tk == NULL)
357                 return NULL;
358
359         if (completed_tk->cur == NULL) {
360                 completed_tk->cur = TAILQ_FIRST(&completed_tk->elts);
361         } else {
362                 completed_tk->cur = TAILQ_NEXT(completed_tk->cur, next);
363         }
364
365         return completed_tk->cur;
366 }