Changes On Branch th1-query-api
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch th1-query-api Excluding Merge-Ins

This is equivalent to a diff from 336e1355b9 to 82e78034da

2012-08-06
11:15
Fix minor documentation typo. check-in: 372879b388 user: drh tags: trunk
2012-08-03
18:41
merged in [336e1355b927f]. Leaf check-in: 82e78034da user: stephan tags: th1-query-api
2012-07-27
00:26
Fix another faulty though harmless memset() initialization. check-in: 336e1355b9 user: drh tags: trunk
00:00
Fix a faulty (but harmless) initialization in the MD5 code. check-in: ed005e302d user: drh tags: trunk
2012-07-22
17:59
merged in trunk [11abffbb497d67a], added a test script to check a th1 memleak case. check-in: 13b0e0b4d4 user: stephan tags: th1-query-api

Changes to src/blob.c.

17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
..
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
**
** A Blob is a variable-length containers for arbitrary string
** or binary data.
*/
#include "config.h"
#include <zlib.h>
#include "blob.h"

#if INTERFACE

/*
** A Blob can hold a string or a binary object of arbitrary size.  The
** size changes as necessary.
*/
struct Blob {
  unsigned int nUsed;            /* Number of bytes used in aData[] */
  unsigned int nAlloc;           /* Number of bytes allocated for aData[] */
................................................................................

/*
** Seek whence parameter values
*/
#define BLOB_SEEK_SET 1
#define BLOB_SEEK_CUR 2
#define BLOB_SEEK_END 3

#endif /* INTERFACE */

/*
** Make sure a blob is initialized
*/
#define blob_is_init(x) \
  assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)






<

>







 







<







17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
..
47
48
49
50
51
52
53

54
55
56
57
58
59
60
**
** A Blob is a variable-length containers for arbitrary string
** or binary data.
*/
#include "config.h"
#include <zlib.h>
#include "blob.h"

#if INTERFACE

/*
** A Blob can hold a string or a binary object of arbitrary size.  The
** size changes as necessary.
*/
struct Blob {
  unsigned int nUsed;            /* Number of bytes used in aData[] */
  unsigned int nAlloc;           /* Number of bytes allocated for aData[] */
................................................................................

/*
** Seek whence parameter values
*/
#define BLOB_SEEK_SET 1
#define BLOB_SEEK_CUR 2
#define BLOB_SEEK_END 3

#endif /* INTERFACE */

/*
** Make sure a blob is initialized
*/
#define blob_is_init(x) \
  assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)

Changes to src/db.c.

1096
1097
1098
1099
1100
1101
1102






1103
1104
1105
1106
1107
1108
1109
**
** Check for unfinalized statements and report errors if the reportErrors
** argument is true.  Ignore unfinalized statements when false.
*/
void db_close(int reportErrors){
  sqlite3_stmt *pStmt;
  if( g.db==0 ) return;






  if( g.fSqlStats ){
    int cur, hiwtr;
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);






>
>
>
>
>
>







1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
**
** Check for unfinalized statements and report errors if the reportErrors
** argument is true.  Ignore unfinalized statements when false.
*/
void db_close(int reportErrors){
  sqlite3_stmt *pStmt;
  if( g.db==0 ) return;
  if(g.interp){
    /* clean up up any query_prepare statements */
    Th_DeleteInterp(g.interp);
    g.interp = 0;
  }

  if( g.fSqlStats ){
    int cur, hiwtr;
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);

Changes to src/main.c.

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
...
785
786
787
788
789
790
791
792
793




















794
795
796
797
798
799
800

801
802
803
804
805
806
807
808
809
810
...
813
814
815
816
817
818
819


820



821
822
823
824








825



826
827
828
829


830
831
832
833
834
835
836
  if( p==0 ) fossil_panic("out of memory");
  return p;
}
void fossil_free(void *p){
  free(p);
}
void *fossil_realloc(void *p, size_t n){
  p = realloc(p, n);
  if( p==0 ) fossil_panic("out of memory");
  return p;
}

/*
** This function implements a cross-platform "system()" interface.
*/
int fossil_system(const char *zOrigCmd){
  int rc;
................................................................................


/*
** Look for a command-line option.  If present, return a pointer.
** Return NULL if missing.
**
** hasArg==0 means the option is a flag.  It is either present or not.
** hasArg==1 means the option has an argument.  Return a pointer to the
** argument.




















*/
const char *find_option(const char *zLong, const char *zShort, int hasArg){
  int i;
  int nLong;
  const char *zReturn = 0;
  assert( hasArg==0 || hasArg==1 );
  nLong = strlen(zLong);

  for(i=1; i<g.argc; i++){
    char *z;
    if (i+hasArg >= g.argc) break;
    z = g.argv[i];
    if( z[0]!='-' ) continue;
    z++;
    if( z[0]=='-' ){
      if( z[1]==0 ){
        remove_from_argv(i, 1);
        break;
................................................................................
    }
    if( strncmp(z,zLong,nLong)==0 ){
      if( hasArg && z[nLong]=='=' ){
        zReturn = &z[nLong+1];
        remove_from_argv(i, 1);
        break;
      }else if( z[nLong]==0 ){


        zReturn = g.argv[i+hasArg];



        remove_from_argv(i, 1+hasArg);
        break;
      }
    }else if( fossil_strcmp(z,zShort)==0 ){








      zReturn = g.argv[i+hasArg];



      remove_from_argv(i, 1+hasArg);
      break;
    }
  }


  return zReturn;
}

/*
** Verify that there are no unprocessed command-line options.  If
** Any remaining command-line argument begins with "-" print
** an error message and quit.






|
|
|







 







|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|



>


<







 







>
>
|
>
>
>



<
>
>
>
>
>
>
>
>
|
>
>
>
|
|
|
|
>
>







677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
...
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823

824
825
826
827
828
829
830
...
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848

849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
  if( p==0 ) fossil_panic("out of memory");
  return p;
}
void fossil_free(void *p){
  free(p);
}
void *fossil_realloc(void *p, size_t n){
  void * re = realloc(p, n);
  if( re==0 && n>0 ) fossil_panic("out of memory");
  return (n > 0) ? re : 0;
}

/*
** This function implements a cross-platform "system()" interface.
*/
int fossil_system(const char *zOrigCmd){
  int rc;
................................................................................


/*
** Look for a command-line option.  If present, return a pointer.
** Return NULL if missing.
**
** hasArg==0 means the option is a flag.  It is either present or not.
** hasArg==1 means the option has an argument.  Return a pointer to
** the argument. If hasArg is 0 and the argument is found then a
** pointer to an empty string is returned (to distinguish from
** NULL). If hasArg==1 and the option lies at the end of the argument
** list, it is treated as if it had not been found.
**
** Note that this function REMOVES any found entry from the args list,
** so calling this twice for the same var will cause NULL to be
** returned after the first time.
**
** zLong may not be NULL but zShort may be.
**
** Options are accepted in these forms, depending on the value of
** hasArg:
**
** hasArg=true:
**  -long        (hasArg==0)
**  -long VALUE  (hasArg==1)
**  -long=VALUE  (hasArg==1)
**  -short       (hasArg==0)
**  -short VALUE (hasArg==1)
**  -short=VALUE (hasArg==1)
*/
const char *find_option(const char *zLong, const char *zShort, int hasArg){
  int i;
  int nLong, nShort;
  const char *zReturn = 0;
  assert( hasArg==0 || hasArg==1 );
  nLong = strlen(zLong);
  nShort = zShort ? strlen(zShort) : 0;
  for(i=1; i<g.argc; i++){
    char *z;

    z = g.argv[i];
    if( z[0]!='-' ) continue;
    z++;
    if( z[0]=='-' ){
      if( z[1]==0 ){
        remove_from_argv(i, 1);
        break;
................................................................................
    }
    if( strncmp(z,zLong,nLong)==0 ){
      if( hasArg && z[nLong]=='=' ){
        zReturn = &z[nLong+1];
        remove_from_argv(i, 1);
        break;
      }else if( z[nLong]==0 ){
        if( hasArg ){
          if (i+hasArg >= g.argc) break;
          zReturn = g.argv[i+hasArg];
        }else{
          zReturn = "";
        }
        remove_from_argv(i, 1+hasArg);
        break;
      }

    }else if( strncmp(z,zShort,nShort)==0 ){
      if( hasArg && z[nShort]=='=' ){
        zReturn = &z[nShort+1];
        remove_from_argv(i, 1);
        break;
      }else if( z[nShort]==0 ){
        if( hasArg ){
          if (i+hasArg >= g.argc) break;
          zReturn = g.argv[i+hasArg];
        }else{
          zReturn = "";
        }
        remove_from_argv(i, 1+hasArg);
        break;
      }
    }
  }

  return zReturn;
}

/*
** Verify that there are no unprocessed command-line options.  If
** Any remaining command-line argument begins with "-" print
** an error message and quit.

Changes to src/report.c.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
                         href("%R/rptsql?rn=%d", rn));
    }
    blob_appendf(&ril, "</li>\n");
  }

  Th_Store("report_items", blob_str(&ril));
  
  Th_Render(zScript);
  
  blob_reset(&ril);
  if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1);

  style_footer();
}







|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
                         href("%R/rptsql?rn=%d", rn));
    }
    blob_appendf(&ril, "</li>\n");
  }

  Th_Store("report_items", blob_str(&ril));
  
  Th_Render(zScript, Th_Render_Flags_DEFAULT);
  
  blob_reset(&ril);
  if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1);

  style_footer();
}

Changes to src/style.c.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
**
** This file contains code to implement the basic web page look and feel.
**
*/
#include "config.h"
#include "style.h"


/*
** Elements of the submenu are collected into the following
** structure and displayed below the main menu by style_header().
**
** Populate this structure with calls to style_submenu_element()
** prior to calling style_header().
*/
................................................................................
  Th_Store("manifest_version", MANIFEST_VERSION);
  Th_Store("manifest_date", MANIFEST_DATE);
  Th_Store("compiler_name", COMPILER_NAME);
  if( g.zLogin ){
    Th_Store("login", g.zLogin);
  }
  if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
  Th_Render(zHeader);
  if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
  Th_Unstore("title");   /* Avoid collisions with ticket field names */
  cgi_destination(CGI_BODY);
  g.cgiOutput = 1;
  headerHasBeenGenerated = 1;
  sideboxUsed = 0;

................................................................................

  /* Set the href= field on hyperlinks.  Do this before the footer since
  ** the footer will be generating </html> */
  style_resolve_href();

  zFooter = db_get("footer", (char*)zDefaultFooter);
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
  
  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</span>\n", -1);
................................................................................
  }

  /* Process through TH1 in order to give an opportunity to substitute
  ** variables such as $baseurl.
  */
  Th_Store("baseurl", g.zBaseURL);
  Th_Store("home", g.zTop);
  Th_Render(blob_str(&css));

  /* Tell CGI that the content returned by this page is considered cacheable */
  g.isConst = 1;
}

/*
** WEBPAGE: test_env






<







 







|







 







|







 







|







17
18
19
20
21
22
23

24
25
26
27
28
29
30
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
...
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
**
** This file contains code to implement the basic web page look and feel.
**
*/
#include "config.h"
#include "style.h"


/*
** Elements of the submenu are collected into the following
** structure and displayed below the main menu by style_header().
**
** Populate this structure with calls to style_submenu_element()
** prior to calling style_header().
*/
................................................................................
  Th_Store("manifest_version", MANIFEST_VERSION);
  Th_Store("manifest_date", MANIFEST_DATE);
  Th_Store("compiler_name", COMPILER_NAME);
  if( g.zLogin ){
    Th_Store("login", g.zLogin);
  }
  if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
  Th_Render(zHeader, Th_Render_Flags_DEFAULT);
  if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
  Th_Unstore("title");   /* Avoid collisions with ticket field names */
  cgi_destination(CGI_BODY);
  g.cgiOutput = 1;
  headerHasBeenGenerated = 1;
  sideboxUsed = 0;

................................................................................

  /* Set the href= field on hyperlinks.  Do this before the footer since
  ** the footer will be generating </html> */
  style_resolve_href();

  zFooter = db_get("footer", (char*)zDefaultFooter);
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter, Th_Render_Flags_DEFAULT);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
  
  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</span>\n", -1);
................................................................................
  }

  /* Process through TH1 in order to give an opportunity to substitute
  ** variables such as $baseurl.
  */
  Th_Store("baseurl", g.zBaseURL);
  Th_Store("home", g.zTop);
  Th_Render(blob_str(&css), Th_Render_Flags_DEFAULT);

  /* Tell CGI that the content returned by this page is considered cacheable */
  g.isConst = 1;
}

/*
** WEBPAGE: test_env

Changes to src/th.c.

3
4
5
6
7
8
9

10
11
12
13
14




















15
16
17
18
19
20
21
22
23
24






25
26
27
28
29
30
31
...
104
105
106
107
108
109
110

111
112
113
114
115
116
117
...
286
287
288
289
290
291
292
















293
294
295
296
297
298
299
....
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376


1377
1378
1379
1380
1381
1382














































1383
1384
1385
1386
1387
1388
1389
....
1636
1637
1638
1639
1640
1641
1642












1643
1644
1645
1646
1647
1648
1649
1650
1651
1652

1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
....
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
....
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
....
2371
2372
2373
2374
2375
2376
2377
2378

2379
2380
2381
2382
2383
2384
2385
2386


2387
2388
2389
2390
2391
2392
2393
....
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475



































2476
2477
2478
2479
2480
2481
2482
....
2484
2485
2486
2487
2488
2489
2490
2491

2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
....
2603
2604
2605
2606
2607
2608
2609
























































































































































































































































































































































































































































































































** The implementation of the TH core. This file contains the parser, and 
** the implementation of the interface in th.h.
*/

#include "th.h"
#include <string.h>
#include <assert.h>


typedef struct Th_Command   Th_Command;
typedef struct Th_Frame     Th_Frame;
typedef struct Th_Variable  Th_Variable;





















/*
** Interpreter structure.
*/
struct Th_Interp {
  Th_Vtab *pVtab;     /* Copy of the argument passed to Th_CreateInterp() */
  char *zResult;     /* Current interpreter result (Th_Malloc()ed) */
  int nResult;        /* number of bytes in zResult */
  Th_Hash *paCmd;     /* Table of registered commands */
  Th_Frame *pFrame;   /* Current execution frame */
  int isListMode;     /* True if thSplitList() should operate in "list" mode */






};

/*
** Each TH command registered using Th_CreateCommand() is represented
** by an instance of the following structure stored in the Th_Interp.paCmd
** hash-table.
*/
................................................................................
static int thEndOfLine(const char *, int);

static int  thPushFrame(Th_Interp*, Th_Frame*);
static void thPopFrame(Th_Interp*);

static void thFreeVariable(Th_HashEntry*, void*);
static void thFreeCommand(Th_HashEntry*, void*);


/*
** The following are used by both the expression and language parsers.
** Given that the start of the input string (z, n) is a language 
** construct of the relevant type (a command enclosed in [], an escape
** sequence etc.), these functions determine the number of bytes
** of the input consumed by the construct. For example:
................................................................................
  Th_Command *pCommand = (Th_Command *)pEntry->pData;
  if( pCommand->xDel ){
    pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
  }
  Th_Free((Th_Interp *)pContext, pEntry->pData);
  pEntry->pData = 0;
}

















/*
** Push a new frame onto the stack.
*/
static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){
  pFrame->paVar = Th_HashNew(interp);
  pFrame->pCaller = interp->pFrame;
................................................................................
}


/* 
** Wrappers around the supplied malloc() and free() 
*/
void *Th_Malloc(Th_Interp *pInterp, int nByte){
  void *p = pInterp->pVtab->xMalloc(nByte);
  if( p ){
    memset(p, 0, nByte);


  }
  return p;
}
void Th_Free(Th_Interp *pInterp, void *z){
  if( z ){
    pInterp->pVtab->xFree(z);














































  }
}

/*
** Install a new th1 command. 
**
** If a command of the same name already exists, it is deleted automatically.
................................................................................
/* 
** Delete an interpreter.
*/
void Th_DeleteInterp(Th_Interp *interp){
  assert(interp->pFrame);
  assert(0==interp->pFrame->pCaller);













  /* Delete the contents of the global frame. */
  thPopFrame(interp);

  /* Delete any result currently stored in the interpreter. */
  Th_SetResult(interp, 0, 0);

  /* Delete all registered commands and the command hash-table itself. */
  Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp);
  Th_HashDelete(interp, interp->paCmd);


  /* Delete the interpreter structure itself. */
  Th_Free(interp, (void *)interp);
}

/* 
** Create a new interpreter.
*/
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
  Th_Interp *p;

  /* Allocate and initialise the interpreter and the global frame */
  p = pVtab->xMalloc(sizeof(Th_Interp) + sizeof(Th_Frame));
  memset(p, 0, sizeof(Th_Interp));
  p->pVtab = pVtab;
  p->paCmd = Th_HashNew(p);
  thPushFrame(p, (Th_Frame *)&p[1]);

  return p;
}
................................................................................
** This function is the same as the standard strlen() function, except
** that it returns 0 (instead of being undefined) if the argument is
** a null pointer.
*/
int th_strlen(const char *zStr){
  int n = 0;
  if( zStr ){
    while( zStr[n] ) n++;
  }
  return n;
}

/* Whitespace characters:
**
**     ' '    0x20
................................................................................
int th_isalnum(char c){
  return (aCharProp[(unsigned char)c] & 0x0A);
}

#ifndef LONGDOUBLE_TYPE
# define LONGDOUBLE_TYPE long double
#endif
typedef char u8;


/*
** Return TRUE if z is a pure numeric string.  Return FALSE if the
** string contains any character which is not part of a number. If
** the string is numeric and contains the '.' character, set *realnum
** to TRUE (otherwise FALSE).
................................................................................
    if( realnum ) *realnum = 1;
  }
  return *z==0;
}

/*
** The string z[] is an ascii representation of a real number.
** Convert this string to a double.

**
** This routine assumes that z[] really is a valid number.  If it
** is not, the result is undefined.
**
** This routine is used instead of the library atof() function because
** the library atof() might want to use "," as the decimal point instead
** of "." depending on how locale is set.  But that would cause problems
** for SQL.  So this routine always uses "." regardless of locale.


*/
static int sqlite3AtoF(const char *z, double *pResult){
  int sign = 1;
  const char *zBegin = z;
  LONGDOUBLE_TYPE v1 = 0.0;
  while( th_isspace(*(u8*)z) ) z++;
  if( *z=='-' ){
................................................................................
    }
  }
  *pResult = sign<0 ? -v1 : v1;
  return z - zBegin;
}

