add config for node_re
[protos/libecoli.git] / libecoli / ecoli_node_re.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 <errno.h>
9 #include <regex.h>
10
11 #include <ecoli_log.h>
12 #include <ecoli_malloc.h>
13 #include <ecoli_test.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_node.h>
16 #include <ecoli_parse.h>
17 #include <ecoli_complete.h>
18 #include <ecoli_config.h>
19 #include <ecoli_node_re.h>
20
21 EC_LOG_TYPE_REGISTER(node_re);
22
23 struct ec_node_re {
24         struct ec_node gen;
25         char *re_str;
26         regex_t re;
27 };
28
29 static int
30 ec_node_re_parse(const struct ec_node *gen_node,
31                 struct ec_parse *state,
32                 const struct ec_strvec *strvec)
33 {
34         struct ec_node_re *node = (struct ec_node_re *)gen_node;
35         const char *str;
36         regmatch_t pos;
37
38         (void)state;
39
40         if (ec_strvec_len(strvec) == 0)
41                 return EC_PARSE_NOMATCH;
42
43         str = ec_strvec_val(strvec, 0);
44         if (regexec(&node->re, str, 1, &pos, 0) != 0)
45                 return EC_PARSE_NOMATCH;
46         if (pos.rm_so != 0 || pos.rm_eo != (int)strlen(str))
47                 return EC_PARSE_NOMATCH;
48
49         return 1;
50 }
51
52 static void ec_node_re_free_priv(struct ec_node *gen_node)
53 {
54         struct ec_node_re *node = (struct ec_node_re *)gen_node;
55
56         if (node->re_str != NULL) {
57                 ec_free(node->re_str);
58                 regfree(&node->re);
59         }
60 }
61
62 static const struct ec_config_schema ec_node_re_schema[] = {
63         {
64                 .key = "pattern",
65                 .desc = "The pattern to match.",
66                 .type = EC_CONFIG_TYPE_STRING,
67         },
68         {
69                 .type = EC_CONFIG_TYPE_NONE,
70         },
71 };
72
73 static int ec_node_re_set_config(struct ec_node *gen_node,
74                                 const struct ec_config *config)
75 {
76         struct ec_node_re *node = (struct ec_node_re *)gen_node;
77         const struct ec_config *value = NULL;
78         char *s = NULL;
79         regex_t re;
80         int ret;
81
82         value = ec_config_dict_get(config, "pattern");
83         if (value == NULL) {
84                 errno = EINVAL;
85                 goto fail;
86         }
87
88         s = ec_strdup(value->string);
89         if (s == NULL)
90                 goto fail;
91
92         ret = regcomp(&re, s, REG_EXTENDED);
93         if (ret != 0) {
94                 if (ret == REG_ESPACE)
95                         errno = ENOMEM;
96                 else
97                         errno = EINVAL;
98                 goto fail;
99         }
100
101         if (node->re_str != NULL) {
102                 ec_free(node->re_str);
103                 regfree(&node->re);
104         }
105         node->re_str = s;
106         node->re = re;
107
108         return 0;
109
110 fail:
111         ec_free(s);
112         return -1;
113 }
114
115 static struct ec_node_type ec_node_re_type = {
116         .name = "re",
117         .schema = ec_node_re_schema,
118         .set_config = ec_node_re_set_config,
119         .parse = ec_node_re_parse,
120         .complete = ec_node_complete_unknown,
121         .size = sizeof(struct ec_node_re),
122         .free_priv = ec_node_re_free_priv,
123 };
124
125 EC_NODE_TYPE_REGISTER(ec_node_re_type);
126
127 int ec_node_re_set_regexp(struct ec_node *gen_node, const char *str)
128 {
129         struct ec_config *config = NULL;
130         int ret;
131
132         EC_CHECK_ARG(str != NULL, -1, EINVAL);
133
134         if (ec_node_check_type(gen_node, &ec_node_re_type) < 0)
135                 goto fail;
136
137         config = ec_config_dict();
138         if (config == NULL)
139                 goto fail;
140
141         ret = ec_config_dict_set(config, "pattern", ec_config_string(str));
142         if (ret < 0)
143                 goto fail;
144
145         ret = ec_node_set_config(gen_node, config);
146         config = NULL;
147         if (ret < 0)
148                 goto fail;
149
150         return 0;
151
152 fail:
153         ec_config_free(config);
154         return -1;
155 }
156
157 struct ec_node *ec_node_re(const char *id, const char *re_str)
158 {
159         struct ec_node *gen_node = NULL;
160
161         gen_node = ec_node_from_type(&ec_node_re_type, id);
162         if (gen_node == NULL)
163                 goto fail;
164
165         if (ec_node_re_set_regexp(gen_node, re_str) < 0)
166                 goto fail;
167
168         return gen_node;
169
170 fail:
171         ec_node_free(gen_node);
172         return NULL;
173 }
174
175 /* LCOV_EXCL_START */
176 static int ec_node_re_testcase(void)
177 {
178         struct ec_node *node;
179         int testres = 0;
180
181         node = ec_node_re(EC_NO_ID, "fo+|bar");
182         if (node == NULL) {
183                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
184                 return -1;
185         }
186         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
187         testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
188         testres |= EC_TEST_CHECK_PARSE(node, 1, "bar");
189         testres |= EC_TEST_CHECK_PARSE(node, -1, "foobar");
190         testres |= EC_TEST_CHECK_PARSE(node, -1, " foo");
191         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
192         ec_node_free(node);
193
194         return testres;
195 }
196 /* LCOV_EXCL_STOP */
197
198 static struct ec_test ec_node_re_test = {
199         .name = "node_re",
200         .test = ec_node_re_testcase,
201 };
202
203 EC_TEST_REGISTER(ec_node_re_test);