Fossil

Check-in [3167ff33]
Login

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

Overview
Comment:Refactored th1/sqlite bits to use Th_Data_Get/Set(), removed sqlite data from Th_Interp class. Other minor cleanups.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | th1-query-api
Files: files | file ages | folders
SHA1: 3167ff33f84ef8324c4a8c2af0cdbbb83494b08c
User & Date: stephan 2012-07-15 12:27:14
Context
2012-07-15
12:52
Made a few more functions static. th1 ob doc additions. check-in: 8027581c user: stephan tags: th1-query-api
12:27
Refactored th1/sqlite bits to use Th_Data_Get/Set(), removed sqlite data from Th_Interp class. Other minor cleanups. check-in: 3167ff33 user: stephan tags: th1-query-api
11:16
Refactord ob api to only swap out the Vtab output state, as opposed to the whole Vtab state (which includes the allocator). check-in: d9e0ee2f user: stephan tags: th1-query-api
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/th.c.

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
....
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
....
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
....
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
....
2919
2920
2921
2922
2923
2924
2925

2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940

2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951

2952

2953
2954
2955
2956
2957
2958
2959
....
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
....
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206



3207
3208

3209
3210

3211
3212
3213
3214
3215
3216
3217
3218
3219
  int isListMode;    /* True if thSplitList() should operate in "list" mode */
  Th_Hash * paGc;    /* Holds client-provided data owned by this
                        object. It would be more efficient to store
                        these in a list (we don't expect many
                        entries), but Th_Hash has the strong advantage
                        of being here and working.
                     */
#ifdef TH_USE_SQLITE
  struct {
    sqlite3_stmt ** aStmt;
    int nStmt;
  } stmt;           /* list of prepared statements */
#endif
};

/*
** Each TH command registered using Th_CreateCommand() is represented
** by an instance of the following structure stored in the Th_Interp.paCmd
** hash-table.
*/
................................................................................
  /* Delete any result currently stored in the interpreter. */
  Th_SetResult(interp, 0, 0);

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

#ifdef TH_USE_SQLITE
  {
    int i;
    sqlite3_stmt * st;
    for( i = 0; i < interp->stmt.nStmt; ++i ){
      st = interp->stmt.aStmt[i];
      if(NULL != st){
        fossil_warning("Auto-finalizing unfinalized query_prepare "
                       "statement id #%d: %s",
                       i+1, sqlite3_sql(st));
        Th_FinalizeStmt( interp, i+1 );
      }
    }
    Th_Free(interp, interp->stmt.aStmt);
  }
#endif
  
  /* Delete the interpreter structure itself. */
  Th_Free(interp, (void *)interp);
}

/* 
** Create a new interpreter.
................................................................................
  return 0;
 
}
void * Th_Data_Get( Th_Interp * interp, char const * key ){
  Th_HashEntry * e = interp->paGc
    ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
    : NULL;
  return e ? e->pData : NULL;
}

#ifdef TH_USE_SQLITE
int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
  int i, x;
  sqlite3_stmt * s;
  sqlite3_stmt ** list = interp->stmt.aStmt;
  for( i = 0; i < interp->stmt.nStmt; ++i ){
    s = list[i];
    if(NULL==s){
      list[i] = pStmt;
      return i+1;
    }
  }
  x = (interp->stmt.nStmt + 1) * 2;
  list = (sqlite3_stmt**)fossil_realloc( list, sizeof(sqlite3_stmt*)*x );
  for( i = interp->stmt.nStmt; i < x; ++i ){
    list[i] = NULL;
  }
  list[interp->stmt.nStmt] = pStmt;
  x = interp->stmt.nStmt;
  interp->stmt.nStmt = i;
  interp->stmt.aStmt = list;
  return x + 1;
}


int Th_FinalizeStmt(Th_Interp *interp, int stmtId){
  sqlite3_stmt * st;
  int rc = 0;
  assert( stmtId>0 && stmtId<=interp->stmt.nStmt );
  st = interp->stmt.aStmt[stmtId-1];
  if(NULL != st){
    interp->stmt.aStmt[stmtId-1] = NULL;
    sqlite3_finalize(st);
    return 0;
  }else{
    return 1;
  }
}

sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){
  return ((stmtId<1) || (stmtId > interp->stmt.nStmt))
    ? NULL
    : interp->stmt.aStmt[stmtId-1];
}

#endif
/* end TH_USE_SQLITE */


