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     17   **
    18     18   ** A Blob is a variable-length containers for arbitrary string
    19     19   ** or binary data.
    20     20   */
    21     21   #include "config.h"
    22     22   #include <zlib.h>
    23     23   #include "blob.h"
    24         -
    25     24   #if INTERFACE
           25  +
    26     26   /*
    27     27   ** A Blob can hold a string or a binary object of arbitrary size.  The
    28     28   ** size changes as necessary.
    29     29   */
    30     30   struct Blob {
    31     31     unsigned int nUsed;            /* Number of bytes used in aData[] */
    32     32     unsigned int nAlloc;           /* Number of bytes allocated for aData[] */
................................................................................
    47     47   
    48     48   /*
    49     49   ** Seek whence parameter values
    50     50   */
    51     51   #define BLOB_SEEK_SET 1
    52     52   #define BLOB_SEEK_CUR 2
    53     53   #define BLOB_SEEK_END 3
    54         -
    55     54   #endif /* INTERFACE */
    56     55   
    57     56   /*
    58     57   ** Make sure a blob is initialized
    59     58   */
    60     59   #define blob_is_init(x) \
    61     60     assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)

Changes to src/db.c.

  1096   1096   **
  1097   1097   ** Check for unfinalized statements and report errors if the reportErrors
  1098   1098   ** argument is true.  Ignore unfinalized statements when false.
  1099   1099   */
  1100   1100   void db_close(int reportErrors){
  1101   1101     sqlite3_stmt *pStmt;
  1102   1102     if( g.db==0 ) return;
         1103  +  if(g.interp){
         1104  +    /* clean up up any query_prepare statements */
         1105  +    Th_DeleteInterp(g.interp);
         1106  +    g.interp = 0;
         1107  +  }
         1108  +
  1103   1109     if( g.fSqlStats ){
  1104   1110       int cur, hiwtr;
  1105   1111       sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
  1106   1112       fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
  1107   1113       sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
  1108   1114       fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
  1109   1115       sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);

Changes to src/main.c.

   677    677     if( p==0 ) fossil_panic("out of memory");
   678    678     return p;
   679    679   }
   680    680   void fossil_free(void *p){
   681    681     free(p);
   682    682   }
   683    683   void *fossil_realloc(void *p, size_t n){
   684         -  p = realloc(p, n);
   685         -  if( p==0 ) fossil_panic("out of memory");
   686         -  return p;
          684  +  void * re = realloc(p, n);
          685  +  if( re==0 && n>0 ) fossil_panic("out of memory");
          686  +  return (n > 0) ? re : 0;
   687    687   }
   688    688   
   689    689   /*
   690    690   ** This function implements a cross-platform "system()" interface.
   691    691   */
   692    692   int fossil_system(const char *zOrigCmd){
   693    693     int rc;
................................................................................
   785    785   
   786    786   
   787    787   /*
   788    788   ** Look for a command-line option.  If present, return a pointer.
   789    789   ** Return NULL if missing.
   790    790   **
   791    791   ** hasArg==0 means the option is a flag.  It is either present or not.
   792         -** hasArg==1 means the option has an argument.  Return a pointer to the
   793         -** argument.
          792  +** hasArg==1 means the option has an argument.  Return a pointer to
          793  +** the argument. If hasArg is 0 and the argument is found then a
          794  +** pointer to an empty string is returned (to distinguish from
          795  +** NULL). If hasArg==1 and the option lies at the end of the argument
          796  +** list, it is treated as if it had not been found.
          797  +**
          798  +** Note that this function REMOVES any found entry from the args list,
          799  +** so calling this twice for the same var will cause NULL to be
          800  +** returned after the first time.
          801  +**
          802  +** zLong may not be NULL but zShort may be.
          803  +**
          804  +** Options are accepted in these forms, depending on the value of
          805  +** hasArg:
          806  +**
          807  +** hasArg=true:
          808  +**  -long        (hasArg==0)
          809  +**  -long VALUE  (hasArg==1)
          810  +**  -long=VALUE  (hasArg==1)
          811  +**  -short       (hasArg==0)
          812  +**  -short VALUE (hasArg==1)
          813  +**  -short=VALUE (hasArg==1)
   794    814   */
   795    815   const char *find_option(const char *zLong, const char *zShort, int hasArg){
   796    816     int i;
   797         -  int nLong;
          817  +  int nLong, nShort;
   798    818     const char *zReturn = 0;
   799    819     assert( hasArg==0 || hasArg==1 );
   800    820     nLong = strlen(zLong);
          821  +  nShort = zShort ? strlen(zShort) : 0;
   801    822     for(i=1; i<g.argc; i++){
   802    823       char *z;
   803         -    if (i+hasArg >= g.argc) break;
   804    824       z = g.argv[i];
   805    825       if( z[0]!='-' ) continue;
   806    826       z++;
   807    827       if( z[0]=='-' ){
   808    828         if( z[1]==0 ){
   809    829           remove_from_argv(i, 1);
   810    830           break;
................................................................................
   813    833       }
   814    834       if( strncmp(z,zLong,nLong)==0 ){
   815    835         if( hasArg && z[nLong]=='=' ){
   816    836           zReturn = &z[nLong+1];
   817    837           remove_from_argv(i, 1);
   818    838           break;
   819    839         }else if( z[nLong]==0 ){
   820         -        zReturn = g.argv[i+hasArg];
          840  +        if( hasArg ){
          841  +          if (i+hasArg >= g.argc) break;
          842  +          zReturn = g.argv[i+hasArg];
          843  +        }else{
          844  +          zReturn = "";
          845  +        }
          846  +        remove_from_argv(i, 1+hasArg);
          847  +        break;
          848  +      }
          849  +    }else if( strncmp(z,zShort,nShort)==0 ){
          850  +      if( hasArg && z[nShort]=='=' ){
          851  +        zReturn = &z[nShort+1];
          852  +        remove_from_argv(i, 1);
          853  +        break;
          854  +      }else if( z[nShort]==0 ){
          855  +        if( hasArg ){
          856  +          if (i+hasArg >= g.argc) break;
          857  +          zReturn = g.argv[i+hasArg];
          858  +        }else{
          859  +          zReturn = "";
          860  +        }
   821    861           remove_from_argv(i, 1+hasArg);
   822    862           break;
   823    863         }
   824         -    }else if( fossil_strcmp(z,zShort)==0 ){
   825         -      zReturn = g.argv[i+hasArg];
   826         -      remove_from_argv(i, 1+hasArg);
   827         -      break;
   828    864       }
   829    865     }
          866  +
   830    867     return zReturn;
   831    868   }
   832    869   
   833    870   /*
   834    871   ** Verify that there are no unprocessed command-line options.  If
   835    872   ** Any remaining command-line argument begins with "-" print
   836    873   ** an error message and quit.

Changes to src/report.c.

    79     79                            href("%R/rptsql?rn=%d", rn));
    80     80       }
    81     81       blob_appendf(&ril, "</li>\n");
    82     82     }
    83     83   
    84     84     Th_Store("report_items", blob_str(&ril));
    85     85     
    86         -  Th_Render(zScript);
           86  +  Th_Render(zScript, Th_Render_Flags_DEFAULT);
    87     87     
    88     88     blob_reset(&ril);
    89     89     if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1);
    90     90   
    91     91     style_footer();
    92     92   }
    93     93   

Changes to src/style.c.

    17     17   **
    18     18   ** This file contains code to implement the basic web page look and feel.
    19     19   **
    20     20   */
    21     21   #include "config.h"
    22     22   #include "style.h"
    23     23   
    24         -
    25     24   /*
    26     25   ** Elements of the submenu are collected into the following
    27     26   ** structure and displayed below the main menu by style_header().
    28     27   **
    29     28   ** Populate this structure with calls to style_submenu_element()
    30     29   ** prior to calling style_header().
    31     30   */
................................................................................
   192    191     Th_Store("manifest_version", MANIFEST_VERSION);
   193    192     Th_Store("manifest_date", MANIFEST_DATE);
   194    193     Th_Store("compiler_name", COMPILER_NAME);
   195    194     if( g.zLogin ){
   196    195       Th_Store("login", g.zLogin);
   197    196     }
   198    197     if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
   199         -  Th_Render(zHeader);
          198  +  Th_Render(zHeader, Th_Render_Flags_DEFAULT);
   200    199     if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
   201    200     Th_Unstore("title");   /* Avoid collisions with ticket field names */
   202    201     cgi_destination(CGI_BODY);
   203    202     g.cgiOutput = 1;
   204    203     headerHasBeenGenerated = 1;
   205    204     sideboxUsed = 0;
   206    205   
................................................................................
   279    278   
   280    279     /* Set the href= field on hyperlinks.  Do this before the footer since
   281    280     ** the footer will be generating </html> */
   282    281     style_resolve_href();
   283    282   
   284    283     zFooter = db_get("footer", (char*)zDefaultFooter);
   285    284     if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
   286         -  Th_Render(zFooter);
          285  +  Th_Render(zFooter, Th_Render_Flags_DEFAULT);
   287    286     if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
   288    287     
   289    288     /* Render trace log if TH1 tracing is enabled. */
   290    289     if( g.thTrace ){
   291    290       cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
   292    291       cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
   293    292       cgi_append_content("</span>\n", -1);
................................................................................
   958    957     }
   959    958   
   960    959     /* Process through TH1 in order to give an opportunity to substitute
   961    960     ** variables such as $baseurl.
   962    961     */
   963    962     Th_Store("baseurl", g.zBaseURL);
   964    963     Th_Store("home", g.zTop);
   965         -  Th_Render(blob_str(&css));
          964  +  Th_Render(blob_str(&css), Th_Render_Flags_DEFAULT);
   966    965   
   967    966     /* Tell CGI that the content returned by this page is considered cacheable */
   968    967     g.isConst = 1;
   969    968   }
   970    969   
   971    970   /*
   972    971   ** WEBPAGE: test_env

Changes to src/th.c.

     3      3   ** The implementation of the TH core. This file contains the parser, and 
     4      4   ** the implementation of the interface in th.h.
     5      5   */
     6      6   
     7      7   #include "th.h"
     8      8   #include <string.h>
     9      9   #include <assert.h>
           10  +#include <stdio.h> /* FILE class */
    10     11   
    11     12   typedef struct Th_Command   Th_Command;
    12     13   typedef struct Th_Frame     Th_Frame;
    13     14   typedef struct Th_Variable  Th_Variable;
    14     15   
           16  +/*
           17  +** Shared instance. See th.h for the docs.
           18  +*/
           19  +const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE = {
           20  +  Th_Output_f_FILE /* xWrite() */,
           21  +  Th_Output_dispose_FILE /* dispose() */,
           22  +  NULL /*pState*/,
           23  +  1/*enabled*/
           24  +};
           25  +
           26  +/*
           27  +** Holds client-provided "garbage collected" data for
           28  +** a Th_Interp instance.
           29  +*/
           30  +struct Th_GcEntry {
           31  +  void * pData;                        /* arbitrary data */
           32  +  void (*xDel)( Th_Interp *, void * ); /* finalizer for pData */
           33  +};
           34  +typedef struct Th_GcEntry Th_GcEntry;
           35  +
    15     36   /*
    16     37   ** Interpreter structure.
    17     38   */
    18     39   struct Th_Interp {
    19         -  Th_Vtab *pVtab;     /* Copy of the argument passed to Th_CreateInterp() */
           40  +  Th_Vtab *pVtab;    /* Copy of the argument passed to Th_CreateInterp() */
    20     41     char *zResult;     /* Current interpreter result (Th_Malloc()ed) */
    21         -  int nResult;        /* number of bytes in zResult */
    22         -  Th_Hash *paCmd;     /* Table of registered commands */
    23         -  Th_Frame *pFrame;   /* Current execution frame */
    24         -  int isListMode;     /* True if thSplitList() should operate in "list" mode */
           42  +  int nResult;       /* number of bytes in zResult */
           43  +  Th_Hash *paCmd;    /* Table of registered commands */
           44  +  Th_Frame *pFrame;  /* Current execution frame */
           45  +  int isListMode;    /* True if thSplitList() should operate in "list" mode */
           46  +  Th_Hash * paGc;    /* Holds client-provided data owned by this
           47  +                        object. It would be more efficient to store
           48  +                        these in a list (because we don't expect many
           49  +                        entries), but Th_Hash has the strong advantage
           50  +                        of being here and working.
           51  +                     */
    25     52   };
    26     53   
    27     54   /*
    28     55   ** Each TH command registered using Th_CreateCommand() is represented
    29     56   ** by an instance of the following structure stored in the Th_Interp.paCmd
    30     57   ** hash-table.
    31     58   */
................................................................................
   104    131   static int thEndOfLine(const char *, int);
   105    132   
   106    133   static int  thPushFrame(Th_Interp*, Th_Frame*);
   107    134   static void thPopFrame(Th_Interp*);
   108    135   
   109    136   static void thFreeVariable(Th_HashEntry*, void*);
   110    137   static void thFreeCommand(Th_HashEntry*, void*);
          138  +static void thFreeGc(Th_HashEntry*, void*);
   111    139   
   112    140   /*
   113    141   ** The following are used by both the expression and language parsers.
   114    142   ** Given that the start of the input string (z, n) is a language 
   115    143   ** construct of the relevant type (a command enclosed in [], an escape
   116    144   ** sequence etc.), these functions determine the number of bytes
   117    145   ** of the input consumed by the construct. For example:
................................................................................
   286    314     Th_Command *pCommand = (Th_Command *)pEntry->pData;
   287    315     if( pCommand->xDel ){
   288    316       pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
   289    317     }
   290    318     Th_Free((Th_Interp *)pContext, pEntry->pData);
   291    319     pEntry->pData = 0;
   292    320   }
          321  +
          322  +/*
          323  +** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees
          324  +** pEntry->pData but not pEntry.
          325  +*/
          326  +static void thFreeGc(Th_HashEntry *pEntry, void *pContext){
          327  +  Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData;
          328  +  if(gc){
          329  +    if( gc->xDel ){
          330  +      gc->xDel( (Th_Interp*)pContext, gc->pData );
          331  +    }
          332  +    Th_Free((Th_Interp *)pContext, pEntry->pData);
          333  +    pEntry->pData = 0;
          334  +  }
          335  +}
          336  +
   293    337   
   294    338   /*
   295    339   ** Push a new frame onto the stack.
   296    340   */
   297    341   static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){
   298    342     pFrame->paVar = Th_HashNew(interp);
   299    343     pFrame->pCaller = interp->pFrame;
................................................................................
  1367   1411   }
  1368   1412   
  1369   1413   
  1370   1414   /* 
  1371   1415   ** Wrappers around the supplied malloc() and free() 
  1372   1416   */
  1373   1417   void *Th_Malloc(Th_Interp *pInterp, int nByte){
  1374         -  void *p = pInterp->pVtab->xMalloc(nByte);
         1418  +  void * p = pInterp->pVtab->xRealloc(NULL, nByte);
  1375   1419     if( p ){
  1376   1420       memset(p, 0, nByte);
         1421  +  }else{
         1422  +    assert( 0 == nByte );
  1377   1423     }
  1378   1424     return p;
  1379   1425   }
  1380   1426   void Th_Free(Th_Interp *pInterp, void *z){
  1381   1427     if( z ){
  1382         -    pInterp->pVtab->xFree(z);
         1428  +    pInterp->pVtab->xRealloc(z, 0);
         1429  +  }
         1430  +}
         1431  +void *Th_Realloc(Th_Interp *pInterp, void *z, int nByte){
         1432  +  void *p = pInterp->pVtab->xRealloc(z, nByte);
         1433  +  return p;
         1434  +}
         1435  +
         1436  +
         1437  +int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int nData ){
         1438  +  if(!vTab->out.xWrite){
         1439  +    return -1;
         1440  +  }else if(!vTab->out.enabled){
         1441  +    return 0;
         1442  +  }else{
         1443  +    return vTab->out.xWrite( zData, nData, vTab->out.pState );
         1444  +  }
         1445  +}
         1446  +
         1447  +
         1448  +int Th_Output( Th_Interp *pInterp, char const * zData, int nData ){
         1449  +  return Th_Vtab_Output( pInterp->pVtab, zData, nData );
         1450  +}
         1451  +
         1452  +void Th_OutputEnable( Th_Interp *pInterp, char flag ){
         1453  +  pInterp->pVtab->out.enabled = flag;
         1454  +}
         1455  +
         1456  +char Th_OutputEnabled( Th_Interp *pInterp ){
         1457  +  return pInterp->pVtab->out.enabled ? 1 : 0;
         1458  +}
         1459  +
         1460  +int Th_Output_f_FILE( char const * zData, int nData, void * pState ){
         1461  +  FILE * dest = pState ? (FILE*)pState : stdout;
         1462  +  int rc = (int)fwrite(zData, 1, nData, dest);
         1463  +  fflush(dest);
         1464  +  return rc;
         1465  +}
         1466  +
         1467  +void Th_Output_dispose_FILE( void * pState ){
         1468  +  FILE * f = pState ? (FILE*)pState : NULL;
         1469  +  if(f
         1470  +     && (f != stdout)
         1471  +     && (f != stderr)
         1472  +     && (f != stdin)){
         1473  +    fflush(f);
         1474  +    fclose(f);
  1383   1475     }
  1384   1476   }
  1385   1477   
  1386   1478   /*
  1387   1479   ** Install a new th1 command. 
  1388   1480   **
  1389   1481   ** If a command of the same name already exists, it is deleted automatically.
................................................................................
  1636   1728   /* 
  1637   1729   ** Delete an interpreter.
  1638   1730   */
  1639   1731   void Th_DeleteInterp(Th_Interp *interp){
  1640   1732     assert(interp->pFrame);
  1641   1733     assert(0==interp->pFrame->pCaller);
  1642   1734   
         1735  +  /* Delete any client-side gc entries first. */
         1736  +  if( interp->paGc ){
         1737  +    Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
         1738  +    Th_HashDelete(interp, interp->paGc);
         1739  +    interp->paGc = NULL;
         1740  +  }
         1741  +
         1742  +  /* Clean up the output abstraction. */
         1743  +  if( interp->pVtab && interp->pVtab->out.xDispose ){
         1744  +      interp->pVtab->out.xDispose( interp->pVtab->out.pState );
         1745  +  }
         1746  +  
  1643   1747     /* Delete the contents of the global frame. */
  1644   1748     thPopFrame(interp);
  1645   1749   
  1646   1750     /* Delete any result currently stored in the interpreter. */
  1647   1751     Th_SetResult(interp, 0, 0);
  1648   1752   
  1649   1753     /* Delete all registered commands and the command hash-table itself. */
  1650   1754     Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp);
  1651   1755     Th_HashDelete(interp, interp->paCmd);
  1652   1756   
         1757  +  
  1653   1758     /* Delete the interpreter structure itself. */
  1654   1759     Th_Free(interp, (void *)interp);
  1655   1760   }
  1656   1761   
  1657   1762   /* 
  1658   1763   ** Create a new interpreter.
  1659   1764   */
  1660   1765   Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
  1661   1766     Th_Interp *p;
  1662   1767   
  1663   1768     /* Allocate and initialise the interpreter and the global frame */
  1664         -  p = pVtab->xMalloc(sizeof(Th_Interp) + sizeof(Th_Frame));
         1769  +  p = pVtab->xRealloc(NULL, sizeof(Th_Interp) + sizeof(Th_Frame));
  1665   1770     memset(p, 0, sizeof(Th_Interp));
  1666   1771     p->pVtab = pVtab;
  1667   1772     p->paCmd = Th_HashNew(p);
  1668   1773     thPushFrame(p, (Th_Frame *)&p[1]);
  1669   1774   
  1670   1775     return p;
  1671   1776   }
