blob: 129fbd96b87849056bae0f3c4711600e0e5982e8 [file] [log] [blame]
Doug Zongker37bee622009-06-08 17:35:39 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string.h>
18#include <stdbool.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <stdarg.h>
Doug Zongker9931f7f2009-06-10 14:11:53 -070022#include <unistd.h>
Doug Zongker37bee622009-06-08 17:35:39 -070023
24#include "expr.h"
25
26// Functions should:
27//
28// - return a malloc()'d string
29// - if Evaluate() on any argument returns NULL, return NULL.
30
31int BooleanString(const char* s) {
32 return s[0] != '\0';
33}
34
35char* Evaluate(void* cookie, Expr* expr) {
36 return expr->fn(expr->name, cookie, expr->argc, expr->argv);
37}
38
39char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]) {
40 if (argc == 0) {
41 return strdup("");
42 }
43 char** strings = malloc(argc * sizeof(char*));
44 int i;
45 for (i = 0; i < argc; ++i) {
46 strings[i] = NULL;
47 }
48 char* result = NULL;
49 int length = 0;
50 for (i = 0; i < argc; ++i) {
51 strings[i] = Evaluate(cookie, argv[i]);
52 if (strings[i] == NULL) {
53 goto done;
54 }
55 length += strlen(strings[i]);
56 }
57
58 result = malloc(length+1);
59 int p = 0;
60 for (i = 0; i < argc; ++i) {
61 strcpy(result+p, strings[i]);
62 p += strlen(strings[i]);
63 }
64 result[p] = '\0';
65
66done:
67 for (i = 0; i < argc; ++i) {
68 free(strings[i]);
69 }
70 return result;
71}
72
73char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) {
74 if (argc != 2 && argc != 3) {
75 return NULL;
76 }
77 char* cond = Evaluate(cookie, argv[0]);
78 if (cond == NULL) {
79 return NULL;
80 }
81
82 if (BooleanString(cond) == true) {
83 free(cond);
84 return Evaluate(cookie, argv[1]);
85 } else {
86 if (argc == 3) {
87 free(cond);
88 return Evaluate(cookie, argv[2]);
89 } else {
90 return cond;
91 }
92 }
93}
94
95char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -070096 char* msg = NULL;
97 if (argc > 0) {
98 msg = Evaluate(cookie, argv[0]);
99 }
100 SetError(msg == NULL ? "called abort()" : msg);
101 free(msg);
Doug Zongker37bee622009-06-08 17:35:39 -0700102 return NULL;
103}
104
105char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) {
106 int i;
107 for (i = 0; i < argc; ++i) {
108 char* v = Evaluate(cookie, argv[i]);
109 if (v == NULL) {
110 return NULL;
111 }
112 int b = BooleanString(v);
113 free(v);
114 if (!b) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700115 SetError("assert() failed");
Doug Zongker37bee622009-06-08 17:35:39 -0700116 return NULL;
117 }
118 }
119 return strdup("");
120}
121
Doug Zongker9931f7f2009-06-10 14:11:53 -0700122char* SleepFn(const char* name, void* cookie, int argc, Expr* argv[]) {
123 char* val = Evaluate(cookie, argv[0]);
124 if (val == NULL) {
125 return NULL;
126 }
127 int v = strtol(val, NULL, 10);
128 sleep(v);
129 return val;
130}
131
Doug Zongker37bee622009-06-08 17:35:39 -0700132char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) {
133 int i;
134 for (i = 0; i < argc; ++i) {
135 char* v = Evaluate(cookie, argv[i]);
136 if (v == NULL) {
137 return NULL;
138 }
139 fputs(v, stdout);
140 free(v);
141 }
142 return strdup("");
143}
144
145char* LogicalAndFn(const char* name, void* cookie,
146 int argc, Expr* argv[]) {
147 char* left = Evaluate(cookie, argv[0]);
148 if (left == NULL) return NULL;
149 if (BooleanString(left) == true) {
150 free(left);
151 return Evaluate(cookie, argv[1]);
152 } else {
153 return left;
154 }
155}
156
157char* LogicalOrFn(const char* name, void* cookie,
158 int argc, Expr* argv[]) {
159 char* left = Evaluate(cookie, argv[0]);
160 if (left == NULL) return NULL;
161 if (BooleanString(left) == false) {
162 free(left);
163 return Evaluate(cookie, argv[1]);
164 } else {
165 return left;
166 }
167}
168
169char* LogicalNotFn(const char* name, void* cookie,
170 int argc, Expr* argv[]) {
171 char* val = Evaluate(cookie, argv[0]);
172 if (val == NULL) return NULL;
173 bool bv = BooleanString(val);
174 free(val);
175 if (bv) {
176 return strdup("");
177 } else {
178 return strdup("t");
179 }
180}
181
182char* SubstringFn(const char* name, void* cookie,
183 int argc, Expr* argv[]) {
184 char* needle = Evaluate(cookie, argv[0]);
185 if (needle == NULL) return NULL;
186 char* haystack = Evaluate(cookie, argv[1]);
187 if (haystack == NULL) {
188 free(needle);
189 return NULL;
190 }
191
192 char* result = strdup(strstr(haystack, needle) ? "t" : "");
193 free(needle);
194 free(haystack);
195 return result;
196}
197
198char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]) {
199 char* left = Evaluate(cookie, argv[0]);
200 if (left == NULL) return NULL;
201 char* right = Evaluate(cookie, argv[1]);
202 if (right == NULL) {
203 free(left);
204 return NULL;
205 }
206
207 char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
208 free(left);
209 free(right);
210 return result;
211}
212
213char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]) {
214 char* left = Evaluate(cookie, argv[0]);
215 if (left == NULL) return NULL;
216 char* right = Evaluate(cookie, argv[1]);
217 if (right == NULL) {
218 free(left);
219 return NULL;
220 }
221
222 char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
223 free(left);
224 free(right);
225 return result;
226}
227
228char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]) {
229 char* left = Evaluate(cookie, argv[0]);
230 if (left == NULL) return NULL;
231 free(left);
232 return Evaluate(cookie, argv[1]);
233}
234
235char* Literal(const char* name, void* cookie, int argc, Expr* argv[]) {
236 return strdup(name);
237}
238
239Expr* Build(Function fn, int count, ...) {
240 va_list v;
241 va_start(v, count);
242 Expr* e = malloc(sizeof(Expr));
243 e->fn = fn;
244 e->name = "(operator)";
245 e->argc = count;
246 e->argv = malloc(count * sizeof(Expr*));
247 int i;
248 for (i = 0; i < count; ++i) {
249 e->argv[i] = va_arg(v, Expr*);
250 }
251 va_end(v);
252 return e;
253}
254
Doug Zongker9931f7f2009-06-10 14:11:53 -0700255// -----------------------------------------------------------------
256// error reporting
257// -----------------------------------------------------------------
258
259static char* error_message = NULL;
260
261void SetError(const char* message) {
262 if (error_message) {
263 free(error_message);
264 }
265 error_message = strdup(message);
266}
267
268const char* GetError() {
269 return error_message;
270}
271
272void ClearError() {
273 free(error_message);
274 error_message = NULL;
275}
276
277// -----------------------------------------------------------------
278// the function table
279// -----------------------------------------------------------------
280
Doug Zongker37bee622009-06-08 17:35:39 -0700281static int fn_entries = 0;
282static int fn_size = 0;
283NamedFunction* fn_table = NULL;
284
285void RegisterFunction(const char* name, Function fn) {
286 if (fn_entries <= fn_size) {
287 fn_size = fn_size*2 + 1;
288 fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
289 }
290 fn_table[fn_entries].name = name;
291 fn_table[fn_entries].fn = fn;
292 ++fn_entries;
293}
294
295static int fn_entry_compare(const void* a, const void* b) {
296 const char* na = ((const NamedFunction*)a)->name;
297 const char* nb = ((const NamedFunction*)b)->name;
298 return strcmp(na, nb);
299}
300
301void FinishRegistration() {
302 qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
303}
304
305Function FindFunction(const char* name) {
306 NamedFunction key;
307 key.name = name;
308 NamedFunction* nf = bsearch(&key, fn_table, fn_entries, sizeof(NamedFunction),
309 fn_entry_compare);
310 if (nf == NULL) {
311 return NULL;
312 }
313 return nf->fn;
314}
315
316void RegisterBuiltins() {
317 RegisterFunction("ifelse", IfElseFn);
318 RegisterFunction("abort", AbortFn);
319 RegisterFunction("assert", AssertFn);
320 RegisterFunction("concat", ConcatFn);
321 RegisterFunction("is_substring", SubstringFn);
322 RegisterFunction("print", PrintFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700323 RegisterFunction("sleep", SleepFn);
324}
325
326
327// -----------------------------------------------------------------
328// convenience methods for functions
329// -----------------------------------------------------------------
330
331// Evaluate the expressions in argv, giving 'count' char* (the ... is
332// zero or more char** to put them in). If any expression evaluates
333// to NULL, free the rest and return -1. Return 0 on success.
334int ReadArgs(void* cookie, Expr* argv[], int count, ...) {
335 char** args = malloc(count * sizeof(char*));
336 va_list v;
337 va_start(v, count);
338 int i;
339 for (i = 0; i < count; ++i) {
340 args[i] = Evaluate(cookie, argv[i]);
341 if (args[i] == NULL) {
342 va_end(v);
343 int j;
344 for (j = 0; j < i; ++j) {
345 free(args[j]);
346 }
347 return -1;
348 }
349 *(va_arg(v, char**)) = args[i];
350 }
351 va_end(v);
352 return 0;
353}
354
355// Evaluate the expressions in argv, returning an array of char*
356// results. If any evaluate to NULL, free the rest and return NULL.
357// The caller is responsible for freeing the returned array and the
358// strings it contains.
359char** ReadVarArgs(void* cookie, int argc, Expr* argv[]) {
360 char** args = (char**)malloc(argc * sizeof(char*));
361 int i = 0;
362 for (i = 0; i < argc; ++i) {
363 args[i] = Evaluate(cookie, argv[i]);
364 if (args[i] == NULL) {
365 int j;
366 for (j = 0; j < i; ++j) {
367 free(args[j]);
368 }
369 free(args);
370 return NULL;
371 }
372 }
373 return args;
Doug Zongker37bee622009-06-08 17:35:39 -0700374}