8c0a2de9dfda04007ffca76af16d6a81bab12ce7
[protos/libecoli.git] / lib / ecoli_node_many.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 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_test.h>
15 #include <ecoli_strvec.h>
16 #include <ecoli_node.h>
17 #include <ecoli_parsed.h>
18 #include <ecoli_completed.h>
19 #include <ecoli_node_str.h>
20 #include <ecoli_node_option.h>
21 #include <ecoli_node_many.h>
22
23 EC_LOG_TYPE_REGISTER(node_many);
24
25 struct ec_node_many {
26         struct ec_node gen;
27         unsigned int min;
28         unsigned int max;
29         struct ec_node *child;
30 };
31
32 static int ec_node_many_parse(const struct ec_node *gen_node,
33                         struct ec_parsed *state,
34                         const struct ec_strvec *strvec)
35 {
36         struct ec_node_many *node = (struct ec_node_many *)gen_node;
37         struct ec_parsed *child_parsed;
38         struct ec_strvec *childvec = NULL;
39         size_t off = 0, count;
40         int ret;
41
42         for (count = 0; node->max == 0 || count < node->max; count++) {
43                 childvec = ec_strvec_ndup(strvec, off,
44                         ec_strvec_len(strvec) - off);
45                 if (childvec == NULL) {
46                         ret = -ENOMEM;
47                         goto fail;
48                 }
49
50                 ret = ec_node_parse_child(node->child, state, childvec);
51                 if (ret < 0)
52                         goto fail;
53
54                 ec_strvec_free(childvec);
55                 childvec = NULL;
56
57                 if (ret == EC_PARSED_NOMATCH)
58                         break;
59
60                 /* it matches an empty strvec, no need to continue */
61                 if (ret == 0) {
62                         child_parsed = ec_parsed_get_last_child(state);
63                         ec_parsed_del_child(state, child_parsed);
64                         ec_parsed_free(child_parsed);
65                         break;
66                 }
67
68                 off += ret;
69         }
70
71         if (count < node->min) {
72                 ec_parsed_free_children(state);
73                 return EC_PARSED_NOMATCH;
74         }
75
76         return off;
77
78 fail:
79         ec_strvec_free(childvec);
80         return ret;
81 }
82
83 static int
84 __ec_node_many_complete(struct ec_node_many *node, unsigned int max,
85                         struct ec_completed *completed,
86                         const struct ec_strvec *strvec)
87 {
88         struct ec_parsed *parsed = ec_completed_get_state(completed);
89         struct ec_strvec *childvec = NULL;
90         unsigned int i;
91         int ret;
92
93         /* first, try to complete with the child node */
94         ret = ec_node_complete_child(node->child, completed, strvec);
95         if (ret < 0)
96                 goto fail;
97
98         /* we're done, we reached the max number of nodes */
99         if (max == 1)
100                 return 0;
101
102         /* if there is a maximum, decrease it before recursion */
103         if (max != 0)
104                 max--;
105
106         /* then, if the node matches the beginning of the strvec, try to
107          * complete the rest */
108         for (i = 0; i < ec_strvec_len(strvec); i++) {
109                 childvec = ec_strvec_ndup(strvec, 0, i);
110                 if (childvec == NULL)
111                         goto fail;
112
113                 ret = ec_node_parse_child(node->child, parsed, childvec);
114                 if (ret < 0)
115                         goto fail;
116
117                 ec_strvec_free(childvec);
118                 childvec = NULL;
119
120                 if ((unsigned int)ret != i) {
121                         if (ret != EC_PARSED_NOMATCH)
122                                 ec_parsed_del_last_child(parsed);
123                         continue;
124                 }
125
126                 childvec = ec_strvec_ndup(strvec, i, ec_strvec_len(strvec) - i);
127                 if (childvec == NULL) {
128                         ec_parsed_del_last_child(parsed);
129                         goto fail;
130                 }
131
132                 ret = __ec_node_many_complete(node, max, completed, childvec);
133                 ec_parsed_del_last_child(parsed);
134                 ec_strvec_free(childvec);
135                 childvec = NULL;
136
137                 if (ret < 0)
138                         goto fail;
139         }
140
141         return 0;
142
143 fail:
144         ec_strvec_free(childvec);
145         return -1;
146 }
147
148 static int
149 ec_node_many_complete(const struct ec_node *gen_node,
150                 struct ec_completed *completed,
151                 const struct ec_strvec *strvec)
152 {
153         struct ec_node_many *node = (struct ec_node_many *)gen_node;
154
155         return __ec_node_many_complete(node, node->max, completed,
156                                 strvec);
157 }
158
159 static void ec_node_many_free_priv(struct ec_node *gen_node)
160 {
161         struct ec_node_many *node = (struct ec_node_many *)gen_node;
162
163         ec_node_free(node->child);
164 }
165
166 static struct ec_node_type ec_node_many_type = {
167         .name = "many",
168         .parse = ec_node_many_parse,
169         .complete = ec_node_many_complete,
170         .size = sizeof(struct ec_node_many),
171         .free_priv = ec_node_many_free_priv,
172 };
173
174 EC_NODE_TYPE_REGISTER(ec_node_many_type);
175
176 struct ec_node *ec_node_many(const char *id, struct ec_node *child,
177         unsigned int min, unsigned int max)
178 {
179         struct ec_node_many *node = NULL;
180
181         if (child == NULL)
182                 return NULL;
183
184         node = (struct ec_node_many *)__ec_node(&ec_node_many_type, id);
185         if (node == NULL) {
186                 ec_node_free(child);
187                 return NULL;
188         }
189
190         node->child = child;
191         node->min = min;
192         node->max = max;
193
194         return &node->gen;
195 }
196
197 /* LCOV_EXCL_START */
198 static int ec_node_many_testcase(void)
199 {
200         struct ec_node *node;
201         int testres = 0;
202
203         node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 0, 0);
204         if (node == NULL) {
205                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
206                 return -1;
207         }
208         testres |= EC_TEST_CHECK_PARSE(node, 0);
209         testres |= EC_TEST_CHECK_PARSE(node, 0, "bar");
210         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
211         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar");
212         testres |= EC_TEST_CHECK_PARSE(node, 0);
213         ec_node_free(node);
214
215         node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 1, 0);
216         if (node == NULL) {
217                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
218                 return -1;
219         }
220         testres |= EC_TEST_CHECK_PARSE(node, -1, "bar");
221         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
222         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar");
223         testres |= EC_TEST_CHECK_PARSE(node, -1);
224         ec_node_free(node);
225
226         node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 1, 2);
227         if (node == NULL) {
228                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
229                 return -1;
230         }
231         testres |= EC_TEST_CHECK_PARSE(node, -1, "bar");
232         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
233         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "bar");
234         testres |= EC_TEST_CHECK_PARSE(node, 2, "foo", "foo", "foo");
235         testres |= EC_TEST_CHECK_PARSE(node, -1);
236         ec_node_free(node);
237
238         /* test completion */
239         node = ec_node_many(EC_NO_ID, ec_node_str(EC_NO_ID, "foo"), 2, 4);
240         if (node == NULL) {
241                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
242                 return -1;
243         }
244         testres |= EC_TEST_CHECK_COMPLETE(node,
245                 "", EC_NODE_ENDLIST,
246                 "foo", EC_NODE_ENDLIST);
247         testres |= EC_TEST_CHECK_COMPLETE(node,
248                 "f", EC_NODE_ENDLIST,
249                 "foo", EC_NODE_ENDLIST);
250         testres |= EC_TEST_CHECK_COMPLETE(node,
251                 "foo", EC_NODE_ENDLIST,
252                 "foo", EC_NODE_ENDLIST);
253         testres |= EC_TEST_CHECK_COMPLETE(node,
254                 "foo", "", EC_NODE_ENDLIST,
255                 "foo", EC_NODE_ENDLIST);
256         testres |= EC_TEST_CHECK_COMPLETE(node,
257                 "foo", "foo", "", EC_NODE_ENDLIST,
258                 "foo", EC_NODE_ENDLIST);
259         testres |= EC_TEST_CHECK_COMPLETE(node,
260                 "foo", "foo", "foo", "", EC_NODE_ENDLIST,
261                 "foo", EC_NODE_ENDLIST);
262         testres |= EC_TEST_CHECK_COMPLETE(node,
263                 "foo", "foo", "foo", "foo", "", EC_NODE_ENDLIST,
264                 EC_NODE_ENDLIST);
265         ec_node_free(node);
266
267         return testres;
268 }
269 /* LCOV_EXCL_STOP */
270
271 static struct ec_test ec_node_many_test = {
272         .name = "node_many",
273         .test = ec_node_many_testcase,
274 };
275
276 EC_TEST_REGISTER(ec_node_many_test);