1 /*
2 Copyright 2021 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
ParseQname(const char * expr,int start,int end)37 static StringParseResult ParseQname(const char *expr, int start, int end)
38 {
39 StringParseResult lhs, rhs;
40 StringExpression *ret, *subret, *dot;
41
42 lhs = ParseStringExpression(expr, start, end);
43
44 if (!lhs.result)
45 {
46 return lhs;
47 }
48
49 if (lhs.position == end || expr[lhs.position] != '.')
50 {
51 return lhs;
52 }
53
54 rhs = ParseStringExpression(expr, lhs.position + 1, end);
55
56 if (!rhs.result)
57 {
58 FreeStringExpression(lhs.result);
59 return rhs;
60 }
61
62 dot = xcalloc(1, sizeof(StringExpression));
63 dot->op = LITERAL;
64 dot->val.literal.literal = xstrdup(".");
65
66 subret = xcalloc(1, sizeof(StringExpression));
67 subret->op = CONCAT;
68 subret->val.concat.lhs = dot;
69 subret->val.concat.rhs = rhs.result;
70
71 ret = xcalloc(1, sizeof(StringExpression));
72 ret->op = CONCAT;
73 ret->val.concat.lhs = lhs.result;
74 ret->val.concat.rhs = subret;
75
76 return (StringParseResult) {ret, rhs.position};
77 }
78
79 /* <var-ref> */
80
ParseVarRef(const char * expr,int start,int end)81 static StringParseResult ParseVarRef(const char *expr, int start, int end)
82 {
83 if (start + 1 < end && (expr[start] == '$' || expr[start] == '@'))
84 {
85 if (expr[start + 1] == '(' || expr[start + 1] == '{')
86 {
87 char closing_bracket = expr[start + 1] == '(' ? ')' : '}';
88 StringParseResult res = ParseQname(expr, start + 2, end);
89
90 if (res.result)
91 {
92 if (res.position < end && expr[res.position] == closing_bracket)
93 {
94 StringExpression *ret = xcalloc(1, sizeof(StringExpression));
95
96 ret->op = VARREF;
97 ret->val.varref.name = res.result;
98
99 if (expr[start] == '$')
100 {
101 ret->val.varref.type = VAR_REF_TYPE_SCALAR;
102 }
103 else if (expr[start] == '@')
104 {
105 ret->val.varref.type = VAR_REF_TYPE_LIST;
106 }
107 else
108 {
109 ProgrammingError("Unrecognized var ref type");
110 }
111
112 return (StringParseResult) {ret, res.position + 1};
113 }
114 else
115 {
116 FreeStringExpression(res.result);
117 return (StringParseResult) {NULL, res.position};
118 }
119 }
120 else
121 {
122 return res;
123 }
124 }
125 else
126 {
127 return (StringParseResult) {NULL, start + 1};
128 }
129 }
130 else
131 {
132 return (StringParseResult) {NULL, start};
133 }
134 }
135
136 /* <token> */
137
ValidTokenCharacter(char c,bool * inside_index)138 static inline bool ValidTokenCharacter(char c, bool *inside_index)
139 {
140 assert(inside_index != NULL);
141
142 if (c >= 'a' && c <= 'z')
143 {
144 return true;
145 }
146
147 if (c >= 'A' && c <= 'Z')
148 {
149 return true;
150 }
151
152 if (c >= '0' && c <= '9')
153 {
154 return true;
155 }
156
157 if (c == '_' || c == ':')
158 {
159 return true;
160 }
161
162 if (c == '[')
163 {
164 *inside_index = true;
165 return true;
166 }
167
168 if (c == ']')
169 {
170 if (*inside_index)
171 {
172 *inside_index = false;
173 }
174 return true;
175 }
176
177 if ((c == ' ') && *inside_index)
178 {
179 return true;
180 }
181
182 return false;
183 }
184
ParseToken(const char * expr,int start,int end)185 static StringParseResult ParseToken(const char *expr, int start, int end)
186 {
187 int endlit = start;
188
189 bool inside_index = false;
190 while (endlit < end && ValidTokenCharacter(expr[endlit], &inside_index))
191 {
192 endlit++;
193 }
194
195 if (endlit > start)
196 {
197 StringExpression *ret = xcalloc(1, sizeof(StringExpression));
198
199 ret->op = LITERAL;
200 ret->val.literal.literal = xstrndup(expr + start, endlit - start);
201
202 return (StringParseResult) {ret, endlit};
203 }
204 else
205 {
206 return (StringParseResult) {NULL, endlit};
207 }
208 }
209
210 /* <term> */
211
ParseTerm(const char * expr,int start,int end)212 static StringParseResult ParseTerm(const char *expr, int start, int end)
213 {
214 StringParseResult res = ParseToken(expr, start, end);
215
216 if (res.result)
217 {
218 return res;
219 }
220 else
221 {
222 return ParseVarRef(expr, start, end);
223 }
224 }
225
226 /* <name> */
227
ParseStringExpression(const char * expr,int start,int end)228 StringParseResult ParseStringExpression(const char *expr, int start, int end)
229 {
230 StringParseResult lhs = ParseTerm(expr, start, end);
231
232 if (lhs.result)
233 {
234 StringParseResult rhs = ParseStringExpression(expr, lhs.position, end);
235
236 if (rhs.result)
237 {
238 StringExpression *ret = xcalloc(1, sizeof(StringExpression));
239
240 ret->op = CONCAT;
241 ret->val.concat.lhs = lhs.result;
242 ret->val.concat.rhs = rhs.result;
243
244 return (StringParseResult) {ret, rhs.position};
245 }
246 else
247 {
248 return lhs;
249 }
250 }
251 else
252 {
253 return lhs;
254 }
255 }
256
257 /* Evaluation */
258
EvalConcat(const StringExpression * expr,VarRefEvaluator evalfn,void * param)259 static char *EvalConcat(const StringExpression *expr, VarRefEvaluator evalfn, void *param)
260 {
261 char *lhs, *rhs, *res;
262
263 lhs = EvalStringExpression(expr->val.concat.lhs, evalfn, param);
264 if (!lhs)
265 {
266 return NULL;
267 }
268
269 rhs = EvalStringExpression(expr->val.concat.rhs, evalfn, param);
270 if (!rhs)
271 {
272 free(lhs);
273 return NULL;
274 }
275
276 xasprintf(&res, "%s%s", lhs, rhs);
277 free(lhs);
278 free(rhs);
279 return res;
280 }
281
EvalVarRef(const StringExpression * expr,VarRefEvaluator evalfn,void * param)282 static char *EvalVarRef(const StringExpression *expr, VarRefEvaluator evalfn, void *param)
283 {
284 char *name, *eval;
285
286 name = EvalStringExpression(expr->val.varref.name, evalfn, param);
287 if (!name)
288 {
289 return NULL;
290 }
291
292 eval = (*evalfn) (name, expr->val.varref.type, param);
293 free(name);
294 return eval;
295 }
296
EvalStringExpression(const StringExpression * expr,VarRefEvaluator evalfn,void * param)297 char *EvalStringExpression(const StringExpression *expr, VarRefEvaluator evalfn, void *param)
298 {
299 switch (expr->op)
300 {
301 case CONCAT:
302 return EvalConcat(expr, evalfn, param);
303 case LITERAL:
304 return xstrdup(expr->val.literal.literal);
305 case VARREF:
306 return EvalVarRef(expr, evalfn, param);
307 default:
308 ProgrammingError("Unknown type of string expression" "encountered during evaluation: %d", expr->op);
309 }
310 }
311
312 /* Freeing results */
313
FreeStringExpression(StringExpression * expr)314 void FreeStringExpression(StringExpression *expr)
315 {
316 if (!expr)
317 {
318 return;
319 }
320
321 switch (expr->op)
322 {
323 case CONCAT:
324 FreeStringExpression(expr->val.concat.lhs);
325 FreeStringExpression(expr->val.concat.rhs);
326 break;
327 case LITERAL:
328 free(expr->val.literal.literal);
329 break;
330 case VARREF:
331 FreeStringExpression(expr->val.varref.name);
332 break;
333 default:
334 ProgrammingError("Unknown type of string expression encountered: %d", expr->op);
335 }
336
337 free(expr);
338 }
339