/*
** Try to convert the string passed as arguments (z, n) to an integer.
** If successful, store the result in *piOut and return TH_OK. 
**
** If the string cannot be converted to an integer, return TH_ERROR. 
** If the interp argument is not NULL, leave an error message in the 
** interpreter result too.
*/
int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
  int i = 0;
  int iOut = 0;

  if( n<0 ){
    n = th_strlen(z);
  }

  if( n>0 && (z[0]=='-' || z[0]=='+') ){
    i = 1;
  }
  for(; i<n; i++){
    if( !th_isdigit(z[i]) ){
      Th_ErrorMessage(interp, "expected integer, got: \"", z, n);
      return TH_ERROR;
    }
    iOut = iOut * 10 + (z[i] - 48);
  }

  if( n>0 && z[0]=='-' ){
    iOut *= -1;
  }

  *piOut = iOut;
  return TH_OK;
}




































/*
** Try to convert the string passed as arguments (z, n) to a double.
** If successful, store the result in *pfOut and return TH_OK. 
**
** If the string cannot be converted to a double, return TH_ERROR. 
** If the interp argument is not NULL, leave an error message in the 
................................................................................
*/
int Th_ToDouble(
  Th_Interp *interp, 
  const char *z, 
  int n, 
  double *pfOut
){
  if( !sqlite3IsNumber((const char *)z, 0) ){

    Th_ErrorMessage(interp, "expected number, got: \"", z, n);
    return TH_ERROR;
  }

  sqlite3AtoF((const char *)z, pfOut);
  return TH_OK;
}

/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
................................................................................
      *z++ = zExp[i];
    }
  }

  *z = '\0';
  return Th_SetResult(interp, zBuf, -1);
}






























































































































































































































































































































































































































































































































>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|

|
|
|
|
>
>
>
>
>
>







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|


>
>





|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>










>











|







 







|







 







<







 







|
>








>
>







 







|
|
|
|
<
<

|












<












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
|
<

<
<
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
....
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
....
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
....
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
....
2438
2439
2440
2441
2442
2443
2444

2445
2446
2447
2448
2449
2450
2451
....
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
....
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553


2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567

2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
....
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632

2633


2634
2635
2636
2637
2638
2639
2640
2641
....
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
** The implementation of the TH core. This file contains the parser, and 
** the implementation of the interface in th.h.
*/

#include "th.h"
#include <string.h>
#include <assert.h>
#include <stdio.h> /* FILE class */

typedef struct Th_Command   Th_Command;
typedef struct Th_Frame     Th_Frame;
typedef struct Th_Variable  Th_Variable;

/*
** Shared instance. See th.h for the docs.
*/
const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE = {
  Th_Output_f_FILE /* xWrite() */,
  Th_Output_dispose_FILE /* dispose() */,
  NULL /*pState*/,
  1/*enabled*/
};

/*
** Holds client-provided "garbage collected" data for
** a Th_Interp instance.
*/
struct Th_GcEntry {
  void * pData;                        /* arbitrary data */
  void (*xDel)( Th_Interp *, void * ); /* finalizer for pData */
};
typedef struct Th_GcEntry Th_GcEntry;

/*
** Interpreter structure.
*/
struct Th_Interp {
  Th_Vtab *pVtab;    /* Copy of the argument passed to Th_CreateInterp() */
  char *zResult;     /* Current interpreter result (Th_Malloc()ed) */
  int nResult;       /* number of bytes in zResult */
  Th_Hash *paCmd;    /* Table of registered commands */
  Th_Frame *pFrame;  /* Current execution frame */
  int isListMode;    /* True if thSplitList() should operate in "list" mode */
  Th_Hash * paGc;    /* Holds client-provided data owned by this
                        object. It would be more efficient to store
                        these in a list (because we don't expect many
                        entries), but Th_Hash has the strong advantage
                        of being here and working.
                     */
};

/*
** Each TH command registered using Th_CreateCommand() is represented
** by an instance of the following structure stored in the Th_Interp.paCmd
** hash-table.
*/
................................................................................
static int thEndOfLine(const char *, int);

static int  thPushFrame(Th_Interp*, Th_Frame*);
static void thPopFrame(Th_Interp*);

static void thFreeVariable(Th_HashEntry*, void*);
static void thFreeCommand(Th_HashEntry*, void*);
static void thFreeGc(Th_HashEntry*, void*);

/*
** The following are used by both the expression and language parsers.
** Given that the start of the input string (z, n) is a language 
** construct of the relevant type (a command enclosed in [], an escape
** sequence etc.), these functions determine the number of bytes
** of the input consumed by the construct. For example:
................................................................................
  Th_Command *pCommand = (Th_Command *)pEntry->pData;
  if( pCommand->xDel ){
    pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
  }
  Th_Free((Th_Interp *)pContext, pEntry->pData);
  pEntry->pData = 0;
}

/*
** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees
** pEntry->pData but not pEntry.
*/
static void thFreeGc(Th_HashEntry *pEntry, void *pContext){
  Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData;
  if(gc){
    if( gc->xDel ){
      gc->xDel( (Th_Interp*)pContext, gc->pData );
    }
    Th_Free((Th_Interp *)pContext, pEntry->pData);
    pEntry->pData = 0;
  }
}


/*
** Push a new frame onto the stack.
*/
static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){
  pFrame->paVar = Th_HashNew(interp);
  pFrame->pCaller = interp->pFrame;
................................................................................
}


/* 
** Wrappers around the supplied malloc() and free() 
*/
void *Th_Malloc(Th_Interp *pInterp, int nByte){
  void * p = pInterp->pVtab->xRealloc(NULL, nByte);
  if( p ){
    memset(p, 0, nByte);
  }else{
    assert( 0 == nByte );
  }
  return p;
}
void Th_Free(Th_Interp *pInterp, void *z){
  if( z ){
    pInterp->pVtab->xRealloc(z, 0);
  }
}
void *Th_Realloc(Th_Interp *pInterp, void *z, int nByte){
  void *p = pInterp->pVtab->xRealloc(z, nByte);
  return p;
}


int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int nData ){
  if(!vTab->out.xWrite){
    return -1;
  }else if(!vTab->out.enabled){
    return 0;
  }else{
    return vTab->out.xWrite( zData, nData, vTab->out.pState );
  }
}


int Th_Output( Th_Interp *pInterp, char const * zData, int nData ){
  return Th_Vtab_Output( pInterp->pVtab, zData, nData );
}

void Th_OutputEnable( Th_Interp *pInterp, char flag ){
  pInterp->pVtab->out.enabled = flag;
}

char Th_OutputEnabled( Th_Interp *pInterp ){
  return pInterp->pVtab->out.enabled ? 1 : 0;
}

int Th_Output_f_FILE( char const * zData, int nData, void * pState ){
  FILE * dest = pState ? (FILE*)pState : stdout;
  int rc = (int)fwrite(zData, 1, nData, dest);
  fflush(dest);
  return rc;
}

void Th_Output_dispose_FILE( void * pState ){
  FILE * f = pState ? (FILE*)pState : NULL;
  if(f
     && (f != stdout)
     && (f != stderr)
     && (f != stdin)){
    fflush(f);
    fclose(f);
  }
}

/*
** Install a new th1 command. 
**
** If a command of the same name already exists, it is deleted automatically.
................................................................................
/* 
** Delete an interpreter.
*/
void Th_DeleteInterp(Th_Interp *interp){
  assert(interp->pFrame);
  assert(0==interp->pFrame->pCaller);

  /* Delete any client-side gc entries first. */
  if( interp->paGc ){
    Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
    Th_HashDelete(interp, interp->paGc);
    interp->paGc = NULL;
  }

  /* Clean up the output abstraction. */
  if( interp->pVtab && interp->pVtab->out.xDispose ){
      interp->pVtab->out.xDispose( interp->pVtab->out.pState );
  }
  
  /* Delete the contents of the global frame. */
  thPopFrame(interp);

  /* Delete any result currently stored in the interpreter. */
  Th_SetResult(interp, 0, 0);

  /* Delete all registered commands and the command hash-table itself. */
  Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp);
  Th_HashDelete(interp, interp->paCmd);

  
  /* Delete the interpreter structure itself. */
  Th_Free(interp, (void *)interp);
}

/* 
** Create a new interpreter.
*/
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
  Th_Interp *p;

  /* Allocate and initialise the interpreter and the global frame */
  p = pVtab->xRealloc(NULL, sizeof(Th_Interp) + sizeof(Th_Frame));
  memset(p, 0, sizeof(Th_Interp));
  p->pVtab = pVtab;
  p->paCmd = Th_HashNew(p);
  thPushFrame(p, (Th_Frame *)&p[1]);

  return p;
}
................................................................................
** This function is the same as the standard strlen() function, except
** that it returns 0 (instead of being undefined) if the argument is
** a null pointer.
*/
int th_strlen(const char *zStr){
  int n = 0;
  if( zStr ){
    while( zStr[n] ) ++n;
  }
  return n;
}

/* Whitespace characters:
**
**     ' '    0x20
................................................................................
int th_isalnum(char c){
  return (aCharProp[(unsigned char)c] & 0x0A);
}

#ifndef LONGDOUBLE_TYPE
# define LONGDOUBLE_TYPE long double
#endif



/*
** Return TRUE if z is a pure numeric string.  Return FALSE if the
** string contains any character which is not part of a number. If
** the string is numeric and contains the '.' character, set *realnum
** to TRUE (otherwise FALSE).
................................................................................
    if( realnum ) *realnum = 1;
  }
  return *z==0;
}

/*
** The string z[] is an ascii representation of a real number.
** Convert this string to a double and assigns its value to
** pResult.
**
** This routine assumes that z[] really is a valid number.  If it
** is not, the result is undefined.
**
** This routine is used instead of the library atof() function because
** the library atof() might want to use "," as the decimal point instead
** of "." depending on how locale is set.  But that would cause problems
** for SQL.  So this routine always uses "." regardless of locale.
**
** Returns the number of bytes of z consumed in parsing the value.
*/
static int sqlite3AtoF(const char *z, double *pResult){
  int sign = 1;
  const char *zBegin = z;
  LONGDOUBLE_TYPE v1 = 0.0;
  while( th_isspace(*(u8*)z) ) z++;
  if( *z=='-' ){
................................................................................
    }
  }
  *pResult = sign<0 ? -v1 : v1;
  return z - zBegin;
}

/*
** Attempts to convert (zArg,nArg) to an integer. On success *piOut is
** assigned to its value and TH_OK is returned, else piOut is not
** modified and TH_ERROR is returned. Conversion errors are considered
** non-fatal here, so interp's error state is not set.


*/
int Th_TryInt(Th_Interp *interp, const char *z, int n, int *piOut){
  int i = 0;
  int iOut = 0;

  if( n<0 ){
    n = th_strlen(z);
  }

  if( n>0 && (z[0]=='-' || z[0]=='+') ){
    i = 1;
  }
  for(; i<n; i++){
    if( !th_isdigit(z[i]) ){

      return TH_ERROR;
    }
    iOut = iOut * 10 + (z[i] - 48);
  }

  if( n>0 && z[0]=='-' ){
    iOut *= -1;
  }

  *piOut = iOut;
  return TH_OK;
}

/*
** Try to convert the string passed as arguments (z, n) to an integer.
** If successful, store the result in *piOut and return TH_OK. 
**
** If the string cannot be converted to an integer, return TH_ERROR. 
** If the interp argument is not NULL, leave an error message in the 
** interpreter result too.
*/
int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
  const int rc = Th_TryInt(interp, z, n, piOut);
  if( TH_OK != rc ){
    Th_ErrorMessage(interp, "expected integer, got: \"", z, n);
  }
  return rc;
}


/*
** Functionally/semantically identical to Th_TryInt() but works on
** doubles.
*/
int Th_TryDouble(
  Th_Interp *interp, 
  const char *z, 
  int n, 
  double *pfOut
){
  if( !sqlite3IsNumber((const char *)z, 0) ){
    return TH_ERROR;
  }else{
    sqlite3AtoF((const char *)z, pfOut);
    return TH_OK;
  }
}

/*
** Try to convert the string passed as arguments (z, n) to a double.
** If successful, store the result in *pfOut and return TH_OK. 
**
** If the string cannot be converted to a double, return TH_ERROR. 
** If the interp argument is not NULL, leave an error message in the 
................................................................................
*/
int Th_ToDouble(
  Th_Interp *interp, 
  const char *z, 
  int n, 
  double *pfOut
){
  const int rc = Th_TryDouble(interp, z, n, pfOut);
  if( TH_OK != rc ){
      Th_ErrorMessage(interp, "expected number, got: \"", z, n);

  }


  return rc;
}

/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
................................................................................
      *z++ = zExp[i];
    }
  }

  *z = '\0';
  return Th_SetResult(interp, zBuf, -1);
}


int Th_SetData( Th_Interp * interp, char const * key,
                 void * pData,
                 void (*finalizer)( Th_Interp *, void * ) ){
  Th_HashEntry * pEnt;
  Th_GcEntry * pGc;
  if(NULL == interp->paGc){
    interp->paGc = Th_HashNew(interp);
    assert(NULL != interp->paGc);
    if(!interp->paGc){
      return TH_ERROR;
    }
  }
  pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1);
  if( pEnt->pData ){
    thFreeGc( pEnt, interp );
  }
  assert( NULL == pEnt->pData );
  pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry));
  pGc->pData = pData;
  pGc->xDel = finalizer;
  return 0;
 
}
void * Th_GetData( Th_Interp * interp, char const * key ){
  Th_HashEntry * e = interp->paGc
    ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
    : NULL;
  return e ? ((Th_GcEntry*)e->pData)->pData : NULL;
}

int Th_RegisterCommands( Th_Interp * interp,
                          Th_Command_Reg const * aCommand ){
  int i;
  int rc = TH_OK;
  for(i=0; (TH_OK==rc) && aCommand[i].zName; ++i){
    if ( !aCommand[i].zName ) break;
    else if( !aCommand[i].xProc ) continue;
    else{
      rc = Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
                            aCommand[i].pContext, 0);
    }
  }
  return rc;
}



#ifdef TH_ENABLE_OB
/* Reminder: the ob code "really" belongs in th_lang.c or th_main.c,
   but it needs access to Th_Interp::pVtab in order to swap out
   Th_Vtab_OutputMethods parts for purposes of stacking layers of
   buffers. We could add access to it via the public interface,
   but that didn't seem appropriate.
*/

/*
** Th_Output_f() impl which redirects output to a Th_Ob_Manager.
** Requires that pState be a (Th_Ob_Man*).
*/
static int Th_Output_f_ob( char const * zData, int len, void * pState );

/*
** Th_Output::dispose() impl which requires pState to be-a Th_Ob_Manager.
*/
static void Th_Output_dispose_ob( void * pState );

/* Empty-initialized Th_Ob_Manager instance. */
#define Th_Ob_Man_empty_m { \
  NULL/*aBuf*/,           \
  0/*nBuf*/,            \
  -1/*cursor*/,       \
  NULL/*interp*/,     \
  NULL/*aOutput*/       \
}

/* Empty-initialized Th_Vtab_OutputMethods instance. */
#define Th_Vtab_OutputMethods_empty_m { \
  NULL /* write() */, \
  NULL /* dispose() */, \
  NULL /*pState*/,\
  1/*enabled*/\
}
/* Vtab_OutputMethods instance initialized for OB support. */
#define Th_Vtab_OutputMethods_ob_m { \
  Th_Output_f_ob /*write()*/, \
  Th_Output_dispose_ob /* dispose() */, \
  NULL /*pState*/,\
  1/*enabled*/\
}
/* Empty-initialized Th_Vtab_Man instance. */
static const Th_Ob_Manager Th_Ob_Man_empty = Th_Ob_Man_empty_m;
/* Empty-initialized Th_Vtab_OutputMethods instance. */
static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_empty = Th_Vtab_OutputMethods_empty_m;
/* Th_Vtab_OutputMethods instance initialized for OB support. */
static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_ob = Th_Vtab_OutputMethods_ob_m;

/*
**   Internal key for Th_Set/GetData(), for storing a Th_Ob_Manager instance.
*/
#define Th_Ob_Man_KEY "Th_Ob_Manager"

Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *interp){
  return (Th_Ob_Manager*) Th_GetData(interp, Th_Ob_Man_KEY );
}

Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan ){
  return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
}


/*
** Th_Output_f() impl which expects pState to be (Th_Ob_Manager*).
** (zData,len) are appended to pState's current output buffer.
*/
int Th_Output_f_ob( char const * zData, int len, void * pState ){
  Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
  Blob * b = Th_Ob_GetCurrentBuffer( pMan );
  assert( NULL != pMan );
  assert( b );
  blob_append( b, zData, len );
  return len;
}

void Th_Output_dispose_ob( void * pState ){
  /* possible todo: move the cleanup logic from
     Th_Ob_Pop() to here? */
#if 0
  Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
  Blob * b = Th_Ob_GetCurrentBuffer( pMan );
  assert( NULL != pMan );
  assert( b );
#endif
}



int Th_Ob_Push( Th_Ob_Manager * pMan,
                Th_Vtab_OutputMethods const * pWriter,
                Blob ** pOut ){
  Blob * pBlob;
  int x, i;
  if( NULL == pWriter ){
    pWriter = &Th_Vtab_OutputMethods_ob;
  }
  assert( NULL != pMan->interp );
  pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob));
  *pBlob = empty_blob;

  if( pMan->cursor >= pMan->nBuf-2 ){
    /* expand if needed */
    void * re;
    x = pMan->nBuf + 5;
    if( pMan->cursor >= x ) {
      assert( 0 && "This really should not happen." );
      x = pMan->cursor + 5;
    }
    re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
    if(NULL==re){
      goto error;
    }
    pMan->aBuf = (Blob **)re;
    re = Th_Realloc( pMan->interp, pMan->aOutput, x * sizeof(Th_Vtab_OutputMethods) );
    if(NULL==re){
      goto error;
    }
    pMan->aOutput = (Th_Vtab_OutputMethods*)re;
    for( i = pMan->nBuf; i < x; ++i ){
      pMan->aOutput[i] = Th_Vtab_OutputMethods_empty;
      pMan->aBuf[i] = NULL;
    }
    pMan->nBuf = x;
  }
  assert( pMan->nBuf > pMan->cursor );
  assert( pMan->cursor >= -1 );
  ++pMan->cursor;
  pMan->aBuf[pMan->cursor] = pBlob;
  pMan->aOutput[pMan->cursor] = pMan->interp->pVtab->out;
  pMan->interp->pVtab->out = *pWriter;
  pMan->interp->pVtab->out.pState = pMan;
  if( pOut ){
    *pOut = pBlob;
  }
  return TH_OK;
  error:
  if( pBlob ){
    Th_Free( pMan->interp, pBlob );
  }
  return TH_ERROR;
}

