277d696fb161bf9adc4993e615dfa59a1b6f1b27
[protos/libecoli.git] / lib / ecoli_node_cmd.c
1 /*
2  * Copyright (c) 2016-2017, 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 <sys/queue.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33 #include <stdarg.h>
34 #include <errno.h>
35 #include <limits.h>
36
37 #include <ecoli_malloc.h>
38 #include <ecoli_log.h>
39 #include <ecoli_test.h>
40 #include <ecoli_strvec.h>
41 #include <ecoli_node.h>
42 #include <ecoli_parsed.h>
43 #include <ecoli_completed.h>
44 #include <ecoli_node_expr.h>
45 #include <ecoli_node_str.h>
46 #include <ecoli_node_or.h>
47 #include <ecoli_node_subset.h>
48 #include <ecoli_node_int.h>
49 #include <ecoli_node_many.h>
50 #include <ecoli_node_seq.h>
51 #include <ecoli_node_option.h>
52 #include <ecoli_node_re.h>
53 #include <ecoli_node_re_lex.h>
54 #include <ecoli_node_cmd.h>
55
56 EC_LOG_TYPE_REGISTER(node_cmd);
57
58 struct ec_node_cmd {
59         struct ec_node gen;
60         char *cmd_str;           /* the command string. */
61         struct ec_node *cmd;       /* the command node. */
62         struct ec_node *lex;       /* the lexer node. */
63         struct ec_node *expr;      /* the expression parser. */
64         struct ec_node **table;    /* table of node referenced in command. */
65         unsigned int len;        /* len of the table. */
66 };
67
68 static int
69 ec_node_cmd_eval_var(void **result, void *userctx,
70         const struct ec_parsed *var)
71 {
72         const struct ec_strvec *vec;
73         struct ec_node_cmd *node = userctx;
74         struct ec_node *eval = NULL;
75         const char *str, *id;
76         unsigned int i;
77
78         (void)userctx;
79
80         /* get parsed string vector, it should contain only one str */
81         vec = ec_parsed_strvec(var);
82         if (ec_strvec_len(vec) != 1)
83                 return -EINVAL;
84         str = ec_strvec_val(vec, 0);
85
86         for (i = 0; i < node->len; i++) {
87                 id = ec_node_id(node->table[i]);
88                 if (id == NULL)
89                         continue;
90                 if (strcmp(str, id))
91                         continue;
92                 /* if id matches, use a node provided by the user... */
93                 eval = ec_node_clone(node->table[i]);
94                 if (eval == NULL)
95                         return -ENOMEM;
96                 break;
97         }
98
99         /* ...or create a string node */
100         if (eval == NULL) {
101                 eval = ec_node_str(EC_NO_ID, str);
102                 if (eval == NULL)
103                         return -ENOMEM;
104         }
105
106         *result = eval;
107
108         return 0;
109 }
110
111 static int
112 ec_node_cmd_eval_pre_op(void **result, void *userctx, void *operand,
113         const struct ec_parsed *operator)
114 {
115         (void)result;
116         (void)userctx;
117         (void)operand;
118         (void)operator;
119
120         return -EINVAL;
121 }
122
123 static int
124 ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand,
125         const struct ec_parsed *operator)
126 {
127         const struct ec_strvec *vec;
128         struct ec_node *in = operand;;
129         struct ec_node *out = NULL;;
130
131         (void)userctx;
132
133         /* get parsed string vector, it should contain only one str */
134         vec = ec_parsed_strvec(operator);
135         if (ec_strvec_len(vec) != 1)
136                 return -EINVAL;
137
138         if (!strcmp(ec_strvec_val(vec, 0), "*")) {
139                 out = ec_node_many(EC_NO_ID,
140                                 ec_node_clone(in), 0, 0);
141                 if (out == NULL)
142                         return -EINVAL;
143                 ec_node_free(in);
144                 *result = out;
145         } else {
146                 return -EINVAL;
147         }
148
149         return 0;
150 }
151
152 static int
153 ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
154         const struct ec_parsed *operator, void *operand2)
155
156 {
157         const struct ec_strvec *vec;
158         struct ec_node *out = NULL;
159         struct ec_node *in1 = operand1;
160         struct ec_node *in2 = operand2;
161
162         (void)userctx;
163
164         /* get parsed string vector, it should contain only one str */
165         vec = ec_parsed_strvec(operator);
166         if (ec_strvec_len(vec) > 1)
167                 return -EINVAL;
168
169         if (ec_strvec_len(vec) == 0) {
170                 if (!strcmp(in1->type->name, "seq")) {
171                         if (ec_node_seq_add(in1, ec_node_clone(in2)) < 0)
172                                 return -EINVAL;
173                         ec_node_free(in2);
174                         *result = in1;
175                 } else {
176                         out = EC_NODE_SEQ(EC_NO_ID, ec_node_clone(in1),
177                                         ec_node_clone(in2));
178                         if (out == NULL)
179                                 return -EINVAL;
180                         ec_node_free(in1);
181                         ec_node_free(in2);
182                         *result = out;
183                 }
184         } else if (!strcmp(ec_strvec_val(vec, 0), "|")) {
185                 if (!strcmp(in2->type->name, "or")) {
186                         if (ec_node_or_add(in2, ec_node_clone(in1)) < 0)
187                                 return -EINVAL;
188                         ec_node_free(in1);
189                         *result = in2;
190                 } else if (!strcmp(in1->type->name, "or")) {
191                         if (ec_node_or_add(in1, ec_node_clone(in2)) < 0)
192                                 return -EINVAL;
193                         ec_node_free(in2);
194                         *result = in1;
195                 } else {
196                         out = EC_NODE_OR(EC_NO_ID, ec_node_clone(in1),
197                                         ec_node_clone(in2));
198                         if (out == NULL)
199                                 return -EINVAL;
200                         ec_node_free(in1);
201                         ec_node_free(in2);
202                         *result = out;
203                 }
204         } else if (!strcmp(ec_strvec_val(vec, 0), ",")) {
205                 if (!strcmp(in2->type->name, "subset")) {
206                         if (ec_node_subset_add(in2, ec_node_clone(in1)) < 0)
207                                 return -EINVAL;
208                         ec_node_free(in1);
209                         *result = in2;
210                 } else if (!strcmp(in1->type->name, "subset")) {
211                         if (ec_node_subset_add(in1, ec_node_clone(in2)) < 0)
212                                 return -EINVAL;
213                         ec_node_free(in2);
214                         *result = in1;
215                 } else {
216                         out = EC_NODE_SUBSET(EC_NO_ID, ec_node_clone(in1),
217                                         ec_node_clone(in2));
218                         if (out == NULL)
219                                 return -EINVAL;
220                         ec_node_free(in1);
221                         ec_node_free(in2);
222                         *result = out;
223                 }
224         } else {
225                 return -EINVAL;
226         }
227
228         return 0;
229 }
230
231 static int
232 ec_node_cmd_eval_parenthesis(void **result, void *userctx,
233         const struct ec_parsed *open_paren,
234         const struct ec_parsed *close_paren,
235         void *value)
236 {
237         const struct ec_strvec *vec;
238         struct ec_node *in = value;;
239         struct ec_node *out = NULL;;
240
241         (void)userctx;
242         (void)close_paren;
243
244         /* get parsed string vector, it should contain only one str */
245         vec = ec_parsed_strvec(open_paren);
246         if (ec_strvec_len(vec) != 1)
247                 return -EINVAL;
248
249         if (!strcmp(ec_strvec_val(vec, 0), "[")) {
250                 out = ec_node_option(EC_NO_ID, ec_node_clone(in));
251                 if (out == NULL)
252                         return -EINVAL;
253                 ec_node_free(in);
254         } else if (!strcmp(ec_strvec_val(vec, 0), "(")) {
255                 out = in;
256         } else {
257                 return -EINVAL;
258         }
259
260         *result = out;
261
262         return 0;
263 }
264
265 static void
266 ec_node_cmd_eval_free(void *result, void *userctx)
267 {
268         (void)userctx;
269         ec_free(result);
270 }
271
272 static const struct ec_node_expr_eval_ops test_ops = {
273         .eval_var = ec_node_cmd_eval_var,
274         .eval_pre_op = ec_node_cmd_eval_pre_op,
275         .eval_post_op = ec_node_cmd_eval_post_op,
276         .eval_bin_op = ec_node_cmd_eval_bin_op,
277         .eval_parenthesis = ec_node_cmd_eval_parenthesis,
278         .eval_free = ec_node_cmd_eval_free,
279 };
280
281 static int ec_node_cmd_build(struct ec_node_cmd *node)
282 {
283         struct ec_node *expr = NULL, *lex = NULL, *cmd = NULL;
284         struct ec_parsed *p = NULL;
285         void *result;
286         int ret;
287
288         ec_node_free(node->expr);
289         node->expr = NULL;
290         ec_node_free(node->lex);
291         node->lex = NULL;
292         ec_node_free(node->cmd);
293         node->cmd = NULL;
294
295         /* build the expression parser */
296         ret = -ENOMEM;
297         expr = ec_node("expr", "expr");
298         if (expr == NULL)
299                 goto fail;
300         ret = ec_node_expr_set_val_node(expr, ec_node_re(EC_NO_ID, "[a-zA-Z0-9]+"));
301         if (ret < 0)
302                 goto fail;
303         ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, ","));
304         if (ret < 0)
305                 goto fail;
306         ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, "|"));
307         if (ret < 0)
308                 goto fail;
309         ret = ec_node_expr_add_bin_op(expr, ec_node("empty", EC_NO_ID));
310         if (ret < 0)
311                 goto fail;
312         ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "+"));
313         if (ret < 0)
314                 goto fail;
315         ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "*"));
316         if (ret < 0)
317                 goto fail;
318         ret = ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "["),
319                 ec_node_str(EC_NO_ID, "]"));
320         if (ret < 0)
321                 goto fail;
322         ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "("),
323                 ec_node_str(EC_NO_ID, ")"));
324         if (ret < 0)
325                 goto fail;
326
327         /* prepend a lexer to the expression node */
328         ret = -ENOMEM;
329         lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
330         if (lex == NULL)
331                 goto fail;
332
333         ret = ec_node_re_lex_add(lex, "[a-zA-Z0-9]+", 1);
334         if (ret < 0)
335                 goto fail;
336         ret = ec_node_re_lex_add(lex, "[*|,()]", 1);
337         if (ret < 0)
338                 goto fail;
339         ret = ec_node_re_lex_add(lex, "\\[", 1);
340         if (ret < 0)
341                 goto fail;
342         ret = ec_node_re_lex_add(lex, "\\]", 1);
343         if (ret < 0)
344                 goto fail;
345         ret = ec_node_re_lex_add(lex, "[         ]+", 0);
346         if (ret < 0)
347                 goto fail;
348
349         /* parse the command expression */
350         ret = -ENOMEM;
351         p = ec_node_parse(lex, node->cmd_str);
352         if (p == NULL)
353                 goto fail;
354
355         ret = -EINVAL;
356         if (!ec_parsed_matches(p))
357                 goto fail;
358         if (!ec_parsed_has_child(p))
359                 goto fail;
360
361         ret = ec_node_expr_eval(&result, expr, ec_parsed_get_first_child(p),
362                                 &test_ops, node);
363         if (ret < 0)
364                 goto fail;
365
366         ec_parsed_free(p);
367         p = NULL;
368
369         node->expr = expr;
370         node->lex = lex;
371         node->cmd = result;
372
373         return 0;
374
375 fail:
376         ec_parsed_free(p);
377         ec_node_free(expr);
378         ec_node_free(lex);
379         ec_node_free(cmd);
380         return ret;
381 }
382
383 static int
384 ec_node_cmd_parse(const struct ec_node *gen_node, struct ec_parsed *state,
385                 const struct ec_strvec *strvec)
386 {
387         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
388
389         if (node->cmd == NULL)
390                 return -ENOENT;
391         return ec_node_parse_child(node->cmd, state, strvec);
392 }
393
394 static int
395 ec_node_cmd_complete(const struct ec_node *gen_node,
396                 struct ec_completed *completed,
397                 const struct ec_strvec *strvec)
398 {
399         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
400
401         if (node->cmd == NULL)
402                 return -ENOENT;
403         return ec_node_complete_child(node->cmd, completed, strvec);
404 }
405
406 static void ec_node_cmd_free_priv(struct ec_node *gen_node)
407 {
408         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
409         unsigned int i;
410
411         ec_free(node->cmd_str);
412         ec_node_free(node->cmd);
413         ec_node_free(node->expr);
414         ec_node_free(node->lex);
415         for (i = 0; i < node->len; i++)
416                 ec_node_free(node->table[i]);
417         ec_free(node->table);
418 }
419
420
421 static struct ec_node_type ec_node_cmd_type = {
422         .name = "cmd",
423         .parse = ec_node_cmd_parse,
424         .complete = ec_node_cmd_complete,
425         .size = sizeof(struct ec_node_cmd),
426         .free_priv = ec_node_cmd_free_priv,
427 };
428
429 EC_NODE_TYPE_REGISTER(ec_node_cmd_type);
430
431 int ec_node_cmd_add_child(struct ec_node *gen_node, struct ec_node *child)
432 {
433         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
434         struct ec_node **table;
435         int ret;
436
437         assert(node != NULL);
438
439         if (child == NULL) {
440                 errno = EINVAL;
441                 goto fail;
442         }
443
444         if (ec_node_check_type(gen_node, &ec_node_cmd_type) < 0)
445                 goto fail;
446
447         if (node->cmd == NULL) {
448                 ret = ec_node_cmd_build(node);
449                 if (ret < 0)
450                         return ret;
451         }
452
453         table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
454         if (table == NULL)
455                 goto fail;
456
457         node->table = table;
458
459         if (ec_node_add_child(gen_node, child) < 0)
460                 goto fail;
461
462         table[node->len] = child;
463         node->len++;
464
465         return 0;
466
467 fail:
468         ec_node_free(child);
469         return -1;
470 }
471
472 struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...)
473 {
474         struct ec_node *gen_node = NULL;
475         struct ec_node_cmd *node = NULL;
476         struct ec_node *child;
477         va_list ap;
478         int fail = 0;
479
480         gen_node = __ec_node(&ec_node_cmd_type, id);
481         if (gen_node == NULL)
482                 fail = 1;
483
484         if (fail == 0) {
485                 node = (struct ec_node_cmd *)gen_node;
486                 node->cmd_str = ec_strdup(cmd);
487                 if (node->cmd_str == NULL)
488                         fail = 1;
489         }
490
491         va_start(ap, cmd);
492
493         for (child = va_arg(ap, struct ec_node *);
494              child != EC_NODE_ENDLIST;
495              child = va_arg(ap, struct ec_node *)) {
496
497                 /* on error, don't quit the loop to avoid leaks */
498                 if (fail == 1 || child == NULL ||
499                                 ec_node_cmd_add_child(&node->gen, child) < 0) {
500                         fail = 1;
501                         ec_node_free(child);
502                 }
503         }
504
505         va_end(ap);
506
507         if (fail == 1)
508                 goto fail;
509
510         if (ec_node_cmd_build(node) < 0)
511                 goto fail;
512
513         return gen_node;
514
515 fail:
516         ec_node_free(gen_node); /* will also free children */
517         return NULL;
518 }
519
520 struct ec_node *ec_node_cmd(const char *id, const char *cmd_str)
521 {
522         return __ec_node_cmd(id, cmd_str, EC_NODE_ENDLIST);
523 }
524
525 /* LCOV_EXCL_START */
526 static int ec_node_cmd_testcase(void)
527 {
528         struct ec_node *node;
529         int testres = 0;
530
531         node = EC_NODE_CMD(EC_NO_ID,
532                 "command [option] (subset1, subset2, subset3, subset4) x|y z*",
533                 ec_node_int("x", 0, 10, 10),
534                 ec_node_int("y", 20, 30, 10)
535         );
536         if (node == NULL) {
537                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
538                 return -1;
539         }
540         testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "1");
541         testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "subset1", "1");
542         testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "subset3", "subset2",
543                                 "1");
544         testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "subset2", "subset3",
545                                 "subset1", "1");
546         testres |= EC_TEST_CHECK_PARSE(node, 6, "command", "subset3", "subset1",
547                                 "subset4", "subset2", "4");
548         testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "23");
549         testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23");
550         testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "option", "23",
551                                 "z", "z");
552         testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "15");
553         testres |= EC_TEST_CHECK_PARSE(node, -1, "foo");
554         ec_node_free(node);
555
556         node = EC_NODE_CMD(EC_NO_ID, "good morning [count] bob|bobby|michael",
557                         ec_node_int("count", 0, 10, 10));
558         if (node == NULL) {
559                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
560                 return -1;
561         }
562         testres |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob");
563
564         testres |= EC_TEST_CHECK_COMPLETE(node,
565                 "", EC_NODE_ENDLIST,
566                 "good", EC_NODE_ENDLIST);
567         testres |= EC_TEST_CHECK_COMPLETE(node,
568                 "g", EC_NODE_ENDLIST,
569                 "good", EC_NODE_ENDLIST);
570         testres |= EC_TEST_CHECK_COMPLETE(node,
571                 "good", "morning", "", EC_NODE_ENDLIST,
572                 "bob", "bobby", "michael", EC_NODE_ENDLIST);
573
574         ec_node_free(node);
575
576         node = EC_NODE_CMD(EC_NO_ID, "[foo [bar]]");
577         if (node == NULL) {
578                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
579                 return -1;
580         }
581         testres |= EC_TEST_CHECK_PARSE(node, 0);
582         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
583         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
584         testres |= EC_TEST_CHECK_PARSE(node, 0, "x");
585         ec_node_free(node);
586
587         return testres;
588 }
589 /* LCOV_EXCL_STOP */
590
591 static struct ec_test ec_node_cmd_test = {
592         .name = "node_cmd",
593         .test = ec_node_cmd_testcase,
594 };
595
596 EC_TEST_REGISTER(ec_node_cmd_test);