/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 | 861k | { |
39 | 861k | StringParseResult lhs, rhs; |
40 | 861k | StringExpression *ret, *subret, *dot; |
41 | | |
42 | 861k | lhs = ParseStringExpression(expr, start, end); |
43 | | |
44 | 861k | if (!lhs.result) |
45 | 12.5k | { |
46 | 12.5k | return lhs; |
47 | 12.5k | } |
48 | | |
49 | 849k | if (lhs.position == end || expr[lhs.position] != '.') |
50 | 751k | { |
51 | 751k | return lhs; |
52 | 751k | } |
53 | | |
54 | 97.2k | rhs = ParseStringExpression(expr, lhs.position + 1, end); |
55 | | |
56 | 97.2k | if (!rhs.result) |
57 | 1.97k | { |
58 | 1.97k | FreeStringExpression(lhs.result); |
59 | 1.97k | return rhs; |
60 | 1.97k | } |
61 | | |
62 | 95.2k | dot = xcalloc(1, sizeof(StringExpression)); |
63 | 95.2k | dot->op = LITERAL; |
64 | 95.2k | dot->val.literal.literal = xstrdup("."); |
65 | | |
66 | 95.2k | subret = xcalloc(1, sizeof(StringExpression)); |
67 | 95.2k | subret->op = CONCAT; |
68 | 95.2k | subret->val.concat.lhs = dot; |
69 | 95.2k | subret->val.concat.rhs = rhs.result; |
70 | | |
71 | 95.2k | ret = xcalloc(1, sizeof(StringExpression)); |
72 | 95.2k | ret->op = CONCAT; |
73 | 95.2k | ret->val.concat.lhs = lhs.result; |
74 | 95.2k | ret->val.concat.rhs = subret; |
75 | | |
76 | 95.2k | return (StringParseResult) {ret, rhs.position}; |
77 | 97.2k | } |
78 | | |
79 | | /* <var-ref> */ |
80 | | |
81 | | static StringParseResult ParseVarRef(const char *expr, int start, int end) |
82 | 972k | { |
83 | 972k | if (start + 1 < end && (expr[start] == '$' || expr[start] == '@')) |
84 | 861k | { |
85 | 861k | if (expr[start + 1] == '(' || expr[start + 1] == '{') |
86 | 861k | { |
87 | 861k | char closing_bracket = expr[start + 1] == '(' ? ')' : '}'; |
88 | 861k | StringParseResult res = ParseQname(expr, start + 2, end); |
89 | | |
90 | 861k | if (res.result) |
91 | 847k | { |
92 | 847k | if (res.position < end && expr[res.position] == closing_bracket) |
93 | 12.7k | { |
94 | 12.7k | StringExpression *ret = xcalloc(1, sizeof(StringExpression)); |
95 | | |
96 | 12.7k | ret->op = VARREF; |
97 | 12.7k | ret->val.varref.name = res.result; |
98 | | |
99 | 12.7k | if (expr[start] == '$') |
100 | 10.5k | { |
101 | 10.5k | ret->val.varref.type = VAR_REF_TYPE_SCALAR; |
102 | 10.5k | } |
103 | 2.22k | else if (expr[start] == '@') |
104 | 2.22k | { |
105 | 2.22k | ret->val.varref.type = VAR_REF_TYPE_LIST; |
106 | 2.22k | } |
107 | 0 | else |
108 | 0 | { |
109 | 0 | ProgrammingError("Unrecognized var ref type"); |
110 | 0 | } |
111 | | |
112 | 12.7k | return (StringParseResult) {ret, res.position + 1}; |
113 | 12.7k | } |
114 | 834k | else |
115 | 834k | { |
116 | 834k | FreeStringExpression(res.result); |
117 | 834k | return (StringParseResult) {NULL, res.position}; |
118 | 834k | } |
119 | 847k | } |
120 | 14.5k | else |
121 | 14.5k | { |
122 | 14.5k | return res; |
123 | 14.5k | } |
124 | 861k | } |
125 | 25 | else |
126 | 25 | { |
127 | 25 | return (StringParseResult) {NULL, start + 1}; |
128 | 25 | } |
129 | 861k | } |
130 | 110k | else |
131 | 110k | { |
132 | 110k | return (StringParseResult) {NULL, start}; |
133 | 110k | } |
134 | 972k | } |
135 | | |
136 | | /* <token> */ |
137 | | |
138 | | static inline bool ValidTokenCharacter(char c, bool *inside_index) |
139 | 12.2M | { |
140 | 12.2M | assert(inside_index != NULL); |
141 | | |
142 | 12.2M | if (c >= 'a' && c <= 'z') |
143 | 1.44M | { |
144 | 1.44M | return true; |
145 | 1.44M | } |
146 | | |
147 | 10.7M | if (c >= 'A' && c <= 'Z') |
148 | 3.78M | { |
149 | 3.78M | return true; |
150 | 3.78M | } |
151 | | |
152 | 7.01M | if (c >= '0' && c <= '9') |
153 | 3.75M | { |
154 | 3.75M | return true; |
155 | 3.75M | } |
156 | | |
157 | 3.25M | if (c == '_' || c == ':') |
158 | 87.1k | { |
159 | 87.1k | return true; |
160 | 87.1k | } |
161 | | |
162 | 3.16M | if (c == '[') |
163 | 687k | { |
164 | 687k | *inside_index = true; |
165 | 687k | return true; |
166 | 687k | } |
167 | | |
168 | 2.48M | if (c == ']') |
169 | 502k | { |
170 | 502k | if (*inside_index) |
171 | 442k | { |
172 | 442k | *inside_index = false; |
173 | 442k | } |
174 | 502k | return true; |
175 | 502k | } |
176 | | |
177 | 1.97M | if ((c == ' ') && *inside_index) |
178 | 50.9k | { |
179 | 50.9k | return true; |
180 | 50.9k | } |
181 | | |
182 | 1.92M | return false; |
183 | 1.97M | } |
184 | | |
185 | | static StringParseResult ParseToken(const char *expr, int start, int end) |
186 | 1.92M | { |
187 | 1.92M | int endlit = start; |
188 | | |
189 | 1.92M | bool inside_index = false; |
190 | 12.2M | while (endlit < end && ValidTokenCharacter(expr[endlit], &inside_index)) |
191 | 10.3M | { |
192 | 10.3M | endlit++; |
193 | 10.3M | } |
194 | | |
195 | 1.92M | if (endlit > start) |
196 | 955k | { |
197 | 955k | StringExpression *ret = xcalloc(1, sizeof(StringExpression)); |
198 | | |
199 | 955k | ret->op = LITERAL; |
200 | 955k | ret->val.literal.literal = xstrndup(expr + start, endlit - start); |
201 | | |
202 | 955k | return (StringParseResult) {ret, endlit}; |
203 | 955k | } |
204 | 972k | else |
205 | 972k | { |
206 | 972k | return (StringParseResult) {NULL, endlit}; |
207 | 972k | } |
208 | 1.92M | } |
209 | | |
210 | | /* <term> */ |
211 | | |
212 | | static StringParseResult ParseTerm(const char *expr, int start, int end) |
213 | 1.92M | { |
214 | 1.92M | StringParseResult res = ParseToken(expr, start, end); |
215 | | |
216 | 1.92M | if (res.result) |
217 | 955k | { |
218 | 955k | return res; |
219 | 955k | } |
220 | 972k | else |
221 | 972k | { |
222 | 972k | return ParseVarRef(expr, start, end); |
223 | 972k | } |
224 | 1.92M | } |
225 | | |
226 | | /* <name> */ |
227 | | |
228 | | StringParseResult ParseStringExpression(const char *expr, int start, int end) |
229 | 1.92M | { |
230 | 1.92M | StringParseResult lhs = ParseTerm(expr, start, end); |
231 | | |
232 | 1.92M | if (lhs.result) |
233 | 968k | { |
234 | 968k | StringParseResult rhs = ParseStringExpression(expr, lhs.position, end); |
235 | | |
236 | 968k | if (rhs.result) |
237 | 23.5k | { |
238 | 23.5k | StringExpression *ret = xcalloc(1, sizeof(StringExpression)); |
239 | | |
240 | 23.5k | ret->op = CONCAT; |
241 | 23.5k | ret->val.concat.lhs = lhs.result; |
242 | 23.5k | ret->val.concat.rhs = rhs.result; |
243 | | |
244 | 23.5k | return (StringParseResult) {ret, rhs.position}; |
245 | 23.5k | } |
246 | 944k | else |
247 | 944k | { |
248 | 944k | return lhs; |
249 | 944k | } |
250 | 968k | } |
251 | 959k | else |
252 | 959k | { |
253 | 959k | return lhs; |
254 | 959k | } |
255 | 1.92M | } |
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.27M | { |
316 | 1.27M | if (!expr) |
317 | 159 | { |
318 | 159 | return; |
319 | 159 | } |
320 | | |
321 | 1.27M | switch (expr->op) |
322 | 1.27M | { |
323 | 214k | case CONCAT: |
324 | 214k | FreeStringExpression(expr->val.concat.lhs); |
325 | 214k | FreeStringExpression(expr->val.concat.rhs); |
326 | 214k | break; |
327 | 1.05M | case LITERAL: |
328 | 1.05M | free(expr->val.literal.literal); |
329 | 1.05M | break; |
330 | 12.7k | case VARREF: |
331 | 12.7k | FreeStringExpression(expr->val.varref.name); |
332 | 12.7k | break; |
333 | 0 | default: |
334 | 0 | ProgrammingError("Unknown type of string expression encountered: %d", expr->op); |
335 | 1.27M | } |
336 | | |
337 | 1.27M | free(expr); |
338 | 1.27M | } |