Blob * Th_Ob_Pop( Th_Ob_Manager * pMan ){
  if( pMan->cursor < 0 ){
    return NULL;
  }else{
    Blob * rc;
    Th_Vtab_OutputMethods * theOut;
    assert( pMan->nBuf > pMan->cursor );
    rc = pMan->aBuf[pMan->cursor];
    pMan->aBuf[pMan->cursor] = NULL;
    theOut = &pMan->aOutput[pMan->cursor];
#if 0
    /* We need something like this (but not this!) if we extend the
       support to use other (non-Blob) proxies. We will likely need
       another callback function or two for that case, e.g. xStart()
       and xEnd(), which would be called when they are pushed/popped
       to/from the stack.
    */
    if( theOut->xDispose ){
      theOut->xDispose( theOut->pState );
    }
#endif
    pMan->interp->pVtab->out = *theOut;
    pMan->aOutput[pMan->cursor] = Th_Vtab_OutputMethods_empty;
    if(-1 == --pMan->cursor){
      Th_Interp * interp = pMan->interp;
      Th_Free( pMan->interp, pMan->aBuf );
      Th_Free( pMan->interp, pMan->aOutput );
      *pMan = Th_Ob_Man_empty;
      pMan->interp = interp;
      assert(-1 == pMan->cursor);
    }
    return rc;
  }
}

int Th_Ob_PopAndFree( Th_Ob_Manager * pMan ){
  Blob * b = Th_Ob_Pop( pMan );
  if(!b) return 1;
  else {
    blob_reset(b);
    Th_Free( pMan->interp, b );
    return 0;
  }
}


void Th_ob_cleanup( Th_Ob_Manager * man ){
  while( 0 == Th_Ob_PopAndFree(man) ){}
}


/*
** TH command:
**
** ob clean
**
** Erases any currently buffered contents but does not modify
** the buffering level.
*/
static int ob_clean_command( Th_Interp *interp, void *ctx,
                             int argc,  const char **argv, int *argl
){
  const char doRc = ctx ? 1 : 0;
  Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
  Blob * b;
  assert( pMan && (interp == pMan->interp) );
  b = pMan ? Th_Ob_GetCurrentBuffer(pMan) : NULL;
  if(!b){
    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
    return TH_ERROR;
  }else{
    blob_reset(b);
    if( doRc ) {
      Th_SetResultInt( interp, 0 );
    }
    return TH_OK;
  }
}

/*
** TH command:
**
** ob end
**
** Erases any currently buffered contents and pops the current buffer
** from the stack.
*/
static int ob_end_command( Th_Interp *interp, void *ctx,
                           int argc,  const char **argv, int *argl ){
  const char doRc = ctx ? 1 : 0;
  Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
  Blob * b;
  assert( pMan && (interp == pMan->interp) );
  b = Th_Ob_Pop(pMan);
  if(!b){
    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
    return TH_ERROR;
  }else{
    blob_reset(b);
    Th_Free( interp, b );
    if(doRc){
      Th_SetResultInt( interp, 0 );
    }
    return TH_OK;
  }
}

/*
** TH command:
**
** ob flush ?pop|end?
**
** Briefly reverts the output layer to the next-lower
** level, flushes the current buffer to that output layer,
** and clears out the current buffer. Does not change the
** buffering level unless "end" is specified, in which case
** it behaves as if "ob end" had been called (after flushing
** the buffer).
*/
static int ob_flush_command( Th_Interp *interp, void *ctx,
                             int argc,  const char **argv, int *argl ){
  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
  Blob * b = NULL;
  Th_Vtab * oldVtab;
  int rc = TH_OK;
  assert( pMan && (interp == pMan->interp) );
  b = Th_Ob_GetCurrentBuffer(pMan);
  if( NULL == b ){
    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
    return TH_ERROR;
  }
  oldVtab = interp->pVtab;
  interp->pVtab->out = pMan->aOutput[pMan->cursor];
  Th_Output( interp, blob_str(b), b->nUsed );
  interp->pVtab = oldVtab;
  blob_reset(b);

  if(!rc && argc>2){
    int argPos = 2;
    char const * sub = argv[argPos];
    int subL = argl[argPos];
    /* "flush end" */
    if(th_strlen(sub)==3 &&
       ((0==memcmp("end", sub, subL)
         || (0==memcmp("pop", sub, subL))))){
      rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
    }
  }
  Th_SetResultInt( interp, 0 );
  return rc;
}

/*
** TH command:
**
** ob get ?clean|end|pop?
**
** Fetches the contents of the current buffer level.  If either
** 'clean' or 'end' are specified then the effect is as if "ob clean"
** or "ob end", respectively, are called after fetching the
** value. Calling "ob get end" is functionality equivalent to "ob get"
** followed by "ob end".
*/
static int ob_get_command( Th_Interp *interp, void *ctx,
                           int argc,  const char **argv, int *argl){
  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
  Blob * b = NULL;
  assert( pMan && (interp == pMan->interp) );
  b = Th_Ob_GetCurrentBuffer(pMan);
  if( NULL == b ){
    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
    return TH_ERROR;
  }else{
    int argPos = 2;
    char const * sub;
    int subL;
    int rc = TH_OK;
    Th_SetResult( interp, blob_str(b), b->nUsed );
    if(argc>argPos){
      sub = argv[argPos];
      subL = argl[argPos];
      /* "ob get clean" */
      if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){
        rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1);
      }/* "ob get end" */
      else if(!rc && th_strlen(sub)==3 &&
              ((0==memcmp("end", sub, subL))
               || (0==memcmp("pop", sub, subL)))){
        rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
      }
    }
    return rc;
  }
}

/*
** TH command:
**
** ob level
**
** Returns the buffering level, where 0 means no buffering is
** active, 1 means 1 level is active, etc.
*/
static int ob_level_command( Th_Interp *interp, void *ctx,
                             int argc,  const char **argv, int *argl
){
  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
  Th_SetResultInt( interp, 1 + pMan->cursor );
  return TH_OK;
}

/*
** TH command:
**
** ob start|push
**
** Pushes a new level of buffering onto the buffer stack.
** Returns the new buffering level (1-based).
**
** TODO: take an optional final argument naming the output handler.
** e.g. "stdout" or "cgi" or "default"
** 
*/
static int ob_start_command( Th_Interp *interp, void *ctx,
                             int argc,  const char **argv, int *argl
){
  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
  Blob * b = NULL;
  int rc;
  Th_Vtab_OutputMethods const * pWriter = &Th_Vtab_OutputMethods_ob;
  assert( pMan && (interp == pMan->interp) );
  rc = Th_Ob_Push(pMan, NULL, &b);
  if( TH_OK != rc ){
    assert( NULL == b );
    return rc;
  }
  assert( NULL != b );
  Th_SetResultInt( interp, 1 + pMan->cursor );
  return TH_OK;
}

static void finalizerObMan( Th_Interp * interp, void * p ){
  Th_Ob_Manager * man = (Th_Ob_Manager*)p;
  if(man){
    assert( interp == man->interp );
    Th_ob_cleanup( man );
    Th_Free( interp, p );
  }
}

/*
** TH command:
**
** ob clean|(end|pop)|flush|get|level|(start|push)
**
** Runs the given subcommand. Some subcommands have other subcommands
** (see their docs for details).
** 
*/
static int ob_cmd(
  Th_Interp *interp, 
  void *ignored, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Ob_Manager * pMan = Th_Ob_GetManager(interp);
  Th_SubCommand aSub[] = {
    { "clean",     ob_clean_command },
    { "end",       ob_end_command },
    { "flush",     ob_flush_command },
    { "get",       ob_get_command },
    { "level",     ob_level_command },
    { "pop",       ob_end_command },
    { "push",      ob_start_command },
    { "start",     ob_start_command },
    /* TODO: enable/disable commands which call Th_OutputEnable(). */
    { 0, 0 }
  };
  assert(NULL != pMan && pMan->interp==interp);
  return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
}

int th_register_ob(Th_Interp * interp){
  int rc;
  static Th_Command_Reg aCommand[] = {
    {"ob",    ob_cmd,   0},
    {0,0,0}
  };
  rc = Th_RegisterCommands( interp, aCommand );
  if(NULL == Th_Ob_GetManager(interp)){
    Th_Ob_Manager * pMan;
    pMan = Th_Malloc(interp, sizeof(Th_Ob_Manager));
    if(!pMan){
      rc = TH_ERROR;
    }else{
      *pMan = Th_Ob_Man_empty;
      pMan->interp = interp;
      assert( -1 == pMan->cursor );
      Th_SetData( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
      assert( NULL != Th_Ob_GetManager(interp) );
    }
  }
  return rc;
}

#undef Th_Ob_Man_empty_m
#undef Th_Vtab_OutputMethods_empty_m
#undef Th_Vtab_OutputMethods_ob_m
#undef Th_Ob_Man_KEY
#endif
/* end TH_ENABLE_OB */

Changes to src/th.h.


1


























2
3
4
5
6
7




































8
9
10
11
12
13
14













15
16

17
18
19
20
21
22

23
24








25
26




27
28
29
30
31
32
33
34
..
52
53
54
55
56
57
58












59
60
61
62








63
64
65
66
67
68
69
70
71
72
73
74
...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
134
135
136
137
138
139
140


141
142
143
144
145
146
147
...
149
150
151
152
153
154
155
156
157
158





159




















160
161
162
163
164
165
166
...
173
174
175
176
177
178
179





180
181







182
183





































































































































































































































/* This header file defines the external interface to the custom Scripting
** Language (TH) interpreter.  TH is very similar to TCL but is not an
** exact clone.
*/

/*




































** Before creating an interpreter, the application must allocate and
** populate an instance of the following structure. It must remain valid
** for the lifetime of the interpreter.
*/
struct Th_Vtab {
  void *(*xMalloc)(unsigned int);
  void (*xFree)(void *);













};
typedef struct Th_Vtab Th_Vtab;


/*
** Opaque handle for interpeter.
*/
typedef struct Th_Interp Th_Interp;


/* 
** Create and delete interpreters. 








*/
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);




void Th_DeleteInterp(Th_Interp *);

/* 
** Evaluate an TH program in the stack frame identified by parameter
** iFrame, according to the following rules:
**
**   * If iFrame is 0, this means the current frame.
**
................................................................................
** begins with "::", the lookup is in the top level (global) frame. 
*/
int Th_GetVar(Th_Interp *, const char *, int);
int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
int Th_UnsetVar(Th_Interp *, const char *, int);













typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);

/* 
** Register new commands. 








*/
int Th_CreateCommand(
  Th_Interp *interp, 
  const char *zName, 
  /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
  Th_CommandProc xProc,
  void *pContext,
  void (*xDel)(Th_Interp *, void *)
);

/* 
** Delete or rename commands.
................................................................................

/* 
** Access the memory management functions associated with the specified
** interpreter.
*/
void *Th_Malloc(Th_Interp *, int);
void Th_Free(Th_Interp *, void *);

/* 
** Functions for handling TH lists.
*/
int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *);

int Th_StringAppend(Th_Interp *, char **, int *, const char *, int);
................................................................................
/* 
** Functions for handling numbers and pointers.
*/
int Th_ToInt(Th_Interp *, const char *, int, int *);
int Th_ToDouble(Th_Interp *, const char *, int, double *);
int Th_SetResultInt(Th_Interp *, int);
int Th_SetResultDouble(Th_Interp *, double);



/*
** Drop in replacements for the corresponding standard library functions.
*/
int th_strlen(const char *);
int th_isdigit(char);
int th_isspace(char);
................................................................................
int th_isspecial(char);
char *th_strdup(Th_Interp *interp, const char *z, int n);

/*
** Interfaces to register the language extensions.
*/
int th_register_language(Th_Interp *interp);            /* th_lang.c */
int th_register_sqlite(Th_Interp *interp);              /* th_sqlite.c */
int th_register_vfs(Th_Interp *interp);                 /* th_vfs.c */
int th_register_testvfs(Th_Interp *interp);             /* th_testvfs.c */





int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */





















/*
** General purpose hash table from th_lang.c.
*/
typedef struct Th_Hash      Th_Hash;
typedef struct Th_HashEntry Th_HashEntry;
struct Th_HashEntry {
................................................................................
void Th_HashDelete(Th_Interp *, Th_Hash *);
void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);

/*
** Useful functions from th_lang.c.
*/





int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);








typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);










































































































































































































>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
<
>
>
>
>
>
>
>
>
>
>
>
>
>


>






>

<
>
>
>
>
>
>
>
>


>
>
>
>
|







 







>
>
>
>
>
>
>
>
>
>
>
>
|


<
>
>
>
>
>
>
>
>




<







 







|







 







>
>







 







<


>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>


>
>
>
>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
...
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
#include "config.h"

/*
** TH_ENABLE_QUERY, if defined, enables the "query" family of functions.
** They provide SELECT-only access to the repository db.
*/
#define TH_ENABLE_QUERY

/*
** TH_ENABLE_OB, if defined, enables the "ob" family of functions.
** They are functionally similar to PHP's ob_start(), ob_end(), etc.
** family of functions, providing output capturing/buffering.
*/
#define TH_ENABLE_OB

/*
** TH_ENABLE_ARGV, if defined, enables the "argv" family of functions.
** They provide access to CLI arguments as well as GET/POST arguments.
** They do not provide access to POST data submitted in JSON mode.
*/
#define TH_ENABLE_ARGV

#ifdef TH_ENABLE_OB
#ifndef INTERFACE
#include "blob.h" /* maintenance reminder: also pulls in fossil_realloc() and friends */
#endif
#endif

/* This header file defines the external interface to the custom Scripting
** Language (TH) interpreter.  TH is very similar to TCL but is not an
** exact clone.
*/

/*
** Th_Output_f() specifies a generic output routine for use by
** Th_Vtab_OutputMethods and friends. Its first argument is the data to
** write, the second is the number of bytes to write, and the 3rd is
** an implementation-specific state pointer (may be NULL, depending on
** the implementation). The return value is the number of bytes output
** (which may differ from len due to encoding and whatnot).  On error
** a negative value must be returned.
*/
typedef int (*Th_Output_f)( char const * zData, int len, void * pState );

/*
** This structure defines the output state associated with a
** Th_Vtab. It is intended that a given Vtab be able to swap out
** output back-ends during its lifetime, e.g. to form a stack of
** buffers.
*/
struct Th_Vtab_OutputMethods {
  Th_Output_f xWrite;   /* output handler */
    void (*xDispose)( void * pState ); /* Called when the framework is done with
                                         this output handler,passed this object's
                                         pState pointer.. */
  void * pState;   /* final argument for xWrite() and xDispose()*/
  char enabled;    /* if 0, Th_Output() does nothing. */
};
typedef struct Th_Vtab_OutputMethods Th_Vtab_OutputMethods;

/*
** Shared Th_Vtab_OutputMethods instance used for copy-initialization. This
** implementation uses Th_Output_f_FILE as its write() impl and
** Th_Output_dispose_FILE() for cleanup. If its pState member is NULL
** it outputs to stdout, else pState must be a (FILE*) which it will
** output to.
*/
extern const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE;

/*
** Before creating an interpreter, the application must allocate and
** populate an instance of the following structure. It must remain valid
** for the lifetime of the interpreter.
*/
struct Th_Vtab {
  void *(*xRealloc)(void *, unsigned int); /**

                                           Re/deallocation routine. Must behave like
                                           realloc(3), with the minor extension that
                                           realloc(anything,positiveValue) _must_ return
                                           NULL on allocation error. The Standard's wording
                                           allows realloc() to return "some value suitable for
                                           passing to free()" on error, but because client code
                                           has no way of knowing if any non-NULL value is an error
                                           value, no sane realloc() implementation would/should
                                           return anything _but_ NULL on allocation error.
                                           */
  Th_Vtab_OutputMethods out;                      /** Output handler. TH functions which generate
                                               output should send it here (via Th_Output()).
                                           */
};
typedef struct Th_Vtab Th_Vtab;


/*
** Opaque handle for interpeter.
*/
typedef struct Th_Interp Th_Interp;


/* 

** Creates a new interpreter instance using the given v-table. pVtab
** must outlive the returned object, and pVtab->out.xDispose() will be
** called when the interpreter is cleaned up. The optional "ob" API
** swaps out Vtab::out instances, so pVtab->out might not be active
** for the entire lifetime of the interpreter.
**
** Potential TODO: we "should probably" add a dispose() method to the
** Th_Vtab interface.
*/
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);

/*
** Frees up all resources associated with interp then frees interp.
*/
void Th_DeleteInterp(Th_Interp *interp);

/* 
** Evaluate an TH program in the stack frame identified by parameter
** iFrame, according to the following rules:
**
**   * If iFrame is 0, this means the current frame.
**
................................................................................
** begins with "::", the lookup is in the top level (global) frame. 
*/
int Th_GetVar(Th_Interp *, const char *, int);
int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
int Th_UnsetVar(Th_Interp *, const char *, int);

/*
** Typedef for Th interpreter callbacks, i.e. script-bound native C
** functions.
**
** The interp argument is the interpreter running the function. pState
** is arbitrary state which is passed to Th_CreateCommand(). arg
** contains the number of arguments (argument #0 is the command's
** name, in the same way that main()'s argv[0] is the binary's
** name). argv is the list of arguments. argl is an array argc items
** long which contains the length of each argument in the
** list. e.g. argv[0] is argl[0] bytes long.
*/
typedef int (*Th_CommandProc)(Th_Interp * interp, void * pState, int argc, const char ** argv, int * argl);

/* 

** Registers a new command with interp. zName must be a NUL-terminated
** name for the function. xProc is the native implementation of the
** function.  pContext is arbitrary data to pass as xProc()'s 2nd
** argument. xDel is an optional finalizer which should be called when
** interpreter is finalized. If xDel is not NULL then it is passed
** (interp,pContext) when interp is finalized.
**
** Return TH_OK on success.
*/
int Th_CreateCommand(
  Th_Interp *interp, 
  const char *zName, 

  Th_CommandProc xProc,
  void *pContext,
  void (*xDel)(Th_Interp *, void *)
);

/* 
** Delete or rename commands.
................................................................................

/* 
** Access the memory management functions associated with the specified
** interpreter.
*/
void *Th_Malloc(Th_Interp *, int);
void Th_Free(Th_Interp *, void *);
void *Th_Realloc(Th_Interp *, void *, int);
/* 
** Functions for handling TH lists.
*/
int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *);

