4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The implementation of the TH core. This file contains the parser, and 4ee9e31a2d 2008-02-13 drh: ** the implementation of the interface in th.h. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: #include "th.h" 4ee9e31a2d 2008-02-13 drh: #include <string.h> 4ee9e31a2d 2008-02-13 drh: #include <assert.h> 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: typedef struct Th_Command Th_Command; 4ee9e31a2d 2008-02-13 drh: typedef struct Th_Frame Th_Frame; 4ee9e31a2d 2008-02-13 drh: typedef struct Th_Variable Th_Variable; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Interpreter structure. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: struct Th_Interp { 4ee9e31a2d 2008-02-13 drh: Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ 0c99a1554a 2008-10-24 drh: char *zResult; /* Current interpreter result (Th_Malloc()ed) */ 4ee9e31a2d 2008-02-13 drh: int nResult; /* number of bytes in zResult */ 4ee9e31a2d 2008-02-13 drh: Th_Hash *paCmd; /* Table of registered commands */ 4ee9e31a2d 2008-02-13 drh: Th_Frame *pFrame; /* Current execution frame */ 4ee9e31a2d 2008-02-13 drh: int isListMode; /* True if thSplitList() should operate in "list" mode */ 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Each TH command registered using Th_CreateCommand() is represented 4ee9e31a2d 2008-02-13 drh: ** by an instance of the following structure stored in the Th_Interp.paCmd 4ee9e31a2d 2008-02-13 drh: ** hash-table. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: struct Th_Command { 0c99a1554a 2008-10-24 drh: int (*xProc)(Th_Interp *, void *, int, const char **, int *); 4ee9e31a2d 2008-02-13 drh: void *pContext; 4ee9e31a2d 2008-02-13 drh: void (*xDel)(Th_Interp *, void *); 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Each stack frame (variable scope) is represented by an instance 4ee9e31a2d 2008-02-13 drh: ** of this structure. Variable values set using the Th_SetVar command 4ee9e31a2d 2008-02-13 drh: ** are stored in the Th_Frame.paVar hash table member of the associated 4ee9e31a2d 2008-02-13 drh: ** stack frame object. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** When an interpreter is created, a single Th_Frame structure is also 4ee9e31a2d 2008-02-13 drh: ** allocated - the global variable scope. Th_Interp.pFrame (the current 4ee9e31a2d 2008-02-13 drh: ** interpreter frame) is initialised to point to this Th_Frame. It is 4ee9e31a2d 2008-02-13 drh: ** not deleted for the lifetime of the interpreter (because the global 4ee9e31a2d 2008-02-13 drh: ** frame never goes out of scope). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** New stack frames are created by the Th_InFrame() function. Before 4ee9e31a2d 2008-02-13 drh: ** invoking its callback function, Th_InFrame() allocates a new Th_Frame 4ee9e31a2d 2008-02-13 drh: ** structure with pCaller set to the current frame (Th_Interp.pFrame), 4ee9e31a2d 2008-02-13 drh: ** and sets the current frame to the new frame object. After the callback 4ee9e31a2d 2008-02-13 drh: ** has been invoked, the allocated Th_Frame is deleted and the value 4ee9e31a2d 2008-02-13 drh: ** of the current frame pointer restored. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions 4ee9e31a2d 2008-02-13 drh: ** access variable values in the current frame. If they need to access 4ee9e31a2d 2008-02-13 drh: ** the global frame, they do so by traversing the pCaller pointer list. 4ee9e31a2d 2008-02-13 drh: ** Likewise, the Th_LinkVar() function uses the pCaller pointers to 4ee9e31a2d 2008-02-13 drh: ** link to variables located in the global or other stack frames. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: struct Th_Frame { 4ee9e31a2d 2008-02-13 drh: Th_Hash *paVar; /* Variables defined in this scope */ 4ee9e31a2d 2008-02-13 drh: Th_Frame *pCaller; /* Calling frame */ 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** This structure represents a value assigned to a th1 variable. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** The Th_Frame.paVar hash table maps from variable name (a th1 string) 4ee9e31a2d 2008-02-13 drh: ** to a pointer to an instance of the following structure. More than 4ee9e31a2d 2008-02-13 drh: ** one hash table entry may map to a single structure if variable 4ee9e31a2d 2008-02-13 drh: ** links have been created using Th_LinkVar(). The number of references 4ee9e31a2d 2008-02-13 drh: ** is stored in Th_Variable.nRef. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** For scalar variables, Th_Variable.zData is never 0. Th_Variable.nData 4ee9e31a2d 2008-02-13 drh: ** stores the number of bytes in the value pointed to by zData. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** For an array variable, Th_Variable.zData is 0 and pHash points to 4ee9e31a2d 2008-02-13 drh: ** a hash table mapping between array key name (a th1 string) and 4ee9e31a2d 2008-02-13 drh: ** a the pointer to the Th_Variable structure holding the scalar 4ee9e31a2d 2008-02-13 drh: ** value. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: struct Th_Variable { 4ee9e31a2d 2008-02-13 drh: int nRef; /* Number of references to this structure */ 4ee9e31a2d 2008-02-13 drh: int nData; /* Number of bytes at Th_Variable.zData */ 0c99a1554a 2008-10-24 drh: char *zData; /* Data for scalar variables */ 4ee9e31a2d 2008-02-13 drh: Th_Hash *pHash; /* Data for array variables */ 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Hash table API: 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: #define TH_HASHSIZE 257 4ee9e31a2d 2008-02-13 drh: struct Th_Hash { 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *a[TH_HASHSIZE]; 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 0c99a1554a 2008-10-24 drh: static int thEvalLocal(Th_Interp *, const char *, int); 0c99a1554a 2008-10-24 drh: static int thSplitList(Th_Interp*, const char*, int, char***, int **, int*); 0c99a1554a 2008-10-24 drh: 0c99a1554a 2008-10-24 drh: static int thHexdigit(char c); 0c99a1554a 2008-10-24 drh: static int thEndOfLine(const char *, int); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: static int thPushFrame(Th_Interp*, Th_Frame*); 4ee9e31a2d 2008-02-13 drh: static void thPopFrame(Th_Interp*); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: static void thFreeVariable(Th_HashEntry*, void*); 4ee9e31a2d 2008-02-13 drh: static void thFreeCommand(Th_HashEntry*, void*); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The following are used by both the expression and language parsers. 4ee9e31a2d 2008-02-13 drh: ** Given that the start of the input string (z, n) is a language 4ee9e31a2d 2008-02-13 drh: ** construct of the relevant type (a command enclosed in [], an escape 4ee9e31a2d 2008-02-13 drh: ** sequence etc.), these functions determine the number of bytes 4ee9e31a2d 2008-02-13 drh: ** of the input consumed by the construct. For example: 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** int nByte; 4ee9e31a2d 2008-02-13 drh: ** thNextCommand(interp, "[expr $a+1] $nIter", 18, &nByte); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** results in variable nByte being set to 11. Or, 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** thNextVarname(interp, "$a+1", 4, &nByte); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** results in nByte being set to 2. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thNextCommand(Th_Interp*, const char *z, int n, int *pN); 0c99a1554a 2008-10-24 drh: static int thNextEscape (Th_Interp*, const char *z, int n, int *pN); 0c99a1554a 2008-10-24 drh: static int thNextVarname(Th_Interp*, const char *z, int n, int *pN); 0c99a1554a 2008-10-24 drh: static int thNextNumber (Th_Interp*, const char *z, int n, int *pN); 0c99a1554a 2008-10-24 drh: static int thNextSpace (Th_Interp*, const char *z, int n, int *pN); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Given that the input string (z, n) contains a language construct of 4ee9e31a2d 2008-02-13 drh: ** the relevant type (a command enclosed in [], an escape sequence 4ee9e31a2d 2008-02-13 drh: ** like "\xFF" or a variable reference like "${varname}", perform 4ee9e31a2d 2008-02-13 drh: ** substitution on the string and store the resulting string in 4ee9e31a2d 2008-02-13 drh: ** the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thSubstCommand(Th_Interp*, const char *z, int n); 0c99a1554a 2008-10-24 drh: static int thSubstEscape (Th_Interp*, const char *z, int n); 0c99a1554a 2008-10-24 drh: static int thSubstVarname(Th_Interp*, const char *z, int n); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Given that there is a th1 word located at the start of the input 4ee9e31a2d 2008-02-13 drh: ** string (z, n), determine the length in bytes of that word. If the 4ee9e31a2d 2008-02-13 drh: ** isCmd argument is non-zero, then an unescaped ";" byte not 4ee9e31a2d 2008-02-13 drh: ** located inside of a block or quoted string is considered to mark 4ee9e31a2d 2008-02-13 drh: ** the end of the word. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thNextWord(Th_Interp*, const char *z, int n, int *pN, int isCmd); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Perform substitution on the word contained in the input string (z, n). 4ee9e31a2d 2008-02-13 drh: ** Store the resulting string in the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thSubstWord(Th_Interp*, const char *z, int n); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The Buffer structure and the thBufferXXX() functions are used to make 4ee9e31a2d 2008-02-13 drh: ** memory allocation easier when building up a result. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: struct Buffer { 0c99a1554a 2008-10-24 drh: char *zBuf; 4ee9e31a2d 2008-02-13 drh: int nBuf; 4ee9e31a2d 2008-02-13 drh: int nBufAlloc; 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: typedef struct Buffer Buffer; 0c99a1554a 2008-10-24 drh: static int thBufferWrite(Th_Interp *interp, Buffer *, const char *, int); 4ee9e31a2d 2008-02-13 drh: static void thBufferInit(Buffer *); 4ee9e31a2d 2008-02-13 drh: static void thBufferFree(Th_Interp *interp, Buffer *); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Append nAdd bytes of content copied from zAdd to the end of buffer 4ee9e31a2d 2008-02-13 drh: ** pBuffer. If there is not enough space currently allocated, resize 4ee9e31a2d 2008-02-13 drh: ** the allocation to make space. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thBufferWrite( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 4ee9e31a2d 2008-02-13 drh: Buffer *pBuffer, 0c99a1554a 2008-10-24 drh: const char *zAdd, 4ee9e31a2d 2008-02-13 drh: int nAdd 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int nReq; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nAdd<0 ){ 4ee9e31a2d 2008-02-13 drh: nAdd = th_strlen(zAdd); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: nReq = pBuffer->nBuf+nAdd+1; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nReq>pBuffer->nBufAlloc ){ 0c99a1554a 2008-10-24 drh: char *zNew; 4ee9e31a2d 2008-02-13 drh: int nNew; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: nNew = nReq*2; 0c99a1554a 2008-10-24 drh: zNew = (char *)Th_Malloc(interp, nNew); 4ee9e31a2d 2008-02-13 drh: memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pBuffer->zBuf); 4ee9e31a2d 2008-02-13 drh: pBuffer->nBufAlloc = nNew; 4ee9e31a2d 2008-02-13 drh: pBuffer->zBuf = zNew; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); 4ee9e31a2d 2008-02-13 drh: pBuffer->nBuf += nAdd; 4ee9e31a2d 2008-02-13 drh: pBuffer->zBuf[pBuffer->nBuf] = '\0'; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 0c99a1554a 2008-10-24 drh: #define thBufferWrite(a,b,c,d) thBufferWrite(a,b,(const char *)c,d) 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Initialize the Buffer structure pointed to by pBuffer. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void thBufferInit(Buffer *pBuffer){ 4ee9e31a2d 2008-02-13 drh: memset(pBuffer, 0, sizeof(Buffer)); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Zero the buffer pointed to by pBuffer and free the associated memory 4ee9e31a2d 2008-02-13 drh: ** allocation. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void thBufferFree(Th_Interp *interp, Buffer *pBuffer){ 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pBuffer->zBuf); 4ee9e31a2d 2008-02-13 drh: thBufferInit(pBuffer); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Assuming parameter c contains a hexadecimal digit character, 4ee9e31a2d 2008-02-13 drh: ** return the corresponding value of that digit. If c is not 4ee9e31a2d 2008-02-13 drh: ** a hexadecimal digit character, -1 is returned. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thHexdigit(char c){ 4ee9e31a2d 2008-02-13 drh: switch (c) { 4ee9e31a2d 2008-02-13 drh: case '0': return 0; 4ee9e31a2d 2008-02-13 drh: case '1': return 1; 4ee9e31a2d 2008-02-13 drh: case '2': return 2; 4ee9e31a2d 2008-02-13 drh: case '3': return 3; 4ee9e31a2d 2008-02-13 drh: case '4': return 4; 4ee9e31a2d 2008-02-13 drh: case '5': return 5; 4ee9e31a2d 2008-02-13 drh: case '6': return 6; 4ee9e31a2d 2008-02-13 drh: case '7': return 7; 4ee9e31a2d 2008-02-13 drh: case '8': return 8; 4ee9e31a2d 2008-02-13 drh: case '9': return 9; 4ee9e31a2d 2008-02-13 drh: case 'a': case 'A': return 10; 4ee9e31a2d 2008-02-13 drh: case 'b': case 'B': return 11; 4ee9e31a2d 2008-02-13 drh: case 'c': case 'C': return 12; 4ee9e31a2d 2008-02-13 drh: case 'd': case 'D': return 13; 4ee9e31a2d 2008-02-13 drh: case 'e': case 'E': return 14; 4ee9e31a2d 2008-02-13 drh: case 'f': case 'F': return 15; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return -1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Argument pEntry points to an entry in a stack frame hash table 4ee9e31a2d 2008-02-13 drh: ** (Th_Frame.paVar). Decrement the refrerence count of the Th_Variable 4ee9e31a2d 2008-02-13 drh: ** structure that the entry points to. Free the Th_Variable if its 4ee9e31a2d 2008-02-13 drh: ** reference count reaches 0. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Argument pContext is a pointer to the interpreter structure. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ 4ee9e31a2d 2008-02-13 drh: Th_Variable *pValue = (Th_Variable *)pEntry->pData; 4ee9e31a2d 2008-02-13 drh: pValue->nRef--; 4ee9e31a2d 2008-02-13 drh: assert( pValue->nRef>=0 ); 4ee9e31a2d 2008-02-13 drh: if( pValue->nRef==0 ){ 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp = (Th_Interp *)pContext; 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pValue->zData); 4ee9e31a2d 2008-02-13 drh: if( pValue->pHash ){ 4ee9e31a2d 2008-02-13 drh: Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); 4ee9e31a2d 2008-02-13 drh: Th_HashDelete(interp, pValue->pHash); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pValue); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Argument pEntry points to an entry in the command hash table 4ee9e31a2d 2008-02-13 drh: ** (Th_Interp.paCmd). Delete the Th_Command structure that the 4ee9e31a2d 2008-02-13 drh: ** entry points to. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Argument pContext is a pointer to the interpreter structure. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ 4ee9e31a2d 2008-02-13 drh: Th_Command *pCommand = (Th_Command *)pEntry->pData; 4ee9e31a2d 2008-02-13 drh: if( pCommand->xDel ){ 4ee9e31a2d 2008-02-13 drh: pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: Th_Free((Th_Interp *)pContext, pEntry->pData); 4ee9e31a2d 2008-02-13 drh: pEntry->pData = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Push a new frame onto the stack. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ 4ee9e31a2d 2008-02-13 drh: pFrame->paVar = Th_HashNew(interp); 4ee9e31a2d 2008-02-13 drh: pFrame->pCaller = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: interp->pFrame = pFrame; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Pop a frame off the top of the stack. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void thPopFrame(Th_Interp *interp){ 4ee9e31a2d 2008-02-13 drh: Th_Frame *pFrame = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: Th_HashIterate(interp, pFrame->paVar, thFreeVariable, (void *)interp); 4ee9e31a2d 2008-02-13 drh: Th_HashDelete(interp, pFrame->paVar); 4ee9e31a2d 2008-02-13 drh: interp->pFrame = pFrame->pCaller; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The first part of the string (zInput,nInput) contains an escape 4ee9e31a2d 2008-02-13 drh: ** sequence. Set *pnEscape to the number of bytes in the escape sequence. 4ee9e31a2d 2008-02-13 drh: ** If there is a parse error, return TH_ERROR and set the interpreter 4ee9e31a2d 2008-02-13 drh: ** result to an error message. Otherwise return TH_OK. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thNextEscape( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zInput, 4ee9e31a2d 2008-02-13 drh: int nInput, 4ee9e31a2d 2008-02-13 drh: int *pnEscape 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int i = 2; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(nInput>0); 4ee9e31a2d 2008-02-13 drh: assert(zInput[0]=='\\'); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nInput<=1 ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: switch( zInput[1] ){ 4ee9e31a2d 2008-02-13 drh: case 'x': i = 4; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( i>nInput ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: *pnEscape = i; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The first part of the string (zInput,nInput) contains a variable 4ee9e31a2d 2008-02-13 drh: ** reference. Set *pnVarname to the number of bytes in the variable 4ee9e31a2d 2008-02-13 drh: ** reference. If there is a parse error, return TH_ERROR and set the 4ee9e31a2d 2008-02-13 drh: ** interpreter result to an error message. Otherwise return TH_OK. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int thNextVarname( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zInput, 4ee9e31a2d 2008-02-13 drh: int nInput, 4ee9e31a2d 2008-02-13 drh: int *pnVarname 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(nInput>0); 4ee9e31a2d 2008-02-13 drh: assert(zInput[0]=='$'); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nInput>0 && zInput[1]=='{' ){ 4ee9e31a2d 2008-02-13 drh: for(i=2; i<nInput && zInput[i]!='}'; i++); 4ee9e31a2d 2008-02-13 drh: if( i==nInput ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: i++; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: i = 1; 4ee9e31a2d 2008-02-13 drh: if( nInput>2 && zInput[1]==':' && zInput[2]==':' ){ 4ee9e31a2d 2008-02-13 drh: i += 2; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: for(; i<nInput; i++){ 4ee9e31a2d 2008-02-13 drh: if( zInput[i]=='(' ){ 4ee9e31a2d 2008-02-13 drh: for(i++; i<nInput; i++){ 4ee9e31a2d 2008-02-13 drh: if( zInput[i]==')' ) break; 4ee9e31a2d 2008-02-13 drh: if( zInput[i]=='\\' ) i++; 4ee9e31a2d 2008-02-13 drh: if( zInput[i]=='{' || zInput[i]=='[' || zInput[i]=='"' ){ 4ee9e31a2d 2008-02-13 drh: int nWord; 4ee9e31a2d 2008-02-13 drh: int rc = thNextWord(interp, &zInput[i], nInput-i, &nWord, 0); 4ee9e31a2d 2008-02-13 drh: if( rc!=TH_OK ){ 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: i += nWord; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( i>=nInput ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "Unmatched brackets:", zInput, nInput); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: i++; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( !th_isalnum(zInput[i]) && zInput[i]!='_' ) break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *pnVarname = i; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The first part of the string (zInput,nInput) contains a command 4ee9e31a2d 2008-02-13 drh: ** enclosed in a "[]" block. Set *pnCommand to the number of bytes in 4ee9e31a2d 2008-02-13 drh: ** the variable reference. If there is a parse error, return TH_ERROR 4ee9e31a2d 2008-02-13 drh: ** and set the interpreter result to an error message. Otherwise return 4ee9e31a2d 2008-02-13 drh: ** TH_OK. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int thNextCommand( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zInput, 4ee9e31a2d 2008-02-13 drh: int nInput, 4ee9e31a2d 2008-02-13 drh: int *pnCommand 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int nBrace = 0; 4ee9e31a2d 2008-02-13 drh: int nSquare = 0; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(nInput>0); 4ee9e31a2d 2008-02-13 drh: assert( zInput[0]=='[' || zInput[0]=='{' ); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nInput && (i==0 || nBrace>0 || nSquare>0); i++){ 4ee9e31a2d 2008-02-13 drh: switch( zInput[i] ){ 4ee9e31a2d 2008-02-13 drh: case '\\': i++; break; 4ee9e31a2d 2008-02-13 drh: case '{': nBrace++; break; 4ee9e31a2d 2008-02-13 drh: case '}': nBrace--; break; 4ee9e31a2d 2008-02-13 drh: case '[': nSquare++; break; 4ee9e31a2d 2008-02-13 drh: case ']': nSquare--; break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( nBrace || nSquare ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *pnCommand = i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Set *pnSpace to the number of whitespace bytes at the start of 4ee9e31a2d 2008-02-13 drh: ** input string (zInput, nInput). Always return TH_OK. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int thNextSpace( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zInput, 4ee9e31a2d 2008-02-13 drh: int nInput, 4ee9e31a2d 2008-02-13 drh: int *pnSpace 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nInput && th_isspace(zInput[i]); i++); 4ee9e31a2d 2008-02-13 drh: *pnSpace = i; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The first byte of the string (zInput,nInput) is not white-space. 4ee9e31a2d 2008-02-13 drh: ** Set *pnWord to the number of bytes in the th1 word that starts 4ee9e31a2d 2008-02-13 drh: ** with this byte. If a complete word cannot be parsed or some other 4ee9e31a2d 2008-02-13 drh: ** error occurs, return TH_ERROR and set the interpreter result to 4ee9e31a2d 2008-02-13 drh: ** an error message. Otherwise return TH_OK. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the isCmd argument is non-zero, then an unescaped ";" byte not 4ee9e31a2d 2008-02-13 drh: ** located inside of a block or quoted string is considered to mark 4ee9e31a2d 2008-02-13 drh: ** the end of the word. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thNextWord( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zInput, 4ee9e31a2d 2008-02-13 drh: int nInput, 4ee9e31a2d 2008-02-13 drh: int *pnWord, 4ee9e31a2d 2008-02-13 drh: int isCmd 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int iEnd = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert( !th_isspace(zInput[0]) ); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( zInput[0]=='"' ){ 4ee9e31a2d 2008-02-13 drh: /* The word is terminated by the next unescaped '"' character. */ 4ee9e31a2d 2008-02-13 drh: iEnd++; 4ee9e31a2d 2008-02-13 drh: while( iEnd<nInput && zInput[iEnd]!='"' ){ 4ee9e31a2d 2008-02-13 drh: if( zInput[iEnd]=='\\' ){ 4ee9e31a2d 2008-02-13 drh: iEnd++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iEnd++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iEnd++; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: int nBrace = 0; 4ee9e31a2d 2008-02-13 drh: int nSq = 0; 4ee9e31a2d 2008-02-13 drh: while( iEnd<nInput && (nBrace>0 || nSq>0 || 4ee9e31a2d 2008-02-13 drh: (!th_isspace(zInput[iEnd]) && (!isCmd || zInput[iEnd]!=';')) 4ee9e31a2d 2008-02-13 drh: )){ 4ee9e31a2d 2008-02-13 drh: switch( zInput[iEnd] ){ 4ee9e31a2d 2008-02-13 drh: case '\\': iEnd++; break; 4ee9e31a2d 2008-02-13 drh: case '{': if( nSq==0 ) nBrace++; break; 4ee9e31a2d 2008-02-13 drh: case '}': if( nSq==0 ) nBrace--; break; 4ee9e31a2d 2008-02-13 drh: case '[': if( nBrace==0 ) nSq++; break; 4ee9e31a2d 2008-02-13 drh: case ']': if( nBrace==0 ) nSq--; break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iEnd++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( nBrace>0 || nSq>0 ){ 4ee9e31a2d 2008-02-13 drh: /* Parse error */ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( iEnd>nInput ){ 4ee9e31a2d 2008-02-13 drh: /* Parse error */ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: *pnWord = iEnd; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The input string (zWord, nWord) contains a th1 script enclosed in 4ee9e31a2d 2008-02-13 drh: ** a [] block. Perform substitution on the input string and store the 4ee9e31a2d 2008-02-13 drh: ** resulting string in the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thSubstCommand( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zWord, 4ee9e31a2d 2008-02-13 drh: int nWord 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: assert(nWord>=2); 4ee9e31a2d 2008-02-13 drh: assert(zWord[0]=='[' && zWord[nWord-1]==']'); 4ee9e31a2d 2008-02-13 drh: return thEvalLocal(interp, &zWord[1], nWord-2); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The input string (zWord, nWord) contains a th1 variable reference 4ee9e31a2d 2008-02-13 drh: ** (a '$' byte followed by a variable name). Perform substitution on 4ee9e31a2d 2008-02-13 drh: ** the input string and store the resulting string in the interpreter 4ee9e31a2d 2008-02-13 drh: ** result. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thSubstVarname( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zWord, 4ee9e31a2d 2008-02-13 drh: int nWord 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: assert(nWord>=1); 4ee9e31a2d 2008-02-13 drh: assert(zWord[0]=='$'); 4ee9e31a2d 2008-02-13 drh: assert(nWord==1 || zWord[1]!='{' || zWord[nWord-1]=='}'); 4ee9e31a2d 2008-02-13 drh: if( nWord>1 && zWord[1]=='{' ){ 4ee9e31a2d 2008-02-13 drh: zWord++; 4ee9e31a2d 2008-02-13 drh: nWord -= 2; 4ee9e31a2d 2008-02-13 drh: }else if( zWord[nWord-1]==')' ){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: for(i=1; i<nWord && zWord[i]!='('; i++); 4ee9e31a2d 2008-02-13 drh: if( i<nWord ){ 4ee9e31a2d 2008-02-13 drh: Buffer varname; 4ee9e31a2d 2008-02-13 drh: int nInner; 0c99a1554a 2008-10-24 drh: const char *zInner; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: int rc = thSubstWord(interp, &zWord[i+1], nWord-i-2); 4ee9e31a2d 2008-02-13 drh: if( rc!=TH_OK ) return rc; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: zInner = Th_GetResult(interp, &nInner); 4ee9e31a2d 2008-02-13 drh: thBufferInit(&varname); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &varname, &zWord[1], i); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &varname, zInner, nInner); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &varname, ")", 1); 4ee9e31a2d 2008-02-13 drh: rc = Th_GetVar(interp, varname.zBuf, varname.nBuf); 4ee9e31a2d 2008-02-13 drh: thBufferFree(interp, &varname); 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return Th_GetVar(interp, &zWord[1], nWord-1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The input string (zWord, nWord) contains a th1 escape sequence. 4ee9e31a2d 2008-02-13 drh: ** Perform substitution on the input string and store the resulting 4ee9e31a2d 2008-02-13 drh: ** string in the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thSubstEscape( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zWord, 4ee9e31a2d 2008-02-13 drh: int nWord 4ee9e31a2d 2008-02-13 drh: ){ 0c99a1554a 2008-10-24 drh: char c; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(nWord>=2); 4ee9e31a2d 2008-02-13 drh: assert(zWord[0]=='\\'); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: switch( zWord[1] ){ 4ee9e31a2d 2008-02-13 drh: case 'x': { 4ee9e31a2d 2008-02-13 drh: assert(nWord==4); 4ee9e31a2d 2008-02-13 drh: c = ((thHexdigit(zWord[2])<<4) + thHexdigit(zWord[3])); 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: case 'n': { 4ee9e31a2d 2008-02-13 drh: c = '\n'; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: default: { 4ee9e31a2d 2008-02-13 drh: assert(nWord==2); 4ee9e31a2d 2008-02-13 drh: c = zWord[1]; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_SetResult(interp, &c, 1); 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The input string (zWord, nWord) contains a th1 word. Perform 4ee9e31a2d 2008-02-13 drh: ** substitution on the input string and store the resulting 4ee9e31a2d 2008-02-13 drh: ** string in the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thSubstWord( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zWord, 4ee9e31a2d 2008-02-13 drh: int nWord 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int rc = TH_OK; 4ee9e31a2d 2008-02-13 drh: Buffer output; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: thBufferInit(&output); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nWord>1 && (zWord[0]=='{' && zWord[nWord-1]=='}') ){ 4ee9e31a2d 2008-02-13 drh: rc = thBufferWrite(interp, &output, &zWord[1], nWord-2); 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* If the word is surrounded by double-quotes strip these away. */ 4ee9e31a2d 2008-02-13 drh: if( nWord>1 && (zWord[0]=='"' && zWord[nWord-1]=='"') ){ 4ee9e31a2d 2008-02-13 drh: zWord++; 4ee9e31a2d 2008-02-13 drh: nWord -= 2; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(i=0; rc==TH_OK && i<nWord; i++){ 4ee9e31a2d 2008-02-13 drh: int nGet; 4ee9e31a2d 2008-02-13 drh: 0c99a1554a 2008-10-24 drh: int (*xGet)(Th_Interp *, const char*, int, int *) = 0; 0c99a1554a 2008-10-24 drh: int (*xSubst)(Th_Interp *, const char*, int) = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: switch( zWord[i] ){ 4ee9e31a2d 2008-02-13 drh: case '\\': 4ee9e31a2d 2008-02-13 drh: xGet = thNextEscape; xSubst = thSubstEscape; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: case '[': 4ee9e31a2d 2008-02-13 drh: if( !interp->isListMode ){ 4ee9e31a2d 2008-02-13 drh: xGet = thNextCommand; xSubst = thSubstCommand; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: case '$': 4ee9e31a2d 2008-02-13 drh: if( !interp->isListMode ){ 4ee9e31a2d 2008-02-13 drh: xGet = thNextVarname; xSubst = thSubstVarname; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: default: { 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &output, &zWord[i], 1); 4ee9e31a2d 2008-02-13 drh: continue; /* Go to the next iteration of the for(...) loop */ 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: rc = xGet(interp, &zWord[i], nWord-i, &nGet); 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: rc = xSubst(interp, &zWord[i], nGet); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 0c99a1554a 2008-10-24 drh: const char *zRes; 4ee9e31a2d 2008-02-13 drh: int nRes; 4ee9e31a2d 2008-02-13 drh: zRes = Th_GetResult(interp, &nRes); 4ee9e31a2d 2008-02-13 drh: rc = thBufferWrite(interp, &output, zRes, nRes); 4ee9e31a2d 2008-02-13 drh: i += (nGet-1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: Th_SetResult(interp, output.zBuf, output.nBuf); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: thBufferFree(interp, &output); 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Return true if one of the following is true of the buffer pointed 4ee9e31a2d 2008-02-13 drh: ** to by zInput, length nInput: 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** + It is empty, or 4ee9e31a2d 2008-02-13 drh: ** + It contains nothing but white-space, or 4ee9e31a2d 2008-02-13 drh: ** + It contains no non-white-space characters before the first 4ee9e31a2d 2008-02-13 drh: ** newline character. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Otherwise return false. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thEndOfLine(const char *zInput, int nInput){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nInput && zInput[i]!='\n' && th_isspace(zInput[i]); i++); 4ee9e31a2d 2008-02-13 drh: return ((i==nInput || zInput[i]=='\n')?1:0); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** This function splits the supplied th1 list (contained in buffer zList, 4ee9e31a2d 2008-02-13 drh: ** size nList) into elements and performs word-substitution on each 4ee9e31a2d 2008-02-13 drh: ** element. If the Th_Interp.isListMode variable is true, then only 4ee9e31a2d 2008-02-13 drh: ** escape sequences are substituted (used by the Th_SplitList() function). 4ee9e31a2d 2008-02-13 drh: ** If Th_Interp.isListMode is false, then variable and command substitution 4ee9e31a2d 2008-02-13 drh: ** is also performed (used by Th_Eval()). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If zList/nList does not contain a valid list, TH_ERROR is returned 4ee9e31a2d 2008-02-13 drh: ** and an error message stored in interp. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If TH_OK is returned and pazElem is not NULL, the caller should free the 4ee9e31a2d 2008-02-13 drh: ** pointer written to (*pazElem) using Th_Free(). This releases memory 4ee9e31a2d 2008-02-13 drh: ** allocated for both the (*pazElem) and (*panElem) arrays. Example: 4ee9e31a2d 2008-02-13 drh: ** 0c99a1554a 2008-10-24 drh: ** char **argv; 4ee9e31a2d 2008-02-13 drh: ** int *argl; 4ee9e31a2d 2008-02-13 drh: ** int argc; 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** // After this call, argv and argl point to valid arrays. The 4ee9e31a2d 2008-02-13 drh: ** // number of elements in each is argc. 4ee9e31a2d 2008-02-13 drh: ** // 4ee9e31a2d 2008-02-13 drh: ** Th_SplitList(interp, zList, nList, &argv, &argl, &argc); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** // Free all memory allocated by Th_SplitList(). The arrays pointed 4ee9e31a2d 2008-02-13 drh: ** // to by argv and argl are invalidated by this call. 4ee9e31a2d 2008-02-13 drh: ** // 4ee9e31a2d 2008-02-13 drh: ** Th_Free(interp, argv); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thSplitList( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, /* Interpreter context */ 0c99a1554a 2008-10-24 drh: const char *zList, /* Pointer to buffer containing input list */ 4ee9e31a2d 2008-02-13 drh: int nList, /* Size of buffer pointed to by zList */ 0c99a1554a 2008-10-24 drh: char ***pazElem, /* OUT: Array of list elements */ 4ee9e31a2d 2008-02-13 drh: int **panElem, /* OUT: Lengths of each list element */ 4ee9e31a2d 2008-02-13 drh: int *pnCount /* OUT: Number of list elements */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int rc = TH_OK; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Buffer strbuf; 4ee9e31a2d 2008-02-13 drh: Buffer lenbuf; 4ee9e31a2d 2008-02-13 drh: int nCount = 0; 4ee9e31a2d 2008-02-13 drh: 0c99a1554a 2008-10-24 drh: const char *zInput = zList; 4ee9e31a2d 2008-02-13 drh: int nInput = nList; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: thBufferInit(&strbuf); 4ee9e31a2d 2008-02-13 drh: thBufferInit(&lenbuf); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: while( nInput>0 ){ 0c99a1554a 2008-10-24 drh: const char *zWord; 4ee9e31a2d 2008-02-13 drh: int nWord; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: thNextSpace(interp, zInput, nInput, &nWord); 4ee9e31a2d 2008-02-13 drh: zInput += nWord; 4ee9e31a2d 2008-02-13 drh: nInput = nList-(zInput-zList); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( TH_OK!=(rc = thNextWord(interp, zInput, nInput, &nWord, 0)) 4ee9e31a2d 2008-02-13 drh: || TH_OK!=(rc = thSubstWord(interp, zInput, nWord)) 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: goto finish; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: zInput = &zInput[nWord]; 4ee9e31a2d 2008-02-13 drh: nInput = nList-(zInput-zList); 4ee9e31a2d 2008-02-13 drh: if( nWord>0 ){ 4ee9e31a2d 2008-02-13 drh: zWord = Th_GetResult(interp, &nWord); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &strbuf, zWord, nWord); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &strbuf, "\0", 1); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &lenbuf, &nWord, sizeof(int)); 4ee9e31a2d 2008-02-13 drh: nCount++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: assert((lenbuf.nBuf/sizeof(int))==nCount); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert((pazElem && panElem) || (!pazElem && !panElem)); 4ee9e31a2d 2008-02-13 drh: if( pazElem && rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: int i; 0c99a1554a 2008-10-24 drh: char *zElem; 4ee9e31a2d 2008-02-13 drh: int *anElem; 0c99a1554a 2008-10-24 drh: char **azElem = Th_Malloc(interp, 0c99a1554a 2008-10-24 drh: sizeof(char*) * nCount + /* azElem */ 4ee9e31a2d 2008-02-13 drh: sizeof(int) * nCount + /* anElem */ 4ee9e31a2d 2008-02-13 drh: strbuf.nBuf /* space for list element strings */ 4ee9e31a2d 2008-02-13 drh: ); 4ee9e31a2d 2008-02-13 drh: anElem = (int *)&azElem[nCount]; 0c99a1554a 2008-10-24 drh: zElem = (char *)&anElem[nCount]; 4ee9e31a2d 2008-02-13 drh: memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); 4ee9e31a2d 2008-02-13 drh: memcpy(zElem, strbuf.zBuf, strbuf.nBuf); 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nCount;i++){ 4ee9e31a2d 2008-02-13 drh: azElem[i] = zElem; 4ee9e31a2d 2008-02-13 drh: zElem += (anElem[i] + 1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: *pazElem = azElem; 4ee9e31a2d 2008-02-13 drh: *panElem = anElem; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( pnCount ){ 4ee9e31a2d 2008-02-13 drh: *pnCount = nCount; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: finish: 4ee9e31a2d 2008-02-13 drh: thBufferFree(interp, &strbuf); 4ee9e31a2d 2008-02-13 drh: thBufferFree(interp, &lenbuf); 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Evaluate the th1 script contained in the string (zProgram, nProgram) 4ee9e31a2d 2008-02-13 drh: ** in the current stack frame. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){ 4ee9e31a2d 2008-02-13 drh: int rc = TH_OK; 0c99a1554a 2008-10-24 drh: const char *zInput = zProgram; 4ee9e31a2d 2008-02-13 drh: int nInput = nProgram; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: while( rc==TH_OK && nInput ){ 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pEntry; 4ee9e31a2d 2008-02-13 drh: int nSpace; 0c99a1554a 2008-10-24 drh: const char *zFirst; 0c99a1554a 2008-10-24 drh: 0c99a1554a 2008-10-24 drh: char **argv; 4ee9e31a2d 2008-02-13 drh: int *argl; 4ee9e31a2d 2008-02-13 drh: int argc; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(nInput>=0); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Skip a semi-colon */ 4ee9e31a2d 2008-02-13 drh: if( *zInput==';' ){ 4ee9e31a2d 2008-02-13 drh: zInput++; 4ee9e31a2d 2008-02-13 drh: nInput--; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Skip past leading white-space. */ 4ee9e31a2d 2008-02-13 drh: thNextSpace(interp, zInput, nInput, &nSpace); 4ee9e31a2d 2008-02-13 drh: zInput += nSpace; 4ee9e31a2d 2008-02-13 drh: nInput -= nSpace; 4ee9e31a2d 2008-02-13 drh: zFirst = zInput; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Check for a comment. If found, skip to the end of the line. */ 4ee9e31a2d 2008-02-13 drh: if( zInput[0]=='#' ){ 4ee9e31a2d 2008-02-13 drh: while( !thEndOfLine(zInput, nInput) ){ 4ee9e31a2d 2008-02-13 drh: zInput++; 4ee9e31a2d 2008-02-13 drh: nInput--; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: continue; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Gobble up input a word at a time until the end of the command 4ee9e31a2d 2008-02-13 drh: ** (a semi-colon or end of line). 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){ 4ee9e31a2d 2008-02-13 drh: int nWord; 4ee9e31a2d 2008-02-13 drh: thNextSpace(interp, zInput, nInput, &nSpace); 4ee9e31a2d 2008-02-13 drh: rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1); 4ee9e31a2d 2008-02-13 drh: zInput += (nSpace+nWord); 4ee9e31a2d 2008-02-13 drh: nInput -= (nSpace+nWord); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( rc!=TH_OK ) continue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Split the command into an array of words. This call also does 4ee9e31a2d 2008-02-13 drh: ** substitution of each individual word. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: rc = thSplitList(interp, zFirst, zInput-zFirst, &argv, &argl, &argc); 4ee9e31a2d 2008-02-13 drh: if( rc!=TH_OK ) continue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( argc>0 ){ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Look up the command name in the command hash-table. */ 4ee9e31a2d 2008-02-13 drh: pEntry = Th_HashFind(interp, interp->paCmd, argv[0], argl[0], 0); 4ee9e31a2d 2008-02-13 drh: if( !pEntry ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "no such command: ", argv[0], argl[0]); 4ee9e31a2d 2008-02-13 drh: rc = TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Call the command procedure. */ 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: Th_Command *p = (Th_Command *)(pEntry->pData); 0c99a1554a 2008-10-24 drh: const char **azArg = (const char **)argv; 4ee9e31a2d 2008-02-13 drh: rc = p->xProc(interp, p->pContext, argc, azArg, argl); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* If an error occured, add this command to the stack trace report. */ 4ee9e31a2d 2008-02-13 drh: if( rc==TH_ERROR ){ 0c99a1554a 2008-10-24 drh: char *zRes; 4ee9e31a2d 2008-02-13 drh: int nRes; 0c99a1554a 2008-10-24 drh: char *zStack = 0; 4ee9e31a2d 2008-02-13 drh: int nStack = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: zRes = Th_TakeResult(interp, &nRes); 0c99a1554a 2008-10-24 drh: if( TH_OK==Th_GetVar(interp, (char *)"::th_stack_trace", -1) ){ 4ee9e31a2d 2008-02-13 drh: zStack = Th_TakeResult(interp, &nStack); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: Th_ListAppend(interp, &zStack, &nStack, zFirst, zInput-zFirst); 0c99a1554a 2008-10-24 drh: Th_SetVar(interp, (char *)"::th_stack_trace", -1, zStack, nStack); 4ee9e31a2d 2008-02-13 drh: Th_SetResult(interp, zRes, nRes); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, zRes); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, zStack); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, argv); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Interpret an integer frame identifier passed to either Th_Eval() or 4ee9e31a2d 2008-02-13 drh: ** Th_LinkVar(). If successful, return a pointer to the identified 4ee9e31a2d 2008-02-13 drh: ** Th_Frame structure. If unsuccessful (no such frame), return 0 and 4ee9e31a2d 2008-02-13 drh: ** leave an error message in the interpreter result. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Argument iFrame is interpreted as follows: 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** * If iFrame is 0, this means the current frame. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** * If iFrame is negative, then the nth frame up the stack, where 4ee9e31a2d 2008-02-13 drh: ** n is the absolute value of iFrame. A value of -1 means the 4ee9e31a2d 2008-02-13 drh: ** calling procedure. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** * If iFrame is +ve, then the nth frame from the bottom of the 4ee9e31a2d 2008-02-13 drh: ** stack. An iFrame value of 1 means the toplevel (global) frame. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static Th_Frame *getFrame(Th_Interp *interp, int iFrame){ 4ee9e31a2d 2008-02-13 drh: Th_Frame *p = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: if( iFrame>0 ){ 4ee9e31a2d 2008-02-13 drh: for(i=0; p; i++){ 4ee9e31a2d 2008-02-13 drh: p = p->pCaller; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iFrame = (i*-1) + iFrame; 4ee9e31a2d 2008-02-13 drh: p = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: for(i=0; p && i<(iFrame*-1); i++){ 4ee9e31a2d 2008-02-13 drh: p = p->pCaller; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( !p ){ 0c99a1554a 2008-10-24 drh: char *zFrame; 4ee9e31a2d 2008-02-13 drh: int nFrame; 4ee9e31a2d 2008-02-13 drh: Th_SetResultInt(interp, iFrame); 4ee9e31a2d 2008-02-13 drh: zFrame = Th_TakeResult(interp, &nFrame); 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "no such frame:", zFrame, nFrame); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, zFrame); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return p; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Evaluate th1 script (zProgram, nProgram) in the frame identified by 4ee9e31a2d 2008-02-13 drh: ** argument iFrame. Leave either an error message or a result in the 4ee9e31a2d 2008-02-13 drh: ** interpreter result and return a th1 error code (TH_OK, TH_ERROR, 4ee9e31a2d 2008-02-13 drh: ** TH_RETURN, TH_CONTINUE or TH_BREAK). 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_Eval(Th_Interp *interp, int iFrame, const char *zProgram, int nProgram){ 4ee9e31a2d 2008-02-13 drh: int rc = TH_OK; 4ee9e31a2d 2008-02-13 drh: Th_Frame *pSavedFrame = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Set Th_Interp.pFrame to the frame that this script is to be 4ee9e31a2d 2008-02-13 drh: ** evaluated in. The current frame is saved in pSavedFrame and will 4ee9e31a2d 2008-02-13 drh: ** be restored before this function returns. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: interp->pFrame = getFrame(interp, iFrame); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( !interp->pFrame ){ 4ee9e31a2d 2008-02-13 drh: rc = TH_ERROR; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: int nInput = nProgram; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nInput<0 ){ 4ee9e31a2d 2008-02-13 drh: nInput = th_strlen(zProgram); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: rc = thEvalLocal(interp, zProgram, nInput); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: interp->pFrame = pSavedFrame; 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Input string (zVarname, nVarname) contains a th1 variable name. It 4ee9e31a2d 2008-02-13 drh: ** may be a simple scalar variable name or it may be a reference 4ee9e31a2d 2008-02-13 drh: ** to an array member. The variable name may or may not begin with 4ee9e31a2d 2008-02-13 drh: ** "::", indicating that the name refers to a global variable, not 4ee9e31a2d 2008-02-13 drh: ** a local scope one. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** This function inspects and categorizes the supplied variable name. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the name is a global reference, *pisGlobal is set to true. Otherwise 4ee9e31a2d 2008-02-13 drh: ** false. Output string (*pzOuter, *pnOuter) is set to the variable name 4ee9e31a2d 2008-02-13 drh: ** if it is a scalar reference, or the name of the array if it is an 4ee9e31a2d 2008-02-13 drh: ** array variable. If the variable is a scalar, *pzInner is set to 0. 4ee9e31a2d 2008-02-13 drh: ** If it is an array variable, (*pzInner, *pnInner) is set to the 4ee9e31a2d 2008-02-13 drh: ** array key name. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thAnalyseVarname( 0c99a1554a 2008-10-24 drh: const char *zVarname, 4ee9e31a2d 2008-02-13 drh: int nVarname, 0c99a1554a 2008-10-24 drh: const char **pzOuter, /* OUT: Pointer to scalar/array name */ 4ee9e31a2d 2008-02-13 drh: int *pnOuter, /* OUT: Number of bytes at *pzOuter */ 0c99a1554a 2008-10-24 drh: const char **pzInner, /* OUT: Pointer to array key (or null) */ 4ee9e31a2d 2008-02-13 drh: int *pnInner, /* OUT: Number of bytes at *pzInner */ 4ee9e31a2d 2008-02-13 drh: int *pisGlobal /* OUT: Set to true if this is a global ref */ 4ee9e31a2d 2008-02-13 drh: ){ 0c99a1554a 2008-10-24 drh: const char *zOuter = zVarname; 4ee9e31a2d 2008-02-13 drh: int nOuter; 0c99a1554a 2008-10-24 drh: const char *zInner = 0; 4ee9e31a2d 2008-02-13 drh: int nInner = 0; 4ee9e31a2d 2008-02-13 drh: int isGlobal = 0; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nVarname<0 ){ 4ee9e31a2d 2008-02-13 drh: nVarname = th_strlen(zVarname); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: nOuter = nVarname; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* If the variable name starts with "::", then do the lookup is in the 4ee9e31a2d 2008-02-13 drh: ** uppermost (global) frame. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( nVarname>2 && zVarname[0]==':' && zVarname[1]==':' ){ 4ee9e31a2d 2008-02-13 drh: zOuter += 2; 4ee9e31a2d 2008-02-13 drh: nOuter -= 2; 4ee9e31a2d 2008-02-13 drh: isGlobal = 1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Check if this is an array reference. */ 4ee9e31a2d 2008-02-13 drh: if( zOuter[nOuter-1]==')' ){ 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nOuter; i++){ 4ee9e31a2d 2008-02-13 drh: if( zOuter[i]=='(' ){ 4ee9e31a2d 2008-02-13 drh: zInner = &zOuter[i+1]; 4ee9e31a2d 2008-02-13 drh: nInner = nOuter-i-2; 4ee9e31a2d 2008-02-13 drh: nOuter = i; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *pzOuter = zOuter; 4ee9e31a2d 2008-02-13 drh: *pnOuter = nOuter; 4ee9e31a2d 2008-02-13 drh: *pzInner = zInner; 4ee9e31a2d 2008-02-13 drh: *pnInner = nInner; 4ee9e31a2d 2008-02-13 drh: *pisGlobal = isGlobal; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Input string (zVar, nVar) contains a variable name. This function locates 4ee9e31a2d 2008-02-13 drh: ** the Th_Variable structure associated with the named variable. The 4ee9e31a2d 2008-02-13 drh: ** variable name may be a global or local scalar or array variable 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the create argument is non-zero and the named variable does not exist 4ee9e31a2d 2008-02-13 drh: ** it is created. Otherwise, an error is left in the interpreter result 4ee9e31a2d 2008-02-13 drh: ** and NULL returned. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the arrayok argument is false and the named variable is an array, 4ee9e31a2d 2008-02-13 drh: ** an error is left in the interpreter result and NULL returned. If 4ee9e31a2d 2008-02-13 drh: ** arrayok is true an array name is Ok. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static Th_Variable *thFindValue( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zVar, /* Pointer to variable name */ 4ee9e31a2d 2008-02-13 drh: int nVar, /* Number of bytes at nVar */ 4ee9e31a2d 2008-02-13 drh: int create, /* If true, create the variable if not found */ 4ee9e31a2d 2008-02-13 drh: int arrayok /* If true, an array is Ok. Othewise array==error */ 4ee9e31a2d 2008-02-13 drh: ){ 0c99a1554a 2008-10-24 drh: const char *zOuter; 4ee9e31a2d 2008-02-13 drh: int nOuter; 0c99a1554a 2008-10-24 drh: const char *zInner; 4ee9e31a2d 2008-02-13 drh: int nInner; 4ee9e31a2d 2008-02-13 drh: int isGlobal; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pEntry; 4ee9e31a2d 2008-02-13 drh: Th_Frame *pFrame = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: Th_Variable *pValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal); 4ee9e31a2d 2008-02-13 drh: if( isGlobal ){ 4ee9e31a2d 2008-02-13 drh: while( pFrame->pCaller ) pFrame = pFrame->pCaller; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); 4ee9e31a2d 2008-02-13 drh: assert(pEntry || !create); 4ee9e31a2d 2008-02-13 drh: if( !pEntry ){ 4ee9e31a2d 2008-02-13 drh: goto no_such_var; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pValue = (Th_Variable *)pEntry->pData; 4ee9e31a2d 2008-02-13 drh: if( !pValue ){ 4ee9e31a2d 2008-02-13 drh: assert(create); 4ee9e31a2d 2008-02-13 drh: pValue = Th_Malloc(interp, sizeof(Th_Variable)); 4ee9e31a2d 2008-02-13 drh: pValue->nRef = 1; 4ee9e31a2d 2008-02-13 drh: pEntry->pData = (void *)pValue; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( zInner ){ 4ee9e31a2d 2008-02-13 drh: if( pValue->zData ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter); 4ee9e31a2d 2008-02-13 drh: return 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( !pValue->pHash ){ 4ee9e31a2d 2008-02-13 drh: if( !create ){ 4ee9e31a2d 2008-02-13 drh: goto no_such_var; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pValue->pHash = Th_HashNew(interp); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); 4ee9e31a2d 2008-02-13 drh: if( !pEntry ){ 4ee9e31a2d 2008-02-13 drh: goto no_such_var; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pValue = (Th_Variable *)pEntry->pData; 4ee9e31a2d 2008-02-13 drh: if( !pValue ){ 4ee9e31a2d 2008-02-13 drh: assert(create); 4ee9e31a2d 2008-02-13 drh: pValue = Th_Malloc(interp, sizeof(Th_Variable)); 4ee9e31a2d 2008-02-13 drh: pValue->nRef = 1; 4ee9e31a2d 2008-02-13 drh: pEntry->pData = (void *)pValue; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: if( pValue->pHash && !arrayok ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter); 4ee9e31a2d 2008-02-13 drh: return 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return pValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: no_such_var: 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "no such variable:", zVar, nVar); 4ee9e31a2d 2008-02-13 drh: return 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** String (zVar, nVar) must contain the name of a scalar variable or 4ee9e31a2d 2008-02-13 drh: ** array member. Look up the variable, store its current value in 4ee9e31a2d 2008-02-13 drh: ** the interpreter result and return TH_OK. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the named variable does not exist, return TH_ERROR and leave 4ee9e31a2d 2008-02-13 drh: ** an error message in the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ 4ee9e31a2d 2008-02-13 drh: Th_Variable *pValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pValue = thFindValue(interp, zVar, nVar, 0, 0); 4ee9e31a2d 2008-02-13 drh: if( !pValue ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( !pValue->zData ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "no such variable:", zVar, nVar); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return Th_SetResult(interp, pValue->zData, pValue->nData); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** String (zVar, nVar) must contain the name of a scalar variable or 4ee9e31a2d 2008-02-13 drh: ** array member. If the variable does not exist it is created. The 4ee9e31a2d 2008-02-13 drh: ** variable is set to the value supplied in string (zValue, nValue). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If (zVar, nVar) refers to an existing array, TH_ERROR is returned 4ee9e31a2d 2008-02-13 drh: ** and an error message left in the interpreter result. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_SetVar( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zVar, 4ee9e31a2d 2008-02-13 drh: int nVar, 0c99a1554a 2008-10-24 drh: const char *zValue, 4ee9e31a2d 2008-02-13 drh: int nValue 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: Th_Variable *pValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pValue = thFindValue(interp, zVar, nVar, 1, 0); 4ee9e31a2d 2008-02-13 drh: if( !pValue ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nValue<0 ){ 4ee9e31a2d 2008-02-13 drh: nValue = th_strlen(zValue); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( pValue->zData ){ 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pValue->zData); 4ee9e31a2d 2008-02-13 drh: pValue->zData = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(zValue || nValue==0); 4ee9e31a2d 2008-02-13 drh: pValue->zData = Th_Malloc(interp, nValue+1); 4ee9e31a2d 2008-02-13 drh: pValue->zData[nValue] = '\0'; 4ee9e31a2d 2008-02-13 drh: memcpy(pValue->zData, zValue, nValue); 4ee9e31a2d 2008-02-13 drh: pValue->nData = nValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Create a variable link so that accessing variable (zLocal, nLocal) is 4ee9e31a2d 2008-02-13 drh: ** the same as accessing variable (zLink, nLink) in stack frame iFrame. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_LinkVar( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, /* Interpreter */ 0c99a1554a 2008-10-24 drh: const char *zLocal, int nLocal, /* Local varname */ 4ee9e31a2d 2008-02-13 drh: int iFrame, /* Stack frame of linked var */ 0c99a1554a 2008-10-24 drh: const char *zLink, int nLink /* Linked varname */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: Th_Frame *pSavedFrame = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: Th_Frame *pFrame; 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pEntry; 4ee9e31a2d 2008-02-13 drh: Th_Variable *pValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pFrame = getFrame(interp, iFrame); 4ee9e31a2d 2008-02-13 drh: if( !pFrame ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pSavedFrame = interp->pFrame; 4ee9e31a2d 2008-02-13 drh: interp->pFrame = pFrame; 4ee9e31a2d 2008-02-13 drh: pValue = thFindValue(interp, zLink, nLink, 1, 1); 4ee9e31a2d 2008-02-13 drh: interp->pFrame = pSavedFrame; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); 4ee9e31a2d 2008-02-13 drh: if( pEntry->pData ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pEntry->pData = (void *)pValue; 4ee9e31a2d 2008-02-13 drh: pValue->nRef++; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Input string (zVar, nVar) must contain the name of a scalar variable, 4ee9e31a2d 2008-02-13 drh: ** an array, or an array member. If the identified variable exists, it 4ee9e31a2d 2008-02-13 drh: ** is deleted and TH_OK returned. Otherwise, an error message is left 4ee9e31a2d 2008-02-13 drh: ** in the interpreter result and TH_ERROR is returned. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ 4ee9e31a2d 2008-02-13 drh: Th_Variable *pValue; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pValue = thFindValue(interp, zVar, nVar, 1, 1); 4ee9e31a2d 2008-02-13 drh: if( !pValue ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pValue->zData); 4ee9e31a2d 2008-02-13 drh: pValue->zData = 0; 4ee9e31a2d 2008-02-13 drh: if( pValue->pHash ){ 4ee9e31a2d 2008-02-13 drh: Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp); 4ee9e31a2d 2008-02-13 drh: Th_HashDelete(interp, pValue->pHash); 4ee9e31a2d 2008-02-13 drh: pValue->pHash = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Return an allocated buffer containing a copy of string (z, n). The 4ee9e31a2d 2008-02-13 drh: ** caller is responsible for eventually calling Th_Free() to free 4ee9e31a2d 2008-02-13 drh: ** the returned buffer. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: char *th_strdup(Th_Interp *interp, const char *z, int n){ 0c99a1554a 2008-10-24 drh: char *zRes; 4ee9e31a2d 2008-02-13 drh: if( n<0 ){ 4ee9e31a2d 2008-02-13 drh: n = th_strlen(z); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: zRes = Th_Malloc(interp, n+1); 4ee9e31a2d 2008-02-13 drh: memcpy(zRes, z, n); 4ee9e31a2d 2008-02-13 drh: zRes[n] = '\0'; 4ee9e31a2d 2008-02-13 drh: return zRes; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Argument zPre must be a nul-terminated string. Set the interpreter 4ee9e31a2d 2008-02-13 drh: ** result to a string containing the contents of zPre, followed by 4ee9e31a2d 2008-02-13 drh: ** a space (" ") character, followed by a copy of string (z, n). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** In other words, the equivalent of: 4ee9e31a2d 2008-02-13 drh: * 4ee9e31a2d 2008-02-13 drh: ** printf("%s %.*s", zPre, n, z); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Example: 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Th_ErrorMessage(interp, "no such variable:", zVarname, nVarname); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_ErrorMessage(Th_Interp *interp, const char *zPre, const char *z, int n){ 4ee9e31a2d 2008-02-13 drh: if( interp ){ 0c99a1554a 2008-10-24 drh: char *zRes = 0; 4ee9e31a2d 2008-02-13 drh: int nRes = 0; 0c99a1554a 2008-10-24 drh: 0c99a1554a 2008-10-24 drh: Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_StringAppend(interp, &zRes, &nRes, zPre, -1); 4ee9e31a2d 2008-02-13 drh: if( zRes[nRes-1]=='"' ){ 4ee9e31a2d 2008-02-13 drh: Th_StringAppend(interp, &zRes, &nRes, z, n); 0c99a1554a 2008-10-24 drh: Th_StringAppend(interp, &zRes, &nRes, (const char *)"\"", 1); 4ee9e31a2d 2008-02-13 drh: }else{ 0c99a1554a 2008-10-24 drh: Th_StringAppend(interp, &zRes, &nRes, (const char *)" ", 1); 4ee9e31a2d 2008-02-13 drh: Th_StringAppend(interp, &zRes, &nRes, z, n); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_SetResult(interp, zRes, nRes); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, zRes); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Set the current interpreter result by taking a copy of the buffer 4ee9e31a2d 2008-02-13 drh: ** pointed to by z, size n bytes. TH_OK is always returned. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_SetResult(Th_Interp *pInterp, const char *z, int n){ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Free the current result */ 4ee9e31a2d 2008-02-13 drh: Th_Free(pInterp, pInterp->zResult); 4ee9e31a2d 2008-02-13 drh: pInterp->zResult = 0; 4ee9e31a2d 2008-02-13 drh: pInterp->nResult = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( n<0 ){ 4ee9e31a2d 2008-02-13 drh: n = th_strlen(z); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( z && n>0 ){ 0c99a1554a 2008-10-24 drh: char *zResult; 4ee9e31a2d 2008-02-13 drh: zResult = Th_Malloc(pInterp, n+1); 4ee9e31a2d 2008-02-13 drh: memcpy(zResult, z, n); 4ee9e31a2d 2008-02-13 drh: zResult[n] = '\0'; 4ee9e31a2d 2008-02-13 drh: pInterp->zResult = zResult; 4ee9e31a2d 2008-02-13 drh: pInterp->nResult = n; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Return a pointer to the buffer containing the current interpreter 4ee9e31a2d 2008-02-13 drh: ** result. If pN is not NULL, set *pN to the size of the returned 4ee9e31a2d 2008-02-13 drh: ** buffer. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: const char *Th_GetResult(Th_Interp *pInterp, int *pN){ 4ee9e31a2d 2008-02-13 drh: assert(pInterp->zResult || pInterp->nResult==0); 4ee9e31a2d 2008-02-13 drh: if( pN ){ 4ee9e31a2d 2008-02-13 drh: *pN = pInterp->nResult; 4ee9e31a2d 2008-02-13 drh: } 0c99a1554a 2008-10-24 drh: return (pInterp->zResult ? pInterp->zResult : (const char *)""); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Return a pointer to the buffer containing the current interpreter 4ee9e31a2d 2008-02-13 drh: ** result. If pN is not NULL, set *pN to the size of the returned 4ee9e31a2d 2008-02-13 drh: ** buffer. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** This function is the same as Th_GetResult() except that the 4ee9e31a2d 2008-02-13 drh: ** caller is responsible for eventually calling Th_Free() on the 4ee9e31a2d 2008-02-13 drh: ** returned buffer. The internal interpreter result is cleared 4ee9e31a2d 2008-02-13 drh: ** after this function is called. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: char *Th_TakeResult(Th_Interp *pInterp, int *pN){ 4ee9e31a2d 2008-02-13 drh: if( pN ){ 4ee9e31a2d 2008-02-13 drh: *pN = pInterp->nResult; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( pInterp->zResult ){ 0c99a1554a 2008-10-24 drh: char *zResult = pInterp->zResult; 4ee9e31a2d 2008-02-13 drh: pInterp->zResult = 0; 4ee9e31a2d 2008-02-13 drh: pInterp->nResult = 0; 4ee9e31a2d 2008-02-13 drh: return zResult; 4ee9e31a2d 2008-02-13 drh: }else{ 0c99a1554a 2008-10-24 drh: return (char *)Th_Malloc(pInterp, 1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Wrappers around the supplied malloc() and free() 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: void *Th_Malloc(Th_Interp *pInterp, int nByte){ 4ee9e31a2d 2008-02-13 drh: void *p = pInterp->pVtab->xMalloc(nByte); 4ee9e31a2d 2008-02-13 drh: if( p ){ 4ee9e31a2d 2008-02-13 drh: memset(p, 0, nByte); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return p; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: void Th_Free(Th_Interp *pInterp, void *z){ 4ee9e31a2d 2008-02-13 drh: if( z ){ 4ee9e31a2d 2008-02-13 drh: pInterp->pVtab->xFree(z); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Install a new th1 command. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If a command of the same name already exists, it is deleted automatically. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_CreateCommand( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 4ee9e31a2d 2008-02-13 drh: const char *zName, /* New command name */ 4ee9e31a2d 2008-02-13 drh: Th_CommandProc xProc, /* Command callback proc */ 4ee9e31a2d 2008-02-13 drh: void *pContext, /* Value to pass as second arg to xProc */ 4ee9e31a2d 2008-02-13 drh: void (*xDel)(Th_Interp *, void *) /* Command destructor callback */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pEntry; 4ee9e31a2d 2008-02-13 drh: Th_Command *pCommand; 4ee9e31a2d 2008-02-13 drh: 0c99a1554a 2008-10-24 drh: pEntry = Th_HashFind(interp, interp->paCmd, (const char *)zName, -1, 1); 4ee9e31a2d 2008-02-13 drh: if( pEntry->pData ){ 4ee9e31a2d 2008-02-13 drh: pCommand = pEntry->pData; 4ee9e31a2d 2008-02-13 drh: if( pCommand->xDel ){ 4ee9e31a2d 2008-02-13 drh: pCommand->xDel(interp, pCommand->pContext); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: pCommand = Th_Malloc(interp, sizeof(Th_Command)); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pCommand->xProc = xProc; 4ee9e31a2d 2008-02-13 drh: pCommand->pContext = pContext; 4ee9e31a2d 2008-02-13 drh: pCommand->xDel = xDel; 4ee9e31a2d 2008-02-13 drh: pEntry->pData = (void *)pCommand; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0, 4ee9e31a2d 2008-02-13 drh: ** the command is deleted instead of renamed. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If successful, TH_OK is returned. If command zName does not exist, or 4ee9e31a2d 2008-02-13 drh: ** if command zNew already exists, an error message is left in the 4ee9e31a2d 2008-02-13 drh: ** interpreter result and TH_ERROR is returned. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_RenameCommand( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zName, /* Existing command name */ 4ee9e31a2d 2008-02-13 drh: int nName, /* Number of bytes at zName */ 0c99a1554a 2008-10-24 drh: const char *zNew, /* New command name */ 4ee9e31a2d 2008-02-13 drh: int nNew /* Number of bytes at zNew */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pEntry; 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pNewEntry; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: pEntry = Th_HashFind(interp, interp->paCmd, zName, nName, 0); 4ee9e31a2d 2008-02-13 drh: if( !pEntry ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "no such command:", zName, nName); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: assert(pEntry->pData); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nNew>0 ){ 4ee9e31a2d 2008-02-13 drh: pNewEntry = Th_HashFind(interp, interp->paCmd, zNew, nNew, 1); 4ee9e31a2d 2008-02-13 drh: if( pNewEntry->pData ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "command exists:", zNew, nNew); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pNewEntry->pData = pEntry->pData; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: Th_Command *pCommand = (Th_Command *)(pEntry->pData); 4ee9e31a2d 2008-02-13 drh: if( pCommand->xDel ){ 4ee9e31a2d 2008-02-13 drh: pCommand->xDel(interp, pCommand->pContext); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pCommand); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_HashFind(interp, interp->paCmd, zName, nName, -1); 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Push a stack frame onto the interpreter stack, invoke the 4ee9e31a2d 2008-02-13 drh: ** callback, and pop the frame back off again. See the implementation 4ee9e31a2d 2008-02-13 drh: ** of [proc] (th_lang.c) for an example. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_InFrame(Th_Interp *interp, 4ee9e31a2d 2008-02-13 drh: int (*xCall)(Th_Interp *, void *pContext1, void *pContext2), 4ee9e31a2d 2008-02-13 drh: void *pContext1, 4ee9e31a2d 2008-02-13 drh: void *pContext2 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: Th_Frame frame; 4ee9e31a2d 2008-02-13 drh: int rc; 4ee9e31a2d 2008-02-13 drh: thPushFrame(interp, &frame); 4ee9e31a2d 2008-02-13 drh: rc = xCall(interp, pContext1, pContext2); 4ee9e31a2d 2008-02-13 drh: thPopFrame(interp); 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Split a th1 list into its component elements. The list to split is 4ee9e31a2d 2008-02-13 drh: ** passed via arguments (zList, nList). If successful, TH_OK is returned. 4ee9e31a2d 2008-02-13 drh: ** If an error occurs (if (zList, nList) is not a valid list) an error 4ee9e31a2d 2008-02-13 drh: ** message is left in the interpreter result and TH_ERROR returned. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If successful, *pnCount is set to the number of elements in the list. 4ee9e31a2d 2008-02-13 drh: ** panElem is set to point at an array of *pnCount integers - the lengths 4ee9e31a2d 2008-02-13 drh: ** of the element values. *pazElem is set to point at an array of 4ee9e31a2d 2008-02-13 drh: ** pointers to buffers containing the array element's data. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** To free the arrays allocated at *pazElem and *panElem, the caller 4ee9e31a2d 2008-02-13 drh: ** should call Th_Free() on *pazElem only. Exactly one such call to 4ee9e31a2d 2008-02-13 drh: ** Th_Free() must be made per call to Th_SplitList(). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Example: 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** int nElem; 4ee9e31a2d 2008-02-13 drh: ** int *anElem; 0c99a1554a 2008-10-24 drh: ** char **azElem; 4ee9e31a2d 2008-02-13 drh: ** int i; 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Th_SplitList(interp, zList, nList, &azElem, &anElem, &nElem); 4ee9e31a2d 2008-02-13 drh: ** for(i=0; i<nElem; i++){ 4ee9e31a2d 2008-02-13 drh: ** int nData = anElem[i]; 0c99a1554a 2008-10-24 drh: ** char *zData = azElem[i]; 4ee9e31a2d 2008-02-13 drh: ** ... 4ee9e31a2d 2008-02-13 drh: ** } 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Th_Free(interp, azElem); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_SplitList( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zList, /* Pointer to buffer containing list */ 4ee9e31a2d 2008-02-13 drh: int nList, /* Number of bytes at zList */ 0c99a1554a 2008-10-24 drh: char ***pazElem, /* OUT: Array of pointers to element data */ 4ee9e31a2d 2008-02-13 drh: int **panElem, /* OUT: Array of element data lengths */ 4ee9e31a2d 2008-02-13 drh: int *pnCount /* OUT: Number of elements in list */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int rc; 4ee9e31a2d 2008-02-13 drh: interp->isListMode = 1; 4ee9e31a2d 2008-02-13 drh: rc = thSplitList(interp, zList, nList, pazElem, panElem, pnCount); 4ee9e31a2d 2008-02-13 drh: interp->isListMode = 0; 4ee9e31a2d 2008-02-13 drh: if( rc ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "Expected list, got: \"", zList, nList); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Append a new element to an existing th1 list. The element to append 4ee9e31a2d 2008-02-13 drh: ** to the list is (zElem, nElem). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** A pointer to the existing list must be stored at *pzList when this 4ee9e31a2d 2008-02-13 drh: ** function is called. The length must be stored in *pnList. The value 4ee9e31a2d 2008-02-13 drh: ** of *pzList must either be NULL (in which case *pnList must be 0), or 4ee9e31a2d 2008-02-13 drh: ** a pointer to memory obtained from Th_Malloc(). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** This function calls Th_Free() to free the buffer at *pzList and sets 4ee9e31a2d 2008-02-13 drh: ** *pzList to point to a new buffer containing the new list value. *pnList 4ee9e31a2d 2008-02-13 drh: ** is similarly updated before returning. The return value is always TH_OK. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Example: 4ee9e31a2d 2008-02-13 drh: ** 0c99a1554a 2008-10-24 drh: ** char *zList = 0; 4ee9e31a2d 2008-02-13 drh: ** int nList = 0; 4ee9e31a2d 2008-02-13 drh: ** for (...) { 0c99a1554a 2008-10-24 drh: ** char *zElem = <some expression>; 4ee9e31a2d 2008-02-13 drh: ** Th_ListAppend(interp, &zList, &nList, zElem, -1); 4ee9e31a2d 2008-02-13 drh: ** } 4ee9e31a2d 2008-02-13 drh: ** Th_SetResult(interp, zList, nList); 4ee9e31a2d 2008-02-13 drh: ** Th_Free(interp, zList); 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_ListAppend( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, /* Interpreter context */ 0c99a1554a 2008-10-24 drh: char **pzList, /* IN/OUT: Ptr to ptr to list */ 4ee9e31a2d 2008-02-13 drh: int *pnList, /* IN/OUT: Current length of *pzList */ 0c99a1554a 2008-10-24 drh: const char *zElem, /* Data to append */ 4ee9e31a2d 2008-02-13 drh: int nElem /* Length of nElem */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: Buffer output; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: int hasSpecialChar = 0; 4ee9e31a2d 2008-02-13 drh: int hasEscapeChar = 0; 4ee9e31a2d 2008-02-13 drh: int nBrace = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: output.zBuf = *pzList; 4ee9e31a2d 2008-02-13 drh: output.nBuf = *pnList; 4ee9e31a2d 2008-02-13 drh: output.nBufAlloc = output.nBuf; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nElem<0 ){ 4ee9e31a2d 2008-02-13 drh: nElem = th_strlen(zElem); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( output.nBuf>0 ){ 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &output, " ", 1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nElem; i++){ 0c99a1554a 2008-10-24 drh: char c = zElem[i]; 4ee9e31a2d 2008-02-13 drh: if( th_isspecial(c) ) hasSpecialChar = 1; 4ee9e31a2d 2008-02-13 drh: if( c=='\\' ) hasEscapeChar = 1; 4ee9e31a2d 2008-02-13 drh: if( c=='{' ) nBrace++; 4ee9e31a2d 2008-02-13 drh: if( c=='}' ) nBrace--; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nElem==0 || (!hasEscapeChar && hasSpecialChar && nBrace==0) ){ 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &output, "{", 1); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &output, zElem, nElem); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &output, "}", 1); 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nElem; i++){ 0c99a1554a 2008-10-24 drh: char c = zElem[i]; 4ee9e31a2d 2008-02-13 drh: if( th_isspecial(c) ) thBufferWrite(interp, &output, "\\", 1); 4ee9e31a2d 2008-02-13 drh: thBufferWrite(interp, &output, &c, 1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *pzList = output.zBuf; 4ee9e31a2d 2008-02-13 drh: *pnList = output.nBuf; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Append a new element to an existing th1 string. This function uses 4ee9e31a2d 2008-02-13 drh: ** the same interface as the Th_ListAppend() function. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_StringAppend( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, /* Interpreter context */ 0c99a1554a 2008-10-24 drh: char **pzStr, /* IN/OUT: Ptr to ptr to list */ 4ee9e31a2d 2008-02-13 drh: int *pnStr, /* IN/OUT: Current length of *pzStr */ 0c99a1554a 2008-10-24 drh: const char *zElem, /* Data to append */ 4ee9e31a2d 2008-02-13 drh: int nElem /* Length of nElem */ 4ee9e31a2d 2008-02-13 drh: ){ 0c99a1554a 2008-10-24 drh: char *zNew; 4ee9e31a2d 2008-02-13 drh: int nNew; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nElem<0 ){ 4ee9e31a2d 2008-02-13 drh: nElem = th_strlen(zElem); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: nNew = *pnStr + nElem; 4ee9e31a2d 2008-02-13 drh: zNew = Th_Malloc(interp, nNew); 4ee9e31a2d 2008-02-13 drh: memcpy(zNew, *pzStr, *pnStr); 4ee9e31a2d 2008-02-13 drh: memcpy(&zNew[*pnStr], zElem, nElem); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, *pzStr); 4ee9e31a2d 2008-02-13 drh: *pzStr = zNew; 4ee9e31a2d 2008-02-13 drh: *pnStr = nNew; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Delete an interpreter. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: void Th_DeleteInterp(Th_Interp *interp){ 4ee9e31a2d 2008-02-13 drh: assert(interp->pFrame); 4ee9e31a2d 2008-02-13 drh: assert(0==interp->pFrame->pCaller); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Delete the contents of the global frame. */ 4ee9e31a2d 2008-02-13 drh: thPopFrame(interp); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Delete any result currently stored in the interpreter. */ 4ee9e31a2d 2008-02-13 drh: Th_SetResult(interp, 0, 0); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Delete all registered commands and the command hash-table itself. */ 4ee9e31a2d 2008-02-13 drh: Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp); 4ee9e31a2d 2008-02-13 drh: Th_HashDelete(interp, interp->paCmd); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Delete the interpreter structure itself. */ 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, (void *)interp); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Create a new interpreter. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){ 4ee9e31a2d 2008-02-13 drh: Th_Interp *p; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Allocate and initialise the interpreter and the global frame */ 4ee9e31a2d 2008-02-13 drh: p = pVtab->xMalloc(sizeof(Th_Interp) + sizeof(Th_Frame)); 4ee9e31a2d 2008-02-13 drh: memset(p, 0, sizeof(Th_Interp)); 4ee9e31a2d 2008-02-13 drh: p->pVtab = pVtab; 4ee9e31a2d 2008-02-13 drh: p->paCmd = Th_HashNew(p); 4ee9e31a2d 2008-02-13 drh: thPushFrame(p, (Th_Frame *)&p[1]); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return p; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** These two types are used only by the expression module, where 4ee9e31a2d 2008-02-13 drh: ** the expression module means the Th_Expr() and exprXXX() functions. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: typedef struct Operator Operator; 4ee9e31a2d 2008-02-13 drh: struct Operator { 4ee9e31a2d 2008-02-13 drh: const char *zOp; 4ee9e31a2d 2008-02-13 drh: int eOp; 4ee9e31a2d 2008-02-13 drh: int iPrecedence; 4ee9e31a2d 2008-02-13 drh: int eArgType; 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: typedef struct Expr Expr; 4ee9e31a2d 2008-02-13 drh: struct Expr { 4ee9e31a2d 2008-02-13 drh: Operator *pOp; 4ee9e31a2d 2008-02-13 drh: Expr *pParent; 4ee9e31a2d 2008-02-13 drh: Expr *pLeft; 4ee9e31a2d 2008-02-13 drh: Expr *pRight; 4ee9e31a2d 2008-02-13 drh: 0c99a1554a 2008-10-24 drh: char *zValue; /* Pointer to literal value */ 4ee9e31a2d 2008-02-13 drh: int nValue; /* Length of literal value buffer */ 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Unary operators */ 4ee9e31a2d 2008-02-13 drh: #define OP_UNARY_MINUS 2 4ee9e31a2d 2008-02-13 drh: #define OP_UNARY_PLUS 3 4ee9e31a2d 2008-02-13 drh: #define OP_BITWISE_NOT 4 4ee9e31a2d 2008-02-13 drh: #define OP_LOGICAL_NOT 5 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Binary operators */ 4ee9e31a2d 2008-02-13 drh: #define OP_MULTIPLY 6 4ee9e31a2d 2008-02-13 drh: #define OP_DIVIDE 7 4ee9e31a2d 2008-02-13 drh: #define OP_MODULUS 8 4ee9e31a2d 2008-02-13 drh: #define OP_ADD 9 4ee9e31a2d 2008-02-13 drh: #define OP_SUBTRACT 10 4ee9e31a2d 2008-02-13 drh: #define OP_LEFTSHIFT 11 4ee9e31a2d 2008-02-13 drh: #define OP_RIGHTSHIFT 12 4ee9e31a2d 2008-02-13 drh: #define OP_LT 13 4ee9e31a2d 2008-02-13 drh: #define OP_GT 14 4ee9e31a2d 2008-02-13 drh: #define OP_LE 15 4ee9e31a2d 2008-02-13 drh: #define OP_GE 16 4ee9e31a2d 2008-02-13 drh: #define OP_EQ 17 4ee9e31a2d 2008-02-13 drh: #define OP_NE 18 4ee9e31a2d 2008-02-13 drh: #define OP_SEQ 19 4ee9e31a2d 2008-02-13 drh: #define OP_SNE 20 4ee9e31a2d 2008-02-13 drh: #define OP_BITWISE_AND 21 4ee9e31a2d 2008-02-13 drh: #define OP_BITWISE_XOR 22 4ee9e31a2d 2008-02-13 drh: #define OP_BITWISE_OR 24 4ee9e31a2d 2008-02-13 drh: #define OP_LOGICAL_AND 25 4ee9e31a2d 2008-02-13 drh: #define OP_LOGICAL_OR 26 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Other symbols */ 4ee9e31a2d 2008-02-13 drh: #define OP_OPEN_BRACKET 27 4ee9e31a2d 2008-02-13 drh: #define OP_CLOSE_BRACKET 28 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Argument types. Each operator in the expression syntax is defined 4ee9e31a2d 2008-02-13 drh: ** as requiring either integer, number (real or integer) or string 4ee9e31a2d 2008-02-13 drh: ** operands. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: #define ARG_INTEGER 1 4ee9e31a2d 2008-02-13 drh: #define ARG_NUMBER 2 4ee9e31a2d 2008-02-13 drh: #define ARG_STRING 3 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: static Operator aOperator[] = { 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: {"(", OP_OPEN_BRACKET, -1, 0}, 4ee9e31a2d 2008-02-13 drh: {")", OP_CLOSE_BRACKET, -1, 0}, 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Note: all unary operators have (iPrecedence==1) */ 4ee9e31a2d 2008-02-13 drh: {"-", OP_UNARY_MINUS, 1, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"+", OP_UNARY_PLUS, 1, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"~", OP_BITWISE_NOT, 1, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {"!", OP_LOGICAL_NOT, 1, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Binary operators. It is important to the parsing in Th_Expr() that 4ee9e31a2d 2008-02-13 drh: * the two-character symbols ("==") appear before the one-character 4ee9e31a2d 2008-02-13 drh: * ones ("="). And that the priorities of all binary operators are 4ee9e31a2d 2008-02-13 drh: * integers between 2 and 12. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: {"<<", OP_LEFTSHIFT, 4, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {">>", OP_RIGHTSHIFT, 4, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {"<=", OP_LE, 5, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {">=", OP_GE, 5, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"==", OP_EQ, 6, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"!=", OP_NE, 6, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"eq", OP_SEQ, 7, ARG_STRING}, 4ee9e31a2d 2008-02-13 drh: {"ne", OP_SNE, 7, ARG_STRING}, 4ee9e31a2d 2008-02-13 drh: {"&&", OP_LOGICAL_AND, 11, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {"||", OP_LOGICAL_OR, 12, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: {"*", OP_MULTIPLY, 2, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"/", OP_DIVIDE, 2, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"%", OP_MODULUS, 2, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {"+", OP_ADD, 3, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"-", OP_SUBTRACT, 3, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"<", OP_LT, 5, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {">", OP_GT, 5, ARG_NUMBER}, 4ee9e31a2d 2008-02-13 drh: {"&", OP_BITWISE_AND, 8, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {"^", OP_BITWISE_XOR, 9, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: {"|", OP_BITWISE_OR, 10, ARG_INTEGER}, 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: {0,0,0} 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The first part of the string (zInput,nInput) contains a number. 4ee9e31a2d 2008-02-13 drh: ** Set *pnVarname to the number of bytes in the numeric string. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int thNextNumber( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *zInput, 4ee9e31a2d 2008-02-13 drh: int nInput, 4ee9e31a2d 2008-02-13 drh: int *pnLiteral 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: int seenDot = 0; 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nInput; i++){ 0c99a1554a 2008-10-24 drh: char c = zInput[i]; 4ee9e31a2d 2008-02-13 drh: if( (seenDot || c!='.') && !th_isdigit(c) ) break; 4ee9e31a2d 2008-02-13 drh: if( c=='.' ) seenDot = 1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: *pnLiteral = i; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Free an expression tree. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void exprFree(Th_Interp *interp, Expr *pExpr){ 4ee9e31a2d 2008-02-13 drh: if( pExpr ){ 4ee9e31a2d 2008-02-13 drh: exprFree(interp, pExpr->pLeft); 4ee9e31a2d 2008-02-13 drh: exprFree(interp, pExpr->pRight); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pExpr->zValue); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pExpr); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Evaluate an expression tree. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int exprEval(Th_Interp *interp, Expr *pExpr){ 4ee9e31a2d 2008-02-13 drh: int rc = TH_OK; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( pExpr->pOp==0 ){ 4ee9e31a2d 2008-02-13 drh: /* A literal */ 4ee9e31a2d 2008-02-13 drh: rc = thSubstWord(interp, pExpr->zValue, pExpr->nValue); 4ee9e31a2d 2008-02-13 drh: }else{ cd965de682 2009-01-24 drh: int eArgType = 0; /* Actual type of arguments */ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Argument values */ 4ee9e31a2d 2008-02-13 drh: int iLeft; 4ee9e31a2d 2008-02-13 drh: int iRight; 4ee9e31a2d 2008-02-13 drh: double fLeft; 4ee9e31a2d 2008-02-13 drh: double fRight; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Left and right arguments as strings */ 0c99a1554a 2008-10-24 drh: char *zLeft = 0; int nLeft; 0c99a1554a 2008-10-24 drh: char *zRight = 0; int nRight; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Evaluate left and right arguments, if they exist. */ 4ee9e31a2d 2008-02-13 drh: if( pExpr->pLeft ){ 4ee9e31a2d 2008-02-13 drh: rc = exprEval(interp, pExpr->pLeft); 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: zLeft = Th_TakeResult(interp, &nLeft); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK && pExpr->pRight ){ 4ee9e31a2d 2008-02-13 drh: rc = exprEval(interp, pExpr->pRight); 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: zRight = Th_TakeResult(interp, &nRight); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Convert arguments to their required forms. */ 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: eArgType = pExpr->pOp->eArgType; 4ee9e31a2d 2008-02-13 drh: if( eArgType==ARG_NUMBER ){ 4ee9e31a2d 2008-02-13 drh: if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft)) 4ee9e31a2d 2008-02-13 drh: && (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight)) 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: eArgType = ARG_INTEGER; 4ee9e31a2d 2008-02-13 drh: }else if( 4ee9e31a2d 2008-02-13 drh: (zLeft && TH_OK!=Th_ToDouble(interp, zLeft, nLeft, &fLeft)) || 4ee9e31a2d 2008-02-13 drh: (zRight && TH_OK!=Th_ToDouble(interp, zRight, nRight, &fRight)) 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: /* A type error. */ 4ee9e31a2d 2008-02-13 drh: rc = TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: }else if( eArgType==ARG_INTEGER ){ 4ee9e31a2d 2008-02-13 drh: rc = Th_ToInt(interp, zLeft, nLeft, &iLeft); 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK && zRight ){ 4ee9e31a2d 2008-02-13 drh: rc = Th_ToInt(interp, zRight, nRight, &iRight); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK && eArgType==ARG_INTEGER ){ cd965de682 2009-01-24 drh: int iRes = 0; 4ee9e31a2d 2008-02-13 drh: switch( pExpr->pOp->eOp ) { 4ee9e31a2d 2008-02-13 drh: case OP_MULTIPLY: iRes = iLeft*iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_DIVIDE: iRes = iLeft/iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_MODULUS: iRes = iLeft%iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_ADD: iRes = iLeft+iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_SUBTRACT: iRes = iLeft-iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_LEFTSHIFT: iRes = iLeft<<iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_RIGHTSHIFT: iRes = iLeft>>iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_LT: iRes = iLeft<iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_GT: iRes = iLeft>iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_LE: iRes = iLeft<=iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_GE: iRes = iLeft>=iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_EQ: iRes = iLeft==iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_NE: iRes = iLeft!=iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_BITWISE_AND: iRes = iLeft&iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_BITWISE_XOR: iRes = iLeft^iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_BITWISE_OR: iRes = iLeft|iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_LOGICAL_AND: iRes = iLeft&&iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_LOGICAL_OR: iRes = iLeft||iRight; break; 4ee9e31a2d 2008-02-13 drh: case OP_UNARY_MINUS: iRes = -iLeft; break; 4ee9e31a2d 2008-02-13 drh: case OP_UNARY_PLUS: iRes = +iLeft; break; 4ee9e31a2d 2008-02-13 drh: case OP_LOGICAL_NOT: iRes = !iLeft; break; 4ee9e31a2d 2008-02-13 drh: default: assert(!"Internal error"); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: Th_SetResultInt(interp, iRes); 4ee9e31a2d 2008-02-13 drh: }else if( rc==TH_OK && eArgType==ARG_NUMBER ){ 4ee9e31a2d 2008-02-13 drh: switch( pExpr->pOp->eOp ) { 4ee9e31a2d 2008-02-13 drh: case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_DIVIDE: Th_SetResultDouble(interp, fLeft/fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_GE: Th_SetResultInt(interp, fLeft>=fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_EQ: Th_SetResultInt(interp, fLeft==fRight); break; 4ee9e31a2d 2008-02-13 drh: case OP_NE: Th_SetResultInt(interp, fLeft!=fRight); break; 4ee9e31a2d 2008-02-13 drh: default: assert(!"Internal error"); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: }else if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: int iEqual = 0; 4ee9e31a2d 2008-02-13 drh: assert( eArgType==ARG_STRING ); 4ee9e31a2d 2008-02-13 drh: if( nRight==nLeft && 0==memcmp(zRight, zLeft, nRight) ){ 4ee9e31a2d 2008-02-13 drh: iEqual = 1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: switch( pExpr->pOp->eOp ) { 4ee9e31a2d 2008-02-13 drh: case OP_SEQ: Th_SetResultInt(interp, iEqual); break; 4ee9e31a2d 2008-02-13 drh: case OP_SNE: Th_SetResultInt(interp, !iEqual); break; 4ee9e31a2d 2008-02-13 drh: default: assert(!"Internal error"); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, zLeft); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, zRight); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Create an expression tree from an array of tokens. If successful, 4ee9e31a2d 2008-02-13 drh: ** the root of the tree is stored in apToken[0]. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int exprMakeTree(Th_Interp *interp, Expr **apToken, int nToken){ 4ee9e31a2d 2008-02-13 drh: int iLeft; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: int jj; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: assert(nToken>0); 4ee9e31a2d 2008-02-13 drh: #define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft)) 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(jj=0; jj<nToken; jj++){ 4ee9e31a2d 2008-02-13 drh: if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){ 4ee9e31a2d 2008-02-13 drh: int nNest = 1; 4ee9e31a2d 2008-02-13 drh: int iLeft = jj; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(jj++; jj<nToken; jj++){ 4ee9e31a2d 2008-02-13 drh: Operator *pOp = apToken[jj]->pOp; 4ee9e31a2d 2008-02-13 drh: if( pOp && pOp->eOp==OP_OPEN_BRACKET ) nNest++; 4ee9e31a2d 2008-02-13 drh: if( pOp && pOp->eOp==OP_CLOSE_BRACKET ) nNest--; 4ee9e31a2d 2008-02-13 drh: if( nNest==0 ) break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( jj==nToken ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( (jj-iLeft)>1 ){ 4ee9e31a2d 2008-02-13 drh: if( exprMakeTree(interp, &apToken[iLeft+1], jj-iLeft-1) ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: exprFree(interp, apToken[jj]); 4ee9e31a2d 2008-02-13 drh: exprFree(interp, apToken[iLeft]); 4ee9e31a2d 2008-02-13 drh: apToken[jj] = 0; 4ee9e31a2d 2008-02-13 drh: apToken[iLeft] = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: iLeft = 0; 4ee9e31a2d 2008-02-13 drh: for(jj=nToken-1; jj>=0; jj--){ 4ee9e31a2d 2008-02-13 drh: if( apToken[jj] ){ 4ee9e31a2d 2008-02-13 drh: if( apToken[jj]->pOp && apToken[jj]->pOp->iPrecedence==1 && iLeft>0 ){ 4ee9e31a2d 2008-02-13 drh: apToken[jj]->pLeft = apToken[iLeft]; 4ee9e31a2d 2008-02-13 drh: apToken[jj]->pLeft->pParent = apToken[jj]; 4ee9e31a2d 2008-02-13 drh: apToken[iLeft] = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iLeft = jj; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: for(i=2; i<=12; i++){ 4ee9e31a2d 2008-02-13 drh: iLeft = -1; 4ee9e31a2d 2008-02-13 drh: for(jj=0; jj<nToken; jj++){ 4ee9e31a2d 2008-02-13 drh: Expr *pToken = apToken[jj]; 4ee9e31a2d 2008-02-13 drh: if( apToken[jj] ){ 4ee9e31a2d 2008-02-13 drh: if( pToken->pOp && !pToken->pLeft && pToken->pOp->iPrecedence==i ){ 4ee9e31a2d 2008-02-13 drh: int iRight = jj+1; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: iRight = jj+1; 4ee9e31a2d 2008-02-13 drh: for(iRight=jj+1; !apToken[iRight] && iRight<nToken; iRight++); 4ee9e31a2d 2008-02-13 drh: if( iRight==nToken || iLeft<0 || !ISTERM(iRight) || !ISTERM(iLeft) ){ 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: pToken->pLeft = apToken[iLeft]; 4ee9e31a2d 2008-02-13 drh: apToken[iLeft] = 0; 4ee9e31a2d 2008-02-13 drh: pToken->pLeft->pParent = pToken; 4ee9e31a2d 2008-02-13 drh: pToken->pRight = apToken[iRight]; 4ee9e31a2d 2008-02-13 drh: apToken[iRight] = 0; 4ee9e31a2d 2008-02-13 drh: pToken->pRight->pParent = pToken; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iLeft = jj; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: for(jj=1; jj<nToken; jj++){ 4ee9e31a2d 2008-02-13 drh: assert( !apToken[jj] || !apToken[0] ); 4ee9e31a2d 2008-02-13 drh: if( apToken[jj] ){ 4ee9e31a2d 2008-02-13 drh: apToken[0] = apToken[jj]; 4ee9e31a2d 2008-02-13 drh: apToken[jj] = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Parse a string containing a TH expression to a list of tokens. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int exprParse( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, /* Interpreter to leave error message in */ 0c99a1554a 2008-10-24 drh: const char *zExpr, /* Pointer to input string */ 4ee9e31a2d 2008-02-13 drh: int nExpr, /* Number of bytes at zExpr */ 4ee9e31a2d 2008-02-13 drh: Expr ***papToken, /* OUT: Array of tokens. */ 4ee9e31a2d 2008-02-13 drh: int *pnToken /* OUT: Size of token array */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: int rc = TH_OK; 4ee9e31a2d 2008-02-13 drh: int nToken = 0; 4ee9e31a2d 2008-02-13 drh: Expr **apToken = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(i=0; rc==TH_OK && i<nExpr; ){ 0c99a1554a 2008-10-24 drh: char c = zExpr[i]; 4ee9e31a2d 2008-02-13 drh: if( th_isspace(c) ){ /* White-space */ 4ee9e31a2d 2008-02-13 drh: i++; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: Expr *pNew = (Expr *)Th_Malloc(interp, sizeof(Expr)); 0c99a1554a 2008-10-24 drh: const char *z = &zExpr[i]; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: switch (c) { 4ee9e31a2d 2008-02-13 drh: case '0': case '1': case '2': case '3': case '4': 4ee9e31a2d 2008-02-13 drh: case '5': case '6': case '7': case '8': case '9': 4ee9e31a2d 2008-02-13 drh: thNextNumber(interp, z, nExpr-i, &pNew->nValue); 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: case '$': 4ee9e31a2d 2008-02-13 drh: thNextVarname(interp, z, nExpr-i, &pNew->nValue); 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: case '{': case '[': { 4ee9e31a2d 2008-02-13 drh: thNextCommand(interp, z, nExpr-i, &pNew->nValue); 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: case '"': { 4ee9e31a2d 2008-02-13 drh: int iEnd = i; 4ee9e31a2d 2008-02-13 drh: while( ++iEnd<nExpr && zExpr[iEnd]!='"' ){ 4ee9e31a2d 2008-02-13 drh: if( zExpr[iEnd]=='\\' ) iEnd++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( iEnd<nExpr ){ 4ee9e31a2d 2008-02-13 drh: pNew->nValue = iEnd+1-i; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: default: { 4ee9e31a2d 2008-02-13 drh: int j; 4ee9e31a2d 2008-02-13 drh: for(j=0; aOperator[j].zOp; j++){ 4ee9e31a2d 2008-02-13 drh: int nOp; 4ee9e31a2d 2008-02-13 drh: if( aOperator[j].iPrecedence==1 && nToken>0 ){ 4ee9e31a2d 2008-02-13 drh: Expr *pPrev = apToken[nToken-1]; 4ee9e31a2d 2008-02-13 drh: if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){ 4ee9e31a2d 2008-02-13 drh: continue; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 0c99a1554a 2008-10-24 drh: nOp = th_strlen((const char *)aOperator[j].zOp); 4ee9e31a2d 2008-02-13 drh: if( (nExpr-i)>=nOp && 0==memcmp(aOperator[j].zOp, &zExpr[i], nOp) ){ 4ee9e31a2d 2008-02-13 drh: pNew->pOp = &aOperator[j]; 4ee9e31a2d 2008-02-13 drh: i += nOp; 4ee9e31a2d 2008-02-13 drh: break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( pNew->pOp || pNew->nValue ){ 4ee9e31a2d 2008-02-13 drh: if( pNew->nValue ){ 4ee9e31a2d 2008-02-13 drh: /* A terminal. Copy the string value. */ 4ee9e31a2d 2008-02-13 drh: assert( !pNew->pOp ); 4ee9e31a2d 2008-02-13 drh: pNew->zValue = Th_Malloc(interp, pNew->nValue); 4ee9e31a2d 2008-02-13 drh: memcpy(pNew->zValue, z, pNew->nValue); 4ee9e31a2d 2008-02-13 drh: i += pNew->nValue; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( (nToken%16)==0 ){ 4ee9e31a2d 2008-02-13 drh: /* Grow the apToken array. */ 4ee9e31a2d 2008-02-13 drh: Expr **apTokenOld = apToken; 4ee9e31a2d 2008-02-13 drh: apToken = Th_Malloc(interp, sizeof(Expr *)*(nToken+16)); 4ee9e31a2d 2008-02-13 drh: memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Put the new token at the end of the apToken array */ 4ee9e31a2d 2008-02-13 drh: apToken[nToken] = pNew; 4ee9e31a2d 2008-02-13 drh: nToken++; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pNew); 4ee9e31a2d 2008-02-13 drh: rc = TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *papToken = apToken; 4ee9e31a2d 2008-02-13 drh: *pnToken = nToken; 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Evaluate the string (zExpr, nExpr) as a Th expression. Store 4ee9e31a2d 2008-02-13 drh: ** the result in the interpreter interp and return TH_OK if 4ee9e31a2d 2008-02-13 drh: ** successful. If an error occurs, store an error message in 4ee9e31a2d 2008-02-13 drh: ** the interpreter result and return an error code. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_Expr(Th_Interp *interp, const char *zExpr, int nExpr){ 4ee9e31a2d 2008-02-13 drh: int rc; /* Return Code */ 4ee9e31a2d 2008-02-13 drh: int i; /* Loop counter */ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: int nToken = 0; 4ee9e31a2d 2008-02-13 drh: Expr **apToken = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nExpr<0 ){ 4ee9e31a2d 2008-02-13 drh: nExpr = th_strlen(zExpr); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Parse the expression to a list of tokens. */ 4ee9e31a2d 2008-02-13 drh: rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* If the parsing was successful, create an expression tree from 4ee9e31a2d 2008-02-13 drh: ** the parsed list of tokens. If successful, apToken[0] is set 4ee9e31a2d 2008-02-13 drh: ** to point to the root of the expression tree. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: rc = exprMakeTree(interp, apToken, nToken); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( rc!=TH_OK ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "syntax error in expression: \"", zExpr, nExpr); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Evaluate the expression tree. */ 4ee9e31a2d 2008-02-13 drh: if( rc==TH_OK ){ 4ee9e31a2d 2008-02-13 drh: rc = exprEval(interp, apToken[0]); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Free memory allocated by exprParse(). */ 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nToken; i++){ 4ee9e31a2d 2008-02-13 drh: exprFree(interp, apToken[i]); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, apToken); 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return rc; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Allocate and return a pointer to a new hash-table. The caller should 4ee9e31a2d 2008-02-13 drh: ** (eventually) delete the hash-table by passing it to Th_HashDelete(). 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: Th_Hash *Th_HashNew(Th_Interp *interp){ 4ee9e31a2d 2008-02-13 drh: Th_Hash *p; 4ee9e31a2d 2008-02-13 drh: p = Th_Malloc(interp, sizeof(Th_Hash)); 4ee9e31a2d 2008-02-13 drh: return p; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Iterate through all values currently stored in the hash table. Invoke 4ee9e31a2d 2008-02-13 drh: ** the callback function xCallback for each entry. The second argument 4ee9e31a2d 2008-02-13 drh: ** passed to xCallback is a copy of the fourth argument passed to this 4ee9e31a2d 2008-02-13 drh: ** function. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: void Th_HashIterate( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 4ee9e31a2d 2008-02-13 drh: Th_Hash *pHash, 4ee9e31a2d 2008-02-13 drh: void (*xCallback)(Th_HashEntry *pEntry, void *pContext), 4ee9e31a2d 2008-02-13 drh: void *pContext 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: for(i=0; i<TH_HASHSIZE; i++){ 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pEntry; 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pNext; 4ee9e31a2d 2008-02-13 drh: for(pEntry=pHash->a[i]; pEntry; pEntry=pNext){ 4ee9e31a2d 2008-02-13 drh: pNext = pEntry->pNext; 4ee9e31a2d 2008-02-13 drh: xCallback(pEntry, pContext); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Helper function for Th_HashDelete(). 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ 4ee9e31a2d 2008-02-13 drh: Th_Free((Th_Interp *)pContext, (void *)pEntry); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Free a hash-table previously allocated by Th_HashNew(). 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: void Th_HashDelete(Th_Interp *interp, Th_Hash *pHash){ 4ee9e31a2d 2008-02-13 drh: if( pHash ){ 4ee9e31a2d 2008-02-13 drh: Th_HashIterate(interp, pHash, xFreeHashEntry, (void *)interp); 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pHash); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** This function is used to insert or delete hash table items, or to 4ee9e31a2d 2008-02-13 drh: ** query a hash table for an existing item. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If parameter op is less than zero, then the hash-table element 4ee9e31a2d 2008-02-13 drh: ** identified by (zKey, nKey) is removed from the hash-table if it 4ee9e31a2d 2008-02-13 drh: ** exists. NULL is returned. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Otherwise, if the hash-table contains an item with key (zKey, nKey), 4ee9e31a2d 2008-02-13 drh: ** a pointer to the associated Th_HashEntry is returned. If parameter 4ee9e31a2d 2008-02-13 drh: ** op is greater than zero, then a new entry is added if one cannot 4ee9e31a2d 2008-02-13 drh: ** be found. If op is zero, then NULL is returned if the item is 4ee9e31a2d 2008-02-13 drh: ** not already present in the hash-table. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *Th_HashFind( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 4ee9e31a2d 2008-02-13 drh: Th_Hash *pHash, 0c99a1554a 2008-10-24 drh: const char *zKey, 4ee9e31a2d 2008-02-13 drh: int nKey, 4ee9e31a2d 2008-02-13 drh: int op /* -ve = delete, 0 = find, +ve = insert */ 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: unsigned int iKey = 0; 4ee9e31a2d 2008-02-13 drh: int i; 4ee9e31a2d 2008-02-13 drh: Th_HashEntry *pRet; 4ee9e31a2d 2008-02-13 drh: Th_HashEntry **ppRet; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( nKey<0 ){ 4ee9e31a2d 2008-02-13 drh: nKey = th_strlen(zKey); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(i=0; i<nKey; i++){ 4ee9e31a2d 2008-02-13 drh: iKey = (iKey<<3) ^ iKey ^ zKey[i]; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iKey = iKey % TH_HASHSIZE; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: for(ppRet=&pHash->a[iKey]; (pRet=*ppRet); ppRet=&pRet->pNext){ 4ee9e31a2d 2008-02-13 drh: assert( pRet && ppRet && *ppRet==pRet ); 4ee9e31a2d 2008-02-13 drh: if( pRet->nKey==nKey && 0==memcmp(pRet->zKey, zKey, nKey) ) break; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( op<0 && pRet ){ 4ee9e31a2d 2008-02-13 drh: assert( ppRet && *ppRet==pRet ); 4ee9e31a2d 2008-02-13 drh: *ppRet = pRet->pNext; 4ee9e31a2d 2008-02-13 drh: Th_Free(interp, pRet); 4ee9e31a2d 2008-02-13 drh: pRet = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( op>0 && !pRet ){ 4ee9e31a2d 2008-02-13 drh: pRet = (Th_HashEntry *)Th_Malloc(interp, sizeof(Th_HashEntry) + nKey); 0c99a1554a 2008-10-24 drh: pRet->zKey = (char *)&pRet[1]; 4ee9e31a2d 2008-02-13 drh: pRet->nKey = nKey; 4ee9e31a2d 2008-02-13 drh: memcpy(pRet->zKey, zKey, nKey); 4ee9e31a2d 2008-02-13 drh: pRet->pNext = pHash->a[iKey]; 4ee9e31a2d 2008-02-13 drh: pHash->a[iKey] = pRet; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return pRet; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** This function is the same as the standard strlen() function, except 4ee9e31a2d 2008-02-13 drh: ** that it returns 0 (instead of being undefined) if the argument is 4ee9e31a2d 2008-02-13 drh: ** a null pointer. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int th_strlen(const char *zStr){ 4ee9e31a2d 2008-02-13 drh: int n = 0; 4ee9e31a2d 2008-02-13 drh: if( zStr ){ 4ee9e31a2d 2008-02-13 drh: while( zStr[n] ) n++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return n; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Whitespace characters: 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** ' ' 0x20 4ee9e31a2d 2008-02-13 drh: ** '\t' 0x09 4ee9e31a2d 2008-02-13 drh: ** '\n' 0x0A 4ee9e31a2d 2008-02-13 drh: ** '\v' 0x0B 4ee9e31a2d 2008-02-13 drh: ** '\f' 0x0C 4ee9e31a2d 2008-02-13 drh: ** '\r' 0x0D 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** Whitespace characters have the 0x01 flag set. Decimal digits have the 4ee9e31a2d 2008-02-13 drh: ** 0x2 flag set. Single byte printable characters have the 0x4 flag set. 4ee9e31a2d 2008-02-13 drh: ** Alphabet characters have the 0x8 bit set. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** The special list characters have the 0x10 flag set 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** { } [ ] \ ; ' " 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** " 0x22 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static unsigned char aCharProp[256] = { 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, /* 0x0. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x1. */ 4ee9e31a2d 2008-02-13 drh: 5, 4, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x2. */ 4ee9e31a2d 2008-02-13 drh: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 20, 4, 4, 4, 4, /* 0x3. */ 4ee9e31a2d 2008-02-13 drh: 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, /* 0x4. */ 4ee9e31a2d 2008-02-13 drh: 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 4, 4, /* 0x5. */ 4ee9e31a2d 2008-02-13 drh: 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, /* 0x6. */ 4ee9e31a2d 2008-02-13 drh: 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 4, 20, 4, 4, /* 0x7. */ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x8. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x9. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE. */ 4ee9e31a2d 2008-02-13 drh: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xF. */ 4ee9e31a2d 2008-02-13 drh: }; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Clone of the standard isspace() and isdigit function/macros. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int th_isspace(char c){ 0c99a1554a 2008-10-24 drh: return (aCharProp[(unsigned char)c] & 0x01); 0c99a1554a 2008-10-24 drh: } 0c99a1554a 2008-10-24 drh: int th_isdigit(char c){ 0c99a1554a 2008-10-24 drh: return (aCharProp[(unsigned char)c] & 0x02); 4ee9e31a2d 2008-02-13 drh: } 0c99a1554a 2008-10-24 drh: int th_isspecial(char c){ 0c99a1554a 2008-10-24 drh: return (aCharProp[(unsigned char)c] & 0x11); 4ee9e31a2d 2008-02-13 drh: } 0c99a1554a 2008-10-24 drh: int th_isalnum(char c){ 0c99a1554a 2008-10-24 drh: return (aCharProp[(unsigned char)c] & 0x0A); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: #ifndef LONGDOUBLE_TYPE 4ee9e31a2d 2008-02-13 drh: # define LONGDOUBLE_TYPE long double 4ee9e31a2d 2008-02-13 drh: #endif 0c99a1554a 2008-10-24 drh: typedef char u8; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Return TRUE if z is a pure numeric string. Return FALSE if the 4ee9e31a2d 2008-02-13 drh: ** string contains any character which is not part of a number. If 4ee9e31a2d 2008-02-13 drh: ** the string is numeric and contains the '.' character, set *realnum 4ee9e31a2d 2008-02-13 drh: ** to TRUE (otherwise FALSE). 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** An empty string is considered non-numeric. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int sqlite3IsNumber(const char *z, int *realnum){ 4ee9e31a2d 2008-02-13 drh: int incr = 1; 4ee9e31a2d 2008-02-13 drh: if( *z=='-' || *z=='+' ) z += incr; 4ee9e31a2d 2008-02-13 drh: if( !th_isdigit(*(u8*)z) ){ 4ee9e31a2d 2008-02-13 drh: return 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: z += incr; 4ee9e31a2d 2008-02-13 drh: if( realnum ) *realnum = 0; 4ee9e31a2d 2008-02-13 drh: while( th_isdigit(*(u8*)z) ){ z += incr; } 4ee9e31a2d 2008-02-13 drh: if( *z=='.' ){ 4ee9e31a2d 2008-02-13 drh: z += incr; 4ee9e31a2d 2008-02-13 drh: if( !th_isdigit(*(u8*)z) ) return 0; 4ee9e31a2d 2008-02-13 drh: while( th_isdigit(*(u8*)z) ){ z += incr; } 4ee9e31a2d 2008-02-13 drh: if( realnum ) *realnum = 1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( *z=='e' || *z=='E' ){ 4ee9e31a2d 2008-02-13 drh: z += incr; 4ee9e31a2d 2008-02-13 drh: if( *z=='+' || *z=='-' ) z += incr; 4ee9e31a2d 2008-02-13 drh: if( !th_isdigit(*(u8*)z) ) return 0; 4ee9e31a2d 2008-02-13 drh: while( th_isdigit(*(u8*)z) ){ z += incr; } 4ee9e31a2d 2008-02-13 drh: if( realnum ) *realnum = 1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: return *z==0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** The string z[] is an ascii representation of a real number. 4ee9e31a2d 2008-02-13 drh: ** Convert this string to a double. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** This routine assumes that z[] really is a valid number. If it 4ee9e31a2d 2008-02-13 drh: ** is not, the result is undefined. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** This routine is used instead of the library atof() function because 4ee9e31a2d 2008-02-13 drh: ** the library atof() might want to use "," as the decimal point instead 4ee9e31a2d 2008-02-13 drh: ** of "." depending on how locale is set. But that would cause problems 4ee9e31a2d 2008-02-13 drh: ** for SQL. So this routine always uses "." regardless of locale. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: static int sqlite3AtoF(const char *z, double *pResult){ 4ee9e31a2d 2008-02-13 drh: int sign = 1; 4ee9e31a2d 2008-02-13 drh: const char *zBegin = z; 4ee9e31a2d 2008-02-13 drh: LONGDOUBLE_TYPE v1 = 0.0; 4ee9e31a2d 2008-02-13 drh: while( th_isspace(*(u8*)z) ) z++; 4ee9e31a2d 2008-02-13 drh: if( *z=='-' ){ 4ee9e31a2d 2008-02-13 drh: sign = -1; 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: }else if( *z=='+' ){ 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: while( th_isdigit(*(u8*)z) ){ 4ee9e31a2d 2008-02-13 drh: v1 = v1*10.0 + (*z - '0'); 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( *z=='.' ){ 4ee9e31a2d 2008-02-13 drh: LONGDOUBLE_TYPE divisor = 1.0; 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: while( th_isdigit(*(u8*)z) ){ 4ee9e31a2d 2008-02-13 drh: v1 = v1*10.0 + (*z - '0'); 4ee9e31a2d 2008-02-13 drh: divisor *= 10.0; 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: v1 /= divisor; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( *z=='e' || *z=='E' ){ 4ee9e31a2d 2008-02-13 drh: int esign = 1; 4ee9e31a2d 2008-02-13 drh: int eval = 0; 4ee9e31a2d 2008-02-13 drh: LONGDOUBLE_TYPE scale = 1.0; 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: if( *z=='-' ){ 4ee9e31a2d 2008-02-13 drh: esign = -1; 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: }else if( *z=='+' ){ 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: while( th_isdigit(*(u8*)z) ){ 4ee9e31a2d 2008-02-13 drh: eval = eval*10 + *z - '0'; 4ee9e31a2d 2008-02-13 drh: z++; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: while( eval>=64 ){ scale *= 1.0e+64; eval -= 64; } 4ee9e31a2d 2008-02-13 drh: while( eval>=16 ){ scale *= 1.0e+16; eval -= 16; } 4ee9e31a2d 2008-02-13 drh: while( eval>=4 ){ scale *= 1.0e+4; eval -= 4; } 4ee9e31a2d 2008-02-13 drh: while( eval>=1 ){ scale *= 1.0e+1; eval -= 1; } 4ee9e31a2d 2008-02-13 drh: if( esign<0 ){ 4ee9e31a2d 2008-02-13 drh: v1 /= scale; 4ee9e31a2d 2008-02-13 drh: }else{ 4ee9e31a2d 2008-02-13 drh: v1 *= scale; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: *pResult = sign<0 ? -v1 : v1; 4ee9e31a2d 2008-02-13 drh: return z - zBegin; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Try to convert the string passed as arguments (z, n) to an integer. 4ee9e31a2d 2008-02-13 drh: ** If successful, store the result in *piOut and return TH_OK. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the string cannot be converted to an integer, return TH_ERROR. 4ee9e31a2d 2008-02-13 drh: ** If the interp argument is not NULL, leave an error message in the 4ee9e31a2d 2008-02-13 drh: ** interpreter result too. 4ee9e31a2d 2008-02-13 drh: */ 0c99a1554a 2008-10-24 drh: int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){ 4ee9e31a2d 2008-02-13 drh: int i = 0; 4ee9e31a2d 2008-02-13 drh: int iOut = 0; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( n<0 ){ 4ee9e31a2d 2008-02-13 drh: n = th_strlen(z); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( n>0 && (z[0]=='-' || z[0]=='+') ){ 4ee9e31a2d 2008-02-13 drh: i = 1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: for(; i<n; i++){ 4ee9e31a2d 2008-02-13 drh: if( !th_isdigit(z[i]) ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "expected integer, got: \"", z, n); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iOut = iOut * 10 + (z[i] - 48); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( n>0 && z[0]=='-' ){ 4ee9e31a2d 2008-02-13 drh: iOut *= -1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *piOut = iOut; 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Try to convert the string passed as arguments (z, n) to a double. 4ee9e31a2d 2008-02-13 drh: ** If successful, store the result in *pfOut and return TH_OK. 4ee9e31a2d 2008-02-13 drh: ** 4ee9e31a2d 2008-02-13 drh: ** If the string cannot be converted to a double, return TH_ERROR. 4ee9e31a2d 2008-02-13 drh: ** If the interp argument is not NULL, leave an error message in the 4ee9e31a2d 2008-02-13 drh: ** interpreter result too. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_ToDouble( 4ee9e31a2d 2008-02-13 drh: Th_Interp *interp, 0c99a1554a 2008-10-24 drh: const char *z, 4ee9e31a2d 2008-02-13 drh: int n, 4ee9e31a2d 2008-02-13 drh: double *pfOut 4ee9e31a2d 2008-02-13 drh: ){ 4ee9e31a2d 2008-02-13 drh: if( !sqlite3IsNumber((const char *)z, 0) ){ 4ee9e31a2d 2008-02-13 drh: Th_ErrorMessage(interp, "expected number, got: \"", z, n); 4ee9e31a2d 2008-02-13 drh: return TH_ERROR; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: sqlite3AtoF((const char *)z, pfOut); 4ee9e31a2d 2008-02-13 drh: return TH_OK; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Set the result of the interpreter to the th1 representation of 4ee9e31a2d 2008-02-13 drh: ** the integer iVal and return TH_OK. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_SetResultInt(Th_Interp *interp, int iVal){ 4ee9e31a2d 2008-02-13 drh: int isNegative = 0; 0c99a1554a 2008-10-24 drh: char zBuf[32]; 0c99a1554a 2008-10-24 drh: char *z = &zBuf[32]; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: if( iVal<0 ){ 4ee9e31a2d 2008-02-13 drh: isNegative = 1; 4ee9e31a2d 2008-02-13 drh: iVal = iVal * -1; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: *(--z) = '\0'; 0c99a1554a 2008-10-24 drh: *(--z) = (char)(48+(iVal%10)); 4ee9e31a2d 2008-02-13 drh: while( (iVal = (iVal/10))>0 ){ 0c99a1554a 2008-10-24 drh: *(--z) = (char)(48+(iVal%10)); 4ee9e31a2d 2008-02-13 drh: assert(z>zBuf); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: if( isNegative ){ 4ee9e31a2d 2008-02-13 drh: *(--z) = '-'; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: return Th_SetResult(interp, z, -1); 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* 4ee9e31a2d 2008-02-13 drh: ** Set the result of the interpreter to the th1 representation of 4ee9e31a2d 2008-02-13 drh: ** the double fVal and return TH_OK. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: int Th_SetResultDouble(Th_Interp *interp, double fVal){ 4ee9e31a2d 2008-02-13 drh: int i; /* Iterator variable */ 4ee9e31a2d 2008-02-13 drh: double v = fVal; /* Input value */ 0c99a1554a 2008-10-24 drh: char zBuf[128]; /* Output buffer */ 0c99a1554a 2008-10-24 drh: char *z = zBuf; /* Output cursor */ 4ee9e31a2d 2008-02-13 drh: int iDot = 0; /* Digit after which to place decimal point */ 4ee9e31a2d 2008-02-13 drh: int iExp = 0; /* Exponent (NN in eNN) */ 0c99a1554a 2008-10-24 drh: const char *zExp; /* String representation of iExp */ 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Precision: */ 4ee9e31a2d 2008-02-13 drh: #define INSIGNIFICANT 0.000000000001 4ee9e31a2d 2008-02-13 drh: #define ROUNDER 0.0000000000005 4ee9e31a2d 2008-02-13 drh: double insignificant = INSIGNIFICANT; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* If the real value is negative, write a '-' character to the 4ee9e31a2d 2008-02-13 drh: * output and transform v to the corresponding positive number. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( v<0.0 ){ 4ee9e31a2d 2008-02-13 drh: *z++ = '-'; 4ee9e31a2d 2008-02-13 drh: v *= -1.0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Normalize v to a value between 1.0 and 10.0. Integer 4ee9e31a2d 2008-02-13 drh: * variable iExp is set to the exponent. i.e the original 4ee9e31a2d 2008-02-13 drh: * value is (v * 10^iExp) (or the negative thereof). 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( v>0.0 ){ 4ee9e31a2d 2008-02-13 drh: while( (v+ROUNDER)>=10.0 ) { iExp++; v *= 0.1; } 4ee9e31a2d 2008-02-13 drh: while( (v+ROUNDER)<1.0 ) { iExp--; v *= 10.0; } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: v += ROUNDER; 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* For a small (<12) positive exponent, move the decimal point 4ee9e31a2d 2008-02-13 drh: * instead of using the "eXX" notation. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( iExp>0 && iExp<12 ){ 4ee9e31a2d 2008-02-13 drh: iDot = iExp; 4ee9e31a2d 2008-02-13 drh: iExp = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* For a small (>-4) negative exponent, write leading zeroes 4ee9e31a2d 2008-02-13 drh: * instead of using the "eXX" notation. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( iExp<0 && iExp>-4 ){ 4ee9e31a2d 2008-02-13 drh: *z++ = '0'; 4ee9e31a2d 2008-02-13 drh: *z++ = '.'; 4ee9e31a2d 2008-02-13 drh: for(i=0; i>(iExp+1); i--){ 4ee9e31a2d 2008-02-13 drh: *z++ = '0'; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: iDot = -1; 4ee9e31a2d 2008-02-13 drh: iExp = 0; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* Output the digits in real value v. The value of iDot determines 4ee9e31a2d 2008-02-13 drh: * where (if at all) the decimal point is placed. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: for(i=0; i<=(iDot+1) || v>=insignificant; i++){ 0c99a1554a 2008-10-24 drh: *z++ = (char)(48 + (int)v); 4ee9e31a2d 2008-02-13 drh: v = (v - ((double)(int)v)) * 10.0; 4ee9e31a2d 2008-02-13 drh: insignificant *= 10.0; 4ee9e31a2d 2008-02-13 drh: if( iDot==i ){ 4ee9e31a2d 2008-02-13 drh: *z++ = '.'; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: /* If the exponent is not zero, add the "eXX" notation to the 4ee9e31a2d 2008-02-13 drh: * end of the string. 4ee9e31a2d 2008-02-13 drh: */ 4ee9e31a2d 2008-02-13 drh: if( iExp!=0 ){ 4ee9e31a2d 2008-02-13 drh: *z++ = 'e'; 4ee9e31a2d 2008-02-13 drh: Th_SetResultInt(interp, iExp); 4ee9e31a2d 2008-02-13 drh: zExp = Th_GetResult(interp, 0); 4ee9e31a2d 2008-02-13 drh: for(i=0; zExp[i]; i++){ 4ee9e31a2d 2008-02-13 drh: *z++ = zExp[i]; 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: } 4ee9e31a2d 2008-02-13 drh: 4ee9e31a2d 2008-02-13 drh: *z = '\0'; 4ee9e31a2d 2008-02-13 drh: return Th_SetResult(interp, zBuf, -1); 4ee9e31a2d 2008-02-13 drh: }