#ifdef TH_USE_OUTBUF
/* Reminder: the ob code "really" belongs in th_lang.c,
   but we need access to Th_Interp internals in order to
   swap out Th_Vtab parts for purposes of stacking layers
   of buffers.
................................................................................
}
#define Th_Vtab_Output_ob_m {                \
  Th_output_f_ob /*f*/, \
  NULL /*pState*/,\
  1/*enabled*/\
}
static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;
static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m;
static Th_Vtab_Output Th_Vtab_Output_ob = Th_Vtab_Output_ob_m;
static Th_Vtab_Output Th_Vtab_Output_empty = Th_Vtab_Output_empty_m;
#define Th_Ob_Man_KEY "Th_Ob_Man"
Th_Ob_Man * Th_ob_manager(Th_Interp *interp){
  void * rc = Th_Data_Get(interp, Th_Ob_Man_KEY );
  return rc
    ? ((Th_GcEntry*)rc)->pData
    : NULL;
}


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

................................................................................
  pMan->aBuf[pMan->cursor] = pBlob;
  pMan->aOutput[pMan->cursor] = pMan->interp->pVtab->out;
  pMan->interp->pVtab->out = Th_Vtab_Ob.out;
  pMan->interp->pVtab->out.pState = pMan;
  if( pOut ){
    *pOut = pBlob;
  }

  return TH_OK;
  error:
  if( pBlob ){
    Th_Free( pMan->interp, pBlob );
  }
  return TH_ERROR;
}



Blob * Th_ob_pop( Th_Ob_Man * pMan ){
  if( pMan->cursor < 0 ){
    return NULL;
  }else{
    Blob * rc;

    assert( pMan->nBuf > pMan->cursor );
    rc = pMan->aBuf[pMan->cursor];
    pMan->aBuf[pMan->cursor] = NULL;
    pMan->interp->pVtab->out = pMan->aOutput[pMan->cursor];
    pMan->aOutput[pMan->cursor] = Th_Vtab_Output_empty;
    if(-1 == --pMan->cursor){
      Th_Interp * interp = pMan->interp;
      Th_Free( pMan->interp, pMan->aBuf );
      Th_Free( pMan->interp, pMan->aOutput );
      *pMan = Th_Ob_Man_empty;
      pMan->interp = interp;

    }

    return rc;
  }
}

void Th_ob_cleanup( Th_Ob_Man * man ){
  Blob * b;
  while( (b = Th_ob_pop(man)) ){
................................................................................

static void finalizerObMan( Th_Interp * interp, void * p ){
  Th_Ob_Man * man = (Th_Ob_Man*)p;
  /*printf("finalizerObMan(%p,%p)\n", interp, p );*/
  if(man){
    assert( interp == man->interp );
    Th_ob_cleanup( man );
    if( man != &Th_Ob_Man_instance ){
      Th_Free( interp, man );
    }
  }
}

/*
** TH Syntax:
**
** ob clean|(end|pop)|flush|get|level|(start|push)
................................................................................
  static Th_Command_Reg aCommand[] = {
    {"ob",    ob_cmd,   0},
    {0,0,0}
  };
  rc = Th_register_commands( interp, aCommand );
  if(NULL == Th_ob_manager(interp)){
    Th_Ob_Man * pMan;
    pMan = 1
      ? &Th_Ob_Man_instance
      : Th_Malloc(interp, sizeof(Th_Ob_Man));



    /* *pMan = Th_Ob_Man_empty;*/
    pMan->interp = interp;

    Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
    assert( NULL != Th_ob_manager(interp) );

  }
  return rc;
}

#undef Th_Ob_Man_empty_m
#undef Th_Ob_Man_KEY
#endif
/* end TH_USE_OUTBUF */








<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<




|
<
<
<







 







>








<
<





>











>

>







 







<
|
<







 







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









42
43
44
45
46
47
48






49
50
51
52
53
54
55
....
1723
1724
1725
1726
1727
1728
1729
















1730
1731
1732
1733
1734
1735
1736
....
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749















































2750
2751
2752
2753
2754
2755
2756
....
2769
2770
2771
2772
2773
2774
2775

2776
2777
2778
2779
2780



2781
2782
2783
2784
2785
2786
2787
....
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861


2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
....
3079
3080
3081
3082
3083
3084
3085

3086

3087
3088
3089
3090
3091
3092
3093
....
3124
3125
3126
3127
3128
3129
3130