int Th_StringAppend(Th_Interp *, char **, int *, const char *, int);
................................................................................
/* 
** Functions for handling numbers and pointers.
*/
int Th_ToInt(Th_Interp *, const char *, int, int *);
int Th_ToDouble(Th_Interp *, const char *, int, double *);
int Th_SetResultInt(Th_Interp *, int);
int Th_SetResultDouble(Th_Interp *, double);
int Th_TryInt(Th_Interp *, const char * zArg, int nArg, int * piOut);
int Th_TryDouble(Th_Interp *, const char * zArg, int nArg, double * pfOut);

/*
** Drop in replacements for the corresponding standard library functions.
*/
int th_strlen(const char *);
int th_isdigit(char);
int th_isspace(char);
................................................................................
int th_isspecial(char);
char *th_strdup(Th_Interp *interp, const char *z, int n);

/*
** Interfaces to register the language extensions.
*/
int th_register_language(Th_Interp *interp);            /* th_lang.c */

int th_register_vfs(Th_Interp *interp);                 /* th_vfs.c */
int th_register_testvfs(Th_Interp *interp);             /* th_testvfs.c */

/*
** Registers the TCL extensions. Only available if FOSSIL_ENABLE_TCL
** is enabled at compile-time.
*/
int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */

#ifdef TH_ENABLE_ARGV
/*
** Registers the "argv" API. See www/th1_argv.wiki.
*/
int th_register_argv(Th_Interp *interp);                /* th_main.c */
#endif

#ifdef TH_ENABLE_QUERY
/*
** Registers the "query" API. See www/th1_query.wiki.
*/
int th_register_query(Th_Interp *interp);              /* th_main.c */
#endif
#ifdef TH_ENABLE_OB
/*
** Registers the "ob" API. See www/th1_ob.wiki.
*/
int th_register_ob(Th_Interp * interp);                 /* th.c */
#endif

/*
** General purpose hash table from th_lang.c.
*/
typedef struct Th_Hash      Th_Hash;
typedef struct Th_HashEntry Th_HashEntry;
struct Th_HashEntry {
................................................................................
void Th_HashDelete(Th_Interp *, Th_Hash *);
void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);

/*
** Useful functions from th_lang.c.
*/

/*
** Generic "wrong number of arguments" helper which sets the error
** state of interp to the given message plus a generic prefix.
*/
int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);

/*
** Works like Th_WrongNumArgs() but expects (zCmdName,zCmdLen) to be
** the current command's (name,length), i.e. (argv[0],argl[0]).
*/
int Th_WrongNumArgs2(Th_Interp *interp, const char *zCmdName,
                     int zCmdLen, const char *zMsg);

typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);

/*
** Works similarly to Th_CallSubCommand() but adjusts argc/argv/argl
** by 1 before passing on the call to the subcommand. This allows them
** to function the same whether they are called as top-level commands
** or as sub-sub-commands.
*/
int Th_CallSubCommand2(Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl, Th_SubCommand *aSub);

/*
** Sends the given data through vTab->out.f() if vTab->out.enabled is
** true, otherwise this is a no-op. Returns 0 or higher on success, *
** a negative value if vTab->out.f is NULL.
*/
int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int len );

/*
** Sends the given output through pInterp's vtab's output
** implementation. See Th_Vtab_OutputMethods() for the argument and
** return value semantics.
*/
int Th_Output( Th_Interp *pInterp, char const * zData, int len );

/*
** Enables or disables output of the current Vtab API, depending on
** whether flag is true (non-0) or false (0). Note that when output
** buffering/stacking is enabled (e.g. via the "ob" API) this modifies
** only the current output mechanism, and not any further down the
** stack.
*/
void Th_OutputEnable( Th_Interp *pInterp, char flag );

/*
** Returns true if output is enabled for the current output mechanism
** of pInterp, else false. See Th_OutputEnable().
*/
char Th_OutputEnabled( Th_Interp *pInterp );



/*
** A Th_Output_f() implementation which sends its output to either
** pState (which must be NULL or a (FILE*)) or stdout (if pState is
** NULL).
*/
int Th_Output_f_FILE( char const * zData, int len, void * pState );

/*
** A Th_Vtab_OutputMethods::xDispose() impl for FILE handles. If pState is not
** one of the standard streams (stdin, stdout, stderr) then it is
** fclose()d by this call.
*/
void Th_Output_dispose_FILE( void * pState );

/*
** A helper type for holding lists of function registration information.
** For use with Th_RegisterCommands().
*/
struct Th_Command_Reg {
  const char *zName;     /* Function name. */
  Th_CommandProc xProc;  /* Callback function */
  void *pContext;        /* Arbitrary data for the callback. */
};
typedef struct Th_Command_Reg Th_Command_Reg;

/*
** Th_Render_Flags_XXX are flags for Th_Render().
*/
/* makeheaders cannot do enums: enum Th_Render_Flags {...};*/
/*
** Default flags ("compatibility mode").
*/
#define Th_Render_Flags_DEFAULT 0
/*
** If set, Th_Render() will not process $var and $<var>
** variable references outside of TH1 blocks.
*/
#define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)

/*
** Runs the given th1 program through Fossil's th1 interpreter. Flags
** may contain a bitmask made up of any of the Th_Render_Flags_XXX
** values.
*/
int Th_Render(const char *zTh1Program, int Th_Render_Flags);

/*
** Adds a piece of memory to the given interpreter, such that:
**
** a) it will be cleaned up when the interpreter is destroyed, by
** calling finalizer(interp, pData). The finalizer may be NULL.
** Cleanup happens in an unspecified/unpredictable order.
**
** b) it can be fetched via Th_GetData().
**
** If a given key is added more than once then any previous
** entry is cleaned up before adding it.
**
** Returns 0 on success, non-0 on allocation error.
*/
int Th_SetData( Th_Interp * interp, char const * key,
                 void * pData,
                 void (*finalizer)( Th_Interp *, void * ) );

/*
** Fetches data added via Th_SetData(), or NULL if no data
** has been associated with the given key.
*/
void * Th_GetData( Th_Interp * interp, char const * key );


/*
** Registers a list of commands with the interpreter. pList must be a non-NULL
** pointer to an array of Th_Command_Reg objects, the last one of which MUST
** have a NULL zName field (that is the end-of-list marker).
** Returns TH_OK on success, "something else" on error.
*/
int Th_RegisterCommands( Th_Interp * interp, Th_Command_Reg const * pList );

#ifdef TH_ENABLE_OB
/*
** Output buffer stack manager for TH. Used/managed by the Th_ob_xxx()
** functions. This class manages Th_Interp::pVtab->out for a specific
** interpreter, swapping it in and out in order to redirect output
** generated via Th_Output() to internal buffers. The buffers can be
** pushed and popped from the stack, allowing clients to selectively
** capture output for a given block of TH1 code.
*/
struct Th_Ob_Manager {
  Blob ** aBuf;        /* Stack of Blobs */
  int nBuf;            /* Number of blobs */
  int cursor;          /* Current level (-1=not active) */
  Th_Interp * interp;  /* The associated interpreter */
  Th_Vtab_OutputMethods * aOutput
                       /* Stack of output routines corresponding
                          to the current buffering level.
                          Has nBuf entries.
                       */;
};

/*
** Manager of a stack of Th_Vtab_Output objects for output buffering.
** It gets its name ("ob") from the similarly-named PHP functionality.
**
** See Th_Ob_GetManager().
**
** Potential TODO: remove the Blob from the interface and replace it
** with a Th_Output_f (or similar) which clients can pass in to have
** the data transfered from Th_Ob_Manager to them. We would also need to
** add APIs for clearing the buffer.
*/
typedef struct Th_Ob_Manager Th_Ob_Manager;

/*
** Returns the ob manager for the given interpreter. The manager gets
** installed by the th_register_ob(). In Fossil ob support is
** installed automatically if it is available at built time.
*/
Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *ignored);

/*
** Returns the top-most Blob in pMan's stack, or NULL if buffering is
** not active or if the current buffering level does not refer to a
** blob. (Note: the latter will never currently be the case, but may
** be if the API is expanded to offer other output direction options,
** e.g.  (ob start file /tmp/foo.out).)
*/
Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan );

/*
** Pushes a new blob onto pMan's stack. On success returns TH_OK and
** assigns *pOut (if pOut is not NULL) to the new blob (which is owned
** by pMan). On error pOut is not modified and non-0 is returned. The
** new blob can be cleaned up via Th_Ob_Pop() or Th_Ob_PopAndFree()
** (please read both to understand the difference!).
*/
int Th_Ob_Push( Th_Ob_Manager * pMan, Th_Vtab_OutputMethods const * pWriter, Blob ** pOut );

/*
** Pops the top-most output buffer off the stack and returns
** it. Returns NULL if there is no current buffer. When the last
** buffer is popped, pMan's internals are cleaned up (but pMan is not
** freed).
**
** The caller owns the returned object and must eventually clean it up
** by first passing it to blob_reset() and then Th_Free() it.
**
** See also: Th_Ob_PopAndFree().
*/
Blob * Th_Ob_Pop( Th_Ob_Manager * pMan );

/*
** Convenience form of Th_Ob_Pop() which pops and frees the
** top-most buffer. Returns 0 on success, non-0 if there is no
** stack to pop. Thus is can be used in a loop like:
**
** while( !Th_Ob_PopAndFree(theManager) ) {}
*/
int Th_Ob_PopAndFree( Th_Ob_Manager * pMan );

#endif
/* end TH_ENABLE_OB */

Changes to src/th_lang.c.

14
15
16
17
18
19
20













21
22
23
24
25
26
27
...
456
457
458
459
460
461
462

463
464
465

466
467
468
469
470
471
472
...
868
869
870
871
872
873
874




















875
876
877
878
879
880
881
....
1026
1027
1028
1029
1030
1031
1032

1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
....
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
#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(
................................................................................
      argl[2];     /* Space for copies of parameter names and default 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( 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];
................................................................................
      return aSub[i].xProc(interp, ctx, argc, argv, argl);
    }
  }

  Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]);
  return TH_ERROR;
}





















/*
** TH Syntax:
**
**   string compare STR1 STR2
**   string first   NEEDLE HAYSTACK ?STARTINDEX?
**   string is      CLASS STRING
................................................................................
}

/*
** 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[] = {
    {"catch",    catch_command,   0},
    {"expr",     expr_command,    0},
    {"for",      for_command,     0},
    {"if",       if_command,      0},
    {"info",     info_command,    0},
    {"lindex",   lindex_command,  0},
    {"list",     list_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}
  };
  int 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;
}






>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
|
|
|
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>

<
<
<
<
|







 







|
<
<
<
<
<
<
<
<
<
|

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
...
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
....
1061
1062
1063
1064
1065
1066
1067
1068
1069




1070
1071
1072
1073
1074
1075
1076
1077
....
1089
1090
1091
1092
1093
1094
1095
1096









1097
1098
#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;
}

int Th_WrongNumArgs2(Th_Interp *interp, const char *zCmdName,
                     int zCmdLen, const char *zMsg){
  char * zBuf = 0;
  int nBuf = 0;
  Th_StringAppend(interp, &zBuf, &nBuf, zCmdName, zCmdLen);
  Th_StringAppend(interp, &zBuf, &nBuf, ": wrong # args: expecting: ", -1);
  Th_StringAppend(interp, &zBuf, &nBuf, zMsg, -1);
  Th_StringAppend(interp, &zBuf, &nBuf, "", 1);
  Th_ErrorMessage(interp, zBuf, NULL, 0);
  Th_Free(interp, zBuf);
  return TH_ERROR;
}

/*
** Syntax: 
**
**   catch script ?varname?
*/
static int catch_command(
................................................................................
      argl[2];     /* Space for copies of parameter names and default 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( 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];
................................................................................
      return aSub[i].xProc(interp, ctx, argc, argv, argl);
    }
  }

  Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]);
  return TH_ERROR;
}

int Th_CallSubCommand2(
  Th_Interp *interp, 
  void *ctx,
  int argc,
  const char **argv,
  int *argl,
  Th_SubCommand *aSub
){
  int i;
  for(i=0; aSub[i].zName; i++){
    char const *zName = aSub[i].zName;
    if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){
      return aSub[i].xProc(interp, ctx, argc-1, argv+1, argl+1);
    }
  }
  Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]);
  return TH_ERROR;
}


/*
** TH Syntax:
**
**   string compare STR1 STR2
**   string first   NEEDLE HAYSTACK ?STARTINDEX?
**   string is      CLASS STRING
................................................................................
}

/*
** 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){
  int rc;
  /* Array of built-in commands. */




  struct Th_Command_Reg aCommand[] = {
    {"catch",    catch_command,   0},
    {"expr",     expr_command,    0},
    {"for",      for_command,     0},
    {"if",       if_command,      0},
    {"info",     info_command,    0},
    {"lindex",   lindex_command,  0},
    {"list",     list_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}
  };
  rc = Th_RegisterCommands(interp, aCommand);









  return rc;
}

Changes to src/th_main.c.

17
18
19
20
21
22
23






24
25
26
27
28
29
30
..
38
39
40
41
42
43
44









45








46
47

48
49
50
51
52
53
54
..
55
56
57
58
59
60
61








62
63
64
65
66
67
68
..
71
72
73
74
75
76
77
78





79
80
81









82
83
84
85
86
87




88
89
90
91
92
93
94
95
96
97
98
99
100
101
102















103
104
105
106
107
108



109
110
111
112
113
114
115
116



117
118


119
120









121
122
123
124
125
126
127
...
130
131
132
133
134
135
136
137


138
139
140
141
142
143
144
...
156
157
158
159
160
161
162
163


164
165
166
167
168
169
170






















































171
172
173
174
175
176
177
...
203
204
205
206
207
208
209
210


211
212
213
214
215
216
217
...
234
235
236
237
238
239
240
241


242
243
244
245
246
247
248
...
278
279
280
281
282
283
284
285


286
287
288
289
290
291
292
...
308
309
310
311
312
313
314


315
316
317
318
319
320
321
322
...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
...
366
367
368
369
370
371
372
373


374
375
376
377
378
379
380
...
401
402
403
404
405
406
407
408


409
410
411
412
413
414
415
416
417
418
419


420





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































421
422
423
424
425
426
427
428
429
430











431
432
433

434
435
436
437

438
439
440

441

442

443


444
445
446
447







448
449
450
451
452
453
454
455
456
457
458
459










460
461
462
463
464
465
466
...
551
552
553
554
555
556
557
558


559
560
561
562
563
564

565
566
567
568
569
570

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618












619
620
621
622
623

624
625
626





627
628
629
**
** This file contains an interface between the TH scripting language
** (an independent project) and fossil.
*/
#include "config.h"
#include "th_main.h"







/*
** Global variable counting the number of outstanding calls to malloc()
** made by the th1 implementation. This is used to catch memory leaks
** in the interpreter. Obviously, it also means th1 is not threadsafe.
*/
static int nOutstandingMalloc = 0;

................................................................................
  }
  return p;
}
static void xFree(void *p){
  if( p ){
    nOutstandingMalloc--;
  }









  free(p);








}
static Th_Vtab vtab = { xMalloc, xFree };


/*
** Generate a TH1 trace message if debugging is enabled.
*/
void Th_Trace(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
................................................................................
  blob_vappendf(&g.thLog, zFormat, ap);
  va_end(ap);
}


/*
** True if output is enabled.  False if disabled.








*/
static int enableOutput = 1;

/*
** TH command:     enable_output BOOLEAN
**
** Enable or disable the puts and hputs commands.
................................................................................
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "enable_output BOOLEAN");





  }
  return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
}










/*
** Send text to the appropriate output:  Either to the console
** or to the CGI reply buffer.
*/
static void sendText(const char *z, int n, int encode){




  if( enableOutput && n ){
    if( n<0 ) n = strlen(z);
    if( encode ){
      z = htmlize(z, n);
      n = strlen(z);
    }
    if( g.cgiOutput ){
      cgi_append_content(z, n);
    }else{
      fwrite(z, 1, n, stdout);
      fflush(stdout);
    }
    if( encode ) free((char*)z);
  }
}
















/*
** TH command:     puts STRING
** TH command:     html STRING
**
** Output STRING as HTML (html) or unchanged (puts).  



*/
static int putsCmd(
  Th_Interp *interp, 
  void *pConvert, 
  int argc, 
  const char **argv, 
  int *argl
){



  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "puts STRING");


  }
  sendText((char*)argv[1], argl[1], pConvert!=0);









  return TH_OK;
}

/*
** TH command:      wiki STRING
**
** Render the input string as wiki.
................................................................................
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "wiki STRING");


  }
  if( enableOutput ){
    Blob src;
    blob_init(&src, (char*)argv[1], argl[1]);
    wiki_convert(&src, 0, WIKI_INLINE);
    blob_reset(&src);
  }
................................................................................
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  char *zOut;
  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "htmlize STRING");


  }
  zOut = htmlize((char*)argv[1], argl[1]);
  Th_SetResult(interp, zOut, -1);
  free(zOut);
  return TH_OK;
}























































/*
** TH command:      date
**
** Return a string which is the current time and date.  If the
** -local option is used, the date appears using localtime instead
** of UTC.
*/
................................................................................
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  int rc;
  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "hascap STRING");


  }
  rc = login_has_capability((char*)argv[1],argl[1]);
  if( g.thTrace ){
    Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
  }
  Th_SetResultInt(interp, rc);
  return TH_OK;
................................................................................
  int argc, 
  const char **argv, 
  int *argl
){
  int rc = 0;
  char const * zArg;
  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "hasfeature STRING");


  }
  zArg = (char const*)argv[1];
  if(NULL==zArg){
    /* placeholder for following ifdefs... */
  }
#if defined(FOSSIL_ENABLE_JSON)
  else if( 0 == fossil_strnicmp( zArg, "json", 4 ) ){
................................................................................
  int argc, 
  const char **argv, 
  int *argl
){
  int rc = 0;
  int i;
  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "anycap STRING");


  }
  for(i=0; rc==0 && i<argl[1]; i++){
    rc = login_has_capability((char*)&argv[1][i],1);
  }
  if( g.thTrace ){
    Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
  }
................................................................................
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc!=4 ){


    return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES");
  }
  if( enableOutput ){
    int height;
    Blob name;
    int nValue;
    const char *zValue;
    char *z, *zH;
................................................................................

    if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
    Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem);
    blob_init(&name, (char*)argv[1], argl[1]);
    zValue = Th_Fetch(blob_str(&name), &nValue);
    z = mprintf("<select name=\"%z\" size=\"%d\">", 
                 htmlize(blob_buffer(&name), blob_size(&name)), height);
    sendText(z, -1, 0);
    free(z);
    blob_reset(&name);
    for(i=0; i<nElem; i++){
      zH = htmlize((char*)azElem[i], aszElem[i]);
      if( zValue && aszElem[i]==nValue 
             && memcmp(zValue, azElem[i], nValue)==0 ){
        z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
                     zH, zH);
      }else{
        z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
      }
      free(zH);
      sendText(z, -1, 0);
      free(z);
    }
    sendText("</select>", -1, 0);
    Th_Free(interp, azElem);
  }
  return TH_OK;
}

