1750cabd2b25c4cd92b06de46baa6df7d6e3c9c1
[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         for (i = 0; i < node->len; i++)
420                 ec_node_free(node->table[i]);
421         ec_free(node->table);
422 }
423
424 static const struct ec_config_schema ec_node_cmd_subschema[] = {
425         {
426                 .desc = "A child node whose id is referenced in the expression.",
427                 .type = EC_CONFIG_TYPE_NODE,
428         },
429 };
430
431 static const struct ec_config_schema ec_node_cmd_schema[] = {
432         {
433                 .key = "expr",
434                 .desc = "The expression to match. Supported operators "
435                 "are or '|', list ',', many '+', many-or-zero '*', "
436                 "option '[]', group '()'. An identifier (alphanumeric) can "
437                 "reference a node whose node_id matches. Else it is "
438                 "interpreted as ec_node_str() matching this string. "
439                 "Example: command [option] (subset1, subset2) x|y",
440                 .type = EC_CONFIG_TYPE_STRING,
441         },
442         {
443                 .key = "children",
444                 .desc = "The list of children nodes.",
445                 .type = EC_CONFIG_TYPE_LIST,
446                 .subschema = ec_node_cmd_subschema,
447                 .subschema_len = EC_COUNT_OF(ec_node_cmd_subschema),
448         },
449 };
450
451 static int ec_node_cmd_set_config(struct ec_node *gen_node,
452                                 const struct ec_config *config)
453 {
454         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
455         const struct ec_config *expr = NULL, *children = NULL, *child;
456         struct ec_node *cmd = NULL;
457         struct ec_node **table = NULL;
458         char *cmd_str = NULL;
459         size_t n, i;
460
461         /* retrieve config locally */
462         expr = ec_config_dict_get(config, "expr");
463         if (expr == NULL) {
464                 errno = EINVAL;
465                 goto fail;
466         }
467
468         children = ec_config_dict_get(config, "children");
469         if (children == NULL) {
470                 errno = EINVAL;
471                 goto fail;
472         }
473
474         cmd_str = ec_strdup(expr->string);
475         if (cmd_str == NULL)
476                 goto fail;
477
478         n = 0;
479         TAILQ_FOREACH(child, &children->list, next)
480                 n++;
481
482         table = ec_malloc(n * sizeof(*table));
483         if (table == NULL)
484                 goto fail;
485
486         n = 0;
487         TAILQ_FOREACH(child, &children->list, next) {
488                 table[n] = ec_node_clone(child->node);
489                 n++;
490         }
491
492         /* parse expression to build the cmd child node */
493         cmd = ec_node_cmd_build(node, cmd_str, table, n);
494         if (cmd == NULL)
495                 goto fail;
496
497         ec_node_free(node->cmd);
498         node->cmd = cmd;
499         ec_free(node->cmd_str);
500         node->cmd_str = cmd_str;
501         for (i = 0; i < node->len; i++)
502                 ec_node_free(node->table[i]);
503         ec_free(node->table);
504         node->table = table;
505         node->len = n;
506
507         return 0;
508
509 fail:
510         if (table != NULL) {
511                 for (i = 0; i < n; i++)
512                         ec_node_free(table[i]);
513         }
514         ec_free(table);
515         ec_free(cmd_str);
516         ec_node_free(cmd);
517         return -1;
518 }
519
520 static size_t
521 ec_node_cmd_get_children_count(const struct ec_node *gen_node)
522 {
523         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
524
525         if (node->cmd == NULL)
526                 return 0;
527         return 1;
528 }
529
530 static struct ec_node *
531 ec_node_cmd_get_child(const struct ec_node *gen_node, size_t i)
532 {
533         struct ec_node_cmd *node = (struct ec_node_cmd *)gen_node;
534
535         if (i > 0)
536                 return NULL;
537
538         return node->cmd;
539 }
540
541 static struct ec_node_type ec_node_cmd_type = {
542         .name = "cmd",
543         .schema = ec_node_cmd_schema,
544         .schema_len = EC_COUNT_OF(ec_node_cmd_schema),
545         .set_config = ec_node_cmd_set_config,
546         .parse = ec_node_cmd_parse,
547         .complete = ec_node_cmd_complete,
548         .size = sizeof(struct ec_node_cmd),
549         .free_priv = ec_node_cmd_free_priv,
550         .get_children_count = ec_node_cmd_get_children_count,
551         .get_child = ec_node_cmd_get_child,
552 };
553
554 EC_NODE_TYPE_REGISTER(ec_node_cmd_type);
555
556 struct ec_node *__ec_node_cmd(const char *id, const char *cmd, ...)
557 {
558         struct ec_config *config = NULL, *children = NULL;
559         struct ec_node *gen_node = NULL;
560         struct ec_node_cmd *node = NULL;
561         struct ec_node *child;
562         va_list ap;
563         int ret;
564
565         va_start(ap, cmd);
566         child = va_arg(ap, struct ec_node *);
567
568         gen_node = __ec_node(&ec_node_cmd_type, id);
569         if (gen_node == NULL)
570                 goto fail_free_children;
571         node = (struct ec_node_cmd *)gen_node;
572
573         node->expr = ec_node_cmd_build_expr();
574         if (node->expr == NULL)
575                 goto fail_free_children;
576
577         node->parser = ec_node_cmd_build_parser(node->expr);
578         if (node->parser == NULL)
579                 goto fail_free_children;
580
581         config = ec_config_dict();
582         if (config == NULL)
583                 goto fail_free_children;
584
585         if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
586                 goto fail_free_children;
587
588         children = ec_config_list();
589         if (children == NULL)
590                 goto fail_free_children;
591
592         for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) {
593                 if (child == NULL)
594                         goto fail_free_children;
595
596                 if (ec_config_list_add(children, ec_config_node(child)) < 0)
597                         goto fail_free_children;
598         }
599
600         if (ec_config_dict_set(config, "children", children) < 0) {
601                 children = NULL; /* freed */
602                 goto fail;
603         }
604         children = NULL;
605
606         ret = ec_node_set_config(gen_node, config);
607         config = NULL; /* freed */
608         if (ret < 0)
609                 goto fail;
610
611         va_end(ap);
612
613         return gen_node;
614
615 fail_free_children:
616         for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *))
617                 ec_node_free(child);
618 fail:
619         ec_node_free(gen_node); /* will also free added children */
620         ec_config_free(children);
621         ec_config_free(config);
622         va_end(ap);
623
624         return NULL;
625 }
626
627 /* LCOV_EXCL_START */
628 static int ec_node_cmd_testcase(void)
629 {
630         struct ec_node *node;
631         int testres = 0;
632
633         node = EC_NODE_CMD(EC_NO_ID,
634                 "command [option] (subset1, subset2, subset3, subset4) x|y z*",
635                 ec_node_int("x", 0, 10, 10),
636                 ec_node_int("y", 20, 30, 10)
637         );
638         if (node == NULL) {
639                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
640                 return -1;
641         }
642         testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "1");
643         testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "subset1", "1");
644         testres |= EC_TEST_CHECK_PARSE(node, 4, "command", "subset3", "subset2",
645                                 "1");
646         testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "subset2", "subset3",
647                                 "subset1", "1");
648         testres |= EC_TEST_CHECK_PARSE(node, 6, "command", "subset3", "subset1",
649                                 "subset4", "subset2", "4");
650         testres |= EC_TEST_CHECK_PARSE(node, 2, "command", "23");
651         testres |= EC_TEST_CHECK_PARSE(node, 3, "command", "option", "23");
652         testres |= EC_TEST_CHECK_PARSE(node, 5, "command", "option", "23",
653                                 "z", "z");
654         testres |= EC_TEST_CHECK_PARSE(node, -1, "command", "15");
655         testres |= EC_TEST_CHECK_PARSE(node, -1, "foo");
656         ec_node_free(node);
657
658         node = EC_NODE_CMD(EC_NO_ID, "good morning [count] bob|bobby|michael",
659                         ec_node_int("count", 0, 10, 10));
660         if (node == NULL) {
661                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
662                 return -1;
663         }
664         testres |= EC_TEST_CHECK_PARSE(node, 4, "good", "morning", "1", "bob");
665
666         testres |= EC_TEST_CHECK_COMPLETE(node,
667                 "", EC_NODE_ENDLIST,
668                 "good", EC_NODE_ENDLIST);
669         testres |= EC_TEST_CHECK_COMPLETE(node,
670                 "g", EC_NODE_ENDLIST,
671                 "good", EC_NODE_ENDLIST);
672         testres |= EC_TEST_CHECK_COMPLETE(node,
673                 "good", "morning", "", EC_NODE_ENDLIST,
674                 "bob", "bobby", "michael", EC_NODE_ENDLIST);
675
676         ec_node_free(node);
677
678         node = EC_NODE_CMD(EC_NO_ID, "[foo [bar]]");
679         if (node == NULL) {
680                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
681                 return -1;
682         }
683         testres |= EC_TEST_CHECK_PARSE(node, 0);
684         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
685         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
686         testres |= EC_TEST_CHECK_PARSE(node, 0, "x");
687         ec_node_free(node);
688
689         return testres;
690 }
691 /* LCOV_EXCL_STOP */
692
693 static struct ec_test ec_node_cmd_test = {
694         .name = "node_cmd",
695         .test = ec_node_cmd_testcase,
696 };
697
698 EC_TEST_REGISTER(ec_node_cmd_test);