................................................................................
  2269   2374   ** This function is the same as the standard strlen() function, except
  2270   2375   ** that it returns 0 (instead of being undefined) if the argument is
  2271   2376   ** a null pointer.
  2272   2377   */
  2273   2378   int th_strlen(const char *zStr){
  2274   2379     int n = 0;
  2275   2380     if( zStr ){
  2276         -    while( zStr[n] ) n++;
         2381  +    while( zStr[n] ) ++n;
  2277   2382     }
  2278   2383     return n;
  2279   2384   }
  2280   2385   
  2281   2386   /* Whitespace characters:
  2282   2387   **
  2283   2388   **     ' '    0x20
................................................................................
  2333   2438   int th_isalnum(char c){
  2334   2439     return (aCharProp[(unsigned char)c] & 0x0A);
  2335   2440   }
  2336   2441   
  2337   2442   #ifndef LONGDOUBLE_TYPE
  2338   2443   # define LONGDOUBLE_TYPE long double
  2339   2444   #endif
  2340         -typedef char u8;
  2341   2445   
  2342   2446   
  2343   2447   /*
  2344   2448   ** Return TRUE if z is a pure numeric string.  Return FALSE if the
  2345   2449   ** string contains any character which is not part of a number. If
  2346   2450   ** the string is numeric and contains the '.' character, set *realnum
  2347   2451   ** to TRUE (otherwise FALSE).
................................................................................
  2371   2475       if( realnum ) *realnum = 1;
  2372   2476     }
  2373   2477     return *z==0;
  2374   2478   }
  2375   2479   
  2376   2480   /*
  2377   2481   ** The string z[] is an ascii representation of a real number.
  2378         -** Convert this string to a double.
         2482  +** Convert this string to a double and assigns its value to
         2483  +** pResult.
  2379   2484   **
  2380   2485   ** This routine assumes that z[] really is a valid number.  If it
  2381   2486   ** is not, the result is undefined.
  2382   2487   **
  2383   2488   ** This routine is used instead of the library atof() function because
  2384   2489   ** the library atof() might want to use "," as the decimal point instead
  2385   2490   ** of "." depending on how locale is set.  But that would cause problems
  2386   2491   ** for SQL.  So this routine always uses "." regardless of locale.
         2492  +**
         2493  +** Returns the number of bytes of z consumed in parsing the value.
  2387   2494   */
  2388   2495   static int sqlite3AtoF(const char *z, double *pResult){
  2389   2496     int sign = 1;
  2390   2497     const char *zBegin = z;
  2391   2498     LONGDOUBLE_TYPE v1 = 0.0;
  2392   2499     while( th_isspace(*(u8*)z) ) z++;
  2393   2500     if( *z=='-' ){
................................................................................
  2436   2543       }
  2437   2544     }
  2438   2545     *pResult = sign<0 ? -v1 : v1;
  2439   2546     return z - zBegin;
  2440   2547   }
  2441   2548   
  2442   2549   /*
  2443         -** Try to convert the string passed as arguments (z, n) to an integer.
  2444         -** If successful, store the result in *piOut and return TH_OK. 
  2445         -**
  2446         -** If the string cannot be converted to an integer, return TH_ERROR. 
  2447         -** If the interp argument is not NULL, leave an error message in the 
  2448         -** interpreter result too.
         2550  +** Attempts to convert (zArg,nArg) to an integer. On success *piOut is
         2551  +** assigned to its value and TH_OK is returned, else piOut is not
         2552  +** modified and TH_ERROR is returned. Conversion errors are considered
         2553  +** non-fatal here, so interp's error state is not set.
  2449   2554   */
  2450         -int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
         2555  +int Th_TryInt(Th_Interp *interp, const char *z, int n, int *piOut){
  2451   2556     int i = 0;
  2452   2557     int iOut = 0;
  2453   2558   
  2454   2559     if( n<0 ){
  2455   2560       n = th_strlen(z);
  2456   2561     }
  2457   2562   
  2458   2563     if( n>0 && (z[0]=='-' || z[0]=='+') ){
  2459   2564       i = 1;
  2460   2565     }
  2461   2566     for(; i<n; i++){
  2462   2567       if( !th_isdigit(z[i]) ){
  2463         -      Th_ErrorMessage(interp, "expected integer, got: \"", z, n);
  2464   2568         return TH_ERROR;
  2465   2569       }
  2466   2570       iOut = iOut * 10 + (z[i] - 48);
  2467   2571     }
  2468   2572   
  2469   2573     if( n>0 && z[0]=='-' ){
  2470   2574       iOut *= -1;
  2471   2575     }
  2472   2576   
  2473   2577     *piOut = iOut;
  2474   2578     return TH_OK;
  2475   2579   }
         2580  +
         2581  +/*
         2582  +** Try to convert the string passed as arguments (z, n) to an integer.
         2583  +** If successful, store the result in *piOut and return TH_OK. 
         2584  +**
         2585  +** If the string cannot be converted to an integer, return TH_ERROR. 
         2586  +** If the interp argument is not NULL, leave an error message in the 
         2587  +** interpreter result too.
         2588  +*/
         2589  +int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){
         2590  +  const int rc = Th_TryInt(interp, z, n, piOut);
         2591  +  if( TH_OK != rc ){
         2592  +    Th_ErrorMessage(interp, "expected integer, got: \"", z, n);
         2593  +  }
         2594  +  return rc;
         2595  +}
         2596  +
         2597  +
         2598  +/*
         2599  +** Functionally/semantically identical to Th_TryInt() but works on
         2600  +** doubles.
         2601  +*/
         2602  +int Th_TryDouble(
         2603  +  Th_Interp *interp, 
         2604  +  const char *z, 
         2605  +  int n, 
         2606  +  double *pfOut
         2607  +){
         2608  +  if( !sqlite3IsNumber((const char *)z, 0) ){
         2609  +    return TH_ERROR;
         2610  +  }else{
         2611  +    sqlite3AtoF((const char *)z, pfOut);
         2612  +    return TH_OK;
         2613  +  }
         2614  +}
  2476   2615   
  2477   2616   /*
  2478   2617   ** Try to convert the string passed as arguments (z, n) to a double.
  2479   2618   ** If successful, store the result in *pfOut and return TH_OK. 
  2480   2619   **
  2481   2620   ** If the string cannot be converted to a double, return TH_ERROR. 
  2482   2621   ** If the interp argument is not NULL, leave an error message in the 
................................................................................
  2484   2623   */
  2485   2624   int Th_ToDouble(
  2486   2625     Th_Interp *interp, 
  2487   2626     const char *z, 
  2488   2627     int n, 
  2489   2628     double *pfOut
  2490   2629   ){
  2491         -  if( !sqlite3IsNumber((const char *)z, 0) ){
  2492         -    Th_ErrorMessage(interp, "expected number, got: \"", z, n);
  2493         -    return TH_ERROR;
         2630  +  const int rc = Th_TryDouble(interp, z, n, pfOut);
         2631  +  if( TH_OK != rc ){
         2632  +      Th_ErrorMessage(interp, "expected number, got: \"", z, n);
  2494   2633     }
  2495         -
  2496         -  sqlite3AtoF((const char *)z, pfOut);
  2497         -  return TH_OK;
         2634  +  return rc;
  2498   2635   }
  2499   2636   
  2500   2637   /*
  2501   2638   ** Set the result of the interpreter to the th1 representation of
  2502   2639   ** the integer iVal and return TH_OK.
  2503   2640   */
  2504   2641   int Th_SetResultInt(Th_Interp *interp, int iVal){
................................................................................
  2603   2740         *z++ = zExp[i];
  2604   2741       }
  2605   2742     }
  2606   2743   
  2607   2744     *z = '\0';
  2608   2745     return Th_SetResult(interp, zBuf, -1);
  2609   2746   }
         2747  +
         2748  +
         2749  +int Th_SetData( Th_Interp * interp, char const * key,
         2750  +                 void * pData,
         2751  +                 void (*finalizer)( Th_Interp *, void * ) ){
         2752  +  Th_HashEntry * pEnt;
         2753  +  Th_GcEntry * pGc;
         2754  +  if(NULL == interp->paGc){
         2755  +    interp->paGc = Th_HashNew(interp);
         2756  +    assert(NULL != interp->paGc);
         2757  +    if(!interp->paGc){
         2758  +      return TH_ERROR;
         2759  +    }
         2760  +  }
         2761  +  pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1);
         2762  +  if( pEnt->pData ){
         2763  +    thFreeGc( pEnt, interp );
         2764  +  }
         2765  +  assert( NULL == pEnt->pData );
         2766  +  pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry));
         2767  +  pGc->pData = pData;
         2768  +  pGc->xDel = finalizer;
         2769  +  return 0;
         2770  + 
         2771  +}
         2772  +void * Th_GetData( Th_Interp * interp, char const * key ){
         2773  +  Th_HashEntry * e = interp->paGc
         2774  +    ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
         2775  +    : NULL;
         2776  +  return e ? ((Th_GcEntry*)e->pData)->pData : NULL;
         2777  +}
         2778  +
         2779  +int Th_RegisterCommands( Th_Interp * interp,
         2780  +                          Th_Command_Reg const * aCommand ){
         2781  +  int i;
         2782  +  int rc = TH_OK;
         2783  +  for(i=0; (TH_OK==rc) && aCommand[i].zName; ++i){
         2784  +    if ( !aCommand[i].zName ) break;
         2785  +    else if( !aCommand[i].xProc ) continue;
         2786  +    else{
         2787  +      rc = Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
         2788  +                            aCommand[i].pContext, 0);
         2789  +    }
         2790  +  }
         2791  +  return rc;
         2792  +}
         2793  +
         2794  +
         2795  +
         2796  +#ifdef TH_ENABLE_OB
         2797  +/* Reminder: the ob code "really" belongs in th_lang.c or th_main.c,
         2798  +   but it needs access to Th_Interp::pVtab in order to swap out
         2799  +   Th_Vtab_OutputMethods parts for purposes of stacking layers of
         2800  +   buffers. We could add access to it via the public interface,
         2801  +   but that didn't seem appropriate.
         2802  +*/
         2803  +
         2804  +/*
         2805  +** Th_Output_f() impl which redirects output to a Th_Ob_Manager.
         2806  +** Requires that pState be a (Th_Ob_Man*).
         2807  +*/
         2808  +static int Th_Output_f_ob( char const * zData, int len, void * pState );
         2809  +
         2810  +/*
         2811  +** Th_Output::dispose() impl which requires pState to be-a Th_Ob_Manager.
         2812  +*/
         2813  +static void Th_Output_dispose_ob( void * pState );
         2814  +
         2815  +/* Empty-initialized Th_Ob_Manager instance. */
         2816  +#define Th_Ob_Man_empty_m { \
         2817  +  NULL/*aBuf*/,           \
         2818  +  0/*nBuf*/,            \
         2819  +  -1/*cursor*/,       \
         2820  +  NULL/*interp*/,     \
         2821  +  NULL/*aOutput*/       \
         2822  +}
         2823  +
         2824  +/* Empty-initialized Th_Vtab_OutputMethods instance. */
         2825  +#define Th_Vtab_OutputMethods_empty_m { \
         2826  +  NULL /* write() */, \
         2827  +  NULL /* dispose() */, \
         2828  +  NULL /*pState*/,\
         2829  +  1/*enabled*/\
         2830  +}
         2831  +/* Vtab_OutputMethods instance initialized for OB support. */
         2832  +#define Th_Vtab_OutputMethods_ob_m { \
         2833  +  Th_Output_f_ob /*write()*/, \
         2834  +  Th_Output_dispose_ob /* dispose() */, \
         2835  +  NULL /*pState*/,\
         2836  +  1/*enabled*/\
         2837  +}
         2838  +/* Empty-initialized Th_Vtab_Man instance. */
         2839  +static const Th_Ob_Manager Th_Ob_Man_empty = Th_Ob_Man_empty_m;
         2840  +/* Empty-initialized Th_Vtab_OutputMethods instance. */
         2841  +static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_empty = Th_Vtab_OutputMethods_empty_m;
         2842  +/* Th_Vtab_OutputMethods instance initialized for OB support. */
         2843  +static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_ob = Th_Vtab_OutputMethods_ob_m;
         2844  +
         2845  +/*
         2846  +**   Internal key for Th_Set/GetData(), for storing a Th_Ob_Manager instance.
         2847  +*/
         2848  +#define Th_Ob_Man_KEY "Th_Ob_Manager"
         2849  +
         2850  +Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *interp){
         2851  +  return (Th_Ob_Manager*) Th_GetData(interp, Th_Ob_Man_KEY );
         2852  +}
         2853  +
         2854  +Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan ){
         2855  +  return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
         2856  +}
         2857  +
         2858  +
         2859  +/*
         2860  +** Th_Output_f() impl which expects pState to be (Th_Ob_Manager*).
         2861  +** (zData,len) are appended to pState's current output buffer.
         2862  +*/
         2863  +int Th_Output_f_ob( char const * zData, int len, void * pState ){
         2864  +  Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
         2865  +  Blob * b = Th_Ob_GetCurrentBuffer( pMan );
         2866  +  assert( NULL != pMan );
         2867  +  assert( b );
         2868  +  blob_append( b, zData, len );
         2869  +  return len;
         2870  +}
         2871  +
         2872  +void Th_Output_dispose_ob( void * pState ){
         2873  +  /* possible todo: move the cleanup logic from
         2874  +     Th_Ob_Pop() to here? */
         2875  +#if 0
         2876  +  Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
         2877  +  Blob * b = Th_Ob_GetCurrentBuffer( pMan );
         2878  +  assert( NULL != pMan );
         2879  +  assert( b );
         2880  +#endif
         2881  +}
         2882  +
         2883  +
         2884  +
         2885  +int Th_Ob_Push( Th_Ob_Manager * pMan,
         2886  +                Th_Vtab_OutputMethods const * pWriter,
         2887  +                Blob ** pOut ){
         2888  +  Blob * pBlob;
         2889  +  int x, i;
         2890  +  if( NULL == pWriter ){
         2891  +    pWriter = &Th_Vtab_OutputMethods_ob;
         2892  +  }
         2893  +  assert( NULL != pMan->interp );
         2894  +  pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob));
         2895  +  *pBlob = empty_blob;
         2896  +
         2897  +  if( pMan->cursor >= pMan->nBuf-2 ){
         2898  +    /* expand if needed */
         2899  +    void * re;
         2900  +    x = pMan->nBuf + 5;
         2901  +    if( pMan->cursor >= x ) {
         2902  +      assert( 0 && "This really should not happen." );
         2903  +      x = pMan->cursor + 5;
         2904  +    }
         2905  +    re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
         2906  +    if(NULL==re){
         2907  +      goto error;
         2908  +    }
         2909  +    pMan->aBuf = (Blob **)re;
         2910  +    re = Th_Realloc( pMan->interp, pMan->aOutput, x * sizeof(Th_Vtab_OutputMethods) );
         2911  +    if(NULL==re){
         2912  +      goto error;
         2913  +    }
         2914  +    pMan->aOutput = (Th_Vtab_OutputMethods*)re;
         2915  +    for( i = pMan->nBuf; i < x; ++i ){
         2916  +      pMan->aOutput[i] = Th_Vtab_OutputMethods_empty;
         2917  +      pMan->aBuf[i] = NULL;
         2918  +    }
         2919  +    pMan->nBuf = x;
         2920  +  }
         2921  +  assert( pMan->nBuf > pMan->cursor );
         2922  +  assert( pMan->cursor >= -1 );
         2923  +  ++pMan->cursor;
         2924  +  pMan->aBuf[pMan->cursor] = pBlob;
         2925  +  pMan->aOutput[pMan->cursor] = pMan->interp->pVtab->out;
         2926  +  pMan->interp->pVtab->out = *pWriter;
         2927  +  pMan->interp->pVtab->out.pState = pMan;
         2928  +  if( pOut ){
         2929  +    *pOut = pBlob;
         2930  +  }
         2931  +  return TH_OK;
         2932  +  error:
         2933  +  if( pBlob ){
         2934  +    Th_Free( pMan->interp, pBlob );
         2935  +  }
         2936  +  return TH_ERROR;
         2937  +}
         2938  +
         2939  +Blob * Th_Ob_Pop( Th_Ob_Manager * pMan ){
         2940  +  if( pMan->cursor < 0 ){
         2941  +    return NULL;
         2942  +  }else{
         2943  +    Blob * rc;
         2944  +    Th_Vtab_OutputMethods * theOut;
         2945  +    assert( pMan->nBuf > pMan->cursor );
         2946  +    rc = pMan->aBuf[pMan->cursor];
         2947  +    pMan->aBuf[pMan->cursor] = NULL;
         2948  +    theOut = &pMan->aOutput[pMan->cursor];
         2949  +#if 0
         2950  +    /* We need something like this (but not this!) if we extend the
         2951  +       support to use other (non-Blob) proxies. We will likely need
         2952  +       another callback function or two for that case, e.g. xStart()
         2953  +       and xEnd(), which would be called when they are pushed/popped
         2954  +       to/from the stack.
         2955  +    */
         2956  +    if( theOut->xDispose ){
         2957  +      theOut->xDispose( theOut->pState );
         2958  +    }
         2959  +#endif
         2960  +    pMan->interp->pVtab->out = *theOut;
         2961  +    pMan->aOutput[pMan->cursor] = Th_Vtab_OutputMethods_empty;
         2962  +    if(-1 == --pMan->cursor){
         2963  +      Th_Interp * interp = pMan->interp;
         2964  +      Th_Free( pMan->interp, pMan->aBuf );
         2965  +      Th_Free( pMan->interp, pMan->aOutput );
         2966  +      *pMan = Th_Ob_Man_empty;
         2967  +      pMan->interp = interp;
         2968  +      assert(-1 == pMan->cursor);
         2969  +    }
         2970  +    return rc;
         2971  +  }
         2972  +}
         2973  +
         2974  +int Th_Ob_PopAndFree( Th_Ob_Manager * pMan ){
         2975  +  Blob * b = Th_Ob_Pop( pMan );
         2976  +  if(!b) return 1;
         2977  +  else {
         2978  +    blob_reset(b);
         2979  +    Th_Free( pMan->interp, b );
         2980  +    return 0;
         2981  +  }
         2982  +}
         2983  +
         2984  +
         2985  +void Th_ob_cleanup( Th_Ob_Manager * man ){
         2986  +  while( 0 == Th_Ob_PopAndFree(man) ){}
         2987  +}
         2988  +
         2989  +
         2990  +/*
         2991  +** TH command:
         2992  +**
         2993  +** ob clean
         2994  +**
         2995  +** Erases any currently buffered contents but does not modify
         2996  +** the buffering level.
         2997  +*/
         2998  +static int ob_clean_command( Th_Interp *interp, void *ctx,
         2999  +                             int argc,  const char **argv, int *argl
         3000  +){
         3001  +  const char doRc = ctx ? 1 : 0;
         3002  +  Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
         3003  +  Blob * b;
         3004  +  assert( pMan && (interp == pMan->interp) );
         3005  +  b = pMan ? Th_Ob_GetCurrentBuffer(pMan) : NULL;
         3006  +  if(!b){
         3007  +    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
         3008  +    return TH_ERROR;
         3009  +  }else{
         3010  +    blob_reset(b);
         3011  +    if( doRc ) {
         3012  +      Th_SetResultInt( interp, 0 );
         3013  +    }
         3014  +    return TH_OK;
         3015  +  }
         3016  +}
         3017  +
         3018  +/*
         3019  +** TH command:
         3020  +**
         3021  +** ob end
         3022  +**
         3023  +** Erases any currently buffered contents and pops the current buffer
         3024  +** from the stack.
         3025  +*/
         3026  +static int ob_end_command( Th_Interp *interp, void *ctx,
         3027  +                           int argc,  const char **argv, int *argl ){
         3028  +  const char doRc = ctx ? 1 : 0;
         3029  +  Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
         3030  +  Blob * b;
         3031  +  assert( pMan && (interp == pMan->interp) );
         3032  +  b = Th_Ob_Pop(pMan);
         3033  +  if(!b){
         3034  +    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
         3035  +    return TH_ERROR;
         3036  +  }else{
         3037  +    blob_reset(b);
         3038  +    Th_Free( interp, b );
         3039  +    if(doRc){
         3040  +      Th_SetResultInt( interp, 0 );
         3041  +    }
         3042  +    return TH_OK;
         3043  +  }
         3044  +}
         3045  +
         3046  +/*
         3047  +** TH command:
         3048  +**
         3049  +** ob flush ?pop|end?
         3050  +**
         3051  +** Briefly reverts the output layer to the next-lower
         3052  +** level, flushes the current buffer to that output layer,
         3053  +** and clears out the current buffer. Does not change the
         3054  +** buffering level unless "end" is specified, in which case
         3055  +** it behaves as if "ob end" had been called (after flushing
         3056  +** the buffer).
         3057  +*/
         3058  +static int ob_flush_command( Th_Interp *interp, void *ctx,
         3059  +                             int argc,  const char **argv, int *argl ){
         3060  +  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
         3061  +  Blob * b = NULL;
         3062  +  Th_Vtab * oldVtab;
         3063  +  int rc = TH_OK;
         3064  +  assert( pMan && (interp == pMan->interp) );
         3065  +  b = Th_Ob_GetCurrentBuffer(pMan);
         3066  +  if( NULL == b ){
         3067  +    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
         3068  +    return TH_ERROR;
         3069  +  }
         3070  +  oldVtab = interp->pVtab;
         3071  +  interp->pVtab->out = pMan->aOutput[pMan->cursor];
         3072  +  Th_Output( interp, blob_str(b), b->nUsed );
         3073  +  interp->pVtab = oldVtab;
         3074  +  blob_reset(b);
         3075  +
         3076  +  if(!rc && argc>2){
         3077  +    int argPos = 2;
         3078  +    char const * sub = argv[argPos];
         3079  +    int subL = argl[argPos];
         3080  +    /* "flush end" */
         3081  +    if(th_strlen(sub)==3 &&
         3082  +       ((0==memcmp("end", sub, subL)
         3083  +         || (0==memcmp("pop", sub, subL))))){
         3084  +      rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
         3085  +    }
         3086  +  }
         3087  +  Th_SetResultInt( interp, 0 );
         3088  +  return rc;
         3089  +}
         3090  +
         3091  +/*
         3092  +** TH command:
         3093  +**
         3094  +** ob get ?clean|end|pop?
         3095  +**
         3096  +** Fetches the contents of the current buffer level.  If either
         3097  +** 'clean' or 'end' are specified then the effect is as if "ob clean"
         3098  +** or "ob end", respectively, are called after fetching the
         3099  +** value. Calling "ob get end" is functionality equivalent to "ob get"
         3100  +** followed by "ob end".
         3101  +*/
         3102  +static int ob_get_command( Th_Interp *interp, void *ctx,
         3103  +                           int argc,  const char **argv, int *argl){
         3104  +  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
         3105  +  Blob * b = NULL;
         3106  +  assert( pMan && (interp == pMan->interp) );
         3107  +  b = Th_Ob_GetCurrentBuffer(pMan);
         3108  +  if( NULL == b ){
         3109  +    Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
         3110  +    return TH_ERROR;
         3111  +  }else{
         3112  +    int argPos = 2;
         3113  +    char const * sub;
         3114  +    int subL;
         3115  +    int rc = TH_OK;
         3116  +    Th_SetResult( interp, blob_str(b), b->nUsed );
         3117  +    if(argc>argPos){
         3118  +      sub = argv[argPos];
         3119  +      subL = argl[argPos];
         3120  +      /* "ob get clean" */
         3121  +      if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){
         3122  +        rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1);
         3123  +      }/* "ob get end" */
         3124  +      else if(!rc && th_strlen(sub)==3 &&
         3125  +              ((0==memcmp("end", sub, subL))
         3126  +               || (0==memcmp("pop", sub, subL)))){
         3127  +        rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
         3128  +      }
         3129  +    }
         3130  +    return rc;
         3131  +  }
         3132  +}
         3133  +
         3134  +/*
         3135  +** TH command:
         3136  +**
         3137  +** ob level
         3138  +**
         3139  +** Returns the buffering level, where 0 means no buffering is
         3140  +** active, 1 means 1 level is active, etc.
         3141  +*/
         3142  +static int ob_level_command( Th_Interp *interp, void *ctx,
         3143  +                             int argc,  const char **argv, int *argl
         3144  +){
         3145  +  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
         3146  +  Th_SetResultInt( interp, 1 + pMan->cursor );
         3147  +  return TH_OK;
         3148  +}
         3149  +
         3150  +/*
         3151  +** TH command:
         3152  +**
         3153  +** ob start|push
         3154  +**
         3155  +** Pushes a new level of buffering onto the buffer stack.
         3156  +** Returns the new buffering level (1-based).
         3157  +**
         3158  +** TODO: take an optional final argument naming the output handler.
         3159  +** e.g. "stdout" or "cgi" or "default"
         3160  +** 
         3161  +*/
         3162  +static int ob_start_command( Th_Interp *interp, void *ctx,
         3163  +                             int argc,  const char **argv, int *argl
         3164  +){
         3165  +  Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
         3166  +  Blob * b = NULL;
         3167  +  int rc;
         3168  +  Th_Vtab_OutputMethods const * pWriter = &Th_Vtab_OutputMethods_ob;
         3169  +  assert( pMan && (interp == pMan->interp) );
         3170  +  rc = Th_Ob_Push(pMan, NULL, &b);
         3171  +  if( TH_OK != rc ){
         3172  +    assert( NULL == b );
         3173  +    return rc;
         3174  +  }
         3175  +  assert( NULL != b );
         3176  +  Th_SetResultInt( interp, 1 + pMan->cursor );
         3177  +  return TH_OK;
         3178  +}
         3179  +
         3180  +static void finalizerObMan( Th_Interp * interp, void * p ){
         3181  +  Th_Ob_Manager * man = (Th_Ob_Manager*)p;
         3182  +  if(man){
         3183  +    assert( interp == man->interp );
         3184  +    Th_ob_cleanup( man );
         3185  +    Th_Free( interp, p );
         3186  +  }
         3187  +}
         3188  +
         3189  +/*
         3190  +** TH command:
         3191  +**
         3192  +** ob clean|(end|pop)|flush|get|level|(start|push)
         3193  +**
         3194  +** Runs the given subcommand. Some subcommands have other subcommands
         3195  +** (see their docs for details).
         3196  +** 
         3197  +*/
         3198  +static int ob_cmd(
         3199  +  Th_Interp *interp, 
         3200  +  void *ignored, 
         3201  +  int argc, 
         3202  +  const char **argv, 
         3203  +  int *argl
         3204  +){
         3205  +  Th_Ob_Manager * pMan = Th_Ob_GetManager(interp);
         3206  +  Th_SubCommand aSub[] = {
         3207  +    { "clean",     ob_clean_command },
         3208  +    { "end",       ob_end_command },
         3209  +    { "flush",     ob_flush_command },
         3210  +    { "get",       ob_get_command },
         3211  +    { "level",     ob_level_command },
         3212  +    { "pop",       ob_end_command },
         3213  +    { "push",      ob_start_command },
         3214  +    { "start",     ob_start_command },
         3215  +    /* TODO: enable/disable commands which call Th_OutputEnable(). */
         3216  +    { 0, 0 }
         3217  +  };
         3218  +  assert(NULL != pMan && pMan->interp==interp);
         3219  +  return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
         3220  +}
         3221  +
         3222  +int th_register_ob(Th_Interp * interp){
         3223  +  int rc;
         3224  +  static Th_Command_Reg aCommand[] = {
         3225  +    {"ob",    ob_cmd,   0},
         3226  +    {0,0,0}
         3227  +  };
         3228  +  rc = Th_RegisterCommands( interp, aCommand );
         3229  +  if(NULL == Th_Ob_GetManager(interp)){
         3230  +    Th_Ob_Manager * pMan;
         3231  +    pMan = Th_Malloc(interp, sizeof(Th_Ob_Manager));
         3232  +    if(!pMan){
         3233  +      rc = TH_ERROR;
         3234  +    }else{
         3235  +      *pMan = Th_Ob_Man_empty;
         3236  +      pMan->interp = interp;
         3237  +      assert( -1 == pMan->cursor );
         3238  +      Th_SetData( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
         3239  +      assert( NULL != Th_Ob_GetManager(interp) );
         3240  +    }
         3241  +  }
         3242  +  return rc;
         3243  +}
         3244  +
         3245  +#undef Th_Ob_Man_empty_m
         3246  +#undef Th_Vtab_OutputMethods_empty_m
         3247  +#undef Th_Vtab_OutputMethods_ob_m
         3248  +#undef Th_Ob_Man_KEY
         3249  +#endif
         3250  +/* end TH_ENABLE_OB */

Changes to src/th.h.

            1  +#include "config.h"
            2  +
            3  +/*
            4  +** TH_ENABLE_QUERY, if defined, enables the "query" family of functions.
            5  +** They provide SELECT-only access to the repository db.
            6  +*/
            7  +#define TH_ENABLE_QUERY
            8  +
            9  +/*
           10  +** TH_ENABLE_OB, if defined, enables the "ob" family of functions.
           11  +** They are functionally similar to PHP's ob_start(), ob_end(), etc.
           12  +** family of functions, providing output capturing/buffering.
           13  +*/
           14  +#define TH_ENABLE_OB
           15  +
           16  +/*
           17  +** TH_ENABLE_ARGV, if defined, enables the "argv" family of functions.
           18  +** They provide access to CLI arguments as well as GET/POST arguments.
           19  +** They do not provide access to POST data submitted in JSON mode.
           20  +*/
           21  +#define TH_ENABLE_ARGV
           22  +
           23  +#ifdef TH_ENABLE_OB
           24  +#ifndef INTERFACE
           25  +#include "blob.h" /* maintenance reminder: also pulls in fossil_realloc() and friends */
           26  +#endif
           27  +#endif
     1     28   
     2     29   /* This header file defines the external interface to the custom Scripting
     3     30   ** Language (TH) interpreter.  TH is very similar to TCL but is not an
     4     31   ** exact clone.
     5     32   */
     6     33   
           34  +/*
           35  +** Th_Output_f() specifies a generic output routine for use by
           36  +** Th_Vtab_OutputMethods and friends. Its first argument is the data to
           37  +** write, the second is the number of bytes to write, and the 3rd is
           38  +** an implementation-specific state pointer (may be NULL, depending on
           39  +** the implementation). The return value is the number of bytes output
           40  +** (which may differ from len due to encoding and whatnot).  On error
           41  +** a negative value must be returned.
           42  +*/
           43  +typedef int (*Th_Output_f)( char const * zData, int len, void * pState );
           44  +
           45  +/*
           46  +** This structure defines the output state associated with a
           47  +** Th_Vtab. It is intended that a given Vtab be able to swap out
           48  +** output back-ends during its lifetime, e.g. to form a stack of
           49  +** buffers.
           50  +*/
           51  +struct Th_Vtab_OutputMethods {
           52  +  Th_Output_f xWrite;   /* output handler */
           53  +    void (*xDispose)( void * pState ); /* Called when the framework is done with
           54  +                                         this output handler,passed this object's
           55  +                                         pState pointer.. */
           56  +  void * pState;   /* final argument for xWrite() and xDispose()*/
           57  +  char enabled;    /* if 0, Th_Output() does nothing. */
           58  +};
           59  +typedef struct Th_Vtab_OutputMethods Th_Vtab_OutputMethods;
           60  +
           61  +/*
           62  +** Shared Th_Vtab_OutputMethods instance used for copy-initialization. This
           63  +** implementation uses Th_Output_f_FILE as its write() impl and
           64  +** Th_Output_dispose_FILE() for cleanup. If its pState member is NULL
           65  +** it outputs to stdout, else pState must be a (FILE*) which it will
           66  +** output to.
           67  +*/
           68  +extern const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE;
           69  +
     7     70   /*
     8     71   ** Before creating an interpreter, the application must allocate and
     9     72   ** populate an instance of the following structure. It must remain valid
    10     73   ** for the lifetime of the interpreter.
    11     74   */
    12     75   struct Th_Vtab {
    13         -  void *(*xMalloc)(unsigned int);
    14         -  void (*xFree)(void *);
           76  +  void *(*xRealloc)(void *, unsigned int); /**
           77  +                                           Re/deallocation routine. Must behave like
           78  +                                           realloc(3), with the minor extension that
           79  +                                           realloc(anything,positiveValue) _must_ return
           80  +                                           NULL on allocation error. The Standard's wording
           81  +                                           allows realloc() to return "some value suitable for
           82  +                                           passing to free()" on error, but because client code
           83  +                                           has no way of knowing if any non-NULL value is an error
           84  +                                           value, no sane realloc() implementation would/should
           85  +                                           return anything _but_ NULL on allocation error.
           86  +                                           */
           87  +  Th_Vtab_OutputMethods out;                      /** Output handler. TH functions which generate
           88  +                                               output should send it here (via Th_Output()).
           89  +                                           */
    15     90   };
    16     91   typedef struct Th_Vtab Th_Vtab;
           92  +
    17     93   
    18     94   /*
    19     95   ** Opaque handle for interpeter.
    20     96   */
    21     97   typedef struct Th_Interp Th_Interp;
           98  +
    22     99   
    23    100   /* 
    24         -** Create and delete interpreters. 
          101  +** Creates a new interpreter instance using the given v-table. pVtab
          102  +** must outlive the returned object, and pVtab->out.xDispose() will be
          103  +** called when the interpreter is cleaned up. The optional "ob" API
          104  +** swaps out Vtab::out instances, so pVtab->out might not be active
          105  +** for the entire lifetime of the interpreter.
          106  +**
          107  +** Potential TODO: we "should probably" add a dispose() method to the
          108  +** Th_Vtab interface.
    25    109   */
    26    110   Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
    27         -void Th_DeleteInterp(Th_Interp *);
          111  +
          112  +/*
          113  +** Frees up all resources associated with interp then frees interp.
          114  +*/
          115  +void Th_DeleteInterp(Th_Interp *interp);
    28    116   
    29    117   /* 
    30    118   ** Evaluate an TH program in the stack frame identified by parameter
    31    119   ** iFrame, according to the following rules:
    32    120   **
    33    121   **   * If iFrame is 0, this means the current frame.
    34    122   **
................................................................................
    52    140   ** begins with "::", the lookup is in the top level (global) frame. 
    53    141   */
    54    142   int Th_GetVar(Th_Interp *, const char *, int);
    55    143   int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
    56    144   int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
    57    145   int Th_UnsetVar(Th_Interp *, const char *, int);
    58    146   
    59         -typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
          147  +/*
          148  +** Typedef for Th interpreter callbacks, i.e. script-bound native C
          149  +** functions.
          150  +**
          151  +** The interp argument is the interpreter running the function. pState
          152  +** is arbitrary state which is passed to Th_CreateCommand(). arg
          153  +** contains the number of arguments (argument #0 is the command's
          154  +** name, in the same way that main()'s argv[0] is the binary's
          155  +** name). argv is the list of arguments. argl is an array argc items
          156  +** long which contains the length of each argument in the
          157  +** list. e.g. argv[0] is argl[0] bytes long.
          158  +*/
          159  +typedef int (*Th_CommandProc)(Th_Interp * interp, void * pState, int argc, const char ** argv, int * argl);
    60    160   
    61    161   /* 
    62         -** Register new commands. 
          162  +** Registers a new command with interp. zName must be a NUL-terminated
          163  +** name for the function. xProc is the native implementation of the
          164  +** function.  pContext is arbitrary data to pass as xProc()'s 2nd
          165  +** argument. xDel is an optional finalizer which should be called when
          166  +** interpreter is finalized. If xDel is not NULL then it is passed
          167  +** (interp,pContext) when interp is finalized.
          168  +**
          169  +** Return TH_OK on success.
    63    170   */
    64    171   int Th_CreateCommand(
    65    172     Th_Interp *interp, 
    66    173     const char *zName, 
    67         -  /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
    68    174     Th_CommandProc xProc,
    69    175     void *pContext,
    70    176     void (*xDel)(Th_Interp *, void *)
    71    177   );
    72    178   
    73    179   /* 
    74    180   ** Delete or rename commands.
................................................................................
   118    224   
   119    225   /* 
   120    226   ** Access the memory management functions associated with the specified
   121    227   ** interpreter.
   122    228   */
   123    229   void *Th_Malloc(Th_Interp *, int);
   124    230   void Th_Free(Th_Interp *, void *);
   125         -
          231  +void *Th_Realloc(Th_Interp *, void *, int);
   126    232   /* 
   127    233   ** Functions for handling TH lists.
   128    234   */
   129    235   int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
   130    236   int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *);
   131    237   
   132    238   int Th_StringAppend(Th_Interp *, char **, int *, const char *, int);
