/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Stack.c * * A stack for the script interpreter. */ #include #include #include "lib/framework/frame.h" #include "interp.h" #include "stack.h" #include "codeprint.h" #include "script.h" #include "script_parser.tab.h" /* number of values in each stack chunk */ #define INIT_SIZE 30 //15 #define EXT_SIZE 10 //2 char STRSTACK[MAXSTACKLEN][MAXSTRLEN]; //simple string 'stack' UDWORD CURSTACKSTR = 0; //Points to the top of the string stack /* store for a 'chunk' of the stack */ typedef struct _stack_chunk { INTERP_VAL *aVals; UDWORD size; struct _stack_chunk *psNext, *psPrev; } STACK_CHUNK; /* The first chunk of the stack */ static STACK_CHUNK *psStackBase=NULL; /* The current stack chunk */ static STACK_CHUNK *psCurrChunk=NULL; /* The current free entry on the current stack chunk */ static UDWORD currEntry=0; /* Get rid of the top value without returning it */ static inline BOOL stackRemoveTop(void); /* Check if the stack is empty */ BOOL stackEmpty(void) { return psCurrChunk == psStackBase && currEntry == 0; } /* Allocate a new chunk for the stack */ static BOOL stackNewChunk(UDWORD size) { /* see if a chunk has already been allocated */ if (psCurrChunk->psNext != NULL) { psCurrChunk = psCurrChunk->psNext; currEntry = 0; } else { /* Allocate a new chunk */ psCurrChunk->psNext = (STACK_CHUNK *)MALLOC(sizeof(STACK_CHUNK)); if (!psCurrChunk->psNext) { return FALSE; } psCurrChunk->psNext->aVals = (INTERP_VAL*)MALLOC(sizeof(INTERP_VAL) * size); if (!psCurrChunk->psNext->aVals) { FREE(psCurrChunk->psNext); return FALSE; } psCurrChunk->psNext->size = size; psCurrChunk->psNext->psPrev = psCurrChunk; psCurrChunk->psNext->psNext = NULL; psCurrChunk = psCurrChunk->psNext; currEntry = 0; /* initialize pointers note: 0 means type == VAL_BOOL */ memset(psCurrChunk->psNext->aVals, 0, sizeof(INTERP_VAL) * size); } return TRUE; } /* Push a value onto the stack */ BOOL stackPush(INTERP_VAL *psVal) { /* Store the value in the stack - psCurrChunk/currEntry always point to valid space */ /* String support: Copy the string, otherwise the stack will operate directly on the original string (like & opcode will actually store the result in the first variable which would point to the original string) */ if(psVal->type == VAL_STRING) //pushing string { /* strings should already have memory allocated */ if(psCurrChunk->aVals[currEntry].type != VAL_STRING) //needs to have memory allocated for string psCurrChunk->aVals[currEntry].v.sval = (char*)MALLOC(MAXSTRLEN); strcpy(psCurrChunk->aVals[currEntry].v.sval, psVal->v.sval); //copy string to stack psCurrChunk->aVals[currEntry].type = VAL_STRING; } else /* pushing non-string */ { /* free stack var allocated string memory, if stack var used to be of type VAL_STRING */ if(psCurrChunk->aVals[currEntry].type == VAL_STRING) FREE(psCurrChunk->aVals[currEntry].v.sval); //don't need it anymore /* copy type/data as union */ memcpy(&(psCurrChunk->aVals[currEntry]), psVal, sizeof(INTERP_VAL)); } /* Now update psCurrChunk and currEntry */ currEntry++; if (currEntry == psCurrChunk->size) { /* At the end of this chunk, need a new one */ if (!stackNewChunk(EXT_SIZE)) { /* Out of memory */ debug(LOG_ERROR, "stackPush: Out of memory"); return FALSE; } } return TRUE; } /* Pop a value off the stack */ BOOL stackPop(INTERP_VAL *psVal) { if ((psCurrChunk->psPrev == NULL) && (currEntry == 0)) { debug(LOG_ERROR, "stackPop: stack empty"); ASSERT( FALSE, "stackPop: stack empty" ); return FALSE; } /* move the stack pointer down one */ if (currEntry == 0) { /* have to move onto the previous chunk. */ psCurrChunk = psCurrChunk->psPrev; currEntry = psCurrChunk->size -1; } else { currEntry--; } /* copy the entire value off the stack */ memcpy(psVal, &(psCurrChunk->aVals[currEntry]), sizeof(INTERP_VAL)); return TRUE; } /* Return pointer to the top value without poping it */ BOOL stackPeekTop(INTERP_VAL **ppsVal) { if ((psCurrChunk->psPrev == NULL) && (currEntry == 0)) { debug(LOG_ERROR, "stackPeekTop: stack empty"); ASSERT( FALSE, "stackPeekTop: stack empty" ); return FALSE; } if (currEntry == 0) { STACK_CHUNK *pPrevChunck; /* have to move onto the previous chunk */ pPrevChunck = psCurrChunk->psPrev; /* Return top value */ *ppsVal = &(pPrevChunck->aVals[pPrevChunck->size - 1]); } else { /* Return top value of the current chunk */ *ppsVal = &(psCurrChunk->aVals[currEntry - 1]); } return TRUE; } /* Pop a value off the stack, checking that the type matches what is passed in */ BOOL stackPopType(INTERP_VAL *psVal) { INTERP_VAL *psTop; if ((psCurrChunk->psPrev == NULL) && (currEntry == 0)) { debug(LOG_ERROR, "stackPopType: stack empty"); ASSERT( FALSE, "stackPopType: stack empty" ); return FALSE; } /* move the stack pointer down one */ if (currEntry == 0) { /* have to move onto the previous chunk. */ psCurrChunk = psCurrChunk->psPrev; currEntry = psCurrChunk->size -1; } else { currEntry--; } psTop = psCurrChunk->aVals + currEntry; //String support if(psVal->type == VAL_STRING) //If we are about to assign something to a string variable (psVal) { if(psTop->type != VAL_STRING) //if assigning a non-string value to a string variable, then convert the value { /* Check for compatible types */ switch (psTop->type) { case VAL_INT: /* int->string */ sprintf(psVal->v.sval, "%d", psTop->v.ival); break; case VAL_BOOL: /* bool->string */ sprintf(psVal->v.sval, "%d", psTop->v.bval); break; case VAL_FLOAT: /* float->string */ sprintf(psVal->v.sval, "%f", psTop->v.fval); break; default: debug(LOG_ERROR, "stackPopType: trying to assign an incompatible data type to a string variable (type: %d)", psTop->type); return FALSE; break; } } else //assigning string value to a string variable - COPY string { strcpy(psVal->v.sval, psTop->v.sval); } } else // we are about to assign something to a non-string variable (psVal) { if (!interpCheckEquiv(psVal->type,psTop->type)) { debug(LOG_ERROR, "stackPopType: type mismatch: %d/%d", psVal->type, psTop->type); ASSERT( FALSE, "stackPopType: type mismatch: %d/%d", psVal->type, psTop->type); return FALSE; } /* copy the entire union off the stack */ memcpy(&(psVal->v), &(psTop->v), sizeof(psTop->v)); } return TRUE; } /* Pop a number of values off the stack checking their types * This is used by instinct functions to get their parameters */ BOOL stackPopParams(SDWORD numParams, ...) { va_list args; SDWORD i; INTERP_TYPE type; void *pData; INTERP_VAL *psVal; UDWORD index, params; STACK_CHUNK *psCurr; //debug(LOG_SCRIPT,"stackPopParams"); va_start(args, numParams); // Find the position of the first parameter, and set // the stack top to it if ((UDWORD)numParams <= currEntry) { // parameters are all on current chunk currEntry = currEntry - numParams; psCurr = psCurrChunk; } else { // Have to work down the previous chunks to find the first param params = numParams - currEntry; for(psCurr = psCurrChunk->psPrev; psCurr != NULL; psCurr = psCurr->psPrev) { if (params <= psCurr->size) { // found the first param currEntry = psCurr->size - params; psCurrChunk = psCurr; break; } else { params -= psCurr->size; } } } if (!psCurr) { debug(LOG_ERROR,"stackPopParams: not enough parameters on stack"); ASSERT( FALSE, "stackPopParams: not enough parameters on stack" ); return FALSE; } // Get the values, checking their types index = currEntry; for (i=0; i< numParams; i++) { type = (INTERP_TYPE)(va_arg(args, int)); pData = va_arg(args, void *); psVal = psCurr->aVals + index; if(type == VAL_FLOAT) //expecting a float { /* if (!interpCheckEquiv(type,psVal->type)) { ASSERT( FALSE, "stackPopParams: type mismatch (%d/%d)" , type, psVal->type); va_end(args); return FALSE; } */ *((float*)pData) = psVal->v.fval; } else if(type != VAL_STRING) //anything (including references) { if (!interpCheckEquiv(type,psVal->type)) { ASSERT( FALSE, "stackPopParams: type mismatch" ); va_end(args); return FALSE; } if (scriptTypeIsPointer(psVal->type)) { if(psVal->type >= VAL_REF) //if it's a reference { INTERP_VAL *refVal; refVal = (INTERP_VAL*)psVal->v.oval; //oval is a pointer to INTERP_VAL in this case /* doublecheck types */ ASSERT(interpCheckEquiv(type & ~VAL_REF, refVal->type), "stackPopParams: type mismatch for a reference: %d/%d", type & ~VAL_REF, refVal->type); //type of provided container and type of the INTERP_VAL pointed by psVal->v.oval *((void**)pData) = &(refVal->v.ival); /* get pointer to the union */ } else //some pointer type { *((void**)pData) = psVal->v.oval; } } else { *((SDWORD*)pData) = psVal->v.ival; } } else //TODO: allow only compatible types { if(psVal->type == VAL_STRING) //Passing a String { //*((char **)pData) = psVal->v.sval; strcpy(((char *)pData), psVal->v.sval); //COPY string } else //Integer or float { switch (psVal->type) { case VAL_INT: sprintf(((char *)pData), "%d", psVal->v.ival); break; case VAL_FLOAT: sprintf(((char *)pData), "%f", psVal->v.fval); break; case VAL_BOOL: sprintf(((char *)pData), "%d", psVal->v.bval); break; default: ASSERT(FALSE, "StackPopParam - wrong data type being converted to string (type: %d)", psVal->type); break; } //*((char **)pData) = psVal->v.sval; } } index += 1; if (index >= psCurr->size) { psCurr = psCurr->psNext; index = 0; } } va_end(args); return TRUE; } /* Push a value onto the stack without using a value structure NOTE: result->type is _not_ set yet - use 'type' instead */ BOOL stackPushResult(INTERP_TYPE type, INTERP_VAL *result) { /* assign type, wasn't done before */ result->type = type; /* String support: Copy the string, otherwise the stack will operate directly on the original string (like & opcode will actually store the result in the first variable which would point to the original string) */ if(result->type == VAL_STRING) //pushing string { /* strings should already have memory allocated */ if(psCurrChunk->aVals[currEntry].type != VAL_STRING) //needs to have memory allocated for string psCurrChunk->aVals[currEntry].v.sval = (char*)MALLOC(MAXSTRLEN); strcpy(psCurrChunk->aVals[currEntry].v.sval, result->v.sval); //copy string to stack psCurrChunk->aVals[currEntry].type = VAL_STRING; } else /* pushing non-string */ { /* free stack var allocated string memory, if stack var used to be of type VAL_STRING */ if(psCurrChunk->aVals[currEntry].type == VAL_STRING) FREE(psCurrChunk->aVals[currEntry].v.sval); //don't need it anymore /* copy type/data as union */ memcpy(&(psCurrChunk->aVals[currEntry]), result, sizeof(INTERP_VAL)); } // Now update psCurrChunk and currEntry currEntry++; if (currEntry == psCurrChunk->size) { // At the end of this chunk, need a new one if (!stackNewChunk(EXT_SIZE)) { // Out of memory return FALSE; } } return TRUE; } /* Look at a value on the stack without removing it. * index is how far down the stack to look. * Index 0 is the top entry on the stack. */ BOOL stackPeek(INTERP_VAL *psVal, UDWORD index) { STACK_CHUNK *psCurr; if (index < currEntry) { /* Looking at entry on current chunk */ memcpy(psVal, &(psCurrChunk->aVals[currEntry - index - 1]), sizeof(INTERP_VAL)); return TRUE; } else { /* Have to work down the previous chunks to find the entry */ index -= currEntry; for(psCurr = psCurrChunk->psPrev; psCurr != NULL; psCurr = psCurr->psPrev) { if (index < psCurr->size) { /* found the entry */ memcpy(psVal, &(psCurr->aVals[psCurr->size - 1 - index]), sizeof(INTERP_VAL)); return TRUE; } else { index -= psCurr->size; } } } /* If we got here the index is off the bottom of the stack */ ASSERT( FALSE, "stackPeek: index too large" ); return FALSE; } /* Print the top value on the stack */ void stackPrintTop(void) { INTERP_VAL sVal; if (stackPeek(&sVal, 0)) { cpPrintVal(&sVal); } else { debug( LOG_NEVER, "STACK EMPTY" ); } } /* Do binary operations on the top of the stack * This effectively pops two values and pushes the result */ BOOL stackBinaryOp(OPCODE opcode) { STACK_CHUNK *psChunk; INTERP_VAL *psV1, *psV2; // Get the parameters if (psCurrChunk->psPrev == NULL && currEntry < 2) { debug(LOG_ERROR, "stackBinaryOp: not enough entries on stack"); ASSERT( FALSE, "stackBinaryOp: not enough entries on stack" ); return FALSE; } if (currEntry > 1) { psV1 = psCurrChunk->aVals + currEntry - 2; psV2 = psCurrChunk->aVals + currEntry - 1; currEntry -= 1; } else if (currEntry == 1) { // One value is on the previous chunk psChunk = psCurrChunk->psPrev; psV1 = psChunk->aVals + psChunk->size - 1; psV2 = psCurrChunk->aVals + currEntry - 1; currEntry -= 1; } else { // both on the previous chunk, pop to the previous chunk psCurrChunk = psCurrChunk->psPrev; currEntry = psCurrChunk->size - 1; psV1 = psCurrChunk->aVals + psCurrChunk->size - 2; psV2 = psCurrChunk->aVals + psCurrChunk->size - 1; } if(opcode != OP_CONC) //string - Don't check if OP_CONC, since types can be mixed here { if (!interpCheckEquiv(psV1->type, psV2->type)) { debug(LOG_ERROR, "stackBinaryOp: type mismatch"); ASSERT( FALSE, "stackBinaryOp: type mismatch" ); return FALSE; } /* find out if the result will be a float. Both or neither arguments are floats - should be taken care of by bison*/ if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT) { ASSERT(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT, "Can't implicitly convert float->int (type1: %d, type2: %d)", psV1->type , psV2->type ); } } // do the operation switch (opcode) { case OP_ADD: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT) { psV1->type = VAL_FLOAT; psV1->v.fval = psV1->v.fval + psV2->v.fval; } else { psV1->v.ival = psV1->v.ival + psV2->v.ival; } break; case OP_SUB: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT) { psV1->type = VAL_FLOAT; psV1->v.fval = psV1->v.fval - psV2->v.fval; } else { psV1->v.ival = psV1->v.ival - psV2->v.ival; } break; case OP_MUL: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT) { psV1->type = VAL_FLOAT; psV1->v.fval = psV1->v.fval * psV2->v.fval; } else { psV1->v.ival = psV1->v.ival * psV2->v.ival; } break; case OP_DIV: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT) { if(psV2->v.fval != 0.0f) { psV1->type = VAL_FLOAT; psV1->v.fval = psV1->v.fval / psV2->v.fval; } else { ASSERT(FALSE, "stackBinaryOp: division by zero (float)"); } } else { if(psV2->v.ival != 0) { psV1->v.ival = psV1->v.ival / psV2->v.ival; } else { ASSERT(FALSE, "stackBinaryOp: division by zero (integer)"); } } break; case OP_AND: psV1->v.bval = psV1->v.bval && psV2->v.bval; break; case OP_OR: psV1->v.bval = psV1->v.bval || psV2->v.bval; break; case OP_EQUAL: if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){ psV1->v.bval = psV1->v.fval == psV2->v.fval; }else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){ psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) == 0); /* case-insensitive */ }else{ psV1->v.bval = psV1->v.ival == psV2->v.ival; } psV1->type = VAL_BOOL; break; case OP_NOTEQUAL: if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){ psV1->v.bval = psV1->v.fval != psV2->v.fval; }else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){ psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) != 0); /* case-insensitive */ }else{ psV1->v.bval = psV1->v.ival != psV2->v.ival; } psV1->type = VAL_BOOL; break; case OP_GREATEREQUAL: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){ psV1->v.bval = psV1->v.fval >= psV2->v.fval; }else{ psV1->v.bval = psV1->v.ival >= psV2->v.ival; } psV1->type = VAL_BOOL; break; case OP_LESSEQUAL: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){ psV1->v.bval = psV1->v.fval <= psV2->v.fval; }else{ psV1->v.bval = psV1->v.ival <= psV2->v.ival; } psV1->type = VAL_BOOL; break; case OP_GREATER: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){ psV1->v.bval = psV1->v.fval > psV2->v.fval; }else{ psV1->v.bval = psV1->v.ival > psV2->v.ival; } psV1->type = VAL_BOOL; break; case OP_LESS: if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){ psV1->v.bval = psV1->v.fval < psV2->v.fval; }else{ psV1->v.bval = psV1->v.ival < psV2->v.ival; } psV1->type = VAL_BOOL; break; case OP_CONC: //String concatenation { char tempstr1[MAXSTRLEN]; char tempstr2[MAXSTRLEN]; /* Check first value if it's compatible with Strings */ switch (psV1->type) { case VAL_INT: //first value isn't string, but can be converted to string sprintf(tempstr1,"%d",psV1->v.ival); //int->string psV1->type = VAL_STRING; //Mark as string psV1->v.sval = (char*)MALLOC(MAXSTRLEN); //allocate space for the string, since the result (string) of concatenation will be saved here break; case VAL_BOOL: sprintf(tempstr1,"%d",psV1->v.bval); //bool->string psV1->type = VAL_STRING; //Mark as string psV1->v.sval = (char*)MALLOC(MAXSTRLEN); //allocate space for the string, since the result (string) of concatenation will be saved here break; case VAL_FLOAT: sprintf(tempstr1,"%f",psV1->v.fval); //float->string psV1->type = VAL_STRING; //Mark as string psV1->v.sval = (char*)MALLOC(MAXSTRLEN); //allocate space for the string, since the result (string) of concatenation will be saved here break; case VAL_STRING: strcpy(tempstr1,psV1->v.sval); break; default: debug(LOG_ERROR, "stackBinaryOp: OP_CONC: first parameter is not compatible with Strings"); return FALSE; break; } /* Check second value if it's compatible with Strings */ switch (psV2->type) { case VAL_INT: sprintf(tempstr2,"%d",psV2->v.ival); //int->string break; case VAL_BOOL: sprintf(tempstr2,"%d",psV2->v.bval); //bool->string break; case VAL_FLOAT: sprintf(tempstr2,"%f",psV2->v.fval); //float->string break; case VAL_STRING: strcpy(tempstr2,psV2->v.sval); break; default: debug(LOG_ERROR, "stackBinaryOp: OP_CONC: first parameter is not compatible with Strings"); return FALSE; break; } strcat(tempstr1,tempstr2); strcpy(psV1->v.sval,tempstr1); //Assign } break; default: debug(LOG_ERROR, "stackBinaryOp: unknown opcode"); ASSERT( FALSE, "stackBinaryOp: unknown opcode" ); return FALSE; break; } return TRUE; } /* Perform a unary operation on the top of the stack * This effectively pops a value and pushes the result */ BOOL stackUnaryOp(OPCODE opcode) { STACK_CHUNK *psChunk; INTERP_VAL *psVal; // Get the value if (psCurrChunk->psPrev == NULL && currEntry == 0) { ASSERT( FALSE, "stackUnaryOp: not enough entries on stack" ); return FALSE; } if (currEntry > 0) { psVal = psCurrChunk->aVals + currEntry - 1; } else { // Value is on the previous chunk psChunk = psCurrChunk->psPrev; psVal = psChunk->aVals + psChunk->size - 1; } // Do the operation switch (opcode) { case OP_INC: switch (psVal->type) { case (VAL_REF | VAL_INT): psVal = (INTERP_VAL*)psVal->v.oval; //get variable ASSERT(psVal->type == VAL_INT, "Invalid type for increment opcode: %d", psVal->type); psVal->v.ival++; /* Just get rid of the variable pointer, since already increased it */ if (!stackRemoveTop()) { debug( LOG_ERROR, "stackUnaryOpcode: OP_INC: could not pop" ); return FALSE; } break; default: ASSERT( FALSE, "stackUnaryOp: invalid type for OP_INC (type: %d)", psVal->type ); return FALSE; break; } break; case OP_DEC: switch (psVal->type) { case (VAL_REF | VAL_INT): psVal = (INTERP_VAL*)psVal->v.oval; //get variable ASSERT(psVal->type == VAL_INT, "Invalid type for decrement opcode: %d", psVal->type); psVal->v.ival--; /* Just get rid of the variable pointer, since already decreased it */ if (!stackRemoveTop()) { debug( LOG_ERROR, "stackUnaryOpcode: OP_DEC: could not pop" ); return FALSE; } break; default: ASSERT( FALSE, "stackUnaryOp: invalid type for OP_DEC (type: %d)", psVal->type ); return FALSE; break; } break; case OP_NEG: switch (psVal->type) { case VAL_INT: psVal->v.ival = - psVal->v.ival; break; case VAL_FLOAT: psVal->v.fval = - psVal->v.fval; break; default: ASSERT( FALSE, "stackUnaryOp: invalid type for negation (type: %d)", psVal->type ); return FALSE; break; } break; case OP_NOT: switch (psVal->type) { case VAL_BOOL: psVal->v.bval = !psVal->v.bval; break; default: ASSERT( FALSE, "stackUnaryOp: invalid type for NOT (type: %d)", psVal->type ); return FALSE; break; } break; default: ASSERT( FALSE, "stackUnaryOp: unknown opcode (opcode: %d)", opcode ); return FALSE; break; } return TRUE; } BOOL castTop(INTERP_TYPE neededType) { INTERP_VAL *pTop=NULL; //debug(LOG_WZ, "casting to %d", neededType); ASSERT(neededType == VAL_INT || neededType == VAL_FLOAT, "stackCast: can't cast to %d", neededType); if (!stackPeekTop(&pTop) || pTop==NULL) { ASSERT(FALSE, "castTop: failed to peek stack"); return FALSE; } //debug(LOG_WZ, "castTop: stack type %d", pTop->type); /* see if we can cast this data type */ switch (pTop->type) { case VAL_BOOL: switch (neededType) { case VAL_FLOAT: /* casting from bool to float */ pTop->v.fval = (float)pTop->v.bval; pTop->type = VAL_FLOAT; return TRUE; break; default: debug(LOG_ERROR, "cast error %d->%d", pTop->type, neededType); break; } break; case VAL_INT: switch (neededType) { case VAL_FLOAT: /* casting from int to float */ pTop->v.fval = (float)pTop->v.ival; pTop->type = VAL_FLOAT; return TRUE; break; default: debug(LOG_ERROR, "cast error %d->%d", pTop->type, neededType); break; } break; case VAL_FLOAT: switch (neededType) { case VAL_INT: /* casting from float to int */ pTop->v.ival = (SDWORD)pTop->v.fval; pTop->type = VAL_INT; return TRUE; break; default: debug(LOG_ERROR, "cast error %d->%d", pTop->type, neededType); break; } break; default: debug(LOG_ERROR, "can't cast from %d to %d", pTop->type, neededType); break; } return FALSE; } /* Initialise the stack */ BOOL stackInitialise(void) { psStackBase = (STACK_CHUNK *)MALLOC(sizeof(STACK_CHUNK)); if (psStackBase == NULL) { debug( LOG_ERROR, "Out of memory" ); abort(); return FALSE; } psStackBase->aVals = (INTERP_VAL*)MALLOC(sizeof(INTERP_VAL) * INIT_SIZE); if (!psStackBase->aVals) { debug( LOG_ERROR, "Out of memory" ); abort(); return FALSE; } psStackBase->size = INIT_SIZE; psStackBase->psPrev = NULL; psStackBase->psNext = NULL; psCurrChunk = psStackBase; /* initialize pointers note: this means type == VAL_BOOL */ memset(psStackBase->aVals, 0, sizeof(INTERP_VAL) * INIT_SIZE); //string support CURSTACKSTR = 0; //initialize string 'stack' return TRUE; } /* Shutdown the stack */ void stackShutDown(void) { STACK_CHUNK *psCurr, *psNext; UDWORD i; if ((psCurrChunk != psStackBase) && (currEntry != 0)) { debug( LOG_NEVER, "stackShutDown: stack is not empty on shutdown" ); } for(psCurr = psStackBase; psCurr != NULL; psCurr = psNext) { psNext = psCurr->psNext; /* Free strings */ for(i=0; i< psCurr->size; i++) //go through all values on this chunk { if(psCurr->aVals[i].type == VAL_STRING) { if(psCurr->aVals[i].v.sval != NULL) //FIXME: seems to be causing problems sometimes { debug(LOG_WZ, "freeing '%s' ", psCurr->aVals[i].v.sval); FREE(psCurr->aVals[i].v.sval); psCurr->aVals[i].v.sval = NULL; } else { debug(LOG_SCRIPT, "stackShutDown: VAL_STRING with null pointer"); } } } FREE(psCurr->aVals); FREE(psCurr); } } /* Get rid of the top value without returning it */ static inline BOOL stackRemoveTop(void) { if ((psCurrChunk->psPrev == NULL) && (currEntry == 0)) { debug(LOG_ERROR, "stackRemoveTop: stack empty"); ASSERT( FALSE, "stackRemoveTop: stack empty" ); return FALSE; } /* move the stack pointer down one */ if (currEntry == 0) { /* have to move onto the previous chunk. */ psCurrChunk = psCurrChunk->psPrev; currEntry = psCurrChunk->size -1; } else { currEntry--; } return TRUE; } /* Reset the stack to an empty state */ void stackReset(void) { ASSERT( ((psCurrChunk == psStackBase) && (currEntry == 0)), "stackReset: stack is not empty" ); psCurrChunk = psStackBase; currEntry = 0; } /* Get the first entry on the stack * (i.e. the second value for a binary operator). */ /*BOOL stackGetTop(UDWORD *pType, UDWORD *pData) { STACK_CHUNK *psChunk; INTERP_VAL *psVal; if (psCurrChunk->psPrev == NULL && currEntry == 0) { ASSERT( FALSE, "stackGetTop: not enough entries on stack" ); return FALSE; } if (currEntry == 0) { // Value is on the previous chunk psChunk = psCurrChunk->psPrev; psVal = psChunk->aVals + psChunk->size - 1; } else { psVal = psCurrChunk->aVals + currEntry - 1; } // The data could be any type but this will copy it over *pType = (UDWORD)psVal->type; *pData = (UDWORD)psVal->v.ival; return TRUE; }*/ /* Replace the value at the top of the stack */ /*BOOL stackSetTop(UDWORD type, UDWORD data) { STACK_CHUNK *psChunk; INTERP_VAL *psVal; if (psCurrChunk->psPrev == NULL && currEntry == 0) { ASSERT( FALSE, "stackSetTop: not enough entries on stack" ); return FALSE; } if (currEntry == 0) { // Value is on the previous chunk psChunk = psCurrChunk->psPrev; psVal = psChunk->aVals + psChunk->size - 1; } else { psVal = psCurrChunk->aVals + currEntry - 1; } // The data could be any type but this will copy it over psVal->type = type; psVal->v.ival = (SDWORD)data; return TRUE; }*/ /* Get the second entry on the stack * (i.e. the first value for a binary operator). */ /*BOOL stackGetSecond(UDWORD *pType, UDWORD *pData) { STACK_CHUNK *psChunk; INTERP_VAL *psVal; if (psCurrChunk->psPrev == NULL && currEntry < 2) { ASSERT( FALSE, "stackGetSecond: not enough entries on stack" ); return FALSE; } if (currEntry < 2) { // Value is on the previous chunk psChunk = psCurrChunk->psPrev; psVal = psChunk->aVals + psChunk->size + currEntry - 2; } else { psVal = psCurrChunk->aVals + currEntry - 2; } // The data could be any type but this will copy it over *pType = (UDWORD)psVal->type; *pData = (UDWORD)psVal->v.ival; return TRUE; }*/ /* Get pointers to the two top values */ /*BOOL stackTopTwo(INTERP_VAL **ppsV1, INTERP_VAL **ppsV2) { STACK_CHUNK *psChunk; if (psCurrChunk->psPrev == NULL && currEntry < 2) { ASSERT( FALSE, "stackGetSecond: not enough entries on stack" ); return FALSE; } if (currEntry > 1) { *ppsV1 = psCurrChunk->aVals + currEntry - 2; *ppsV2 = psCurrChunk->aVals + currEntry - 1; } else if (currEntry == 1) { // Value is on the previous chunk, but pop doesn't change the chunk psChunk = psCurrChunk->psPrev; *ppsV1 = psChunk->aVals + psChunk->size - 1; *ppsV2 = psCurrChunk->aVals + currEntry - 1; } else { // both on the previous chunk psChunk = psCurrChunk->psPrev; *ppsV1 = psChunk->aVals + psChunk->size - 2; *ppsV2 = psChunk->aVals + psChunk->size - 1; } return TRUE; }*/ /* Pop the top value from the stack and replace the new top value * This is used to return the result of a binary maths operator */ /*BOOL stackPopAndSet(UDWORD type, UDWORD data) { STACK_CHUNK *psChunk; INTERP_VAL *psVal; if (psCurrChunk->psPrev == NULL && currEntry < 2) { ASSERT( FALSE, "stackGetSecond: not enough entries on stack" ); return FALSE; } if (currEntry > 1) { psVal = psCurrChunk->aVals + currEntry - 2; currEntry -= 1; } else if (currEntry == 1) { // Value is on the previous chunk, but pop doesn't change the chunk psChunk = psCurrChunk->psPrev; psVal = psChunk->aVals + psChunk->size - 1; currEntry -= 1; } else { // pop to the previous chunk psCurrChunk = psCurrChunk->psPrev; psVal = psCurrChunk->aVals + psCurrChunk->size - 2; currEntry = psCurrChunk->size - 1; } // The data could be any type but this will copy it over psVal->type = type; psVal->v.ival = (SDWORD)data; return TRUE; }*/