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