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