1 
2 /*
3 ** This file contains the implementation of all of the TH language
4 ** built-in commands.
5 **
6 ** All built-in commands are implemented using the public interface
7 ** declared in th.h, so this file serves as both a part of the language
8 ** implementation and an example of how to extend the language with
9 ** new commands.
10 */
11 
12 #include "config.h"
13 #include "th.h"
14 #include <string.h>
15 #include <assert.h>
16 
Th_WrongNumArgs(Th_Interp * interp,const char * zMsg)17 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){
18   Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1);
19   return TH_ERROR;
20 }
21 
22 /*
23 ** Syntax:
24 **
25 **   catch script ?varname?
26 */
catch_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)27 static int catch_command(
28   Th_Interp *interp,
29   void *ctx,
30   int argc,
31   const char **argv,
32   int *argl
33 ){
34   int rc;
35 
36   if( argc!=2 && argc!=3 ){
37     return Th_WrongNumArgs(interp, "catch script ?varname?");
38   }
39 
40   rc = Th_Eval(interp, 0, argv[1], -1);
41   if( argc==3 ){
42     int nResult;
43     const char *zResult = Th_GetResult(interp, &nResult);
44     Th_SetVar(interp, argv[2], argl[2], zResult, nResult);
45   }
46 
47   Th_SetResultInt(interp, rc);
48   return TH_OK;
49 }
50 
51 /*
52 ** TH Syntax:
53 **
54 **   if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN?
55 */
if_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)56 static int if_command(
57   Th_Interp *interp,
58   void *ctx,
59   int argc,
60   const char **argv,
61   int *argl
62 ){
63   int rc = TH_OK;
64 
65   int iCond;           /* Result of evaluating expression */
66   int i;
67 
68   const char *zResult;
69   int nResult;
70 
71   if( argc<3 ){
72     goto wrong_args;
73   }
74 
75   for(i=0; i<argc && rc==TH_OK; i+=3){
76     if( i>argc-3 ){
77       i = argc-3;
78       iCond = 1;
79     }else{
80       if( TH_OK!=Th_Expr(interp, argv[i+1], argl[i+1]) ){
81         return TH_ERROR;
82       }
83       zResult = Th_GetResult(interp, &nResult);
84       rc = Th_ToInt(interp, zResult, nResult, &iCond);
85     }
86     if( iCond && rc==TH_OK ){
87       rc = Th_Eval(interp, 0, argv[i+2], -1);
88       break;
89     }
90   }
91 
92   return rc;
93 
94 wrong_args:
95   return Th_WrongNumArgs(interp, "if ...");
96 }
97 
98 /*
99 ** TH Syntax:
100 **
101 **   expr expr
102 */
expr_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)103 static int expr_command(
104   Th_Interp *interp,
105   void *ctx,
106   int argc,
107   const char **argv,
108   int *argl
109 ){
110   if( argc!=2 ){
111     return Th_WrongNumArgs(interp, "expr expression");
112   }
113 
114   return Th_Expr(interp, argv[1], argl[1]);
115 }
116 
117 /*
118 ** Evaluate the th1 script (zBody, nBody) in the local stack frame.
119 ** Return the result of the evaluation, except if the result
120 ** is TH_CONTINUE, return TH_OK instead.
121 */
eval_loopbody(Th_Interp * interp,const char * zBody,int nBody)122 static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){
123   int rc = Th_Eval(interp, 0, zBody, nBody);
124   if( rc==TH_CONTINUE ){
125     rc = TH_OK;
126   }
127   return rc;
128 }
129 
130 /*
131 ** TH Syntax:
132 **
133 **   for init condition incr script
134 */
for_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)135 static int for_command(
136   Th_Interp *interp,
137   void *ctx,
138   int argc,
139   const char **argv,
140   int *argl
141 ){
142   int rc;
143   int iCond;
144 
145   if( argc!=5 ){
146     return Th_WrongNumArgs(interp, "for init condition incr script");
147   }
148 
149   /* Evaluate the 'init' script */
150   rc = Th_Eval(interp, 0, argv[1], -1);
151 
152   while( rc==TH_OK
153      && TH_OK==(rc = Th_Expr(interp, argv[2], -1))
154      && TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond))
155      && iCond
156      && TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4]))
157   ){
158     rc = Th_Eval(interp, 0, argv[3], -1);
159   }
160 
161   if( rc==TH_BREAK ) rc = TH_OK;
162   return rc;
163 }
164 
165 /*
166 ** TH Syntax:
167 **
168 **   foreach VARLIST LIST SCRIPT
169 */
foreach_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)170 static int foreach_command(
171   Th_Interp *interp,
172   void *ctx,
173   int argc,
174   const char **argv,
175   int *argl
176 ){
177   int rc;
178   char **azVar = 0;
179   int *anVar;
180   int nVar;
181   char **azValue = 0;
182   int *anValue;
183   int nValue;
184   int ii, jj;
185 
186   if( argc!=4 ){
187     return Th_WrongNumArgs(interp, "foreach varlist list script");
188   }
189   rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
190   if( rc ) return rc;
191   rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
192   for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
193     for(jj=0; jj<nVar; jj++){
194       Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], anValue[ii+jj]);
195     }
196     rc = eval_loopbody(interp, argv[3], argl[3]);
197   }
198   if( rc==TH_BREAK ) rc = TH_OK;
199   Th_Free(interp, azVar);
200   Th_Free(interp, azValue);
201   return rc;
202 }
203 
204 
205 /*
206 ** TH Syntax:
207 **
208 **   list ?arg1 ?arg2? ...?
209 */
list_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)210 static int list_command(
211   Th_Interp *interp,
212   void *ctx,
213   int argc,
214   const char **argv,
215   int *argl
216 ){
217   char *zList = 0;
218   int nList = 0;
219   int i;
220 
221   for(i=1; i<argc; i++){
222     Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
223   }
224 
225   Th_SetResult(interp, zList, nList);
226   Th_Free(interp, zList);
227 
228   return TH_OK;
229 }
230 
231 /*
232 ** TH Syntax:
233 **
234 **    lappend var ?arg1? ?arg2? ...?
235 **
236 ** Interpret the content of variable var as a list.  Create var if it
237 ** does not already exist.  Append each argument as a new list element.
238 */
lappend_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)239 static int lappend_command(
240   Th_Interp *interp,
241   void *ctx,
242   int argc,
243   const char **argv,
244   int *argl
245 ){
246   char *zList = 0;
247   int nList = 0;
248   int i, rc;
249 
250   if( argc<2 ){
251     return Th_WrongNumArgs(interp, "lappend var ...");
252   }
253   rc = Th_GetVar(interp, argv[1], argl[1]);
254   if( rc==TH_OK ){
255     zList = Th_TakeResult(interp, &nList);
256   }
257 
258   for(i=2; i<argc; i++){
259     Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
260   }
261 
262   Th_SetVar(interp, argv[1], argl[1], zList, nList);
263   Th_SetResult(interp, zList, nList);
264   Th_Free(interp, zList);
265 
266   return TH_OK;
267 }
268 
269 
270 /*
271 ** TH Syntax:
272 **
273 **   lindex list index
274 */
lindex_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)275 static int lindex_command(
276   Th_Interp *interp,
277   void *ctx,
278   int argc,
279   const char **argv,
280   int *argl
281 ){
282   int iElem;
283   int rc;
284 
285   char **azElem;
286   int *anElem;
287   int nCount;
288 
289   if( argc!=3 ){
290     return Th_WrongNumArgs(interp, "lindex list index");
291   }
292 
293   if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
294     return TH_ERROR;
295   }
296 
297   rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
298   if( rc==TH_OK ){
299     if( iElem<nCount && iElem>=0 ){
300       Th_SetResult(interp, azElem[iElem], anElem[iElem]);
301     }else{
302       Th_SetResult(interp, 0, 0);
303     }
304     Th_Free(interp, azElem);
305   }
306 
307   return rc;
308 }
309 
310 /*
311 ** TH Syntax:
312 **
313 **   llength list
314 */
llength_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)315 static int llength_command(
316   Th_Interp *interp,
317   void *ctx,
318   int argc,
319   const char **argv,
320   int *argl
321 ){
322   int nElem;
323   int rc;
324 
325   if( argc!=2 ){
326     return Th_WrongNumArgs(interp, "llength list");
327   }
328 
329   rc = Th_SplitList(interp, argv[1], argl[1], 0, 0, &nElem);
330   if( rc==TH_OK ){
331     Th_SetResultInt(interp, nElem);
332   }
333 
334   return rc;
335 }
336 
337 /*
338 ** TH Syntax:
339 **
340 **   lsearch list string
341 */
lsearch_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)342 static int lsearch_command(
343   Th_Interp *interp,
344   void *ctx,
345   int argc,
346   const char **argv,
347   int *argl
348 ){
349   int rc;
350   char **azElem;
351   int *anElem;
352   int nCount;
353   int i;
354 
355   if( argc!=3 ){
356     return Th_WrongNumArgs(interp, "lsearch list string");
357   }
358 
359   rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
360   if( rc==TH_OK ){
361     Th_SetResultInt(interp, -1);
362     for(i=0; i<nCount; i++){
363       if( anElem[i]==argl[2] && 0==memcmp(azElem[i], argv[2], argl[2]) ){
364         Th_SetResultInt(interp, i);
365         break;
366       }
367     }
368     Th_Free(interp, azElem);
369   }
370 
371   return rc;
372 }
373 
374 /*
375 ** TH Syntax:
376 **
377 **   set varname ?value?
378 */
set_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)379 static int set_command(
380   Th_Interp *interp,
381   void *ctx,
382   int argc,
383   const char **argv,
384   int *argl
385 ){
386   if( argc!=2 && argc!=3 ){
387     return Th_WrongNumArgs(interp, "set varname ?value?");
388   }
389 
390   if( argc==3 ){
391     Th_SetVar(interp, argv[1], argl[1], argv[2], argl[2]);
392   }
393   return Th_GetVar(interp, argv[1], argl[1]);
394 }
395 
396 /*
397 ** When a new command is created using the built-in [proc] command, an
398 ** instance of the following structure is allocated and populated. A
399 ** pointer to the structure is passed as the context (second) argument
400 ** to function proc_call1() when the new command is executed.
401 */
402 typedef struct ProcDefn ProcDefn;
403 struct ProcDefn {
404   int nParam;                /* Number of formal (non "args") parameters */
405   char **azParam;            /* Parameter names */
406   int *anParam;              /* Lengths of parameter names */
407   char **azDefault;          /* Default values */
408   int *anDefault;            /* Lengths of default values */
409   int hasArgs;               /* True if there is an "args" parameter */
410   char *zProgram;            /* Body of proc */
411   int nProgram;              /* Number of bytes at zProgram */
412   char *zUsage;              /* Usage message */
413   int nUsage;                /* Number of bytes at zUsage */
414 };
415 
416 /* This structure is used to temporarily store arguments passed to an
417 ** invocation of a command created using [proc]. A pointer to an
418 ** instance is passed as the second argument to the proc_call2() function.
419 */
420 typedef struct ProcArgs ProcArgs;
421 struct ProcArgs {
422   int argc;
423   const char **argv;
424   int *argl;
425 };
426 
427 /*
428 ** Each time a command created using [proc] is invoked, a new
429 ** th1 stack frame is allocated (for the proc's local variables) and
430 ** this function invoked.
431 **
432 ** Argument pContext1 points to the associated ProcDefn structure.
433 ** Argument pContext2  points to a ProcArgs structure that contains
434 ** the arguments passed to this specific invocation of the proc.
435 */
proc_call2(Th_Interp * interp,void * pContext1,void * pContext2)436 static int proc_call2(Th_Interp *interp, void *pContext1, void *pContext2){
437   int i;
438   ProcDefn *p = (ProcDefn *)pContext1;
439   ProcArgs *pArgs = (ProcArgs *)pContext2;
440 
441   /* Check if there are the right number of arguments. If there are
442   ** not, generate a usage message for the command.
443   */
444   if( (pArgs->argc>(p->nParam+1) && !p->hasArgs)
445    || (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1])
446   ){
447     char *zUsage = 0;
448     int nUsage = 0;
449     Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]);
450     Th_StringAppend(interp, &zUsage, &nUsage, p->zUsage, p->nUsage);
451     Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"", 1);
452     Th_WrongNumArgs(interp, zUsage);
453     Th_Free(interp, zUsage);
454     return TH_ERROR;
455   }
456 
457   /* Populate the formal proc parameters. */
458   for(i=0; i<p->nParam; i++){
459     const char *zVal;
460     int nVal;
461     if( pArgs->argc>(i+1) ){
462       zVal = pArgs->argv[i+1];
463       nVal = pArgs->argl[i+1];
464     }else{
465       zVal = p->azDefault[i];
466       nVal = p->anDefault[i];
467     }
468     Th_SetVar(interp, p->azParam[i], p->anParam[i], zVal, nVal);
469   }
470 
471   /* Populate the "args" parameter, if it exists */
472   if( p->hasArgs ){
473     char *zArgs = 0;
474     int nArgs = 0;
475     for(i=p->nParam+1; i<pArgs->argc; i++){
476       Th_ListAppend(interp, &zArgs, &nArgs, pArgs->argv[i], pArgs->argl[i]);
477     }
478     Th_SetVar(interp, (const char *)"args", -1, zArgs, nArgs);
479     if(zArgs){
480       Th_Free(interp, zArgs);
481     }
482   }
483 
484   Th_SetResult(interp, 0, 0);
485   return Th_Eval(interp, 0, p->zProgram, p->nProgram);
486 }
487 
488 /*
489 ** This function is the command callback registered for all commands
490 ** created using the [proc] command. The second argument, pContext,
491 ** is a pointer to the associated ProcDefn structure.
492 */
proc_call1(Th_Interp * interp,void * pContext,int argc,const char ** argv,int * argl)493 static int proc_call1(
494   Th_Interp *interp,
495   void *pContext,
496   int argc,
497   const char **argv,
498   int *argl
499 ){
500   int rc;
501 
502   ProcDefn *p = (ProcDefn *)pContext;
503   ProcArgs procargs;
504 
505   /* Call function proc_call2(), which will call Th_Eval() to evaluate
506   ** the body of the [proc], in a new Th stack frame. This is so that
507   ** the proc body has its own local variable context.
508   */
509   procargs.argc = argc;
510   procargs.argv = argv;
511   procargs.argl = argl;
512   rc = Th_InFrame(interp, proc_call2, (void *)p, (void *)&procargs);
513 
514   if( rc==TH_RETURN ){
515     rc = TH_OK;
516   }
517   if( rc==TH_RETURN2 ){
518     rc = TH_RETURN;
519   }
520   return rc;
521 }
522 
523 /*
524 ** This function is registered as the delete callback for all commands
525 ** created using the built-in [proc] command. It is called automatically
526 ** when a command created using [proc] is deleted.
527 **
528 ** It frees the ProcDefn structure allocated when the command was created.
529 */
proc_del(Th_Interp * interp,void * pContext)530 static void proc_del(Th_Interp *interp, void *pContext){
531   ProcDefn *p = (ProcDefn *)pContext;
532   Th_Free(interp, (void *)p->zUsage);
533   Th_Free(interp, (void *)p);
534 }
535 
536 /*
537 ** TH Syntax:
538 **
539 **   proc name arglist code
540 */
proc_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)541 static int proc_command(
542   Th_Interp *interp,
543   void *ctx,
544   int argc,
545   const char **argv,
546   int *argl
547 ){
548   int rc;
549   const char *zName;
550 
551   ProcDefn *p;
552   int nByte;
553   int i;
554   char *zSpace;
555 
556   char **azParam;
557   int *anParam;
558   int nParam;
559 
560   char *zUsage = 0;                /* Build up a usage message here */
561   int nUsage = 0;                  /* Number of bytes at zUsage */
562 
563   if( argc!=4 ){
564     return Th_WrongNumArgs(interp, "proc name arglist code");
565   }
566   if( Th_SplitList(interp, argv[2], argl[2], &azParam, &anParam, &nParam) ){
567     return TH_ERROR;
568   }
569 
570   /* Allocate the new ProcDefn structure. */
571   nByte = sizeof(ProcDefn) +                        /* ProcDefn structure */
572       (sizeof(char *) + sizeof(int)) * nParam +     /* azParam, anParam */
573       (sizeof(char *) + sizeof(int)) * nParam +     /* azDefault, anDefault */
574       argl[3] +                                     /* zProgram */
575       argl[2];    /* Space for copies of parameter names and default values */
576   p = (ProcDefn *)Th_Malloc(interp, nByte);
577 
578   /* If the last parameter in the parameter list is "args", then set the
579   ** ProcDefn.hasArgs flag. The "args" parameter does not require an
580   ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
581   */
582   if( nParam>0 ){
583     if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){
584       p->hasArgs = 1;
585       nParam--;
586     }
587   }
588 
589   p->nParam    = nParam;
590   p->azParam   = (char **)&p[1];
591   p->anParam   = (int *)&p->azParam[nParam];
592   p->azDefault = (char **)&p->anParam[nParam];
593   p->anDefault = (int *)&p->azDefault[nParam];
594   p->zProgram = (char *)&p->anDefault[nParam];
595   memcpy(p->zProgram, argv[3], argl[3]);
596   p->nProgram = argl[3];
597   zSpace = &p->zProgram[p->nProgram];
598 
599   for(i=0; i<nParam; i++){
600     char **az;
601     int *an;
602     int n;
603     if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){
604       goto error_out;
605     }
606     if( n<1 || n>2 ){
607       const char expected[] = "expected parameter, got \"";
608       Th_ErrorMessage(interp, expected, azParam[i], anParam[i]);
609       Th_Free(interp, az);
610       goto error_out;
611     }
612     p->anParam[i] = an[0];
613     p->azParam[i] = zSpace;
614     memcpy(zSpace, az[0], an[0]);
615     zSpace += an[0];
616     if( n==2 ){
617       p->anDefault[i] = an[1];
618       p->azDefault[i] = zSpace;
619       memcpy(zSpace, az[1], an[1]);
620       zSpace += an[1];
621     }
622 
623     Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ", 1);
624     if( n==2 ){
625       Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"?", 1);
626       Th_StringAppend(interp, &zUsage, &nUsage, az[0], an[0]);
627       Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"?", 1);
628     }else{
629       Th_StringAppend(interp, &zUsage, &nUsage, az[0], an[0]);
630     }
631 
632     Th_Free(interp, az);
633   }
634   assert( zSpace-(char *)p<=nByte );
635 
636   /* If there is an "args" parameter, append it to the end of the usage
637   ** message. Set ProcDefn.zUsage to point at the usage message. It will
638   ** be freed along with the rest of the proc-definition by proc_del().
639   */
640   if( p->hasArgs ){
641     Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ?args...?", -1);
642   }
643   p->zUsage = zUsage;
644   p->nUsage = nUsage;
645 
646   /* Register the new command with the th1 interpreter. */
647   zName = argv[1];
648   rc = Th_CreateCommand(interp, zName, proc_call1, (void *)p, proc_del);
649   if( rc==TH_OK ){
650     Th_SetResult(interp, 0, 0);
651   }
652 
653   Th_Free(interp, azParam);
654   return TH_OK;
655 
656  error_out:
657   Th_Free(interp, azParam);
658   Th_Free(interp, zUsage);
659   return TH_ERROR;
660 }
661 
662 /*
663 ** TH Syntax:
664 **
665 **   rename oldcmd newcmd
666 */
rename_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)667 static int rename_command(
668   Th_Interp *interp,
669   void *ctx,
670   int argc,
671   const char **argv,
672   int *argl
673 ){
674   if( argc!=3 ){
675     return Th_WrongNumArgs(interp, "rename oldcmd newcmd");
676   }
677   return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]);
678 }
679 
680 /*
681 ** TH Syntax:
682 **
683 **   break    ?value...?
684 **   continue ?value...?
685 **   ok       ?value...?
686 **   error    ?value...?
687 */
simple_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)688 static int simple_command(
689   Th_Interp *interp,
690   void *ctx,
691   int argc,
692   const char **argv,
693   int *argl
694 ){
695   if( argc!=1 && argc!=2 ){
696     return Th_WrongNumArgs(interp, "return ?value?");
697   }
698   if( argc==2 ){
699     Th_SetResult(interp, argv[1], argl[1]);
700   }
701   return FOSSIL_PTR_TO_INT(ctx);
702 }
703 
704 /*
705 ** TH Syntax:
706 **
707 **   return ?-code code? ?value?
708 */
return_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)709 static int return_command(
710   Th_Interp *interp,
711   void *ctx,
712   int argc,
713   const char **argv,
714   int *argl
715 ){
716   int iCode = TH_RETURN;
717   if( argc<1 || argc>4 ){
718     return Th_WrongNumArgs(interp, "return ?-code code? ?value?");
719   }
720   if( argc>2 ){
721     int rc = Th_ToInt(interp, argv[2], argl[2], &iCode);
722     if( rc!=TH_OK ){
723       return rc;
724     }
725   }
726   if( argc==2 || argc==4 ){
727     Th_SetResult(interp, argv[argc-1], argl[argc-1]);
728   }
729   return iCode;
730 }
731 
732 /*
733 ** TH Syntax:
734 **
735 **   string compare STRING1 STRING2
736 */
string_compare_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)737 static int string_compare_command(
738   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
739 ){
740   const char *zRight; int nRight;
741   const char *zLeft; int nLeft;
742 
743   int i;
744   int iRes = 0;
745 
746   if( argc!=4 ){
747     return Th_WrongNumArgs(interp, "string compare str1 str2");
748   }
749 
750   zLeft = argv[2];
751   nLeft = argl[2];
752   zRight = argv[3];
753   nRight = argl[3];
754 
755   for(i=0; iRes==0 && i<nLeft && i<nRight; i++){
756     iRes = zLeft[i]-zRight[i];
757   }
758   if( iRes==0 ){
759     iRes = nLeft-nRight;
760   }
761 
762   if( iRes<0 ) iRes = -1;
763   if( iRes>0 ) iRes = 1;
764 
765   return Th_SetResultInt(interp, iRes);
766 }
767 
768 /*
769 ** TH Syntax:
770 **
771 **   string first NEEDLE HAYSTACK
772 */
string_first_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)773 static int string_first_command(
774   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
775 ){
776   int nNeedle;
777   int nHaystack;
778   int iRes = -1;
779 
780   if( argc!=4 ){
781     return Th_WrongNumArgs(interp, "string first needle haystack");
782   }
783 
784   nNeedle = argl[2];
785   nHaystack = argl[3];
786 
787   if( nNeedle && nHaystack && nNeedle<=nHaystack ){
788     const char *zNeedle = argv[2];
789     const char *zHaystack = argv[3];
790     int i;
791 
792     for(i=0; i<=(nHaystack-nNeedle); i++){
793       if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
794         iRes = i;
795         break;
796       }
797     }
798   }
799 
800   return Th_SetResultInt(interp, iRes);
801 }
802 
803 /*
804 ** TH Syntax:
805 **
806 **   string index STRING INDEX
807 */
string_index_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)808 static int string_index_command(
809   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
810 ){
811   int iIndex;
812 
813   if( argc!=4 ){
814     return Th_WrongNumArgs(interp, "string index string index");
815   }
816 
817   if( argl[3]==3 && 0==memcmp("end", argv[3], 3) ){
818     iIndex = argl[2]-1;
819   }else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){
820     Th_ErrorMessage(
821         interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
822     return TH_ERROR;
823   }
824 
825   if( iIndex>=0 && iIndex<argl[2] ){
826     return Th_SetResult(interp, &argv[2][iIndex], 1);
827   }else{
828     return Th_SetResult(interp, 0, 0);
829   }
830 }
831 
832 /*
833 ** TH Syntax:
834 **
835 **   string is CLASS STRING
836 */
string_is_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)837 static int string_is_command(
838   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
839 ){
840   if( argc!=4 ){
841     return Th_WrongNumArgs(interp, "string is class string");
842   }
843   if( argl[2]==5 && 0==memcmp(argv[2], "alnum", 5) ){
844     int i;
845     int iRes = 1;
846 
847     for(i=0; i<argl[3]; i++){
848       if( !th_isalnum(argv[3][i]) ){
849         iRes = 0;
850       }
851     }
852 
853     return Th_SetResultInt(interp, iRes);
854   }else if( argl[2]==6 && 0==memcmp(argv[2], "double", 6) ){
855     double fVal;
856     if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){
857       return Th_SetResultInt(interp, 1);
858     }
859     return Th_SetResultInt(interp, 0);
860   }else if( argl[2]==7 && 0==memcmp(argv[2], "integer", 7) ){
861     int iVal;
862     if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){
863       return Th_SetResultInt(interp, 1);
864     }
865     return Th_SetResultInt(interp, 0);
866   }else if( argl[2]==4 && 0==memcmp(argv[2], "list", 4) ){
867     if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){
868       return Th_SetResultInt(interp, 1);
869     }
870     return Th_SetResultInt(interp, 0);
871   }else{
872     Th_ErrorMessage(interp,
873         "Expected alnum, double, integer, or list, got:", argv[2], argl[2]);
874     return TH_ERROR;
875   }
876 }
877 
878 /*
879 ** TH Syntax:
880 **
881 **   string last NEEDLE HAYSTACK
882 */
string_last_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)883 static int string_last_command(
884   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
885 ){
886   int nNeedle;
887   int nHaystack;
888   int iRes = -1;
889 
890   if( argc!=4 ){
891     return Th_WrongNumArgs(interp, "string last needle haystack");
892   }
893 
894   nNeedle = argl[2];
895   nHaystack = argl[3];
896 
897   if( nNeedle && nHaystack && nNeedle<=nHaystack ){
898     const char *zNeedle = argv[2];
899     const char *zHaystack = argv[3];
900     int i;
901 
902     for(i=nHaystack-nNeedle; i>=0; i--){
903       if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
904         iRes = i;
905         break;
906       }
907     }
908   }
909 
910   return Th_SetResultInt(interp, iRes);
911 }
912 
913 /*
914 ** TH Syntax:
915 **
916 **   string length STRING
917 */
string_length_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)918 static int string_length_command(
919   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
920 ){
921   if( argc!=3 ){
922     return Th_WrongNumArgs(interp, "string length string");
923   }
924   return Th_SetResultInt(interp, argl[2]);
925 }
926 
927 /*
928 ** TH Syntax:
929 **
930 **   string match PATTERN STRING
931 **
932 */
string_match_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)933 static int string_match_command(
934   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
935 ){
936   extern char *fossil_strndup(const char*,int);
937   extern void fossil_free(void*);
938   char *zPat, *zStr;
939   int rc;
940   if( argc!=4 ){
941     return Th_WrongNumArgs(interp, "string match pattern string");
942   }
943   zPat = fossil_strndup(argv[2],argl[2]);
944   zStr = fossil_strndup(argv[3],argl[3]);
945   rc = sqlite3_strglob(zPat,zStr);
946   fossil_free(zPat);
947   fossil_free(zStr);
948   return Th_SetResultInt(interp, !rc);
949 }
950 
951 /*
952 ** TH Syntax:
953 **
954 **   string range STRING FIRST LAST
955 */
string_range_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)956 static int string_range_command(
957   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
958 ){
959   int iStart;
960   int iEnd;
961 
962   if( argc!=5 ){
963     return Th_WrongNumArgs(interp, "string range string first last");
964   }
965 
966   if( argl[4]==3 && 0==memcmp("end", argv[4], 3) ){
967     iEnd = argl[2];
968   }else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){
969     Th_ErrorMessage(
970         interp, "Expected \"end\" or integer, got:", argv[4], argl[4]);
971     return TH_ERROR;
972   }
973   if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){
974     return TH_ERROR;
975   }
976 
977   if( iStart<0 ) iStart = 0;
978   if( iEnd>=argl[2] ) iEnd = argl[2]-1;
979   if( iStart>iEnd ) iEnd = iStart-1;
980 
981   return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1);
982 }
983 
984 /*
985 ** TH Syntax:
986 **
987 **   string repeat STRING COUNT
988 */
string_repeat_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)989 static int string_repeat_command(
990   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
991 ){
992   int n;
993   int i;
994   int nByte;
995   char *zByte;
996 
997   if( argc!=4 ){
998     return Th_WrongNumArgs(interp, "string repeat string n");
999   }
1000   if( Th_ToInt(interp, argv[3], argl[3], &n) ){
1001     return TH_ERROR;
1002   }
1003 
1004   nByte = argl[2] * n;
1005   zByte = Th_Malloc(interp, nByte+1);
1006   for(i=0; i<nByte; i+=argl[2]){
1007     memcpy(&zByte[i], argv[2], argl[2]);
1008   }
1009 
1010   Th_SetResult(interp, zByte, nByte);
1011   Th_Free(interp, zByte);
1012   return TH_OK;
1013 }
1014 
1015 /*
1016 ** TH Syntax:
1017 **
1018 **   string trim STRING
1019 **   string trimleft STRING
1020 **   string trimright STRING
1021 */
string_trim_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1022 static int string_trim_command(
1023   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
1024 ){
1025   int n;
1026   const char *z;
1027 
1028   if( argc!=3 ){
1029     return Th_WrongNumArgs(interp, "string trim string");
1030   }
1031   z = argv[2];
1032   n = argl[2];
1033   if( argl[1]<5 || argv[1][4]=='l' ){
1034     while( n && th_isspace(z[0]) ){ z++; n--; }
1035   }
1036   if( argl[1]<5 || argv[1][4]=='r' ){
1037     while( n && th_isspace(z[n-1]) ){ n--; }
1038   }
1039   Th_SetResult(interp, z, n);
1040   return TH_OK;
1041 }
1042 
1043 /*
1044 ** TH Syntax:
1045 **
1046 **   info exists VARNAME
1047 */
info_exists_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1048 static int info_exists_command(
1049   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
1050 ){
1051   int rc;
1052 
1053   if( argc!=3 ){
1054     return Th_WrongNumArgs(interp, "info exists var");
1055   }
1056   rc = Th_ExistsVar(interp, argv[2], argl[2]);
1057   Th_SetResultInt(interp, rc);
1058   return TH_OK;
1059 }
1060 
1061 /*
1062 ** TH Syntax:
1063 **
1064 **   info commands
1065 */
info_commands_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1066 static int info_commands_command(
1067   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
1068 ){
1069   int rc;
1070   char *zElem = 0;
1071   int nElem = 0;
1072 
1073   if( argc!=2 ){
1074     return Th_WrongNumArgs(interp, "info commands");
1075   }
1076   rc = Th_ListAppendCommands(interp, &zElem, &nElem);
1077   if( rc!=TH_OK ){
1078     return rc;
1079   }
1080   Th_SetResult(interp, zElem, nElem);
1081   if( zElem ) Th_Free(interp, zElem);
1082   return TH_OK;
1083 }
1084 
1085 /*
1086 ** TH Syntax:
1087 **
1088 **   info vars
1089 */
info_vars_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1090 static int info_vars_command(
1091   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
1092 ){
1093   int rc;
1094   char *zElem = 0;
1095   int nElem = 0;
1096 
1097   if( argc!=2 ){
1098     return Th_WrongNumArgs(interp, "info vars");
1099   }
1100   rc = Th_ListAppendVariables(interp, &zElem, &nElem);
1101   if( rc!=TH_OK ){
1102     return rc;
1103   }
1104   Th_SetResult(interp, zElem, nElem);
1105   if( zElem ) Th_Free(interp, zElem);
1106   return TH_OK;
1107 }
1108 
1109 /*
1110 ** TH Syntax:
1111 **
1112 **   array exists VARNAME
1113 */
array_exists_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1114 static int array_exists_command(
1115   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
1116 ){
1117   int rc;
1118 
1119   if( argc!=3 ){
1120     return Th_WrongNumArgs(interp, "array exists var");
1121   }
1122   rc = Th_ExistsArrayVar(interp, argv[2], argl[2]);
1123   Th_SetResultInt(interp, rc);
1124   return TH_OK;
1125 }
1126 
1127 /*
1128 ** TH Syntax:
1129 **
1130 **   array names VARNAME
1131 */
array_names_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1132 static int array_names_command(
1133   Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
1134 ){
1135   int rc;
1136   char *zElem = 0;
1137   int nElem = 0;
1138 
1139   if( argc!=3 ){
1140     return Th_WrongNumArgs(interp, "array names varname");
1141   }
1142   rc = Th_ListAppendArray(interp, argv[2], argl[2], &zElem, &nElem);
1143   if( rc!=TH_OK ){
1144     return rc;
1145   }
1146   Th_SetResult(interp, zElem, nElem);
1147   if( zElem ) Th_Free(interp, zElem);
1148   return TH_OK;
1149 }
1150 
1151 /*
1152 ** TH Syntax:
1153 **
1154 **   unset VARNAME
1155 */
unset_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1156 static int unset_command(
1157   Th_Interp *interp,
1158   void *ctx,
1159   int argc,
1160   const char **argv,
1161   int *argl
1162 ){
1163   if( argc!=2 ){
1164     return Th_WrongNumArgs(interp, "unset var");
1165   }
1166   return Th_UnsetVar(interp, argv[1], argl[1]);
1167 }
1168 
Th_CallSubCommand(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl,const Th_SubCommand * aSub)1169 int Th_CallSubCommand(
1170   Th_Interp *interp,
1171   void *ctx,
1172   int argc,
1173   const char **argv,
1174   int *argl,
1175   const Th_SubCommand *aSub
1176 ){
1177   if( argc>1 ){
1178     int i;
1179     for(i=0; aSub[i].zName; i++){
1180       const char *zName = aSub[i].zName;
1181       if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){
1182         return aSub[i].xProc(interp, ctx, argc, argv, argl);
1183       }
1184     }
1185   }
1186   if(argc<2){
1187     Th_ErrorMessage(interp, "Expected sub-command for", argv[0], argl[0]);
1188   }else{
1189     Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]);
1190   }
1191   return TH_ERROR;
1192 }
1193 
1194 /*
1195 ** TH Syntax:
1196 **
1197 **   string compare   STR1 STR2
1198 **   string first     NEEDLE HAYSTACK ?STARTINDEX?
1199 **   string index     STRING INDEX
1200 **   string is        CLASS STRING
1201 **   string last      NEEDLE HAYSTACK ?STARTINDEX?
1202 **   string length    STRING
1203 **   string range     STRING FIRST LAST
1204 **   string repeat    STRING COUNT
1205 **   string trim      STRING
1206 **   string trimleft  STRING
1207 **   string trimright STRING
1208 */
string_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1209 static int string_command(
1210   Th_Interp *interp,
1211   void *ctx,
1212   int argc,
1213   const char **argv,
1214   int *argl
1215 ){
1216   static const Th_SubCommand aSub[] = {
1217     { "compare",   string_compare_command },
1218     { "first",     string_first_command },
1219     { "index",     string_index_command },
1220     { "is",        string_is_command },
1221     { "last",      string_last_command },
1222     { "length",    string_length_command },
1223     { "match",     string_match_command },
1224     { "range",     string_range_command },
1225     { "repeat",    string_repeat_command },
1226     { "trim",      string_trim_command },
1227     { "trimleft",  string_trim_command },
1228     { "trimright", string_trim_command },
1229     { 0, 0 }
1230   };
1231   return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
1232 }
1233 
1234 /*
1235 ** TH Syntax:
1236 **
1237 **   info commands
1238 **   info exists VARNAME
1239 **   info vars
1240 */
info_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1241 static int info_command(
1242   Th_Interp *interp,
1243   void *ctx,
1244   int argc,
1245   const char **argv,
1246   int *argl
1247 ){
1248   static const Th_SubCommand aSub[] = {
1249     { "commands", info_commands_command },
1250     { "exists",   info_exists_command },
1251     { "vars",     info_vars_command },
1252     { 0, 0 }
1253   };
1254   return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
1255 }
1256 
1257 /*
1258 ** TH Syntax:
1259 **
1260 **   array exists VARNAME
1261 **   array names VARNAME
1262 */
array_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1263 static int array_command(
1264   Th_Interp *interp,
1265   void *ctx,
1266   int argc,
1267   const char **argv,
1268   int *argl
1269 ){
1270   static const Th_SubCommand aSub[] = {
1271     { "exists", array_exists_command },
1272     { "names",  array_names_command },
1273     { 0, 0 }
1274   };
1275   return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub);
1276 }
1277 
1278 /*
1279 ** Convert the script level frame specification (used by the commands
1280 ** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as
1281 ** used by Th_LinkVar() and Th_Eval(). If successful, write the integer
1282 ** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR
1283 ** and leave an error message in the interpreter result.
1284 */
thToFrame(Th_Interp * interp,const char * zFrame,int nFrame,int * piFrame)1285 static int thToFrame(
1286   Th_Interp *interp,
1287   const char *zFrame,
1288   int nFrame,
1289   int *piFrame
1290 ){
1291   int iFrame;
1292   if( th_isdigit(zFrame[0]) ){
1293     int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame);
1294     if( rc!=TH_OK ) return rc;
1295     iFrame = iFrame * -1;
1296   }else if( zFrame[0]=='#' ){
1297     int rc = Th_ToInt(interp, &zFrame[1], nFrame-1, &iFrame);
1298     if( rc!=TH_OK ) return rc;
1299     iFrame = iFrame + 1;
1300   }else{
1301     return TH_ERROR;
1302   }
1303   *piFrame = iFrame;
1304   return TH_OK;
1305 }
1306 
1307 /*
1308 ** TH Syntax:
1309 **
1310 **   uplevel ?LEVEL? SCRIPT
1311 */
uplevel_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1312 static int uplevel_command(
1313   Th_Interp *interp,
1314   void *ctx,
1315   int argc,
1316   const char **argv,
1317   int *argl
1318 ){
1319   int iFrame = -1;
1320 
1321   if( argc!=2 && argc!=3 ){
1322     return Th_WrongNumArgs(interp, "uplevel ?level? script...");
1323   }
1324   if( argc==3 && TH_OK!=thToFrame(interp, argv[1], argl[1], &iFrame) ){
1325     return TH_ERROR;
1326   }
1327   return Th_Eval(interp, iFrame, argv[argc-1], -1);
1328 }
1329 
1330 /*
1331 ** TH Syntax:
1332 **
1333 **   upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...?
1334 */
upvar_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1335 static int upvar_command(
1336   Th_Interp *interp,
1337   void *ctx,
1338   int argc,
1339   const char **argv,
1340   int *argl
1341 ){
1342   int iVar = 1;
1343   int iFrame = -1;
1344   int rc = TH_OK;
1345   int i;
1346 
1347   if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){
1348     iVar++;
1349   }
1350   if( argc==iVar || (argc-iVar)%2 ){
1351     return Th_WrongNumArgs(interp,
1352         "upvar frame othervar myvar ?othervar myvar...?");
1353   }
1354   for(i=iVar; rc==TH_OK && i<argc; i=i+2){
1355     rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]);
1356   }
1357   return rc;
1358 }
1359 
1360 /*
1361 ** TH Syntax:
1362 **
1363 **   breakpoint ARGS
1364 **
1365 ** This command does nothing at all. Its purpose in life is to serve
1366 ** as a point for setting breakpoints in a debugger.
1367 */
breakpoint_command(Th_Interp * interp,void * ctx,int argc,const char ** argv,int * argl)1368 static int breakpoint_command(
1369   Th_Interp *interp,
1370   void *ctx,
1371   int argc,
1372   const char **argv,
1373   int *argl
1374 ){
1375   int cnt = 0;
1376   cnt++;
1377   return TH_OK;
1378 }
1379 
1380 /*
1381 ** Register the built-in th1 language commands with interpreter interp.
1382 ** Usually this is called soon after interpreter creation.
1383 */
th_register_language(Th_Interp * interp)1384 int th_register_language(Th_Interp *interp){
1385   /* Array of built-in commands. */
1386   struct _Command {
1387     const char *zName;
1388     Th_CommandProc xProc;
1389     void *pContext;
1390   } aCommand[] = {
1391     {"array",    array_command,   0},
1392     {"catch",    catch_command,   0},
1393     {"expr",     expr_command,    0},
1394     {"for",      for_command,     0},
1395     {"foreach",  foreach_command, 0},
1396     {"if",       if_command,      0},
1397     {"info",     info_command,    0},
1398     {"lappend",  lappend_command, 0},
1399     {"lindex",   lindex_command,  0},
1400     {"list",     list_command,    0},
1401     {"llength",  llength_command, 0},
1402     {"lsearch",  lsearch_command, 0},
1403     {"proc",     proc_command,    0},
1404     {"rename",   rename_command,  0},
1405     {"set",      set_command,     0},
1406     {"string",   string_command,  0},
1407     {"unset",    unset_command,   0},
1408     {"uplevel",  uplevel_command, 0},
1409     {"upvar",    upvar_command,   0},
1410 
1411     {"breakpoint", breakpoint_command, 0},
1412 
1413     {"return",   return_command, 0},
1414     {"break",    simple_command, (void *)TH_BREAK},
1415     {"continue", simple_command, (void *)TH_CONTINUE},
1416     {"error",    simple_command, (void *)TH_ERROR},
1417 
1418     {0, 0, 0}
1419   };
1420   size_t i;
1421 
1422   /* Add the language commands. */
1423   for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
1424     void *ctx;
1425     if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
1426     ctx = aCommand[i].pContext;
1427     Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
1428   }
1429 
1430   return TH_OK;
1431 }
1432