From c1cdaa0ce8017b075487e6670f89eb4e715258ea Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 27 Oct 1999 12:50:33 +0000 Subject: Patch from Emmanuel Raguet and Eric Valette to add a port of the GoAhead web server (httpd) to the RTEMS build tree. They have successfully used this BSP on i386/pc386 and PowerPC/mcp750. Mark and Joel spoke with Nick Berliner on 26 Oct 1999 about this port and got verbal approval to include it in RTEMS distributions. --- c/src/libnetworking/rtems_webserver/ejparse.c | 1665 +++++++++++++++++++++++++ 1 file changed, 1665 insertions(+) create mode 100644 c/src/libnetworking/rtems_webserver/ejparse.c (limited to 'c/src/libnetworking/rtems_webserver/ejparse.c') diff --git a/c/src/libnetworking/rtems_webserver/ejparse.c b/c/src/libnetworking/rtems_webserver/ejparse.c new file mode 100644 index 0000000000..1734f6f719 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/ejparse.c @@ -0,0 +1,1665 @@ +/* + * ejparse.c -- Ejscript(TM) Parser + * + * Copyright (c) Go Ahead Software, Inc., 1995-1999 + * + * See the file "license.txt" for usage and redistribution license requirements + */ + +/******************************** Description *********************************/ + +/* + * Ejscript parser. This implementes a subset of the JavaScript language. + * Multiple Ejscript parsers can be opened at a time. + */ + +/********************************** Includes **********************************/ + +#include "ej.h" + +/********************************** Local Data ********************************/ + +ej_t **ejHandles; /* List of ej handles */ +int ejMax = -1; /* Maximum size of */ + +/****************************** Forward Declarations **************************/ + +static ej_t *ejPtr(int eid); +static void clearString(char_t **ptr); +static void setString(char_t **ptr, char_t *s); +static void appendString(char_t **ptr, char_t *s); +static void freeVar(sym_t* sp); +static int parse(ej_t *ep, int state, int flags); +static int parseStmt(ej_t *ep, int state, int flags); +static int parseDeclaration(ej_t *ep, int state, int flags); +static int parseArgs(ej_t *ep, int state, int flags); +static int parseCond(ej_t *ep, int state, int flags); +static int parseExpr(ej_t *ep, int state, int flags); +static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs); +static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs); +static int evalFunction(ej_t *ep); +static void freeFunc(ejfunc_t *func); + +/************************************* Code ***********************************/ +/* + * Initialize a Ejscript engine + */ + +int ejOpenEngine(sym_fd_t variables, sym_fd_t functions) +{ + ej_t *ep; + int eid, vid; + + if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) { + return -1; + } + ep = ejHandles[eid]; + ep->eid = eid; + +/* + * Create a top level symbol table if one is not provided for variables and + * functions. Variables may create other symbol tables for block level + * declarations so we use hAlloc to manage a list of variable tables. + */ + if ((vid = hAlloc((void***) &ep->variables)) < 0) { + ejMax = hFree((void***) &ejHandles, ep->eid); + return -1; + } + if (vid >= ep->variableMax) { + ep->variableMax = vid + 1; + } + + if (variables == -1) { + ep->variables[vid] = symOpen(64) + EJ_OFFSET; + ep->flags |= FLAGS_VARIABLES; + + } else { + ep->variables[vid] = variables + EJ_OFFSET; + } + + if (functions == -1) { + ep->functions = symOpen(64); + ep->flags |= FLAGS_FUNCTIONS; + } else { + ep->functions = functions; + } + + ejLexOpen(ep); + +/* + * Define standard constants + */ + ejSetGlobalVar(ep->eid, T("null"), NULL); + +#if EMF + ejEmfOpen(ep->eid); +#endif + return ep->eid; +} + +/******************************************************************************/ +/* + * Close + */ + +void ejCloseEngine(int eid) +{ + ej_t *ep; + int i; + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + +#if EMF + ejEmfClose(eid); +#endif + + bfreeSafe(B_L, ep->error); + ep->error = NULL; + bfreeSafe(B_L, ep->result); + ep->result = NULL; + + ejLexClose(ep); + + if (ep->flags & FLAGS_VARIABLES) { + for (i = ep->variableMax - 1; i >= 0; i--) { + symClose(ep->variables[i] - EJ_OFFSET, freeVar); + ep->variableMax = hFree((void***) &ep->variables, i); + } + } + if (ep->flags & FLAGS_FUNCTIONS) { + symClose(ep->functions, freeVar); + } + + ejMax = hFree((void***) &ejHandles, ep->eid); + bfree(B_L, ep); +} + +/******************************************************************************/ +/* + * Callback from symClose. Free the variable. + */ + +static void freeVar(sym_t* sp) +{ + valueFree(&sp->content); +} + +/******************************************************************************/ +/* + * Evaluate a Ejscript file + */ + +#if DEV +char_t *ejEvalFile(int eid, char_t *path, char_t **emsg) +{ + gstat_t sbuf; + ej_t *ep; + char_t *script, *rs; + char *fileBuf; + int fd; + + a_assert(path && *path); + + if (emsg) { + *emsg = NULL; + } + if ((ep = ejPtr(eid)) == NULL) { + return NULL; + } + + if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) { + ejError(ep, T("Bad handle %d"), eid); + return NULL; + } + if (gstat(path, &sbuf) < 0) { + close(fd); + ejError(ep, T("Cant stat %s"), path); + return NULL; + } + if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) { + close(fd); + ejError(ep, T("Cant malloc %d"), sbuf.st_size); + return NULL; + } + if (read(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) { + close(fd); + bfree(B_L, fileBuf); + ejError(ep, T("Error reading %s"), path); + return NULL; + } + fileBuf[sbuf.st_size] = '\0'; + close(fd); + + if ((script = ballocAscToUni(fileBuf)) == NULL) { + bfree(B_L, fileBuf); + ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1); + return NULL; + } + bfree(B_L, fileBuf); + + rs = ejEvalBlock(eid, script, emsg); + + bfree(B_L, script); + return rs; +} +#endif + +/******************************************************************************/ +/* + * Create a new variable scope block so that consecutive ejEval calls may + * be made with the same varible scope. This space MUST be closed with + * ejCloseBlock when the evaluations are complete. + */ + +int ejOpenBlock(int eid) +{ + ej_t *ep; + int vid; + + if((ep = ejPtr(eid)) == NULL) { + return -1; + } + if ((vid = hAlloc((void***) &ep->variables)) < 0) { + return -1; + } + if (vid >= ep->variableMax) { + ep->variableMax = vid + 1; + } + ep->variables[vid] = symOpen(64) + EJ_OFFSET; + return vid; + +} + +/******************************************************************************/ +/* + * Close a variable scope block. The vid parameter is the return value from + * the call to ejOpenBlock + */ + +int ejCloseBlock(int eid, int vid) +{ + ej_t *ep; + + if((ep = ejPtr(eid)) == NULL) { + return -1; + } + symClose(ep->variables[vid] - EJ_OFFSET, freeVar); + ep->variableMax = hFree((void***) &ep->variables, vid); + return 0; + +} +/******************************************************************************/ +/* + * Create a new variable scope block and evaluate a script. All variables + * created during this context will be automatically deleted when complete. + */ + +char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg) +{ + char_t* returnVal; + int vid; + + a_assert(script); + + vid = ejOpenBlock(eid); + returnVal = ejEval(eid, script, emsg); + ejCloseBlock(eid, vid); + + return returnVal; +} + +/******************************************************************************/ +/* + * Parse and evaluate a Ejscript. The caller may provide a symbol table to + * use for variables and function definitions. Return char_t pointer on + * success otherwise NULL pointer is returned. + */ + +char_t *ejEval(int eid, char_t *script, char_t **emsg) +{ + ej_t *ep; + ejinput_t *oldBlock; + int state; + + a_assert(script); + + if (emsg) { + *emsg = NULL; + } + if ((ep = ejPtr(eid)) == NULL) { + return NULL; + } + + setString(&ep->result, T("")); + +/* + * Allocate a new evaluation block, and save the old one + */ + oldBlock = ep->input; + ejLexOpenScript(ep, script); + +/* + * Do the actual parsing and evaluation + */ + do { + state = parse(ep, STATE_BEGIN, FLAGS_EXE); + } while (state != STATE_EOF && state != STATE_ERR); + + ejLexCloseScript(ep); + +/* + * Return any error string to the user + */ + if (state == STATE_ERR && emsg) { + *emsg = bstrdup(B_L, ep->error); + } + +/* + * Restore the old evaluation block + */ + ep->input = oldBlock; + + if (state == STATE_EOF) { + return ep->result; + } + if (state == STATE_ERR) { + return NULL; + } + return ep->result; +} + +/******************************************************************************/ +/* + * Recursive descent parser for Ejscript + */ + +static int parse(ej_t *ep, int state, int flags) +{ + a_assert(ep); + + switch (state) { +/* + * Any statement, function arguments or conditional expressions + */ + case STATE_STMT: + case STATE_DEC: + state = parseStmt(ep, state, flags); + break; + + case STATE_EXPR: + state = parseStmt(ep, state, flags); + break; + +/* + * Variable declaration list + */ + case STATE_DEC_LIST: + state = parseDeclaration(ep, state, flags); + break; + +/* + * Function argument string + */ + case STATE_ARG_LIST: + state = parseArgs(ep, state, flags); + break; + +/* + * Logical condition list (relational operations separated by &&, ||) + */ + case STATE_COND: + state = parseCond(ep, state, flags); + break; + +/* + * Expression list + */ + case STATE_RELEXP: + state = parseExpr(ep, state, flags); + break; + } + + if (state == STATE_ERR && ep->error == NULL) { + ejError(ep, T("Syntax error")); + } + return state; +} + +/******************************************************************************/ +/* + * Parse any statement including functions and simple relational operations + */ + +static int parseStmt(ej_t *ep, int state, int flags) +{ + ejfunc_t func; + ejfunc_t *saveFunc; + ejinput_t condScript, endScript, bodyScript, incrScript; + char_t *value; + char_t *identifier; + int done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags; + + a_assert(ep); + +/* + * Set these to NULL, else we try to free them if an error occurs. + */ + endScript.putBackToken = NULL; + bodyScript.putBackToken = NULL; + incrScript.putBackToken = NULL; + condScript.putBackToken = NULL; + + expectSemi = 0; + saveFunc = NULL; + + for (done = 0; !done; ) { + tid = ejLexGetToken(ep, state); + + switch (tid) { + default: + ejLexPutbackToken(ep, TOK_EXPR, ep->token); + done++; + break; + + case TOK_ERR: + state = STATE_ERR; + done++; + break; + + case TOK_EOF: + state = STATE_EOF; + done++; + break; + + case TOK_NEWLINE: + break; + + case TOK_SEMI: +/* + * This case is when we discover no statement and just a lone ';' + */ + if (state != STATE_STMT) { + ejLexPutbackToken(ep, tid, ep->token); + } + done++; + break; + + case TOK_ID: +/* + * This could either be a reference to a variable or an assignment + */ + identifier = NULL; + setString(&identifier, ep->token); +/* + * Peek ahead to see if this is an assignment + */ + tid = ejLexGetToken(ep, state); + if (tid == TOK_ASSIGNMENT) { + if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + goto error; + } + if (flags & FLAGS_EXE) { + if ( state == STATE_DEC ) { + ejSetLocalVar(ep->eid, identifier, ep->result); + } + else { + if (ejGetVar(ep->eid, identifier, &value) > 0) { + ejSetLocalVar(ep->eid, identifier, ep->result); + } else { + ejSetGlobalVar(ep->eid, identifier, ep->result); + } + } + } + + } else if (tid == TOK_INC_DEC ) { + value = NULL; + if ( flags & FLAGS_EXE ) { + if (ejGetVar(ep->eid, identifier, &value) < 0) { + ejError(ep, T("Undefined variable %s\n"), identifier); + goto error; + } + setString(&ep->result, value); + if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) { + state = STATE_ERR; + break; + } + ejSetGlobalVar(ep->eid, identifier, ep->result); + } + + } else { +/* + * If we are processing a declaration, allow undefined vars + */ + value = NULL; + if (state == STATE_DEC) { + if (ejGetVar(ep->eid, identifier, &value) > 0) { + ejError(ep, T("Variable already declared"), + identifier); + clearString(&identifier); + goto error; + } + ejSetLocalVar(ep->eid, identifier, NULL); + } else { + if ( flags & FLAGS_EXE ) { + if (ejGetVar(ep->eid, identifier, &value) < 0) { + ejError(ep, T("Undefined variable %s\n"), + identifier); + clearString(&identifier); + goto error; + } + } + } + setString(&ep->result, value); + ejLexPutbackToken(ep, tid, ep->token); + } + clearString(&identifier); + + if (state == STATE_STMT) { + expectSemi++; + } + done++; + break; + + case TOK_LITERAL: +/* + * Set the result to the literal (number or string constant) + */ + setString(&ep->result, ep->token); + if (state == STATE_STMT) { + expectSemi++; + } + done++; + break; + + case TOK_FUNCTION: +/* + * We must save any current ep->func value for the current stack frame + */ + if (ep->func) { + saveFunc = ep->func; + } + memset(&func, 0, sizeof(ejfunc_t)); + setString(&func.fname, ep->token); + ep->func = &func; + + setString(&ep->result, T("")); + if (ejLexGetToken(ep, state) != TOK_LPAREN) { + freeFunc(&func); + goto error; + } + + if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) { + freeFunc(&func); + ep->func = saveFunc; + goto error; + } +/* + * Evaluate the function if required + */ + if (flags & FLAGS_EXE && evalFunction(ep) < 0) { + freeFunc(&func); + ep->func = saveFunc; + goto error; + } + + freeFunc(&func); + ep->func = saveFunc; + + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } + if (state == STATE_STMT) { + expectSemi++; + } + done++; + break; + + case TOK_IF: + if (state != STATE_STMT) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_LPAREN) { + goto error; + } +/* + * Evaluate the entire condition list "(condition)" + */ + if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } +/* + * This is the "then" case. We need to always parse both cases and + * execute only the relevant case. + */ + if (*ep->result == '1') { + thenFlags = flags; + elseFlags = flags & ~FLAGS_EXE; + } else { + thenFlags = flags & ~FLAGS_EXE; + elseFlags = flags; + } +/* + * Process the "then" case + */ + if (parse(ep, STATE_STMT, thenFlags) != STATE_STMT_DONE) { + goto error; + } + tid = ejLexGetToken(ep, state); + if (tid != TOK_ELSE) { + ejLexPutbackToken(ep, tid, ep->token); + done++; + break; + } +/* + * Process the "else" case + */ + if (parse(ep, STATE_STMT, elseFlags) != STATE_STMT_DONE) { + goto error; + } + done++; + break; + + case TOK_FOR: +/* + * Format for the expression is: + * + * for (initial; condition; incr) { + * body; + * } + */ + if (state != STATE_STMT) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_LPAREN) { + goto error; + } + +/* + * Evaluate the for loop initialization statement + */ + if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_SEMI) { + goto error; + } + +/* + * The first time through, we save the current input context just + * to each step: prior to the conditional, the loop increment and the + * loop body. + */ + ejLexSaveInputState(ep, &condScript); + if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) { + goto error; + } + cond = (*ep->result != '0'); + + if (ejLexGetToken(ep, state) != TOK_SEMI) { + goto error; + } + +/* + * Don't execute the loop increment statement or the body first time + */ + forFlags = flags & ~FLAGS_EXE; + ejLexSaveInputState(ep, &incrScript); + if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } + +/* + * Parse the body and remember the end of the body script + */ + ejLexSaveInputState(ep, &bodyScript); + if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) { + goto error; + } + ejLexSaveInputState(ep, &endScript); + +/* + * Now actually do the for loop. Note loop has been rotated + */ + while (cond && (flags & FLAGS_EXE) ) { +/* + * Evaluate the body + */ + ejLexRestoreInputState(ep, &bodyScript); + if (parse(ep, STATE_STMT, flags) != STATE_STMT_DONE) { + goto error; + } +/* + * Evaluate the increment script + */ + ejLexRestoreInputState(ep, &incrScript); + if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) { + goto error; + } +/* + * Evaluate the condition + */ + ejLexRestoreInputState(ep, &condScript); + if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) { + goto error; + } + cond = (*ep->result != '0'); + } + ejLexRestoreInputState(ep, &endScript); + done++; + break; + + case TOK_VAR: + if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) { + goto error; + } + done++; + break; + + case TOK_COMMA: + ejLexPutbackToken(ep, TOK_EXPR, ep->token); + done++; + break; + + case TOK_LPAREN: + if (state == STATE_EXPR) { + if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + goto error; + } + if (ejLexGetToken(ep, state) != TOK_RPAREN) { + goto error; + } + return STATE_EXPR_DONE; + } + done++; + break; + + case TOK_RPAREN: + ejLexPutbackToken(ep, tid, ep->token); + return STATE_EXPR_DONE; + + case TOK_LBRACE: +/* + * This handles any code in braces except "if () {} else {}" + */ + if (state != STATE_STMT) { + goto error; + } + +/* + * Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen + */ + do { + state = parse(ep, STATE_STMT, flags); + } while (state == STATE_STMT_DONE); + + if (ejLexGetToken(ep, state) != TOK_RBRACE) { + goto error; + } + return STATE_STMT_DONE; + + case TOK_RBRACE: + if (state == STATE_STMT) { + ejLexPutbackToken(ep, tid, ep->token); + return STATE_STMT_BLOCK_DONE; + } + goto error; + + case TOK_RETURN: + if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) { + goto error; + } + if (flags & FLAGS_EXE) { + while ( ejLexGetToken(ep, state) != TOK_EOF ); + done++; + return STATE_EOF; + } + break; + } + } + + if (expectSemi) { + tid = ejLexGetToken(ep, state); + if (tid != TOK_SEMI && tid != TOK_NEWLINE) { + goto error; + } + +/* + * Skip newline after semi-colon + */ + tid = ejLexGetToken(ep, state); + if (tid != TOK_NEWLINE) { + ejLexPutbackToken(ep, tid, ep->token); + } + } + +/* + * Free resources and return the correct status + */ +doneParse: + if (tid == TOK_FOR) { + ejLexFreeInputState(ep, &condScript); + ejLexFreeInputState(ep, &incrScript); + ejLexFreeInputState(ep, &endScript); + ejLexFreeInputState(ep, &bodyScript); + } + if (state == STATE_STMT) { + return STATE_STMT_DONE; + } else if (state == STATE_DEC) { + return STATE_DEC_DONE; + } else if (state == STATE_EXPR) { + return STATE_EXPR_DONE; + } else if (state == STATE_EOF) { + return state; + } else { + return STATE_ERR; + } + +/* + * Common error exit + */ +error: + state = STATE_ERR; + goto doneParse; +} + +/******************************************************************************/ +/* + * Parse variable declaration list + */ + +static int parseDeclaration(ej_t *ep, int state, int flags) +{ + int tid; + + a_assert(ep); + +/* + * Declarations can be of the following forms: + * var x; + * var x, y, z; + * var x = 1 + 2 / 3, y = 2 + 4; + * + * We set the variable to NULL if there is no associated assignment. + */ + + do { + if ((tid = ejLexGetToken(ep, state)) != TOK_ID) { + return STATE_ERR; + } + ejLexPutbackToken(ep, tid, ep->token); + +/* + * Parse the entire assignment or simple identifier declaration + */ + if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) { + return STATE_ERR; + } + +/* + * Peek at the next token, continue if comma seen + */ + tid = ejLexGetToken(ep, state); + if (tid == TOK_SEMI) { + return STATE_DEC_LIST_DONE; + } else if (tid != TOK_COMMA) { + return STATE_ERR; + } + } while (tid == TOK_COMMA); + + if (tid != TOK_SEMI) { + return STATE_ERR; + } + return STATE_DEC_LIST_DONE; +} + +/******************************************************************************/ +/* + * Parse function arguments + */ + +static int parseArgs(ej_t *ep, int state, int flags) +{ + int tid, aid; + + a_assert(ep); + + do { + state = parse(ep, STATE_RELEXP, flags); + if (state == STATE_EOF || state == STATE_ERR) { + return state; + } + if (state == STATE_RELEXP_DONE) { + aid = hAlloc((void***) &ep->func->args); + ep->func->args[aid] = bstrdup(B_L, ep->result); + ep->func->nArgs++; + } +/* + * Peek at the next token, continue if more args (ie. comma seen) + */ + tid = ejLexGetToken(ep, state); + if (tid != TOK_COMMA) { + ejLexPutbackToken(ep, tid, ep->token); + } + } while (tid == TOK_COMMA); + + if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) { + return STATE_ERR; + } + return STATE_ARG_LIST_DONE; +} + +/******************************************************************************/ +/* + * Parse conditional expression (relational ops separated by ||, &&) + */ + +static int parseCond(ej_t *ep, int state, int flags) +{ + char_t *lhs, *rhs; + int tid, operator; + + a_assert(ep); + + setString(&ep->result, T("")); + rhs = lhs = NULL; + operator = 0; + + do { +/* + * Recurse to handle one side of a conditional. Accumulate the + * left hand side and the final result in ep->result. + */ + state = parse(ep, STATE_RELEXP, flags); + if (state != STATE_RELEXP_DONE) { + state = STATE_ERR; + break; + } + if (operator > 0) { + setString(&rhs, ep->result); + if (evalCond(ep, lhs, operator, rhs) < 0) { + state = STATE_ERR; + break; + } + } + setString(&lhs, ep->result); + + tid = ejLexGetToken(ep, state); + if (tid == TOK_LOGICAL) { + operator = (int) *ep->token; + + } else if (tid == TOK_RPAREN || tid == TOK_SEMI) { + ejLexPutbackToken(ep, tid, ep->token); + state = STATE_COND_DONE; + break; + + } else { + ejLexPutbackToken(ep, tid, ep->token); + } + + } while (state == STATE_RELEXP_DONE); + + if (lhs) { + bfree(B_L, lhs); + } + if (rhs) { + bfree(B_L, rhs); + } + return state; +} + +/******************************************************************************/ +/* + * Parse expression (leftHandSide operator rightHandSide) + */ + +static int parseExpr(ej_t *ep, int state, int flags) +{ + char_t *lhs, *rhs; + int rel, tid; + + a_assert(ep); + + setString(&ep->result, T("")); + rhs = lhs = NULL; + rel = 0; + + do { +/* + * This loop will handle an entire expression list. We call parse + * to evalutate each term which returns the result in ep->result. + */ + state = parse(ep, STATE_EXPR, flags); + if (state != STATE_EXPR_DONE) { + state = STATE_ERR; + break; + } + if (rel > 0) { + setString(&rhs, ep->result); + if (evalExpr(ep, lhs, rel, rhs) < 0) { + state = STATE_ERR; + break; + } + } + setString(&lhs, ep->result); + + if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR) { + rel = (int) *ep->token; + + } else if (tid == TOK_INC_DEC) { + rel = (int) *ep->token; + + } else { + ejLexPutbackToken(ep, tid, ep->token); + state = STATE_RELEXP_DONE; + } + + } while (state == STATE_EXPR_DONE); + + if (rhs) + bfree(B_L, rhs); + if (lhs) + bfree(B_L, lhs); + return state; +} + +/******************************************************************************/ +/* + * Evaluate a condition. Implements &&, ||, ! + */ + +static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs) +{ + char_t buf[16]; + int l, r, lval; + + a_assert(lhs); + a_assert(rhs); + a_assert(rel > 0); + + lval = 0; + if (gisdigit(*lhs) && gisdigit(*rhs)) { + l = gatoi(lhs); + r = gatoi(rhs); + switch (rel) { + case COND_AND: + lval = l && r; + break; + case COND_OR: + lval = l || r; + break; + default: + ejError(ep, T("Bad operator %d"), rel); + return -1; + } + } else { + if (!gisdigit(*lhs)) { + ejError(ep, T("Conditional must be numeric"), lhs); + } else { + ejError(ep, T("Conditional must be numeric"), rhs); + } + } + + stritoa(lval, buf, sizeof(buf)); + setString(&ep->result, buf); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate an operation + */ + +static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs) +{ + char_t *cp, buf[16]; + int numeric, l, r, lval; + + a_assert(lhs); + a_assert(rhs); + a_assert(rel > 0); + +/* + * All of the characters in the lhs and rhs must be numeric + */ + numeric = 1; + for (cp = lhs; *cp; cp++) { + if (!gisdigit(*cp)) { + numeric = 0; + break; + } + } + if (numeric) { + for (cp = rhs; *cp; cp++) { + if (!gisdigit(*cp)) { + numeric = 0; + break; + } + } + } + if (numeric) { + l = gatoi(lhs); + r = gatoi(rhs); + switch (rel) { + case EXPR_PLUS: + lval = l + r; + break; + case EXPR_INC: + lval = l + 1; + break; + case EXPR_MINUS: + lval = l - r; + break; + case EXPR_DEC: + lval = l - 1; + break; + case EXPR_MUL: + lval = l * r; + break; + case EXPR_DIV: + if (r != 0) { + lval = l / r; + } else { + lval = 0; + } + break; + case EXPR_MOD: + if (r != 0) { + lval = l % r; + } else { + lval = 0; + } + break; + case EXPR_LSHIFT: + lval = l << r; + break; + case EXPR_RSHIFT: + lval = l >> r; + break; + case EXPR_EQ: + lval = l == r; + break; + case EXPR_NOTEQ: + lval = l != r; + break; + case EXPR_LESS: + lval = (l < r) ? 1 : 0; + break; + case EXPR_LESSEQ: + lval = (l <= r) ? 1 : 0; + break; + case EXPR_GREATER: + lval = (l > r) ? 1 : 0; + break; + case EXPR_GREATEREQ: + lval = (l >= r) ? 1 : 0; + break; + default: + ejError(ep, T("Bad operator %d"), rel); + return -1; + } + + } else { + switch (rel) { + case EXPR_PLUS: + clearString(&ep->result); + appendString(&ep->result, lhs); + appendString(&ep->result, rhs); + return 0; + case EXPR_LESS: + lval = gstrcmp(lhs, rhs) < 0; + break; + case EXPR_LESSEQ: + lval = gstrcmp(lhs, rhs) <= 0; + break; + case EXPR_GREATER: + lval = gstrcmp(lhs, rhs) > 0; + break; + case EXPR_GREATEREQ: + lval = gstrcmp(lhs, rhs) >= 0; + break; + case EXPR_EQ: + lval = gstrcmp(lhs, rhs) == 0; + break; + case EXPR_NOTEQ: + lval = gstrcmp(lhs, rhs) != 0; + break; + case EXPR_INC: + case EXPR_DEC: + case EXPR_MINUS: + case EXPR_DIV: + case EXPR_MOD: + case EXPR_LSHIFT: + case EXPR_RSHIFT: + default: + ejError(ep, T("Bad operator")); + return -1; + } + } + + stritoa(lval, buf, sizeof(buf)); + setString(&ep->result, buf); + return 0; +} + +/******************************************************************************/ +/* + * Evaluate a function + */ + +static int evalFunction(ej_t *ep) +{ + sym_t *sp; + int (*fn)(int eid, void *handle, int argc, char_t **argv); + + if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) { + ejError(ep, T("Undefined procedure %s"), ep->func->fname); + return -1; + } + fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer; + if (fn == NULL) { + ejError(ep, T("Undefined procedure %s"), ep->func->fname); + return -1; + } + + return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs, + ep->func->args); +} + +/******************************************************************************/ +/* + * Output a parse ej_error message + */ + +void ejError(ej_t* ep, char_t* fmt, ...) +{ + va_list args; + ejinput_t *ip; + char_t *errbuf, *msgbuf; + + a_assert(ep); + a_assert(fmt); + ip = ep->input; + + va_start(args, fmt); + msgbuf = NULL; + gvsnprintf(&msgbuf, E_MAX_ERROR, fmt, args); + va_end(args); + + if (ep && ip) { + errbuf = NULL; + gsnprintf(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"), + msgbuf, ip->lineNumber, ip->line); + bfreeSafe(B_L, ep->error); + ep->error = errbuf; + } + bfreeSafe(B_L, msgbuf); +} + +/******************************************************************************/ +/* + * Clear a string value + */ + +static void clearString(char_t **ptr) +{ + a_assert(ptr); + + if (*ptr) { + bfree(B_L, *ptr); + } + *ptr = NULL; +} + +/******************************************************************************/ +/* + * Set a string value + */ + +static void setString(char_t **ptr, char_t *s) +{ + a_assert(ptr); + + if (*ptr) { + bfree(B_L, *ptr); + } + *ptr = bstrdup(B_L, s); +} + +/******************************************************************************/ +/* + * Append to the pointer value + */ + +static void appendString(char_t **ptr, char_t *s) +{ + int len, oldlen; + + a_assert(ptr); + + if (*ptr) { + len = gstrlen(s); + oldlen = gstrlen(*ptr); + *ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t)); + gstrcpy(&(*ptr)[oldlen], s); + } else { + *ptr = bstrdup(B_L, s); + } +} + +/******************************************************************************/ +/* + * Define a function + */ + +int ejSetGlobalFunction(int eid, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ejSetGlobalFunctionDirect(ep->functions, name, fn); +} + +/******************************************************************************/ +/* + * Define a function directly into the function symbol table. + */ + +int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name, + int (*fn)(int eid, void *handle, int argc, char_t **argv)) +{ + if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) { + return -1; + } + return 0; +} + +/******************************************************************************/ +/* + * Get a function definition + */ + +void *ejGetGlobalFunction(int eid, char_t *name) +{ + ej_t *ep; + sym_t *sp; + int (*fn)(int eid, void *handle, int argc, char_t **argv); + + if ((ep = ejPtr(eid)) == NULL) { + return NULL; + } + if ((sp = symLookup(ep->functions, name)) != NULL) { + fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer; + return (void*) fn; + } + return NULL; +} + +/******************************************************************************/ +/* + * Utility routine to crack Ejscript arguments. Return the number of args + * seen. This routine only supports %s and %d type args. + * + * Typical usage: + * + * if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) { + * error("Insufficient args\n"); + * return -1; + * } + */ + +int ejArgs(int argc, char_t **argv, char_t *fmt, ...) +{ + va_list vargs; + char_t *cp, **sp; + int *ip; + int argn; + + va_start(vargs, fmt); + + if (argv == NULL) { + return 0; + } + + for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) { + if (*cp++ != '%') { + continue; + } + + switch (*cp) { + case 'd': + ip = va_arg(vargs, int*); + *ip = gatoi(argv[argn]); + break; + + case 's': + sp = va_arg(vargs, char_t**); + *sp = argv[argn]; + break; + + default: +/* + * Unsupported + */ + a_assert(0); + } + argn++; + } + + va_end(vargs); + return argn; +} + +/******************************************************************************/ +/* + * Define the user handle + */ + +void ejSetUserHandle(int eid, int handle) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + ep->userHandle = handle; +} + +/******************************************************************************/ +/* + * Get the user handle + */ + +int ejGetUserHandle(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->userHandle; +} + +/******************************************************************************/ +/* + * Get the current line number + */ + +int ejGetLineNumber(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->input->lineNumber; +} + +/******************************************************************************/ +/* + * Set the result + */ + +void ejSetResult(int eid, char_t *s) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + setString(&ep->result, s); +} + +/******************************************************************************/ +/* + * Get the result + */ + +char_t *ejGetResult(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return NULL; + } + return ep->result; +} + +/******************************************************************************/ +/* + * Set a variable. Note: a variable with a value of NULL means declared but + * undefined. The value is defined in the top-most variable frame. + */ + +void ejSetVar(int eid, char_t *var, char_t *value) +{ + ej_t *ep; + value_t v; + + a_assert(var && *var); + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + if (value == NULL) { + v = valueString(value, 0); + } else { + v = valueString(value, VALUE_ALLOCATE); + } + symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0); +} + +/******************************************************************************/ +/* + * Set a local variable. Note: a variable with a value of NULL means + * declared but undefined. The value is defined in the top-most variable frame. + */ + +void ejSetLocalVar(int eid, char_t *var, char_t *value) +{ + ej_t *ep; + value_t v; + + a_assert(var && *var); + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + if (value == NULL) { + v = valueString(value, 0); + } else { + v = valueString(value, VALUE_ALLOCATE); + } + symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0); +} + +/******************************************************************************/ +/* + * Set a global variable. Note: a variable with a value of NULL means + * declared but undefined. The value is defined in the global variable frame. + */ + +void ejSetGlobalVar(int eid, char_t *var, char_t *value) +{ + ej_t *ep; + value_t v; + + a_assert(var && *var); + + if ((ep = ejPtr(eid)) == NULL) { + return; + } + if (value == NULL) { + v = valueString(value, 0); + } else { + v = valueString(value, VALUE_ALLOCATE); + } + symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0); +} + +/******************************************************************************/ +/* + * Get a variable + */ + +int ejGetVar(int eid, char_t *var, char_t **value) +{ + ej_t *ep; + sym_t *sp; + int i; + + a_assert(var && *var); + a_assert(value); + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + + for (i = ep->variableMax - 1; i >= 0; i--) { + if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) { + continue; + } + a_assert(sp->content.type == string); + *value = sp->content.value.string; + return i; + } + return -1; +} + +/******************************************************************************/ +#if UNUSED +/* + * Get the variable symbol table + */ + +sym_fd_t ejGetVariableTable(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->variables; +} +#endif +/******************************************************************************/ +/* + * Get the functions symbol table + */ + +sym_fd_t ejGetFunctionTable(int eid) +{ + ej_t *ep; + + if ((ep = ejPtr(eid)) == NULL) { + return -1; + } + return ep->functions; +} + +/******************************************************************************/ +/* + * Free an argument list + */ + +static void freeFunc(ejfunc_t *func) +{ + int i; + + for (i = func->nArgs - 1; i >= 0; i--) { + bfree(B_L, func->args[i]); + func->nArgs = hFree((void***) &func->args, i); + } + if (func->fname) { + bfree(B_L, func->fname); + func->fname = NULL; + } +} + +/******************************************************************************/ +/* + * Get Ejscript pointer + */ + +static ej_t *ejPtr(int eid) +{ + a_assert(0 <= eid && eid < ejMax); + + if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) { + ejError(NULL, T("Bad handle %d"), eid); + return NULL; + } + return ejHandles[eid]; +} + +/******************************************************************************/ -- cgit v1.2.3