/* ** This file contains the implementation of all of the TH language ** built-in commands. ** ** All built-in commands are implemented using the public interface ** declared in th.h, so this file serves as both a part of the language ** implementation and an example of how to extend the language with ** new commands. */ #include "config.h" #include "th.h" #include <string.h> #include <assert.h> int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1); return TH_ERROR; } /* ** Syntax: ** ** catch script ?varname? */ static int catch_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "catch script ?varname?"); } rc = Th_Eval(interp, 0, argv[1], -1); if( argc==3 ){ int nResult; const char *zResult = Th_GetResult(interp, &nResult); Th_SetVar(interp, argv[2], TH1_LEN(argl[2]), zResult, nResult); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH Syntax: ** ** if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN? */ static int if_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc = TH_OK; int iCond; /* Result of evaluating expression */ int i; const char *zResult; int nResult; if( argc<3 ){ goto wrong_args; } for(i=0; i<argc && rc==TH_OK; i+=3){ if( i>argc-3 ){ i = argc-3; iCond = 1; }else{ if( TH_OK!=Th_Expr(interp, argv[i+1], argl[i+1]) ){ return TH_ERROR; } zResult = Th_GetResult(interp, &nResult); rc = Th_ToInt(interp, zResult, nResult, &iCond); } if( iCond && rc==TH_OK ){ rc = Th_Eval(interp, 0, argv[i+2], -1); break; } } return rc; wrong_args: return Th_WrongNumArgs(interp, "if ..."); } /* ** TH Syntax: ** ** expr expr */ static int expr_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "expr expression"); } return Th_Expr(interp, argv[1], argl[1]); } /* ** Evaluate the th1 script (zBody, nBody) in the local stack frame. ** Return the result of the evaluation, except if the result ** is TH_CONTINUE, return TH_OK instead. */ static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){ int rc = Th_Eval(interp, 0, zBody, nBody); if( rc==TH_CONTINUE ){ rc = TH_OK; } return rc; } /* ** TH Syntax: ** ** for init condition incr script */ static int for_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; int iCond; if( argc!=5 ){ return Th_WrongNumArgs(interp, "for init condition incr script"); } /* Evaluate the 'init' script */ rc = Th_Eval(interp, 0, argv[1], -1); while( rc==TH_OK && TH_OK==(rc = Th_Expr(interp, argv[2], -1)) && TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond)) && iCond && TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4])) ){ rc = Th_Eval(interp, 0, argv[3], -1); } if( rc==TH_BREAK ) rc = TH_OK; return rc; } /* ** TH Syntax: ** ** foreach VARLIST LIST SCRIPT */ static int foreach_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char **azVar = 0; int *anVar; int nVar; char **azValue = 0; int *anValue; int nValue; int ii, jj; int bTaint = 0; if( argc!=4 ){ return Th_WrongNumArgs(interp, "foreach varlist list script"); } rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar); if( rc ) return rc; TH1_XFER_TAINT(bTaint, argl[2]); rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue); for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){ for(jj=0; jj<nVar; jj++){ int x = anValue[ii+jj]; TH1_XFER_TAINT(x, bTaint); Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], x); } rc = eval_loopbody(interp, argv[3], argl[3]); } if( rc==TH_BREAK ) rc = TH_OK; Th_Free(interp, azVar); Th_Free(interp, azValue); return rc; } /* ** TH Syntax: ** ** list ?arg1 ?arg2? ...? */ static int list_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ char *zList = 0; int nList = 0; int i; int bTaint = 0; for(i=1; i<argc; i++){ TH1_XFER_TAINT(bTaint,argl[i]); Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); } TH1_XFER_TAINT(nList, bTaint); Th_SetResult(interp, zList, nList); Th_Free(interp, zList); return TH_OK; } /* ** TH Syntax: ** ** lappend var ?arg1? ?arg2? ...? ** ** Interpret the content of variable var as a list. Create var if it ** does not already exist. Append each argument as a new list element. */ static int lappend_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ char *zList = 0; int nList = 0; int i, rc; int bTaint = 0; if( argc<2 ){ return Th_WrongNumArgs(interp, "lappend var ..."); } rc = Th_GetVar(interp, argv[1], argl[1]); if( rc==TH_OK ){ zList = Th_TakeResult(interp, &nList); } TH1_XFER_TAINT(bTaint, nList); for(i=2; i<argc; i++){ TH1_XFER_TAINT(bTaint, argl[i]); Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); } TH1_XFER_TAINT(nList, bTaint); Th_SetVar(interp, argv[1], argl[1], zList, nList); Th_SetResult(interp, zList, nList); Th_Free(interp, zList); return TH_OK; } /* ** TH Syntax: ** ** lindex list index */ static int lindex_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iElem; int rc; char **azElem; int *anElem; int nCount; int bTaint = 0; if( argc!=3 ){ return Th_WrongNumArgs(interp, "lindex list index"); } if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){ return TH_ERROR; } TH1_XFER_TAINT(bTaint, argl[1]); rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); if( rc==TH_OK ){ if( iElem<nCount && iElem>=0 ){ int sz = anElem[iElem]; TH1_XFER_TAINT(sz, bTaint); Th_SetResult(interp, azElem[iElem], sz); }else{ Th_SetResult(interp, 0, 0); } Th_Free(interp, azElem); } return rc; } /* ** TH Syntax: ** ** llength list */ static int llength_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int nElem; int rc; if( argc!=2 ){ return Th_WrongNumArgs(interp, "llength list"); } rc = Th_SplitList(interp, argv[1], argl[1], 0, 0, &nElem); if( rc==TH_OK ){ Th_SetResultInt(interp, nElem); } return rc; } /* ** TH Syntax: ** ** lsearch list string */ static int lsearch_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char **azElem; int *anElem; int nCount; int i; if( argc!=3 ){ return Th_WrongNumArgs(interp, "lsearch list string"); } rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); if( rc==TH_OK ){ int nn = TH1_LEN(argl[2]); Th_SetResultInt(interp, -1); for(i=0; i<nCount; i++){ if( TH1_LEN(anElem[i])==nn && 0==memcmp(azElem[i], argv[2], nn) ){ Th_SetResultInt(interp, i); break; } } Th_Free(interp, azElem); } return rc; } /* ** TH Syntax: ** ** set varname ?value? */ static int set_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "set varname ?value?"); } if( argc==3 ){ Th_SetVar(interp, argv[1], argl[1], argv[2], argl[2]); } return Th_GetVar(interp, argv[1], argl[1]); } /* ** When a new command is created using the built-in [proc] command, an ** instance of the following structure is allocated and populated. A ** pointer to the structure is passed as the context (second) argument ** to function proc_call1() when the new command is executed. */ typedef struct ProcDefn ProcDefn; struct ProcDefn { int nParam; /* Number of formal (non "args") parameters */ char **azParam; /* Parameter names */ int *anParam; /* Lengths of parameter names */ char **azDefault; /* Default values */ int *anDefault; /* Lengths of default values */ int hasArgs; /* True if there is an "args" parameter */ char *zProgram; /* Body of proc */ int nProgram; /* Number of bytes at zProgram */ char *zUsage; /* Usage message */ int nUsage; /* Number of bytes at zUsage */ }; /* This structure is used to temporarily store arguments passed to an ** invocation of a command created using [proc]. A pointer to an ** instance is passed as the second argument to the proc_call2() function. */ typedef struct ProcArgs ProcArgs; struct ProcArgs { int argc; const char **argv; int *argl; }; /* ** Each time a command created using [proc] is invoked, a new ** th1 stack frame is allocated (for the proc's local variables) and ** this function invoked. ** ** Argument pContext1 points to the associated ProcDefn structure. ** Argument pContext2 points to a ProcArgs structure that contains ** the arguments passed to this specific invocation of the proc. */ static int proc_call2(Th_Interp *interp, void *pContext1, void *pContext2){ int i; ProcDefn *p = (ProcDefn *)pContext1; ProcArgs *pArgs = (ProcArgs *)pContext2; /* Check if there are the right number of arguments. If there are ** not, generate a usage message for the command. */ if( (pArgs->argc>(p->nParam+1) && !p->hasArgs) || (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1]) ){ char *zUsage = 0; int nUsage = 0; Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]); Th_StringAppend(interp, &zUsage, &nUsage, p->zUsage, p->nUsage); Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"", 1); Th_WrongNumArgs(interp, zUsage); Th_Free(interp, zUsage); return TH_ERROR; } /* Populate the formal proc parameters. */ for(i=0; i<p->nParam; i++){ const char *zVal; int nVal; if( pArgs->argc>(i+1) ){ zVal = pArgs->argv[i+1]; nVal = pArgs->argl[i+1]; }else{ zVal = p->azDefault[i]; nVal = p->anDefault[i]; } Th_SetVar(interp, p->azParam[i], p->anParam[i], zVal, nVal); } /* Populate the "args" parameter, if it exists */ if( p->hasArgs ){ char *zArgs = 0; int nArgs = 0; for(i=p->nParam+1; i<pArgs->argc; i++){ Th_ListAppend(interp, &zArgs, &nArgs, pArgs->argv[i], pArgs->argl[i]); } Th_SetVar(interp, (const char *)"args", -1, zArgs, nArgs); if(zArgs){ Th_Free(interp, zArgs); } } Th_SetResult(interp, 0, 0); return Th_Eval(interp, 0, p->zProgram, p->nProgram); } /* ** This function is the command callback registered for all commands ** created using the [proc] command. The second argument, pContext, ** is a pointer to the associated ProcDefn structure. */ static int proc_call1( Th_Interp *interp, void *pContext, int argc, const char **argv, int *argl ){ int rc; ProcDefn *p = (ProcDefn *)pContext; ProcArgs procargs; /* Call function proc_call2(), which will call Th_Eval() to evaluate ** the body of the [proc], in a new Th stack frame. This is so that ** the proc body has its own local variable context. */ procargs.argc = argc; procargs.argv = argv; procargs.argl = argl; rc = Th_InFrame(interp, proc_call2, (void *)p, (void *)&procargs); if( rc==TH_RETURN ){ rc = TH_OK; } if( rc==TH_RETURN2 ){ rc = TH_RETURN; } return rc; } /* ** This function is registered as the delete callback for all commands ** created using the built-in [proc] command. It is called automatically ** when a command created using [proc] is deleted. ** ** It frees the ProcDefn structure allocated when the command was created. */ static void proc_del(Th_Interp *interp, void *pContext){ ProcDefn *p = (ProcDefn *)pContext; Th_Free(interp, (void *)p->zUsage); Th_Free(interp, (void *)p); } /* ** TH Syntax: ** ** proc name arglist code */ static int proc_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; const char *zName; ProcDefn *p; int nByte; int i; char *zSpace; char **azParam; int *anParam; int nParam; char *zUsage = 0; /* Build up a usage message here */ int nUsage = 0; /* Number of bytes at zUsage */ if( argc!=4 ){ return Th_WrongNumArgs(interp, "proc name arglist code"); } if( Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), &azParam, &anParam, &nParam) ){ return TH_ERROR; } /* Allocate the new ProcDefn structure. */ nByte = sizeof(ProcDefn) + /* ProcDefn structure */ (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */ (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */ TH1_LEN(argl[3]) + /* zProgram */ TH1_LEN(argl[2]); /* Space for copies of param names and dflt values */ p = (ProcDefn *)Th_Malloc(interp, nByte); /* If the last parameter in the parameter list is "args", then set the ** ProcDefn.hasArgs flag. The "args" parameter does not require an ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. */ if( nParam>0 ){ if( TH1_LEN(anParam[nParam-1])==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){ p->hasArgs = 1; nParam--; } } p->nParam = nParam; p->azParam = (char **)&p[1]; p->anParam = (int *)&p->azParam[nParam]; p->azDefault = (char **)&p->anParam[nParam]; p->anDefault = (int *)&p->azDefault[nParam]; p->zProgram = (char *)&p->anDefault[nParam]; memcpy(p->zProgram, argv[3], TH1_LEN(argl[3])); p->nProgram = TH1_LEN(argl[3]); zSpace = &p->zProgram[p->nProgram]; for(i=0; i<nParam; i++){ char **az; int *an; int n; if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){ goto error_out; } if( n<1 || n>2 ){ const char expected[] = "expected parameter, got \""; Th_ErrorMessage(interp, expected, azParam[i], anParam[i]); Th_Free(interp, az); goto error_out; } p->anParam[i] = an[0]; p->azParam[i] = zSpace; memcpy(zSpace, az[0], an[0]); zSpace += an[0]; if( n==2 ){ p->anDefault[i] = an[1]; p->azDefault[i] = zSpace; memcpy(zSpace, az[1], an[1]); zSpace += an[1]; } Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ", 1); if( n==2 ){ Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"?", 1); Th_StringAppend(interp, &zUsage, &nUsage, az[0], an[0]); Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"?", 1); }else{ Th_StringAppend(interp, &zUsage, &nUsage, az[0], an[0]); } Th_Free(interp, az); } assert( zSpace-(char *)p<=nByte ); /* If there is an "args" parameter, append it to the end of the usage ** message. Set ProcDefn.zUsage to point at the usage message. It will ** be freed along with the rest of the proc-definition by proc_del(). */ if( p->hasArgs ){ Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ?args...?", -1); } p->zUsage = zUsage; p->nUsage = nUsage; /* Register the new command with the th1 interpreter. */ zName = argv[1]; rc = Th_CreateCommand(interp, zName, proc_call1, (void *)p, proc_del); if( rc==TH_OK ){ Th_SetResult(interp, 0, 0); } Th_Free(interp, azParam); return TH_OK; error_out: Th_Free(interp, azParam); Th_Free(interp, zUsage); return TH_ERROR; } /* ** TH Syntax: ** ** rename oldcmd newcmd */ static int rename_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=3 ){ return Th_WrongNumArgs(interp, "rename oldcmd newcmd"); } return Th_RenameCommand(interp, argv[1], TH1_LEN(argl[1]), argv[2], TH1_LEN(argl[2])); } /* ** TH Syntax: ** ** break ?value...? ** continue ?value...? ** ok ?value...? ** error ?value...? */ static int simple_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "return ?value?"); } if( argc==2 ){ Th_SetResult(interp, argv[1], argl[1]); } return FOSSIL_PTR_TO_INT(ctx); } /* ** TH Syntax: ** ** return ?-code code? ?value? */ static int return_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iCode = TH_RETURN; if( argc<1 || argc>4 ){ return Th_WrongNumArgs(interp, "return ?-code code? ?value?"); } if( argc>2 ){ int rc = Th_ToInt(interp, argv[2], argl[2], &iCode); if( rc!=TH_OK ){ return rc; } } if( argc==2 || argc==4 ){ Th_SetResult(interp, argv[argc-1], argl[argc-1]); } return iCode; } /* ** TH Syntax: ** ** string compare STRING1 STRING2 */ static int string_compare_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ const char *zRight; int nRight; const char *zLeft; int nLeft; int i; int iRes = 0; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string compare str1 str2"); } zLeft = argv[2]; nLeft = TH1_LEN(argl[2]); zRight = argv[3]; nRight = TH1_LEN(argl[3]); for(i=0; iRes==0 && i<nLeft && i<nRight; i++){ iRes = zLeft[i]-zRight[i]; } if( iRes==0 ){ iRes = nLeft-nRight; } if( iRes<0 ) iRes = -1; if( iRes>0 ) iRes = 1; return Th_SetResultInt(interp, iRes); } /* ** TH Syntax: ** ** string first NEEDLE HAYSTACK */ static int string_first_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int nNeedle; int nHaystack; int iRes = -1; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string first needle haystack"); } nNeedle = TH1_LEN(argl[2]); nHaystack = TH1_LEN(argl[3]); if( nNeedle && nHaystack && nNeedle<=nHaystack ){ const char *zNeedle = argv[2]; const char *zHaystack = argv[3]; int i; for(i=0; i<=(nHaystack-nNeedle); i++){ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){ iRes = i; break; } } } return Th_SetResultInt(interp, iRes); } /* ** TH Syntax: ** ** string index STRING INDEX */ static int string_index_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iIndex; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string index string index"); } if( TH1_LEN(argl[3])==3 && 0==memcmp("end", argv[3], 3) ){ iIndex = TH1_LEN(argl[2])-1; }else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){ Th_ErrorMessage( interp, "Expected \"end\" or integer, got:", argv[3], argl[3]); return TH_ERROR; } if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){ int sz = 1; TH1_XFER_TAINT(sz, argl[2]); return Th_SetResult(interp, &argv[2][iIndex], sz); }else{ return Th_SetResult(interp, 0, 0); } } /* ** TH Syntax: ** ** string is CLASS STRING */ static int string_is_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=4 ){ return Th_WrongNumArgs(interp, "string is class string"); } if( TH1_LEN(argl[2])==5 && 0==memcmp(argv[2], "alnum", 5) ){ int i; int iRes = 1; for(i=0; i<TH1_LEN(argl[3]); i++){ if( !th_isalnum(argv[3][i]) ){ iRes = 0; } } return Th_SetResultInt(interp, iRes); }else if( TH1_LEN(argl[2])==6 && 0==memcmp(argv[2], "double", 6) ){ double fVal; if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ return Th_SetResultInt(interp, 1); } return Th_SetResultInt(interp, 0); }else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "integer", 7) ){ int iVal; if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ return Th_SetResultInt(interp, 1); } return Th_SetResultInt(interp, 0); }else if( TH1_LEN(argl[2])==4 && 0==memcmp(argv[2], "list", 4) ){ if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ return Th_SetResultInt(interp, 1); } return Th_SetResultInt(interp, 0); }else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "tainted", 7) ){ return Th_SetResultInt(interp, TH1_TAINTED(argl[3])); }else{ Th_ErrorMessage(interp, "Expected alnum, double, integer, list, or tainted, got:", argv[2], TH1_LEN(argl[2])); return TH_ERROR; } } /* ** TH Syntax: ** ** string last NEEDLE HAYSTACK */ static int string_last_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int nNeedle; int nHaystack; int iRes = -1; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string last needle haystack"); } nNeedle = TH1_LEN(argl[2]); nHaystack = TH1_LEN(argl[3]); if( nNeedle && nHaystack && nNeedle<=nHaystack ){ const char *zNeedle = argv[2]; const char *zHaystack = argv[3]; int i; for(i=nHaystack-nNeedle; i>=0; i--){ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){ iRes = i; break; } } } return Th_SetResultInt(interp, iRes); } /* ** TH Syntax: ** ** string length STRING */ static int string_length_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=3 ){ return Th_WrongNumArgs(interp, "string length string"); } return Th_SetResultInt(interp, TH1_LEN(argl[2])); } /* ** TH Syntax: ** ** string match PATTERN STRING ** */ static int string_match_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ extern char *fossil_strndup(const char*,int); extern void fossil_free(void*); char *zPat, *zStr; int rc; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string match pattern string"); } zPat = fossil_strndup(argv[2],TH1_LEN(argl[2])); zStr = fossil_strndup(argv[3],TH1_LEN(argl[3])); rc = sqlite3_strglob(zPat,zStr); fossil_free(zPat); fossil_free(zStr); return Th_SetResultInt(interp, !rc); } /* ** TH Syntax: ** ** string range STRING FIRST LAST */ static int string_range_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iStart; int iEnd; int sz; if( argc!=5 ){ return Th_WrongNumArgs(interp, "string range string first last"); } if( TH1_LEN(argl[4])==3 && 0==memcmp("end", argv[4], 3) ){ iEnd = TH1_LEN(argl[2]); }else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){ Th_ErrorMessage( interp, "Expected \"end\" or integer, got:", argv[4], TH1_LEN(argl[4])); return TH_ERROR; } if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){ return TH_ERROR; } if( iStart<0 ) iStart = 0; if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1; if( iStart>iEnd ) iEnd = iStart-1; sz = iEnd - iStart + 1; TH1_XFER_TAINT(sz, argl[2]); return Th_SetResult(interp, &argv[2][iStart], sz); } /* ** TH Syntax: ** ** string repeat STRING COUNT */ static int string_repeat_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int n; int i; int sz; long long int nByte; char *zByte; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string repeat string n"); } if( Th_ToInt(interp, argv[3], argl[3], &n) ){ return TH_ERROR; } nByte = n; sz = TH1_LEN(argl[2]); nByte *= sz; TH1_SIZECHECK(nByte+1); zByte = Th_Malloc(interp, nByte+1); for(i=0; i<nByte; i+=sz){ memcpy(&zByte[i], argv[2], sz); } n = nByte; TH1_XFER_TAINT(n, argl[2]); Th_SetResult(interp, zByte, n); Th_Free(interp, zByte); return TH_OK; } /* ** TH Syntax: ** ** string trim STRING ** string trimleft STRING ** string trimright STRING */ static int string_trim_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int n; const char *z; if( argc!=3 ){ return Th_WrongNumArgs(interp, "string trim string"); } z = argv[2]; n = TH1_LEN(argl[2]); if( TH1_LEN(argl[1])<5 || argv[1][4]=='l' ){ while( n && th_isspace(z[0]) ){ z++; n--; } } if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){ while( n && th_isspace(z[n-1]) ){ n--; } } TH1_XFER_TAINT(n, argl[2]); Th_SetResult(interp, z, n); return TH_OK; } /* ** TH Syntax: ** ** info exists VARNAME */ static int info_exists_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; if( argc!=3 ){ return Th_WrongNumArgs(interp, "info exists var"); } rc = Th_ExistsVar(interp, argv[2], TH1_LEN(argl[2])); Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH Syntax: ** ** info commands */ static int info_commands_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char *zElem = 0; int nElem = 0; if( argc!=2 ){ return Th_WrongNumArgs(interp, "info commands"); } rc = Th_ListAppendCommands(interp, &zElem, &nElem); if( rc!=TH_OK ){ return rc; } Th_SetResult(interp, zElem, nElem); if( zElem ) Th_Free(interp, zElem); return TH_OK; } /* ** TH Syntax: ** ** info vars */ static int info_vars_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char *zElem = 0; int nElem = 0; if( argc!=2 ){ return Th_WrongNumArgs(interp, "info vars"); } rc = Th_ListAppendVariables(interp, &zElem, &nElem); if( rc!=TH_OK ){ return rc; } Th_SetResult(interp, zElem, nElem); if( zElem ) Th_Free(interp, zElem); return TH_OK; } /* ** TH Syntax: ** ** array exists VARNAME */ static int array_exists_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; if( argc!=3 ){ return Th_WrongNumArgs(interp, "array exists var"); } rc = Th_ExistsArrayVar(interp, argv[2], TH1_LEN(argl[2])); Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH Syntax: ** ** array names VARNAME */ static int array_names_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char *zElem = 0; int nElem = 0; if( argc!=3 ){ return Th_WrongNumArgs(interp, "array names varname"); } rc = Th_ListAppendArray(interp, argv[2], TH1_LEN(argl[2]), &zElem, &nElem); if( rc!=TH_OK ){ return rc; } Th_SetResult(interp, zElem, nElem); if( zElem ) Th_Free(interp, zElem); return TH_OK; } /* ** TH Syntax: ** ** unset VARNAME */ static int unset_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "unset var"); } return Th_UnsetVar(interp, argv[1], TH1_LEN(argl[1])); } int Th_CallSubCommand( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl, const Th_SubCommand *aSub ){ if( argc>1 ){ int i; for(i=0; aSub[i].zName; i++){ const char *zName = aSub[i].zName; if( th_strlen(zName)==TH1_LEN(argl[1]) && 0==memcmp(zName, argv[1], TH1_LEN(argl[1])) ){ return aSub[i].xProc(interp, ctx, argc, argv, argl); } } } if(argc<2){ Th_ErrorMessage(interp, "Expected sub-command for", argv[0], TH1_LEN(argl[0])); }else{ Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], TH1_LEN(argl[1])); } return TH_ERROR; } /* ** TH Syntax: ** ** string compare STR1 STR2 ** string first NEEDLE HAYSTACK ?STARTINDEX? ** string index STRING INDEX ** string is CLASS STRING ** string last NEEDLE HAYSTACK ?STARTINDEX? ** string length STRING ** string range STRING FIRST LAST ** string repeat STRING COUNT ** string trim STRING ** string trimleft STRING ** string trimright STRING */ static int string_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "compare", string_compare_command }, { "first", string_first_command }, { "index", string_index_command }, { "is", string_is_command }, { "last", string_last_command }, { "length", string_length_command }, { "match", string_match_command }, { "range", string_range_command }, { "repeat", string_repeat_command }, { "trim", string_trim_command }, { "trimleft", string_trim_command }, { "trimright", string_trim_command }, { 0, 0 } }; return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); } /* ** TH Syntax: ** ** info commands ** info exists VARNAME ** info vars */ static int info_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "commands", info_commands_command }, { "exists", info_exists_command }, { "vars", info_vars_command }, { 0, 0 } }; return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); } /* ** TH Syntax: ** ** array exists VARNAME ** array names VARNAME */ static int array_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "exists", array_exists_command }, { "names", array_names_command }, { 0, 0 } }; return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); } /* ** Convert the script level frame specification (used by the commands ** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as ** used by Th_LinkVar() and Th_Eval(). If successful, write the integer ** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR ** and leave an error message in the interpreter result. */ static int thToFrame( Th_Interp *interp, const char *zFrame, int nFrame, int *piFrame ){ int iFrame; if( th_isdigit(zFrame[0]) ){ int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame); if( rc!=TH_OK ) return rc; iFrame = iFrame * -1; }else if( zFrame[0]=='#' ){ int rc = Th_ToInt(interp, &zFrame[1], nFrame-1, &iFrame); if( rc!=TH_OK ) return rc; iFrame = iFrame + 1; }else{ return TH_ERROR; } *piFrame = iFrame; return TH_OK; } /* ** TH Syntax: ** ** uplevel ?LEVEL? SCRIPT */ static int uplevel_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iFrame = -1; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "uplevel ?level? script..."); } if( argc==3 && TH_OK!=thToFrame(interp, argv[1], TH1_LEN(argl[1]), &iFrame) ){ return TH_ERROR; } return Th_Eval(interp, iFrame, argv[argc-1], -1); } /* ** TH Syntax: ** ** upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...? */ static int upvar_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iVar = 1; int iFrame = -1; int rc = TH_OK; int i; if( TH_OK==thToFrame(0, argv[1], TH1_LEN(argl[1]), &iFrame) ){ iVar++; } if( argc==iVar || (argc-iVar)%2 ){ return Th_WrongNumArgs(interp, "upvar frame othervar myvar ?othervar myvar...?"); } for(i=iVar; rc==TH_OK && i<argc; i=i+2){ rc = Th_LinkVar(interp, argv[i+1], TH1_LEN(argl[i+1]), iFrame, argv[i], TH1_LEN(argl[i])); } return rc; } /* ** TH Syntax: ** ** breakpoint ARGS ** ** This command does nothing at all. Its purpose in life is to serve ** as a point for setting breakpoints in a debugger. */ static int breakpoint_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static unsigned int cnt = 0; if( (cnt++)==0xffffffff ) printf("too many TH3 breakpoints\n"); return TH_OK; } /* ** Register the built-in th1 language commands with interpreter interp. ** Usually this is called soon after interpreter creation. */ int th_register_language(Th_Interp *interp){ /* Array of built-in commands. */ struct _Command { const char *zName; Th_CommandProc xProc; void *pContext; } aCommand[] = { {"array", array_command, 0}, {"catch", catch_command, 0}, {"expr", expr_command, 0}, {"for", for_command, 0}, {"foreach", foreach_command, 0}, {"if", if_command, 0}, {"info", info_command, 0}, {"lappend", lappend_command, 0}, {"lindex", lindex_command, 0}, {"list", list_command, 0}, {"llength", llength_command, 0}, {"lsearch", lsearch_command, 0}, {"proc", proc_command, 0}, {"rename", rename_command, 0}, {"set", set_command, 0}, {"string", string_command, 0}, {"unset", unset_command, 0}, {"uplevel", uplevel_command, 0}, {"upvar", upvar_command, 0}, {"breakpoint", breakpoint_command, 0}, {"return", return_command, 0}, {"break", simple_command, (void *)TH_BREAK}, {"continue", simple_command, (void *)TH_CONTINUE}, {"error", simple_command, (void *)TH_ERROR}, {0, 0, 0} }; size_t i; /* Add the language commands. */ for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ void *ctx; if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; ctx = aCommand[i].pContext; Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0); } return TH_OK; }