3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
  int isListMode;    /* True if thSplitList() should operate in "list" mode */
  Th_Hash * paGc;    /* Holds client-provided data owned by this
                        object. It would be more efficient to store
                        these in a list (we don't expect many
                        entries), but Th_Hash has the strong advantage
                        of being here and working.
                     */






};

/*
** Each TH command registered using Th_CreateCommand() is represented
** by an instance of the following structure stored in the Th_Interp.paCmd
** hash-table.
*/
................................................................................
  /* Delete any result currently stored in the interpreter. */
  Th_SetResult(interp, 0, 0);

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

















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

/* 
** Create a new interpreter.
................................................................................
  return 0;
 
}
void * Th_Data_Get( Th_Interp * interp, char const * key ){
  Th_HashEntry * e = interp->paGc
    ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
    : NULL;
  return e ? ((Th_GcEntry*)e->pData)->pData : NULL;
}


















































#ifdef TH_USE_OUTBUF
/* Reminder: the ob code "really" belongs in th_lang.c,
   but we need access to Th_Interp internals in order to
   swap out Th_Vtab parts for purposes of stacking layers
   of buffers.
................................................................................
}
#define Th_Vtab_Output_ob_m {                \
  Th_output_f_ob /*f*/, \
  NULL /*pState*/,\
  1/*enabled*/\
}
static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;

static Th_Vtab_Output Th_Vtab_Output_ob = Th_Vtab_Output_ob_m;
static Th_Vtab_Output Th_Vtab_Output_empty = Th_Vtab_Output_empty_m;
#define Th_Ob_Man_KEY "Th_Ob_Man"
Th_Ob_Man * Th_ob_manager(Th_Interp *interp){
  return (Th_Ob_Man*) Th_Data_Get(interp, Th_Ob_Man_KEY );



}


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

................................................................................
  pMan->aBuf[pMan->cursor] = pBlob;
  pMan->aOutput[pMan->cursor] = pMan->interp->pVtab->out;
  pMan->interp->pVtab->out = Th_Vtab_Ob.out;
  pMan->interp->pVtab->out.pState = pMan;
  if( pOut ){
    *pOut = pBlob;
  }
  /*printf( "push: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
  return TH_OK;
  error:
  if( pBlob ){
    Th_Free( pMan->interp, pBlob );
  }
  return TH_ERROR;
}



Blob * Th_ob_pop( Th_Ob_Man * pMan ){
  if( pMan->cursor < 0 ){
    return NULL;
  }else{
    Blob * rc;
    /*printf( "pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
    assert( pMan->nBuf > pMan->cursor );
    rc = pMan->aBuf[pMan->cursor];
    pMan->aBuf[pMan->cursor] = NULL;
    pMan->interp->pVtab->out = pMan->aOutput[pMan->cursor];
    pMan->aOutput[pMan->cursor] = Th_Vtab_Output_empty;
    if(-1 == --pMan->cursor){
      Th_Interp * interp = pMan->interp;
      Th_Free( pMan->interp, pMan->aBuf );
      Th_Free( pMan->interp, pMan->aOutput );
      *pMan = Th_Ob_Man_empty;
      pMan->interp = interp;
      assert(-1 == pMan->cursor);
    }
    /*printf( "post-pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
    return rc;
  }
}

void Th_ob_cleanup( Th_Ob_Man * man ){
  Blob * b;
  while( (b = Th_ob_pop(man)) ){
................................................................................

static void finalizerObMan( Th_Interp * interp, void * p ){
  Th_Ob_Man * man = (Th_Ob_Man*)p;
  /*printf("finalizerObMan(%p,%p)\n", interp, p );*/
  if(man){
    assert( interp == man->interp );
    Th_ob_cleanup( man );

    Th_Free( interp, man );

  }
}

/*
** TH Syntax:
**
** ob clean|(end|pop)|flush|get|level|(start|push)
................................................................................
  static Th_Command_Reg aCommand[] = {
    {"ob",    ob_cmd,   0},
    {0,0,0}
  };
  rc = Th_register_commands( interp, aCommand );
  if(NULL == Th_ob_manager(interp)){
    Th_Ob_Man * pMan;


    pMan = Th_Malloc(interp, sizeof(Th_Ob_Man));
    if(!pMan){
      rc = TH_ERROR;
    }else{
      *pMan = Th_Ob_Man_empty;
      pMan->interp = interp;
      assert( -1 == pMan->cursor );
      Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
      assert( NULL != Th_ob_manager(interp) );
    }
  }
  return rc;
}

#undef Th_Ob_Man_empty_m
#undef Th_Ob_Man_KEY
#endif
/* end TH_USE_OUTBUF */

