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