e5754ba01b263e3b98b8de7f351bdf3641148058
[protos/libecoli.git] / libecoli / ecoli_node_re_lex.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 <stdbool.h>
8 #include <string.h>
9 #include <regex.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_complete.h>
18 #include <ecoli_parse.h>
19 #include <ecoli_config.h>
20 #include <ecoli_node_many.h>
21 #include <ecoli_node_or.h>
22 #include <ecoli_node_str.h>
23 #include <ecoli_node_int.h>
24 #include <ecoli_node_re_lex.h>
25
26 EC_LOG_TYPE_REGISTER(node_re_lex);
27
28 struct regexp_pattern {
29         char *pattern;
30         regex_t r;
31         bool keep;
32 };
33
34 struct ec_node_re_lex {
35         struct ec_node gen;
36         struct ec_node *child;
37         struct regexp_pattern *table;
38         size_t len;
39 };
40
41 static struct ec_strvec *
42 tokenize(struct regexp_pattern *table, size_t table_len, const char *str)
43 {
44         struct ec_strvec *strvec = NULL;
45         char *dup = NULL;
46         char c;
47         size_t len, off = 0;
48         size_t i;
49         int ret;
50         regmatch_t pos;
51
52         dup = ec_strdup(str);
53         if (dup == NULL)
54                 goto fail;
55
56         strvec = ec_strvec();
57         if (strvec == NULL)
58                 goto fail;
59
60         len = strlen(dup);
61         while (off < len) {
62                 for (i = 0; i < table_len; i++) {
63                         ret = regexec(&table[i].r, &dup[off], 1, &pos, 0);
64                         if (ret != 0)
65                                 continue;
66                         if (pos.rm_so != 0 || pos.rm_eo == 0) {
67                                 ret = -1;
68                                 continue;
69                         }
70
71                         if (table[i].keep == 0)
72                                 break;
73
74                         c = dup[pos.rm_eo + off];
75                         dup[pos.rm_eo + off] = '\0';
76                         EC_LOG(EC_LOG_DEBUG, "re_lex match <%s>\n", &dup[off]);
77                         if (ec_strvec_add(strvec, &dup[off]) < 0)
78                                 goto fail;
79
80                         dup[pos.rm_eo + off] = c;
81                         break;
82                 }
83
84                 if (ret != 0)
85                         goto fail;
86
87                 off += pos.rm_eo;
88         }
89
90         ec_free(dup);
91         return strvec;
92
93 fail:
94         ec_free(dup);
95         ec_strvec_free(strvec);
96         return NULL;
97 }
98
99 static int
100 ec_node_re_lex_parse(const struct ec_node *gen_node,
101                 struct ec_parse *state,
102                 const struct ec_strvec *strvec)
103 {
104         struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
105         struct ec_strvec *new_vec = NULL;
106         struct ec_parse *child_parse;
107         const char *str;
108         int ret;
109
110         if (node->child == NULL) {
111                 errno = EINVAL;
112                 goto fail;
113         }
114
115         if (ec_strvec_len(strvec) == 0) {
116                 new_vec = ec_strvec();
117         } else {
118                 str = ec_strvec_val(strvec, 0);
119                 new_vec = tokenize(node->table, node->len, str);
120         }
121         if (new_vec == NULL)
122                 goto fail;
123
124         ret = ec_node_parse_child(node->child, state, new_vec);
125         if (ret < 0)
126                 goto fail;
127
128         if ((unsigned)ret == ec_strvec_len(new_vec)) {
129                 ret = 1;
130         } else if (ret != EC_PARSE_NOMATCH) {
131                 child_parse = ec_parse_get_last_child(state);
132                 ec_parse_unlink_child(state, child_parse);
133                 ec_parse_free(child_parse);
134                 ret = EC_PARSE_NOMATCH;
135         }
136
137         ec_strvec_free(new_vec);
138         new_vec = NULL;
139
140         return ret;
141
142  fail:
143         ec_strvec_free(new_vec);
144         return -1;
145 }
146
147 static void ec_node_re_lex_free_priv(struct ec_node *gen_node)
148 {
149         struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
150         unsigned int i;
151
152         ec_node_free(node->child);
153         for (i = 0; i < node->len; i++) {
154                 ec_free(node->table[i].pattern);
155                 regfree(&node->table[i].r);
156         }
157
158         ec_free(node->table);
159 }
160
161 static size_t
162 ec_node_re_lex_get_children_count(const struct ec_node *gen_node)
163 {
164         struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
165
166         if (node->child)
167                 return 1;
168         return 0;
169 }
170
171 static int
172 ec_node_re_lex_get_child(const struct ec_node *gen_node, size_t i,
173                         struct ec_node **child, unsigned int *refs)
174 {
175         struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
176
177         if (i >= 1)
178                 return -1;
179
180         *child = node->child;
181         *refs = 2;
182         return 0;
183 }
184
185 static const struct ec_config_schema ec_node_re_lex_dict[] = {
186         {
187                 .key = "pattern",
188                 .desc = "The pattern to match.",
189                 .type = EC_CONFIG_TYPE_STRING,
190         },
191         {
192                 .key = "keep",
193                 .desc = "Whether to keep or drop the string matching "
194                 "the regular expression.",
195                 .type = EC_CONFIG_TYPE_BOOL,
196         },
197         {
198                 .type = EC_CONFIG_TYPE_NONE,
199         },
200 };
201
202 static const struct ec_config_schema ec_node_re_lex_elt[] = {
203         {
204                 .desc = "A pattern element.",
205                 .type = EC_CONFIG_TYPE_DICT,
206                 .subschema = ec_node_re_lex_dict,
207         },
208         {
209                 .type = EC_CONFIG_TYPE_NONE,
210         },
211 };
212
213 static const struct ec_config_schema ec_node_re_lex_schema[] = {
214         {
215                 .key = "patterns",
216                 .desc = "The list of patterns elements.",
217                 .type = EC_CONFIG_TYPE_LIST,
218                 .subschema = ec_node_re_lex_elt,
219         },
220         {
221                 .key = "child",
222                 .desc = "The child node.",
223                 .type = EC_CONFIG_TYPE_NODE,
224         },
225         {
226                 .type = EC_CONFIG_TYPE_NONE,
227         },
228 };
229
230 static int ec_node_re_lex_set_config(struct ec_node *gen_node,
231                                 const struct ec_config *config)
232 {
233         struct ec_node_re_lex *node = (struct ec_node_re_lex *)gen_node;
234         struct regexp_pattern *table = NULL;
235         const struct ec_config *patterns, *child, *elt, *pattern, *keep;
236         char *pattern_str = NULL;
237         ssize_t i, n = 0;
238         int ret;
239
240         child = ec_config_dict_get(config, "child");
241         if (child == NULL)
242                 goto fail;
243         if (ec_config_get_type(child) != EC_CONFIG_TYPE_NODE) {
244                 errno = EINVAL;
245                 goto fail;
246         }
247
248         patterns = ec_config_dict_get(config, "patterns");
249         if (patterns != NULL) {
250                 n = ec_config_count(patterns);
251                 if (n < 0)
252                         goto fail;
253
254                 table = ec_calloc(n, sizeof(*table));
255                 if (table == NULL)
256                         goto fail;
257
258                 n = 0;
259                 TAILQ_FOREACH(elt, &patterns->list, next) {
260                         if (ec_config_get_type(elt) != EC_CONFIG_TYPE_DICT) {
261                                 errno = EINVAL;
262                                 goto fail;
263                         }
264                         pattern = ec_config_dict_get(elt, "pattern");
265                         if (pattern == NULL) {
266                                 errno = EINVAL;
267                                 goto fail;
268                         }
269                         if (ec_config_get_type(pattern) != EC_CONFIG_TYPE_STRING) {
270                                 errno = EINVAL;
271                                 goto fail;
272                         }
273                         keep = ec_config_dict_get(elt, "keep");
274                         if (keep == NULL) {
275                                 errno = EINVAL;
276                                 goto fail;
277                         }
278                         if (ec_config_get_type(keep) != EC_CONFIG_TYPE_BOOL) {
279                                 errno = EINVAL;
280                                 goto fail;
281                         }
282                         pattern_str = ec_strdup(pattern->string);
283                         if (pattern_str == NULL)
284                                 goto fail;
285
286                         ret = regcomp(&table[n].r, pattern_str, REG_EXTENDED);
287                         if (ret != 0) {
288                                 EC_LOG(EC_LOG_ERR,
289                                         "Regular expression <%s> compilation failed: %d\n",
290                                         pattern_str, ret);
291                                 if (ret == REG_ESPACE)
292                                         errno = ENOMEM;
293                                 else
294                                         errno = EINVAL;
295                                 goto fail;
296                         }
297                         table[n].pattern = pattern_str;
298                         table[n].keep = keep->boolean;
299                         pattern_str = NULL;
300
301                         n++;
302                 }
303         }
304
305         if (node->child != NULL)
306                 ec_node_free(node->child);
307         node->child = ec_node_clone(child->node);
308         for (i = 0; i < (ssize_t)node->len; i++) {
309                 ec_free(node->table[i].pattern);
310                 regfree(&node->table[i].r);
311         }
312         ec_free(node->table);
313         node->table = table;
314         node->len = n;
315
316         return 0;
317
318 fail:
319         if (table != NULL) {
320                 for (i = 0; i < n; i++) {
321                         if (table[i].pattern != NULL) {
322                                 ec_free(table[i].pattern);
323                                 regfree(&table[i].r);
324                         }
325                 }
326         }
327         ec_free(table);
328         ec_free(pattern_str);
329         return -1;
330 }
331
332 static struct ec_node_type ec_node_re_lex_type = {
333         .name = "re_lex",
334         .schema = ec_node_re_lex_schema,
335         .set_config = ec_node_re_lex_set_config,
336         .parse = ec_node_re_lex_parse,
337         .complete = ec_node_complete_unknown,
338         .size = sizeof(struct ec_node_re_lex),
339         .free_priv = ec_node_re_lex_free_priv,
340         .get_children_count = ec_node_re_lex_get_children_count,
341         .get_child = ec_node_re_lex_get_child,
342 };
343
344 EC_NODE_TYPE_REGISTER(ec_node_re_lex_type);
345
346 int ec_node_re_lex_add(struct ec_node *gen_node, const char *pattern, int keep)
347 {
348         const struct ec_config *cur_config = NULL;
349         struct ec_config *config = NULL, *patterns = NULL, *elt = NULL;
350         int ret;
351
352         if (ec_node_check_type(gen_node, &ec_node_re_lex_type) < 0)
353                 goto fail;
354
355         elt = ec_config_dict();
356         if (elt == NULL)
357                 goto fail;
358         if (ec_config_dict_set(elt, "pattern", ec_config_string(pattern)) < 0)
359                 goto fail;
360         if (ec_config_dict_set(elt, "keep", ec_config_bool(keep)) < 0)
361                 goto fail;
362
363         cur_config = ec_node_get_config(gen_node);
364         if (cur_config == NULL)
365                 config = ec_config_dict();
366         else
367                 config = ec_config_dup(cur_config);
368         if (config == NULL)
369                 goto fail;
370
371         patterns = ec_config_dict_get(config, "patterns");
372         if (patterns == NULL) {
373                 patterns = ec_config_list();
374                 if (patterns == NULL)
375                         goto fail;
376
377                 if (ec_config_dict_set(config, "patterns", patterns) < 0)
378                         goto fail; /* patterns list is freed on error */
379         }
380
381         if (ec_config_list_add(patterns, elt) < 0) {
382                 elt = NULL;
383                 goto fail;
384         }
385         elt = NULL;
386
387         ret = ec_node_set_config(gen_node, config);
388         config = NULL; /* freed */
389         if (ret < 0)
390                 goto fail;
391
392         return 0;
393
394 fail:
395         ec_config_free(config);
396         ec_config_free(elt);
397         return -1;
398 }
399
400 static int
401 ec_node_re_lex_set_child(struct ec_node *gen_node, struct ec_node *child)
402 {
403         const struct ec_config *cur_config = NULL;
404         struct ec_config *config = NULL;
405         int ret;
406
407         if (ec_node_check_type(gen_node, &ec_node_re_lex_type) < 0)
408                 goto fail;
409
410         cur_config = ec_node_get_config(gen_node);
411         if (cur_config == NULL)
412                 config = ec_config_dict();
413         else
414                 config = ec_config_dup(cur_config);
415         if (config == NULL)
416                 goto fail;
417
418         if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
419                 child = NULL; /* freed */
420                 goto fail;
421         }
422         child = NULL; /* freed */
423
424         ret = ec_node_set_config(gen_node, config);
425         config = NULL; /* freed */
426         if (ret < 0)
427                 goto fail;
428
429         return 0;
430
431 fail:
432         ec_config_free(config);
433         ec_node_free(child);
434         return -1;
435 }
436
437 struct ec_node *ec_node_re_lex(const char *id, struct ec_node *child)
438 {
439         struct ec_node *gen_node = NULL;
440
441         if (child == NULL)
442                 return NULL;
443
444         gen_node = ec_node_from_type(&ec_node_re_lex_type, id);
445         if (gen_node == NULL)
446                 goto fail;
447
448         if (ec_node_re_lex_set_child(gen_node, child) < 0) {
449                 child = NULL; /* freed */
450                 goto fail;
451         }
452
453         return gen_node;
454
455 fail:
456         ec_node_free(gen_node);
457         ec_node_free(child);
458         return NULL;
459 }
460
461 /* LCOV_EXCL_START */
462 static int ec_node_re_lex_testcase(void)
463 {
464         struct ec_node *node;
465         int ret, testres = 0;
466
467         node = ec_node_re_lex(EC_NO_ID,
468                 ec_node_many(EC_NO_ID,
469                         EC_NODE_OR(EC_NO_ID,
470                                 ec_node_str(EC_NO_ID, "foo"),
471                                 ec_node_str(EC_NO_ID, "bar"),
472                                 ec_node_int(EC_NO_ID, 0, 1000, 0)
473                         ), 0, 0
474                 )
475         );
476         if (node == NULL) {
477                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
478                 return -1;
479         }
480
481         ret = ec_node_re_lex_add(node, "[a-zA-Z]+", 1);
482         testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp");
483         ec_node_free(node);
484         return 0;
485
486         ret = ec_node_re_lex_add(node, "[0-9]+", 1);
487         testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp");
488         ret = ec_node_re_lex_add(node, "=", 1);
489         testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp");
490         ret = ec_node_re_lex_add(node, "-", 1);
491         testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp");
492         ret = ec_node_re_lex_add(node, "\\+", 1);
493         testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp");
494         ret = ec_node_re_lex_add(node, "[       ]+", 0);
495         testres |= EC_TEST_CHECK(ret == 0, "cannot add regexp");
496         if (ret != 0) {
497                 EC_LOG(EC_LOG_ERR, "cannot add regexp to node\n");
498                 ec_node_free(node);
499                 return -1;
500         }
501
502         testres |= EC_TEST_CHECK_PARSE(node, 1, "  foo bar  324 bar234");
503         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo bar324");
504         testres |= EC_TEST_CHECK_PARSE(node, 1, "");
505         testres |= EC_TEST_CHECK_PARSE(node, -1, "foobar");
506
507         /* no completion */
508         testres |= EC_TEST_CHECK_COMPLETE(node,
509                 "", EC_NODE_ENDLIST,
510                 EC_NODE_ENDLIST);
511
512         ec_node_free(node);
513
514         return testres;
515 }
516 /* LCOV_EXCL_STOP */
517
518 static struct ec_test ec_node_re_lex_test = {
519         .name = "node_re_lex",
520         .test = ec_node_re_lex_testcase,
521 };
522
523 EC_TEST_REGISTER(ec_node_re_lex_test);