................................................................................
   134    240   /* 
   135    241   ** Functions for handling numbers and pointers.
   136    242   */
   137    243   int Th_ToInt(Th_Interp *, const char *, int, int *);
   138    244   int Th_ToDouble(Th_Interp *, const char *, int, double *);
   139    245   int Th_SetResultInt(Th_Interp *, int);
   140    246   int Th_SetResultDouble(Th_Interp *, double);
          247  +int Th_TryInt(Th_Interp *, const char * zArg, int nArg, int * piOut);
          248  +int Th_TryDouble(Th_Interp *, const char * zArg, int nArg, double * pfOut);
   141    249   
   142    250   /*
   143    251   ** Drop in replacements for the corresponding standard library functions.
   144    252   */
   145    253   int th_strlen(const char *);
   146    254   int th_isdigit(char);
   147    255   int th_isspace(char);
................................................................................
   149    257   int th_isspecial(char);
   150    258   char *th_strdup(Th_Interp *interp, const char *z, int n);
   151    259   
   152    260   /*
   153    261   ** Interfaces to register the language extensions.
   154    262   */
   155    263   int th_register_language(Th_Interp *interp);            /* th_lang.c */
   156         -int th_register_sqlite(Th_Interp *interp);              /* th_sqlite.c */
   157    264   int th_register_vfs(Th_Interp *interp);                 /* th_vfs.c */
   158    265   int th_register_testvfs(Th_Interp *interp);             /* th_testvfs.c */
          266  +
          267  +/*
          268  +** Registers the TCL extensions. Only available if FOSSIL_ENABLE_TCL
          269  +** is enabled at compile-time.
          270  +*/
   159    271   int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */
          272  +
          273  +#ifdef TH_ENABLE_ARGV
          274  +/*
          275  +** Registers the "argv" API. See www/th1_argv.wiki.
          276  +*/
          277  +int th_register_argv(Th_Interp *interp);                /* th_main.c */
          278  +#endif
          279  +
          280  +#ifdef TH_ENABLE_QUERY
          281  +/*
          282  +** Registers the "query" API. See www/th1_query.wiki.
          283  +*/
          284  +int th_register_query(Th_Interp *interp);              /* th_main.c */
          285  +#endif
          286  +#ifdef TH_ENABLE_OB
          287  +/*
          288  +** Registers the "ob" API. See www/th1_ob.wiki.
          289  +*/
          290  +int th_register_ob(Th_Interp * interp);                 /* th.c */
          291  +#endif
   160    292   
   161    293   /*
   162    294   ** General purpose hash table from th_lang.c.
   163    295   */
   164    296   typedef struct Th_Hash      Th_Hash;
   165    297   typedef struct Th_HashEntry Th_HashEntry;
   166    298   struct Th_HashEntry {
................................................................................
   173    305   void Th_HashDelete(Th_Interp *, Th_Hash *);
   174    306   void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
   175    307   Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
   176    308   
   177    309   /*
   178    310   ** Useful functions from th_lang.c.
   179    311   */
          312  +
          313  +/*
          314  +** Generic "wrong number of arguments" helper which sets the error
          315  +** state of interp to the given message plus a generic prefix.
          316  +*/
   180    317   int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
   181    318   
          319  +/*
          320  +** Works like Th_WrongNumArgs() but expects (zCmdName,zCmdLen) to be
          321  +** the current command's (name,length), i.e. (argv[0],argl[0]).
          322  +*/
          323  +int Th_WrongNumArgs2(Th_Interp *interp, const char *zCmdName,
          324  +                     int zCmdLen, const char *zMsg);
          325  +
   182    326   typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
   183    327   int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
          328  +
          329  +/*
          330  +** Works similarly to Th_CallSubCommand() but adjusts argc/argv/argl
          331  +** by 1 before passing on the call to the subcommand. This allows them
          332  +** to function the same whether they are called as top-level commands
          333  +** or as sub-sub-commands.
          334  +*/
          335  +int Th_CallSubCommand2(Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl, Th_SubCommand *aSub);
          336  +
          337  +/*
          338  +** Sends the given data through vTab->out.f() if vTab->out.enabled is
          339  +** true, otherwise this is a no-op. Returns 0 or higher on success, *
          340  +** a negative value if vTab->out.f is NULL.
          341  +*/
          342  +int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int len );
          343  +
          344  +/*
          345  +** Sends the given output through pInterp's vtab's output
          346  +** implementation. See Th_Vtab_OutputMethods() for the argument and
          347  +** return value semantics.
          348  +*/
          349  +int Th_Output( Th_Interp *pInterp, char const * zData, int len );
          350  +
          351  +/*
          352  +** Enables or disables output of the current Vtab API, depending on
          353  +** whether flag is true (non-0) or false (0). Note that when output
          354  +** buffering/stacking is enabled (e.g. via the "ob" API) this modifies
          355  +** only the current output mechanism, and not any further down the
          356  +** stack.
          357  +*/
          358  +void Th_OutputEnable( Th_Interp *pInterp, char flag );
          359  +
          360  +/*
          361  +** Returns true if output is enabled for the current output mechanism
          362  +** of pInterp, else false. See Th_OutputEnable().
          363  +*/
          364  +char Th_OutputEnabled( Th_Interp *pInterp );
          365  +
          366  +
          367  +
          368  +/*
          369  +** A Th_Output_f() implementation which sends its output to either
          370  +** pState (which must be NULL or a (FILE*)) or stdout (if pState is
          371  +** NULL).
          372  +*/
          373  +int Th_Output_f_FILE( char const * zData, int len, void * pState );
          374  +
          375  +/*
          376  +** A Th_Vtab_OutputMethods::xDispose() impl for FILE handles. If pState is not
          377  +** one of the standard streams (stdin, stdout, stderr) then it is
          378  +** fclose()d by this call.
          379  +*/
          380  +void Th_Output_dispose_FILE( void * pState );
          381  +
          382  +/*
          383  +** A helper type for holding lists of function registration information.
          384  +** For use with Th_RegisterCommands().
          385  +*/
          386  +struct Th_Command_Reg {
          387  +  const char *zName;     /* Function name. */
          388  +  Th_CommandProc xProc;  /* Callback function */
          389  +  void *pContext;        /* Arbitrary data for the callback. */
          390  +};
          391  +typedef struct Th_Command_Reg Th_Command_Reg;
          392  +
          393  +/*
          394  +** Th_Render_Flags_XXX are flags for Th_Render().
          395  +*/
          396  +/* makeheaders cannot do enums: enum Th_Render_Flags {...};*/
          397  +/*
          398  +** Default flags ("compatibility mode").
          399  +*/
          400  +#define Th_Render_Flags_DEFAULT 0
          401  +/*
          402  +** If set, Th_Render() will not process $var and $<var>
          403  +** variable references outside of TH1 blocks.
          404  +*/
          405  +#define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)
          406  +
          407  +/*
          408  +** Runs the given th1 program through Fossil's th1 interpreter. Flags
          409  +** may contain a bitmask made up of any of the Th_Render_Flags_XXX
          410  +** values.
          411  +*/
          412  +int Th_Render(const char *zTh1Program, int Th_Render_Flags);
          413  +
          414  +/*
          415  +** Adds a piece of memory to the given interpreter, such that:
          416  +**
          417  +** a) it will be cleaned up when the interpreter is destroyed, by
          418  +** calling finalizer(interp, pData). The finalizer may be NULL.
          419  +** Cleanup happens in an unspecified/unpredictable order.
          420  +**
          421  +** b) it can be fetched via Th_GetData().
          422  +**
          423  +** If a given key is added more than once then any previous
          424  +** entry is cleaned up before adding it.
          425  +**
          426  +** Returns 0 on success, non-0 on allocation error.
          427  +*/
          428  +int Th_SetData( Th_Interp * interp, char const * key,
          429  +                 void * pData,
          430  +                 void (*finalizer)( Th_Interp *, void * ) );
          431  +
          432  +/*
          433  +** Fetches data added via Th_SetData(), or NULL if no data
          434  +** has been associated with the given key.
          435  +*/
          436  +void * Th_GetData( Th_Interp * interp, char const * key );
          437  +
          438  +
          439  +/*
          440  +** Registers a list of commands with the interpreter. pList must be a non-NULL
          441  +** pointer to an array of Th_Command_Reg objects, the last one of which MUST
          442  +** have a NULL zName field (that is the end-of-list marker).
          443  +** Returns TH_OK on success, "something else" on error.
          444  +*/
          445  +int Th_RegisterCommands( Th_Interp * interp, Th_Command_Reg const * pList );
          446  +
          447  +#ifdef TH_ENABLE_OB
          448  +/*
          449  +** Output buffer stack manager for TH. Used/managed by the Th_ob_xxx()
          450  +** functions. This class manages Th_Interp::pVtab->out for a specific
          451  +** interpreter, swapping it in and out in order to redirect output
          452  +** generated via Th_Output() to internal buffers. The buffers can be
          453  +** pushed and popped from the stack, allowing clients to selectively
          454  +** capture output for a given block of TH1 code.
          455  +*/
          456  +struct Th_Ob_Manager {
          457  +  Blob ** aBuf;        /* Stack of Blobs */
          458  +  int nBuf;            /* Number of blobs */
          459  +  int cursor;          /* Current level (-1=not active) */
          460  +  Th_Interp * interp;  /* The associated interpreter */
          461  +  Th_Vtab_OutputMethods * aOutput
          462  +                       /* Stack of output routines corresponding
          463  +                          to the current buffering level.
          464  +                          Has nBuf entries.
          465  +                       */;
          466  +};
          467  +
          468  +/*
          469  +** Manager of a stack of Th_Vtab_Output objects for output buffering.
          470  +** It gets its name ("ob") from the similarly-named PHP functionality.
          471  +**
          472  +** See Th_Ob_GetManager().
          473  +**
          474  +** Potential TODO: remove the Blob from the interface and replace it
          475  +** with a Th_Output_f (or similar) which clients can pass in to have
          476  +** the data transfered from Th_Ob_Manager to them. We would also need to
          477  +** add APIs for clearing the buffer.
          478  +*/
          479  +typedef struct Th_Ob_Manager Th_Ob_Manager;
          480  +
          481  +/*
          482  +** Returns the ob manager for the given interpreter. The manager gets
          483  +** installed by the th_register_ob(). In Fossil ob support is
          484  +** installed automatically if it is available at built time.
          485  +*/
          486  +Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *ignored);
          487  +
          488  +/*
          489  +** Returns the top-most Blob in pMan's stack, or NULL if buffering is
          490  +** not active or if the current buffering level does not refer to a
          491  +** blob. (Note: the latter will never currently be the case, but may
          492  +** be if the API is expanded to offer other output direction options,
          493  +** e.g.  (ob start file /tmp/foo.out).)
          494  +*/
          495  +Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan );
          496  +
          497  +/*
          498  +** Pushes a new blob onto pMan's stack. On success returns TH_OK and
          499  +** assigns *pOut (if pOut is not NULL) to the new blob (which is owned
          500  +** by pMan). On error pOut is not modified and non-0 is returned. The
          501  +** new blob can be cleaned up via Th_Ob_Pop() or Th_Ob_PopAndFree()
          502  +** (please read both to understand the difference!).
          503  +*/
          504  +int Th_Ob_Push( Th_Ob_Manager * pMan, Th_Vtab_OutputMethods const * pWriter, Blob ** pOut );
          505  +
          506  +/*
          507  +** Pops the top-most output buffer off the stack and returns
          508  +** it. Returns NULL if there is no current buffer. When the last
          509  +** buffer is popped, pMan's internals are cleaned up (but pMan is not
          510  +** freed).
          511  +**
          512  +** The caller owns the returned object and must eventually clean it up
          513  +** by first passing it to blob_reset() and then Th_Free() it.
          514  +**
          515  +** See also: Th_Ob_PopAndFree().
          516  +*/
          517  +Blob * Th_Ob_Pop( Th_Ob_Manager * pMan );
          518  +
          519  +/*
          520  +** Convenience form of Th_Ob_Pop() which pops and frees the
          521  +** top-most buffer. Returns 0 on success, non-0 if there is no
          522  +** stack to pop. Thus is can be used in a loop like:
          523  +**
          524  +** while( !Th_Ob_PopAndFree(theManager) ) {}
          525  +*/
          526  +int Th_Ob_PopAndFree( Th_Ob_Manager * pMan );
          527  +
          528  +#endif
          529  +/* end TH_ENABLE_OB */

