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