/*
** TH1 command:     linecount STRING MAX MIN
................................................................................
  const char **argv, 
  int *argl
){
  const char *z;
  int size, n, i;
  int iMin, iMax;
  if( argc!=4 ){
    return Th_WrongNumArgs(interp, "linecount STRING MAX MIN");


  }
  if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
  if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
  z = argv[1];
  size = argl[1];
  for(n=1, i=0; i<size; i++){
    if( z[i]=='\n' ){
................................................................................
  int argc, 
  const char **argv, 
  int *argl
){
  int openRepository;

  if( argc!=1 && argc!=2 ){
    return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");


  }
  if( argc==2 ){
    if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
      return TH_ERROR;
    }
    if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
  }
  Th_SetResult(interp, g.zRepositoryName, -1);
  return TH_OK;
}



/*





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































** Make sure the interpreter has been initialized.  Initialize it if
** it has not been already.
**
** The interpreter is stored in the g.interp global variable.
*/
void Th_FossilInit(void){
  static struct _Command {
    const char *zName;
    Th_CommandProc xProc;
    void *pContext;











  } aCommand[] = {
    {"anycap",        anycapCmd,            0},
    {"combobox",      comboboxCmd,          0},

    {"enable_output", enableOutputCmd,      0},
    {"linecount",     linecntCmd,           0},
    {"hascap",        hascapCmd,            0},
    {"hasfeature",    hasfeatureCmd,        0},

    {"htmlize",       htmlizeCmd,           0},
    {"date",          dateCmd,              0},
    {"html",          putsCmd,              0},

    {"puts",          putsCmd,       (void*)1},

    {"wiki",          wikiCmd,              0},

    {"repository",    repositoryCmd,        0},


    {0, 0, 0}
  };
  if( g.interp==0 ){
    int i;







    g.interp = Th_CreateInterp(&vtab);
    th_register_language(g.interp);       /* Basic scripting commands. */
#ifdef FOSSIL_ENABLE_TCL
    if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
      th_register_tcl(g.interp, &g.tcl);  /* Tcl integration commands. */
    }
#endif
    for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
      if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
      Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
                       aCommand[i].pContext, 0);
    }










  }
}

/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
................................................................................
    i += 2;
  }
  return i;
}

/*
** The z[] input contains text mixed with TH1 scripts.
** The TH1 scripts are contained within <th1>...</th1>. 


** TH1 variables are $aaa or $<aaa>.  The first form of
** variable is literal.  The second is run through htmlize
** before being inserted.
**
** This routine processes the template and writes the results
** on either stdout or into CGI.

*/
int Th_Render(const char *z){
  int i = 0;
  int n;
  int rc = TH_OK;
  char *zResult;

  Th_FossilInit();
  while( z[i] ){
    if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
      const char *zVar;
      int nVar;
      int encode = 1;
      sendText(z, i, 0);
      if( z[i+1]=='<' ){
        /* Variables of the form $<aaa> are html escaped */
        zVar = &z[i+2];
        nVar = n-2;
      }else{
        /* Variables of the form $aaa are output raw */
        zVar = &z[i+1];
        nVar = n;
        encode = 0;
      }
      rc = Th_GetVar(g.interp, (char*)zVar, nVar);
      z += i+1+n;
      i = 0;
      zResult = (char*)Th_GetResult(g.interp, &n);
      sendText((char*)zResult, n, encode);
    }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
      sendText(z, i, 0);
      z += i+5;
      for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
      rc = Th_Eval(g.interp, 0, (const char*)z, i);
      if( rc!=TH_OK ) break;
      z += i;
      if( z[0] ){ z += 6; }
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==TH_ERROR ){
    sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0);
    zResult = (char*)Th_GetResult(g.interp, &n);
    sendText((char*)zResult, n, 1);
    sendText("</p>", -1, 0);
  }else{
    sendText(z, i, 0);
  }
  return rc;
}

/*
** COMMAND: test-th-render












*/
void test_th_render(void){
  Blob in;
  if( g.argc<3 ){
    usage("FILE");

  }
  db_open_config(0); /* Needed for global "tcl" setting. */
  blob_zero(&in);





  blob_read_from_file(&in, g.argv[2]);
  Th_Render(blob_str(&in));
}






>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
<
>







 







>
>
>
>
>
>
>
>







 







|
>
>
>
>
>

<

>
>
>
>
>
>
>
>
>





|
>
>
>
>






|
|
<
<
<
|
<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
>
>
>








>
>
>
|
|
>
>

<
>
>
>
>
>
>
>
>
>







 







|
>
>







 







|
>
>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>







 







|
>
>







 







|
>
>







 







>
>
|







 







|












|


|







 







|
>
>







 







|
>
>











>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






<
<
<
<
>
>
>
>
>
>
>
>
>
>
>
|


>

<


>

<
<
>
|
>
|
>

>
>




>
>
>
>
>
>
>







|
|
|
|
<
>
>
>
>
>
>
>
>
>
>







 







|
>
>
|
|
|


<
>

|



|
>


|



|










|


|
|

|












|
|
|
|

|






>
>
>
>
>
>
>
>
>
>
>
>





>

<

>
>
>
>
>

|

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143



144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
...
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
...
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
...
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
...
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
...
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066




2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082

2083
2084
2085
2086


2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116

2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
....
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232

2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307

2308
2309
2310
2311
2312
2313
2314
2315
2316
**
** This file contains an interface between the TH scripting language
** (an independent project) and fossil.
*/
#include "config.h"
#include "th_main.h"

#ifdef TH_ENABLE_QUERY
#ifndef INTERFACE
#include "sqlite3.h"
#endif
#endif

/*
** Global variable counting the number of outstanding calls to malloc()
** made by the th1 implementation. This is used to catch memory leaks
** in the interpreter. Obviously, it also means th1 is not threadsafe.
*/
static int nOutstandingMalloc = 0;

................................................................................
  }
  return p;
}
static void xFree(void *p){
  if( p ){
    nOutstandingMalloc--;
  }
  fossil_free(p);
}

/*
** Default Th_Vtab::xRealloc() implementation.
*/
static void *xRealloc(void * p, unsigned int n){
  assert(n>=0 && "Invalid memory (re/de)allocation size.");
  if(0 == n){
    xFree(p);
    return NULL;
  }else if(NULL == p){
    return xMalloc(n);
  }else{
    return fossil_realloc(p, n)
      /* In theory nOutstandingMalloc doesn't need to be updated here
         unless xRealloc() is sorely misused.
      */;
  }

}

/*
** Generate a TH1 trace message if debugging is enabled.
*/
void Th_Trace(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
................................................................................
  blob_vappendf(&g.thLog, zFormat, ap);
  va_end(ap);
}


/*
** True if output is enabled.  False if disabled.
**
** We "could" replace this with Th_OutputEnable() and friends, but
** there is a functional difference: this particular flag prohibits
** some extra escaping which would happen (but be discared, unused) if
** relied solely on that API. Also, because that API only works on the
** current Vtab_Output handler, relying soly on that handling would
** introduce incompatible behaviour with the historical enable_output
** command.
*/
static int enableOutput = 1;

/*
** TH command:     enable_output BOOLEAN
**
** Enable or disable the puts and hputs commands.
................................................................................
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                           "BOOLEAN");
  }else{
    int rc = Th_ToInt(interp, argv[1], argl[1], &enableOutput);
    return rc;
  }

}

/*
** Th_Output_f() impl which sends all output to cgi_append_content().
*/
static int Th_Output_f_cgi_content( char const * zData, int nData, void * pState ){
  cgi_append_content(zData, nData);
  return nData;
}


/*
** Send text to the appropriate output:  Either to the console
** or to the CGI reply buffer.
*/
static void sendText(Th_Interp *pInterp, const char *z, int n, int encode){
  if(NULL == pInterp){
    pInterp = g.interp;
  }
  assert( NULL != pInterp );
  if( enableOutput && n ){
    if( n<0 ) n = strlen(z);
    if( encode ){
      z = htmlize(z, n);
      n = strlen(z);
    }
    Th_Output( pInterp, z, n );
    if( encode ) fossil_free((char*)z);



  }

}

/*
** Internal state for the putsCmd() function, allowing it to be used
** as the basis for multiple implementations with slightly different
** behaviours based on the context. An instance of this type must be
** set as the Context parameter for any putsCmd()-based script command
** binding.
*/
struct PutsCmdData {
  char escapeHtml;    /* If true, htmlize all output. */
  char const * sep;   /* Optional NUL-terminated separator to output
                         between arguments. May be NULL. */
  char const * eol;   /* Optional NUL-terminated end-of-line separator,
                         output after the final argument. May be NULL. */
};
typedef struct PutsCmdData PutsCmdData;

/*
** TH command:     puts STRING
** TH command:     html STRING
**
** Output STRING as HTML (html) or unchanged (puts).
**
** pConvert MUST be a (PutsCmdData [const]*). It is not modified by
** this function.
*/
static int putsCmd(
  Th_Interp *interp, 
  void *pConvert, 
  int argc, 
  const char **argv, 
  int *argl
){
  PutsCmdData const * fmt = (PutsCmdData const *)pConvert;
  const int sepLen = fmt->sep ? strlen(fmt->sep) : 0;
  int i;
  if( argc<2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING ...STRING_N");
  }

  for( i = 1; i < argc; ++i ){
    if(sepLen && (i>1)){
      sendText(interp, fmt->sep, sepLen, 0);
    }
    sendText(interp, (char const*)argv[i], argl[i], fmt->escapeHtml);
  }
  if(fmt->eol){
    sendText(interp, fmt->eol, strlen(fmt->eol), 0);
  }
  return TH_OK;
}

/*
** TH command:      wiki STRING
**
** Render the input string as wiki.
................................................................................
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING");
  }
  if( enableOutput ){
    Blob src;
    blob_init(&src, (char*)argv[1], argl[1]);
    wiki_convert(&src, 0, WIKI_INLINE);
    blob_reset(&src);
  }
................................................................................
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  char *zOut;
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING");
  }
  zOut = htmlize((char*)argv[1], argl[1]);
  Th_SetResult(interp, zOut, -1);
  free(zOut);
  return TH_OK;
}

#if 0
/* This is not yet needed, but something like it may become useful for
   custom page/command support, for rendering snippets/templates. */
/*
** TH command:      render STRING
**
** Render the input string as TH1.
*/
static int renderCmd(
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc<2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING ?STRING...?");
  }else{
    Th_Ob_Manager * man = Th_Ob_GetManager(interp);
    Blob * b = NULL;
    Blob buf = empty_blob;
    int rc, i;
    /*FIXME: assert(NULL != man && man->interp==interp);*/
    man->interp = interp;
    /* Combine all inputs into one buffer so that we can use that to
       embed TH1 tags across argument boundaries.

       FIX:E optimize away buf for the 1-arg case.
     */
    for( i = 1; TH_OK==rc && i < argc; ++i ){
      char const * str = argv[i];
      blob_append( &buf, str, argl[i] );
      /*rc = Th_Render( str, Th_Render_Flags_NO_DOLLAR_DEREF );*/
    }
    rc = Th_Ob_Push( man, &b );
    if(rc){
      blob_reset( &buf );
      return rc;
    }
    rc = Th_Render( buf.aData, Th_Render_Flags_DEFAULT );
    blob_reset(&buf);
    b = Th_Ob_Pop( man );
    if(TH_OK==rc){
      Th_SetResult( interp, b->aData, b->nUsed );
    }
    blob_reset( b );
    Th_Free( interp, b );
    return rc;
  }
}/* renderCmd() */
#endif

/*
** TH command:      date
**
** Return a string which is the current time and date.  If the
** -local option is used, the date appears using localtime instead
** of UTC.
*/
................................................................................
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  int rc;
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                           argv[0], argl[0],
                           "STRING");
  }
  rc = login_has_capability((char*)argv[1],argl[1]);
  if( g.thTrace ){
    Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
  }
  Th_SetResultInt(interp, rc);
  return TH_OK;
................................................................................
  int argc, 
  const char **argv, 
  int *argl
){
  int rc = 0;
  char const * zArg;
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING");
  }
  zArg = (char const*)argv[1];
  if(NULL==zArg){
    /* placeholder for following ifdefs... */
  }
#if defined(FOSSIL_ENABLE_JSON)
  else if( 0 == fossil_strnicmp( zArg, "json", 4 ) ){
................................................................................
  int argc, 
  const char **argv, 
  int *argl
){
  int rc = 0;
  int i;
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING");
  }
  for(i=0; rc==0 && i<argl[1]; i++){
    rc = login_has_capability((char*)&argv[1][i],1);
  }
  if( g.thTrace ){
    Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
  }
................................................................................
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  if( argc!=4 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "NAME TEXT-LIST NUMLINES");
  }
  if( enableOutput ){
    int height;
    Blob name;
    int nValue;
    const char *zValue;
    char *z, *zH;
................................................................................

    if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
    Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem);
    blob_init(&name, (char*)argv[1], argl[1]);
    zValue = Th_Fetch(blob_str(&name), &nValue);
    z = mprintf("<select name=\"%z\" size=\"%d\">", 
                 htmlize(blob_buffer(&name), blob_size(&name)), height);
    sendText(interp, z, -1, 0);
    free(z);
    blob_reset(&name);
    for(i=0; i<nElem; i++){
      zH = htmlize((char*)azElem[i], aszElem[i]);
      if( zValue && aszElem[i]==nValue 
             && memcmp(zValue, azElem[i], nValue)==0 ){
        z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
                     zH, zH);
      }else{
        z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
      }
      free(zH);
      sendText(interp, z, -1, 0);
      free(z);
    }
    sendText(interp, "</select>", -1, 0);
    Th_Free(interp, azElem);
  }
  return TH_OK;
}

/*
** TH1 command:     linecount STRING MAX MIN
................................................................................
  const char **argv, 
  int *argl
){
  const char *z;
  int size, n, i;
  int iMin, iMax;
  if( argc!=4 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING MAX MIN");
  }
  if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
  if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
  z = argv[1];
  size = argl[1];
  for(n=1, i=0; i<size; i++){
    if( z[i]=='\n' ){
................................................................................
  int argc, 
  const char **argv, 
  int *argl
){
  int openRepository;

  if( argc!=1 && argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "?BOOLEAN?");
  }
  if( argc==2 ){
    if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
      return TH_ERROR;
    }
    if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
  }
  Th_SetResult(interp, g.zRepositoryName, -1);
  return TH_OK;
}


#ifdef TH_ENABLE_ARGV
/*
** TH command:
**
** argv len
**
** Returns the number of command-line arguments.
*/
static int argvArgcCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_SetResultInt( interp, g.argc );
  return TH_OK;
}



/*
** TH command:
**
** argv at Index
**
** Returns the raw argument at the given index, throwing if
** out of bounds.
*/
static int argvGetAtCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  char const * zVal;
  int pos = 0;
  if( argc != 2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "Index");
  }
  if( TH_OK != Th_ToInt(interp, argv[1], argl[1], &pos) ){
    return TH_ERROR;
  }
  if( pos < 0 || pos >= g.argc ){
    Th_ErrorMessage(interp, "Argument out of range:", argv[1], argl[1]);
    return TH_ERROR;
  }
  if( 0 == pos ){/*special case*/
    zVal = fossil_nameofexe();
  }else{
    zVal = (pos>0 && pos<g.argc) ? g.argv[pos] : 0;
  }
  Th_SetResult( interp, zVal, zVal ? strlen(zVal) : 0 );
  return TH_OK;  
}


/*
** TH command:
**
** argv getstr longName ??shortName? ?defaultValue??
**
** Functions more or less like Fossil's find_option().
** If the given argument is found then its value is returned,
** else defaultValue is returned. If that is not set
** and the option is not found, an error is thrown.
** If defaultValue is provided, shortName must also be provided
** but it may be empty. For example:
**
** set foo [argv getstr foo "" "hi, world"]
**
** ACHTUNG: find_option() removes any entries it finds from
** g.argv, such that future calls to find_option() will not
** find the same option.
*/
static int argvFindOptionStringCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  enum { BufLen = 100 };
  char zLong[BufLen] = {0};
  char zShort[BufLen] = {0};
  char aBuf[BufLen] = {0};
  int hasArg;
  char const * zVal = NULL;
  char const * zDefault = NULL;
  int check;
  if( 1 < argc ){
    assert( argl[1] < BufLen );
    check = snprintf( zLong, BufLen, "%s", argv[1] );
    assert( check <= BufLen );
  }
  if( (2 < argc) && (0 < argl[2]) ){
    assert( argl[2] < BufLen );
    check = snprintf( zShort, BufLen, "%s", argv[2] );
    assert( check <= BufLen );
  }
  if( 3 < argc){
    zDefault = argv[3];
  }

  if(0 == zLong[0]){
    return Th_WrongNumArgs2(interp,
                           argv[0], argl[0],
                           "longName ?shortName? ?defaultVal?");
  }
  if(g.cgiOutput){
      zVal = cgi_parameter( zLong, NULL );
      if( !zVal && zShort[0] ){
          zVal = cgi_parameter( zShort, NULL );
      }
  }else{
      zVal = find_option( zLong, zShort[0] ? zShort : NULL, 1 );
  }
  if(!zVal){
    zVal = zDefault;
    if(!zVal){
      Th_ErrorMessage(interp, "Option not found and no default provided:", zLong, -1);
      return TH_ERROR;
    }
  }
  Th_SetResult( interp, zVal, zVal ? strlen(zVal) : 0 );
  return TH_OK;  
}