Changes to src/th_lang.c.

    14     14   #include <string.h>
    15     15   #include <assert.h>
    16     16   
    17     17   int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){
    18     18     Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1);
    19     19     return TH_ERROR;
    20     20   }
           21  +
           22  +int Th_WrongNumArgs2(Th_Interp *interp, const char *zCmdName,
           23  +                     int zCmdLen, const char *zMsg){
           24  +  char * zBuf = 0;
           25  +  int nBuf = 0;
           26  +  Th_StringAppend(interp, &zBuf, &nBuf, zCmdName, zCmdLen);
           27  +  Th_StringAppend(interp, &zBuf, &nBuf, ": wrong # args: expecting: ", -1);
           28  +  Th_StringAppend(interp, &zBuf, &nBuf, zMsg, -1);
           29  +  Th_StringAppend(interp, &zBuf, &nBuf, "", 1);
           30  +  Th_ErrorMessage(interp, zBuf, NULL, 0);
           31  +  Th_Free(interp, zBuf);
           32  +  return TH_ERROR;
           33  +}
    21     34   
    22     35   /*
    23     36   ** Syntax: 
    24     37   **
    25     38   **   catch script ?varname?
    26     39   */
    27     40   static int catch_command(
................................................................................
   456    469         argl[2];     /* Space for copies of parameter names and default values */
   457    470     p = (ProcDefn *)Th_Malloc(interp, nByte);
   458    471   
   459    472     /* If the last parameter in the parameter list is "args", then set the
   460    473     ** ProcDefn.hasArgs flag. The "args" parameter does not require an
   461    474     ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
   462    475     */
   463         -  if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){
   464         -    p->hasArgs = 1;
   465         -    nParam--;
          476  +  if(nParam>0){
          477  +    if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){
          478  +      p->hasArgs = 1;
          479  +      nParam--;
          480  +    }
   466    481     }
   467    482   
   468    483     p->nParam    = nParam;
   469    484     p->azParam   = (char **)&p[1];
   470    485     p->anParam   = (int *)&p->azParam[nParam];
   471    486     p->azDefault = (char **)&p->anParam[nParam];
   472    487     p->anDefault = (int *)&p->azDefault[nParam];
................................................................................
   868    883         return aSub[i].xProc(interp, ctx, argc, argv, argl);
   869    884       }
   870    885     }
   871    886   
   872    887     Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]);
   873    888     return TH_ERROR;
   874    889   }
          890  +
          891  +int Th_CallSubCommand2(
          892  +  Th_Interp *interp, 
          893  +  void *ctx,
          894  +  int argc,
          895  +  const char **argv,
          896  +  int *argl,
          897  +  Th_SubCommand *aSub
          898  +){
          899  +  int i;
          900  +  for(i=0; aSub[i].zName; i++){
          901  +    char const *zName = aSub[i].zName;
          902  +    if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){
          903  +      return aSub[i].xProc(interp, ctx, argc-1, argv+1, argl+1);
          904  +    }
          905  +  }
          906  +  Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]);
          907  +  return TH_ERROR;
          908  +}
          909  +
   875    910   
   876    911   /*
   877    912   ** TH Syntax:
   878    913   **
   879    914   **   string compare STR1 STR2
   880    915   **   string first   NEEDLE HAYSTACK ?STARTINDEX?
   881    916   **   string is      CLASS STRING
................................................................................
  1026   1061   }
  1027   1062   
  1028   1063   /*
  1029   1064   ** Register the built-in th1 language commands with interpreter interp.
  1030   1065   ** Usually this is called soon after interpreter creation.
  1031   1066   */
  1032   1067   int th_register_language(Th_Interp *interp){
         1068  +  int rc;
  1033   1069     /* Array of built-in commands. */
  1034         -  struct _Command {
  1035         -    const char *zName;
  1036         -    Th_CommandProc xProc;
  1037         -    void *pContext;
  1038         -  } aCommand[] = {
         1070  +  struct Th_Command_Reg aCommand[] = {
  1039   1071       {"catch",    catch_command,   0},
  1040   1072       {"expr",     expr_command,    0},
  1041   1073       {"for",      for_command,     0},
  1042   1074       {"if",       if_command,      0},
  1043   1075       {"info",     info_command,    0},
  1044   1076       {"lindex",   lindex_command,  0},
  1045   1077       {"list",     list_command,    0},
................................................................................
  1057   1089       {"return",   return_command, 0},
  1058   1090       {"break",    simple_command, (void *)TH_BREAK}, 
  1059   1091       {"continue", simple_command, (void *)TH_CONTINUE}, 
  1060   1092       {"error",    simple_command, (void *)TH_ERROR}, 
  1061   1093   
  1062   1094       {0, 0, 0}
  1063   1095     };
  1064         -  int i;
  1065         -
  1066         -  /* Add the language commands. */
  1067         -  for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
  1068         -    void *ctx;
  1069         -    if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
  1070         -    ctx = aCommand[i].pContext;
  1071         -    Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
  1072         -  }
  1073         -
  1074         -  return TH_OK;
         1096  +  rc = Th_RegisterCommands(interp, aCommand);
         1097  +  return rc;
  1075   1098   }

Changes to src/th_main.c.

    17     17   **
    18     18   ** This file contains an interface between the TH scripting language
    19     19   ** (an independent project) and fossil.
    20     20   */
    21     21   #include "config.h"
    22     22   #include "th_main.h"
    23     23   
           24  +#ifdef TH_ENABLE_QUERY
           25  +#ifndef INTERFACE
           26  +#include "sqlite3.h"
           27  +#endif
           28  +#endif
           29  +
    24     30   /*
    25     31   ** Global variable counting the number of outstanding calls to malloc()
    26     32   ** made by the th1 implementation. This is used to catch memory leaks
    27     33   ** in the interpreter. Obviously, it also means th1 is not threadsafe.
    28     34   */
    29     35   static int nOutstandingMalloc = 0;
    30     36   
