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