Changes to src/th.h.

1
2




3
4
5
6
7
8
9
10
11
12
13








14
15
16
17
18
19
20
21
22
23
24
25
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#include "config.h"





#define TH_USE_SQLITE
#ifdef TH_USE_SQLITE
#include "sqlite3.h"
#endif

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








/*#undef TH_USE_OUTBUF*/
#ifndef INTERFACE
#include "blob.h"
#endif


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

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

#ifdef TH_USE_SQLITE

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

/*
** Expects stmtId to be a statement identifier returned by
** Th_AddStmt(). On success, finalizes the statement and returns 0.
** On error (statement not found) non-0 is returned. After this
** call, some subsequent call to Th_AddStmt() may return the
** same statement ID.
*/
int Th_FinalizeStmt(Th_Interp *interp, int stmtId);

/*
** Fetches the statement with the given ID, as returned by
** Th_AddStmt(). Returns NULL if stmtId does not refer (or no longer
** refers) to a statement added via Th_AddStmt().
*/
sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId);
#endif

#ifdef TH_USE_OUTBUF
/*
** Manager of a stack of Blob objects for output buffering.
*/
struct Th_Ob_Man {
  Blob ** aBuf;        /* Stack of Blobs */


>
>
>
>

<
<
<







>
>
>
>
>
>
>
>
|



|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1
2
3
4
5
6
7



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
310
311
312
313
314
315
316






























317
318
319
320
321
322
323
#include "config.h"

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




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

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

#ifdef TH_USE_OUTBUF
#ifndef INTERFACE
#include "blob.h"
#endif
#endif

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

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
































#ifdef TH_USE_OUTBUF
/*
** Manager of a stack of Blob objects for output buffering.
*/
struct Th_Ob_Man {
  Blob ** aBuf;        /* Stack of Blobs */

Changes to src/th_main.c.

15
16
17
18
19
20
21
22
23






24
25
26
27
28
29
30
...
535
536
537
538
539
540
541

542
543


544
545
546
547
548
549
550
551
552
553
554
...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
...
831
832
833
834
835
836
837
838
839


840












































































































841
842
843
844
845
846
847
....
1542
1543
1544
1545
1546
1547
1548

1549
1550
1551
1552
1553
1554
1555
1556
1557

1558
1559
1560
1561
1562
1563
1564
1565
....
1571
1572
1573
1574
1575
1576
1577

1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598












1599
1600
1601
1602
1603
1604
1605
....
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
....
1847
1848
1849
1850
1851
1852
1853
1854

1855
1856
1857
1858
**
*******************************************************************************
**
** This file contains an interface between the TH scripting language
** (an independent project) and fossil.
*/
#include "config.h"

#include "th_main.h"







/*#include "th_main.h"*/
/*
** Global variable counting the number of outstanding calls to malloc()
** made by the th1 implementation. This is used to catch memory leaks
** in the interpreter. Obviously, it also means th1 is not threadsafe.
*/
................................................................................
    if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
  }
  Th_SetResult(interp, g.zRepositoryName, -1);
  return TH_OK;
}



extern const char *find_option(const char *zLong, const char *zShort, int hasArg);



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

#define TH_USE_ARGV
#ifdef TH_USE_ARGV
/*
** TH Syntax:
**
** argv_getat Index
**
** Returns the raw argument at the given index, throwing if
** out of bounds.
................................................................................
  Th_register_commands( interp, aCommand );
}

#endif
/* end TH_USE_ARGV */

#ifdef TH_USE_SQLITE
#ifndef INTERFACE
#include "blob.h"


#endif













































































