................................................................................
    38     44     }
    39     45     return p;
    40     46   }
    41     47   static void xFree(void *p){
    42     48     if( p ){
    43     49       nOutstandingMalloc--;
    44     50     }
    45         -  free(p);
           51  +  fossil_free(p);
    46     52   }
    47         -static Th_Vtab vtab = { xMalloc, xFree };
           53  +
           54  +/*
           55  +** Default Th_Vtab::xRealloc() implementation.
           56  +*/
           57  +static void *xRealloc(void * p, unsigned int n){
           58  +  assert(n>=0 && "Invalid memory (re/de)allocation size.");
           59  +  if(0 == n){
           60  +    xFree(p);
           61  +    return NULL;
           62  +  }else if(NULL == p){
           63  +    return xMalloc(n);
           64  +  }else{
           65  +    return fossil_realloc(p, n)
           66  +      /* In theory nOutstandingMalloc doesn't need to be updated here
           67  +         unless xRealloc() is sorely misused.
           68  +      */;
           69  +  }
           70  +}
    48     71   
    49     72   /*
    50     73   ** Generate a TH1 trace message if debugging is enabled.
    51     74   */
    52     75   void Th_Trace(const char *zFormat, ...){
    53     76     va_list ap;
    54     77     va_start(ap, zFormat);
................................................................................
    55     78     blob_vappendf(&g.thLog, zFormat, ap);
    56     79     va_end(ap);
    57     80   }
    58     81   
    59     82   
    60     83   /*
    61     84   ** True if output is enabled.  False if disabled.
           85  +**
           86  +** We "could" replace this with Th_OutputEnable() and friends, but
           87  +** there is a functional difference: this particular flag prohibits
           88  +** some extra escaping which would happen (but be discared, unused) if
           89  +** relied solely on that API. Also, because that API only works on the
           90  +** current Vtab_Output handler, relying soly on that handling would
           91  +** introduce incompatible behaviour with the historical enable_output
           92  +** command.
    62     93   */
    63     94   static int enableOutput = 1;
    64     95   
    65     96   /*
    66     97   ** TH command:     enable_output BOOLEAN
    67     98   **
    68     99   ** Enable or disable the puts and hputs commands.
................................................................................
    71    102     Th_Interp *interp, 
    72    103     void *p, 
    73    104     int argc, 
    74    105     const char **argv, 
    75    106     int *argl
    76    107   ){
    77    108     if( argc!=2 ){
    78         -    return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
          109  +    return Th_WrongNumArgs2(interp,
          110  +                            argv[0], argl[0],
          111  +                           "BOOLEAN");
          112  +  }else{
          113  +    int rc = Th_ToInt(interp, argv[1], argl[1], &enableOutput);
          114  +    return rc;
    79    115     }
    80         -  return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
    81    116   }
          117  +
          118  +/*
          119  +** Th_Output_f() impl which sends all output to cgi_append_content().
          120  +*/
          121  +static int Th_Output_f_cgi_content( char const * zData, int nData, void * pState ){
          122  +  cgi_append_content(zData, nData);
          123  +  return nData;
          124  +}
          125  +
    82    126   
    83    127   /*
    84    128   ** Send text to the appropriate output:  Either to the console
    85    129   ** or to the CGI reply buffer.
    86    130   */
    87         -static void sendText(const char *z, int n, int encode){
          131  +static void sendText(Th_Interp *pInterp, const char *z, int n, int encode){
          132  +  if(NULL == pInterp){
          133  +    pInterp = g.interp;
          134  +  }
          135  +  assert( NULL != pInterp );
    88    136     if( enableOutput && n ){
    89    137       if( n<0 ) n = strlen(z);
    90    138       if( encode ){
    91    139         z = htmlize(z, n);
    92    140         n = strlen(z);
    93    141       }
    94         -    if( g.cgiOutput ){
    95         -      cgi_append_content(z, n);
    96         -    }else{
    97         -      fwrite(z, 1, n, stdout);
    98         -      fflush(stdout);
    99         -    }
   100         -    if( encode ) free((char*)z);
          142  +    Th_Output( pInterp, z, n );
          143  +    if( encode ) fossil_free((char*)z);
   101    144     }
   102    145   }
   103    146   
          147  +/*
          148  +** Internal state for the putsCmd() function, allowing it to be used
          149  +** as the basis for multiple implementations with slightly different
          150  +** behaviours based on the context. An instance of this type must be
          151  +** set as the Context parameter for any putsCmd()-based script command
          152  +** binding.
          153  +*/
          154  +struct PutsCmdData {
          155  +  char escapeHtml;    /* If true, htmlize all output. */
          156  +  char const * sep;   /* Optional NUL-terminated separator to output
          157  +                         between arguments. May be NULL. */
          158  +  char const * eol;   /* Optional NUL-terminated end-of-line separator,
          159  +                         output after the final argument. May be NULL. */
          160  +};
          161  +typedef struct PutsCmdData PutsCmdData;
          162  +
   104    163   /*
   105    164   ** TH command:     puts STRING
   106    165   ** TH command:     html STRING
   107    166   **
   108         -** Output STRING as HTML (html) or unchanged (puts).  
          167  +** Output STRING as HTML (html) or unchanged (puts).
          168  +**
          169  +** pConvert MUST be a (PutsCmdData [const]*). It is not modified by
          170  +** this function.
   109    171   */
   110    172   static int putsCmd(
   111    173     Th_Interp *interp, 
   112    174     void *pConvert, 
   113    175     int argc, 
   114    176     const char **argv, 
   115    177     int *argl
   116    178   ){
   117         -  if( argc!=2 ){
   118         -    return Th_WrongNumArgs(interp, "puts STRING");
          179  +  PutsCmdData const * fmt = (PutsCmdData const *)pConvert;
          180  +  const int sepLen = fmt->sep ? strlen(fmt->sep) : 0;
          181  +  int i;
          182  +  if( argc<2 ){
          183  +    return Th_WrongNumArgs2(interp,
          184  +                            argv[0], argl[0],
          185  +                            "STRING ...STRING_N");
   119    186     }
   120         -  sendText((char*)argv[1], argl[1], pConvert!=0);
          187  +  for( i = 1; i < argc; ++i ){
          188  +    if(sepLen && (i>1)){
          189  +      sendText(interp, fmt->sep, sepLen, 0);
          190  +    }
          191  +    sendText(interp, (char const*)argv[i], argl[i], fmt->escapeHtml);
          192  +  }
          193  +  if(fmt->eol){
          194  +    sendText(interp, fmt->eol, strlen(fmt->eol), 0);
          195  +  }
   121    196     return TH_OK;
   122    197   }
   123    198   
   124    199   /*
   125    200   ** TH command:      wiki STRING
   126    201   **
   127    202   ** Render the input string as wiki.
................................................................................
   130    205     Th_Interp *interp, 
   131    206     void *p, 
   132    207     int argc, 
   133    208     const char **argv, 
   134    209     int *argl
   135    210   ){
   136    211     if( argc!=2 ){
   137         -    return Th_WrongNumArgs(interp, "wiki STRING");
          212  +    return Th_WrongNumArgs2(interp,
          213  +                            argv[0], argl[0],
          214  +                            "STRING");
   138    215     }
   139    216     if( enableOutput ){
   140    217       Blob src;
   141    218       blob_init(&src, (char*)argv[1], argl[1]);
   142    219       wiki_convert(&src, 0, WIKI_INLINE);
   143    220       blob_reset(&src);
   144    221     }
................................................................................
   156    233     void *p, 
   157    234     int argc, 
   158    235     const char **argv, 
   159    236     int *argl
   160    237   ){
   161    238     char *zOut;
   162    239     if( argc!=2 ){
   163         -    return Th_WrongNumArgs(interp, "htmlize STRING");
          240  +    return Th_WrongNumArgs2(interp,
          241  +                            argv[0], argl[0],
          242  +                            "STRING");
   164    243     }
   165    244     zOut = htmlize((char*)argv[1], argl[1]);
   166    245     Th_SetResult(interp, zOut, -1);
   167    246     free(zOut);
   168    247     return TH_OK;
   169    248   }
   170    249   
          250  +#if 0
          251  +/* This is not yet needed, but something like it may become useful for
          252  +   custom page/command support, for rendering snippets/templates. */
          253  +/*
          254  +** TH command:      render STRING
          255  +**
          256  +** Render the input string as TH1.
          257  +*/
          258  +static int renderCmd(
          259  +  Th_Interp *interp, 
          260  +  void *p, 
          261  +  int argc, 
          262  +  const char **argv, 
          263  +  int *argl
          264  +){
          265  +  if( argc<2 ){
          266  +    return Th_WrongNumArgs2(interp,
          267  +                            argv[0], argl[0],
          268  +                            "STRING ?STRING...?");
          269  +  }else{
          270  +    Th_Ob_Manager * man = Th_Ob_GetManager(interp);
          271  +    Blob * b = NULL;
          272  +    Blob buf = empty_blob;
          273  +    int rc, i;
          274  +    /*FIXME: assert(NULL != man && man->interp==interp);*/
          275  +    man->interp = interp;
          276  +    /* Combine all inputs into one buffer so that we can use that to
          277  +       embed TH1 tags across argument boundaries.
          278  +
          279  +       FIX:E optimize away buf for the 1-arg case.
          280  +     */
          281  +    for( i = 1; TH_OK==rc && i < argc; ++i ){
          282  +      char const * str = argv[i];
          283  +      blob_append( &buf, str, argl[i] );
          284  +      /*rc = Th_Render( str, Th_Render_Flags_NO_DOLLAR_DEREF );*/
          285  +    }
          286  +    rc = Th_Ob_Push( man, &b );
          287  +    if(rc){
          288  +      blob_reset( &buf );
          289  +      return rc;
          290  +    }
          291  +    rc = Th_Render( buf.aData, Th_Render_Flags_DEFAULT );
          292  +    blob_reset(&buf);
          293  +    b = Th_Ob_Pop( man );
          294  +    if(TH_OK==rc){
          295  +      Th_SetResult( interp, b->aData, b->nUsed );
          296  +    }
          297  +    blob_reset( b );
          298  +    Th_Free( interp, b );
          299  +    return rc;
          300  +  }
          301  +}/* renderCmd() */
          302  +#endif
          303  +
   171    304   /*
   172    305   ** TH command:      date
   173    306   **
   174    307   ** Return a string which is the current time and date.  If the
   175    308   ** -local option is used, the date appears using localtime instead
   176    309   ** of UTC.
   177    310   */
................................................................................
   203    336     void *p, 
   204    337     int argc, 
   205    338     const char **argv, 
   206    339     int *argl
   207    340   ){
   208    341     int rc;
   209    342     if( argc!=2 ){
   210         -    return Th_WrongNumArgs(interp, "hascap STRING");
          343  +    return Th_WrongNumArgs2(interp,
          344  +                           argv[0], argl[0],
          345  +                           "STRING");
   211    346     }
   212    347     rc = login_has_capability((char*)argv[1],argl[1]);
   213    348     if( g.thTrace ){
   214    349       Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
   215    350     }
   216    351     Th_SetResultInt(interp, rc);
   217    352     return TH_OK;
................................................................................
   234    369     int argc, 
   235    370     const char **argv, 
   236    371     int *argl
   237    372   ){
   238    373     int rc = 0;
   239    374     char const * zArg;
   240    375     if( argc!=2 ){
   241         -    return Th_WrongNumArgs(interp, "hasfeature STRING");
          376  +    return Th_WrongNumArgs2(interp,
          377  +                            argv[0], argl[0],
          378  +                            "STRING");
   242    379     }
   243    380     zArg = (char const*)argv[1];
   244    381     if(NULL==zArg){
   245    382       /* placeholder for following ifdefs... */
   246    383     }
   247    384   #if defined(FOSSIL_ENABLE_JSON)
   248    385     else if( 0 == fossil_strnicmp( zArg, "json", 4 ) ){
................................................................................
   278    415     int argc, 
   279    416     const char **argv, 
   280    417     int *argl
   281    418   ){
   282    419     int rc = 0;
   283    420     int i;
   284    421     if( argc!=2 ){
   285         -    return Th_WrongNumArgs(interp, "anycap STRING");
          422  +    return Th_WrongNumArgs2(interp,
          423  +                            argv[0], argl[0],
          424  +                            "STRING");
   286    425     }
   287    426     for(i=0; rc==0 && i<argl[1]; i++){
   288    427       rc = login_has_capability((char*)&argv[1][i],1);
   289    428     }
   290    429     if( g.thTrace ){
   291    430       Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
   292    431     }
................................................................................
   308    447     Th_Interp *interp,
   309    448     void *p, 
   310    449     int argc, 
   311    450     const char **argv, 
   312    451     int *argl
   313    452   ){
   314    453     if( argc!=4 ){
   315         -    return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES");
          454  +    return Th_WrongNumArgs2(interp,
          455  +                            argv[0], argl[0],
          456  +                            "NAME TEXT-LIST NUMLINES");
   316    457     }
   317    458     if( enableOutput ){
   318    459       int height;
   319    460       Blob name;
   320    461       int nValue;
   321    462       const char *zValue;
   322    463       char *z, *zH;
................................................................................
   327    468   
   328    469       if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
   329    470       Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem);
   330    471       blob_init(&name, (char*)argv[1], argl[1]);
   331    472       zValue = Th_Fetch(blob_str(&name), &nValue);
   332    473       z = mprintf("<select name=\"%z\" size=\"%d\">", 
   333    474                    htmlize(blob_buffer(&name), blob_size(&name)), height);
   334         -    sendText(z, -1, 0);
          475  +    sendText(interp, z, -1, 0);
   335    476       free(z);
   336    477       blob_reset(&name);
   337    478       for(i=0; i<nElem; i++){
   338    479         zH = htmlize((char*)azElem[i], aszElem[i]);
   339    480         if( zValue && aszElem[i]==nValue 
   340    481                && memcmp(zValue, azElem[i], nValue)==0 ){
   341    482           z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
   342    483                        zH, zH);
   343    484         }else{
   344    485           z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
   345    486         }
   346    487         free(zH);
   347         -      sendText(z, -1, 0);
          488  +      sendText(interp, z, -1, 0);
   348    489         free(z);
   349    490       }
   350         -    sendText("</select>", -1, 0);
          491  +    sendText(interp, "</select>", -1, 0);
   351    492       Th_Free(interp, azElem);
   352    493     }
   353    494     return TH_OK;
   354    495   }
   355    496   
   356    497   /*
   357    498   ** TH1 command:     linecount STRING MAX MIN
................................................................................
   366    507     const char **argv, 
   367    508     int *argl
   368    509   ){
   369    510     const char *z;
   370    511     int size, n, i;
   371    512     int iMin, iMax;
   372    513     if( argc!=4 ){
   373         -    return Th_WrongNumArgs(interp, "linecount STRING MAX MIN");
          514  +    return Th_WrongNumArgs2(interp,
          515  +                            argv[0], argl[0],
          516  +                            "STRING MAX MIN");
   374    517     }
   375    518     if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
   376    519     if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
   377    520     z = argv[1];
   378    521     size = argl[1];
   379    522     for(n=1, i=0; i<size; i++){
   380    523       if( z[i]=='\n' ){
................................................................................
   401    544     int argc, 
   402    545     const char **argv, 
   403    546     int *argl
   404    547   ){
   405    548     int openRepository;
   406    549   
   407    550     if( argc!=1 && argc!=2 ){
   408         -    return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
          551  +    return Th_WrongNumArgs2(interp,
          552  +                            argv[0], argl[0],
          553  +                            "?BOOLEAN?");
   409    554     }
   410    555     if( argc==2 ){
   411    556       if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
   412    557         return TH_ERROR;
   413    558       }
   414    559       if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
   415    560     }
   416    561     Th_SetResult(interp, g.zRepositoryName, -1);
   417    562     return TH_OK;
   418    563   }
   419    564   
          565  +
          566  +#ifdef TH_ENABLE_ARGV
          567  +/*
          568  +** TH command:
          569  +**
          570  +** argv len
          571  +**
          572  +** Returns the number of command-line arguments.
          573  +*/
          574  +static int argvArgcCmd(
          575  +  Th_Interp *interp,
          576  +  void *p, 
          577  +  int argc, 
          578  +  const char **argv, 
          579  +  int *argl
          580  +){
          581  +  Th_SetResultInt( interp, g.argc );
          582  +  return TH_OK;
          583  +}
          584  +
          585  +
          586  +
          587  +/*
          588  +** TH command:
          589  +**
          590  +** argv at Index
          591  +**
          592  +** Returns the raw argument at the given index, throwing if
          593  +** out of bounds.
          594  +*/
          595  +static int argvGetAtCmd(
          596  +  Th_Interp *interp,
          597  +  void *p, 
          598  +  int argc, 
          599  +  const char **argv, 
          600  +  int *argl
          601  +){
          602  +  char const * zVal;
          603  +  int pos = 0;
          604  +  if( argc != 2 ){
          605  +    return Th_WrongNumArgs2(interp,
          606  +                            argv[0], argl[0],
          607  +                            "Index");
          608  +  }
          609  +  if( TH_OK != Th_ToInt(interp, argv[1], argl[1], &pos) ){
          610  +    return TH_ERROR;
          611  +  }
          612  +  if( pos < 0 || pos >= g.argc ){
          613  +    Th_ErrorMessage(interp, "Argument out of range:", argv[1], argl[1]);
          614  +    return TH_ERROR;
          615  +  }
          616  +  if( 0 == pos ){/*special case*/
          617  +    zVal = fossil_nameofexe();
          618  +  }else{
          619  +    zVal = (pos>0 && pos<g.argc) ? g.argv[pos] : 0;
          620  +  }
          621  +  Th_SetResult( interp, zVal, zVal ? strlen(zVal) : 0 );
          622  +  return TH_OK;  
          623  +}
          624  +
          625  +
          626  +/*
          627  +** TH command:
          628  +**
          629  +** argv getstr longName ??shortName? ?defaultValue??
          630  +**
          631  +** Functions more or less like Fossil's find_option().
          632  +** If the given argument is found then its value is returned,
          633  +** else defaultValue is returned. If that is not set
          634  +** and the option is not found, an error is thrown.
          635  +** If defaultValue is provided, shortName must also be provided
          636  +** but it may be empty. For example:
          637  +**
          638  +** set foo [argv getstr foo "" "hi, world"]
          639  +**
          640  +** ACHTUNG: find_option() removes any entries it finds from
          641  +** g.argv, such that future calls to find_option() will not
          642  +** find the same option.
          643  +*/
          644  +static int argvFindOptionStringCmd(
          645  +  Th_Interp *interp,
          646  +  void *p, 
          647  +  int argc, 
          648  +  const char **argv, 
          649  +  int *argl
          650  +){
          651  +  enum { BufLen = 100 };
          652  +  char zLong[BufLen] = {0};
          653  +  char zShort[BufLen] = {0};
          654  +  char aBuf[BufLen] = {0};
          655  +  int hasArg;
          656  +  char const * zVal = NULL;
          657  +  char const * zDefault = NULL;
          658  +  int check;
          659  +  if( 1 < argc ){
          660  +    assert( argl[1] < BufLen );
          661  +    check = snprintf( zLong, BufLen, "%s", argv[1] );
          662  +    assert( check <= BufLen );
          663  +  }
          664  +  if( (2 < argc) && (0 < argl[2]) ){
          665  +    assert( argl[2] < BufLen );
          666  +    check = snprintf( zShort, BufLen, "%s", argv[2] );
          667  +    assert( check <= BufLen );
          668  +  }
          669  +  if( 3 < argc){
          670  +    zDefault = argv[3];
          671  +  }
          672  +
          673  +  if(0 == zLong[0]){
          674  +    return Th_WrongNumArgs2(interp,
          675  +                           argv[0], argl[0],
          676  +                           "longName ?shortName? ?defaultVal?");
          677  +  }
          678  +  if(g.cgiOutput){
          679  +      zVal = cgi_parameter( zLong, NULL );
          680  +      if( !zVal && zShort[0] ){
          681  +          zVal = cgi_parameter( zShort, NULL );
          682  +      }
          683  +  }else{
          684  +      zVal = find_option( zLong, zShort[0] ? zShort : NULL, 1 );
          685  +  }
          686  +  if(!zVal){
          687  +    zVal = zDefault;
          688  +    if(!zVal){
          689  +      Th_ErrorMessage(interp, "Option not found and no default provided:", zLong, -1);
          690  +      return TH_ERROR;
          691  +    }
          692  +  }
          693  +  Th_SetResult( interp, zVal, zVal ? strlen(zVal) : 0 );
          694  +  return TH_OK;  
          695  +}
          696  +
          697  +/*
          698  +** TH command:
          699  +**
          700  +** argv getbool longName ??shortName? ?defaultValue??
          701  +**
          702  +** Works just like argv getstr but treats any empty value or one
          703  +** starting with the digit '0' as a boolean false.
          704  +**
          705  +** Returns the result as an integer 0 (false) or 1 (true).
          706  +*/
          707  +static int argvFindOptionBoolCmd(
          708  +  Th_Interp *interp,
          709  +  void *p, 
          710  +  int argc, 
          711  +  const char **argv, 
          712  +  int *argl
          713  +){
          714  +  /* FIXME: refactor to re-use the code from getstr */
          715  +  enum { BufLen = 100 };
          716  +  char zLong[BufLen] = {0};
          717  +  char zShort[BufLen] = {0};
          718  +  char aBuf[BufLen] = {0};
          719  +  int hasArg;
          720  +  char const * zVal = NULL;
          721  +  char const * zDefault = NULL;
          722  +  int val;
          723  +  int rc;
          724  +  int check;
          725  +  if( 1 < argc ){
          726  +    assert( argl[1] < BufLen );
          727  +    check = snprintf( zLong, BufLen, "%s", argv[1] );
          728  +    assert( check <= BufLen );
          729  +  }
          730  +  if( (2 < argc) && (0 < argl[2]) ){
          731  +    assert( argl[2] < BufLen );
          732  +    check = snprintf( zShort, BufLen, "%s", argv[2] );
          733  +    assert( check <= BufLen );
          734  +  }
          735  +  if( 3 < argc){
          736  +    zDefault = argv[3];
          737  +  }
          738  +
          739  +  if(0 == zLong[0]){
          740  +    return Th_WrongNumArgs2(interp,
          741  +                            argv[0], argl[0],
          742  +                           "longName ?shortName? ?defaultVal?");
          743  +  }
          744  +  if(g.cgiOutput){
          745  +      zVal = cgi_parameter( zLong, NULL );
          746  +      if( !zVal && zShort[0] ){
          747  +          zVal = cgi_parameter( zShort, NULL );
          748  +      }
          749  +  }else{
          750  +      zVal = find_option( zLong, zShort[0] ? zShort : NULL, 0 );
          751  +  }
          752  +  if(zVal && !*zVal){
          753  +    zVal = "1";
          754  +  }
          755  +  if(!zVal){
          756  +    zVal = zDefault;
          757  +    if(!zVal){
          758  +      Th_ErrorMessage(interp, "Option not found and no default provided:", zLong, -1);
          759  +      return TH_ERROR;
          760  +    }
          761  +  }
          762  +  if( !*zVal ){
          763  +    zVal = "0";
          764  +  }
          765  +  zVal = (zVal && *zVal && (*zVal!='0')) ? zVal : 0;
          766  +  Th_SetResultInt( interp, zVal ? 1 : 0 );
          767  +  return TH_OK;
          768  +}
          769  +
          770  +/*
          771  +** TH command:
          772  +**
          773  +** argv getint longName ?shortName? ?defaultValue?
          774  +**
          775  +** Works like argv getstr but returns the value as an integer
          776  +** (throwing an error if the argument cannot be converted).
          777  +*/
          778  +static int argvFindOptionIntCmd(
          779  +  Th_Interp *interp,
          780  +  void *p, 
          781  +  int argc, 
          782  +  const char **argv, 
          783  +  int *argl
          784  +){
          785  +  /* FIXME: refactor to re-use the code from getstr */
          786  +  enum { BufLen = 100 };
          787  +  char zLong[BufLen] = {0};
          788  +  char zShort[BufLen] = {0};
          789  +  char aBuf[BufLen] = {0};
          790  +  int hasArg;
          791  +  char const * zVal = NULL;
          792  +  char const * zDefault = NULL;
          793  +  int val = 0;
          794  +  int check;
          795  +  if( 1 < argc ){
          796  +    assert( argl[1] < BufLen );
          797  +    check = snprintf( zLong, BufLen, "%s", argv[1] );
          798  +    assert( check <= BufLen );
          799  +  }
          800  +  if( (2 < argc) && (0 < argl[2]) ){
          801  +    assert( argl[2] < BufLen );
          802  +    check = snprintf( zShort, BufLen, "%s", argv[2] );
          803  +    assert( check <= BufLen );
          804  +  }
          805  +  if( 3 < argc){
          806  +    zDefault = argv[3];
          807  +  }
          808  +
          809  +  if(0 == zLong[0]){
          810  +    return Th_WrongNumArgs2(interp,
          811  +                            argv[0], argl[0],
          812  +                           "longName ?shortName? ?defaultVal?");
          813  +  }
          814  +  if(g.cgiOutput){
          815  +      zVal = cgi_parameter( zLong, NULL );
          816  +      if( !zVal && zShort[0] ){
          817  +          zVal = cgi_parameter( zShort, NULL );
          818  +      }
          819  +  }else{
          820  +      zVal = find_option( zLong, zShort[0] ? zShort : NULL, 1 );
          821  +  }
          822  +  if(!zVal){
          823  +    zVal = zDefault;
          824  +    if(!zVal){
          825  +      Th_ErrorMessage(interp, "Option not found and no default provided:", zLong, -1);
          826  +      return TH_ERROR;
          827  +    }
          828  +  }
          829  +  Th_ToInt(interp, zVal, strlen(zVal), &val);
          830  +  Th_SetResultInt( interp, val );
          831  +  return TH_OK;  
          832  +}
          833  +
          834  +/*
          835  +** TH command:
          836  +**
          837  +** argv subcommand
          838  +**
          839  +** This is the top-level dispatching function.
          840  +*/
          841  +static int argvTopLevelCmd(
          842  +  Th_Interp *interp,
          843  +  void *ctx, 
          844  +  int argc, 
          845  +  const char **argv, 
          846  +  int *argl
          847  +){
          848  +  static Th_SubCommand aSub[] = {
          849  +    {"len",      argvArgcCmd},
          850  +    {"at",       argvGetAtCmd},
          851  +    {"getstr",   argvFindOptionStringCmd},
          852  +    {"string",   argvFindOptionStringCmd},
          853  +    {"getbool",  argvFindOptionBoolCmd},
          854  +    {"bool",     argvFindOptionBoolCmd},
          855  +    {"getint",   argvFindOptionIntCmd},
          856  +    {"int",      argvFindOptionIntCmd},
          857  +    {0, 0}
          858  +  };
          859  +  Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
          860  +}
          861  +
          862  +int th_register_argv(Th_Interp *interp){
          863  +  static Th_Command_Reg aCommand[] = {
          864  +    {"argv",            argvTopLevelCmd, 0 },
          865  +    {0, 0, 0}
          866  +  };
          867  +  Th_RegisterCommands( interp, aCommand );
          868  +}
          869  +
          870  +#endif
          871  +/* end TH_ENABLE_ARGV */
          872  +
          873  +#ifdef TH_ENABLE_QUERY
          874  +
          875  +/*
          876  +** Adds the given prepared statement to the interpreter. Returns the
          877  +** statement's opaque identifier (a positive value). Ownerships of
          878  +** pStmt is transfered to interp and it must be cleaned up by the
          879  +** client by calling Th_query_FinalizeStmt(), passing it the value returned
          880  +** by this function.
          881  +**
          882  +** If interp is destroyed before all statements are finalized,
          883  +** it will finalize them but may emit a warning message.
          884  +*/
          885  +static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
          886  +
          887  +
          888  +/*
          889  +** Internal state for the "query" API.
          890  +*/
          891  +struct Th_Query {
          892  +  sqlite3_stmt ** aStmt; /* Array of statement handles. */
          893  +  int nStmt;             /* number of entries in aStmt. */
          894  +  int colCmdIndex;       /* column index argument. Set by some top-level dispatchers
          895  +                            for their subcommands.
          896  +                         */
          897  +};
          898  +/*
          899  +** Internal key for use with Th_Data_Add().
          900  +*/
          901  +#define Th_Query_KEY "Th_Query"
          902  +typedef struct Th_Query Th_Query;
          903  +
          904  +/*
          905  +** Returns the Th_Query object associated with the given interpreter,
          906  +** or 0 if there is not one.
          907  +*/
          908  +static Th_Query * Th_query_manager( Th_Interp * interp ){
          909  +  void * p = Th_GetData( interp, Th_Query_KEY );
          910  +  return p ? (Th_Query*)p : NULL;
          911  +}
          912  +
          913  +static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
          914  +  Th_Query * sq = Th_query_manager(interp);
          915  +  int i, x;
          916  +  sqlite3_stmt * s;
          917  +  sqlite3_stmt ** list = sq->aStmt;
          918  +  for( i = 0; i < sq->nStmt; ++i ){
          919  +    s = list[i];
          920  +    if(NULL==s){
          921  +      list[i] = pStmt;
          922  +      return i+1;
          923  +    }
          924  +  }
          925  +  x = (sq->nStmt + 1) * 2;
          926  +  list = (sqlite3_stmt**)fossil_realloc( list, sizeof(sqlite3_stmt*)*x );
          927  +  for( i = sq->nStmt; i < x; ++i ){
          928  +    list[i] = NULL;
          929  +  }
          930  +  list[sq->nStmt] = pStmt;
          931  +  x = sq->nStmt;
          932  +  sq->nStmt = i;
          933  +  sq->aStmt = list;
          934  +  return x + 1;
          935  +}
          936  +
          937  +
          938  +/*
          939  +** Expects stmtId to be a statement identifier returned by
          940  +** Th_query_AddStmt(). On success, finalizes the statement and returns 0.
          941  +** On error (statement not found) non-0 is returned. After this
          942  +** call, some subsequent call to Th_query_AddStmt() may return the
          943  +** same statement ID.
          944  +*/
          945  +static int Th_query_FinalizeStmt(Th_Interp *interp, int stmtId){
          946  +  Th_Query * sq = Th_query_manager(interp);
          947  +  sqlite3_stmt * st;
          948  +  int rc = 0;
          949  +  assert( stmtId>0 && stmtId<=sq->nStmt );
          950  +  st = sq->aStmt[stmtId-1];
          951  +  if(NULL != st){
          952  +    sq->aStmt[stmtId-1] = NULL;
          953  +    sqlite3_finalize(st);
          954  +    return 0;
          955  +  }else{
          956  +    return 1;
          957  +  }
          958  +}
          959  +
          960  +/*
          961  +** Works like Th_query_FinalizeStmt() but takes a statement pointer, which
          962  +** must have been Th_query_AddStmt()'d to the given interpreter.
          963  +*/
          964  +static int Th_query_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt * pSt){
          965  +  Th_Query * sq = Th_query_manager(interp);
          966  +  int i = 0;
          967  +  sqlite3_stmt * st = NULL;
          968  +  int rc = 0;
          969  +  for( ; i < sq->nStmt; ++i ){
          970  +    st = sq->aStmt[i];
          971  +    if(st == pSt) break;
          972  +  }
          973  +  if( st == pSt ){
          974  +    assert( i>=0 && i<sq->nStmt );
          975  +    sq->aStmt[i] = NULL;
          976  +    sqlite3_finalize(st);
          977  +    return 0;
          978  +  }else{
          979  +    return 1;
          980  +  }
          981  +}
          982  +
          983  +
          984  +/*
          985  +** Fetches the statement with the given ID, as returned by
          986  +** Th_query_AddStmt(). Returns NULL if stmtId does not refer (or no longer
          987  +** refers) to a statement added via Th_query_AddStmt().
          988  +*/
          989  +static sqlite3_stmt * Th_query_GetStmt(Th_Interp *interp, int stmtId){
          990  +  Th_Query * sq = Th_query_manager(interp);
          991  +  return (!sq || (stmtId<1) || (stmtId > sq->nStmt))
          992  +    ? NULL
          993  +    : sq->aStmt[stmtId-1];
          994  +}
          995  +
          996  +
          997  +/*
          998  +** Th_GCEntry finalizer which requires that p be a (Th_Query*).
          999  +*/
         1000  +static void finalizerSqlite( Th_Interp * interp, void * p ){
         1001  +  Th_Query * sq = (Th_Query *)p;
         1002  +  int i;
         1003  +  sqlite3_stmt * st = NULL;
         1004  +  if(!sq) {
         1005  +    fossil_warning("Got a finalizer call for a NULL Th_Query.");
         1006  +    return;
         1007  +  }
         1008  +  for( i = 0; i < sq->nStmt; ++i ){
         1009  +    st = sq->aStmt[i];
         1010  +    if(NULL != st){
         1011  +      fossil_warning("Auto-finalizing unfinalized "
         1012  +                     "statement id #%d: %s",
         1013  +                     i+1, sqlite3_sql(st));
         1014  +      Th_query_FinalizeStmt( interp, i+1 );
         1015  +    }
         1016  +  }
         1017  +  Th_Free(interp, sq->aStmt);
         1018  +  Th_Free(interp, sq);
         1019  +}
         1020  +
         1021  +
         1022  +/*
         1023  +** TH command:
         1024  +**
         1025  +** query prepare SQL
         1026  +**
         1027  +** Returns an opaque statement identifier.
         1028  +*/
         1029  +static int queryPrepareCmd(
         1030  +  Th_Interp *interp,
         1031  +  void *p, 
         1032  +  int argc, 
         1033  +  const char **argv, 
         1034  +  int *argl
         1035  +){
         1036  +  char const * zSql;
         1037  +  sqlite3_stmt * pStmt = NULL;
         1038  +  int rc;
         1039  +  char const * errMsg = NULL;
         1040  +  if( argc!=2 ){
         1041  +    return Th_WrongNumArgs2(interp,
         1042  +                            argv[0], argl[0],
         1043  +                            "STRING");
         1044  +  }
         1045  +  zSql = argv[1];
         1046  +  rc = sqlite3_prepare( g.db, zSql, strlen(zSql), &pStmt, NULL );
         1047  +  if(SQLITE_OK==rc){
         1048  +    if(sqlite3_column_count( pStmt ) < 1){
         1049  +      errMsg = "Only SELECT-like queries are supported.";
         1050  +      rc = SQLITE_ERROR;
         1051  +      sqlite3_finalize( pStmt );
         1052  +      pStmt = NULL;
         1053  +    }
         1054  +  }else{
         1055  +    errMsg = sqlite3_errmsg( g.db );
         1056  +  }
         1057  +  if(SQLITE_OK!=rc){
         1058  +    assert(NULL != errMsg);
         1059  +    assert(NULL == pStmt);
         1060  +    Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
         1061  +    return TH_ERROR;
         1062  +  }
         1063  +  rc = Th_query_AddStmt( interp, pStmt );
         1064  +  assert( rc >= 0 && "AddStmt failed.");
         1065  +  Th_SetResultInt( interp, rc );
         1066  +  return TH_OK;
         1067  +}
         1068  +
         1069  +/*
         1070  +** Tries to convert arg, which must be argLen bytes long, to a
         1071  +** statement handle id and, in turn, to a sqlite3_stmt. On success
         1072  +** (the argument references a prepared statement) it returns the
         1073  +** handle and stmtId (if not NULL) is assigned to the integer value of
         1074  +** arg. On error NULL is returned and stmtId might be modified (if not
         1075  +** NULL). If stmtId is unmodified after an error then it is not a
         1076  +** number, else it is a number but does not reference an opened
         1077  +** statement.
         1078  +*/
         1079  +static sqlite3_stmt * queryStmtHandle(Th_Interp *interp, char const * arg, int argLen, int * stmtId ){
         1080  +  int rc = 0;
         1081  +  sqlite3_stmt * pStmt = NULL;
         1082  +  if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
         1083  +    if(stmtId){
         1084  +      *stmtId = rc;
         1085  +    }
         1086  +    pStmt = Th_query_GetStmt( interp, rc );
         1087  +    if(NULL==pStmt){
         1088  +      Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
         1089  +    }
         1090  +  }
         1091  +  return pStmt;
         1092  +
         1093  +}
         1094  +
         1095  +/*
         1096  +** TH command:
         1097  +**
         1098  +** query finalize stmtId
         1099  +** query stmtId finalize 
         1100  +**
         1101  +** sqlite3_finalize()s the given statement.
         1102  +*/
         1103  +static int queryFinalizeCmd(
         1104  +  Th_Interp *interp,
         1105  +  void *p, 
         1106  +  int argc, 
         1107  +  const char **argv, 
         1108  +  int *argl
         1109  +){
         1110  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1111  +  int requireArgc = pStmt ? 1 : 2;
         1112  +  char * zSql;
         1113  +  int stId = 0;
         1114  +  char const * arg;
         1115  +  int rc;
         1116  +  if( argc!=requireArgc ){
         1117  +    return Th_WrongNumArgs2(interp,
         1118  +                            argv[0], argl[0],
         1119  +                            "StmtHandle");
         1120  +  }
         1121  +  if(!pStmt){
         1122  +    arg = argv[1];
         1123  +    pStmt = queryStmtHandle(interp, arg, argl[1], &stId);
         1124  +    if(!pStmt){
         1125  +      Th_ErrorMessage(interp, "Not a valid statement handle argument.", NULL, 0);
         1126  +      return TH_ERROR;
         1127  +    }
         1128  +  }
         1129  +  assert( NULL != pStmt );
         1130  +  rc = Th_query_FinalizeStmt2( interp, pStmt );
         1131  +  Th_SetResultInt( interp, rc );
         1132  +  return TH_OK;
         1133  +}
         1134  +
         1135  +/*
         1136  +** Reports the current sqlite3_errmsg() via TH and returns TH_ERROR.
         1137  +*/
         1138  +static int queryReportDbErr( Th_Interp * interp ){
         1139  +  char const * msg = sqlite3_errmsg( g.db );
         1140  +  Th_ErrorMessage(interp, "db error:", msg, -1);
         1141  +  return TH_ERROR;
         1142  +}
         1143  +
         1144  +/*
         1145  +** Internal helper for fetching statement handle and index parameters.
         1146  +** The first 4 args should be the args passed to the TH1 callback.
         1147  +** pStmt must be a pointer to a NULL pointer. pIndex may be NULL or
         1148  +** a pointer to store the statement index argument in. If pIndex is
         1149  +** NULL then argc is asserted to be at least 2, else it must be at
         1150  +** least 3.
         1151  +**
         1152  +** On success it returns 0, sets *pStmt to the referenced statement
         1153  +** handle, and pIndex (if not NULL) to the integer value of argv[2]
         1154  +** argument. On error it reports the error via TH, returns non-0, and
         1155  +** modifies neither pStmt nor pIndex.
         1156  +*/
         1157  +static int queryStmtIndexArgs(
         1158  +  Th_Interp * interp,
         1159  +  int argc,
         1160  +  char const ** argv,
         1161  +  int *argl,
         1162  +  sqlite3_stmt ** pStmt,
         1163  +  int * pIndex ){
         1164  +  int index = 0;
         1165  +  sqlite3_stmt * stmt;
         1166  +  if( !pIndex ){
         1167  +    if(argc<2){
         1168  +      return Th_WrongNumArgs2(interp,
         1169  +                              argv[0], argl[0],
         1170  +                              "StmtHandle");
         1171  +    }
         1172  +  }else{
         1173  +    if( argc<3 ){
         1174  +      return Th_WrongNumArgs2(interp,
         1175  +                              argv[0], argl[0],
         1176  +                              "StmtHandle Index");
         1177  +    }
         1178  +    if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){
         1179  +      return TH_ERROR;
         1180  +    }
         1181  +  }
         1182  +  stmt = *pStmt ? *pStmt : queryStmtHandle(interp, argv[1], argl[1], NULL);
         1183  +  if( NULL == stmt ){
         1184  +    return TH_ERROR;
         1185  +  }else{
         1186  +    *pStmt = stmt;
         1187  +    if( pIndex ){
         1188  +      *pIndex = index;
         1189  +    }
         1190  +    return 0;
         1191  +  }
         1192  +}
         1193  +
         1194  +/*
         1195  +** TH command:
         1196  +**
         1197  +** query step stmtId
         1198  +** query stmtId step
         1199  +**
         1200  +** Steps the given statement handle. Returns 0 at the end of the set,
         1201  +** a positive value if it fetches a row, and throws on error.
         1202  +*/
         1203  +static int queryStepCmd(
         1204  +  Th_Interp *interp,
         1205  +  void *p, 
         1206  +  int argc, 
         1207  +  const char **argv, 
         1208  +  int *argl
         1209  +){
         1210  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1211  +  int requireArgc = pStmt ? 1 : 2;
         1212  +  int rc = 0;
         1213  +  if( argc!=requireArgc ){
         1214  +    return Th_WrongNumArgs2(interp,
         1215  +                            argv[0], argl[0],
         1216  +                            "StmtHandle");
         1217  +  }
         1218  +  if(!pStmt && 0 != queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, NULL)){
         1219  +    return TH_ERROR;
         1220  +  }
         1221  +  assert(NULL != pStmt);
         1222  +  rc = sqlite3_step( pStmt );
         1223  +  switch(rc){
         1224  +    case SQLITE_ROW:
         1225  +      rc = 1;
         1226  +      break;
         1227  +    case SQLITE_DONE:
         1228  +      rc = 0;
         1229  +      break;
         1230  +    default:
         1231  +      return queryReportDbErr( interp );
         1232  +  }
         1233  +  Th_SetResultInt( interp, rc );
         1234  +  return TH_OK;
         1235  +}
         1236  +
         1237  +/*
         1238  +** TH command:
         1239  +**
         1240  +** query StmtId reset
         1241  +** query reset StmtId
         1242  +**
         1243  +** Equivalent to sqlite3_reset().
         1244  +*/
         1245  +static int queryResetCmd(
         1246  +  Th_Interp *interp,
         1247  +  void *p, 
         1248  +  int argc, 
         1249  +  const char **argv, 
         1250  +  int *argl
         1251  +){
         1252  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1253  +  int const rc = sqlite3_reset(pStmt);
         1254  +  if(rc){
         1255  +    Th_ErrorMessage(interp, "Reset of statement failed.", NULL, 0);
         1256  +    return TH_ERROR;
         1257  +  }else{
         1258  +    return TH_OK;
         1259  +  }
         1260  +}
         1261  +
         1262  +
         1263  +/*
         1264  +** TH command:
         1265  +**
         1266  +** query col string stmtId Index
         1267  +** query stmtId col string Index
         1268  +** query stmtId col Index string
         1269  +**
         1270  +** Returns the result column value at the given 0-based index.
         1271  +*/
         1272  +static int queryColStringCmd(
         1273  +  Th_Interp *interp,
         1274  +  void *p, 
         1275  +  int argc, 
         1276  +  const char **argv, 
         1277  +  int *argl
         1278  +){
         1279  +  Th_Query * sq = Th_query_manager(interp);
         1280  +  int index = sq->colCmdIndex;
         1281  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1282  +  int requireArgc = pStmt ? 2 : 3;
         1283  +  char const * val;
         1284  +  int valLen;
         1285  +  if( index >= 0 ) --requireArgc;
         1286  +  if( argc!=requireArgc ){
         1287  +    return Th_WrongNumArgs2(interp,
         1288  +                            argv[0], argl[0],
         1289  +                            "StmtHandle Index");
         1290  +  }
         1291  +  if(!pStmt){
         1292  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1293  +  }else if(index<0){
         1294  +    Th_ToInt(interp, argv[1], argl[1], &index);
         1295  +  }
         1296  +  if(index < 0){
         1297  +    return TH_ERROR;
         1298  +  }
         1299  +  val = sqlite3_column_text( pStmt, index );
         1300  +  valLen = val ? sqlite3_column_bytes( pStmt, index ) : 0;
         1301  +  Th_SetResult( interp, val, valLen );
         1302  +  return TH_OK;
         1303  +}
         1304  +
         1305  +/*
         1306  +** TH command:
         1307  +**
         1308  +** query col int stmtId Index
         1309  +** query stmtId col int Index
         1310  +** query stmtId col Index int
         1311  +**
         1312  +** Returns the result column value at the given 0-based index.
         1313  +*/
         1314  +static int queryColIntCmd(
         1315  +  Th_Interp *interp,
         1316  +  void *p, 
         1317  +  int argc, 
         1318  +  const char **argv, 
         1319  +  int *argl
         1320  +){
         1321  +  Th_Query * sq = Th_query_manager(interp);
         1322  +  int index = sq->colCmdIndex;
         1323  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1324  +  int requireArgc = pStmt ? 2 : 3;
         1325  +  int rc = 0;
         1326  +  if( index >= 0 ) --requireArgc;
         1327  +  if( argc!=requireArgc ){
         1328  +    return Th_WrongNumArgs2(interp,
         1329  +                            argv[0], argl[0],
         1330  +                            "StmtHandle Index");
         1331  +  }
         1332  +  if(!pStmt){
         1333  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1334  +  }else if(index<0){
         1335  +    Th_ToInt(interp, argv[1], argl[1], &index);
         1336  +  }
         1337  +  if(index < 0){
         1338  +    return TH_ERROR;
         1339  +  }
         1340  +  Th_SetResultInt( interp, sqlite3_column_int( pStmt, index ) );
         1341  +  return TH_OK;
         1342  +}
         1343  +
         1344  +/*
         1345  +** TH command:
         1346  +**
         1347  +** query col double stmtId Index
         1348  +** query stmtId col double Index
         1349  +** query stmtId col Index double
         1350  +**
         1351  +** Returns the result column value at the given 0-based index.
         1352  +*/
         1353  +static int queryColDoubleCmd(
         1354  +  Th_Interp *interp,
         1355  +  void *p, 
         1356  +  int argc, 
         1357  +  const char **argv, 
         1358  +  int *argl
         1359  +){
         1360  +  Th_Query * sq = Th_query_manager(interp);
         1361  +  int index = sq->colCmdIndex;
         1362  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1363  +  int requireArgc = pStmt ? 2 : 3;
         1364  +  double rc = 0;
         1365  +  if( index >= 0 ) --requireArgc;
         1366  +  if( argc!=requireArgc ){
         1367  +    return Th_WrongNumArgs2(interp,
         1368  +                            argv[0], argl[0],
         1369  +                            "StmtHandle Index");
         1370  +  }
         1371  +  if(!pStmt){
         1372  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1373  +  }else if(index<0){
         1374  +    Th_ToInt(interp, argv[1], argl[1], &index);
         1375  +  }
         1376  +  if(index < 0){
         1377  +    return TH_ERROR;
         1378  +  }
         1379  +  Th_SetResultDouble( interp, sqlite3_column_double( pStmt, index ) );
         1380  +  return TH_OK;
         1381  +}
         1382  +
         1383  +/*
         1384  +** TH command:
         1385  +**
         1386  +** query col isnull stmtId Index
         1387  +** query stmtId col isnull Index
         1388  +** query stmtId col Index isnull
         1389  +**
         1390  +** Returns non-0 if the given 0-based result column index contains
         1391  +** an SQL NULL value, else returns 0.
         1392  +*/
         1393  +static int queryColIsNullCmd(
         1394  +  Th_Interp *interp,
         1395  +  void *p, 
         1396  +  int argc, 
         1397  +  const char **argv, 
         1398  +  int *argl
         1399  +){
         1400  +  Th_Query * sq = Th_query_manager(interp);
         1401  +  int index = sq->colCmdIndex;
         1402  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1403  +  int requireArgc = pStmt ? 2 : 3;
         1404  +  if( index >= 0 ) --requireArgc;
         1405  +  double rc = 0;
         1406  +  if( argc!=requireArgc ){
         1407  +    return Th_WrongNumArgs2(interp,
         1408  +                            argv[0], argl[0],
         1409  +                            "StmtHandle Index");
         1410  +  }
         1411  +  if(!pStmt){
         1412  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1413  +  }else if(index<0){
         1414  +    Th_ToInt(interp, argv[1], argl[1], &index);
         1415  +  }
         1416  +  if(index < 0){
         1417  +    return TH_ERROR;
         1418  +  }
         1419  +  Th_SetResultInt( interp,
         1420  +                   SQLITE_NULL==sqlite3_column_type( pStmt, index )
         1421  +                   ? 1 : 0);
         1422  +  return TH_OK;
         1423  +}
         1424  +
         1425  +/*
         1426  +** TH command:
         1427  +**
         1428  +** query col type stmtId Index
         1429  +** query stmtId col type Index
         1430  +** query stmtId col Index type
         1431  +**
         1432  +** Returns the sqlite type identifier for the given 0-based result
         1433  +** column index. The values are available in TH as $SQLITE_NULL,
         1434  +** $SQLITE_INTEGER, etc.
         1435  +*/
         1436  +static int queryColTypeCmd(
         1437  +  Th_Interp *interp,
         1438  +  void *p, 
         1439  +  int argc, 
         1440  +  const char **argv, 
         1441  +  int *argl
         1442  +){
         1443  +  Th_Query * sq = Th_query_manager(interp);
         1444  +  int index = sq->colCmdIndex;
         1445  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1446  +  int requireArgc = pStmt ? 2 : 3;
         1447  +  if( index >= 0 ) --requireArgc;
         1448  +  double rc = 0;
         1449  +  if( argc!=requireArgc ){
         1450  +    return Th_WrongNumArgs2(interp,
         1451  +                            argv[0], argl[0],
         1452  +                            "StmtHandle Index");
         1453  +  }
         1454  +  if(!pStmt){
         1455  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1456  +  }else if(index<0){
         1457  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1458  +  }
         1459  +  if(index < 0){
         1460  +    return TH_ERROR;
         1461  +  }
         1462  +  Th_SetResultInt( interp, sqlite3_column_type( pStmt, index ) );
         1463  +  return TH_OK;
         1464  +}
         1465  +
         1466  +/*
         1467  +** TH command:
         1468  +**
         1469  +** query col count stmtId
         1470  +** query stmtId col count
         1471  +**
         1472  +** Returns the number of result columns in the query.
         1473  +*/
         1474  +static int queryColCountCmd(
         1475  +  Th_Interp *interp,
         1476  +  void *p, 
         1477  +  int argc, 
         1478  +  const char **argv, 
         1479  +  int *argl
         1480  +){
         1481  +  int rc;
         1482  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1483  +  int requireArgc = pStmt ? 1 : 2;
         1484  +  if( argc!=requireArgc ){
         1485  +    return Th_WrongNumArgs2(interp,
         1486  +                           argv[0], argl[0],
         1487  +                           "StmtHandle");
         1488  +  }
         1489  +  if(!pStmt){
         1490  +    pStmt = queryStmtHandle(interp, argv[1], argl[1], NULL);
         1491  +    if( NULL == pStmt ){
         1492  +      return TH_ERROR;
         1493  +    }
         1494  +  }
         1495  +  rc = sqlite3_column_count( pStmt );
         1496  +  Th_SetResultInt( interp, rc );
         1497  +  return TH_OK;
         1498  +}
         1499  +
         1500  +/*
         1501  +** TH command:
         1502  +**
         1503  +** query col name stmtId Index
         1504  +** query stmtId col name Index
         1505  +** query stmtId col Index name 
         1506  +**
         1507  +** Returns the result column name at the given 0-based index.
         1508  +*/
         1509  +static int queryColNameCmd(
         1510  +  Th_Interp *interp,
         1511  +  void *p, 
         1512  +  int argc, 
         1513  +  const char **argv, 
         1514  +  int *argl
         1515  +){
         1516  +  Th_Query * sq = Th_query_manager(interp);
         1517  +  int index = sq->colCmdIndex;
         1518  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1519  +  int requireArgc = pStmt ? 2 : 3;
         1520  +  char const * val;
         1521  +  int rc = 0;
         1522  +  if( index >= 0 ) --requireArgc;
         1523  +  if( argc!=requireArgc ){
         1524  +    return Th_WrongNumArgs2(interp,
         1525  +                            argv[0], argl[0],
         1526  +                            "StmtHandle Index");
         1527  +  }
         1528  +  if(!pStmt){
         1529  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1530  +  }else if(index<0){
         1531  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1532  +  }
         1533  +  if(index < 0){
         1534  +    return TH_ERROR;
         1535  +  }
         1536  +  assert(NULL!=pStmt);
         1537  +  val = sqlite3_column_name( pStmt, index );
         1538  +  if(NULL==val){
         1539  +    Th_ErrorMessage(interp, "Column index out of bounds(?):", argv[2], -1);
         1540  +    return TH_ERROR;
         1541  +  }else{
         1542  +    Th_SetResult( interp, val, strlen( val ) );
         1543  +    return TH_OK;
         1544  +  }
         1545  +}
         1546  +
         1547  +/*
         1548  +** TH command:
         1549  +**
         1550  +** query col time stmtId Index format
         1551  +** query stmtId col name Index format
         1552  +** query stmtId col Index name format
         1553  +**
         1554  +** Returns the result column name at the given 0-based index.
         1555  +*/
         1556  +static int queryColTimeCmd(
         1557  +  Th_Interp *interp,
         1558  +  void *ctx, 
         1559  +  int argc, 
         1560  +  const char **argv, 
         1561  +  int *argl
         1562  +){
         1563  +  Th_Query * sq = Th_query_manager(interp);
         1564  +  int index = sq->colCmdIndex;
         1565  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)ctx;
         1566  +  int minArgs = pStmt ? 3 : 4;
         1567  +  int argPos;
         1568  +  char const * val;
         1569  +  char * fval;
         1570  +  int i, rc = 0;
         1571  +  char const * fmt;
         1572  +  Blob sql = empty_blob;
         1573  +  if( index >= 0 ) --minArgs;
         1574  +  if( argc<minArgs ){
         1575  +    return Th_WrongNumArgs2(interp,
         1576  +                            argv[0], argl[0],
         1577  +                            "StmtHandle Index Format");
         1578  +  }
         1579  +  if(!pStmt){
         1580  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1581  +    argPos = 3;
         1582  +  }else if(index<0){
         1583  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1584  +    argPos = 2;
         1585  +  }else{
         1586  +    argPos = 1;
         1587  +  }
         1588  +  if(index < 0){
         1589  +    return TH_ERROR;
         1590  +  }
         1591  +  val = sqlite3_column_text( pStmt, index );
         1592  +  fmt = argv[argPos++];
         1593  +  assert(NULL!=pStmt);
         1594  +  blob_appendf(&sql,"SELECT strftime(%Q,%Q",
         1595  +               fmt, val);
         1596  +  if(argc>argPos){
         1597  +    for(i = argPos; i < argc; ++i ){
         1598  +      blob_appendf(&sql, ",%Q", argv[i]);
         1599  +    }
         1600  +  }
         1601  +  blob_append(&sql, ")", 1);
         1602  +  fval = db_text(NULL,"%s", sql.aData);
         1603  +  
         1604  +  blob_reset(&sql);
         1605  +  Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
         1606  +  fossil_free(fval);
         1607  +  return 0;
         1608  +}
         1609  +
         1610  +/*
         1611  +** TH command:
         1612  +**
         1613  +**  query strftime TimeVal ?Modifiers...?
         1614  +**
         1615  +** Acts as a proxy to sqlite3's strftime() SQL function.
         1616  +*/
         1617  +static int queryStrftimeCmd(
         1618  +  Th_Interp *interp,
         1619  +  void *ctx, 
         1620  +  int argc, 
         1621  +  const char **argv, 
         1622  +  int *argl
         1623  +){
         1624  +  char const * val;
         1625  +  char * fval;
         1626  +  int i, rc = 0;
         1627  +  int index = -1;
         1628  +  char const * fmt;
         1629  +  Blob sql = empty_blob;
         1630  +  if( argc<3 ){
         1631  +    return Th_WrongNumArgs2(interp,
         1632  +                            argv[0], argl[0],
         1633  +                            "Format Value ?Modifiers...?");
         1634  +  }
         1635  +  fmt = argv[1];
         1636  +  val = argv[2];
         1637  +  blob_appendf(&sql,"SELECT strftime(%Q,%Q",
         1638  +               fmt, val);
         1639  +  if(argc>3){
         1640  +    for(i = 3; i < argc; ++i ){
         1641  +      blob_appendf(&sql, ",%Q", argv[i]);
         1642  +    }
         1643  +  }
         1644  +  blob_append(&sql, ")", 1);
         1645  +  fval = db_text(NULL,"%s", sql.aData);
         1646  +  blob_reset(&sql);
         1647  +  Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
         1648  +  fossil_free(fval);
         1649  +  return 0;
         1650  +}
         1651  +
         1652  +
         1653  +/*
         1654  +** TH command:
         1655  +**
         1656  +** query bind null stmtId Index
         1657  +** query stmtId bind null Index
         1658  +**
         1659  +** Binds a value to the given 1-based parameter index.
         1660  +*/
         1661  +static int queryBindNullCmd(
         1662  +  Th_Interp *interp,
         1663  +  void *p, 
         1664  +  int argc, 
         1665  +  const char **argv, 
         1666  +  int *argl
         1667  +){
         1668  +  Th_Query * sq = Th_query_manager(interp);
         1669  +  int index = sq->colCmdIndex;
         1670  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1671  +  int requireArgc = pStmt ? 2 : 3;
         1672  +  if( index > 0 ) --requireArgc;
         1673  +  int rc;
         1674  +  if( argc!=requireArgc ){
         1675  +    return Th_WrongNumArgs2(interp,
         1676  +                            argv[0], argl[0],
         1677  +                            "StmtHandle Index");
         1678  +  }
         1679  +  if(!pStmt){
         1680  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1681  +  }else if(index<1){
         1682  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1683  +  }
         1684  +  if(index < 1){
         1685  +    return TH_ERROR;
         1686  +  }
         1687  +  rc = sqlite3_bind_null( pStmt, index );
         1688  +  if(rc){
         1689  +    return queryReportDbErr( interp );
         1690  +  }
         1691  +  Th_SetResultInt( interp, 0 );
         1692  +  return TH_OK;
         1693  +}
         1694  +
         1695  +
         1696  +/*
         1697  +** TH command:
         1698  +**
         1699  +** query bind string stmtId Index Value
         1700  +** query stmtId bind string Index Value
         1701  +**
         1702  +** Binds a value to the given 1-based parameter index.
         1703  +*/
         1704  +static int queryBindStringCmd(
         1705  +  Th_Interp *interp,
         1706  +  void *p, 
         1707  +  int argc, 
         1708  +  const char **argv, 
         1709  +  int *argl
         1710  +){
         1711  +  Th_Query * sq = Th_query_manager(interp);
         1712  +  int index = sq->colCmdIndex;
         1713  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1714  +  int requireArgc = pStmt ? 3 : 4;
         1715  +  int rc;
         1716  +  int argPos;
         1717  +  if( index > 0 ) --requireArgc;
         1718  +  if( argc!=requireArgc ){
         1719  +    return Th_WrongNumArgs2(interp,
         1720  +                            argv[0], argl[0],
         1721  +                            "StmtHandle Index Value");
         1722  +  }
         1723  +  if(!pStmt){
         1724  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1725  +    argPos = 3;
         1726  +  }else if(index<1){
         1727  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1728  +    argPos = 2;
         1729  +  }else{
         1730  +    argPos = 1;
         1731  +  }
         1732  +  if(index < 1){
         1733  +    return TH_ERROR;
         1734  +  }
         1735  +  rc = sqlite3_bind_text( pStmt, index, argv[argPos], argl[argPos], SQLITE_TRANSIENT );
         1736  +  if(rc){
         1737  +    return queryReportDbErr( interp );
         1738  +  }
         1739  +  Th_SetResultInt( interp, 0 );
         1740  +  return TH_OK;
         1741  +}
         1742  +
         1743  +/*
         1744  +** TH command:
         1745  +**
         1746  +** query bind int stmtId Index Value
         1747  +** query stmtId bind int Index Value
         1748  +**
         1749  +** Binds a value to the given 1-based parameter index.
         1750  +*/
         1751  +static int queryBindIntCmd(
         1752  +  Th_Interp *interp,
         1753  +  void *p, 
         1754  +  int argc, 
         1755  +  const char **argv, 
         1756  +  int *argl
         1757  +){
         1758  +  Th_Query * sq = Th_query_manager(interp);
         1759  +  int index = sq->colCmdIndex;
         1760  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1761  +  int requireArgc = pStmt ? 3 : 4;
         1762  +  int rc;
         1763  +  int argPos;
         1764  +  int val;
         1765  +  if( index > 0 ) --requireArgc;
         1766  +  if( argc!=requireArgc ){
         1767  +    return Th_WrongNumArgs2(interp,
         1768  +                            argv[0], argl[0],
         1769  +                            "StmtHandle Index Value");
         1770  +  }
         1771  +  if(!pStmt){
         1772  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1773  +    argPos = 3;
         1774  +  }else if(index<1){
         1775  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1776  +    argPos = 2;
         1777  +  }else{
         1778  +    argPos = 1;
         1779  +  }
         1780  +  if(index < 1){
         1781  +    return TH_ERROR;
         1782  +  }
         1783  +  if( 0 != Th_ToInt( interp, argv[argPos], argl[argPos], &val ) ){
         1784  +    return TH_ERROR;
         1785  +  }
         1786  +
         1787  +  rc = sqlite3_bind_int( pStmt, index, val );
         1788  +  if(rc){
         1789  +    return queryReportDbErr( interp );
         1790  +  }
         1791  +  Th_SetResultInt( interp, 0 );
         1792  +  return TH_OK;
         1793  +}
         1794  +
         1795  +/*
         1796  +** TH command:
         1797  +**
         1798  +** query bind double stmtId Index Value
         1799  +** query stmtId bind double Index Value
         1800  +**
         1801  +** Binds a value to the given 1-based parameter index.
         1802  +*/
         1803  +static int queryBindDoubleCmd(
         1804  +  Th_Interp *interp,
         1805  +  void *p, 
         1806  +  int argc, 
         1807  +  const char **argv, 
         1808  +  int *argl
         1809  +){
         1810  +  Th_Query * sq = Th_query_manager(interp);
         1811  +  int index = sq->colCmdIndex;
         1812  +  sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
         1813  +  int requireArgc = pStmt ? 3 : 4;
         1814  +  int rc;
         1815  +  int argPos;
         1816  +  double val;
         1817  +  if( index > 0 ) --requireArgc;
         1818  +  if( argc!=requireArgc ){
         1819  +    return Th_WrongNumArgs2(interp,
         1820  +                            argv[0], argl[0],
         1821  +                            "StmtHandle Index Value");
         1822  +  }
         1823  +  if(!pStmt){
         1824  +    queryStmtIndexArgs(interp, argc, argv, argl, &pStmt, &index);
         1825  +    argPos = 3;
         1826  +  }else if(index<1){
         1827  +    Th_ToInt( interp, argv[1], argl[1], &index );
         1828  +    argPos = 2;
         1829  +  }else{
         1830  +    argPos = 1;
         1831  +  }
         1832  +  if(index < 1){
         1833  +    return TH_ERROR;
         1834  +  }
         1835  +  if( 0 != Th_ToDouble( interp, argv[argPos], argl[argPos], &val ) ){
         1836  +    return TH_ERROR;
         1837  +  }
         1838  +
         1839  +  rc = sqlite3_bind_double( pStmt, index, val );
         1840  +  if(rc){
         1841  +    return queryReportDbErr( interp );
         1842  +  }
         1843  +  Th_SetResultInt( interp, 0 );
         1844  +  return TH_OK;
         1845  +}
         1846  +
         1847  +/*
         1848  +** TH command:
         1849  +**
         1850  +** bind subcommand StmtId...
         1851  +** bind StmtId subcommand...
         1852  +**
         1853  +** This is the top-level dispatcher for the "bind" family of commands.
         1854  +*/
         1855  +static int queryBindTopLevelCmd(
         1856  +  Th_Interp *interp,
         1857  +  void *ctx, 
         1858  +  int argc, 
         1859  +  const char **argv, 
         1860  +  int *argl
         1861  +){
         1862  +  int colIndex = -1;
         1863  +  static Th_SubCommand aSub[] = {
         1864  +    {"int",    queryBindIntCmd},
         1865  +    {"double", queryBindDoubleCmd},
         1866  +    {"null",   queryBindNullCmd},
         1867  +    {"string", queryBindStringCmd},
         1868  +    {0, 0}
         1869  +  };
         1870  +  Th_Query * sq = Th_query_manager(interp);
         1871  +  assert(NULL != sq);
         1872  +  if( 1 == argc ){
         1873  +      Th_WrongNumArgs2( interp, argv[0], argl[0],
         1874  +                        "subcommand: int|double|null|string");
         1875  +      return TH_ERROR;
         1876  +  }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
         1877  +    if(colIndex <0){
         1878  +      Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
         1879  +      return TH_ERROR;
         1880  +    }
         1881  +    ++argv;
         1882  +    ++argl;
         1883  +    --argc;
         1884  +  }
         1885  +  sq->colCmdIndex = colIndex;
         1886  +  Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
         1887  +
         1888  +}
         1889  +
         1890  +/*
         1891  +** TH command:
         1892  +**
         1893  +** query col subcommand ...
         1894  +** query StmtId col subcommand ...
         1895  +**
         1896  +** This is the top-level dispatcher for the col subcommands.
         1897  +*/
         1898  +static int queryColTopLevelCmd(
         1899  +  Th_Interp *interp,
         1900  +  void *ctx, 
         1901  +  int argc, 
         1902  +  const char **argv, 
         1903  +  int *argl
         1904  +){
         1905  +  int colIndex = -1;
         1906  +  static Th_SubCommand aSub[] = {
         1907  +    {"count",   queryColCountCmd},
         1908  +    {"is_null", queryColIsNullCmd},
         1909  +    {"isnull",  queryColIsNullCmd},
         1910  +    {"name",    queryColNameCmd},
         1911  +    {"double",  queryColDoubleCmd},
         1912  +    {"int",     queryColIntCmd},
         1913  +    {"string",  queryColStringCmd},
         1914  +    {"time",    queryColTimeCmd},
         1915  +    {"type",    queryColTypeCmd},
         1916  +    {0, 0}
         1917  +  };
         1918  +  static Th_SubCommand aSubWithIndex[] = {
         1919  +    /*
         1920  +      This subset is coded to accept the column index
         1921  +      either before the subcommand name or after it.
         1922  +      If called like (bind StmtId subcommand) then
         1923  +      only these commands will be checked.
         1924  +    */
         1925  +    {"is_null", queryColIsNullCmd},
         1926  +    {"isnull",  queryColIsNullCmd},
         1927  +    {"name",    queryColNameCmd},
         1928  +    {"double",  queryColDoubleCmd},
         1929  +    {"int",     queryColIntCmd},
         1930  +    {"string",  queryColStringCmd},
         1931  +    {"time",    queryColTimeCmd},
         1932  +    {"type",    queryColTypeCmd},
         1933  +    {0, 0}
         1934  +  };
         1935  +  Th_Query * sq = Th_query_manager(interp);
         1936  +  assert(NULL != sq);
         1937  +  if( 1 == argc ){
         1938  +      Th_WrongNumArgs2( interp, argv[0], argl[0],
         1939  +                        "subcommand: "
         1940  +                        "count|is_null|isnull|name|"
         1941  +                        "double|int|string|time|type");
         1942  +      return TH_ERROR;
         1943  +  }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
         1944  +    if(colIndex <0){
         1945  +      Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
         1946  +      return TH_ERROR;
         1947  +    }
         1948  +    ++argv;
         1949  +    ++argl;
         1950  +    --argc;
         1951  +  }
         1952  +  sq->colCmdIndex = colIndex;
         1953  +  Th_CallSubCommand2( interp, ctx, argc, argv, argl,
         1954  +                      (colIndex<0) ? aSub : aSubWithIndex );
         1955  +}
         1956  +
         1957  +
         1958  +/*
         1959  +** TH command:
         1960  +**
         1961  +** query subcommand ...
         1962  +** query StmtId subcommand ...
         1963  +**
         1964  +** This is the top-level dispatcher for the query subcommand.
         1965  +*/
         1966  +static int queryTopLevelCmd(
         1967  +  Th_Interp *interp,
         1968  +  void *ctx, 
         1969  +  int argc, 
         1970  +  const char **argv, 
         1971  +  int *argl
         1972  +){
         1973  +  int stmtId = 0;
         1974  +  sqlite3_stmt * pStmt = NULL;
         1975  +  static Th_SubCommand aSubAll[] = {
         1976  +    {"bind",        queryBindTopLevelCmd},
         1977  +    {"col",         queryColTopLevelCmd},
         1978  +    {"finalize",    queryFinalizeCmd},
         1979  +    {"prepare",     queryPrepareCmd},
         1980  +    {"reset",       queryResetCmd},
         1981  +    {"step",        queryStepCmd},
         1982  +    {"strftime",    queryStrftimeCmd},
         1983  +    {0, 0}
         1984  +  };
         1985  +  static Th_SubCommand aSubWithStmt[] = {
         1986  +    /* This subset is coded to deal with being supplied a statement
         1987  +       via pStmt or via one of their args. When called like (query
         1988  +       StmtId ...) only these subcommands will be checked.*/
         1989  +    {"bind",        queryBindTopLevelCmd},
         1990  +    {"col",         queryColTopLevelCmd},
         1991  +    {"step",        queryStepCmd},
         1992  +    {"finalize",    queryFinalizeCmd},
         1993  +    {"reset",       queryResetCmd},
         1994  +    {0, 0}
         1995  +  };
         1996  +
         1997  +
         1998  +  assert( NULL != Th_query_manager(interp) );
         1999  +  if( 1 == argc ){
         2000  +      Th_WrongNumArgs2( interp, argv[0], argl[0],
         2001  +                        "subcommand: bind|col|finalize|prepare|reset|step|strftime");
         2002  +      return TH_ERROR;
         2003  +  }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &stmtId) ){
         2004  +    ++argv;
         2005  +    ++argl;
         2006  +    --argc;
         2007  +    pStmt = Th_query_GetStmt( interp, stmtId );
         2008  +  }
         2009  +
         2010  +  Th_CallSubCommand2( interp, pStmt, argc, argv, argl,
         2011  +                      pStmt ? aSubWithStmt : aSubAll );
         2012  +}
         2013  +
         2014  +/*
         2015  +** Registers the "query" API with the given interpreter. Returns TH_OK
         2016  +** on success, TH_ERROR on error.
         2017  +*/
         2018  +int th_register_query(Th_Interp *interp){
         2019  +  enum { BufLen = 100 };
         2020  +  char buf[BufLen];
         2021  +  int i, l;
         2022  +#define SET(K) l = snprintf(buf, BufLen, "%d", K);      \
         2023  +  Th_SetVar( interp, #K, strlen(#K), buf, l );
         2024  +  SET(SQLITE_BLOB);
         2025  +  SET(SQLITE_FLOAT);
         2026  +  SET(SQLITE_INTEGER);
         2027  +  SET(SQLITE_NULL);
         2028  +  SET(SQLITE_TEXT);
         2029  +#if 0
         2030  +  /* so far we don't need these in script code */
         2031  +  SET(SQLITE_ERROR);
         2032  +  SET(SQLITE_DONE);
         2033  +  SET(SQLITE_OK);
         2034  +  SET(SQLITE_ROW);
         2035  +#endif
         2036  +#undef SET
         2037  +  int rc = TH_OK;
         2038  +  static Th_Command_Reg aCommand[] = {
         2039  +    {"query",             queryTopLevelCmd,  0},
         2040  +    {0, 0, 0}
         2041  +  };
         2042  +  rc = Th_RegisterCommands( interp, aCommand );
         2043  +  if(TH_OK==rc){
         2044  +    Th_Query * sq = Th_Malloc(interp, sizeof(Th_Query));
         2045  +    if(!sq){
         2046  +      rc = TH_ERROR;
         2047  +    }else{
         2048  +      assert( NULL == sq->aStmt );
         2049  +      assert( 0 == sq->nStmt );
         2050  +      Th_SetData( interp, Th_Query_KEY, sq, finalizerSqlite );
         2051  +      assert( sq == Th_query_manager(interp) );
         2052  +    }
         2053  +  }
         2054  +  return rc;
         2055  +}
         2056  +
         2057  +#endif
         2058  +/* end TH_ENABLE_QUERY */
         2059  +
   420   2060   /*
   421   2061   ** Make sure the interpreter has been initialized.  Initialize it if
   422   2062   ** it has not been already.
   423   2063   **
   424   2064   ** The interpreter is stored in the g.interp global variable.
   425   2065   */
   426   2066   void Th_FossilInit(void){
   427         -  static struct _Command {
   428         -    const char *zName;
   429         -    Th_CommandProc xProc;
   430         -    void *pContext;
   431         -  } aCommand[] = {
         2067  +  /* The fossil-internal Th_Vtab instance. */
         2068  +  static Th_Vtab vtab = { xRealloc, {/*out*/
         2069  +    NULL /*write()*/,
         2070  +    NULL/*dispose()*/,
         2071  +    NULL/*pState*/,
         2072  +    1/*enabled*/
         2073  +    }
         2074  +  };
         2075  +
         2076  +  static PutsCmdData puts_Html = {0, 0, 0};
         2077  +  static PutsCmdData puts_Normal = {1, 0, 0};
         2078  +  static Th_Command_Reg aCommand[] = {
   432   2079       {"anycap",        anycapCmd,            0},
   433   2080       {"combobox",      comboboxCmd,          0},
         2081  +    {"date",          dateCmd,              0},
   434   2082       {"enable_output", enableOutputCmd,      0},
   435         -    {"linecount",     linecntCmd,           0},
   436   2083       {"hascap",        hascapCmd,            0},
   437   2084       {"hasfeature",    hasfeatureCmd,        0},
         2085  +    {"html",          putsCmd,     &puts_Html},
   438   2086       {"htmlize",       htmlizeCmd,           0},
   439         -    {"date",          dateCmd,              0},
   440         -    {"html",          putsCmd,              0},
   441         -    {"puts",          putsCmd,       (void*)1},
   442         -    {"wiki",          wikiCmd,              0},
         2087  +    {"linecount",     linecntCmd,           0},
         2088  +    {"puts",          putsCmd,   &puts_Normal},
         2089  +#if 0
         2090  +    {"render",        renderCmd,            0},
         2091  +#endif
   443   2092       {"repository",    repositoryCmd,        0},
         2093  +    {"wiki",          wikiCmd,              0},
         2094  +
   444   2095       {0, 0, 0}
   445   2096     };
   446   2097     if( g.interp==0 ){
   447   2098       int i;
         2099  +    if(g.cgiOutput){
         2100  +      vtab.out.xWrite = Th_Output_f_cgi_content;
         2101  +    }else{
         2102  +      vtab.out = Th_Vtab_OutputMethods_FILE;
         2103  +      vtab.out.pState = stdout;
         2104  +    }
         2105  +    vtab.out.enabled = enableOutput;
   448   2106       g.interp = Th_CreateInterp(&vtab);
   449   2107       th_register_language(g.interp);       /* Basic scripting commands. */
   450   2108   #ifdef FOSSIL_ENABLE_TCL
   451   2109       if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
   452   2110         th_register_tcl(g.interp, &g.tcl);  /* Tcl integration commands. */
   453   2111       }
   454   2112   #endif
   455         -    for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
   456         -      if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
   457         -      Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
   458         -                       aCommand[i].pContext, 0);
   459         -    }
         2113  +#ifdef TH_ENABLE_OB
         2114  +    th_register_ob(g.interp);
         2115  +#endif
         2116  +#ifdef TH_ENABLE_QUERY
         2117  +    th_register_query(g.interp);
         2118  +#endif
         2119  +#ifdef TH_ENABLE_ARGV
         2120  +    th_register_argv(g.interp);
         2121  +#endif
         2122  +    Th_RegisterCommands( g.interp, aCommand );
         2123  +    Th_Eval( g.interp, 0, "proc incr {name {step 1}} {\n"
         2124  +             "upvar $name x\n"
         2125  +             "set x [expr $x+$step]\n"
         2126  +             "}", -1 );
   460   2127     }
   461   2128   }
   462   2129   
   463   2130   /*
   464   2131   ** Store a string value in a variable in the interpreter.
   465   2132   */
   466   2133   void Th_Store(const char *zName, const char *zValue){
................................................................................
   551   2218       i += 2;
   552   2219     }
   553   2220     return i;
   554   2221   }
   555   2222   
   556   2223   /*
   557   2224   ** The z[] input contains text mixed with TH1 scripts.
   558         -** The TH1 scripts are contained within <th1>...</th1>. 
   559         -** TH1 variables are $aaa or $<aaa>.  The first form of
   560         -** variable is literal.  The second is run through htmlize
   561         -** before being inserted.
         2225  +** The TH1 scripts are contained within <th1>...</th1>.
         2226  +**
         2227  +** If flags does NOT contain the Th_Render_Flags_NO_DOLLAR_DEREF bit
         2228  +** then TH1 variables are $aaa or $<aaa>.  The first form of variable
         2229  +** is literal.  The second is run through htmlize before being
         2230  +** inserted.
   562   2231   **
   563   2232   ** This routine processes the template and writes the results
   564         -** on either stdout or into CGI.
         2233  +** via Th_Output().
   565   2234   */
   566         -int Th_Render(const char *z){
         2235  +int Th_Render(const char *z, int flags){
   567   2236     int i = 0;
   568   2237     int n;
   569   2238     int rc = TH_OK;
   570         -  char *zResult;
         2239  +  char const *zResult;
         2240  +  char doDollar = !(flags & Th_Render_Flags_NO_DOLLAR_DEREF);
   571   2241     Th_FossilInit();
   572   2242     while( z[i] ){
   573         -    if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
         2243  +    if( doDollar && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
   574   2244         const char *zVar;
   575   2245         int nVar;
   576   2246         int encode = 1;
   577         -      sendText(z, i, 0);
         2247  +      sendText(g.interp, z, i, 0);
   578   2248         if( z[i+1]=='<' ){
   579   2249           /* Variables of the form $<aaa> are html escaped */
   580   2250           zVar = &z[i+2];
   581   2251           nVar = n-2;
   582   2252         }else{
   583   2253           /* Variables of the form $aaa are output raw */
   584   2254           zVar = &z[i+1];
   585   2255           nVar = n;
   586   2256           encode = 0;
   587   2257         }
   588         -      rc = Th_GetVar(g.interp, (char*)zVar, nVar);
         2258  +      rc = Th_GetVar(g.interp, zVar, nVar);
   589   2259         z += i+1+n;
   590   2260         i = 0;
   591         -      zResult = (char*)Th_GetResult(g.interp, &n);
   592         -      sendText((char*)zResult, n, encode);
         2261  +      zResult = Th_GetResult(g.interp, &n);
         2262  +      sendText(g.interp, zResult, n, encode);
   593   2263       }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
   594         -      sendText(z, i, 0);
         2264  +      sendText(g.interp, z, i, 0);
   595   2265         z += i+5;
   596   2266         for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
   597   2267         rc = Th_Eval(g.interp, 0, (const char*)z, i);
   598   2268         if( rc!=TH_OK ) break;
   599   2269         z += i;
   600   2270         if( z[0] ){ z += 6; }
   601   2271         i = 0;
   602   2272       }else{
   603   2273         i++;
   604   2274       }
   605   2275     }
   606   2276     if( rc==TH_ERROR ){
   607         -    sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0);
   608         -    zResult = (char*)Th_GetResult(g.interp, &n);
   609         -    sendText((char*)zResult, n, 1);
   610         -    sendText("</p>", -1, 0);
         2277  +    sendText(g.interp, "<hr><p class=\"thmainError\">ERROR: ", -1, 0);
         2278  +    zResult = Th_GetResult(g.interp, &n);
         2279  +    sendText(g.interp, zResult, n, 1);
         2280  +    sendText(g.interp, "</p>", -1, 0);
   611   2281     }else{
   612         -    sendText(z, i, 0);
         2282  +    sendText(g.interp, z, i, 0);
   613   2283     }
   614   2284     return rc;
   615   2285   }
   616   2286   
   617   2287   /*
   618   2288   ** COMMAND: test-th-render
         2289  +** COMMAND: th1
         2290  +**
         2291  +** Processes a file provided on the command line as a TH1-capable
         2292  +** script/page. Output is sent to stdout or the CGI output buffer, as
         2293  +** appropriate. The input file is assumed to be text/wiki/HTML content
         2294  +** which may contain TH1 tag blocks and variables in the form $var or
         2295  +** $<var>. Each block is executed in the same TH1 interpreter
         2296  +** instance.
         2297  +**
         2298  +** ACHTUNG: not all of the $variables which are set in CGI mode
         2299  +** are available via this (CLI) command.
         2300  +**
   619   2301   */
   620   2302   void test_th_render(void){
   621   2303     Blob in;
   622   2304     if( g.argc<3 ){
   623   2305       usage("FILE");
         2306  +    assert(0 && "usage() does not return");
   624   2307     }
   625         -  db_open_config(0); /* Needed for global "tcl" setting. */
   626   2308     blob_zero(&in);
         2309  +  db_open_config(0); /* Needed for global "tcl" setting. */
         2310  +#ifdef TH_ENABLE_QUERY
         2311  +  db_find_and_open_repository(OPEN_ANY_SCHEMA,0)
         2312  +    /* required for th1 query API. */;
         2313  +#endif
   627   2314     blob_read_from_file(&in, g.argv[2]);
   628         -  Th_Render(blob_str(&in));
         2315  +  Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT);
   629   2316   }

