/src/core/libpromises/string_expressions.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright 2023 Northern.tech AS |
3 | | |
4 | | This file is part of CFEngine 3 - written and maintained by Northern.tech AS. |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify it |
7 | | under the terms of the GNU General Public License as published by the |
8 | | Free Software Foundation; version 3. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program; if not, write to the Free Software |
17 | | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
18 | | |
19 | | To the extent this program is licensed as part of the Enterprise |
20 | | versions of CFEngine, the applicable Commercial Open Source License |
21 | | (COSL) may apply to this file if you as a licensee so wish it. See |
22 | | included file COSL.txt. |
23 | | */ |
24 | | |
25 | | #include <cf3.defs.h> |
26 | | |
27 | | #include <stdbool.h> |
28 | | #include <string_expressions.h> |
29 | | #include <misc_lib.h> |
30 | | |
31 | | #include <stdlib.h> |
32 | | #include <string.h> |
33 | | #include <stdio.h> |
34 | | |
35 | | /* <qname> */ |
36 | | |
37 | | static StringParseResult ParseQname(const char *expr, int start, int end) |
38 | 840k | { |
39 | 840k | StringParseResult lhs, rhs; |
40 | 840k | StringExpression *ret, *subret, *dot; |
41 | | |
42 | 840k | lhs = ParseStringExpression(expr, start, end); |
43 | | |
44 | 840k | if (!lhs.result) |
45 | 15.3k | { |
46 | 15.3k | return lhs; |
47 | 15.3k | } |
48 | | |
49 | 825k | if (lhs.position == end || expr[lhs.position] != '.') |
50 | 702k | { |
51 | 702k | return lhs; |
52 | 702k | } |
53 | | |
54 | 123k | rhs = ParseStringExpression(expr, lhs.position + 1, end); |
55 | | |
56 | 123k | if (!rhs.result) |
57 | 1.70k | { |
58 | 1.70k | FreeStringExpression(lhs.result); |
59 | 1.70k | return rhs; |
60 | 1.70k | } |
61 | | |
62 | 121k | dot = xcalloc(1, sizeof(StringExpression)); |
63 | 121k | dot->op = LITERAL; |
64 | 121k | dot->val.literal.literal = xstrdup("."); |
65 | | |
66 | 121k | subret = xcalloc(1, sizeof(StringExpression)); |
67 | 121k | subret->op = CONCAT; |
68 | 121k | subret->val.concat.lhs = dot; |
69 | 121k | subret->val.concat.rhs = rhs.result; |
70 | | |
71 | 121k | ret = xcalloc(1, sizeof(StringExpression)); |
72 | 121k | ret->op = CONCAT; |
73 | 121k | ret->val.concat.lhs = lhs.result; |
74 | 121k | ret->val.concat.rhs = subret; |
75 | | |
76 | 121k | return (StringParseResult) {ret, rhs.position}; |
77 | 123k | } |
78 | | |
79 | | /* <var-ref> */ |
80 | | |
81 | | static StringParseResult ParseVarRef(const char *expr, int start, int end) |
82 | 984k | { |
83 | 984k | if (start + 1 < end && (expr[start] == '$' || expr[start] == '@')) |
84 | 840k | { |
85 | 840k | if (expr[start + 1] == '(' || expr[start + 1] == '{') |
86 | 840k | { |
87 | 840k | char closing_bracket = expr[start + 1] == '(' ? ')' : '}'; |
88 | 840k | StringParseResult res = ParseQname(expr, start + 2, end); |
89 | | |
90 | 840k | if (res.result) |
91 | 823k | { |
92 | 823k | if (res.position < end && expr[res.position] == closing_bracket) |
93 | 19.3k | { |
94 | 19.3k | StringExpression *ret = xcalloc(1, sizeof(StringExpression)); |
95 | | |
96 | 19.3k | ret->op = VARREF; |
97 | 19.3k | ret->val.varref.name = res.result; |
98 | | |
99 | 19.3k | if (expr[start] == '$') |
100 | 15.7k | { |
101 | 15.7k | ret->val.varref.type = VAR_REF_TYPE_SCALAR; |
102 | 15.7k | } |
103 | 3.56k | else if (expr[start] == '@') |
104 | 3.56k | { |
105 | 3.56k | ret->val.varref.type = VAR_REF_TYPE_LIST; |
106 | 3.56k | } |
107 | 0 | else |
108 | 0 | { |
109 | 0 | ProgrammingError("Unrecognized var ref type"); |
110 | 0 | } |
111 | | |
112 | 19.3k | return (StringParseResult) {ret, res.position + 1}; |
113 | 19.3k | } |
114 | 804k | else |
115 | 804k | { |
116 | 804k | FreeStringExpression(res.result); |
117 | 804k | return (StringParseResult) {NULL, res.position}; |
118 | 804k | } |
119 | 823k | } |
120 | 17.0k | else |
121 | 17.0k | { |
122 | 17.0k | return res; |
123 | 17.0k | } |
124 | 840k | } |
125 | 31 | else |
126 | 31 | { |
127 | 31 | return (StringParseResult) {NULL, start + 1}; |
128 | 31 | } |
129 | 840k | } |
130 | 143k | else |
131 | 143k | { |
132 | 143k | return (StringParseResult) {NULL, start}; |
133 | 143k | } |
134 | 984k | } |
135 | | |
136 | | /* <token> */ |
137 | | |
138 | | static inline bool ValidTokenCharacter(char c, bool *inside_index) |
139 | 11.9M | { |
140 | 11.9M | assert(inside_index != NULL); |
141 | | |
142 | 11.9M | if (c >= 'a' && c <= 'z') |
143 | 1.13M | { |
144 | 1.13M | return true; |
145 | 1.13M | } |
146 | | |
147 | 10.8M | if (c >= 'A' && c <= 'Z') |
148 | 3.58M | { |
149 | 3.58M | return true; |
150 | 3.58M | } |
151 | | |
152 | 7.25M | if (c >= '0' && c <= '9') |
153 | 2.38M | { |
154 | 2.38M | return true; |
155 | 2.38M | } |
156 | | |
157 | 4.86M | if (c == '_' || c == ':') |
158 | 31.8k | { |
159 | 31.8k | return true; |
160 | 31.8k | } |
161 | | |
162 | 4.83M | if (c == '[') |
163 | 997k | { |
164 | 997k | *inside_index = true; |
165 | 997k | return true; |
166 | 997k | } |
167 | | |
168 | 3.83M | if (c == ']') |
169 | 1.84M | { |
170 | 1.84M | if (*inside_index) |
171 | 862k | { |
172 | 862k | *inside_index = false; |
173 | 862k | } |
174 | 1.84M | return true; |
175 | 1.84M | } |
176 | | |
177 | 1.99M | if ((c == ' ') && *inside_index) |
178 | 47.6k | { |
179 | 47.6k | return true; |
180 | 47.6k | } |
181 | | |
182 | 1.94M | return false; |
183 | 1.99M | } |
184 | | |
185 | | static StringParseResult ParseToken(const char *expr, int start, int end) |
186 | 1.94M | { |
187 | 1.94M | int endlit = start; |
188 | | |
189 | 1.94M | bool inside_index = false; |
190 | 11.9M | while (endlit < end && ValidTokenCharacter(expr[endlit], &inside_index)) |
191 | 10.0M | { |
192 | 10.0M | endlit++; |
193 | 10.0M | } |
194 | | |
195 | 1.94M | if (endlit > start) |
196 | 961k | { |
197 | 961k | StringExpression *ret = xcalloc(1, sizeof(StringExpression)); |
198 | | |
199 | 961k | ret->op = LITERAL; |
200 | 961k | ret->val.literal.literal = xstrndup(expr + start, endlit - start); |
201 | | |
202 | 961k | return (StringParseResult) {ret, endlit}; |
203 | 961k | } |
204 | 984k | else |
205 | 984k | { |
206 | 984k | return (StringParseResult) {NULL, endlit}; |
207 | 984k | } |
208 | 1.94M | } |
209 | | |
210 | | /* <term> */ |
211 | | |
212 | | static StringParseResult ParseTerm(const char *expr, int start, int end) |
213 | 1.94M | { |
214 | 1.94M | StringParseResult res = ParseToken(expr, start, end); |
215 | | |
216 | 1.94M | if (res.result) |
217 | 961k | { |
218 | 961k | return res; |
219 | 961k | } |
220 | 984k | else |
221 | 984k | { |
222 | 984k | return ParseVarRef(expr, start, end); |
223 | 984k | } |
224 | 1.94M | } |
225 | | |
226 | | /* <name> */ |
227 | | |
228 | | StringParseResult ParseStringExpression(const char *expr, int start, int end) |
229 | 1.94M | { |
230 | 1.94M | StringParseResult lhs = ParseTerm(expr, start, end); |
231 | | |
232 | 1.94M | if (lhs.result) |
233 | 980k | { |
234 | 980k | StringParseResult rhs = ParseStringExpression(expr, lhs.position, end); |
235 | | |
236 | 980k | if (rhs.result) |
237 | 32.7k | { |
238 | 32.7k | StringExpression *ret = xcalloc(1, sizeof(StringExpression)); |
239 | | |
240 | 32.7k | ret->op = CONCAT; |
241 | 32.7k | ret->val.concat.lhs = lhs.result; |
242 | 32.7k | ret->val.concat.rhs = rhs.result; |
243 | | |
244 | 32.7k | return (StringParseResult) {ret, rhs.position}; |
245 | 32.7k | } |
246 | 947k | else |
247 | 947k | { |
248 | 947k | return lhs; |
249 | 947k | } |
250 | 980k | } |
251 | 964k | else |
252 | 964k | { |
253 | 964k | return lhs; |
254 | 964k | } |
255 | 1.94M | } |
256 | | |
257 | | /* Evaluation */ |
258 | | |
259 | | static char *EvalConcat(const StringExpression *expr, VarRefEvaluator evalfn, void *param) |
260 | 0 | { |
261 | 0 | char *lhs, *rhs, *res; |
262 | |
|
263 | 0 | lhs = EvalStringExpression(expr->val.concat.lhs, evalfn, param); |
264 | 0 | if (!lhs) |
265 | 0 | { |
266 | 0 | return NULL; |
267 | 0 | } |
268 | | |
269 | 0 | rhs = EvalStringExpression(expr->val.concat.rhs, evalfn, param); |
270 | 0 | if (!rhs) |
271 | 0 | { |
272 | 0 | free(lhs); |
273 | 0 | return NULL; |
274 | 0 | } |
275 | | |
276 | 0 | xasprintf(&res, "%s%s", lhs, rhs); |
277 | 0 | free(lhs); |
278 | 0 | free(rhs); |
279 | 0 | return res; |
280 | 0 | } |
281 | | |
282 | | static char *EvalVarRef(const StringExpression *expr, VarRefEvaluator evalfn, void *param) |
283 | 0 | { |
284 | 0 | char *name, *eval; |
285 | |
|
286 | 0 | name = EvalStringExpression(expr->val.varref.name, evalfn, param); |
287 | 0 | if (!name) |
288 | 0 | { |
289 | 0 | return NULL; |
290 | 0 | } |
291 | | |
292 | 0 | eval = (*evalfn) (name, expr->val.varref.type, param); |
293 | 0 | free(name); |
294 | 0 | return eval; |
295 | 0 | } |
296 | | |
297 | | char *EvalStringExpression(const StringExpression *expr, VarRefEvaluator evalfn, void *param) |
298 | 0 | { |
299 | 0 | switch (expr->op) |
300 | 0 | { |
301 | 0 | case CONCAT: |
302 | 0 | return EvalConcat(expr, evalfn, param); |
303 | 0 | case LITERAL: |
304 | 0 | return xstrdup(expr->val.literal.literal); |
305 | 0 | case VARREF: |
306 | 0 | return EvalVarRef(expr, evalfn, param); |
307 | 0 | default: |
308 | 0 | ProgrammingError("Unknown type of string expression" "encountered during evaluation: %d", expr->op); |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | /* Freeing results */ |
313 | | |
314 | | void FreeStringExpression(StringExpression *expr) |
315 | 1.37M | { |
316 | 1.37M | if (!expr) |
317 | 163 | { |
318 | 163 | return; |
319 | 163 | } |
320 | | |
321 | 1.37M | switch (expr->op) |
322 | 1.37M | { |
323 | 276k | case CONCAT: |
324 | 276k | FreeStringExpression(expr->val.concat.lhs); |
325 | 276k | FreeStringExpression(expr->val.concat.rhs); |
326 | 276k | break; |
327 | 1.08M | case LITERAL: |
328 | 1.08M | free(expr->val.literal.literal); |
329 | 1.08M | break; |
330 | 19.3k | case VARREF: |
331 | 19.3k | FreeStringExpression(expr->val.varref.name); |
332 | 19.3k | break; |
333 | 0 | default: |
334 | 0 | ProgrammingError("Unknown type of string expression encountered: %d", expr->op); |
335 | 1.37M | } |
336 | | |
337 | 1.37M | free(expr); |
338 | 1.37M | } |