9c7c071a1236bad5de97fca7196369d3cc3ae7a8
[protos/libecoli.git] / src / ecoli_node_cond.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019, 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_string.h>
20 #include <ecoli_node.h>
21 #include <ecoli_config.h>
22 #include <ecoli_dict.h>
23 #include <ecoli_htable.h>
24 #include <ecoli_parse.h>
25 #include <ecoli_complete.h>
26 #include <ecoli_node_str.h>
27 #include <ecoli_node_re.h>
28 #include <ecoli_node_many.h>
29 #include <ecoli_node_subset.h>
30 #include <ecoli_node_seq.h>
31 #include <ecoli_node_or.h>
32 #include <ecoli_node_option.h>
33 #include <ecoli_node_any.h>
34 #include <ecoli_node_many.h>
35 #include <ecoli_node_bypass.h>
36 #include <ecoli_node_re_lex.h>
37 #include <ecoli_node_cond.h>
38
39 EC_LOG_TYPE_REGISTER(node_cond);
40
41 static struct ec_node *ec_node_cond_parser; /* the expression parser. */
42 static struct ec_dict *ec_node_cond_functions; /* functions dictionary */
43
44 struct ec_node_cond {
45         struct ec_node gen;
46         char *cond_str;                /* the condition string. */
47         struct ec_parse *parsed_cond;  /* the parsed condition. */
48         struct ec_node *child;         /* the child node. */
49 };
50
51 enum cond_result_type {
52         NODESET,
53         BOOLEAN,
54         INT,
55         STR,
56 };
57
58 /*
59  * XXX missing:
60  * - find by attrs? get_attr ?
61  * - get/set variable
62  */
63
64 struct cond_result {
65         enum cond_result_type type;
66         union {
67                 struct ec_htable *htable;
68                 char *str;
69                 int64_t int64;
70                 bool boolean;
71         };
72 };
73
74 typedef struct cond_result *(cond_func_t)(
75         const struct ec_parse *state,
76         struct cond_result **in, size_t in_len);
77
78 void cond_result_free(struct cond_result *res)
79 {
80         if (res == NULL)
81                 return;
82
83         switch (res->type) {
84         case NODESET:
85                 ec_htable_free(res->htable);
86                 break;
87         case STR:
88                 ec_free(res->str);
89                 break;
90         case BOOLEAN:
91         case INT:
92                 break;
93         }
94
95         ec_free(res);
96 }
97
98 void cond_result_table_free(struct cond_result **table, size_t len)
99 {
100         size_t i;
101
102         for (i = 0; i < len; i++) {
103                 cond_result_free(table[i]);
104                 table[i] = NULL;
105         }
106         ec_free(table);
107 }
108
109 static struct ec_node *
110 ec_node_cond_build_parser(void)
111 {
112         struct ec_node *lex = NULL;
113         struct ec_node *expr = NULL;
114         int ret;
115
116         expr = ec_node("or", "id_arg");
117         if (expr == NULL)
118                 goto fail;
119
120         if (ec_node_or_add(expr,
121                 EC_NODE_SEQ("id_function",
122                         ec_node_any("id_function_name", "a_identifier"),
123                         ec_node_any(EC_NO_ID, "a_open"),
124                         ec_node_option("id_arg_list",
125                                 EC_NODE_SEQ(EC_NO_ID,
126                                         ec_node_clone(expr),
127                                         ec_node_many(EC_NO_ID,
128                                                 EC_NODE_SEQ(EC_NO_ID,
129                                                         ec_node_str(EC_NO_ID,
130                                                                 ","),
131                                                         ec_node_clone(expr)),
132                                         0, 0))),
133                         ec_node_any(EC_NO_ID, "a_close"))) < 0)
134                 goto fail;
135
136         if (ec_node_or_add(expr,
137                         ec_node_any("id_value_str", "a_identifier")) < 0)
138                 goto fail;
139
140         if (ec_node_or_add(expr,
141                         ec_node_any("id_value_int", "a_int")) < 0)
142                 goto fail;
143
144         /* prepend a lexer to the expression node */
145         lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
146         if (lex == NULL)
147                 goto fail;
148
149         ec_node_free(expr);
150         expr = NULL;
151
152         ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1,
153                                 "a_identifier");
154         if (ret < 0)
155                 goto fail;
156         ret = ec_node_re_lex_add(lex, "[0-9]+", 1, "a_int");
157         if (ret < 0)
158                 goto fail;
159         ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open");
160         if (ret < 0)
161                 goto fail;
162         ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close");
163         if (ret < 0)
164                 goto fail;
165         ret = ec_node_re_lex_add(lex, ",", 1, NULL);
166         if (ret < 0)
167                 goto fail;
168         ret = ec_node_re_lex_add(lex, "[        ]", 0, NULL);
169         if (ret < 0)
170                 goto fail;
171
172         return lex;
173
174 fail:
175         ec_node_free(lex);
176         ec_node_free(expr);
177
178         return NULL;
179 }
180
181 static struct ec_parse *
182 ec_node_cond_build(const char *cond_str)
183 {
184         struct ec_parse *p = NULL;
185
186         /* parse the condition expression */
187         p = ec_node_parse(ec_node_cond_parser, cond_str);
188         if (p == NULL)
189                 goto fail;
190
191         if (!ec_parse_matches(p)) {
192                 errno = EINVAL;
193                 goto fail;
194         }
195         if (!ec_parse_has_child(p)) {
196                 errno = EINVAL;
197                 goto fail;
198         }
199
200         return p;
201
202 fail:
203         ec_parse_free(p);
204         return NULL;
205 }
206
207 static struct cond_result *
208 eval_root(const struct ec_parse *state, struct cond_result **in, size_t in_len)
209 {
210         struct cond_result *out = NULL;
211         const struct ec_parse *root = NULL;
212
213         (void)in;
214
215         if (in_len != 0) {
216                 EC_LOG(LOG_ERR, "root() does not take any argument\n");
217                 errno = EINVAL;
218                 goto fail;
219         }
220
221         out = ec_malloc(sizeof(*out));
222         if (out == NULL)
223                 goto fail;
224
225         out->type = NODESET;
226         out->htable = ec_htable();
227         if (out->htable == NULL)
228                 goto fail;
229
230         root = ec_parse_get_root(state);
231         if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
232                 goto fail;
233
234         cond_result_table_free(in, in_len);
235         return out;
236
237 fail:
238         cond_result_free(out);
239         cond_result_table_free(in, in_len);
240         return NULL;
241 }
242
243 static struct cond_result *
244 eval_current(const struct ec_parse *state, struct cond_result **in,
245         size_t in_len)
246 {
247         struct cond_result *out = NULL;
248
249         (void)in;
250
251         if (in_len != 0) {
252                 EC_LOG(LOG_ERR, "current() does not take any argument\n");
253                 errno = EINVAL;
254                 goto fail;
255         }
256
257         out = ec_malloc(sizeof(*out));
258         if (out == NULL)
259                 goto fail;
260
261         out->type = NODESET;
262         out->htable = ec_htable();
263         if (out->htable == NULL)
264                 goto fail;
265
266         if (ec_htable_set(out->htable, &state, sizeof(state), NULL, NULL) < 0)
267                 goto fail;
268
269         cond_result_table_free(in, in_len);
270         return out;
271
272 fail:
273         cond_result_free(out);
274         cond_result_table_free(in, in_len);
275         return NULL;
276 }
277
278 static bool
279 boolean_value(const struct cond_result *res)
280 {
281         switch (res->type) {
282         case NODESET:
283                 return (ec_htable_len(res->htable) > 0);
284         case BOOLEAN:
285                 return res->boolean;
286         case INT:
287                 return (res->int64 != 0);
288         case STR:
289                 return (res->str[0] != 0);
290         }
291
292         return false;
293 }
294
295 static struct cond_result *
296 eval_bool(const struct ec_parse *state, struct cond_result **in, size_t in_len)
297 {
298         struct cond_result *out = NULL;
299
300         (void)state;
301
302         if (in_len != 1) {
303                 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
304                 errno = EINVAL;
305                 goto fail;
306         }
307
308         out = ec_malloc(sizeof(*out));
309         if (out == NULL)
310                 goto fail;
311
312         out->type = BOOLEAN;
313         out->boolean = boolean_value(in[0]);
314
315         cond_result_table_free(in, in_len);
316         return out;
317
318 fail:
319         cond_result_free(out);
320         cond_result_table_free(in, in_len);
321         return NULL;
322 }
323
324 static struct cond_result *
325 eval_or(const struct ec_parse *state, struct cond_result **in, size_t in_len)
326 {
327         struct cond_result *out = NULL;
328         size_t i;
329
330         (void)state;
331
332         if (in_len < 2) {
333                 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
334                 errno = EINVAL;
335                 goto fail;
336         }
337
338         /* return the first true element, or the last one */
339         for (i = 0; i < in_len; i++) {
340                 if (boolean_value(in[i]))
341                         break;
342         }
343         if (i == in_len)
344                 i--;
345
346         out = in[i];
347         in[i] = NULL;
348
349         cond_result_table_free(in, in_len);
350         return out;
351
352 fail:
353         cond_result_free(out);
354         cond_result_table_free(in, in_len);
355         return NULL;
356 }
357
358 static struct cond_result *
359 eval_and(const struct ec_parse *state, struct cond_result **in, size_t in_len)
360 {
361         struct cond_result *out = NULL;
362         size_t i;
363
364         (void)state;
365
366         if (in_len < 2) {
367                 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
368                 errno = EINVAL;
369                 goto fail;
370         }
371
372         /* return the first false element, or the last one */
373         for (i = 0; i < in_len; i++) {
374                 if (!boolean_value(in[i]))
375                         break;
376         }
377         if (i == in_len)
378                 i--;
379
380         out = in[i];
381         in[i] = NULL;
382
383         cond_result_table_free(in, in_len);
384         return out;
385
386 fail:
387         cond_result_free(out);
388         cond_result_table_free(in, in_len);
389         return NULL;
390 }
391
392 static struct cond_result *
393 eval_first_child(const struct ec_parse *state, struct cond_result **in,
394         size_t in_len)
395 {
396         struct cond_result *out = NULL;
397         struct ec_htable_elt_ref *iter;
398         const struct ec_parse * const *pparse;
399         struct ec_parse *parse;
400
401         (void)state;
402
403         if (in_len != 1 || in[0]->type != NODESET) {
404                 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
405                 errno = EINVAL;
406                 goto fail;
407         }
408
409         out = ec_malloc(sizeof(*out));
410         if (out == NULL)
411                 goto fail;
412
413         out->type = NODESET;
414         out->htable = ec_htable();
415         if (out->htable == NULL)
416                 goto fail;
417
418         for (iter = ec_htable_iter(in[0]->htable);
419              iter != NULL; iter = ec_htable_iter_next(iter)) {
420                 pparse = ec_htable_iter_get_key(iter);
421                 parse = ec_parse_get_first_child(*pparse);
422                 if (parse == NULL)
423                         continue;
424                 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
425                                         NULL) < 0)
426                         goto fail;
427         }
428
429         cond_result_table_free(in, in_len);
430         return out;
431
432 fail:
433         cond_result_free(out);
434         cond_result_table_free(in, in_len);
435         return NULL;
436 }
437
438 static struct cond_result *
439 eval_find(const struct ec_parse *state, struct cond_result **in,
440         size_t in_len)
441 {
442         struct cond_result *out = NULL;
443         struct ec_htable_elt_ref *iter;
444         struct ec_parse * const *pparse;
445         struct ec_parse *parse;
446         const char *id;
447
448         (void)state;
449
450         if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
451                 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
452                 errno = EINVAL;
453                 goto fail;
454         }
455
456         out = ec_malloc(sizeof(*out));
457         if (out == NULL)
458                 goto fail;
459
460         out->type = NODESET;
461         out->htable = ec_htable();
462         if (out->htable == NULL)
463                 goto fail;
464
465         id = in[1]->str;
466         for (iter = ec_htable_iter(in[0]->htable);
467              iter != NULL; iter = ec_htable_iter_next(iter)) {
468                 pparse = ec_htable_iter_get_key(iter);
469                 parse = ec_parse_find(*pparse, id);
470                 while (parse != NULL) {
471                         if (ec_htable_set(out->htable, &parse,
472                                                 sizeof(parse), NULL,
473                                                 NULL) < 0)
474                                 goto fail;
475                         parse = ec_parse_find_next(*pparse, parse, id, 1);
476                 }
477         }
478
479         cond_result_table_free(in, in_len);
480         return out;
481
482 fail:
483         cond_result_free(out);
484         cond_result_table_free(in, in_len);
485         return NULL;
486 }
487
488 static struct cond_result *
489 eval_cmp(const struct ec_parse *state, struct cond_result **in,
490         size_t in_len)
491 {
492         struct cond_result *out = NULL;
493         struct ec_htable_elt_ref *iter;
494         bool eq = false, gt = false;
495
496         (void)state;
497
498         if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) {
499                 EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n");
500                 errno = EINVAL;
501                 goto fail;
502         }
503
504         if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
505                         strcmp(in[0]->str, "gt") && strcmp(in[0]->str, "lt") &&
506                         strcmp(in[0]->str, "ge") && strcmp(in[0]->str, "le")) {
507                 EC_LOG(LOG_ERR, "invalid comparison operator in cmp().\n");
508                 errno = EINVAL;
509                 goto fail;
510         }
511
512         if (strcmp(in[0]->str, "eq") && strcmp(in[0]->str, "ne") &&
513                         in[1]->type != INT) {
514                 EC_LOG(LOG_ERR, "cmp(gt|lt|ge|le, ...) is only allowed with integers.\n");
515                 errno = EINVAL;
516                 goto fail;
517         }
518
519         if (in[1]->type == INT) {
520                 eq = in[1]->int64 == in[2]->int64;
521                 gt = in[1]->int64 > in[2]->int64;
522         } else if (in[1]->type == NODESET &&
523                         ec_htable_len(in[1]->htable) !=
524                         ec_htable_len(in[2]->htable)) {
525                 eq = false;
526         } else if (in[1]->type == NODESET) {
527                 eq = true;
528                 for (iter = ec_htable_iter(in[1]->htable);
529                      iter != NULL; iter = ec_htable_iter_next(iter)) {
530                         if (ec_htable_get(
531                                         in[2]->htable,
532                                         ec_htable_iter_get_key(iter),
533                                         sizeof(struct ec_parse *)) == NULL) {
534                                 eq = false;
535                                 break;
536                         }
537                 }
538         } else if (in[1]->type == STR) {
539                 eq = !strcmp(in[1]->str, in[2]->str);;
540         } else if (in[1]->type == BOOLEAN) {
541                 eq = in[1]->boolean == in[2]->boolean;
542         }
543
544         out = ec_malloc(sizeof(*out));
545         if (out == NULL)
546                 goto fail;
547
548         out->type = BOOLEAN;
549         if (!strcmp(in[0]->str, "eq"))
550                 out->boolean = eq;
551         else if (!strcmp(in[0]->str, "ne"))
552                 out->boolean = !eq;
553         else if (!strcmp(in[0]->str, "lt"))
554                 out->boolean = !gt && !eq;
555         else if (!strcmp(in[0]->str, "gt"))
556                 out->boolean = gt && !eq;
557         else if (!strcmp(in[0]->str, "le"))
558                 out->boolean = !gt || eq;
559         else if (!strcmp(in[0]->str, "ge"))
560                 out->boolean = gt || eq;
561
562         cond_result_table_free(in, in_len);
563         return out;
564
565 fail:
566         cond_result_free(out);
567         cond_result_table_free(in, in_len);
568         return NULL;
569 }
570
571 static struct cond_result *
572 eval_count(const struct ec_parse *state, struct cond_result **in, size_t in_len)
573 {
574         struct cond_result *out = NULL;
575
576         (void)state;
577
578         if (in_len != 1 || in[0]->type != NODESET) {
579                 EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n");
580                 errno = EINVAL;
581                 goto fail;
582         }
583
584         out = ec_malloc(sizeof(*out));
585         if (out == NULL)
586                 goto fail;
587
588         out->type = INT;
589         out->int64 = ec_htable_len(in[0]->htable);
590
591         cond_result_table_free(in, in_len);
592         return out;
593
594 fail:
595         cond_result_free(out);
596         cond_result_table_free(in, in_len);
597         return NULL;
598 }
599
600 static struct cond_result *
601 eval_func(const char *name, const struct ec_parse *state,
602         struct cond_result **in, size_t in_len)
603 {
604         cond_func_t *f;
605
606         f = ec_dict_get(ec_node_cond_functions, name);
607         if (f == NULL) {
608                 EC_LOG(LOG_ERR, "No such function <%s>\n",
609                         name);
610                 errno = ENOENT;
611                 cond_result_table_free(in, in_len);
612                 return NULL;
613         }
614
615         return f(state, in, in_len);
616 }
617
618
619 static struct cond_result *
620 eval_condition(const struct ec_parse *cond, const struct ec_parse *state)
621 {
622         const struct ec_parse *iter;
623         struct cond_result *res = NULL;
624         struct cond_result **args = NULL;
625         const struct ec_parse *func = NULL, *func_name = NULL, *arg_list = NULL;
626         const struct ec_parse *value = NULL;
627         const char *id;
628         size_t n_arg = 0;
629
630         // XXX fix cast (x3)
631         func = ec_parse_find((void *)cond, "id_function");
632         if (func != NULL) {
633                 EC_PARSE_FOREACH_CHILD(iter, func) {
634                         id = ec_node_id(ec_parse_get_node(iter));
635                         if (!strcmp(id, "id_function_name"))
636                                 func_name = iter;
637                         if (!strcmp(id, "id_arg_list"))
638                                 arg_list = iter;
639                 }
640
641                 iter = ec_parse_find((void *)arg_list, "id_arg");
642                 while (iter != NULL) {
643                         args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
644                         args[n_arg] = eval_condition(iter, state);
645                         if (args[n_arg] == NULL)
646                                 goto fail;
647                         n_arg++;
648                         iter = ec_parse_find_next((void *)arg_list,
649                                                 (void *)iter, "id_arg", 0);
650                 }
651
652                 res = eval_func(ec_strvec_val(ec_parse_strvec(func_name), 0),
653                                 state, args, n_arg);
654                 args = NULL;
655                 return res;
656         }
657
658         value = ec_parse_find((void *)cond, "id_value_str");
659         if (value != NULL) {
660                 res = ec_malloc(sizeof(*res));
661                 if (res == NULL)
662                         goto fail;
663                 res->type = STR;
664                 res->str = ec_strdup(ec_strvec_val(ec_parse_strvec(value), 0));
665                 if (res->str == NULL)
666                         goto fail;
667                 return res;
668         }
669
670         value = ec_parse_find((void *)cond, "id_value_int");
671         if (value != NULL) {
672                 res = ec_malloc(sizeof(*res));
673                 if (res == NULL)
674                         goto fail;
675                 res->type = INT;
676                 if (ec_str_parse_llint(ec_strvec_val(ec_parse_strvec(value), 0),
677                                         0, LLONG_MIN, LLONG_MAX,
678                                         &res->int64) < 0)
679                         goto fail;
680                 return res;
681         }
682
683 fail:
684         cond_result_free(res);
685         cond_result_table_free(args, n_arg);
686         return NULL;
687 }
688
689 static int
690 validate_condition(const struct ec_parse *cond, const struct ec_parse *state)
691 {
692         struct cond_result *res;
693         int ret;
694
695         res = eval_condition(cond, state);
696         if (res == NULL)
697                 return -1;
698
699         ret = boolean_value(res);
700         cond_result_free(res);
701
702         return ret;
703 }
704
705 static int
706 ec_node_cond_parse(const struct ec_node *gen_node, struct ec_parse *state,
707                 const struct ec_strvec *strvec)
708 {
709         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
710         struct ec_parse *child;
711         int ret, valid;
712
713         ret = ec_node_parse_child(node->child, state, strvec);
714         if (ret <= 0)
715                 return ret;
716
717         valid = validate_condition(node->parsed_cond, state);
718         if (valid < 0)
719                 return valid;
720
721         if (valid == 0) {
722                 child = ec_parse_get_last_child(state);
723                 ec_parse_unlink_child(state, child);
724                 ec_parse_free(child);
725                 return EC_PARSE_NOMATCH;
726         }
727
728         return ret;
729 }
730
731 static int
732 ec_node_cond_complete(const struct ec_node *gen_node,
733                 struct ec_comp *comp,
734                 const struct ec_strvec *strvec)
735 {
736         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
737
738         // XXX eval condition
739         // XXX before or after completing ? configurable ?
740
741         return ec_node_complete_child(node->child, comp, strvec);
742 }
743
744 static void ec_node_cond_free_priv(struct ec_node *gen_node)
745 {
746         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
747
748         ec_free(node->cond_str);
749         node->cond_str = NULL;
750         ec_parse_free(node->parsed_cond);
751         node->parsed_cond = NULL;
752         ec_node_free(node->child);
753 }
754
755 static const struct ec_config_schema ec_node_cond_schema[] = {
756         {
757                 .key = "expr",
758                 .desc = "XXX",
759                 .type = EC_CONFIG_TYPE_STRING,
760         },
761         {
762                 .key = "child",
763                 .desc = "The child node.",
764                 .type = EC_CONFIG_TYPE_NODE,
765         },
766         {
767                 .type = EC_CONFIG_TYPE_NONE,
768         },
769 };
770
771 static int ec_node_cond_set_config(struct ec_node *gen_node,
772                                 const struct ec_config *config)
773 {
774         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
775         const struct ec_config *cond = NULL;
776         struct ec_parse *parsed_cond = NULL;
777         const struct ec_config *child;
778         char *cond_str = NULL;
779
780         cond = ec_config_dict_get(config, "expr");
781         if (cond == NULL) {
782                 errno = EINVAL;
783                 goto fail;
784         }
785
786         cond_str = ec_strdup(cond->string);
787         if (cond_str == NULL)
788                 goto fail;
789
790         child = ec_config_dict_get(config, "child");
791         if (child == NULL)
792                 goto fail;
793
794         /* parse expression to build the cmd child node */
795         parsed_cond = ec_node_cond_build(cond_str);
796         if (parsed_cond == NULL)
797                 goto fail;
798
799         /* ok, store the config */
800         ec_parse_free(node->parsed_cond);
801         node->parsed_cond = parsed_cond;
802         ec_free(node->cond_str);
803         node->cond_str = cond_str;
804         ec_node_free(node->child);
805         node->child = ec_node_clone(child->node);
806
807         return 0;
808
809 fail:
810         ec_parse_free(parsed_cond);
811         ec_free(cond_str);
812         return -1;
813 }
814
815 static size_t
816 ec_node_cond_get_children_count(const struct ec_node *gen_node)
817 {
818         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
819
820         if (node->child == NULL)
821                 return 0;
822         return 1;
823 }
824
825 static int
826 ec_node_cond_get_child(const struct ec_node *gen_node, size_t i,
827                 struct ec_node **child, unsigned int *refs)
828 {
829         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
830
831         if (i > 0)
832                 return -1;
833
834         *child = node->child;
835         *refs = 1;
836         return 0;
837 }
838
839 static struct ec_node_type ec_node_cond_type = {
840         .name = "cond",
841         .schema = ec_node_cond_schema,
842         .set_config = ec_node_cond_set_config,
843         .parse = ec_node_cond_parse,
844         .complete = ec_node_cond_complete,
845         .size = sizeof(struct ec_node_cond),
846         .free_priv = ec_node_cond_free_priv,
847         .get_children_count = ec_node_cond_get_children_count,
848         .get_child = ec_node_cond_get_child,
849 };
850
851 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
852
853 struct ec_node *ec_node_cond(const char *id, const char *cmd,
854                         struct ec_node *child)
855 {
856         struct ec_config *config = NULL;
857         struct ec_node *gen_node = NULL;
858         int ret;
859
860         if (child == NULL)
861                 return NULL;
862
863         gen_node = ec_node_from_type(&ec_node_cond_type, id);
864         if (gen_node == NULL)
865                 goto fail;
866
867         config = ec_config_dict();
868         if (config == NULL)
869                 goto fail;
870
871         if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
872                 goto fail;
873
874         if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
875                 child = NULL; /* freed */
876                 goto fail;
877         }
878         child = NULL;
879
880         ret = ec_node_set_config(gen_node, config);
881         config = NULL; /* freed */
882         if (ret < 0)
883                 goto fail;
884
885         return gen_node;
886
887 fail:
888         ec_node_free(gen_node);
889         ec_node_free(child);
890         ec_config_free(config);
891
892         return NULL;
893 }
894
895 static void ec_node_cond_exit_func(void)
896 {
897         ec_node_free(ec_node_cond_parser);
898         ec_node_cond_parser = NULL;
899         ec_dict_free(ec_node_cond_functions);
900         ec_node_cond_functions = NULL;
901 }
902
903 static int add_func(const char *name, cond_func_t *f)
904 {
905         return ec_dict_set(ec_node_cond_functions, name, f, NULL);
906 }
907
908 static int ec_node_cond_init_func(void)
909 {
910         ec_node_cond_parser = ec_node_cond_build_parser();
911         if (ec_node_cond_parser == NULL)
912                 goto fail;
913
914         ec_node_cond_functions = ec_dict();
915         if (ec_node_cond_functions == NULL)
916                 goto fail;
917
918         if (add_func("root", eval_root) < 0)
919                 goto fail;
920         if (add_func("current", eval_current) < 0)
921                 goto fail;
922         if (add_func("bool", eval_bool) < 0)
923                 goto fail;
924         if (add_func("or", eval_or) < 0)
925                 goto fail;
926         if (add_func("and", eval_and) < 0)
927                 goto fail;
928         if (add_func("first_child", eval_first_child) < 0)
929                 goto fail;
930         if (add_func("find", eval_find) < 0)
931                 goto fail;
932         if (add_func("cmp", eval_cmp) < 0)
933                 goto fail;
934         if (add_func("count", eval_count) < 0)
935                 goto fail;
936
937         return 0;
938
939 fail:
940         EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
941         ec_node_cond_exit_func();
942         return -1;
943 }
944
945 static struct ec_init ec_node_cond_init = {
946         .init = ec_node_cond_init_func,
947         .exit = ec_node_cond_exit_func,
948         .priority = 75,
949 };
950
951 EC_INIT_REGISTER(ec_node_cond_init);
952
953 /* LCOV_EXCL_START */
954 static int ec_node_cond_testcase(void)
955 {
956         struct ec_node *node;
957         int testres = 0;
958
959         if (0) {
960         node =  EC_NODE_SEQ(EC_NO_ID,
961                         EC_NODE_SUBSET(EC_NO_ID,
962                                 ec_node_str("id_node1", "node1"),
963                                 ec_node_str("id_node2", "node2"),
964                                 ec_node_str("id_node3", "node3"),
965                                 ec_node_str("id_node4", "node4")),
966                         ec_node_cond(EC_NO_ID,
967                                 "or(find(root(), id_node1), "
968                                 "  and(find(root(), id_node2),"
969                                 "    find(root(), id_node3)))",
970                                 ec_node_str(EC_NO_ID, "ok")));
971         if (node == NULL) {
972                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
973                 return -1;
974         }
975         testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
976         testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
977         testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
978         testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
979         testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
980         testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
981         testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
982         testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
983         ec_node_free(node);
984         }
985         node =  ec_node_cond(EC_NO_ID,
986                         "cmp(le, count(find(root(), id_node)), 3)",
987                         ec_node_many(EC_NO_ID,
988                                 ec_node_str("id_node", "foo"), 0, 0));
989         if (node == NULL) {
990                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
991                 return -1;
992         }
993         testres |= EC_TEST_CHECK_PARSE(node, 0);
994         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
995         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo");
996         testres |= EC_TEST_CHECK_PARSE(node, 3, "foo", "foo", "foo");
997         testres |= EC_TEST_CHECK_PARSE(node, -1, "foo", "foo", "foo", "foo");
998         ec_node_free(node);
999
1000         // XXX test completion
1001
1002         return testres;
1003 }
1004 /* LCOV_EXCL_STOP */
1005
1006 static struct ec_test ec_node_cond_test = {
1007         .name = "node_cond",
1008         .test = ec_node_cond_testcase,
1009 };
1010
1011 EC_TEST_REGISTER(ec_node_cond_test);