File Annotation
Not logged in
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: }