diff options
Diffstat (limited to 'lib/rlib.c')
-rw-r--r-- | lib/rlib.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/lib/rlib.c b/lib/rlib.c new file mode 100644 index 0000000..db84548 --- /dev/null +++ b/lib/rlib.c @@ -0,0 +1,591 @@ +/* + * 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> + */ + +/* ========================================================================= + * Main RLIB + * 2000-2001 Copyright, Nate Nielsen + */ + +#include <stdarg.h> +#include "common/usuals.h" +#include "common/compat.h" +#include "lib/rlib.h" +#include "priv.h" +#include "rep.h" +#include "common/binfile.h" +#include "common/repfile.h" + +/* rlibInit: -------------------------------------------------------------- + * Initialize a stream. We already expect stream to be zero'd + */ + +int rlibInit(r_stream* stream, long options) +{ + byte* op = NULL; + int i, j; + struct internal_state* state; + + if(stream->state != NULL) + return R_INVARG; + + /* Allocate a state */ + state = stream->state = + (struct internal_state*)malloc(sizeof(struct internal_state)); + + if(!state) + return R_NOMEM; + + zero(*state); + + /* Init the stream for the current session */ + if(!vmInit(stream)) + return R_NOMEM; + + state->options = options; + + /* Init our case table */ + /* we only do this once for the whole bunch */ + for (i = 0; i < 256; i++) + stream->state->caseTranslate[i] = i; + for (j = 'a'; j <= 'z'; j++) + stream->state->caseTranslate[j] = j - ('a' - 'A'); + + return R_OK; +} + + +/* rlibCompile: ---------------------------------------------------- + * Compile a script + */ +int rlibCompile(r_script* script, const char* data) +{ + return compilerRun(script, data); +} + + +/* rlibClear: ------------------------------------------------------------- + * Prepare stream for a new file + */ +void rlibClear(r_stream* stream) +{ + if(stream) + { + if(stream->state) + vmClean(stream); + } +} + + + +/* rLibRun: --------------------------------------------------------------- + * The replacement coordinator, output etc... +*/ +int rlibRun(r_stream* stream, r_script* script, int done) +{ + #define RETURN(r) do { retv = r; goto finally; } while(0) + + struct internal_state* state = stream->state; + size_t backup, curOffset; + int retv = R_OK; + + if(!stream->state) + return R_INVARG; + + /* Usually have to have an output function, unless just a matcher */ + if(!stream->fWrite && !(state->options & RLIB_MODE_MATCHER)) + return R_INVARG; + /* And if we are a matcher we have to have a match function */ + if(state->options & RLIB_MODE_MATCHER && !stream->fMatch) + return R_INVARG; + + + /* + * Keep at least 1/2 amount of backup + * Backup is only kept if no matches occur in backup area + */ + backup = done ? 0 : stream->availIn / 2; + + /* + * Since there are conversions (between absolute and relative offsets) + * below, we need to keep inOffset the same for this session. So copy + * it and update it again at the end of the function. + */ + curOffset = 0; + + /* Need some data to work with */ + if(!stream->nextIn || !stream->availIn) + RETURN(R_IN); + + + stream->total = 0; + + /* Execute the script */ + retv = vmExecute(stream, script); + + /* Error? */ + if(retv < 0) + RETURN(retv); + + /* Fall through to commit code */ + if(retv == R_DONE) + backup = 0; + + if(!(state->options & RLIB_MODE_MATCHER)) + { + /* + * Here we write out what we've found so far and advance the + * pointers + * + * On the first round we look something like this + * + * <- state->searched -> + * |---------------------------------------------| <- stream->nextIn + * |----------------------------------------| <- stream->nextOut + * replacements found ^- leftovers + */ + + /* toWrite is the size of the current block we need to transfer */ + size_t toWrite; + /* block is the amount we can actually transfer (depending on output buffer) */ + size_t block = stream->availIn; + +#ifdef VERBOSE + dumpRelpacements(stream); +#endif + + while(state->replaces) + { + /* Get the amount of data till the current replace */ + toWrite = block = ABS_TO_REL(state->replaces->beg, state) - curOffset; + + if(!(state->options & RLIB_MODE_PARSER)) + { + /* ... write out up to current replacement... */ + if(!(stream->fWrite)(stream, stream->nextIn, block)) + RETURN(R_IOERR); + } + + stream->nextIn += block; + stream->availIn -= block; + curOffset += block; + + + /* ... check space for replacement ... */ + block = strlen(state->replaces->text); + + /* ...write out replacement... */ + if(!(stream->fWrite)(stream, (byte*)state->replaces->text, block)) + RETURN(R_IOERR); + + /* ... and skip (!) the text that we replaced */ + block = state->replaces->end - state->replaces->beg; + stream->nextIn += block; + stream->availIn -= block; + curOffset += block; + +#ifdef _DEBUG + /* Check if things are in the right order */ + if(state->replaces->next) + ASSERT(state->replaces->end <= state->replaces->next->end); +#endif + /* Go to the next replacement */ + state->replaces = replacementPop(state->replaces); + } + + /* + * Now we check how much data we have left and copy + * up to backup if we have more + */ + + /* Copy out till backup marker */ + if((stream->availIn) >= backup) + { + /* Get block size ... */ + toWrite = block = stream->availIn - backup; + + if(!(state->options & RLIB_MODE_PARSER)) + { + if(!(stream->fWrite)(stream, stream->nextIn, block)) + RETURN(R_IOERR); + } + + stream->nextIn += block; + stream->availIn -= block; + curOffset += block; + } + } + + /* + * After this the search could start anew + * unless in flush mode or done + */ + if(done == true) + RETURN(R_DONE); + else + RETURN(R_IN); + +finally: + state->offset += curOffset; + + return retv; +} + + +/* rlibSetVar: ----------------------------------------------------------- + * Set a variable for the script to use + */ +int rlibSetVar(r_stream* stream, const char* var, const char* val) +{ + if(!stream || !stream->state || !var || !val) + return R_INVARG; + + variablesClear(&(stream->state->vars), var); + return variablesAdd(&(stream->state->vars), var, val) ? R_OK : R_NOMEM; +} + + +/* rlibDump: -------------------------------------------------------------- + * Dump the rep script opts for debugging + */ +void rlibDump(r_script* script, FILE* f) +{ + opsDump(script->ops, f); +} + + +/* rlibFree: -------------------------------------------------------------- + * Free associated structures data etc... + */ +void rlibFree(r_stream* stream, r_script* script) +{ + struct internal_state* state; + + if(stream) + { + if(state = stream->state) + { + /* Let execution free it's stuff */ + vmFree(stream); + + /* And free the state */ + free(state); + stream->state = NULL; + } + + zero(*stream); + } + + if(script) + { + if(script->ops) + free(script->ops); + + if(script->error) + free(script->error); + + zero(*script); + } + +} + + +/* scriptSetError: ------------------------------------------------- + * Set the stream error text to whatever + */ +void scriptSetError(r_script* script, const char* format, ...) +{ + char* msg; + + va_list vl; + va_start(vl, format); + + if(script->error) + free(script->error); + + if(vasprintf(&msg, format, vl) != -1) + script->error = msg; + else + script->error = NULL; + + va_end(vl); +} + +/* compileAlready: ------------------------------------------------------ + * See if the file has already been compiled and load if so + */ +int compileAlready(r_context* ctx, FILE* file) +{ + /* Should have already read header */ + bfval val; + BFILE h = NULL; + int retv = R_OK; + r_uint temp; + + if(!(h = bfStartFile(file))) + RETURN(R_INVARG); + + if(!repfReadHeader(h)) + RETURN(R_IN); + + while(bfReadValueInfo(h, &val)) + { + if(ferror(file)) + RETURN(R_IOERR); + + if(feof(file)) + RETURN(R_INVARG); + + switch(val.id) + { + case REPVAL_BUFSIZE: + if(ctx->block == 0) + bfReadValueData(h, &val, &(ctx->block)); + continue; + + case REPVAL_PARSEMODE: + if(bfReadValueData(h, &val, &temp)) + ctx->options |= (temp != 0) ? RLIB_MODE_PARSER : 0; + continue; + + case REPVAL_SCRIPT: + { + ctx->script.ops = (r_byte*)malloc(val.len); + if(!ctx->script.ops) + RETURN(R_NOMEM); + + bfReadValueData(h, &val, ctx->script.ops); + ctx->script.len = val.len; + continue; + } + break; + } + + bfSkipValueData(h, &val); + } + +finally: + if(h) + bfClose(h); + + return retv; +} + + +int repLoad(r_context* ctx, FILE* f) +{ + int retv = R_OK; + char* buff = NULL; + size_t len; + int r; + + rlibFree(NULL, &(ctx->script)); + + /* Okay now try and read header, and possibly process + an already executable file. */ + switch(r = compileAlready(ctx, f)) + { + /* It's already compiled */ + case R_OK: + RETURN(R_OK); + break; + + /* It's not compiled so compile */ + case R_IN: + break; + + /* Failed processing */ + default: + if(r < 0) + RETURN(r); + break; + }; + + /* Get file size */ + len = 0; + + if(fseek(f, 0, SEEK_END) || !(len = ftell(f)) || + fseek(f, 0, SEEK_SET)) + RETURN(R_IOERR); + + buff = (char*)malloc(len + 1); + if(!buff) + RETURN(R_NOMEM); + + if(fread(buff, 1, len, f) != len) + RETURN(R_IOERR); + + /* Needs compiling */ + buff[len] = '\0'; + + /* Init the stream */ + r = rlibCompile(&(ctx->script), buff); + if(r < 0) + RETURN(r); + +finally: + if(buff) + free(buff); + + return retv; +} + +int repInit(r_context* ctx) +{ + rlibInit(&(ctx->stream), ctx->options); + return R_OK; +} + +int repFile(r_context* ctx, FILE* fIn) +{ + r_uint batch; /* Current batch size */ + r_uint block = ctx->block; /* Block size */ + r_byte* buff = NULL; /* Input buffer */ + int retv = R_OK; + int r = R_IN; + int total = 0; + + if(!block) + { + if(!fseek(fIn, 0, SEEK_END) && (block = ftell(fIn)) != ~0) + block++; + + else + block = 0; + + fseek(fIn, 0, SEEK_SET); + } + + else + { + block *= 2; + } + + /* Okay now if it's a sane value just allocate it */ + if(block) + { + if(block > 0x0F00000) + RETURN(R_NOMEM); + + /* Allocate buffers */ + buff = (r_byte*)malloc(block); + if(!buff) + RETURN(R_NOMEM); + } + + /* Hook buffers to the stream */ + ctx->stream.nextIn = buff; + ctx->stream.availIn = 0; + + /* While we either have more data to put in or more output... */ + while(!feof(fIn) || r == R_DONE) + { + /* If rlib wants data then give it */ + if(r == R_IN) + { + /* This is a normal standard read */ + if(buff) + { + /* Move data to front */ + memmove(buff, ctx->stream.nextIn, ctx->stream.availIn); + + /* Set pointer to data */ + ctx->stream.nextIn = buff; + + /* Read rest of block */ + batch = fread(ctx->stream.nextIn + ctx->stream.availIn, + sizeof(r_byte), block - ctx->stream.availIn, fIn); + + ctx->stream.availIn += batch; + + if(ferror(fIn)) + RETURN(R_IOERR); + } + + /* Here we read as much as possible in one shot allocating as we go*/ + else + { + batch = 0; + block = 0; + + while(!feof(fIn)) + { + if(ferror(fIn)) + RETURN(R_IOERR); + + block += 0x4000; + + if(block > MAX_BUFF || + !(buff = reallocf(buff, block))) + RETURN(R_NOMEM); + + batch += fread(buff + batch, sizeof(r_byte), + 0x4000, fIn); + } + + ctx->stream.nextIn = buff; + ctx->stream.availIn += batch; + } + } + + /* call rlib */ + r = rlibRun(&(ctx->stream), &(ctx->script), feof(fIn)); + + // Oops! + if(r < 0) + RETURN(r); + + total += ctx->stream.total; + } + +finally: + if(buff) + free(buff); + + ctx->stream.total = total; + + return retv; +} + +int fileOutput(struct r_stream* stream, byte* data, size_t len) +{ + FILE* f = (FILE*)stream->arg; + return (fwrite(data, 1, len, f) == len) && (!ferror(f)); +} + +int repFiles(r_context* ctx, FILE* fIn, FILE* fOut) +{ + void* arg = ctx->stream.arg; + r_write func = ctx->stream.fWrite; + int ret; + + ctx->stream.fWrite = fileOutput; + ctx->stream.arg = fOut; + + ret = repFile(ctx, fIn); + + ctx->stream.fWrite = func; + ctx->stream.arg = arg; + + return ret; +} + +void repFree(r_context* ctx) +{ + rlibFree(&(ctx->stream), &(ctx->script)); +} |