/*
** TH command:
**
** argv getbool longName ??shortName? ?defaultValue??
**
** Works just like argv getstr but treats any empty value or one
** starting with the digit '0' as a boolean false.
**
** Returns the result as an integer 0 (false) or 1 (true).
*/
static int argvFindOptionBoolCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  /* FIXME: refactor to re-use the code from getstr */
  enum { BufLen = 100 };
  char zLong[BufLen] = {0};
  char zShort[BufLen] = {0};
  char aBuf[BufLen] = {0};
  int hasArg;
  char const * zVal = NULL;
  char const * zDefault = NULL;
  int val;
  int rc;
  int check;
  if( 1 < argc ){
    assert( argl[1] < BufLen );
    check = snprintf( zLong, BufLen, "%s", argv[1] );
    assert( check <= BufLen );
  }
  if( (2 < argc) && (0 < argl[2]) ){
    assert( argl[2] < BufLen );
    check = snprintf( zShort, BufLen, "%s", argv[2] );
    assert( check <= BufLen );
  }
  if( 3 < argc){
    zDefault = argv[3];
  }

  if(0 == zLong[0]){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                           "longName ?shortName? ?defaultVal?");
  }
  if(g.cgiOutput){
      zVal = cgi_parameter( zLong, NULL );
      if( !zVal && zShort[0] ){
          zVal = cgi_parameter( zShort, NULL );
      }
  }else{
      zVal = find_option( zLong, zShort[0] ? zShort : NULL, 0 );
  }
  if(zVal && !*zVal){
    zVal = "1";
  }
  if(!zVal){
    zVal = zDefault;
    if(!zVal){
      Th_ErrorMessage(interp, "Option not found and no default provided:", zLong, -1);
      return TH_ERROR;
    }
  }
  if( !*zVal ){
    zVal = "0";
  }
  zVal = (zVal && *zVal && (*zVal!='0')) ? zVal : 0;
  Th_SetResultInt( interp, zVal ? 1 : 0 );
  return TH_OK;
}

/*
** TH command:
**
** argv getint longName ?shortName? ?defaultValue?
**
** Works like argv getstr but returns the value as an integer
** (throwing an error if the argument cannot be converted).
*/
static int argvFindOptionIntCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  /* FIXME: refactor to re-use the code from getstr */
  enum { BufLen = 100 };
  char zLong[BufLen] = {0};
  char zShort[BufLen] = {0};
  char aBuf[BufLen] = {0};
  int hasArg;
  char const * zVal = NULL;
  char const * zDefault = NULL;
  int val = 0;
  int check;
  if( 1 < argc ){
    assert( argl[1] < BufLen );
    check = snprintf( zLong, BufLen, "%s", argv[1] );
    assert( check <= BufLen );
  }
  if( (2 < argc) && (0 < argl[2]) ){
    assert( argl[2] < BufLen );
    check = snprintf( zShort, BufLen, "%s", argv[2] );
    assert( check <= BufLen );
  }
  if( 3 < argc){
    zDefault = argv[3];
  }

  if(0 == zLong[0]){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                           "longName ?shortName? ?defaultVal?");
  }
  if(g.cgiOutput){
      zVal = cgi_parameter( zLong, NULL );
      if( !zVal && zShort[0] ){
          zVal = cgi_parameter( zShort, NULL );
      }
  }else{
      zVal = find_option( zLong, zShort[0] ? zShort : NULL, 1 );
  }
  if(!zVal){
    zVal = zDefault;
    if(!zVal){
      Th_ErrorMessage(interp, "Option not found and no default provided:", zLong, -1);
      return TH_ERROR;
    }
  }
  Th_ToInt(interp, zVal, strlen(zVal), &val);
  Th_SetResultInt( interp, val );
  return TH_OK;  
}

/*
** TH command:
**
** argv subcommand
**
** This is the top-level dispatching function.
*/
static int argvTopLevelCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  static Th_SubCommand aSub[] = {
    {"len",      argvArgcCmd},
    {"at",       argvGetAtCmd},
    {"getstr",   argvFindOptionStringCmd},
    {"string",   argvFindOptionStringCmd},
    {"getbool",  argvFindOptionBoolCmd},
    {"bool",     argvFindOptionBoolCmd},
    {"getint",   argvFindOptionIntCmd},
    {"int",      argvFindOptionIntCmd},
    {0, 0}
  };
  Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
}

int th_register_argv(Th_Interp *interp){
  static Th_Command_Reg aCommand[] = {
    {"argv",            argvTopLevelCmd, 0 },
    {0, 0, 0}
  };
  Th_RegisterCommands( interp, aCommand );
}

#endif
/* end TH_ENABLE_ARGV */

#ifdef TH_ENABLE_QUERY

/*
** Adds the given prepared statement to the interpreter. Returns the
** statement's opaque identifier (a positive value). Ownerships of
** pStmt is transfered to interp and it must be cleaned up by the
** client by calling Th_query_FinalizeStmt(), passing it the value returned
** by this function.
**
** If interp is destroyed before all statements are finalized,
** it will finalize them but may emit a warning message.
*/
static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);


/*
** Internal state for the "query" API.
*/
struct Th_Query {
  sqlite3_stmt ** aStmt; /* Array of statement handles. */
  int nStmt;             /* number of entries in aStmt. */
  int colCmdIndex;       /* column index argument. Set by some top-level dispatchers
                            for their subcommands.
                         */
};
/*
** Internal key for use with Th_Data_Add().
*/
#define Th_Query_KEY "Th_Query"
typedef struct Th_Query Th_Query;

/*
** Returns the Th_Query object associated with the given interpreter,
** or 0 if there is not one.
*/
static Th_Query * Th_query_manager( Th_Interp * interp ){
  void * p = Th_GetData( interp, Th_Query_KEY );
  return p ? (Th_Query*)p : NULL;
}

static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
  Th_Query * sq = Th_query_manager(interp);
  int i, x;
  sqlite3_stmt * s;
  sqlite3_stmt ** list = sq->aStmt;
  for( i = 0; i < sq->nStmt; ++i ){
    s = list[i];
    if(NULL==s){
      list[i] = pStmt;
      return i+1;
    }
  }
  x = (sq->nStmt + 1) * 2;
  list = (sqlite3_stmt**)fossil_realloc( list, sizeof(sqlite3_stmt*)*x );
  for( i = sq->nStmt; i < x; ++i ){
    list[i] = NULL;
  }
  list[sq->nStmt] = pStmt;
  x = sq->nStmt;
  sq->nStmt = i;
  sq->aStmt = list;
  return x + 1;
}


/*
** Expects stmtId to be a statement identifier returned by
** Th_query_AddStmt(). On success, finalizes the statement and returns 0.
** On error (statement not found) non-0 is returned. After this
** call, some subsequent call to Th_query_AddStmt() may return the
** same statement ID.
*/
static int Th_query_FinalizeStmt(Th_Interp *interp, int stmtId){
  Th_Query * sq = Th_query_manager(interp);
  sqlite3_stmt * st;
  int rc = 0;
  assert( stmtId>0 && stmtId<=sq->nStmt );
  st = sq->aStmt[stmtId-1];
  if(NULL != st){
    sq->aStmt[stmtId-1] = NULL;
    sqlite3_finalize(st);
    return 0;
  }else{
    return 1;
  }
}

/*
** Works like Th_query_FinalizeStmt() but takes a statement pointer, which
** must have been Th_query_AddStmt()'d to the given interpreter.
*/
static int Th_query_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt * pSt){
  Th_Query * sq = Th_query_manager(interp);
  int i = 0;
  sqlite3_stmt * st = NULL;
  int rc = 0;
  for( ; i < sq->nStmt; ++i ){
    st = sq->aStmt[i];
    if(st == pSt) break;
  }
  if( st == pSt ){
    assert( i>=0 && i<sq->nStmt );
    sq->aStmt[i] = NULL;
    sqlite3_finalize(st);
    return 0;
  }else{
    return 1;
  }
}


/*
** Fetches the statement with the given ID, as returned by
** Th_query_AddStmt(). Returns NULL if stmtId does not refer (or no longer
** refers) to a statement added via Th_query_AddStmt().
*/
static sqlite3_stmt * Th_query_GetStmt(Th_Interp *interp, int stmtId){
  Th_Query * sq = Th_query_manager(interp);
  return (!sq || (stmtId<1) || (stmtId > sq->nStmt))
    ? NULL
    : sq->aStmt[stmtId-1];
}


/*
** Th_GCEntry finalizer which requires that p be a (Th_Query*).
*/
static void finalizerSqlite( Th_Interp * interp, void * p ){
  Th_Query * sq = (Th_Query *)p;
  int i;
  sqlite3_stmt * st = NULL;
  if(!sq) {
    fossil_warning("Got a finalizer call for a NULL Th_Query.");
    return;
  }
  for( i = 0; i < sq->nStmt; ++i ){
    st = sq->aStmt[i];
    if(NULL != st){
      fossil_warning("Auto-finalizing unfinalized "
                     "statement id #%d: %s",
                     i+1, sqlite3_sql(st));
      Th_query_FinalizeStmt( interp, i+1 );
    }
  }
  Th_Free(interp, sq->aStmt);
  Th_Free(interp, sq);
}


/*
** TH command:
**
** query prepare SQL
**
** Returns an opaque statement identifier.
*/
static int queryPrepareCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  char const * zSql;
  sqlite3_stmt * pStmt = NULL;
  int rc;
  char const * errMsg = NULL;
  if( argc!=2 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "STRING");
  }
  zSql = argv[1];
  rc = sqlite3_prepare( g.db, zSql, strlen(zSql), &pStmt, NULL );
  if(SQLITE_OK==rc){
    if(sqlite3_column_count( pStmt ) < 1){
      errMsg = "Only SELECT-like queries are supported.";
      rc = SQLITE_ERROR;
      sqlite3_finalize( pStmt );
      pStmt = NULL;
    }
  }else{
    errMsg = sqlite3_errmsg( g.db );
  }
  if(SQLITE_OK!=rc){
    assert(NULL != errMsg);
    assert(NULL == pStmt);
    Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
    return TH_ERROR;
  }
  rc = Th_query_AddStmt( interp, pStmt );
  assert( rc >= 0 && "AddStmt failed.");
  Th_SetResultInt( interp, rc );
  return TH_OK;
}

/*
** Tries to convert arg, which must be argLen bytes long, to a
** statement handle id and, in turn, to a sqlite3_stmt. On success
** (the argument references a prepared statement) it returns the
** handle and stmtId (if not NULL) is assigned to the integer value of
** arg. On error NULL is returned and stmtId might be modified (if not
** NULL). If stmtId is unmodified after an error then it is not a
** number, else it is a number but does not reference an opened
** statement.
*/
static sqlite3_stmt * queryStmtHandle(Th_Interp *interp, char const * arg, int argLen, int * stmtId ){
  int rc = 0;
  sqlite3_stmt * pStmt = NULL;
  if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
    if(stmtId){
      *stmtId = rc;
    }
    pStmt = Th_query_GetStmt( interp, rc );
    if(NULL==pStmt){
      Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
    }
  }
  return pStmt;

}

/*
** TH command:
**
** query finalize stmtId
** query stmtId finalize 
**
** sqlite3_finalize()s the given statement.
*/
static int queryFinalizeCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 1 : 2;
  char * zSql;
  int stId = 0;
  char const * arg;
  int rc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle");
  }
  if(!pStmt){
    arg = argv[1];
    pStmt = queryStmtHandle(interp, arg, argl[1], &stId);
    if(!pStmt){
      Th_ErrorMessage(interp, "Not a valid statement handle argument.", NULL, 0);
      return TH_ERROR;
    }
  }
  assert( NULL != pStmt );
  rc = Th_query_FinalizeStmt2( interp, pStmt );
  Th_SetResultInt( interp, rc );
  return TH_OK;
}

/*
** Reports the current sqlite3_errmsg() via TH and returns TH_ERROR.
*/
static int queryReportDbErr( Th_Interp * interp ){
  char const * msg = sqlite3_errmsg( g.db );
  Th_ErrorMessage(interp, "db error:", msg, -1);
  return TH_ERROR;
}

/*
** Internal helper for fetching statement handle and index parameters.
** The first 4 args should be the args passed to the TH1 callback.
** pStmt must be a pointer to a NULL pointer. pIndex may be NULL or
** a pointer to store the statement index argument in. If pIndex is
** NULL then argc is asserted to be at least 2, else it must be at
** least 3.
**
** On success it returns 0, sets *pStmt to the referenced statement
** handle, and pIndex (if not NULL) to the integer value of argv[2]
** argument. On error it reports the error via TH, returns non-0, and
** modifies neither pStmt nor pIndex.
*/
static int queryStmtIndexArgs(
  Th_Interp * interp,
  int argc,
  char const ** argv,
  int *argl,
  sqlite3_stmt ** pStmt,
  int * pIndex ){
  int index = 0;
  sqlite3_stmt * stmt;
  if( !pIndex ){
    if(argc<2){
      return Th_WrongNumArgs2(interp,
                              argv[0], argl[0],
                              "StmtHandle");
    }
  }else{
    if( argc<3 ){
      return Th_WrongNumArgs2(interp,
                              argv[0], argl[0],
                              "StmtHandle Index");
    }
    if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){
      return TH_ERROR;
    }
  }
  stmt = *pStmt ? *pStmt : queryStmtHandle(interp, argv[1], argl[1], NULL);
  if( NULL == stmt ){
    return TH_ERROR;
  }else{
    *pStmt = stmt;
    if( pIndex ){
      *pIndex = index;
    }
    return 0;
  }
}

/*
** TH command:
**
** query step stmtId
** query stmtId step
**
** Steps the given statement handle. Returns 0 at the end of the set,
** a positive value if it fetches a row, and throws on error.
*/
static int queryStepCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 1 : 2;
  int rc = 0;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle");
  }
  if(!pStmt && 0 != queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, NULL)){
    return TH_ERROR;
  }
  assert(NULL != pStmt);
  rc = sqlite3_step( pStmt );
  switch(rc){
    case SQLITE_ROW:
      rc = 1;
      break;
    case SQLITE_DONE:
      rc = 0;
      break;
    default:
      return queryReportDbErr( interp );
  }
  Th_SetResultInt( interp, rc );
  return TH_OK;
}

/*
** TH command:
**
** query StmtId reset
** query reset StmtId
**
** Equivalent to sqlite3_reset().
*/
static int queryResetCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int const rc = sqlite3_reset(pStmt);
  if(rc){
    Th_ErrorMessage(interp, "Reset of statement failed.", NULL, 0);
    return TH_ERROR;
  }else{
    return TH_OK;
  }
}


/*
** TH command:
**
** query col string stmtId Index
** query stmtId col string Index
** query stmtId col Index string
**
** Returns the result column value at the given 0-based index.
*/
static int queryColStringCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  char const * val;
  int valLen;
  if( index >= 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<0){
    Th_ToInt(interp, argv[1], argl[1], &index);
  }
  if(index < 0){
    return TH_ERROR;
  }
  val = sqlite3_column_text( pStmt, index );
  valLen = val ? sqlite3_column_bytes( pStmt, index ) : 0;
  Th_SetResult( interp, val, valLen );
  return TH_OK;
}

/*
** TH command:
**
** query col int stmtId Index
** query stmtId col int Index
** query stmtId col Index int
**
** Returns the result column value at the given 0-based index.
*/
static int queryColIntCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  int rc = 0;
  if( index >= 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<0){
    Th_ToInt(interp, argv[1], argl[1], &index);
  }
  if(index < 0){
    return TH_ERROR;
  }
  Th_SetResultInt( interp, sqlite3_column_int( pStmt, index ) );
  return TH_OK;
}

/*
** TH command:
**
** query col double stmtId Index
** query stmtId col double Index
** query stmtId col Index double
**
** Returns the result column value at the given 0-based index.
*/
static int queryColDoubleCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  double rc = 0;
  if( index >= 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<0){
    Th_ToInt(interp, argv[1], argl[1], &index);
  }
  if(index < 0){
    return TH_ERROR;
  }
  Th_SetResultDouble( interp, sqlite3_column_double( pStmt, index ) );
  return TH_OK;
}

/*
** TH command:
**
** query col isnull stmtId Index
** query stmtId col isnull Index
** query stmtId col Index isnull
**
** Returns non-0 if the given 0-based result column index contains
** an SQL NULL value, else returns 0.
*/
static int queryColIsNullCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  if( index >= 0 ) --requireArgc;
  double rc = 0;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<0){
    Th_ToInt(interp, argv[1], argl[1], &index);
  }
  if(index < 0){
    return TH_ERROR;
  }
  Th_SetResultInt( interp,
                   SQLITE_NULL==sqlite3_column_type( pStmt, index )
                   ? 1 : 0);
  return TH_OK;
}

/*
** TH command:
**
** query col type stmtId Index
** query stmtId col type Index
** query stmtId col Index type
**
** Returns the sqlite type identifier for the given 0-based result
** column index. The values are available in TH as $SQLITE_NULL,
** $SQLITE_INTEGER, etc.
*/
static int queryColTypeCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  if( index >= 0 ) --requireArgc;
  double rc = 0;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<0){
    Th_ToInt( interp, argv[1], argl[1], &index );
  }
  if(index < 0){
    return TH_ERROR;
  }
  Th_SetResultInt( interp, sqlite3_column_type( pStmt, index ) );
  return TH_OK;
}

/*
** TH command:
**
** query col count stmtId
** query stmtId col count
**
** Returns the number of result columns in the query.
*/
static int queryColCountCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  int rc;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 1 : 2;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                           argv[0], argl[0],
                           "StmtHandle");
  }
  if(!pStmt){
    pStmt = queryStmtHandle(interp, argv[1], argl[1], NULL);
    if( NULL == pStmt ){
      return TH_ERROR;
    }
  }
  rc = sqlite3_column_count( pStmt );
  Th_SetResultInt( interp, rc );
  return TH_OK;
}

/*
** TH command:
**
** query col name stmtId Index
** query stmtId col name Index
** query stmtId col Index name 
**
** Returns the result column name at the given 0-based index.
*/
static int queryColNameCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  char const * val;
  int rc = 0;
  if( index >= 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<0){
    Th_ToInt( interp, argv[1], argl[1], &index );
  }
  if(index < 0){
    return TH_ERROR;
  }
  assert(NULL!=pStmt);
  val = sqlite3_column_name( pStmt, index );
  if(NULL==val){
    Th_ErrorMessage(interp, "Column index out of bounds(?):", argv[2], -1);
    return TH_ERROR;
  }else{
    Th_SetResult( interp, val, strlen( val ) );
    return TH_OK;
  }
}

/*
** TH command:
**
** query col time stmtId Index format
** query stmtId col name Index format
** query stmtId col Index name format
**
** Returns the result column name at the given 0-based index.
*/
static int queryColTimeCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)ctx;
  int minArgs = pStmt ? 3 : 4;
  int argPos;
  char const * val;
  char * fval;
  int i, rc = 0;
  char const * fmt;
  Blob sql = empty_blob;
  if( index >= 0 ) --minArgs;
  if( argc<minArgs ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index Format");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
    argPos = 3;
  }else if(index<0){
    Th_ToInt( interp, argv[1], argl[1], &index );
    argPos = 2;
  }else{
    argPos = 1;
  }
  if(index < 0){
    return TH_ERROR;
  }
  val = sqlite3_column_text( pStmt, index );
  fmt = argv[argPos++];
  assert(NULL!=pStmt);
  blob_appendf(&sql,"SELECT strftime(%Q,%Q",
               fmt, val);
  if(argc>argPos){
    for(i = argPos; i < argc; ++i ){
      blob_appendf(&sql, ",%Q", argv[i]);
    }
  }
  blob_append(&sql, ")", 1);
  fval = db_text(NULL,"%s", sql.aData);
  
  blob_reset(&sql);
  Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
  fossil_free(fval);
  return 0;
}

