free children in free_priv
[protos/libecoli.git] / lib / ecoli_node_cmd.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/queue.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <stdarg.h>
11 #include <errno.h>
12 #include <limits.h>
13
14 #include <ecoli_malloc.h>
15 #include <ecoli_log.h>
16 #include <ecoli_test.h>
17 #include <ecoli_strvec.h>
18 #include <ecoli_node.h>
19 #include <ecoli_config.h>
20 #include <ecoli_parse.h>
21 #include <ecoli_complete.h>
22 #include <ecoli_node_expr.h>
23 #include <ecoli_node_str.h>
24 #include <ecoli_node_or.h>
25 #include <ecoli_node_subset.h>
26 #include <ecoli_node_int.h>
27 #include <ecoli_node_many.h>
28 #include <ecoli_node_seq.h>
29 #include <ecoli_node_option.h>
30 #include <ecoli_node_re.h>
31 #include <ecoli_node_re_lex.h>
32 #include <ecoli_node_cmd.h>
33
34 EC_LOG_TYPE_REGISTER(node_cmd);
35
36 struct ec_node_cmd {
37         struct ec_node gen;
38         char *cmd_str;           /* the command string. */
39         struct ec_node *cmd;     /* the command node. */
40         struct ec_node *parser;  /* the expression parser. */
41         struct ec_node *expr;    /* the expression parser without lexer. */
42         struct ec_node **table;  /* table of node referenced in command. */
43         unsigned int len;        /* len of the table. */
44 };
45
46 /* passed as user context to expression parser */
47 struct ec_node_cmd_ctx {
48         struct ec_node **table;
49         unsigned int len;
50 };
51
52 static int
53 ec_node_cmd_eval_var(void **result, void *userctx,
54         const struct ec_parse *var)
55 {
56         const struct ec_strvec *vec;
57         struct ec_node_cmd_ctx *ctx = userctx;
58         struct ec_node *eval = NULL;
59         const char *str, *id;
60         unsigned int i;
61
62         /* get parsed string vector, it should contain only one str */
63         vec = ec_parse_strvec(var);
64         if (ec_strvec_len(vec) != 1) {
65                 errno = EINVAL;
66                 return -1;
67         }
68         str = ec_strvec_val(vec, 0);
69
70         for (i = 0; i < ctx->len; i++) {
71                 id = ec_node_id(ctx->table[i]);
72                 if (id == NULL)
73                         continue;
74                 if (strcmp(str, id))
75                         continue;
76                 /* if id matches, use a node provided by the user... */
77                 eval = ec_node_clone(ctx->table[i]);
78                 if (eval == NULL)
79                         return -1;
80                 break;
81         }
82
83         /* ...or create a string node */
84         if (eval == NULL) {
85                 eval = ec_node_str(EC_NO_ID, str);
86                 if (eval == NULL)
87                         return -1;
88         }
89
90         *result = eval;
91
92         return 0;
93 }
94
95 static int
96 ec_node_cmd_eval_pre_op(void **result, void *userctx, void *operand,
97         const struct ec_parse *operator)
98 {
99         (void)result;
100         (void)userctx;
101         (void)operand;
102         (void)operator;
103
104         errno = EINVAL;
105         return -1;
106 }
107
108 static int
109 ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand,
110         const struct ec_parse *operator)
111 {
112         const struct ec_strvec *vec;
113         struct ec_node *in = operand;;
114         struct ec_node *out = NULL;;
115
116         (void)userctx;
117
118         /* get parsed string vector, it should contain only one str */
119         vec = ec_parse_strvec(operator);
120         if (ec_strvec_len(vec) != 1) {
121                 errno = EINVAL;
122                 return -1;
123         }
124
125         if (!strcmp(ec_strvec_val(vec, 0), "*")) {
126                 out = ec_node_many(EC_NO_ID,
127                                 ec_node_clone(in), 0, 0);
128                 if (out == NULL)
129                         return -1;
130                 ec_node_free(in);
131                 *result = out;
132         } else {
133                 errno = EINVAL;
134                 return -1;
135         }
136
137         return 0;
138 }
139
140 static int
141 ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
142         const struct ec_parse *operator, void *operand2)
143
144 {
145         const struct ec_strvec *vec;
146         struct ec_node *out = NULL;
147         struct ec_node *in1 = operand1;
148         struct ec_node *in2 = operand2;
149
150         (void)userctx;
151
152         /* get parsed string vector, it should contain only one str */
153         vec = ec_parse_strvec(operator);
154         if (ec_strvec_len(vec) > 1) {
155                 errno = EINVAL;
156                 return -1;
157         }
158
159         if (ec_strvec_len(vec) == 0) {
160                 if (!strcmp(in1->type->name, "seq")) {
161                         if (ec_node_seq_add(in1, ec_node_clone(in2)) < 0)
162                                 return -1;
163                         ec_node_free(in2);
164                         *result = in1;
165                 } else {
166                         out = EC_NODE_SEQ(EC_NO_ID, ec_node_clone(in1),
167                                         ec_node_clone(in2));
168                         if (out == NULL)
169                                 return -1;
170                         ec_node_free(in1);
171                         ec_node_free(in2);
172                         *result = out;
173                 }
174         } else if (!strcmp(ec_strvec_val(vec, 0), "|")) {
175                 if (!strcmp(in2->type->name, "or")) {
176                         if (ec_node_or_add(in2, ec_node_clone(in1)) < 0)
177                                 return -1;
178                         ec_node_free(in1);
179                         *result = in2;
180                 } else if (!strcmp(in1->type->name, "or")) {
181                         if (ec_node_or_add(in1, ec_node_clone(in2)) < 0)
182                                 return -1;
183                         ec_node_free(in2);
184                         *result = in1;
185                 } else {
186                         out = EC_NODE_OR(EC_NO_ID, ec_node_clone(in1),
187                                         ec_node_clone(in2));
188                         if (out == NULL)
189                                 return -1;
190                         ec_node_free(in1);
191                         ec_node_free(in2);
192                         *result = out;
193                 }
194         } else if (!strcmp(ec_strvec_val(vec, 0), ",")) {
195                 if (!strcmp(in2->type->name, "subset")) {
196                         if (ec_node_subset_add(in2, ec_node_clone(in1)) < 0)
197                                 return -1;
198                         ec_node_free(in1);
199                         *result = in2;
200                 } else if (!strcmp(in1->type->name, "subset")) {
201                         if (ec_node_subset_add(in1, ec_node_clone(in2)) < 0)
202                                 return -1;
203                         ec_node_free(in2);
204                         *result = in1;
205                 } else {
206                         out = EC_NODE_SUBSET(EC_NO_ID, ec_node_clone(in1),
207                                         ec_node_clone(in2));
208                         if (out == NULL)
209                                 return -1;
210                         ec_node_free(in1);
211                         ec_node_free(in2);
212                         *result = out;
213                 }
214         } else {
215                 errno = EINVAL;
216                 return -1;
217         }
218
219         return 0;
220 }
221
222 static int
223 ec_node_cmd_eval_parenthesis(void **result, void *userctx,
224         const struct ec_parse *open_paren,
225         const struct ec_parse *close_paren,
226         void *value)
227 {
228         const struct ec_strvec *vec;
229         struct ec_node *in = value;;
230         struct ec_node *out = NULL;;
231
232         (void)userctx;
233         (void)close_paren;
234
235         /* get parsed string vector, it should contain only one str */
236         vec = ec_parse_strvec(open_paren);
237         if (ec_strvec_len(vec) != 1) {
238                 errno = EINVAL;
239                 return -1;
240         }
241
242         if (!strcmp(ec_strvec_val(vec, 0), "[")) {
243                 out = ec_node_option(EC_NO_ID, ec_node_clone(in));
244                 if (out == NULL)
245                         return -1;
246                 ec_node_free(in);
247         } else if (!strcmp(ec_strvec_val(vec, 0), "(")) {
248                 out = in;
249         } else {
250                 errno = EINVAL;
251                 return -1;
252         }
253
254         *result = out;
255
256         return 0;
257 }
258
259 static void
260 ec_node_cmd_eval_free(void *result, void *userctx)
261 {
262         (void)userctx;
263         ec_free(result);
264 }
265
266 static const struct ec_node_expr_eval_ops expr_ops = {
267         .eval_var = ec_node_cmd_eval_var,
268         .eval_pre_op = ec_node_cmd_eval_pre_op,
269         .eval_post_op = ec_node_cmd_eval_post_op,
270         .eval_bin_op = ec_node_cmd_eval_bin_op,
271         .eval_parenthesis = ec_node_cmd_eval_parenthesis,
272         .eval_free = ec_node_cmd_eval_free,
273 };
274
275 static struct ec_node *
276 ec_node_cmd_build_expr(void)
277 {
278         struct ec_node *expr = NULL;
279         int ret;
280
281         /* build the expression parser */
282         expr = ec_node("expr", "expr");
283         if (expr == NULL)
284                 goto fail;
285         ret = ec_node_expr_set_val_node(expr, ec_node_re(EC_NO_ID,
286                                         "[a-zA-Z0-9]+"));
287         if (ret < 0)
288                 goto fail;
289         ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, ","));
290         if (ret < 0)
291                 goto fail;
292         ret = ec_node_expr_add_bin_op(expr, ec_node_str(EC_NO_ID, "|"));
293         if (ret < 0)
294                 goto fail;
295         ret = ec_node_expr_add_bin_op(expr, ec_node("empty", EC_NO_ID));
296         if (ret < 0)
297                 goto fail;
298         ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "+"));
299         if (ret < 0)
300                 goto fail;
301         ret = ec_node_expr_add_post_op(expr, ec_node_str(EC_NO_ID, "*"));
302         if (ret < 0)
303                 goto fail;
304         ret = ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "["),
305                 ec_node_str(EC_NO_ID, "]"));
306         if (ret < 0)
307                 goto fail;
308         ec_node_expr_add_parenthesis(expr, ec_node_str(EC_NO_ID, "("),
309                 ec_node_str(EC_NO_ID, ")"));
310         if (ret < 0)
311                 goto fail;
312
313         return expr;
314
315 fail:
316         ec_node_free(expr);
317         return NULL;
318 }
319
320 static struct ec_node *
321 ec_node_cmd_build_parser(struct ec_node *expr)
322 {
323         struct ec_node *lex = NULL;
324         int ret;
325
326         /* prepend a lexer to the expression node */
327         lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
328         if (lex == NULL)
329                 goto fail;
330
331         ret = ec_node_re_lex_add(lex, "[a-zA-Z0-9]+", 1);
332         if (ret < 0)
333                 goto fail;
334         ret = ec_node_re_lex_add(lex, "[*|,()]", 1);
335         if (ret < 0)
336                 goto fail;
337         ret = ec_node_re_lex_add(lex, "\\[", 1);
338         if (ret < 0)
339                 goto fail;
340         ret = ec_node_re_lex_add(lex, "\\]", 1);
341         if (ret < 0)
342                 goto fail;
343         ret = ec_node_re_lex_add(lex, "[         ]+", 0);
344         if (ret < 0)
345                 goto fail;
346
347         return lex;
348
349 fail:
350         ec_node_free(lex);
351
352         return NULL;
353 }
354
355 static struct ec_node *
356 ec_node_cmd_build(struct ec_node_cmd *node, const char *cmd_str,
357         struct ec_node **table, size_t len)
358 {
359         struct ec_node_cmd_ctx ctx = { table, len };
360         struct ec_parse *p = NULL;
361         void *result;
362         int ret;
363
364         /* parse the command expression */
365         p = ec_node_parse(node->parser, cmd_str);
366         if (p == NULL)
367                 goto fail;
368
369         if (!ec_parse_matches(p)) {
370                 errno = EINVAL;
371                 goto fail;
372         }
373         if (!ec_parse_has_child(p)) {
374                 errno = EINVAL;
375                 goto fail;
376         }
377
378         ret = ec_node_expr_eval(&result, node->expr,
379                                 ec_parse_get_first_child(p),
380                                 &expr_ops, &ctx);
381         if (ret < 0)
382                 goto fail;
383
384         ec_parse_free(p);
385         return result;
386
387 fail:
388         ec_parse_free(p);
389         return NULL;
390 }
391
392 static int
393 ec_node_cmd_parse(const struct ec_node *gen_node, struct ec_parse *state,
394                 const struct ec_strvec *strvec)
395 {
396         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
397
398         return ec_node_parse_child(node->cmd, state, strvec);
399 }
400
401 static int
402 ec_node_cmd_complete(const struct ec_node *gen_node,
403                 struct ec_comp *comp,
404                 const struct ec_strvec *strvec)
405 {
406         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
407
408         return ec_node_complete_child(node->cmd, comp, strvec);
409 }
410
411 static void ec_node_cmd_free_priv(struct ec_node *gen_node)
412 {
413         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
414         size_t i;
415
416         ec_free(node->cmd_str);
417         ec_node_free(node->expr);
418         ec_node_free(node->parser);
419         ec_node_free(node->cmd);
420         for (i = 0; i < node->len; i++)
421                 ec_node_free(node->table[i]);
422         ec_free(node->table);
423 }
424
425 static const struct ec_config_schema ec_node_cmd_subschema[] = {
426         {
427                 .desc = "A child node whose id is referenced in the expression.",
428                 .type = EC_CONFIG_TYPE_NODE,
429         },
430 };
431
432 static const struct ec_config_schema ec_node_cmd_schema[] = {
433         {
434                 .key = "expr",
435                 .desc = "The expression to match. Supported operators "
436                 "are or '|', list ',', many '+', many-or-zero '*', "
437                 "option '[]', group '()'. An identifier (alphanumeric) can "
438                 "reference a node whose node_id matches. Else it is "
439                 "interpreted as ec_node_str() matching this string. "
440                 "Example: command [option] (subset1, subset2) x|y",
441                 .type = EC_CONFIG_TYPE_STRING,
442         },
443         {
444                 .key = "children",
445                 .desc = "The list of children nodes.",
446                 .type = EC_CONFIG_TYPE_LIST,
447                 .subschema = ec_node_cmd_subschema,
448                 .subschema_len = EC_COUNT_OF(ec_node_cmd_subschema),
449         },
450 };
451
452 static int ec_node_cmd_set_config(struct ec_node *gen_node,
453                                 const struct ec_config *config)
454 {
455         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
456         const struct ec_config *expr = NULL, *children = NULL, *child;
457         struct ec_node *cmd = NULL;
458         struct ec_node **table = NULL;
459         char *cmd_str = NULL;
460         size_t n, i;
461
462         /* retrieve config locally */
463         expr = ec_config_dict_get(config, "expr");
464         if (expr == NULL) {
465                 errno = EINVAL;
466                 goto fail;
467         }
468
469         children = ec_config_dict_get(config, "children");
470         if (children == NULL) {
471                 errno = EINVAL;
472                 goto fail;
473         }
474
475         cmd_str = ec_strdup(expr->string);
476         if (cmd_str == NULL)
477                 goto fail;
478
479         n = 0;
480         TAILQ_FOREACH(child, &children->list, next)
481                 n++;
482
483         table = ec_malloc(n * sizeof(*table));
484         if (table == NULL)
485                 goto fail;
486
487         n = 0;
488         TAILQ_FOREACH(child, &children->list, next) {
489                 table[n] = ec_node_clone(child->node);
490                 n++;
491         }
492
493         /* parse expression to build the cmd child node */
494         cmd = ec_node_cmd_build(node, cmd_str, table, n);
495         if (cmd == NULL)
496                 goto fail;
497
498         ec_node_free(node->cmd);
499         node->cmd = cmd;
500         ec_free(node->cmd_str);
501         node->cmd_str = cmd_str;
502         for (i = 0; i < node->len; i++)
503                 ec_node_free(node->table[i]);
504         ec_free(node->table);
505         node->table = table;
506         node->len = n;
507
508         return 0;
509
510 fail:
511         if (table != NULL) {
512                 for (i = 0; i < n; i++)
513                         ec_node_free(table[i]);
514         }
515         ec_free(table);
516         ec_free(cmd_str);
517         ec_node_free(cmd);
518         return -1;
519 }
520
521 static size_t
522 ec_node_cmd_get_children_count(const struct ec_node *gen_node)
523 {
524         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
525
526         if (node->cmd == NULL)
527                 return 0;
528         return 1;
529 }
530
531 static struct ec_node *
532 ec_node_cmd_get_child(const struct ec_node *gen_node, size_t i)
533 {
534         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
535
536         if (i > 0)
537                 return NULL;
538
539         return node->cmd;
540 }
541
542 static struct ec_node_type ec_node_cmd_type = {
543         .name = "cmd",
544         .schema = ec_node_cmd_schema,
545         .schema_len = EC_COUNT_OF(ec_node_cmd_schema),
546         .set_config = ec_node_cmd_set_config,
547         .parse = ec_node_cmd_parse,
548         .complete = ec_node_cmd_complete,
549         .size = sizeof(struct ec_node_cmd),
550         .free_priv = ec_node_cmd_free_priv,
551         .get_children_count = ec_node_cmd_get_children_count,
552         .get_child = ec_node_cmd_get_child,
553 };
554
555 EC_NODE_TYPE_REGISTER(ec_node_cmd_type);
556
557 struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...)
558 {
559         struct ec_config *config = NULL, *children = NULL;
560         struct ec_node *gen_node = NULL;
561         struct ec_node_cmd *node = NULL;
562         struct ec_node *child;
563         va_list ap;
564         int ret;
565
566         va_start(ap, cmd);
567         child = va_arg(ap, struct ec_node *);
568
569         gen_node = __ec_node(&ec_node_cmd_type, id);
570         if (gen_node == NULL)
571                 goto fail_free_children;
572         node = (struct ec_node_cmd *)gen_node;
573
574         node->expr = ec_node_cmd_build_expr();
575         if (node->expr == NULL)
576                 goto fail_free_children;
577
578         node->parser = ec_node_cmd_build_parser(node->expr);
579         if (node->parser == NULL)
580                 goto fail_free_children;
581
582         config = ec_config_dict();
583         if (config == NULL)
584                 goto fail_free_children;
585
586         if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
587                 goto fail_free_children;
588
589         children = ec_config_list();
590         if (children == NULL)
591                 goto fail_free_children;
592
593         for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) {
594                 if (child == NULL)
595                         goto fail_free_children;
596
597                 if (ec_config_list_add(children, ec_config_node(child)) < 0)
598                         goto fail_free_children;
599         }
600
601         if (ec_config_dict_set(config, "children", children) < 0) {
602                 children = NULL; /* freed */
603                 goto fail;
604         }
605         children = NULL;
606
607         ret = ec_node_set_config(gen_node, config);
608         config = NULL; /* freed */
609         if (ret < 0)
610                 goto fail;
611
612         va_end(ap);
613
614         return gen_node;
615
616 fail_free_children:
617         for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *))
618                 ec_node_free(child);
619 fail:
620         ec_node_free(gen_node); /* will also free added children */
621         ec_config_free(children);
622         ec_config_free(config);
623         va_end(ap);
624
625         return NULL;
626 }
627
628 /* LCOV_EXCL_START */
629 static int ec_node_cmd_testcase(void)
630 {
631         struct ec_node *node;
632         int testres = 0;
633
634         node = EC_NODE_CMD(EC_NO_ID,
635                 "command [option] (subset1, subset2, subset3, subset4) x|y z*",
636                 ec_node_int("x", 0, 10, 10),
637                 ec_node_int("y", 20, 30, 10)
638         );
639         if (node == NULL) {
640                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
641                 return -1;
642         }
643         testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "1");
644         testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "subset1", "1");
645         testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "subset3", "subset2",
646                                 "1");
647         testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "subset2", "subset3",
648                                 "subset1", "1");
649         testres |= EC_TEST_CHECK_PARSE(node, 6, "command", "subset3", "subset1",
650                                 "subset4", "subset2", "4");
651         testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "23");
652         testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23");
653         testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "option", "23",
654                                 "z", "z");
655         testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "15");
656         testres |= EC_TEST_CHECK_PARSE(node, -1, "foo");
657         ec_node_free(node);
658
659         node = EC_NODE_CMD(EC_NO_ID, "good morning [count] bob|bobby|michael",
660                         ec_node_int("count", 0, 10, 10));
661         if (node == NULL) {
662                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
663                 return -1;
664         }
665         testres |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob");
666
667         testres |= EC_TEST_CHECK_COMPLETE(node,
668                 "", EC_NODE_ENDLIST,
669                 "good", EC_NODE_ENDLIST);
670         testres |= EC_TEST_CHECK_COMPLETE(node,
671                 "g", EC_NODE_ENDLIST,
672                 "good", EC_NODE_ENDLIST);
673         testres |= EC_TEST_CHECK_COMPLETE(node,
674                 "good", "morning", "", EC_NODE_ENDLIST,
675                 "bob", "bobby", "michael", EC_NODE_ENDLIST);
676
677         ec_node_free(node);
678
679         node = EC_NODE_CMD(EC_NO_ID, "[foo [bar]]");
680         if (node == NULL) {
681                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
682                 return -1;
683         }
684         testres |= EC_TEST_CHECK_PARSE(node, 0);
685         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
686         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
687         testres |= EC_TEST_CHECK_PARSE(node, 0, "x");
688         ec_node_free(node);
689
690         return testres;
691 }
692 /* LCOV_EXCL_STOP */
693
694 static struct ec_test ec_node_cmd_test = {
695         .name = "node_cmd",
696         .test = ec_node_cmd_testcase,
697 };
698
699 EC_TEST_REGISTER(ec_node_cmd_test);