2bf8fc7b20a232c6654f773096e78450e7e1c328
[protos/libecoli.git] / src / ecoli_node_or.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <stdarg.h>
10 #include <errno.h>
11
12 #include <ecoli_malloc.h>
13 #include <ecoli_log.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_node.h>
16 #include <ecoli_parse.h>
17 #include <ecoli_complete.h>
18 #include <ecoli_node.h>
19 #include <ecoli_config.h>
20 #include <ecoli_node_helper.h>
21 #include <ecoli_node_or.h>
22 #include <ecoli_node_str.h>
23 #include <ecoli_test.h>
24
25 EC_LOG_TYPE_REGISTER(node_or);
26
27 struct ec_node_or {
28         struct ec_node gen;
29         struct ec_node **table;
30         size_t len;
31 };
32
33 static int
34 ec_node_or_parse(const struct ec_node *gen_node,
35                 struct ec_parse *state,
36                 const struct ec_strvec *strvec)
37 {
38         struct ec_node_or *node = (struct ec_node_or *)gen_node;
39         unsigned int i;
40         int ret;
41
42         for (i = 0; i < node->len; i++) {
43                 ret = ec_node_parse_child(node->table[i], state, strvec);
44                 if (ret == EC_PARSE_NOMATCH)
45                         continue;
46                 return ret;
47         }
48
49         return EC_PARSE_NOMATCH;
50 }
51
52 static int
53 ec_node_or_complete(const struct ec_node *gen_node,
54                 struct ec_comp *comp,
55                 const struct ec_strvec *strvec)
56 {
57         struct ec_node_or *node = (struct ec_node_or *)gen_node;
58         int ret;
59         size_t n;
60
61         for (n = 0; n < node->len; n++) {
62                 ret = ec_node_complete_child(node->table[n],
63                                         comp, strvec);
64                 if (ret < 0)
65                         return ret;
66         }
67
68         return 0;
69 }
70
71 static void ec_node_or_free_priv(struct ec_node *gen_node)
72 {
73         struct ec_node_or *node = (struct ec_node_or *)gen_node;
74         size_t i;
75
76         for (i = 0; i < node->len; i++)
77                 ec_node_free(node->table[i]);
78         ec_free(node->table);
79         node->table = NULL;
80         node->len = 0;
81 }
82
83 static const struct ec_config_schema ec_node_or_subschema[] = {
84         {
85                 .desc = "A child node which is part of the choice.",
86                 .type = EC_CONFIG_TYPE_NODE,
87         },
88         {
89                 .type = EC_CONFIG_TYPE_NONE,
90         },
91 };
92
93 static const struct ec_config_schema ec_node_or_schema[] = {
94         {
95                 .key = "children",
96                 .desc = "The list of children nodes defining the choice "
97                 "elements.",
98                 .type = EC_CONFIG_TYPE_LIST,
99                 .subschema = ec_node_or_subschema,
100         },
101         {
102                 .type = EC_CONFIG_TYPE_NONE,
103         },
104 };
105
106 static int ec_node_or_set_config(struct ec_node *gen_node,
107                                 const struct ec_config *config)
108 {
109         struct ec_node_or *node = (struct ec_node_or *)gen_node;
110         struct ec_node **table = NULL;
111         size_t i, len = 0;
112
113         table = ec_node_config_node_list_to_table(
114                 ec_config_dict_get(config, "children"), &len);
115         if (table == NULL)
116                 goto fail;
117
118         for (i = 0; i < node->len; i++)
119                 ec_node_free(node->table[i]);
120         ec_free(node->table);
121         node->table = table;
122         node->len = len;
123
124         return 0;
125
126 fail:
127         for (i = 0; i < len; i++)
128                 ec_node_free(table[i]);
129         ec_free(table);
130         return -1;
131 }
132
133 static size_t
134 ec_node_or_get_children_count(const struct ec_node *gen_node)
135 {
136         struct ec_node_or *node = (struct ec_node_or *)gen_node;
137         return node->len;
138 }
139
140 static int
141 ec_node_or_get_child(const struct ec_node *gen_node, size_t i,
142                 struct ec_node **child, unsigned int *refs)
143 {
144         struct ec_node_or *node = (struct ec_node_or *)gen_node;
145
146         if (i >= node->len)
147                 return -1;
148
149         *child = node->table[i];
150         /* each child node is referenced twice: once in the config and
151          * once in the node->table[] */
152         *refs = 2;
153         return 0;
154 }
155
156 static struct ec_node_type ec_node_or_type = {
157         .name = "or",
158         .schema = ec_node_or_schema,
159         .set_config = ec_node_or_set_config,
160         .parse = ec_node_or_parse,
161         .complete = ec_node_or_complete,
162         .size = sizeof(struct ec_node_or),
163         .free_priv = ec_node_or_free_priv,
164         .get_children_count = ec_node_or_get_children_count,
165         .get_child = ec_node_or_get_child,
166 };
167
168 EC_NODE_TYPE_REGISTER(ec_node_or_type);
169
170 int ec_node_or_add(struct ec_node *gen_node, struct ec_node *child)
171 {
172         struct ec_node_or *node = (struct ec_node_or *)gen_node;
173         const struct ec_config *cur_config = NULL;
174         struct ec_config *config = NULL, *children;
175         int ret;
176
177         assert(node != NULL);
178
179         /* XXX factorize this code in a helper */
180
181         if (ec_node_check_type(gen_node, &ec_node_or_type) < 0)
182                 goto fail;
183
184         cur_config = ec_node_get_config(gen_node);
185         if (cur_config == NULL)
186                 config = ec_config_dict();
187         else
188                 config = ec_config_dup(cur_config);
189         if (config == NULL)
190                 goto fail;
191
192         children = ec_config_dict_get(config, "children");
193         if (children == NULL) {
194                 children = ec_config_list();
195                 if (children == NULL)
196                         goto fail;
197
198                 if (ec_config_dict_set(config, "children", children) < 0)
199                         goto fail; /* children list is freed on error */
200         }
201
202         if (ec_config_list_add(children, ec_config_node(child)) < 0) {
203                 child = NULL;
204                 goto fail;
205         }
206
207         ret = ec_node_set_config(gen_node, config);
208         config = NULL; /* freed */
209         if (ret < 0)
210                 goto fail;
211
212         return 0;
213
214 fail:
215         ec_config_free(config);
216         ec_node_free(child);
217         return -1;
218 }
219
220 struct ec_node *__ec_node_or(const char *id, ...)
221 {
222         struct ec_config *config = NULL, *children = NULL;
223         struct ec_node *gen_node = NULL;
224         struct ec_node *child;
225         va_list ap;
226         int ret;
227
228         va_start(ap, id);
229         child = va_arg(ap, struct ec_node *);
230
231         gen_node = ec_node_from_type(&ec_node_or_type, id);
232         if (gen_node == NULL)
233                 goto fail_free_children;
234
235         config = ec_config_dict();
236         if (config == NULL)
237                 goto fail_free_children;
238
239         children = ec_config_list();
240         if (children == NULL)
241                 goto fail_free_children;
242
243         for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *)) {
244                 if (child == NULL)
245                         goto fail_free_children;
246
247                 if (ec_config_list_add(children, ec_config_node(child)) < 0) {
248                         child = NULL;
249                         goto fail_free_children;
250                 }
251         }
252
253         if (ec_config_dict_set(config, "children", children) < 0) {
254                 children = NULL; /* freed */
255                 goto fail;
256         }
257         children = NULL;
258
259         ret = ec_node_set_config(gen_node, config);
260         config = NULL; /* freed */
261         if (ret < 0)
262                 goto fail;
263
264         va_end(ap);
265
266         return gen_node;
267
268 fail_free_children:
269         for (; child != EC_NODE_ENDLIST; child = va_arg(ap, struct ec_node *))
270                 ec_node_free(child);
271 fail:
272         ec_node_free(gen_node); /* will also free added children */
273         ec_config_free(children);
274         ec_config_free(config);
275         va_end(ap);
276
277         return NULL;
278 }
279
280 /* LCOV_EXCL_START */
281 static int ec_node_or_testcase(void)
282 {
283         struct ec_node *node;
284         int testres = 0;
285
286         node = EC_NODE_OR(EC_NO_ID,
287                 ec_node_str(EC_NO_ID, "foo"),
288                 ec_node_str(EC_NO_ID, "bar")
289         );
290         if (node == NULL) {
291                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
292                 return -1;
293         }
294         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
295         testres |= EC_TEST_CHECK_PARSE(node, 1, "bar");
296         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
297         testres |= EC_TEST_CHECK_PARSE(node, -1, " ");
298         testres |= EC_TEST_CHECK_PARSE(node, -1, "foox");
299         testres |= EC_TEST_CHECK_PARSE(node, -1, "toto");
300         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
301         ec_node_free(node);
302
303         /* test completion */
304         node = EC_NODE_OR(EC_NO_ID,
305                 ec_node_str(EC_NO_ID, "foo"),
306                 ec_node_str(EC_NO_ID, "bar"),
307                 ec_node_str(EC_NO_ID, "bar2"),
308                 ec_node_str(EC_NO_ID, "toto"),
309                 ec_node_str(EC_NO_ID, "titi")
310         );
311         if (node == NULL) {
312                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
313                 return -1;
314         }
315         testres |= EC_TEST_CHECK_COMPLETE(node,
316                 "", EC_NODE_ENDLIST,
317                 "foo", "bar", "bar2", "toto", "titi", EC_NODE_ENDLIST);
318         testres |= EC_TEST_CHECK_COMPLETE(node,
319                 "f", EC_NODE_ENDLIST,
320                 "foo", EC_NODE_ENDLIST);
321         testres |= EC_TEST_CHECK_COMPLETE(node,
322                 "b", EC_NODE_ENDLIST,
323                 "bar", "bar2", EC_NODE_ENDLIST);
324         testres |= EC_TEST_CHECK_COMPLETE(node,
325                 "bar", EC_NODE_ENDLIST,
326                 "bar", "bar2", EC_NODE_ENDLIST);
327         testres |= EC_TEST_CHECK_COMPLETE(node,
328                 "t", EC_NODE_ENDLIST,
329                 "toto", "titi", EC_NODE_ENDLIST);
330         testres |= EC_TEST_CHECK_COMPLETE(node,
331                 "to", EC_NODE_ENDLIST,
332                 "toto", EC_NODE_ENDLIST);
333         testres |= EC_TEST_CHECK_COMPLETE(node,
334                 "x", EC_NODE_ENDLIST,
335                 EC_NODE_ENDLIST);
336         ec_node_free(node);
337
338         return testres;
339 }
340 /* LCOV_EXCL_STOP */
341
342 static struct ec_test ec_node_or_test = {
343         .name = "node_or",
344         .test = ec_node_or_testcase,
345 };
346
347 EC_TEST_REGISTER(ec_node_or_test);