Changes to src/tkt.c.

   326    326     }
   327    327     style_header("View Ticket");
   328    328     if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
   329    329     ticket_init();
   330    330     initializeVariablesFromDb();
   331    331     zScript = ticket_viewpage_code();
   332    332     if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
   333         -  Th_Render(zScript);
          333  +  Th_Render(zScript, Th_Render_Flags_DEFAULT );
   334    334     if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
   335    335   
   336    336     zFullName = db_text(0, 
   337    337          "SELECT tkt_uuid FROM ticket"
   338    338          " WHERE tkt_uuid GLOB '%q*'", zUuid);
   339    339     if( zFullName ){
   340    340       int cnt = 0;
................................................................................
   538    538     @ </p>
   539    539     zScript = ticket_newpage_code();
   540    540     Th_Store("login", g.zLogin);
   541    541     Th_Store("date", db_text(0, "SELECT datetime('now')"));
   542    542     Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
   543    543                      (void*)&zNewUuid, 0);
   544    544     if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
   545         -  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
          545  +  if( Th_Render(zScript, Th_Render_Flags_DEFAULT)==TH_RETURN && !g.thTrace && zNewUuid ){
   546    546       cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
   547    547       return;
   548    548     }
   549    549     @ </form>
   550    550     if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
   551    551     style_footer();
   552    552   }
