diff --git a/edify/expr.c b/edify/expr.c index 5470a2b..406c67e 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -29,249 +29,241 @@ // - if Evaluate() on any argument returns NULL, return NULL. int BooleanString(const char* s) { - return s[0] != '\0'; + return s[0] != '\0'; } -char* Evaluate(void* cookie, Expr* expr) { - return expr->fn(expr->name, cookie, expr->argc, expr->argv); +char* Evaluate(State* state, Expr* expr) { + return expr->fn(expr->name, state, expr->argc, expr->argv); } -char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]) { - if (argc == 0) { - return strdup(""); - } - char** strings = malloc(argc * sizeof(char*)); - int i; - for (i = 0; i < argc; ++i) { - strings[i] = NULL; - } - char* result = NULL; - int length = 0; - for (i = 0; i < argc; ++i) { - strings[i] = Evaluate(cookie, argv[i]); - if (strings[i] == NULL) { - goto done; +char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return strdup(""); + } + char** strings = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + strings[i] = NULL; + } + char* result = NULL; + int length = 0; + for (i = 0; i < argc; ++i) { + strings[i] = Evaluate(state, argv[i]); + if (strings[i] == NULL) { + goto done; + } + length += strlen(strings[i]); } - length += strlen(strings[i]); - } - result = malloc(length+1); - int p = 0; - for (i = 0; i < argc; ++i) { - strcpy(result+p, strings[i]); - p += strlen(strings[i]); - } - result[p] = '\0'; + result = malloc(length+1); + int p = 0; + for (i = 0; i < argc; ++i) { + strcpy(result+p, strings[i]); + p += strlen(strings[i]); + } + result[p] = '\0'; -done: - for (i = 0; i < argc; ++i) { - free(strings[i]); - } - return result; + done: + for (i = 0; i < argc; ++i) { + free(strings[i]); + } + return result; } -char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]) { - if (argc != 2 && argc != 3) { - return NULL; - } - char* cond = Evaluate(cookie, argv[0]); - if (cond == NULL) { - return NULL; - } +char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2 && argc != 3) { + return NULL; + } + char* cond = Evaluate(state, argv[0]); + if (cond == NULL) { + return NULL; + } - if (BooleanString(cond) == true) { - free(cond); - return Evaluate(cookie, argv[1]); - } else { - if (argc == 3) { - free(cond); - return Evaluate(cookie, argv[2]); + if (BooleanString(cond) == true) { + free(cond); + return Evaluate(state, argv[1]); } else { - return cond; + if (argc == 3) { + free(cond); + return Evaluate(state, argv[2]); + } else { + return cond; + } } - } } -char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* msg = NULL; - if (argc > 0) { - msg = Evaluate(cookie, argv[0]); - } - SetError(msg == NULL ? "called abort()" : msg); - free(msg); - return NULL; -} - -char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]) { - int i; - for (i = 0; i < argc; ++i) { - char* v = Evaluate(cookie, argv[i]); - if (v == NULL) { - return NULL; +char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) { + char* msg = NULL; + if (argc > 0) { + msg = Evaluate(state, argv[0]); } - int b = BooleanString(v); - free(v); - if (!b) { - SetError("assert() failed"); - return NULL; + free(state->errmsg); + if (msg) { + state->errmsg = msg; + } else { + state->errmsg = strdup("called abort()"); } - } - return strdup(""); -} - -char* SleepFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* val = Evaluate(cookie, argv[0]); - if (val == NULL) { return NULL; - } - int v = strtol(val, NULL, 10); - sleep(v); - return val; } -char* PrintFn(const char* name, void* cookie, int argc, Expr* argv[]) { - int i; - for (i = 0; i < argc; ++i) { - char* v = Evaluate(cookie, argv[i]); - if (v == NULL) { - return NULL; +char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(state, argv[i]); + if (v == NULL) { + return NULL; + } + int b = BooleanString(v); + free(v); + if (!b) { + int prefix_len; + int len = argv[i]->end - argv[i]->start; + char* err_src = malloc(len + 20); + strcpy(err_src, "assert failed: "); + prefix_len = strlen(err_src); + memcpy(err_src + prefix_len, state->script + argv[i]->start, len); + err_src[prefix_len + len] = '\0'; + free(state->errmsg); + state->errmsg = err_src; + return NULL; + } } - fputs(v, stdout); - free(v); - } - return strdup(""); -} - -char* LogicalAndFn(const char* name, void* cookie, - int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - if (BooleanString(left) == true) { - free(left); - return Evaluate(cookie, argv[1]); - } else { - return left; - } -} - -char* LogicalOrFn(const char* name, void* cookie, - int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - if (BooleanString(left) == false) { - free(left); - return Evaluate(cookie, argv[1]); - } else { - return left; - } -} - -char* LogicalNotFn(const char* name, void* cookie, - int argc, Expr* argv[]) { - char* val = Evaluate(cookie, argv[0]); - if (val == NULL) return NULL; - bool bv = BooleanString(val); - free(val); - if (bv) { return strdup(""); - } else { - return strdup("t"); - } } -char* SubstringFn(const char* name, void* cookie, +char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) { + char* val = Evaluate(state, argv[0]); + if (val == NULL) { + return NULL; + } + int v = strtol(val, NULL, 10); + sleep(v); + return val; +} + +char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(state, argv[i]); + if (v == NULL) { + return NULL; + } + fputs(v, stdout); + free(v); + } + return strdup(""); +} + +char* LogicalAndFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == true) { + free(left); + return Evaluate(state, argv[1]); + } else { + return left; + } +} + +char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]) { - char* needle = Evaluate(cookie, argv[0]); - if (needle == NULL) return NULL; - char* haystack = Evaluate(cookie, argv[1]); - if (haystack == NULL) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == false) { + free(left); + return Evaluate(state, argv[1]); + } else { + return left; + } +} + +char* LogicalNotFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* val = Evaluate(state, argv[0]); + if (val == NULL) return NULL; + bool bv = BooleanString(val); + free(val); + if (bv) { + return strdup(""); + } else { + return strdup("t"); + } +} + +char* SubstringFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* needle = Evaluate(state, argv[0]); + if (needle == NULL) return NULL; + char* haystack = Evaluate(state, argv[1]); + if (haystack == NULL) { + free(needle); + return NULL; + } + + char* result = strdup(strstr(haystack, needle) ? "t" : ""); free(needle); - return NULL; - } - - char* result = strdup(strstr(haystack, needle) ? "t" : ""); - free(needle); - free(haystack); - return result; + free(haystack); + return result; } -char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - char* right = Evaluate(cookie, argv[1]); - if (right == NULL) { +char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(state, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } + + char* result = strdup(strcmp(left, right) == 0 ? "t" : ""); free(left); - return NULL; - } - - char* result = strdup(strcmp(left, right) == 0 ? "t" : ""); - free(left); - free(right); - return result; + free(right); + return result; } -char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - char* right = Evaluate(cookie, argv[1]); - if (right == NULL) { +char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(state, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } + + char* result = strdup(strcmp(left, right) != 0 ? "t" : ""); free(left); - return NULL; - } - - char* result = strdup(strcmp(left, right) != 0 ? "t" : ""); - free(left); - free(right); - return result; + free(right); + return result; } -char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]) { - char* left = Evaluate(cookie, argv[0]); - if (left == NULL) return NULL; - free(left); - return Evaluate(cookie, argv[1]); +char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + free(left); + return Evaluate(state, argv[1]); } -char* Literal(const char* name, void* cookie, int argc, Expr* argv[]) { - return strdup(name); +char* Literal(const char* name, State* state, int argc, Expr* argv[]) { + return strdup(name); } -Expr* Build(Function fn, int count, ...) { - va_list v; - va_start(v, count); - Expr* e = malloc(sizeof(Expr)); - e->fn = fn; - e->name = "(operator)"; - e->argc = count; - e->argv = malloc(count * sizeof(Expr*)); - int i; - for (i = 0; i < count; ++i) { - e->argv[i] = va_arg(v, Expr*); - } - va_end(v); - return e; -} - -// ----------------------------------------------------------------- -// error reporting -// ----------------------------------------------------------------- - -static char* error_message = NULL; - -void SetError(const char* message) { - if (error_message) { - free(error_message); - } - error_message = strdup(message); -} - -const char* GetError() { - return error_message; -} - -void ClearError() { - free(error_message); - error_message = NULL; +Expr* Build(Function fn, YYLTYPE loc, int count, ...) { + va_list v; + va_start(v, count); + Expr* e = malloc(sizeof(Expr)); + e->fn = fn; + e->name = "(operator)"; + e->argc = count; + e->argv = malloc(count * sizeof(Expr*)); + int i; + for (i = 0; i < count; ++i) { + e->argv[i] = va_arg(v, Expr*); + } + va_end(v); + e->start = loc.start; + e->end = loc.end; + return e; } // ----------------------------------------------------------------- @@ -283,44 +275,44 @@ static int fn_size = 0; NamedFunction* fn_table = NULL; void RegisterFunction(const char* name, Function fn) { - if (fn_entries >= fn_size) { - fn_size = fn_size*2 + 1; - fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); - } - fn_table[fn_entries].name = name; - fn_table[fn_entries].fn = fn; - ++fn_entries; + if (fn_entries >= fn_size) { + fn_size = fn_size*2 + 1; + fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); + } + fn_table[fn_entries].name = name; + fn_table[fn_entries].fn = fn; + ++fn_entries; } static int fn_entry_compare(const void* a, const void* b) { - const char* na = ((const NamedFunction*)a)->name; - const char* nb = ((const NamedFunction*)b)->name; - return strcmp(na, nb); + const char* na = ((const NamedFunction*)a)->name; + const char* nb = ((const NamedFunction*)b)->name; + return strcmp(na, nb); } void FinishRegistration() { - qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); + qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); } Function FindFunction(const char* name) { - NamedFunction key; - key.name = name; - NamedFunction* nf = bsearch(&key, fn_table, fn_entries, sizeof(NamedFunction), - fn_entry_compare); - if (nf == NULL) { - return NULL; - } - return nf->fn; + NamedFunction key; + key.name = name; + NamedFunction* nf = bsearch(&key, fn_table, fn_entries, + sizeof(NamedFunction), fn_entry_compare); + if (nf == NULL) { + return NULL; + } + return nf->fn; } void RegisterBuiltins() { - RegisterFunction("ifelse", IfElseFn); - RegisterFunction("abort", AbortFn); - RegisterFunction("assert", AssertFn); - RegisterFunction("concat", ConcatFn); - RegisterFunction("is_substring", SubstringFn); - RegisterFunction("print", PrintFn); - RegisterFunction("sleep", SleepFn); + RegisterFunction("ifelse", IfElseFn); + RegisterFunction("abort", AbortFn); + RegisterFunction("assert", AssertFn); + RegisterFunction("concat", ConcatFn); + RegisterFunction("is_substring", SubstringFn); + RegisterFunction("stdout", StdoutFn); + RegisterFunction("sleep", SleepFn); } @@ -331,44 +323,44 @@ void RegisterBuiltins() { // Evaluate the expressions in argv, giving 'count' char* (the ... is // zero or more char** to put them in). If any expression evaluates // to NULL, free the rest and return -1. Return 0 on success. -int ReadArgs(void* cookie, Expr* argv[], int count, ...) { - char** args = malloc(count * sizeof(char*)); - va_list v; - va_start(v, count); - int i; - for (i = 0; i < count; ++i) { - args[i] = Evaluate(cookie, argv[i]); - if (args[i] == NULL) { - va_end(v); - int j; - for (j = 0; j < i; ++j) { - free(args[j]); - } - return -1; +int ReadArgs(State* state, Expr* argv[], int count, ...) { + char** args = malloc(count * sizeof(char*)); + va_list v; + va_start(v, count); + int i; + for (i = 0; i < count; ++i) { + args[i] = Evaluate(state, argv[i]); + if (args[i] == NULL) { + va_end(v); + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + return -1; + } + *(va_arg(v, char**)) = args[i]; } - *(va_arg(v, char**)) = args[i]; - } - va_end(v); - return 0; + va_end(v); + return 0; } // Evaluate the expressions in argv, returning an array of char* // results. If any evaluate to NULL, free the rest and return NULL. // The caller is responsible for freeing the returned array and the // strings it contains. -char** ReadVarArgs(void* cookie, int argc, Expr* argv[]) { - char** args = (char**)malloc(argc * sizeof(char*)); - int i = 0; - for (i = 0; i < argc; ++i) { - args[i] = Evaluate(cookie, argv[i]); - if (args[i] == NULL) { - int j; - for (j = 0; j < i; ++j) { - free(args[j]); - } - free(args); - return NULL; +char** ReadVarArgs(State* state, int argc, Expr* argv[]) { + char** args = (char**)malloc(argc * sizeof(char*)); + int i = 0; + for (i = 0; i < argc; ++i) { + args[i] = Evaluate(state, argv[i]); + if (args[i] == NULL) { + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + free(args); + return NULL; + } } - } - return args; + return args; } diff --git a/edify/expr.h b/edify/expr.h index cfbef90..671b499 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -17,45 +17,64 @@ #ifndef _EXPRESSION_H #define _EXPRESSION_H +#include "yydefs.h" + #define MAX_STRING_LEN 1024 typedef struct Expr Expr; -typedef char* (*Function)(const char* name, void* cookie, +typedef struct { + // Optional pointer to app-specific data; the core of edify never + // uses this value. + void* cookie; + + // The source of the original script. Must be NULL-terminated, + // and in writable memory (Evaluate may make temporary changes to + // it but will restore it when done). + char* script; + + // The error message (if any) returned if the evaluation aborts. + // Should be NULL initially, will be either NULL or a malloc'd + // pointer after Evaluate() returns. + char* errmsg; +} State; + +typedef char* (*Function)(const char* name, State* state, int argc, Expr* argv[]); struct Expr { - Function fn; - char* name; - int argc; - Expr** argv; + Function fn; + char* name; + int argc; + Expr** argv; + int start, end; }; -char* Evaluate(void* cookie, Expr* expr); +char* Evaluate(State* state, Expr* expr); // Glue to make an Expr out of a literal. -char* Literal(const char* name, void* cookie, int argc, Expr* argv[]); +char* Literal(const char* name, State* state, int argc, Expr* argv[]); // Functions corresponding to various syntactic sugar operators. // ("concat" is also available as a builtin function, to concatenate // more than two strings.) -char* ConcatFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* LogicalAndFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* LogicalOrFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* LogicalNotFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* SubstringFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* EqualityFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* InequalityFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* SequenceFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]); +char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]); +char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]); +char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]); +char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]); // Convenience function for building expressions with a fixed number // of arguments. -Expr* Build(Function fn, int count, ...); +Expr* Build(Function fn, YYLTYPE loc, int count, ...); // Global builtins, registered by RegisterBuiltins(). -char* IfElseFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* AssertFn(const char* name, void* cookie, int argc, Expr* argv[]); -char* AbortFn(const char* name, void* cookie, int argc, Expr* argv[]); +char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]); +char* AssertFn(const char* name, State* state, int argc, Expr* argv[]); +char* AbortFn(const char* name, State* state, int argc, Expr* argv[]); // For setting and getting the global error string (when returning @@ -91,13 +110,13 @@ Function FindFunction(const char* name); // Evaluate the expressions in argv, giving 'count' char* (the ... is // zero or more char** to put them in). If any expression evaluates // to NULL, free the rest and return -1. Return 0 on success. -int ReadArgs(void* cookie, Expr* argv[], int count, ...); +int ReadArgs(State* state, Expr* argv[], int count, ...); // Evaluate the expressions in argv, returning an array of char* // results. If any evaluate to NULL, free the rest and return NULL. // The caller is responsible for freeing the returned array and the // strings it contains. -char** ReadVarArgs(void* cookie, int argc, Expr* argv[]); +char** ReadVarArgs(State* state, int argc, Expr* argv[]); #endif // _EXPRESSION_H diff --git a/edify/lexer.l b/edify/lexer.l index cb5eb31..2c4489c 100644 --- a/edify/lexer.l +++ b/edify/lexer.l @@ -16,14 +16,20 @@ */ #include "expr.h" +#include "yydefs.h" #include "parser.h" int gLine = 1; int gColumn = 1; +int gPos = 0; // TODO: enforce MAX_STRING_LEN during lexing char string_buffer[MAX_STRING_LEN]; char* string_pos; + +#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \ + gColumn+=yyleng; gPos+=yyleng;} while(0) + %} %x STR @@ -34,27 +40,32 @@ char* string_pos; \" { - ++gColumn; BEGIN(STR); string_pos = string_buffer; + yylloc.start = gPos; + ++gColumn; + ++gPos; } { \" { ++gColumn; + ++gPos; BEGIN(INITIAL); *string_pos = '\0'; yylval.str = strdup(string_buffer); + yylloc.end = gPos; return STRING; } - \\n { gColumn += yyleng; *string_pos++ = '\n'; } - \\t { gColumn += yyleng; *string_pos++ = '\t'; } - \\\" { gColumn += yyleng; *string_pos++ = '\"'; } - \\\\ { gColumn += yyleng; *string_pos++ = '\\'; } + \\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; } + \\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; } + \\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; } + \\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; } \\x[0-9a-fA-F]{2} { gColumn += yyleng; + gPos += yyleng; int val; sscanf(yytext+2, "%x", &val); *string_pos++ = val; @@ -62,36 +73,38 @@ char* string_pos; \n { ++gLine; + ++gPos; gColumn = 1; *string_pos++ = yytext[0]; } . { ++gColumn; + ++gPos; *string_pos++ = yytext[0]; } } -if { gColumn += yyleng; return IF; } -then { gColumn += yyleng; return THEN; } -else { gColumn += yyleng; return ELSE; } -endif { gColumn += yyleng; return ENDIF; } +if ADVANCE; return IF; +then ADVANCE; return THEN; +else ADVANCE; return ELSE; +endif ADVANCE; return ENDIF; [a-zA-Z0-9_:/.]+ { - gColumn += yyleng; + ADVANCE; yylval.str = strdup(yytext); return STRING; } -\&\& { gColumn += yyleng; return AND; } -\|\| { gColumn += yyleng; return OR; } -== { gColumn += yyleng; return EQ; } -!= { gColumn += yyleng; return NE; } +\&\& ADVANCE; return AND; +\|\| ADVANCE; return OR; +== ADVANCE; return EQ; +!= ADVANCE; return NE; -[+(),!;] { gColumn += yyleng; return yytext[0]; } +[+(),!;] ADVANCE; return yytext[0]; -[ \t]+ gColumn += yyleng; +[ \t]+ ADVANCE; -(#.*)?\n { ++gLine; gColumn = 1; } +(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1; . return BAD; diff --git a/edify/main.c b/edify/main.c index 7da89e2..03eefc6 100644 --- a/edify/main.c +++ b/edify/main.c @@ -21,152 +21,183 @@ #include "expr.h" #include "parser.h" +extern int yyparse(Expr** root, int* error_count); + int expect(const char* expr_str, const char* expected, int* errors) { - Expr* e; - int error; - char* result; + Expr* e; + int error; + char* result; - printf("."); + printf("."); - yy_scan_string(expr_str); - error = yyparse(&e); - if (error > 0) { - fprintf(stderr, "error parsing \"%s\"\n", expr_str); - ++*errors; - return 0; - } + yy_scan_string(expr_str); + int error_count = 0; + error = yyparse(&e, &error_count); + if (error > 0 || error_count > 0) { + fprintf(stderr, "error parsing \"%s\" (%d errors)\n", + expr_str, error_count); + ++*errors; + return 0; + } - result = Evaluate(NULL, e); - if (result == NULL && expected != NULL) { - fprintf(stderr, "error evaluating \"%s\"\n", expr_str); - ++*errors; - return 0; - } + State state; + state.cookie = NULL; + state.script = expr_str; + state.errmsg = NULL; - if (result == NULL && expected == NULL) { - return 1; - } + result = Evaluate(&state, e); + free(state.errmsg); + if (result == NULL && expected != NULL) { + fprintf(stderr, "error evaluating \"%s\"\n", expr_str); + ++*errors; + return 0; + } + + if (result == NULL && expected == NULL) { + return 1; + } + + if (strcmp(result, expected) != 0) { + fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", + expr_str, expected, result); + ++*errors; + free(result); + return 0; + } - if (strcmp(result, expected) != 0) { - fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", - expr_str, expected, result); - ++*errors; free(result); - return 0; - } - - free(result); - return 1; + return 1; } int test() { - int errors = 0; + int errors = 0; - expect("a", "a", &errors); - expect("\"a\"", "a", &errors); - expect("\"\\x61\"", "a", &errors); - expect("# this is a comment\n" - " a\n" - " \n", - "a", &errors); + expect("a", "a", &errors); + expect("\"a\"", "a", &errors); + expect("\"\\x61\"", "a", &errors); + expect("# this is a comment\n" + " a\n" + " \n", + "a", &errors); - // sequence operator - expect("a; b; c", "c", &errors); + // sequence operator + expect("a; b; c", "c", &errors); - // string concat operator - expect("a + b", "ab", &errors); - expect("a + \n \"b\"", "ab", &errors); - expect("a + b +\nc\n", "abc", &errors); + // string concat operator + expect("a + b", "ab", &errors); + expect("a + \n \"b\"", "ab", &errors); + expect("a + b +\nc\n", "abc", &errors); - // string concat function - expect("concat(a, b)", "ab", &errors); - expect("concat(a,\n \"b\")", "ab", &errors); - expect("concat(a + b,\nc,\"d\")", "abcd", &errors); - expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); + // string concat function + expect("concat(a, b)", "ab", &errors); + expect("concat(a,\n \"b\")", "ab", &errors); + expect("concat(a + b,\nc,\"d\")", "abcd", &errors); + expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); - // logical and - expect("a && b", "b", &errors); - expect("a && \"\"", "", &errors); - expect("\"\" && b", "", &errors); - expect("\"\" && \"\"", "", &errors); - expect("\"\" && abort()", "", &errors); // test short-circuiting - expect("t && abort()", NULL, &errors); + // logical and + expect("a && b", "b", &errors); + expect("a && \"\"", "", &errors); + expect("\"\" && b", "", &errors); + expect("\"\" && \"\"", "", &errors); + expect("\"\" && abort()", "", &errors); // test short-circuiting + expect("t && abort()", NULL, &errors); - // logical or - expect("a || b", "a", &errors); - expect("a || \"\"", "a", &errors); - expect("\"\" || b", "b", &errors); - expect("\"\" || \"\"", "", &errors); - expect("a || abort()", "a", &errors); // test short-circuiting - expect("\"\" || abort()", NULL, &errors); + // logical or + expect("a || b", "a", &errors); + expect("a || \"\"", "a", &errors); + expect("\"\" || b", "b", &errors); + expect("\"\" || \"\"", "", &errors); + expect("a || abort()", "a", &errors); // test short-circuiting + expect("\"\" || abort()", NULL, &errors); - // logical not - expect("!a", "", &errors); - expect("! \"\"", "t", &errors); - expect("!!a", "t", &errors); + // logical not + expect("!a", "", &errors); + expect("! \"\"", "t", &errors); + expect("!!a", "t", &errors); - // precedence - expect("\"\" == \"\" && b", "b", &errors); - expect("a + b == ab", "t", &errors); - expect("ab == a + b", "t", &errors); - expect("a + (b == ab)", "a", &errors); - expect("(ab == a) + b", "b", &errors); + // precedence + expect("\"\" == \"\" && b", "b", &errors); + expect("a + b == ab", "t", &errors); + expect("ab == a + b", "t", &errors); + expect("a + (b == ab)", "a", &errors); + expect("(ab == a) + b", "b", &errors); - // substring function - expect("is_substring(cad, abracadabra)", "t", &errors); - expect("is_substring(abrac, abracadabra)", "t", &errors); - expect("is_substring(dabra, abracadabra)", "t", &errors); - expect("is_substring(cad, abracxadabra)", "", &errors); - expect("is_substring(abrac, axbracadabra)", "", &errors); - expect("is_substring(dabra, abracadabrxa)", "", &errors); + // substring function + expect("is_substring(cad, abracadabra)", "t", &errors); + expect("is_substring(abrac, abracadabra)", "t", &errors); + expect("is_substring(dabra, abracadabra)", "t", &errors); + expect("is_substring(cad, abracxadabra)", "", &errors); + expect("is_substring(abrac, axbracadabra)", "", &errors); + expect("is_substring(dabra, abracadabrxa)", "", &errors); - // ifelse function - expect("ifelse(t, yes, no)", "yes", &errors); - expect("ifelse(!t, yes, no)", "no", &errors); - expect("ifelse(t, yes, abort())", "yes", &errors); - expect("ifelse(!t, abort(), no)", "no", &errors); + // ifelse function + expect("ifelse(t, yes, no)", "yes", &errors); + expect("ifelse(!t, yes, no)", "no", &errors); + expect("ifelse(t, yes, abort())", "yes", &errors); + expect("ifelse(!t, abort(), no)", "no", &errors); - // if "statements" - expect("if t then yes else no endif", "yes", &errors); - expect("if \"\" then yes else no endif", "no", &errors); - expect("if \"\" then yes endif", "", &errors); - expect("if \"\"; t then yes endif", "yes", &errors); + // if "statements" + expect("if t then yes else no endif", "yes", &errors); + expect("if \"\" then yes else no endif", "no", &errors); + expect("if \"\" then yes endif", "", &errors); + expect("if \"\"; t then yes endif", "yes", &errors); - printf("\n"); + printf("\n"); - return errors; + return errors; +} + +void ExprDump(int depth, Expr* n, char* script) { + printf("%*s", depth*2, ""); + char temp = script[n->end]; + script[n->end] = '\0'; + printf("%s %p (%d-%d) \"%s\"\n", + n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end, + script+n->start); + script[n->end] = temp; + int i; + for (i = 0; i < n->argc; ++i) { + ExprDump(depth+1, n->argv[i], script); + } } int main(int argc, char** argv) { - RegisterBuiltins(); - FinishRegistration(); + RegisterBuiltins(); + FinishRegistration(); - if (argc == 1) { - return test() != 0; - } - - FILE* f = fopen(argv[1], "r"); - char buffer[8192]; - int size = fread(buffer, 1, 8191, f); - fclose(f); - buffer[size] = '\0'; - - Expr* root; - int error_count = 0; - yy_scan_bytes(buffer, size); - int error = yyparse(&root, &error_count); - printf("parse returned %d; %d errors encountered\n", error, error_count); - if (error == 0 || error_count > 0) { - char* result = Evaluate(NULL, root); - if (result == NULL) { - char* errmsg = GetError(); - printf("result was NULL, message is: %s\n", - (errmsg == NULL ? "(NULL)" : errmsg)); - ClearError(); - } else { - printf("result is [%s]\n", result); + if (argc == 1) { + return test() != 0; } - } - return 0; + + FILE* f = fopen(argv[1], "r"); + char buffer[8192]; + int size = fread(buffer, 1, 8191, f); + fclose(f); + buffer[size] = '\0'; + + Expr* root; + int error_count = 0; + yy_scan_bytes(buffer, size); + int error = yyparse(&root, &error_count); + printf("parse returned %d; %d errors encountered\n", error, error_count); + if (error == 0 || error_count > 0) { + + ExprDump(0, root, buffer); + + State state; + state.cookie = NULL; + state.script = buffer; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); + if (result == NULL) { + printf("result was NULL, message is: %s\n", + (state.errmsg == NULL ? "(NULL)" : state.errmsg)); + free(state.errmsg); + } else { + printf("result is [%s]\n", result); + } + } + return 0; } diff --git a/edify/parser.y b/edify/parser.y index cf163c0..3f9ade1 100644 --- a/edify/parser.y +++ b/edify/parser.y @@ -20,6 +20,7 @@ #include #include "expr.h" +#include "yydefs.h" #include "parser.h" extern int gLine; @@ -30,6 +31,8 @@ int yyparse(Expr** root, int* error_count); %} +%locations + %union { char* str; Expr* expr; @@ -68,19 +71,21 @@ expr: STRING { $$->name = $1; $$->argc = 0; $$->argv = NULL; + $$->start = @$.start; + $$->end = @$.end; } -| '(' expr ')' { $$ = $2; } -| expr ';' { $$ = $1; } -| expr ';' expr { $$ = Build(SequenceFn, 2, $1, $3); } -| error ';' expr { $$ = $3; } -| expr '+' expr { $$ = Build(ConcatFn, 2, $1, $3); } -| expr EQ expr { $$ = Build(EqualityFn, 2, $1, $3); } -| expr NE expr { $$ = Build(InequalityFn, 2, $1, $3); } -| expr AND expr { $$ = Build(LogicalAndFn, 2, $1, $3); } -| expr OR expr { $$ = Build(LogicalOrFn, 2, $1, $3); } -| '!' expr { $$ = Build(LogicalNotFn, 1, $2); } -| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, 2, $2, $4); } -| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, 3, $2, $4, $6); } +| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; } +| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; } +| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); } +| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; } +| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); } +| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); } +| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); } +| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); } +| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); } +| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); } +| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); } +| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); } | STRING '(' arglist ')' { $$ = malloc(sizeof(Expr)); $$->fn = FindFunction($1); @@ -93,6 +98,8 @@ expr: STRING { $$->name = $1; $$->argc = $3.argc; $$->argv = $3.argv; + $$->start = @$.start; + $$->end = @$.end; } ; diff --git a/edify/yydefs.h b/edify/yydefs.h new file mode 100644 index 0000000..6257862 --- /dev/null +++ b/edify/yydefs.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _YYDEFS_H_ +#define _YYDEFS_H_ + +#define YYLTYPE YYLTYPE +typedef struct { + int start, end; +} YYLTYPE; + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) { \ + (Current).start = YYRHSLOC(Rhs, 1).start; \ + (Current).end = YYRHSLOC(Rhs, N).end; \ + } else { \ + (Current).start = YYRHSLOC(Rhs, 0).start; \ + (Current).end = YYRHSLOC(Rhs, 0).end; \ + } \ + } while (0) + +#endif diff --git a/install.c b/install.c index cca9400..c2e1385 100644 --- a/install.c +++ b/install.c @@ -196,6 +196,9 @@ try_update_binary(const char *path, ZipArchive *zip) { // arrange to install the contents of in the // given partition on reboot. // + // ui_print + // display on the screen. + // // - the name of the package zip file. // @@ -248,6 +251,13 @@ try_update_binary(const char *path, ZipArchive *zip) { firmware_filename = strdup(filename); } } + } else if (strcmp(command, "ui_print") == 0) { + char* str = strtok(NULL, "\n"); + if (str) { + ui_print(str); + } else { + ui_print("\n"); + } } else { LOGE("unknown command [%s]\n", command); } diff --git a/updater/install.c b/updater/install.c index 2e965ce..616cb2c 100644 --- a/updater/install.c +++ b/updater/install.c @@ -32,13 +32,14 @@ #include "mtdutils/mtdutils.h" #include "updater.h" -char* ErrorAbort(void* cookie, char* format, ...) { +char* ErrorAbort(State* state, char* format, ...) { char* buffer = malloc(4096); va_list v; va_start(v, format); vsnprintf(buffer, 4096, format, v); va_end(v); - SetError(buffer); + free(state->errmsg); + state->errmsg = buffer; return NULL; } @@ -47,28 +48,28 @@ char* ErrorAbort(void* cookie, char* format, ...) { // // what: type="MTD" location="" to mount a yaffs2 filesystem // type="vfat" location="/dev/block/" to mount a device -char* MountFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* MountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 3) { - return ErrorAbort(cookie, "%s() expects 3 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc); } char* type; char* location; char* mount_point; - if (ReadArgs(cookie, argv, 3, &type, &location, &mount_point) < 0) { + if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) { return NULL; } if (strlen(type) == 0) { - ErrorAbort(cookie, "type argument to %s() can't be empty", name); + ErrorAbort(state, "type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { - ErrorAbort(cookie, "location argument to %s() can't be empty", name); + ErrorAbort(state, "location argument to %s() can't be empty", name); goto done; } if (strlen(mount_point) == 0) { - ErrorAbort(cookie, "mount_point argument to %s() can't be empty", name); + ErrorAbort(state, "mount_point argument to %s() can't be empty", name); goto done; } @@ -109,17 +110,17 @@ done: // is_mounted(mount_point) -char* IsMountedFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { - return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } char* mount_point; - if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + if (ReadArgs(state, argv, 1, &mount_point) < 0) { return NULL; } if (strlen(mount_point) == 0) { - ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); goto done; } @@ -137,17 +138,17 @@ done: } -char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { - return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } char* mount_point; - if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + if (ReadArgs(state, argv, 1, &mount_point) < 0) { return NULL; } if (strlen(mount_point) == 0) { - ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); goto done; } @@ -170,23 +171,23 @@ done: // format(type, location) // // type="MTD" location=partition -char* FormatFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* type; char* location; - if (ReadArgs(cookie, argv, 2, &type, &location) < 0) { + if (ReadArgs(state, argv, 2, &type, &location) < 0) { return NULL; } if (strlen(type) == 0) { - ErrorAbort(cookie, "type argument to %s() can't be empty", name); + ErrorAbort(state, "type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { - ErrorAbort(cookie, "location argument to %s() can't be empty", name); + ErrorAbort(state, "location argument to %s() can't be empty", name); goto done; } @@ -228,11 +229,11 @@ done: } -char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { char** paths = malloc(argc * sizeof(char*)); int i; for (i = 0; i < argc; ++i) { - paths[i] = Evaluate(cookie, argv[i]); + paths[i] = Evaluate(state, argv[i]); if (paths[i] == NULL) { int j; for (j = 0; j < i; ++i) { @@ -259,20 +260,20 @@ char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { } -char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* frac_str; char* sec_str; - if (ReadArgs(cookie, argv, 2, &frac_str, &sec_str) < 0) { + if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) { return NULL; } double frac = strtod(frac_str, NULL); int sec = strtol(sec_str, NULL, 10); - UpdaterInfo* ui = (UpdaterInfo*)cookie; + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); free(frac_str); @@ -281,16 +282,16 @@ char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { } // package_extract_dir(package_path, destination_path) -char* PackageExtractDirFn(const char* name, void* cookie, +char* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* zip_path; char* dest_path; - if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; // To create a consistent system image, never use the clock for timestamps. struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default @@ -305,18 +306,18 @@ char* PackageExtractDirFn(const char* name, void* cookie, // package_extract_file(package_path, destination_path) -char* PackageExtractFileFn(const char* name, void* cookie, +char* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); } char* zip_path; char* dest_path; - if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; bool success = false; - ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { fprintf(stderr, "%s: no %s in package\n", name, zip_path); @@ -340,15 +341,15 @@ char* PackageExtractFileFn(const char* name, void* cookie, // symlink target src1 src2 ... -char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc == 0) { - return ErrorAbort(cookie, "%s() expects 1+ args, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc); } char* target; - target = Evaluate(cookie, argv[0]); + target = Evaluate(state, argv[0]); if (target == NULL) return NULL; - char** srcs = ReadVarArgs(cookie, argc-1, argv+1); + char** srcs = ReadVarArgs(state, argc-1, argv+1); if (srcs == NULL) { free(target); return NULL; @@ -364,16 +365,16 @@ char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { } -char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; bool recursive = (strcmp(name, "set_perm_recursive") == 0); int min_args = 4 + (recursive ? 1 : 0); if (argc < min_args) { - return ErrorAbort(cookie, "%s() expects %d+ args, got %d", name, argc); + return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc); } - char** args = ReadVarArgs(cookie, argc, argv); + char** args = ReadVarArgs(state, argc, argv); if (args == NULL) return NULL; char* end; @@ -381,26 +382,26 @@ char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { int uid = strtoul(args[0], &end, 0); if (*end != '\0' || args[0][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid uid", name, args[0]); + ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]); goto done; } int gid = strtoul(args[1], &end, 0); if (*end != '\0' || args[1][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid gid", name, args[1]); + ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]); goto done; } if (recursive) { int dir_mode = strtoul(args[2], &end, 0); if (*end != '\0' || args[2][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid dirmode", name, args[2]); + ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]); goto done; } int file_mode = strtoul(args[3], &end, 0); if (*end != '\0' || args[3][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid filemode", + ErrorAbort(state, "%s: \"%s\" not a valid filemode", name, args[3]); goto done; } @@ -411,7 +412,7 @@ char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { } else { int mode = strtoul(args[2], &end, 0); if (*end != '\0' || args[2][0] == 0) { - ErrorAbort(cookie, "%s: \"%s\" not a valid mode", name, args[2]); + ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]); goto done; } @@ -432,12 +433,12 @@ done: } -char* GetPropFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { - return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } char* key; - key = Evaluate(cookie, argv[0]); + key = Evaluate(state, argv[0]); if (key == NULL) return NULL; char value[PROPERTY_VALUE_MAX]; @@ -457,21 +458,21 @@ static bool write_raw_image_cb(const unsigned char* data, } // write_raw_image(file, partition) -char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; char* partition; char* filename; - if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { return NULL; } if (strlen(partition) == 0) { - ErrorAbort(cookie, "partition argument to %s can't be empty", name); + ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; } if (strlen(filename) == 0) { - ErrorAbort(cookie, "file argument to %s can't be empty", name); + ErrorAbort(state, "file argument to %s can't be empty", name); goto done; } @@ -515,6 +516,13 @@ char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) { free(buffer); fclose(f); + if (mtd_erase_blocks(ctx, -1) == -1) { + fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition); + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "%s: error closing write of %s\n", name, partition); + } + printf("%s %s partition from %s\n", success ? "wrote" : "failed to write", partition, filename); @@ -532,26 +540,26 @@ done: // file is not used until after updater exits // // TODO: this should live in some HTC-specific library -char* WriteFirmwareImageFn(const char* name, void* cookie, +char* WriteFirmwareImageFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; char* partition; char* filename; - if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { return NULL; } if (strlen(partition) == 0) { - ErrorAbort(cookie, "partition argument to %s can't be empty", name); + ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; } if (strlen(filename) == 0) { - ErrorAbort(cookie, "file argument to %s can't be empty", name); + ErrorAbort(state, "file argument to %s can't be empty", name); goto done; } - FILE* cmd = ((UpdaterInfo*)cookie)->cmd_pipe; + FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe; fprintf(cmd, "firmware %s %s\n", partition, filename); printf("will write %s firmware from %s\n", partition, filename); @@ -569,7 +577,7 @@ extern int applypatch(int argc, char** argv); // apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) // apply_patch_check(file, sha1, ...) // apply_patch_space(bytes) -char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { +char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { printf("in applypatchfn (%s)\n", name); char* prepend = NULL; @@ -579,7 +587,7 @@ char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { prepend = "-s"; } - char** args = ReadVarArgs(cookie, argc, argv); + char** args = ReadVarArgs(state, argc, argv); if (args == NULL) return NULL; // insert the "program name" argv[0] and a copy of the "prepend" @@ -610,10 +618,42 @@ char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { switch (result) { case 0: return strdup("t"); case 1: return strdup(""); - default: return ErrorAbort(cookie, "applypatch couldn't parse args"); + default: return ErrorAbort(state, "applypatch couldn't parse args"); } } +char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) { + return NULL; + } + + int size = 0; + int i; + for (i = 0; i < argc; ++i) { + size += strlen(args[i]); + } + char* buffer = malloc(size+1); + size = 0; + for (i = 0; i < argc; ++i) { + strcpy(buffer+size, args[i]); + size += strlen(args[i]); + free(args[i]); + } + free(args); + buffer[size] = '\0'; + + char* line = strtok(buffer, "\n"); + while (line) { + fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, + "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n"); + + return buffer; +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); @@ -636,4 +676,6 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch", ApplyPatchFn); RegisterFunction("apply_patch_check", ApplyPatchFn); RegisterFunction("apply_patch_space", ApplyPatchFn); + + RegisterFunction("ui_print", UIPrintFn); } diff --git a/updater/updater.c b/updater/updater.c index 0977625..5a2ed2c 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -94,12 +94,26 @@ int main(int argc, char** argv) { updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = &za; - char* result = Evaluate(&updater_info, root); + State state; + state.cookie = &updater_info; + state.script = script; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); if (result == NULL) { - const char* errmsg = GetError(); - fprintf(stderr, "script aborted with error: %s\n", - errmsg == NULL ? "(none)" : errmsg); - ClearError(); + if (state.errmsg == NULL) { + fprintf(stderr, "script aborted (no error message)\n"); + fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); + } else { + fprintf(stderr, "script aborted: %s\n", state.errmsg); + char* line = strtok(state.errmsg, "\n"); + while (line) { + fprintf(cmd_pipe, "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(cmd_pipe, "ui_print\n"); + } + free(state.errmsg); return 7; } else { fprintf(stderr, "script result was [%s]\n", result); @@ -107,6 +121,7 @@ int main(int argc, char** argv) { } mzCloseZipArchive(&za); + free(script); return 0; }