/*
** TH Syntax:
**
** query_prepare SQL
**
** Returns an opaque statement identifier.
................................................................................
static int queryTopLevelCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){

  static Th_SubCommand aSub[] = {
    {"bind",        queryBindTopLevelCmd},
    {"col",         queryColTopLevelCmd},
    {"step",        queryStepCmd},
    {"finalize",    queryFinalizeCmd},
    {"prepare",     queryPrepareCmd},
    {"strftime",    queryStrftimeCmd},
    {0, 0}
  };

  Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
}


int th_register_sqlite(Th_Interp *interp){
  enum { BufLen = 100 };
  char buf[BufLen];
  int i, l;
................................................................................
  SET(SQLITE_FLOAT);
  SET(SQLITE_INTEGER);
  SET(SQLITE_NULL);
  SET(SQLITE_OK);
  SET(SQLITE_ROW);
  SET(SQLITE_TEXT);
#undef SET

  static Th_Command_Reg aCommand[] = {
    {"query",             queryTopLevelCmd,  0},
#if 0
    {"query_bind_int",    queryBindIntCmd,   0},
    {"query_bind_double", queryBindDoubleCmd,0},
    {"query_bind_null",   queryBindNullCmd,  0},
    {"query_bind_string", queryBindStringCmd,0},
    {"query_col_count",   queryColCountCmd,  0},
    {"query_col_double",  queryColDoubleCmd, 0},
    {"query_col_int",     queryColIntCmd,    0},
    {"query_col_is_null", queryColIsNullCmd, 0},
    {"query_col_name",    queryColNameCmd,   0},
    {"query_col_string",  queryColStringCmd, 0},
    {"query_col_type",    queryColTypeCmd,   0},
    {"query_finalize",    queryFinalizeCmd,  0},
    {"query_prepare",     queryPrepareCmd,   0},
    {"query_step",        queryStepCmd,      0},
#endif
    {0, 0, 0}
  };
  Th_register_commands( interp, aCommand );












}

#endif
/* end TH_USE_SQLITE */

int Th_register_commands( Th_Interp * interp,
                           Th_Command_Reg const * aCommand ){
................................................................................
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==TH_ERROR ){
    sendText(g.interp, "<hr><p class=\"thmainError\">ERROR: ", -1, 0);
    zResult = (char*)Th_GetResult(g.interp, &n);
    sendText(g.interp, (char*)zResult, n, 1);
    sendText(g.interp, "</p>", -1, 0);
  }else{
    sendText(g.interp, z, i, 0);
  }
  return rc;
}

................................................................................
  if( g.argc<3 ){
    usage("FILE");
    assert(0 && "usage() does not return");
  }
  blob_zero(&in);
  db_open_config(0); /* Needed for global "tcl" setting. */
#ifdef TH_USE_SQLITE
  db_find_and_open_repository(OPEN_ANY_SCHEMA,0) /* for query_xxx API. */;

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







<

>
>
>
>
>
>







 







>
|
<
>
>



|







 







|
|







 







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







 







>









>
|







 







>


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


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







 







|
|







 







|
>




15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
540
541
542
543
544
545
546
547
548

549
550
551
552
553
554
555
556
557
558
559
560
561
...
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
...
838
839
840
841
842
843
844


845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
....
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
....
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
















1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
....
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
....
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
**
*******************************************************************************
**
** This file contains an interface between the TH scripting language
** (an independent project) and fossil.
*/
#include "config.h"

#include "th_main.h"
#ifndef INTERFACE
#include "blob.h"
#endif
#ifdef TH_USE_SQLITE
#include "sqlite3.h"
#endif

/*#include "th_main.h"*/
/*
** Global variable counting the number of outstanding calls to malloc()
** made by the th1 implementation. This is used to catch memory leaks
** in the interpreter. Obviously, it also means th1 is not threadsafe.
*/
................................................................................
    if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
  }
  Th_SetResult(interp, g.zRepositoryName, -1);
  return TH_OK;
}


#ifdef TH_USE_ARGV
extern const char *find_option(const char *zLong,

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



/*
** TH Syntax:
**
** argv_getat Index
**
** Returns the raw argument at the given index, throwing if
** out of bounds.
................................................................................
  Th_register_commands( interp, aCommand );
}

#endif
/* end TH_USE_ARGV */

#ifdef TH_USE_SQLITE



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

/*
** Expects stmtId to be a statement identifier returned by
** Th_AddStmt(). On success, finalizes the statement and returns 0.
** On error (statement not found) non-0 is returned. After this
** call, some subsequent call to Th_AddStmt() may return the
** same statement ID.
*/
int Th_FinalizeStmt(Th_Interp *interp, int stmtId);

/*
** Fetches the statement with the given ID, as returned by
** Th_AddStmt(). Returns NULL if stmtId does not refer (or no longer
** refers) to a statement added via Th_AddStmt().
*/
sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId);


struct Th_Sqlite {
  sqlite3_stmt ** aStmt;
  int nStmt;
};
#define Th_Sqlite_KEY "Th_Sqlite"
typedef struct Th_Sqlite Th_Sqlite;

static Th_Sqlite * Th_sqlite_manager( Th_Interp * interp ){
  void * p = Th_Data_Get( interp, Th_Sqlite_KEY );
  return p ? (Th_Sqlite*)p : NULL;
}

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