................................................................................
   605    605     @ </p>
   606    606     zScript = ticket_editpage_code();
   607    607     Th_Store("login", g.zLogin);
   608    608     Th_Store("date", db_text(0, "SELECT datetime('now')"));
   609    609     Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
   610    610     Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
   611    611     if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
   612         -  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
          612  +  if( Th_Render(zScript, Th_Render_Flags_DEFAULT)==TH_RETURN && !g.thTrace && zName ){
   613    613       cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
   614    614       return;
   615    615     }
   616    616     @ </form>
   617    617     if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
   618    618     style_footer();
   619    619   }

Added test/th1-ob-1.th1.

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

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

            1  +This is not a formal test suite, but a tinkering ground.
            2  +Run it through "fossil test-th-render THIS_FILE".
            3  +<th1>
            4  +set stmt [query prepare {SELECT login, cap FROM user}]
            5  +set colCount [query $stmt col count]
            6  +puts "query column count: ${colCount}\n"
            7  +puts "stmt id=${stmt}\n"
            8  +
            9  +proc noop {} {}
           10  +proc incr {name {step 1}} {
           11  +    upvar $name x
           12  +    set x [expr $x+$step]
           13  +}
           14  +
           15  +
           16  +set sep "    "
           17  +set i 0
           18  +set colNames(0) 0
           19  +for {set i 0} {$i < $colCount} {incr i} {
           20  +    set colNames($i) [query $stmt col name $i]
           21  +    puts "colNames($i)=" $colNames($i) "\n"
           22  +}
           23  +
           24  +for {set row 0} {[query $stmt step]} {incr row} {
           25  +    for {set i 0} {$i < $colCount} {incr i} {
           26  +        if {$i > 0} {
           27  +            puts $sep
           28  +        } else {
           29  +            puts "#$row: $sep"
           30  +        }
           31  +        puts $colNames($i) = [query $stmt col string $i]
           32  +    }
           33  +    puts "\n"
           34  +}
           35  +unset row
           36  +
           37  +query $stmt finalize
           38  +#query finalize $stmt 
           39  +
           40  +
           41  +proc query_step_each {{stmt} {callback}} {
           42  +    set colNames(0) 0
           43  +    set colCount [query $stmt col count]
           44  +    for {set i 0} {$i < $colCount} {incr i} {
           45  +        set colNames($i) [query $stmt col name $i]
           46  +    }
           47  +    upvar cb $callback
           48  +    for {set row 0} {[query $stmt step]} {incr row} {
           49  +        #puts "Calling callback: $stmt $colCount colNames\n"
           50  +        $callback $stmt $colCount
           51  +    }
           52  +}
           53  +
           54  +set sql {SELECT uid, login FROM user WHERE uid!=?}
           55  +#set sql {SELECT uid, login FROM user WHERE login=?}
           56  +#set sql {SELECT tagid, value, null FROM tagxref WHERE value IS ? LIMIT 3}
           57  +set stmt [query prepare $sql]
           58  +puts "stmt ID=" $stmt "\n"
           59  +#query bind int $stmt 1 3
           60  +#query $stmt bind 1 int 3
           61  +query $stmt bind 1 string 3
           62  +#query $stmt bind string 1 3
           63  +#set stmt [query prepare $sql]
           64  +#query $stmt bind 1 string 1
           65  +#set stmt [query prepare $sql]
           66  +#query $stmt bind null 1
           67  +puts "USER LIST:\n"
           68  +catch {
           69  +    proc my_each {stmt colCount} {
           70  +        upvar 2 sep sep
           71  +        puts [query $stmt col int 0] " (type=" [query $stmt col type 0] ")" $sep
           72  +        puts [query $stmt col double 0] $sep
           73  +        puts [query $stmt col string 1]  " (type=" [query $stmt col type 1] ")" $sep
           74  +        puts "isnull 0 ?= " [query $stmt col is_null 0] $sep
           75  +        puts "isnull 2 ?= " [query col is_null $stmt 2]
           76  +#        for {set i 0} {$i < $colCount} {incr i} {
           77  +#            if {$i > 0} { puts $sep }
           78  +#        }
           79  +        puts "\n"
           80  +#        error "hi!"
           81  +    }
           82  +    query_step_each $stmt my_each
           83  +#    query reset $stmt
           84  +#    query $stmt reset 
           85  +#    query_step_each $stmt {
           86  +#        proc each {stmt cc} { puts hi "\n" }
           87  +#    }
           88  +    return 0
           89  +} rc
           90  +query finalize $stmt
           91  +if { 0 != $rc } {
           92  +        puts "ERROR: $rc\n"
           93  +}
           94  +
           95  +set consts [list SQLITE_BLOB SQLITE_FLOAT SQLITE_INTEGER SQLITE_NULL SQLITE_TEXT]
           96  +#set consts $SQLITE_CONSTANTS
           97  +puts consts = $consts "\n"
           98  +for {set i 0} {$i < [llength $consts]} {incr i} {
           99  +    set x [lindex $consts $i]
          100  +    puts \$$x = [expr \$$x] "\n"
          101  +}
          102  +
          103  +set ARGC [argv len]
          104  +puts ARGC = $ARGC "\n"
          105  +for {set i 0} {$i < $ARGC} {incr i} {
          106  +    puts "argv at $i = " [argv at $i] \n
          107  +}
          108  +
          109  +set magicDefault hi
          110  +set optA [argv string AA a $magicDefault]
          111  +puts "argv string AA = " $optA \n
          112  +
          113  +set optA [argv bool BB b 0]
          114  +puts "argv bool BB = " $optA \n
          115  +
          116  +set exception 0
          117  +catch {
          118  +    argv int noSuchOptionAndNoDefault
          119  +} exception
          120  +puts exception = $exception "\n"
          121  +
          122  +proc multiStmt {} {
          123  +    set max 5
          124  +    set i 0
          125  +    set s(0) 0
          126  +    for {set i 0} {$i < $max} {incr i} {
          127  +       set s($i) [query prepare "SELECT $i"]
          128  +       puts "s($i) = $s($i)\n"
          129  +    }
          130  +    for {set i 0} {$i < $max} {incr i} {
          131  +       query $s($i) step
          132  +    }
          133  +    for {set i 0} {$i < $max} {incr i} {
          134  +       puts "closing stmt $s($i)\n"
          135  +       query $s($i) finalize
          136  +    }
          137  +
          138  +    puts "Preparing again\n"
          139  +
          140  +    for {set i 0} {$i < $max} {incr i} {
          141  +       set s($i) [query prepare "SELECT $i"]
          142  +       puts "s($i) = $s($i)\n"
          143  +    }
          144  +    for {set i 0} {$i < $max} {incr i} {
          145  +       query $s($i) step
          146  +    }
          147  +    puts "Closing again\n"
          148  +
          149  +    for {set i 0} {$i < $max} {incr i} {
          150  +       puts "closing stmt $s($i)\n"
          151  +       query $s($i) finalize
          152  +    }
          153  +}
          154  +multiStmt
          155  +
          156  +puts "If you got this far, you win!\n"
          157  +</th1>

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

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

