diff options
Diffstat (limited to 'lib/execute.c')
-rw-r--r-- | lib/execute.c | 1715 |
1 files changed, 1715 insertions, 0 deletions
diff --git a/lib/execute.c b/lib/execute.c new file mode 100644 index 0000000..3bf3c4b --- /dev/null +++ b/lib/execute.c @@ -0,0 +1,1715 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +/* ---------------------------------------------------------------------- + * Rep Execution Unit + * 2000-2001 Copyright, Nate Nielsen + */ + +#include "common/usuals.h" +#include "common/compat.h" +#include "common/xstring.h" +#include "lib/rlib.h" +#include "priv.h" +#include "execute.h" +#include "ops.h" + +/* 100 million loops max! */ +#define MAX_PASSES 100000000 + +/* NOTE: all *end* counts/pointers point to the last char of the actual data */ + +/* Quick access macro for accessing VM registers */ +#define REGISTER(reg) \ + (state->vmregs[ARG_GET_REGISTER(reg)]) +#define REGISTER2(reg, off) \ + (state->vmregs[ARG_GET_REGISTER(reg) + (off)]) + +/* ========================================================================= + * MEMORY: Memory blocks are used by the code and are simply a set + * of numeric values indexed by key. + */ + +/* _memoryAllocate: --------------------------------------------------- + * Helper which double the size of a 'memory' block if needed + */ +bool _memoryAllocate(memory* mem) +{ + ASSERT_PTR(mem); + + if(!mem->thememory) + { + /* Allocate new set */ + mem->thememory = (struct mem*)malloc(sizeof(struct mem) * BLOCK_SIZE); + if(!mem->thememory) return false; + mem->alloc = BLOCK_SIZE; + memoryClearAll(mem); + } + + /* If no more space ... */ + if(mem->alloc <= mem->cur) + { + /* ...double allocation */ + size_t sz = sizeof(struct mem) * mem->alloc; + mem->thememory = (struct mem*)reallocf(mem->thememory, sz * 2); + if(!mem->thememory) return false; + mem->alloc *= 2; + } + + return true; +} + + +/* _memoryFind: ---------------------------------------------------- + * Find memory location for given key + */ +struct mem* _memoryFind(memory* mem, uint key) +{ + size_t i; + ASSERT_PTR(mem); + + for(i = 0; i < mem->cur; i++) + { + if(key == mem->thememory[i].key) + return &(mem->thememory[i]); + } + + return 0; +} + +/* memoryInit: ----------------------------------------------------- + * Initialize and or allocate a memory block + */ +bool memoryInit(memory* mem) +{ + zero(*mem); + return true;; +} + +/* memoryFree: ---------------------------------------------------- + * Free a memory block + */ +void memoryFree(memory* mem) +{ + if(mem) + { + ASSERT_PTR(mem); + if(mem->thememory) + free(mem->thememory); + + mem->thememory = NULL; + } +} + +/* memoryValue: -------------------------------------------------- + * Get a value for key from memory + */ +uint* memoryValue(memory* mem, uint key) +{ + struct mem* m; + ASSERT_PTR(mem); + if(!_memoryAllocate(mem)) + return NULL; + + m = _memoryFind(mem, key); + if(!m) + { + /* Add new watermark */ + mem->thememory[mem->cur].key = key; + mem->thememory[mem->cur].value = 0; + m = &(mem->thememory[mem->cur]); + mem->cur++; + } + + return &(m->value); +} + +/* memoryClearAll: --------------------------------------------------- + * Clear all values from block + */ +void memoryClearAll(memory* mem) +{ + ASSERT_PTR(mem); + mem->cur = 0; +} + + +/* ========================================================================= + * DATA: + */ + +/* _dataAllocate: --------------------------------------------------- + * Helper which double the size of a 'memory' block if needed + */ +bool _dataAllocate(data* dat) +{ + ASSERT_PTR(dat); + + if(!dat->thedata) + { + /* Allocate new set */ + dat->thedata = (struct dat*)malloc(sizeof(struct dat) * BLOCK_SIZE); + if(!dat->thedata) return false; + dat->alloc = BLOCK_SIZE; + dataClearAll(dat); + } + + /* If no more space ... */ + if(dat->alloc <= dat->cur) + { + /* ...double allocation */ + size_t sz = sizeof(struct dat) * dat->alloc; + dat->thedata = (struct dat*)reallocf(dat->thedata, sz * 2); + if(!dat->thedata) return false; + dat->alloc *= 2; + } + + return true; +} + + +/* _dataFind: ---------------------------------------------------- + * Find memory location for given key + */ +struct dat* _dataFind(data* dat, void* key) +{ + size_t i; + ASSERT_PTR(dat); + + for(i = 0; i < dat->cur; i++) + { + if(key == dat->thedata[i].key) + return &(dat->thedata[i]); + } + + return 0; +} + +/* dataInit: ----------------------------------------------------- + * Initialize and or allocate a data block + */ +bool dataInit(data* dat) +{ + zero(*dat); + return true; +} + +/* dataFree: ---------------------------------------------------- + * Free a data block + */ +void dataFree(data* dat) +{ + if(dat) + { + ASSERT_PTR(dat); + if(dat->thedata) + { + dataClearAll(dat); + free(dat->thedata); + } + + dat->thedata = NULL; + } +} + +/* dataGetValue: -------------------------------------------------- + * Get a value for key from data + */ +void* dataGetValue(data* dat, void* key) +{ + struct dat* d; + ASSERT_PTR(dat); + + if(d = _dataFind(dat, key)) + return d->value; + + return NULL; +} + +/* dataSetValue: -------------------------------------------------- + * Set a value for key + */ +bool dataSetValue(data* dat, void* key, void* value) +{ + struct dat* d; + ASSERT_PTR(dat); + if(!_dataAllocate(dat)) + return false; + + if(d = _dataFind(dat, key)) + { + d->value = value; + } + else + { + /* Add new one */ + dat->thedata[dat->cur].key = key; + dat->thedata[dat->cur].value = value; + dat->cur++; + } + + return true; +} + +/* dataClearAll: --------------------------------------------------- + * Clear all values from block + */ +void dataClearAll(data* dat) +{ + size_t i; + + ASSERT_PTR(dat); + if(dat->thedata) + { + for(i = 0; i < dat->cur; i++) + { + if(dat->thedata[i].value) + free(dat->thedata[i].value); + } + } + + dat->cur = 0; +} + + +/* ======================================================================== + * VARIABLES: Variables are strings indexed by a name. + */ + +/* _variablesAllocate: ---------------------------------------------- + * Expand a block of variables if necessary + */ +bool _variablesAllocate(variables* vars) +{ + ASSERT_PTR(vars); + + if(!vars->thevars) + { + /* Allocate new set */ + vars->thevars = (struct vari*)malloc(sizeof(struct vari) * BLOCK_SIZE); + if(!vars->thevars) return false; + vars->alloc = BLOCK_SIZE; + vars->cur = 0; + } + + /* If no more space ... */ + if(vars->alloc <= vars->cur) + { + /* ...double allocation */ + size_t sz = sizeof(struct vari) * vars->alloc; + vars->thevars = (struct vari*)reallocf(vars->thevars, sz * 2); + if(!vars->thevars) return false; + vars->alloc *= 2; + } + + return true; +} + +/* _variablesNew: -------------------------------------------------------- + * Make a new variable in a given block + */ +struct vari* _variablesNew(variables* vars, const char* name, + const char* val, size_t cnt) +{ + struct vari* v; + ASSERT_PTR(vars); + + if(!_variablesAllocate(vars)) + return NULL; + + v = &(vars->thevars[vars->cur]); + + /* Add new variable */ + v->name = strdup(name); + v->value = (char*)malloc(sizeof(char) * (cnt + 2)); + starclr(v->value); + starnadd(&(v->value), val, cnt); + + /* Oops! */ + if(!v->name || !v->value) + return NULL; + + vars->cur++; + + return v; +} + +/* _variablesFind: ------------------------------------------------------ + * Find a variable in the block by name + */ +struct vari* _variablesFind(variables* vars, const char* name) +{ + size_t i; + char* val; + + ASSERT_PTR(vars); + i = vars->cur; + + while(i--) + { + if(!strcasecmp(name, vars->thevars[i].name)) + return &(vars->thevars[i]); + } + + /* If not found then look up in the Environment */ + if(val = getenv(name)) + return _variablesNew(vars, name, val, strlen(val)); + + return NULL; +} + +/* variablesInit: ------------------------------------------------------ + * Initialize and possibly allocate a variable set + */ +bool variablesInit(variables* vars) +{ + zero(*vars); + return true; +} + +/* variablesFree: ------------------------------------------------------ + * Free a set of variables + */ +void variablesFree(variables* vars) +{ + if(vars) + { + ASSERT_PTR(vars); + if(vars->thevars) + free(vars->thevars); + vars->thevars = NULL; + } +} + +/* variablesAddBytes: -------------------------------------------------- + * Add a counted string to a variable. + */ +bool variablesAddBytes(variables* vars, const char* name, + const char* val, size_t cnt) +{ + struct vari* v; + ASSERT_PTR(vars); + + v = _variablesFind(vars, name); + if(!v) + { + return _variablesNew(vars, name, val, cnt) != NULL; + } + else + { + /* Already have variable. Just add value */ + ASSERT(v->value); + starnadd(&(v->value), val, cnt); + return v->value != NULL; + } +} + +/* variablesAdd: ------------------------------------------------------- + * Add a string to a variable. + */ +bool variablesAdd(variables* vars, const char* name, const char* val) +{ + ASSERT_PTR(vars); + return variablesAddBytes(vars, name, val, strlen(val)); +} + +/* variablesClear: ---------------------------------------------------- + * Clear a variable's value. + */ +bool variablesClear(variables* vars, const char* name) +{ + struct vari* v; + ASSERT_PTR(vars); + if(v = _variablesFind(vars, name)) + starclr(v->value); + + return true; +} + +/* variablesClearAll: ------------------------------------------------- + * Delete all variables from set + */ +bool variablesClearAll(variables* vars) +{ + ASSERT_PTR(vars); + if(vars->thevars) + { + while(vars->cur--) + { + ASSERT(vars->thevars[vars->cur].name); + free(vars->thevars[vars->cur].name); + ASSERT(vars->thevars[vars->cur].value); + free(vars->thevars[vars->cur].value); + } + + vars->cur = 0; + } + + return true; +} + +/* _escapeString: ----------------------------------------------------- + * Helper function to make a string pass through the regex compiler + */ +static char* _escapeString(const char* string) +{ + const char kSpecialChars[] = "$^*(){}[]\\?+."; + size_t pos = 0; + size_t cnt = 0; + char* buff; + + size_t len = strlen(string); + + while((pos += strcspn(string + pos, kSpecialChars)) < len) + cnt++, pos++; + + if(buff = (char*)malloc(sizeof(char) * (len + cnt + 1))) + { + pos = 0; + strcpy(buff, string); + while((pos += strcspn(buff + pos, kSpecialChars)) < len + cnt) + strins(buff + pos, "\\"), pos += 2; + } + + return buff; +} + +/* variablesSubstitute: ----------------------------------------------- + * Perform variable and register substitution on a string + */ +int variablesSubstitute(variables* vars, r_stream* stream, r_script* script, + char** pstr, bool mode) +{ + size_t len = strlen(*pstr); + char* next = *pstr; + struct internal_state* state = stream->state; + + ASSERT_PTR(vars); + ASSERT_PTR(state); + + #define SYNTAX_ERROR(s) \ + do{ \ + scriptSetError(script, s); \ + return R_SYNTAX; \ + } while(0) + + /* + If mode == true then we're substituting inside a regular expression + otherwise we're substituting in replaced text + */ + + /* Find next backslash or percent */ + while(next += strcspn(next, "\\%"), next && next[0] != 0) + { + switch(*next) + { + + /* It's a variable or a register */ + case '%': + + /* Is it a register? */ + if(isdigit(next[1])) + { + /* Registers only in replace mode replace mode */ + if(!mode) + { + uint reg = next[1] - 0x30; + + /* Get register number */ + if(reg < REGISTER(r_cg)) + { + /* Save offset for reallocations */ + size_t off = next - *pstr; + size_t reglen = REGISTER2(r_e0, reg) - REGISTER2(r_b0, reg); + + /* Reallocate to fit register text */ + strrsrv(*pstr, strlen(*pstr) + reglen); + if(!(*pstr)) return R_NOMEM; + + /* offset next properly */ + next = *pstr + off; + + /* Replace \N with register text */ + next = strnrep(next, 2, + (stream->nextIn + ABS_TO_REL(REGISTER2(r_b0, reg), stream->state)), + reglen); + } + else + /* If invalid number just blow away */ + next = strnrep(next, 2, "", 0); + } + } + + + /* Otherwise it's a variable */ + else + { + bool multi = false; + size_t len, off; + char temp; + struct vari* v; + const char* value; + + /* Get the name */ + len = strspn(next + 1, kValidIdentifier); + if(len == 0) SYNTAX_ERROR("Invalid variable name."); + + /* Increment length for the % prefix */ + len++; + + /* Null terminate the variable name */ + temp = next[len]; + next[len] = 0; + + /* Do we have this variable? */ + if(v = _variablesFind(vars, next + 1)) + value = v->value; + else + value = "\0\0"; + + /* Check if the variable is an array */ + if(mode) + multi = starnext(value) ? true : false; + + /* Unnull-terminate it */ + next[len] = temp; + + /* Get offset for reallocations */ + off = next - *pstr; + + /* Reallocate the string to accomodate new replacement */ + strrsrv(*pstr, strlen(*pstr) + (starend(value) - value) + 4); + if(!*pstr) return R_NOMEM; + + /* Offset next back properly */ + next = *pstr + off; + + /* Eat variable name and add open parentheses if needed + The '?:' after the opening paren denotes a non capturing + group */ + next = strrep(next, len, multi ? "(?:" : ""); + + do + { + /* If in regular expression mode ... */ + char* escval; + if(mode) + { + /* ... escape the value */ + escval = _escapeString(value); + if(!escval) return R_NOMEM; + } + + /* Insert the value */ + next = strrep(next, 0, mode ? escval : value); + + /* If in regular expression mode free escaped. */ + if(mode) + free(escval); + + /* If not multi then only put the first one in */ + if(!multi) + break; + + /* Add a pipe (alternation operator) if we have more values */ + else + next = strrep(next, 0, starnext(value) ? "|" : ""); + + } + while(value = starnext(value)); + + /* Add closing parentheses if needed */ + next = strrep(next, 0, multi ? ")" : ""); + } + break; + + + /* It's a variable name or escaped character */ + case '\\': + + /* + Only unescape in replace mode + Regular expressions will unescape everything else + */ + if(!mode) + next = strnrep(next, 1, "", 0); + else + next++; + + next++; + + break; + } + } + + return R_OK; +} + +/* variablesValidName: ------------------------------------------------- + * Is the given name a valid variable name + */ +bool variablesValidName(const char* name) +{ + return strspn(name, kValidIdentifier) >= strlen(name); +} + +/* variablesHasVars: ---------------------------------------------------- + * Does the given string have variables or registers that need + * substitution? + */ +bool variablesHasVars(const char* string) +{ + const char* cur = string; + + if(cur = strchr(cur, '%')) + { + if(!isEscaped(string, cur)) + return true; + + cur++; + } + + return false; +} + +/* ========================================================================= + * LOCKS: Locks are used by the engine to implement the replacement locks + */ + +/* _locksAllocate: ------------------------------------------------------- + * Expand a block of locks if needed + */ +bool _locksAllocate(locks* lcks) +{ + if(!lcks->thelocks) + { + /* Allocate the first set */ + lcks->thelocks = (struct lock*)malloc(sizeof(struct lock) * BLOCK_SIZE); + if(!lcks->thelocks) return false; + lcks->alloc = BLOCK_SIZE; + locksClearAll(lcks); + } + + /* If not enough space ... */ + if(lcks->alloc <= lcks->cur) + { + /* ... reallocate double */ + size_t sz = sizeof(struct lock) * lcks->alloc; + lcks->thelocks = (struct lock*)reallocf(lcks->thelocks, sz * 2); + if(!lcks->thelocks) return false; + lcks->alloc *= 2; + } + + return true; +} + +/* locksInit: ------------------------------------------------------------- + * Initialize and or allocate a block of locks + */ +bool locksInit(locks* lcks) +{ + zero(*lcks); + return true; +} + +/* locksFree: ------------------------------------------------------------- + * Free a set of locks + */ +void locksFree(locks* lcks) +{ + if(lcks) + { + ASSERT_PTR(lcks); + if(lcks->thelocks) + free(lcks->thelocks); + lcks->thelocks = NULL; + } +} + +#define RANGE_ADD(bd, ed, b1, e1) \ + (((bd) = (bd) < (b1) ? (bd) : (b1)), ((ed) = (ed) > (e1) ? (ed) : (e1))) + +#define RANGE_INTERSECTS(b1, e1, b2, e2) \ + ((b1) < (e2) && (e1) > (b2)) + +#define RANGE_BEFORE(b1, e1, b2, e2) \ + ((b1) <= (b2) && (e1) <= (e2)) + + +/* locksAdd: --------------------------------------------------------------- + * Add a lock to the set + */ +bool locksAdd(locks* lcks, size_t beg, size_t end) +{ + size_t i = 0; + + for(; i < lcks->cur; i++) + { + if(RANGE_INTERSECTS(beg, end, lcks->thelocks[i].beg, lcks->thelocks[i].end)) + { + RANGE_ADD(lcks->thelocks[i].beg, lcks->thelocks[i].end, beg, end); + + /* Clean up any doubles */ + for(i = 0; i < lcks->cur - 1; i++) + { + if(RANGE_INTERSECTS(lcks->thelocks[i].beg, lcks->thelocks[i].end, + lcks->thelocks[i + 1].beg, lcks->thelocks[i + 1].end)) + { + RANGE_ADD(lcks->thelocks[i].beg, lcks->thelocks[i].end, + lcks->thelocks[i + 1].beg, lcks->thelocks[i + 1].end); + + lcks->cur--; + memmove(lcks->thelocks + i + 1, lcks->thelocks + i + 2, + sizeof(lcks->thelocks[0]) * (lcks->cur - (i + 1))); + i--; + } + } + + return true; + } + + if(RANGE_BEFORE(beg, end, lcks->thelocks[i].beg, lcks->thelocks[i].end)) + break; + } + + if(!_locksAllocate(lcks)) return false; + + /* Move the locks one down */ + memmove(lcks->thelocks + i + 1, lcks->thelocks + i, + sizeof(lcks->thelocks[0]) * (lcks->cur - i)); + + lcks->thelocks[i].beg = beg; + lcks->thelocks[i].end = end; + + lcks->cur++; + + return true; +} + +#ifdef _DEBUG +static void _locksTestIntersects() +{ + ASSERT(!RANGE_INTERSECTS(0x0F, 0x15, 0x15, 0x15)); + ASSERT(RANGE_INTERSECTS(0x0F, 0x15, 0x0F, 0x15)); + ASSERT(RANGE_INTERSECTS(0x10, 0x15, 0x0F, 0x15)); + ASSERT(RANGE_INTERSECTS(0x0F, 0x12, 0x0F, 0x15)); + ASSERT(RANGE_INTERSECTS(0x10, 0x12, 0x0F, 0x15)); + ASSERT(RANGE_INTERSECTS(0x0F, 0x15, 0x10, 0x12)); + ASSERT(RANGE_INTERSECTS(0x10, 0x1A, 0x0F, 0x15)); + ASSERT(RANGE_INTERSECTS(0x02, 0x12, 0x0F, 0x15)); + ASSERT(!RANGE_INTERSECTS(0x15, 0x15, 0x15, 0x15)); + ASSERT(!RANGE_INTERSECTS(0x15, 0x15, 0x15, 0x20)); +} +#endif + +/* locksCheck: ----------------------------------------------------------- + * Check a range against the locks + */ +bool locksCheck(locks* lcks, r_stream* stream, size_t beg, size_t end) +{ + size_t cnt; + +#ifdef _DEBUG + _locksTestIntersects(); +#endif + + for(cnt = 0; cnt < lcks->cur; cnt++) + if(RANGE_INTERSECTS(beg, end, lcks->thelocks[cnt].beg, lcks->thelocks[cnt].end)) + return true; + + return false; +} + + +/* ============================================================================ + * REPLACEMENT LIST: Contains the text of any replacements. They're copied + * into the output later. + */ + +/* replacementAlloc: --------------------------------------------------------- + * Prepare and allocate a replacement + */ +int replacementAlloc(r_stream* stream, const char* text, replacement** pprep) +{ + int ret; + + size_t len = strlen(text); + ret = R_OK; + + /* Allocate the replacement. Note that text hangs off the end */ + *pprep = (replacement*)malloc(sizeof(replacement) + (sizeof(char) * len)); + if(!*pprep) + ret = R_NOMEM; + else + { + /* Set it up properly */ + memset(*pprep, 0, sizeof(replacement) + len); + (*pprep)->next = NULL; + + /* Copy string on end of buffer */ + strcpy((*pprep)->text, text); + } + + return ret; +} + + +/* replacementAdd: ----------------------------------------------------------- + * Insert replacement into queue in the right order (IMPORTANT!) + */ +void replacementAdd(replacement* repl, r_stream* stream) +{ + replacement top; + replacement* first = ⊤ + top.beg = top.end = 0; + top.next = stream->state->replaces; + + /* Find appropriate pos */ + while(first && first->next && (first->next->end < repl->end)) + first = first->next; + + while(first && first->next && (first->next->beg < repl->beg)) + first = first->next; + + /* Hook in */ + repl->next = first->next; + first->next = repl; + + stream->state->replaces = top.next; +} + + +/* replacementPop: ------------------------------------------------------- + * Remove and return a replacement from the queue + */ +replacement* replacementPop(replacement* repl) +{ + replacement* ret = repl->next; + free(repl); + return ret; +} + +/* replacementDump: ------------------------------------------------------ + * Dump all replacements in the stream to stderr + */ +void replacementDump(r_stream* stream) +{ + replacement* repl = stream->state->replaces; + + while(repl) + { + fprintf(stderr, " beg: %x end: %x text: \'%s\'\n", repl->beg, repl->end, repl->text); + repl = repl->next; + } +} + + +/* ========================================================================= + * REGISTER HELPER FUNCTIONS + */ + +/* regsSet: ---------------------------------------------------------- + * Offset and copy an entire set of regs int the main registers + */ +static void regsSet(struct internal_state* state, int pcreregs[], size_t offset) +{ + size_t i; + for(i = 0; i < REGISTER(r_cg); i++) + { + REGISTER2(r_b0, i) = pcreregs[i * 2] + offset; + REGISTER2(r_e0, i) = pcreregs[(i * 2) + 1] + offset; + } + + /* Set rest of registers as invalid */ + for( ; i < MAX_REGS; i++) + { + REGISTER2(r_b0, i) = ~0; + REGISTER2(r_b0, i) = ~0; + } +} + + +/* =========================================================================== + * MAIN EXECUTION FUNCTIONS + */ + + +/* vmInit: -------------------------------------------------------------- + * Initialize state for execution + */ +bool vmInit(r_stream* stream) +{ + struct internal_state* state = stream->state; + ASSERT(stream->state); + + if(!variablesInit(&(state->vars)) || + !memoryInit(&(state->mem)) || + !locksInit(&(state->lcks)) || + !dataInit(&(state->working))) + return R_NOMEM; + + /* init replaces */ + state->replaces = NULL; + + return true; +} + + +/* vmFree: -------------------------------------------------------------- + * Undo initialization + */ +void vmFree(r_stream* stream) +{ + struct internal_state* state = stream->state; + ASSERT(stream->state); + + while(state->replaces) + state->replaces = replacementPop(state->replaces); + + variablesFree(&(state->vars)); + memoryFree(&(state->mem)); + locksFree(&(state->lcks)); + dataFree(&(state->working)); +} + + +/* vmClean: ------------------------------------------------------------- + * Prepare a VM for a totally new replacement operation + */ +void vmClean(r_stream* stream) +{ + struct internal_state* state = stream->state; + ASSERT(stream->state); + + while(state->replaces) + state->replaces = replacementPop(state->replaces); + + variablesClearAll(&(state->vars)); + memoryClearAll(&(state->mem)); + locksClearAll(&(state->lcks)); + dataClearAll(&(state->working)); + stream->state->offset = 0; +} + + + + +/* rvalue: -------------------------------------------------------------- + * Return a value to be used on the right (value) side of an expression + */ +#ifdef USE_STACK_VARS +uint rvalue(vmop_t* ops, struct internal_state* state, + memory* mem, memory* stackMem) +#else +uint rvalue(vmop_t* ops, struct internal_state* state, memory* mem) +#endif +{ + switch(ARG_TYPE(*ops)) + { + case ARG_VAL_TYPE: + return ARG_GET_VALUE(*((uint*)ops)); + case ARG_REG_TYPE: + return REGISTER(*ops); + case ARG_MEM_TYPE: + return *(memoryValue(mem, ARG_GET_MEMORY(*((uint*)ops)))); + case ARG_STACK_TYPE: +#ifdef USE_STACK_VARS + return *(memoryValue(stackMem, ARG_GET_STACK(*((uint*)ops)))); +#else + return *(memoryValue(mem, ARG_GET_STACK(*((uint*)ops)))); +#endif + default: + ASSERT(false); + return 0; + } +} + + +/* lvalue: ----------------------------------------------------------------- + * Return a value to be used on the left side (assigned) of an expression + */ +#ifdef USE_STACK_VARS +static uint* lvalue(vmop_t* ops, struct internal_state* state, + memory* mem, memory* stackMem) +#else +static uint* lvalue(vmop_t* ops, struct internal_state* state, memory* mem) +#endif +{ + switch(ARG_TYPE(*ops)) + { + case ARG_VAL_TYPE: + ASSERT(false && "Can't put a value on left side."); + return 0; + case ARG_REG_TYPE: + return &(REGISTER(*ops)); + case ARG_MEM_TYPE: + return memoryValue(mem, ARG_GET_MEMORY(*((uint*)ops))); + case ARG_STACK_TYPE: +#ifdef USE_STACK_VARS + return memoryValue(stackMem, ARG_GET_STACK(*((uint*)ops))); +#else + return memoryValue(mem, ARG_GET_MEMORY(*((uint*)ops))); +#endif + default: + ASSERT(false); + return 0; + } +} + + +/* vmExecute: ------------------------------------------------------------- + * The main VM run loop + */ +int vmExecute(r_stream* stream, r_script* script) +{ +#ifdef USE_STACK_VARS + #define RVALUE(ops) rvalue(ops, state, &(state->mem), stackVars) + #define LVALUE(ops) lvalue(ops, state, &(state->mem), stackVars) +#else + #define RVALUE(ops) rvalue(ops, state, &(state->mem)) + #define LVALUE(ops) lvalue(ops, state, &(state->mem)) +#endif + + #define PUSH_STACK(top, v) ((top)++[0] = (v)) + #define POP_STACK(top) ((--top)[0]) + + /* Jump to cleanup label instead of return */ + #define RETURN(r) \ + do { \ + retval = r; \ + goto cleanup; \ + } while (0) + + struct internal_state* state; + int retval = R_OK; + vmop_t* ops; + uint passes = 0; + + /* These are the registers passed to PCRE */ + int pcreregs[MAX_REGS * 3]; + int num_regs = 0; + + /* And over here we have the stack */ + uint* vmStack = NULL; + size_t allocStack = 0; + uint** stack; + #define STACK *stack + +#ifdef USE_STACK_VARS + memory* stackVars; +#endif + + /* The text buffer */ + char* text = NULL; + + /* We just setup some vars for easy access to structs */ + state = stream->state; + ops = script->ops; + zero(state->vmregs); + ASSERT(script->ops); + + stack = (uint**)&(state->vmregs[r_sp >> 2]); + + + /* Set the initial limits in the x1 and y1 registers */ + REGISTER(r_x1) = REL_TO_ABS(0, state); + REGISTER(r_y1) = REL_TO_ABS(stream->availIn, state); + + /* Stack variables */ +#ifdef USE_STACK_VARS + stackVars = (memory*)malloc(sizeof(memory)); + if(!stackVars || !memoryInit(stackVars)) + RETURN(R_NOMEM); +#endif + + /* Preallocate some memory for the text buffer */ + text = malloc(sizeof(char) * 256); + if(!text) RETURN(R_NOMEM); + text[0] = 0; + + + while(1) + { + vmop_t op = *ops; + ops++; + + /* The text buffer should always be pointing to a valid + block of memory */ + ASSERT(text != NULL); + + + /* Check and see if we have enough stack and allocate if not */ + if((STACK + 0x010) > (vmStack + allocStack)) + { + size_t off = STACK - vmStack; + vmStack = (uint*)reallocf(vmStack, (allocStack + 0x080) * sizeof(uint)); + if(!vmStack) + RETURN(R_NOMEM); + + allocStack += 0x080; + STACK = vmStack + off; + } + + + /* Main switch which dispatches the ops */ + switch(op) + { + + /* end: Finished executing script (but can come back again for more) */ + case o_end: + RETURN(R_OK); + + + /* nop: Do nothing */ + case o_nop: + break; + + + /* push: Push a value on the stack */ + case o_push: + PUSH_STACK(STACK, RVALUE(ops)); + + INC_ARGUMENT(ops); + break; + + + /* pop: Pop a value from the stack */ + case o_pop: + *(LVALUE(ops)) = POP_STACK(STACK); + INC_ARGUMENT(ops); + break; + + + /* lock: Lock the area between the selected area */ + case o_lock: + { + uint beg = RVALUE(ops); + INC_ARGUMENT(ops); + + if(!locksAdd(&(state->lcks), beg, RVALUE(ops))) + RETURN(R_NOMEM); + + INC_ARGUMENT(ops); + + /* Locking is an action too */ + REGISTER(r_ac) = 1; + } + break; + + + /* check: Check the selected area against any locks */ + case o_check: + { + uint beg = RVALUE(ops); + INC_ARGUMENT(ops); + + /* Check the lowest registers against the locks it against the locks */ + REGISTER(r_fe) = locksCheck(&(state->lcks), stream, beg, RVALUE(ops)) + ? 0 : 1; + + INC_ARGUMENT(ops); + } + break; + + + + /* match: Match a pattern buffer */ + case o_match: + { + uint beg, end, begLimit, endLimit, begMatch, endMatch; + match_op* header; + pcre* re = NULL; + pcre_extra* extra = NULL; + bool cache = false; + size_t i = 0; + int rt; + + beg = ABS_TO_REL(RVALUE(ops), state); + INC_ARGUMENT(ops); + end = ABS_TO_REL(RVALUE(ops), state); + INC_ARGUMENT(ops); + + begLimit = ABS_TO_REL(REGISTER(r_x1), state); + endLimit = ABS_TO_REL(REGISTER(r_y1), state); + + /* Determine what kind of a match we're talking about here */ + header = (match_op*)ops; + + /* This is the only type of regular expression we support + at the moment */ + ASSERT(header->type & kMatchPcre); + + + /* + * Check if we've already cached the compiled + * regular expression. + * - We use the op header pointer as key to the pcre struct + * - And the pcre struct pointer as the key to the pcre_extra struct + */ + if(re = (pcre*)dataGetValue(&(state->working), header)) + { + /* This prevents the freeing of stuff below */ + cache = true; + + /* Get out the pcre_extra if present */ + extra = (pcre_extra*)dataGetValue(&(state->working), re); + } + + /* Otherwise we compile it */ + else + { + match_op_pcre* pcreop = (match_op_pcre*)header; + char* pattern = strdup(pcreop->pattern); + int erroroffset; + const char* error = NULL; + + /* If there's variables then don't cache and ... */ + if(!(cache = !variablesHasVars(pattern))) + { + /* ... do variable substitution */ + rt = variablesSubstitute(&(state->vars), stream, script, &pattern, true); + if(rt < 0) RETURN(rt); + } + + /* Compile the pattern */ + re = pcre_compile(pattern, pcreop->options, + &error, &erroroffset, NULL); + + if(!re) + { + if(error) + scriptSetError(script, error); + RETURN(R_REGEXP); + } + + /* + * If there's no variables in the regular expression + * then we can cache the compiled pcre and study it + */ + if(cache) + { + if(!dataSetValue(&(state->working), header, re)) + RETURN(R_NOMEM); + + extra = pcre_study(re, 0, &error); + if(error) + { + scriptSetError(script, error); + RETURN(R_REGEXP); + } + + if(extra && !dataSetValue(&(state->working), re, extra)) + RETURN(R_NOMEM); + } + + free(pattern); + } + + + begMatch = beg; + endMatch = end; + + /* Set failed flag */ + REGISTER(r_fe) = 0; + + while(i < locksSize(&(state->lcks)) && + locksEnd(&(state->lcks), i) <= REL_TO_ABS(beg, state)) + i++; + + do + { + if(i < locksSize(&(state->lcks))) + endMatch = ABS_TO_REL(locksBeg(&(state->lcks), i), state); + else if(i >= locksSize(&(state->lcks))) + endMatch = end; + + /* If that put it too high then bring back */ + if(endMatch > end) + endMatch = end; + + /* If we haven't overstepped the bounds, search */ + if(begMatch <= endMatch) + { + int opts = 0; + + /* + * We have to do a little trickery here + * First we fool pcre_exec into thinking that our range start + * is the beginning of the string. We fix up the registers later + * + * Then we fool'm into thinking that the our search end is the + * end of the string, but if it's really not the end of the + * range then set not_eol so that '$' doesn't match. + */ + + if(endMatch != endLimit) + opts |= PCRE_NOTEOL; + + /* Do actual search */ + /* TODO: Get this ready for binary replacements */ + rt = pcre_exec(re, extra, + ((char*)stream->nextIn + begLimit), /* Data */ + endMatch - begLimit, /* size */ + begMatch - begLimit, /* position */ + opts, /* options */ + pcreregs, /* group registers */ + MAX_REGS * 3); /* number of registers */ + + /* These are programmer errors */ + ASSERT(rt != PCRE_ERROR_NULL); + ASSERT(rt != PCRE_ERROR_BADOPTION); + ASSERT(rt != PCRE_ERROR_BADMAGIC); + ASSERT(rt != PCRE_ERROR_UNKNOWN_NODE); + ASSERT(rt != PCRE_ERROR_NOSUBSTRING); + + if(rt == PCRE_ERROR_NOMEMORY) + RETURN(R_NOMEM); + + if(rt >= 0) + { + /* Found a match! */ + + /* Do group register maintainance */ + REGISTER(r_cg) = rt; + regsSet(state, pcreregs, REL_TO_ABS(begLimit, state)); + + /* Set succeeded flag */ + REGISTER(r_fe) = 1; + + /* Get out of here */ + break; + } + + /* Make sure we've got all the errors */ + else if(rt != -1) + ASSERT(0); + + } + + /* The beginning of the next search block should be + the end of the current lock */ + if(i < locksSize(&(state->lcks))) + begMatch = ABS_TO_REL(locksEnd(&(state->lcks), i), state); + + i++; + } + while(endMatch < end); + + if(!cache) + { + if(re) + free(re); + if(extra) + free(extra); + } + + ops += match_op_size(*header); + } + + break; + + + + /* setvar: Add selected text to a variable */ + case o_setvar: + { + var_op* pOp = (var_op*)ops; + + /* TODO: Get this ready for binary replacements */ + if(!variablesAddBytes(&(state->vars), (char*)pOp->name, text, strlen(text))) + RETURN(R_NOMEM); + + ops += var_op_size(*pOp); + } + break; + + + /* setvar: clear a variable */ + case o_clrvar: + { + var_op* pOp = (var_op*)ops; + + /* TODO: Get this ready for binary variables */ + variablesClear(&(state->vars), (char*)pOp->name); + ops += var_op_size(*pOp); + } + break; + + + case o_je: + case o_jne: + case o_jmp: + + /* Get the conditional jumps out of the way */ + if((op == o_je && REGISTER(r_fe) == 0) || + (op == o_jne && REGISTER(r_fe) > 0)) + { + INC_ARGUMENT(ops); + break; + } + else + { + /* Now do the jump */ + vmop_t* opOld = ops; + + /* Change the op to the indicated pos */ + ops = script->ops + RVALUE(ops); + + /* + * If a backwards jump then count as a "pass" thingy + * we use this count to catch endless loop errors + */ + + /* TODO: I don't think we need this anymore */ + if(opOld > ops) + { + passes++; + if(passes > MAX_PASSES) + return R_LOOP; + } + } + break; + + + /* repl: Replace last match with given text */ + case o_repl: + { + uint beg, end; + replacement* rep = NULL; + int rt; + + beg = RVALUE(ops); + INC_ARGUMENT(ops); + end = RVALUE(ops); + INC_ARGUMENT(ops); + + /* Get the formatted replacement */ + rt = replacementAlloc(stream, text, &rep); + if(rt < 0) + RETURN(rt); + + rep->beg = beg; + rep->end = end; + + /* If we have a confirm function then ask about replacement */ + if(stream->fMatch) + { + r_replace repl; + repl.from = ABS_TO_REL(beg, state); + repl.flen = end - beg; + repl.to = (byte*)rep->text; + repl.tlen = strlen(rep->text); + repl.offset = beg; + + if(!(stream->fMatch)(stream, &repl)) + { + free(rep); + rep = NULL; + } + } + + if(rep) + { + /* Add it! */ + REGISTER(r_ac) = 1; + stream->total++; + replacementAdd(rep, stream); + } + } + + break; + + + /* stop: Stop execution of script */ + case o_stop: + { + uint error = RVALUE(ops); + INC_ARGUMENT(ops); + + scriptSetError(script, text); + + if(error == 0) + RETURN(R_DONE); + else + RETURN(R_USER); + } + break; + + + /* cmp: compare two values and set registers accordingly */ + case o_cmp: + { + uint arg1, arg2; + + arg1 = RVALUE(ops); + INC_ARGUMENT(ops); + arg2 = RVALUE(ops); + INC_ARGUMENT(ops); + + REGISTER(r_fe) = (arg1 == arg2) ? 1 : 0; + REGISTER(r_fg) = (arg1 > arg2) ? 1 : 0; + REGISTER(r_fl) = (arg1 < arg2) ? 1 : 0; + } + break; + + + /* test: Test whether value is 0 or not */ + case o_test: + REGISTER(r_fe) = RVALUE(ops) != 0; + INC_ARGUMENT(ops); + break; + + + /* mov: Set one value to another */ + case o_mov: + { + uint* dest = LVALUE(ops); + INC_ARGUMENT(ops); + *dest = RVALUE(ops); + INC_ARGUMENT(ops); + } + break; + + + /* call: Call a function */ + case o_call: + { +#ifdef USE_STACK_VARS + memory* newStack; +#endif + uint pos = RVALUE(ops); + INC_ARGUMENT(ops); + + /* Push current instruction pointer on the stack */ + PUSH_STACK(STACK, ops - script->ops); + + /* Change instruction pointer to function start */ + ops = script->ops + pos; + +#ifdef USE_STACK_VARS + /* Push new set of stack variables */ + newStack = (memory*)malloc(sizeof(memory)); + if(!newStack || !memoryInit(newStack)) + RETURN(R_NOMEM); + + newStack->prev = stackVars; + stackVars = newStack; +#endif + } + break; + + + /* ret: Return from a function */ + case o_ret: + { +#ifdef USE_STACK_VARS + memory* prev; +#endif + /* Get location to return to */ + uint pos = POP_STACK(STACK); + + /* Change instruction pointer to returned location */ + ops = script->ops + pos; + +#ifdef USE_STACK_VARS + /* Get previous set of stack vars */ + ASSERT(stackVars->prev); + prev = stackVars->prev; + memoryFree(stackVars); + free(stackVars); + stackVars = prev; +#endif + } + break; + + + /* add: Add 2 values */ + case o_add: + { + /* Get first value */ + uint* val = LVALUE(ops); + INC_ARGUMENT(ops); + *val += RVALUE(ops); + INC_ARGUMENT(ops); + } + break; + + + /* sub: Subtract values */ + case o_sub: + { + /* Get first value */ + uint* val = LVALUE(ops); + INC_ARGUMENT(ops); + *val -= RVALUE(ops); + INC_ARGUMENT(ops); + } + break; + + + /* text: set the text buffer */ + case o_text: + { + text_op* top = (text_op*)ops; + int rt; + + text = (char*)reallocf(text, top->len + 1); + if(!text) RETURN(R_NOMEM); + + memcpy(text, top->string, top->len * sizeof(char)); + text[top->len] = 0; + + rt = variablesSubstitute(&(state->vars), stream, script, &text, false); + if(rt < 0) RETURN(rt); + + ops += text_op_size(*top); + } + break; + + /* msg: output text in buffer */ + case o_msg: + { + if(stream->fMessage) + (stream->fMessage)(stream, text); + } + break; + + default: + /* Invalid Instruction! */ + ASSERT(false); + } + } + +cleanup: + if(vmStack) + free(vmStack); + + while(stackVars) + { + memory* prev = stackVars->prev; + memoryFree(stackVars); + free(stackVars); + stackVars = prev; + } + + return retval; +} + + + |