/*
** TH command:
**
**  query strftime TimeVal ?Modifiers...?
**
** Acts as a proxy to sqlite3's strftime() SQL function.
*/
static int queryStrftimeCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  char const * val;
  char * fval;
  int i, rc = 0;
  int index = -1;
  char const * fmt;
  Blob sql = empty_blob;
  if( argc<3 ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "Format Value ?Modifiers...?");
  }
  fmt = argv[1];
  val = argv[2];
  blob_appendf(&sql,"SELECT strftime(%Q,%Q",
               fmt, val);
  if(argc>3){
    for(i = 3; i < argc; ++i ){
      blob_appendf(&sql, ",%Q", argv[i]);
    }
  }
  blob_append(&sql, ")", 1);
  fval = db_text(NULL,"%s", sql.aData);
  blob_reset(&sql);
  Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
  fossil_free(fval);
  return 0;
}


/*
** TH command:
**
** query bind null stmtId Index
** query stmtId bind null Index
**
** Binds a value to the given 1-based parameter index.
*/
static int queryBindNullCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 2 : 3;
  if( index > 0 ) --requireArgc;
  int rc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
  }else if(index<1){
    Th_ToInt( interp, argv[1], argl[1], &index );
  }
  if(index < 1){
    return TH_ERROR;
  }
  rc = sqlite3_bind_null( pStmt, index );
  if(rc){
    return queryReportDbErr( interp );
  }
  Th_SetResultInt( interp, 0 );
  return TH_OK;
}


/*
** TH command:
**
** query bind string stmtId Index Value
** query stmtId bind string Index Value
**
** Binds a value to the given 1-based parameter index.
*/
static int queryBindStringCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 3 : 4;
  int rc;
  int argPos;
  if( index > 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index Value");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
    argPos = 3;
  }else if(index<1){
    Th_ToInt( interp, argv[1], argl[1], &index );
    argPos = 2;
  }else{
    argPos = 1;
  }
  if(index < 1){
    return TH_ERROR;
  }
  rc = sqlite3_bind_text( pStmt, index, argv[argPos], argl[argPos], SQLITE_TRANSIENT );
  if(rc){
    return queryReportDbErr( interp );
  }
  Th_SetResultInt( interp, 0 );
  return TH_OK;
}

/*
** TH command:
**
** query bind int stmtId Index Value
** query stmtId bind int Index Value
**
** Binds a value to the given 1-based parameter index.
*/
static int queryBindIntCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 3 : 4;
  int rc;
  int argPos;
  int val;
  if( index > 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index Value");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
    argPos = 3;
  }else if(index<1){
    Th_ToInt( interp, argv[1], argl[1], &index );
    argPos = 2;
  }else{
    argPos = 1;
  }
  if(index < 1){
    return TH_ERROR;
  }
  if( 0 != Th_ToInt( interp, argv[argPos], argl[argPos], &val ) ){
    return TH_ERROR;
  }

  rc = sqlite3_bind_int( pStmt, index, val );
  if(rc){
    return queryReportDbErr( interp );
  }
  Th_SetResultInt( interp, 0 );
  return TH_OK;
}

/*
** TH command:
**
** query bind double stmtId Index Value
** query stmtId bind double Index Value
**
** Binds a value to the given 1-based parameter index.
*/
static int queryBindDoubleCmd(
  Th_Interp *interp,
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Query * sq = Th_query_manager(interp);
  int index = sq->colCmdIndex;
  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
  int requireArgc = pStmt ? 3 : 4;
  int rc;
  int argPos;
  double val;
  if( index > 0 ) --requireArgc;
  if( argc!=requireArgc ){
    return Th_WrongNumArgs2(interp,
                            argv[0], argl[0],
                            "StmtHandle Index Value");
  }
  if(!pStmt){
    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
    argPos = 3;
  }else if(index<1){
    Th_ToInt( interp, argv[1], argl[1], &index );
    argPos = 2;
  }else{
    argPos = 1;
  }
  if(index < 1){
    return TH_ERROR;
  }
  if( 0 != Th_ToDouble( interp, argv[argPos], argl[argPos], &val ) ){
    return TH_ERROR;
  }

  rc = sqlite3_bind_double( pStmt, index, val );
  if(rc){
    return queryReportDbErr( interp );
  }
  Th_SetResultInt( interp, 0 );
  return TH_OK;
}

/*
** TH command:
**
** bind subcommand StmtId...
** bind StmtId subcommand...
**
** This is the top-level dispatcher for the "bind" family of commands.
*/
static int queryBindTopLevelCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  int colIndex = -1;
  static Th_SubCommand aSub[] = {
    {"int",    queryBindIntCmd},
    {"double", queryBindDoubleCmd},
    {"null",   queryBindNullCmd},
    {"string", queryBindStringCmd},
    {0, 0}
  };
  Th_Query * sq = Th_query_manager(interp);
  assert(NULL != sq);
  if( 1 == argc ){
      Th_WrongNumArgs2( interp, argv[0], argl[0],
                        "subcommand: int|double|null|string");
      return TH_ERROR;
  }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
    if(colIndex <0){
      Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
      return TH_ERROR;
    }
    ++argv;
    ++argl;
    --argc;
  }
  sq->colCmdIndex = colIndex;
  Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );

}

/*
** TH command:
**
** query col subcommand ...
** query StmtId col subcommand ...
**
** This is the top-level dispatcher for the col subcommands.
*/
static int queryColTopLevelCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  int colIndex = -1;
  static Th_SubCommand aSub[] = {
    {"count",   queryColCountCmd},
    {"is_null", queryColIsNullCmd},
    {"isnull",  queryColIsNullCmd},
    {"name",    queryColNameCmd},
    {"double",  queryColDoubleCmd},
    {"int",     queryColIntCmd},
    {"string",  queryColStringCmd},
    {"time",    queryColTimeCmd},
    {"type",    queryColTypeCmd},
    {0, 0}
  };
  static Th_SubCommand aSubWithIndex[] = {
    /*
      This subset is coded to accept the column index
      either before the subcommand name or after it.
      If called like (bind StmtId subcommand) then
      only these commands will be checked.
    */
    {"is_null", queryColIsNullCmd},
    {"isnull",  queryColIsNullCmd},
    {"name",    queryColNameCmd},
    {"double",  queryColDoubleCmd},
    {"int",     queryColIntCmd},
    {"string",  queryColStringCmd},
    {"time",    queryColTimeCmd},
    {"type",    queryColTypeCmd},
    {0, 0}
  };
  Th_Query * sq = Th_query_manager(interp);
  assert(NULL != sq);
  if( 1 == argc ){
      Th_WrongNumArgs2( interp, argv[0], argl[0],
                        "subcommand: "
                        "count|is_null|isnull|name|"
                        "double|int|string|time|type");
      return TH_ERROR;
  }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
    if(colIndex <0){
      Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
      return TH_ERROR;
    }
    ++argv;
    ++argl;
    --argc;
  }
  sq->colCmdIndex = colIndex;
  Th_CallSubCommand2( interp, ctx, argc, argv, argl,
                      (colIndex<0) ? aSub : aSubWithIndex );
}


/*
** TH command:
**
** query subcommand ...
** query StmtId subcommand ...
**
** This is the top-level dispatcher for the query subcommand.
*/
static int queryTopLevelCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  int stmtId = 0;
  sqlite3_stmt * pStmt = NULL;
  static Th_SubCommand aSubAll[] = {
    {"bind",        queryBindTopLevelCmd},
    {"col",         queryColTopLevelCmd},
    {"finalize",    queryFinalizeCmd},
    {"prepare",     queryPrepareCmd},
    {"reset",       queryResetCmd},
    {"step",        queryStepCmd},
    {"strftime",    queryStrftimeCmd},
    {0, 0}
  };
  static Th_SubCommand aSubWithStmt[] = {
    /* This subset is coded to deal with being supplied a statement
       via pStmt or via one of their args. When called like (query
       StmtId ...) only these subcommands will be checked.*/
    {"bind",        queryBindTopLevelCmd},
    {"col",         queryColTopLevelCmd},
    {"step",        queryStepCmd},
    {"finalize",    queryFinalizeCmd},
    {"reset",       queryResetCmd},
    {0, 0}
  };


  assert( NULL != Th_query_manager(interp) );
  if( 1 == argc ){
      Th_WrongNumArgs2( interp, argv[0], argl[0],
                        "subcommand: bind|col|finalize|prepare|reset|step|strftime");
      return TH_ERROR;
  }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &stmtId) ){
    ++argv;
    ++argl;
    --argc;
    pStmt = Th_query_GetStmt( interp, stmtId );
  }

  Th_CallSubCommand2( interp, pStmt, argc, argv, argl,
                      pStmt ? aSubWithStmt : aSubAll );
}

/*
** Registers the "query" API with the given interpreter. Returns TH_OK
** on success, TH_ERROR on error.
*/
int th_register_query(Th_Interp *interp){
  enum { BufLen = 100 };
  char buf[BufLen];
  int i, l;
#define SET(K) l = snprintf(buf, BufLen, "%d", K);      \
  Th_SetVar( interp, #K, strlen(#K), buf, l );
  SET(SQLITE_BLOB);
  SET(SQLITE_FLOAT);
  SET(SQLITE_INTEGER);
  SET(SQLITE_NULL);
  SET(SQLITE_TEXT);
#if 0
  /* so far we don't need these in script code */
  SET(SQLITE_ERROR);
  SET(SQLITE_DONE);
  SET(SQLITE_OK);
  SET(SQLITE_ROW);
#endif
#undef SET
  int rc = TH_OK;
  static Th_Command_Reg aCommand[] = {
    {"query",             queryTopLevelCmd,  0},
    {0, 0, 0}
  };
  rc = Th_RegisterCommands( interp, aCommand );
  if(TH_OK==rc){
    Th_Query * sq = Th_Malloc(interp, sizeof(Th_Query));
    if(!sq){
      rc = TH_ERROR;
    }else{
      assert( NULL == sq->aStmt );
      assert( 0 == sq->nStmt );
      Th_SetData( interp, Th_Query_KEY, sq, finalizerSqlite );
      assert( sq == Th_query_manager(interp) );
    }
  }
  return rc;
}

#endif
/* end TH_ENABLE_QUERY */

/*
** Make sure the interpreter has been initialized.  Initialize it if
** it has not been already.
**
** The interpreter is stored in the g.interp global variable.
*/
void Th_FossilInit(void){




  /* The fossil-internal Th_Vtab instance. */
  static Th_Vtab vtab = { xRealloc, {/*out*/
    NULL /*write()*/,
    NULL/*dispose()*/,
    NULL/*pState*/,
    1/*enabled*/
    }
  };

  static PutsCmdData puts_Html = {0, 0, 0};
  static PutsCmdData puts_Normal = {1, 0, 0};
  static Th_Command_Reg aCommand[] = {
    {"anycap",        anycapCmd,            0},
    {"combobox",      comboboxCmd,          0},
    {"date",          dateCmd,              0},
    {"enable_output", enableOutputCmd,      0},

    {"hascap",        hascapCmd,            0},
    {"hasfeature",    hasfeatureCmd,        0},
    {"html",          putsCmd,     &puts_Html},
    {"htmlize",       htmlizeCmd,           0},


    {"linecount",     linecntCmd,           0},
    {"puts",          putsCmd,   &puts_Normal},
#if 0
    {"render",        renderCmd,            0},
#endif
    {"repository",    repositoryCmd,        0},
    {"wiki",          wikiCmd,              0},

    {0, 0, 0}
  };
  if( g.interp==0 ){
    int i;
    if(g.cgiOutput){
      vtab.out.xWrite = Th_Output_f_cgi_content;
    }else{
      vtab.out = Th_Vtab_OutputMethods_FILE;
      vtab.out.pState = stdout;
    }
    vtab.out.enabled = enableOutput;
    g.interp = Th_CreateInterp(&vtab);
    th_register_language(g.interp);       /* Basic scripting commands. */
#ifdef FOSSIL_ENABLE_TCL
    if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
      th_register_tcl(g.interp, &g.tcl);  /* Tcl integration commands. */
    }
#endif
#ifdef TH_ENABLE_OB
    th_register_ob(g.interp);
#endif
#ifdef TH_ENABLE_QUERY

    th_register_query(g.interp);
#endif
#ifdef TH_ENABLE_ARGV
    th_register_argv(g.interp);
#endif
    Th_RegisterCommands( g.interp, aCommand );
    Th_Eval( g.interp, 0, "proc incr {name {step 1}} {\n"
             "upvar $name x\n"
             "set x [expr $x+$step]\n"
             "}", -1 );
  }
}

/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
................................................................................
    i += 2;
  }
  return i;
}

/*
** The z[] input contains text mixed with TH1 scripts.
** The TH1 scripts are contained within <th1>...</th1>.
**
** If flags does NOT contain the Th_Render_Flags_NO_DOLLAR_DEREF bit
** then TH1 variables are $aaa or $<aaa>.  The first form of variable
** is literal.  The second is run through htmlize before being
** inserted.
**
** This routine processes the template and writes the results

** via Th_Output().
*/
int Th_Render(const char *z, int flags){
  int i = 0;
  int n;
  int rc = TH_OK;
  char const *zResult;
  char doDollar = !(flags & Th_Render_Flags_NO_DOLLAR_DEREF);
  Th_FossilInit();
  while( z[i] ){
    if( doDollar && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
      const char *zVar;
      int nVar;
      int encode = 1;
      sendText(g.interp, z, i, 0);
      if( z[i+1]=='<' ){
        /* Variables of the form $<aaa> are html escaped */
        zVar = &z[i+2];
        nVar = n-2;
      }else{
        /* Variables of the form $aaa are output raw */
        zVar = &z[i+1];
        nVar = n;
        encode = 0;
      }
      rc = Th_GetVar(g.interp, zVar, nVar);
      z += i+1+n;
      i = 0;
      zResult = Th_GetResult(g.interp, &n);
      sendText(g.interp, zResult, n, encode);
    }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
      sendText(g.interp, z, i, 0);
      z += i+5;
      for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
      rc = Th_Eval(g.interp, 0, (const char*)z, i);
      if( rc!=TH_OK ) break;
      z += i;
      if( z[0] ){ z += 6; }
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==TH_ERROR ){
    sendText(g.interp, "<hr><p class=\"thmainError\">ERROR: ", -1, 0);
    zResult = Th_GetResult(g.interp, &n);
    sendText(g.interp, zResult, n, 1);
    sendText(g.interp, "</p>", -1, 0);
  }else{
    sendText(g.interp, z, i, 0);
  }
  return rc;
}

/*
** COMMAND: test-th-render
** COMMAND: th1
**
** Processes a file provided on the command line as a TH1-capable
** script/page. Output is sent to stdout or the CGI output buffer, as
** appropriate. The input file is assumed to be text/wiki/HTML content
** which may contain TH1 tag blocks and variables in the form $var or
** $<var>. Each block is executed in the same TH1 interpreter
** instance.
**
** ACHTUNG: not all of the $variables which are set in CGI mode
** are available via this (CLI) command.
**
*/
void test_th_render(void){
  Blob in;
  if( g.argc<3 ){
    usage("FILE");
    assert(0 && "usage() does not return");
  }

  blob_zero(&in);
  db_open_config(0); /* Needed for global "tcl" setting. */
#ifdef TH_ENABLE_QUERY
  db_find_and_open_repository(OPEN_ANY_SCHEMA,0)
    /* required for th1 query API. */;
#endif
  blob_read_from_file(&in, g.argv[2]);
  Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT);
}

Changes to src/tkt.c.

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
...
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
...
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  }
  style_header("View Ticket");
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
  Th_Render(zScript);
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);

  zFullName = db_text(0, 
       "SELECT tkt_uuid FROM ticket"
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    int cnt = 0;
................................................................................
  @ </p>
  zScript = ticket_newpage_code();
  Th_Store("login", g.zLogin);
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
                   (void*)&zNewUuid, 0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
    return;
  }
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_footer();
}
................................................................................
  @ </p>
  zScript = ticket_editpage_code();
  Th_Store("login", g.zLogin);
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
    return;
  }
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_footer();
}






|







 







|







 







|







326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
...
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
...
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  }
  style_header("View Ticket");
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
  Th_Render(zScript, Th_Render_Flags_DEFAULT );
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);

  zFullName = db_text(0, 
       "SELECT tkt_uuid FROM ticket"
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    int cnt = 0;
................................................................................
  @ </p>
  zScript = ticket_newpage_code();
  Th_Store("login", g.zLogin);
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
                   (void*)&zNewUuid, 0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
  if( Th_Render(zScript, Th_Render_Flags_DEFAULT)==TH_RETURN && !g.thTrace && zNewUuid ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
    return;
  }
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_footer();
}
................................................................................
  @ </p>
  zScript = ticket_editpage_code();
  Th_Store("login", g.zLogin);
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
  if( Th_Render(zScript, Th_Render_Flags_DEFAULT)==TH_RETURN && !g.thTrace && zName ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
    return;
  }
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_footer();
}

Added test/th1-ob-1.th1.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<th1>
set i 0
set max 6
for {} {$i < $max} {incr i} {
    ob start
    puts "this is level " [ob level]
    set buf($i) [ob get]
}

for {set i [expr $max-1]} {$i >= 0} {incr i -1} {
    ob pop
}
for {set i [expr $max-1]} {$i >= 0} {incr i -1} {
    puts buf($i) = $buf($i) "\n"
}
puts "buffering level = " [ob level] \n
</th1>

Added test/th1-query-api-1.th1.


























































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
This is not a formal test suite, but a tinkering ground.
Run it through "fossil test-th-render THIS_FILE".
<th1>
set stmt [query prepare {SELECT login, cap FROM user}]
set colCount [query $stmt col count]
puts "query column count: ${colCount}\n"
puts "stmt id=${stmt}\n"

proc noop {} {}
proc incr {name {step 1}} {
    upvar $name x
    set x [expr $x+$step]
}


set sep "    "
set i 0
set colNames(0) 0
for {set i 0} {$i < $colCount} {incr i} {
    set colNames($i) [query $stmt col name $i]
    puts "colNames($i)=" $colNames($i) "\n"
}