Added test/th1-variadic-proc.th1.

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

Added www/th1_argv.wiki.

            1  +<h1>TH1 "argv" API</h1>
            2  +
            3  +The "argv" API provides features for accessing command-line arguments
            4  +and GET/POST values. They (unfortunately) do not provide access to
            5  +POST data submitted in JSON mode (which fossil internally doesn't really
            6  +know about).
            7  +
            8  +Example usage:
            9  +
           10  +<nowiki><pre>
           11  +&lt;th1>
           12  +set argc [argv len]
           13  +set appName [argv at 0]
           14  +# Fetch --foo|-f argument:
           15  +set foo [argv getstr foo f "default value"]
           16  +&lt;th1>
           17  +</pre></nowiki>
           18  +
           19  +(Note that fossil does not actually care if an argument starts
           20  +with 1 or 2 dashes. The convention of using 1 for "short-form"
           21  +flags and 2 for "long-form" is purely historical.)
           22  +
           23  +The various subcommands are described below...
           24  +
           25  +<h2>len</h2>
           26  +
           27  +Returns the number of arguments.
           28  +
           29  +<nowiki><pre>
           30  +set argc [argv len]
           31  +</pre></nowiki>
           32  +
           33  +
           34  +<h2>at</h2>
           35  +
           36  +Fetches the argument at the given index (0-based).
           37  +
           38  +<nowiki><pre>
           39  +set arg [argv at 3]
           40  +</pre></nowiki>
           41  +
           42  +The fossil binary's name is stored in argument #0.
           43  +
           44  +<h2>getstr|string</h2>
           45  +
           46  +Searches for a CLI/GET/POST parameter. In CLI this function has some
           47  +non-intuitive behaviour inherited from fossil's internals: once a
           48  +flag/parameter is fetched, it is removed from the internal arguments
           49  +list, meaning that this function will never see it a second time.
           50  +
           51  +<nowiki><pre>
           52  +set something [argv string "something" "S" "default"]
           53  +</pre></nowiki>
           54  +
           55  +If no default value is provided, an error is triggered if the value is
           56  +not found.
           57  +
           58  +If you do not want to search for a short-form flag, set it to an empty
           59  +string.
           60  +
           61  +NOTE: flag checking does not work in CGI mode when using <em>upper-case</em>
           62  +flags (fossil treats upper-case names as environment variables).
           63  +
           64  +<h2>getbool|bool</h2>
           65  +
           66  +Works almost like <tt>getstr</tt> but searches for boolean flags. CLI boolean flags
           67  +have no explicit value, and are "true" if the are set at all.
           68  +
           69  +<nowiki><pre>
           70  +set doSomething [argv bool "do-something" "D" 0]
           71  +</pre></nowiki>
           72  +
           73  +<h2>getint|int</h2>
           74  +
           75  +Works almost like <tt>getstr</tt> but searches for integer flags.
           76  +
           77  +
           78  +<nowiki><pre>
           79  +set limit [argv int "limit" "L" 10]
           80  +</pre></nowiki>

Added www/th1_ob.wiki.

            1  +<h1>TH1 "ob" (Output Buffering) API</h1>
            2  +
            3  +The "ob" API mimics the
            4  +[http://php.net/manual/en/function.ob-start.php|PHP output buffering] fairly closely,
            5  +and provides these features:
            6  +
            7  +   *  Redirect output from <tt>puts</tt> and friends to a buffer.
            8  +   *  Fetch the buffer as a string or discard it (as you wish).
            9  +   *  Supports nesting buffering arbitrarily deep, but each level which gets opened must also be closed by the scripter.
           10  +
           11  +Example usage:
           12  +
           13  +<nowiki><pre>
           14  +&lt;th1>
           15  +puts "this is unbuffered"
           16  +ob push # or: ob start (same thing)
           17  +# all output until the next ob start|end gets collected
           18  +# in a buffer...
           19  +&lt;/th1>
           20  +
           21  +this is buffered
           22  +
           23  +&lt;th1>
           24  +puts "current buffer level = " [ob level] "\n"
           25  +puts "this part is also collected in the buffer."
           26  +
           27  +# Collect the buffer's contents:
           28  +set buf [ob get pop]
           29  +# That is equivalent to:
           30  +#  set buf [ob get]
           31  +#  ob pop
           32  +
           33  +puts "\nThis is now unbuffered, but we buffered: $buf\n"
           34  +&lt;/th1>
           35  +</pre></nowiki>
           36  +
           37  +The functions are summarized below...
           38  +
           39  +<h2>ob push|start</h2>
           40  +
           41  +<tt>push</tt> and <tt>start</tt> are aliases ("start" comes from the PHP API, but
           42  +"push" is probably more natural to those working with th1).
           43  +
           44  +<tt>ob start</tt> pushes a level of buffering onto the buffer stack, such that
           45  +future calls which generate output through the th1-internal mechanism will have it
           46  +transparently redirected to the current buffer.
           47  +
           48  +It is important that every call to <tt>ob start</tt> be followed up (eventually)
           49  +by either <tt>ob end</tt> or <tt>ob get end</tt>.
           50  +
           51  +<h2>ob pop|end</h2>
           52  +
           53  +<tt>pop</tt> and <tt>end</tt> are aliases ("end" comes from the PHP API, but
           54  +"pop" is probably more natural to those working with th1).
           55  +
           56  +This discards any current buffered contents and reverts the output state to
           57  +the one it had before the previous <tt>ob start</tt>. i.e. that might be another
           58  +buffering level or it might be the th1-normal output mechanism.
           59  +
           60  +The global resources associated with buffering are cleaned up when the
           61  +last buffering level is left (and re-created as needed when a new
           62  +level is started).
           63  +
           64  +<h2>ob clean</h2>
           65  +
           66  +This discards the current contents of the current buffer level but
           67  +does not change the buffer stack level.
           68  +
           69  +<h2>ob get</h2>
           70  +
           71  +This fetches the current contents as a string. It optionally accepts
           72  +either <tt>end</tt> (or its alias <tt>pop</tt>) or <tt>clean</tt>, in which cases it behaves like
           73  +either <tt>ob end|pop</tt> or <tt>ob clean</tt>, respectively, in addition
           74  +to returning the buffer contents. i.e. <tt>ob get clean</tt> will
           75  +fetch the contents and clean up the buffer, but does not change the
           76  +buffering level, whereas <tt>ob get end|pop</tt> pops the buffer off the
           77  +stack after fetching its contents.
           78  +
           79  +<h2>ob level</h2>
           80  +
           81  +Returns the current buffering level (0 if not buffering).
           82  +
           83  +<h2>ob flush</h2>
           84  +
           85  +It is not expected that this will be useful all that often, but for
           86  +the cases where it is, here's how it works: this behaves as if we
           87  +fetched the buffer state (<tt>ob get</tt>), reverted TH1 to its
           88  +previous output mechanism, push the buffer state to TH1, revert TH1
           89  +<em>back</em> to the current buffering state, and then clear the
           90  +current buffer contents (like <tt>ob clean</tt>). This does not change
           91  +the buffering level, though it temporarily behaves as if it does.
           92  +
           93  +In other words, this function pushes the current buffer contents to the
           94  +next-lower output mechanism (which may be another ob buffering level,
           95  +fossil's internal CGI output buffer, or it might be be
           96  +<tt>fwrite(stdout)</tt>).

Added www/th1_query.wiki.

            1  +<h1>TH1 "query" API</h1>
            2  +
            3  +The "query" API provides limited access to the fossil database.
            4  +It restricts usage to queries which return result columns (i.e.
            5  +<tt>SELECT</tt> and friends).
            6  +Example usage:
            7  +
            8  +<nowiki><pre>
            9  +&lt;th1>
           10  +catch {
           11  +    set stmt [query prepare "SELECT login, cap FROM user"]
           12  +    puts "stmt ID=$stmt\n"
           13  +    for {} {[query $stmt step]} {} {
           14  +        puts [query $stmt col string 0] " " [query $stmt col string 1] \n
           15  +    }
           16  +    query $stmt finalize
           17  +    return 0
           18  +} rc
           19  +if {0 != $rc} {
           20  +    puts "ERROR: $rc\n"
           21  +}
           22  +&lt;th1>
           23  +</pre></nowiki>
           24  +
           25  +The various subcommands are summarized in the following subsections, and here
           26  +are some notes regarding calling conventions:
           27  +
           28  +The (bind, col, step, finalize) functions accept their statement ID argument
           29  +either right after the "query" command or right after the final subcommand.
           30  +The following examples demonstrate this:
           31  +
           32  +<nowiki><pre>
           33  +query $stmt step
           34  +query step $stmt
           35  +
           36  +query $stmt finalize
           37  +query finalize $stmt
           38  +
           39  +query col string $stmt 1
           40  +query $stmt col string 1
           41  +
           42  +query bind string $stmt 1 "foo"
           43  +query $stmt bind string 1 "foo"
           44  +</pre></nowiki>
           45  +
           46  +The "prefered" form is:
           47  +
           48  +<nowiki><pre>
           49  +query StmtId command ...
           50  +</pre></nowiki>
           51  +
           52  +(Why, then, are both forms accepted? Because the "preferred" form only
           53  +evolved only after using the first form in script code.)
           54  +
           55  +
           56  +<h2>prepare</h2>
           57  +
           58  +This subcommand prepares a query for execution. It returns a statement handle
           59  +ID which must be passed to any other functions using the API.
           60  +
           61  +All prepared statements must be <tt>finalize</tt>d when they have outlived
           62  +their usefulness.
           63  +
           64  +<nowiki><pre>
           65  +set stmt [query prepare {SELECT ...}]
           66  +...
           67  +query $stmt finalize
           68  +</pre></nowiki>
           69  +
           70  +
           71  +<h2>finalize</h2>
           72  +
           73  +Releases all resources associated with the statement. Note that future
           74  +calls to <tt>prepare</tt> might re-use the same statement statement
           75  +ID.
           76  +
           77  +<nowiki><pre>
           78  +set stmt [query prepare "SELECT ..."]
           79  +...
           80  +query $stmt finalize
           81  +</pre></nowiki>
           82  +
           83  +
           84  +<h2>step</h2>
           85  +
           86  +This subcommand steps the result set by one row. It returns 0
           87  +at the end of the set, a positive value if a new row is available,
           88  +and throws for any other condition.
           89  +
           90  +<nowiki><pre>
           91  +for {} {[query $stmt step]} {} {
           92  +   puts [query $stmt col string 0] "\n"
           93  +}
           94  +</pre></nowiki>
           95  +
           96  +
           97  +<h2>reset</h2>
           98  +
           99  +Resets a query so that it can be executed again. This is only needed when
          100  +binding parameters in a loop - call it at the end of each loop iteration.
          101  +
          102  +<nowiki><pre>
          103  +query $stmt reset
          104  +query reset $stmt
          105  +</pre></nowiki>
          106  +
          107  +
          108  +<h2>bind xxx</h2>
          109  +
          110  +The <tt>bind xxx</tt> family of subcommands attach values to queries
          111  +before stepping through them. The subcommands include:
          112  +
          113  +   *  <tt>bind StmtId int Index Value</tt>
          114  +   *  <tt>bind StmtId double Index Value</tt>
          115  +   *  <tt>bind StmtId null Index</tt>
          116  +   *  <tt>bind StmtId string Index Value</tt>
          117  +
          118  +Note that all of those optionally accept the statement handle directly after
          119  +the "query" command (before the "col" subcommand). e.g.
          120  +<tt>query bind null $stmt 1</tt> and
          121  +<tt>query $stmt bind null 1</tt> are equivalent. They also accept the column index
          122  +either before or after the type name, e.g.
          123  +<tt>query $stmt bind 1 string ...</tt> and <tt>query $stmt bind string 1 ...</tt> are equivalent.
          124  +
          125  +
          126  +Achtung: the bind API uses 1-based indexes, just like SQL does.
          127  +
          128  +<nowiki><pre>
          129  +set stmt [query prepare "SELECT ... WHERE user=?"]
          130  +query $stmt bind int 1 drh
          131  +if {[query $stmt step]} {
          132  +   puts [query $stmt col string 0] "\n"
          133  +}
          134  +query $stmt finalize
          135  +</pre></nowiki>
          136  +
          137  +
          138  +<h2>col xxx</h2>
          139  +
          140  +The <tt>col xxx</tt> familys of subcommands are for fetching values and metadata from result rows.
          141  +
          142  +   *  <tt>col StmtId count</tt> Returns the number of result columns in the statement.
          143  +   *  <tt>col StmtId isnull Index</tt> Returns non-0 if the given column contains an SQL NULL value.
          144  +   *  <tt>col StmtId (double|int|string) Index</tt> Fetches a column's value as either a number or string.
          145  +   *  <tt>col StmtId time Index Format Modifiers</tt> Formats a time value. See below.
          146  +   *  <tt>col StmtId type Index</tt> Returns the given column's type as a value from the <tt>SQLITE_TYPENAME</tt> family of constants.
          147  +
          148  +Note that all of those optionally accept the statement handle directly after
          149  +the "query" command (before the "col" subcommand). e.g.
          150  +<tt>query $stmt col count</tt> and
          151  +<tt>query col count $stmt</tt> are equivalent. They also accept the column index
          152  +either before or after the type name, e.g.
          153  +<tt>query $stmt col 1 string</tt> and <tt>query $stmt col string 1</tt> are equivalent.
          154  +
          155  +Achtung: the col API uses 0-based indexes, just like SQL does.
          156  +
          157  +<h3>col time</h3>
          158  +
          159  +This function is a proxy for sqlite3's
          160  +<tt>[http://www.sqlite.org/lang_datefunc.html|strftime()]</tt> function. It is used like this:
          161  +
          162  +
          163  +<nowiki><pre>
          164  +query $stmt col time $index {%Y%m%d @ %H:%M:%S}
          165  +</pre></nowiki>
          166  +
          167  +Any remaining arguments are treated as "modifiers" and passed as-is to strfmtime. For example:
          168  +
          169  +<nowiki><pre>
          170  +query $stmt col time $index {%Y%m%d @ %H:%M:%S} {+5 years}
          171  +query $stmt col time $index %s unixepoch
          172  +</pre></nowiki>
          173  +
          174  +
          175  +
          176  +
          177  +<h2>strftime</h2>
          178  +
          179  +This works like <tt>col time</tt> (described below) but takes its
          180  +value from an arbitrary source specified by the 3rd argument.
          181  +
          182  +<nowiki><pre>
          183  +query strftime %s 1319211587 unixepoch
          184  +query strftime {%Y%m%d @ %H:%M:%S} [query $stmt col string 2] {+10 years}]
          185  +</pre></nowiki>
          186  +
          187  +<h2>Global Variables</h2>
          188  +
          189  +This API installs the following global variables, all of which correspond to
          190  +<tt>SQLITE_xxx</tt> constant values:
          191  +
          192  +   *  <tt>SQLITE_BLOB</tt>
          193  +   *  <tt>SQLITE_FLOAT</tt>
          194  +   *  <tt>SQLITE_INTEGER</tt>
          195  +   *  <tt>SQLITE_NULL</tt>
          196  +   *  <tt>SQLITE_TEXT</tt>
          197  +
          198  +These values are used only by the <tt>col type</tt> function. They can be
          199  +accessed from script code via <tt>$::SQLITE_xxx</tt>.