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