for {set row 0} {[query $stmt step]} {incr row} {
    for {set i 0} {$i < $colCount} {incr i} {
        if {$i > 0} {
            puts $sep
        } else {
            puts "#$row: $sep"
        }
        puts $colNames($i) = [query $stmt col string $i]
    }
    puts "\n"
}
unset row

query $stmt finalize
#query finalize $stmt 


proc query_step_each {{stmt} {callback}} {
    set colNames(0) 0
    set colCount [query $stmt col count]
    for {set i 0} {$i < $colCount} {incr i} {
        set colNames($i) [query $stmt col name $i]
    }
    upvar cb $callback
    for {set row 0} {[query $stmt step]} {incr row} {
        #puts "Calling callback: $stmt $colCount colNames\n"
        $callback $stmt $colCount
    }
}

set sql {SELECT uid, login FROM user WHERE uid!=?}
#set sql {SELECT uid, login FROM user WHERE login=?}
#set sql {SELECT tagid, value, null FROM tagxref WHERE value IS ? LIMIT 3}
set stmt [query prepare $sql]
puts "stmt ID=" $stmt "\n"
#query bind int $stmt 1 3
#query $stmt bind 1 int 3
query $stmt bind 1 string 3
#query $stmt bind string 1 3
#set stmt [query prepare $sql]
#query $stmt bind 1 string 1
#set stmt [query prepare $sql]
#query $stmt bind null 1
puts "USER LIST:\n"
catch {
    proc my_each {stmt colCount} {
        upvar 2 sep sep
        puts [query $stmt col int 0] " (type=" [query $stmt col type 0] ")" $sep
        puts [query $stmt col double 0] $sep
        puts [query $stmt col string 1]  " (type=" [query $stmt col type 1] ")" $sep
        puts "isnull 0 ?= " [query $stmt col is_null 0] $sep
        puts "isnull 2 ?= " [query col is_null $stmt 2]
#        for {set i 0} {$i < $colCount} {incr i} {
#            if {$i > 0} { puts $sep }
#        }
        puts "\n"
#        error "hi!"
    }
    query_step_each $stmt my_each
#    query reset $stmt
#    query $stmt reset 
#    query_step_each $stmt {
#        proc each {stmt cc} { puts hi "\n" }
#    }
    return 0
} rc
query finalize $stmt
if { 0 != $rc } {
        puts "ERROR: $rc\n"
}

set consts [list SQLITE_BLOB SQLITE_FLOAT SQLITE_INTEGER SQLITE_NULL SQLITE_TEXT]
#set consts $SQLITE_CONSTANTS
puts consts = $consts "\n"
for {set i 0} {$i < [llength $consts]} {incr i} {
    set x [lindex $consts $i]
    puts \$$x = [expr \$$x] "\n"
}

set ARGC [argv len]
puts ARGC = $ARGC "\n"
for {set i 0} {$i < $ARGC} {incr i} {
    puts "argv at $i = " [argv at $i] \n
}

set magicDefault hi
set optA [argv string AA a $magicDefault]
puts "argv string AA = " $optA \n

set optA [argv bool BB b 0]
puts "argv bool BB = " $optA \n

set exception 0
catch {
    argv int noSuchOptionAndNoDefault
} exception
puts exception = $exception "\n"

proc multiStmt {} {
    set max 5
    set i 0
    set s(0) 0
    for {set i 0} {$i < $max} {incr i} {
       set s($i) [query prepare "SELECT $i"]
       puts "s($i) = $s($i)\n"
    }
    for {set i 0} {$i < $max} {incr i} {
       query $s($i) step
    }
    for {set i 0} {$i < $max} {incr i} {
       puts "closing stmt $s($i)\n"
       query $s($i) finalize
    }

    puts "Preparing again\n"

    for {set i 0} {$i < $max} {incr i} {
       set s($i) [query prepare "SELECT $i"]
       puts "s($i) = $s($i)\n"
    }
    for {set i 0} {$i < $max} {incr i} {
       query $s($i) step
    }
    puts "Closing again\n"

    for {set i 0} {$i < $max} {incr i} {
       puts "closing stmt $s($i)\n"
       query $s($i) finalize
    }
}
multiStmt

puts "If you got this far, you win!\n"
</th1>

Added test/th1-query-api-2.th1.












































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<th1>
catch {
    set stmt [query prepare {
        SELECT login, cap, cexpire, mtime, NULL FROM user
        WHERE uid<? AND cexpire IS NOT NULL
        AND mtime IS NOT NULL
    }]
    puts "stmt ID=$stmt\n"
#    query bind int $stmt 1 2
#    query $stmt bind int 1 2
    query $stmt bind 1 int 5
# segfault:    query bind 1 int $stmt 2
    set sep "\n"
    for {} {[query $stmt step]} {} {
        puts [query $stmt col string 0] $sep
        puts [query $stmt col time 2 {%Y%m%d @ %H:%M:%S}] $sep
        puts [query $stmt col time 2 {%Y%m%d @ %H:%M:%S} {+10 years}] $sep
        puts [query $stmt col 2 time {%Y%m%d @ %H:%M:%S} {+10 years}] $sep
#        puts [query col time $stmt 2 %s] $sep
        puts [query col time $stmt 3 %s unixepoch] $sep
#        puts [query strftime %s [query col string $stmt 3] unixepoch]
        puts [query strftime %s [query col string $stmt 3] unixepoch] $sep
        puts [query strftime {%Y%m%d @ %H:%M:%S} [query col string $stmt 2] {+10 years}] $sep
        puts "\n"
        puts "old isnull: " [query col isnull $stmt 4] "\n"

        puts "new old isnull: " [query col 4 isnull $stmt] "\n"
        puts "new isnull: " [query $stmt col isnull 4] "\n"
        puts "new new isnull: " [query $stmt col 4 isnull] "\n"

        puts "old col type: " [query col type $stmt 1] "\n"
        puts "new col type: " [query $stmt col type 1] "\n"
        puts "new new col type: " [query $stmt col 1 type] "\n"

        puts "old col name: " [query col name $stmt 1] "\n"
        puts "new col name: " [query $stmt col name 1] "\n"
        puts "new new col name: " [query $stmt col 1 name] "\n"

        puts "old col double: " [query col double $stmt 2] "\n"
        puts "new col double: " [query $stmt col double 2] "\n"
        puts "new new col double: " [query $stmt col 2 double] "\n"

        puts "old col int: " [query col int $stmt 2] "\n"
        puts "new col int: " [query $stmt col int 2] "\n"
        puts "new new col int: " [query $stmt col 2 int] "\n"

        puts "old col string: " [query col string $stmt 2] "\n"
        puts "new col string: " [query $stmt col string 2] "\n"
        puts "new new col string: " [query $stmt col 2 string] "\n"

        puts "\n"
    }

    puts "alt-form col count: " [query $stmt col count] "\n"
    query finalize $stmt
    return 0
} rc
if {0 != $rc} {
    puts "ERROR: $rc\n"
}
puts "Done!\n"


ob start
puts buffered
set x [ob get pop]
puts x=$x


</th1>

Added test/th1-variadic-proc.th1.




















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
<th1>
proc vfunc {args} {
  set argc [llength $args]
  puts "argc=$argc Check this for a memleak when args length > 0\n"
}
vfunc
vfunc 1
vfunc 1 2
vfunc 1 2 3
</th1>

Added www/th1_argv.wiki.
































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<h1>TH1 "argv" API</h1>

The "argv" API provides features for accessing command-line arguments
and GET/POST values. They (unfortunately) do not provide access to
POST data submitted in JSON mode (which fossil internally doesn't really
know about).

Example usage:

<nowiki><pre>
&lt;th1>
set argc [argv len]
set appName [argv at 0]
# Fetch --foo|-f argument:
set foo [argv getstr foo f "default value"]
&lt;th1>
</pre></nowiki>

(Note that fossil does not actually care if an argument starts
with 1 or 2 dashes. The convention of using 1 for "short-form"
flags and 2 for "long-form" is purely historical.)

The various subcommands are described below...

<h2>len</h2>

Returns the number of arguments.

<nowiki><pre>
set argc [argv len]
</pre></nowiki>


<h2>at</h2>

Fetches the argument at the given index (0-based).

<nowiki><pre>
set arg [argv at 3]
</pre></nowiki>

The fossil binary's name is stored in argument #0.

<h2>getstr|string</h2>

Searches for a CLI/GET/POST parameter. In CLI this function has some
non-intuitive behaviour inherited from fossil's internals: once a
flag/parameter is fetched, it is removed from the internal arguments
list, meaning that this function will never see it a second time.

<nowiki><pre>
set something [argv string "something" "S" "default"]
</pre></nowiki>

If no default value is provided, an error is triggered if the value is
not found.

If you do not want to search for a short-form flag, set it to an empty
string.

NOTE: flag checking does not work in CGI mode when using <em>upper-case</em>
flags (fossil treats upper-case names as environment variables).

<h2>getbool|bool</h2>

Works almost like <tt>getstr</tt> but searches for boolean flags. CLI boolean flags
have no explicit value, and are "true" if the are set at all.

<nowiki><pre>
set doSomething [argv bool "do-something" "D" 0]
</pre></nowiki>

<h2>getint|int</h2>

Works almost like <tt>getstr</tt> but searches for integer flags.


<nowiki><pre>
set limit [argv int "limit" "L" 10]
</pre></nowiki>

Added www/th1_ob.wiki.
































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<h1>TH1 "ob" (Output Buffering) API</h1>

The "ob" API mimics the
[http://php.net/manual/en/function.ob-start.php|PHP output buffering] fairly closely,
and provides these features:

   *  Redirect output from <tt>puts</tt> and friends to a buffer.
   *  Fetch the buffer as a string or discard it (as you wish).
   *  Supports nesting buffering arbitrarily deep, but each level which gets opened must also be closed by the scripter.

Example usage:

<nowiki><pre>
&lt;th1>
puts "this is unbuffered"
ob push # or: ob start (same thing)
# all output until the next ob start|end gets collected
# in a buffer...
&lt;/th1>

this is buffered

&lt;th1>
puts "current buffer level = " [ob level] "\n"
puts "this part is also collected in the buffer."

# Collect the buffer's contents:
set buf [ob get pop]
# That is equivalent to:
#  set buf [ob get]
#  ob pop

puts "\nThis is now unbuffered, but we buffered: $buf\n"
&lt;/th1>
</pre></nowiki>

The functions are summarized below...

<h2>ob push|start</h2>

<tt>push</tt> and <tt>start</tt> are aliases ("start" comes from the PHP API, but
"push" is probably more natural to those working with th1).

<tt>ob start</tt> pushes a level of buffering onto the buffer stack, such that
future calls which generate output through the th1-internal mechanism will have it
transparently redirected to the current buffer.

It is important that every call to <tt>ob start</tt> be followed up (eventually)
by either <tt>ob end</tt> or <tt>ob get end</tt>.

<h2>ob pop|end</h2>

<tt>pop</tt> and <tt>end</tt> are aliases ("end" comes from the PHP API, but
"pop" is probably more natural to those working with th1).

This discards any current buffered contents and reverts the output state to
the one it had before the previous <tt>ob start</tt>. i.e. that might be another
buffering level or it might be the th1-normal output mechanism.

The global resources associated with buffering are cleaned up when the
last buffering level is left (and re-created as needed when a new
level is started).

<h2>ob clean</h2>

This discards the current contents of the current buffer level but
does not change the buffer stack level.

<h2>ob get</h2>

This fetches the current contents as a string. It optionally accepts
either <tt>end</tt> (or its alias <tt>pop</tt>) or <tt>clean</tt>, in which cases it behaves like
either <tt>ob end|pop</tt> or <tt>ob clean</tt>, respectively, in addition
to returning the buffer contents. i.e. <tt>ob get clean</tt> will
fetch the contents and clean up the buffer, but does not change the
buffering level, whereas <tt>ob get end|pop</tt> pops the buffer off the
stack after fetching its contents.

<h2>ob level</h2>

Returns the current buffering level (0 if not buffering).

<h2>ob flush</h2>

It is not expected that this will be useful all that often, but for
the cases where it is, here's how it works: this behaves as if we
fetched the buffer state (<tt>ob get</tt>), reverted TH1 to its
previous output mechanism, push the buffer state to TH1, revert TH1
<em>back</em> to the current buffering state, and then clear the
current buffer contents (like <tt>ob clean</tt>). This does not change
the buffering level, though it temporarily behaves as if it does.

In other words, this function pushes the current buffer contents to the
next-lower output mechanism (which may be another ob buffering level,
fossil's internal CGI output buffer, or it might be be
<tt>fwrite(stdout)</tt>).

Added www/th1_query.wiki.














































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<h1>TH1 "query" API</h1>

The "query" API provides limited access to the fossil database.
It restricts usage to queries which return result columns (i.e.
<tt>SELECT</tt> and friends).
Example usage:

<nowiki><pre>
&lt;th1>
catch {
    set stmt [query prepare "SELECT login, cap FROM user"]
    puts "stmt ID=$stmt\n"
    for {} {[query $stmt step]} {} {
        puts [query $stmt col string 0] " " [query $stmt col string 1] \n
    }
    query $stmt finalize
    return 0
} rc
if {0 != $rc} {
    puts "ERROR: $rc\n"
}
&lt;th1>
</pre></nowiki>

The various subcommands are summarized in the following subsections, and here
are some notes regarding calling conventions:

The (bind, col, step, finalize) functions accept their statement ID argument
either right after the "query" command or right after the final subcommand.
The following examples demonstrate this:

<nowiki><pre>
query $stmt step
query step $stmt

query $stmt finalize
query finalize $stmt

query col string $stmt 1
query $stmt col string 1

query bind string $stmt 1 "foo"
query $stmt bind string 1 "foo"
</pre></nowiki>

The "prefered" form is:

<nowiki><pre>
query StmtId command ...
</pre></nowiki>

(Why, then, are both forms accepted? Because the "preferred" form only
evolved only after using the first form in script code.)


<h2>prepare</h2>

This subcommand prepares a query for execution. It returns a statement handle
ID which must be passed to any other functions using the API.

All prepared statements must be <tt>finalize</tt>d when they have outlived
their usefulness.

<nowiki><pre>
set stmt [query prepare {SELECT ...}]
...
query $stmt finalize
</pre></nowiki>


<h2>finalize</h2>

Releases all resources associated with the statement. Note that future
calls to <tt>prepare</tt> might re-use the same statement statement
ID.

<nowiki><pre>
set stmt [query prepare "SELECT ..."]
...
query $stmt finalize
</pre></nowiki>


<h2>step</h2>

This subcommand steps the result set by one row. It returns 0
at the end of the set, a positive value if a new row is available,
and throws for any other condition.

<nowiki><pre>
for {} {[query $stmt step]} {} {
   puts [query $stmt col string 0] "\n"
}
</pre></nowiki>


<h2>reset</h2>

Resets a query so that it can be executed again. This is only needed when
binding parameters in a loop - call it at the end of each loop iteration.

<nowiki><pre>
query $stmt reset
query reset $stmt
</pre></nowiki>


<h2>bind xxx</h2>

The <tt>bind xxx</tt> family of subcommands attach values to queries
before stepping through them. The subcommands include:

   *  <tt>bind StmtId int Index Value</tt>
   *  <tt>bind StmtId double Index Value</tt>
   *  <tt>bind StmtId null Index</tt>
   *  <tt>bind StmtId string Index Value</tt>

Note that all of those optionally accept the statement handle directly after
the "query" command (before the "col" subcommand). e.g.
<tt>query bind null $stmt 1</tt> and
<tt>query $stmt bind null 1</tt> are equivalent. They also accept the column index
either before or after the type name, e.g.
<tt>query $stmt bind 1 string ...</tt> and <tt>query $stmt bind string 1 ...</tt> are equivalent.


Achtung: the bind API uses 1-based indexes, just like SQL does.

<nowiki><pre>
set stmt [query prepare "SELECT ... WHERE user=?"]
query $stmt bind int 1 drh
if {[query $stmt step]} {
   puts [query $stmt col string 0] "\n"
}
query $stmt finalize
</pre></nowiki>


<h2>col xxx</h2>

The <tt>col xxx</tt> familys of subcommands are for fetching values and metadata from result rows.

   *  <tt>col StmtId count</tt> Returns the number of result columns in the statement.
   *  <tt>col StmtId isnull Index</tt> Returns non-0 if the given column contains an SQL NULL value.
   *  <tt>col StmtId (double|int|string) Index</tt> Fetches a column's value as either a number or string.
   *  <tt>col StmtId time Index Format Modifiers</tt> Formats a time value. See below.
   *  <tt>col StmtId type Index</tt> Returns the given column's type as a value from the <tt>SQLITE_TYPENAME</tt> family of constants.

Note that all of those optionally accept the statement handle directly after
the "query" command (before the "col" subcommand). e.g.
<tt>query $stmt col count</tt> and
<tt>query col count $stmt</tt> are equivalent. They also accept the column index
either before or after the type name, e.g.
<tt>query $stmt col 1 string</tt> and <tt>query $stmt col string 1</tt> are equivalent.

Achtung: the col API uses 0-based indexes, just like SQL does.

<h3>col time</h3>

This function is a proxy for sqlite3's
<tt>[http://www.sqlite.org/lang_datefunc.html|strftime()]</tt> function. It is used like this:


<nowiki><pre>
query $stmt col time $index {%Y%m%d @ %H:%M:%S}
</pre></nowiki>

Any remaining arguments are treated as "modifiers" and passed as-is to strfmtime. For example:

<nowiki><pre>
query $stmt col time $index {%Y%m%d @ %H:%M:%S} {+5 years}
query $stmt col time $index %s unixepoch
</pre></nowiki>




<h2>strftime</h2>

This works like <tt>col time</tt> (described below) but takes its
value from an arbitrary source specified by the 3rd argument.

<nowiki><pre>
query strftime %s 1319211587 unixepoch
query strftime {%Y%m%d @ %H:%M:%S} [query $stmt col string 2] {+10 years}]
</pre></nowiki>

<h2>Global Variables</h2>

This API installs the following global variables, all of which correspond to
<tt>SQLITE_xxx</tt> constant values:

   *  <tt>SQLITE_BLOB</tt>
   *  <tt>SQLITE_FLOAT</tt>
   *  <tt>SQLITE_INTEGER</tt>
   *  <tt>SQLITE_NULL</tt>
   *  <tt>SQLITE_TEXT</tt>

These values are used only by the <tt>col type</tt> function. They can be
accessed from script code via <tt>$::SQLITE_xxx</tt>.