int Th_FinalizeStmt(Th_Interp *interp, int stmtId){
  Th_Sqlite * sq = Th_sqlite_manager(interp);
  sqlite3_stmt * st;
  int rc = 0;
  assert( stmtId>0 && stmtId<=sq->nStmt );
  st = sq->aStmt[stmtId-1];
  if(NULL != st){
    sq->aStmt[stmtId-1] = NULL;
    sqlite3_finalize(st);
    return 0;
  }else{
    return 1;
  }
}

sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){
  Th_Sqlite * sq = Th_sqlite_manager(interp);
  return ((stmtId<1) || (stmtId > sq->nStmt))
    ? NULL
    : sq->aStmt[stmtId-1];
}


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


/*
** TH Syntax:
**
** query_prepare SQL
**
** Returns an opaque statement identifier.
................................................................................
static int queryTopLevelCmd(
  Th_Interp *interp,
  void *ctx, 
  int argc, 
  const char **argv, 
  int *argl
){
  Th_Sqlite * sq = Th_sqlite_manager(interp);
  static Th_SubCommand aSub[] = {
    {"bind",        queryBindTopLevelCmd},
    {"col",         queryColTopLevelCmd},
    {"step",        queryStepCmd},
    {"finalize",    queryFinalizeCmd},
    {"prepare",     queryPrepareCmd},
    {"strftime",    queryStrftimeCmd},
    {0, 0}
  };
  assert( NULL != sq );
  Th_CallSubCommand2( interp, sq, argc, argv, argl, aSub );
}


int th_register_sqlite(Th_Interp *interp){
  enum { BufLen = 100 };
  char buf[BufLen];
  int i, l;
................................................................................
  SET(SQLITE_FLOAT);
  SET(SQLITE_INTEGER);
  SET(SQLITE_NULL);
  SET(SQLITE_OK);
  SET(SQLITE_ROW);
  SET(SQLITE_TEXT);
#undef SET
  int rc = TH_OK;
  static Th_Command_Reg aCommand[] = {
    {"query",             queryTopLevelCmd,  0},
















    {0, 0, 0}
  };
  rc = Th_register_commands( interp, aCommand );
  if(TH_OK==rc){
    Th_Sqlite * sq = Th_Malloc(interp, sizeof(Th_Sqlite));
    if(!sq){
      rc = TH_ERROR;
    }else{
      assert( NULL == sq->aStmt );
      assert( 0 == sq->nStmt );
      Th_Data_Set( interp, Th_Sqlite_KEY, sq, finalizerSqlite );
      assert( sq == Th_sqlite_manager(interp) );
    }
  }
  return rc;
}

#endif
/* end TH_USE_SQLITE */

int Th_register_commands( Th_Interp * interp,
                           Th_Command_Reg const * aCommand ){
................................................................................
      i = 0;
    }else{
      i++;
    }
  }
  if( rc==TH_ERROR ){
    sendText(g.interp, "<hr><p class=\"thmainError\">ERROR: ", -1, 0);
    zResult = Th_GetResult(g.interp, &n);
    sendText(g.interp, zResult, n, 1);
    sendText(g.interp, "</p>", -1, 0);
  }else{
    sendText(g.interp, z, i, 0);
  }
  return rc;
}

................................................................................
  if( g.argc<3 ){
    usage("FILE");
    assert(0 && "usage() does not return");
  }
  blob_zero(&in);
  db_open_config(0); /* Needed for global "tcl" setting. */
#ifdef TH_USE_SQLITE
  db_find_and_open_repository(OPEN_ANY_SCHEMA,0)
    /* required for th1 query API. */;
#endif
  blob_read_from_file(&in, g.argv[2]);
  Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT);
}

Changes to www/th1_argv.wiki.

1
2
3
4

5
6
7
8
9
10
11
<h1>TH1 "argv" API</h1>

The "argv" API provides features for accessing command-line arguments
and GET/POST values.


Example usage:

<nowiki><pre>
&lt;th1>
set argc [argv len]
set appName [argv at 0]



|
>







1
2
3
4
5
6
7
8
9
10
11
12
<h1>TH1 "argv" API</h1>

The "argv" API provides features for accessing command-line arguments
and GET/POST values. They (unfortunately) do not provide access to
POST data submitted in JSON mode.

Example usage:

<nowiki><pre>
&lt;th1>
set argc [argv len]
set appName [argv at 0]