fixes to edify and updater script
A few more changes to edify: - fix write_raw_image(); my last change neglected to close the write context, so the written image was corrupt. - each expression tracks the span of the source code from which it was compiled, so that assert()'s error message can include the source of the expression that failed. - the 'cookie' argument to each Function is replaced with a State object, which contains the cookie, the source script (for use with the above spans), and the current error message (replacing the global variables that were used for this purpose). - in the recovery image, a new command "ui_print" can be sent back through the command pipe to cause text to appear on the screen. Add a new ui_print() function to print things from scripts. Rename existing "print" function to "stdout".
This commit is contained in:
parent
8edb00c990
commit
d9c9d10d9d
520
edify/expr.c
520
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;
|
||||
}
|
||||
|
61
edify/expr.h
61
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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
<STR>{
|
||||
\" {
|
||||
++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;
|
||||
|
269
edify/main.c
269
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;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
;
|
||||
|
||||
|
36
edify/yydefs.h
Normal file
36
edify/yydefs.h
Normal file
@ -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
|
10
install.c
10
install.c
@ -196,6 +196,9 @@ try_update_binary(const char *path, ZipArchive *zip) {
|
||||
// arrange to install the contents of <filename> in the
|
||||
// given partition on reboot.
|
||||
//
|
||||
// ui_print <string>
|
||||
// display <string> 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);
|
||||
}
|
||||
|
@ -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="<partition>" to mount a yaffs2 filesystem
|
||||
// type="vfat" location="/dev/block/<whatever>" 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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user