Fossil SCM

Cleanups requested by DRH, plus some incidental consistency/maintenance-related cleanups found along the way.

stephan 2012-07-16 18:56 th1-query-api
Commit c25a5d199b7dd5276766e196ce9f26ef9a4dc6d1
5 files changed +1 -1 +136 -108 +216 -73 +1 -1 +237 -151
+1 -1
--- src/blob.c
+++ src/blob.c
@@ -20,11 +20,11 @@
2020
*/
2121
#include "config.h"
2222
#include <zlib.h>
2323
#include "blob.h"
2424
#if INTERFACE
25
-typedef struct Blob Blob;
25
+
2626
/*
2727
** A Blob can hold a string or a binary object of arbitrary size. The
2828
** size changes as necessary.
2929
*/
3030
struct Blob {
3131
--- src/blob.c
+++ src/blob.c
@@ -20,11 +20,11 @@
20 */
21 #include "config.h"
22 #include <zlib.h>
23 #include "blob.h"
24 #if INTERFACE
25 typedef struct Blob Blob;
26 /*
27 ** A Blob can hold a string or a binary object of arbitrary size. The
28 ** size changes as necessary.
29 */
30 struct Blob {
31
--- src/blob.c
+++ src/blob.c
@@ -20,11 +20,11 @@
20 */
21 #include "config.h"
22 #include <zlib.h>
23 #include "blob.h"
24 #if INTERFACE
25
26 /*
27 ** A Blob can hold a string or a binary object of arbitrary size. The
28 ** size changes as necessary.
29 */
30 struct Blob {
31
+136 -108
--- src/th.c
+++ src/th.c
@@ -7,37 +7,30 @@
77
#include "th.h"
88
#include <string.h>
99
#include <assert.h>
1010
#include <stdio.h> /* FILE class */
1111
12
-#ifdef TH_ENABLE_OUTBUF
13
-struct Th_Ob_Man {
14
- Blob ** aBuf; /* Stack of Blobs */
15
- int nBuf; /* Number of blobs */
16
- int cursor; /* Current level (-1=not active) */
17
- Th_Interp * interp; /* The associated interpreter */
18
- Th_Vtab_Output * aOutput
19
- /* Stack of output routines corresponding
20
- to the current buffering level.
21
- Has nBuf entries.
22
- */;
23
-};
24
-#endif
25
-
26
-extern void *fossil_realloc(void *p, size_t n);
27
-static void * th_fossil_realloc(void *p, unsigned int n){
28
- return fossil_realloc( p, n );
29
-}
30
-static int Th_output_f_ob( char const * zData, int len, void * pState );
31
-static void Th_output_dispose_ob( void * pState );
12
+/*
13
+** Th_Output_f() impl which redirects output to a Th_Ob_Manager.
14
+*/
15
+static int Th_Output_f_ob( char const * zData, int len, void * pState );
16
+
17
+/*
18
+** Th_Output::dispose() impl which requires pState to be-a Th_Ob_Manager.
19
+*/
20
+static void Th_Output_dispose_ob( void * pState );
21
+
3222
typedef struct Th_Command Th_Command;
3323
typedef struct Th_Frame Th_Frame;
3424
typedef struct Th_Variable Th_Variable;
3525
36
-const Th_Vtab_Output Th_Vtab_Output_FILE = {
37
- Th_output_f_FILE /* write() */,
38
- Th_output_dispose_FILE /* dispose() */,
26
+/*
27
+** Shared instance. See th.h for the docs.
28
+*/
29
+const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE = {
30
+ Th_Output_f_FILE /* write() */,
31
+ Th_Output_dispose_FILE /* dispose() */,
3932
NULL /*pState*/,
4033
1/*enabled*/
4134
};
4235
4336
/*
@@ -60,11 +53,11 @@
6053
Th_Hash *paCmd; /* Table of registered commands */
6154
Th_Frame *pFrame; /* Current execution frame */
6255
int isListMode; /* True if thSplitList() should operate in "list" mode */
6356
Th_Hash * paGc; /* Holds client-provided data owned by this
6457
object. It would be more efficient to store
65
- these in a list (we don't expect many
58
+ these in a list (because we don't expect many
6659
entries), but Th_Hash has the strong advantage
6760
of being here and working.
6861
*/
6962
};
7063
@@ -1449,11 +1442,11 @@
14491442
void *p = pInterp->pVtab->xRealloc(z, nByte);
14501443
return p;
14511444
}
14521445
14531446
1454
-int Th_Vtab_output( Th_Vtab *vTab, char const * zData, int nData ){
1447
+int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int nData ){
14551448
if(!vTab->out.write){
14561449
return -1;
14571450
}else if(!vTab->out.enabled){
14581451
return 0;
14591452
}else{
@@ -1460,22 +1453,30 @@
14601453
return vTab->out.write( zData, nData, vTab->out.pState );
14611454
}
14621455
}
14631456
14641457
1465
-int Th_output( Th_Interp *pInterp, char const * zData, int nData ){
1466
- return Th_Vtab_output( pInterp->pVtab, zData, nData );
1458
+int Th_Output( Th_Interp *pInterp, char const * zData, int nData ){
1459
+ return Th_Vtab_Output( pInterp->pVtab, zData, nData );
1460
+}
1461
+
1462
+void Th_OutputEnable( Th_Interp *pInterp, char flag ){
1463
+ pInterp->pVtab->out.enabled = flag;
1464
+}
1465
+
1466
+char Th_OutputEnabled( Th_Interp *pInterp ){
1467
+ return pInterp->pVtab->out.enabled ? 1 : 0;
14671468
}
14681469
1469
-int Th_output_f_FILE( char const * zData, int nData, void * pState ){
1470
+int Th_Output_f_FILE( char const * zData, int nData, void * pState ){
14701471
FILE * dest = pState ? (FILE*)pState : stdout;
14711472
int rc = (int)fwrite(zData, 1, nData, dest);
14721473
fflush(dest);
14731474
return rc;
14741475
}
14751476
1476
-void Th_output_dispose_FILE( void * pState ){
1477
+void Th_Output_dispose_FILE( void * pState ){
14771478
FILE * f = pState ? (FILE*)pState : NULL;
14781479
if(f
14791480
&& (f != stdout)
14801481
&& (f != stderr)
14811482
&& (f != stdin)){
@@ -1745,10 +1746,15 @@
17451746
if( interp->paGc ){
17461747
Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
17471748
Th_HashDelete(interp, interp->paGc);
17481749
interp->paGc = NULL;
17491750
}
1751
+
1752
+ /* Clean up the output abstraction. */
1753
+ if( interp->pVtab && interp->pVtab->out.dispose ){
1754
+ interp->pVtab->out.dispose( interp->pVtab->out.pState );
1755
+ }
17501756
17511757
/* Delete the contents of the global frame. */
17521758
thPopFrame(interp);
17531759
17541760
/* Delete any result currently stored in the interpreter. */
@@ -2444,11 +2450,10 @@
24442450
}
24452451
24462452
#ifndef LONGDOUBLE_TYPE
24472453
# define LONGDOUBLE_TYPE long double
24482454
#endif
2449
-/*typedef char u8;*/
24502455
24512456
24522457
/*
24532458
** Return TRUE if z is a pure numeric string. Return FALSE if the
24542459
** string contains any character which is not part of a number. If
@@ -2746,11 +2751,11 @@
27462751
*z = '\0';
27472752
return Th_SetResult(interp, zBuf, -1);
27482753
}
27492754
27502755
2751
-int Th_Data_Set( Th_Interp * interp, char const * key,
2756
+int Th_SetData( Th_Interp * interp, char const * key,
27522757
void * pData,
27532758
void (*finalizer)( Th_Interp *, void * ) ){
27542759
Th_HashEntry * pEnt;
27552760
Th_GcEntry * pGc;
27562761
if(NULL == interp->paGc){
@@ -2769,95 +2774,120 @@
27692774
pGc->pData = pData;
27702775
pGc->xDel = finalizer;
27712776
return 0;
27722777
27732778
}
2774
-void * Th_Data_Get( Th_Interp * interp, char const * key ){
2779
+void * Th_GetData( Th_Interp * interp, char const * key ){
27752780
Th_HashEntry * e = interp->paGc
27762781
? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
27772782
: NULL;
27782783
return e ? ((Th_GcEntry*)e->pData)->pData : NULL;
27792784
}
27802785
2786
+int Th_RegisterCommands( Th_Interp * interp,
2787
+ Th_Command_Reg const * aCommand ){
2788
+ int i;
2789
+ int rc = TH_OK;
2790
+ for(i=0; (TH_OK==rc) && aCommand[i].zName; ++i){
2791
+ if ( !aCommand[i].zName ) break;
2792
+ else if( !aCommand[i].xProc ) continue;
2793
+ else{
2794
+ rc = Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
2795
+ aCommand[i].pContext, 0);
2796
+ }
2797
+ }
2798
+ return rc;
2799
+}
2800
+
27812801
27822802
2783
-#ifdef TH_ENABLE_OUTBUF
2784
-/* Reminder: the ob code "really" belongs in th_lang.c,
2785
- but we need access to Th_Interp internals in order to
2786
- swap out Th_Vtab parts for purposes of stacking layers
2787
- of buffers.
2803
+#ifdef TH_ENABLE_OB
2804
+/* Reminder: the ob code "really" belongs in th_lang.c or th_main.c,
2805
+ but it needs access to Th_Interp::pVtab in order to swap out
2806
+ Th_Vtab_OutputMethods parts for purposes of stacking layers of
2807
+ buffers. We could add access to it via the public interface,
2808
+ but that didn't seem appropriate.
27882809
*/
2810
+
2811
+
2812
+/* Empty-initialized Th_Ob_Manager instance. */
27892813
#define Th_Ob_Man_empty_m { \
27902814
NULL/*aBuf*/, \
27912815
0/*nBuf*/, \
27922816
-1/*cursor*/, \
27932817
NULL/*interp*/, \
27942818
NULL/*aOutput*/ \
27952819
}
27962820
2797
-/*
2798
-** Vtab impl for the ob buffering layer.
2799
-*/
2800
-#define Th_Vtab_Output_empty_m { \
2821
+/* Empty-initialized Th_Vtab_OutputMethods instance. */
2822
+#define Th_Vtab_OutputMethods_empty_m { \
28012823
NULL /* write() */, \
28022824
NULL /* dispose() */, \
28032825
NULL /*pState*/,\
28042826
1/*enabled*/\
28052827
}
2806
-#define Th_Vtab_Output_ob_m { \
2807
- Th_output_f_ob /*write()*/, \
2808
- Th_output_dispose_ob /* dispose() */, \
2828
+/* Vtab_OutputMethods instance initialized for OB support. */
2829
+#define Th_Vtab_OutputMethods_ob_m { \
2830
+ Th_Output_f_ob /*write()*/, \
2831
+ Th_Output_dispose_ob /* dispose() */, \
28092832
NULL /*pState*/,\
28102833
1/*enabled*/\
28112834
}
2812
-static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;
2813
-static Th_Vtab_Output Th_Vtab_Output_ob = Th_Vtab_Output_ob_m;
2814
-static Th_Vtab_Output Th_Vtab_Output_empty = Th_Vtab_Output_empty_m;
2815
-#define Th_Ob_Man_KEY "Th_Ob_Man"
2816
-Th_Ob_Man * Th_ob_manager(Th_Interp *interp){
2817
- return (Th_Ob_Man*) Th_Data_Get(interp, Th_Ob_Man_KEY );
2835
+/* Empty-initialized Th_Vtab_Man instance. */
2836
+static const Th_Ob_Manager Th_Ob_Man_empty = Th_Ob_Man_empty_m;
2837
+/* Empty-initialized Th_Vtab_OutputMethods instance. */
2838
+static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_empty = Th_Vtab_OutputMethods_empty_m;
2839
+/* Th_Vtab_OutputMethods instance initialized for OB support. */
2840
+static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_ob = Th_Vtab_OutputMethods_ob_m;
2841
+
2842
+/*
2843
+** Internal key for Th_Set/GetData(), for storing a Th_Ob_Manager instance.
2844
+*/
2845
+#define Th_Ob_Man_KEY "Th_Ob_Manager"
2846
+
2847
+Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *interp){
2848
+ return (Th_Ob_Manager*) Th_GetData(interp, Th_Ob_Man_KEY );
28182849
}
28192850
2820
-Blob * Th_ob_current( Th_Ob_Man * pMan ){
2851
+Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan ){
28212852
return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
28222853
}
28232854
28242855
28252856
/*
2826
-** Th_output_f() impl which expects pState to be (Th_Ob_Man*).
2857
+** Th_Output_f() impl which expects pState to be (Th_Ob_Manager*).
28272858
** (zData,len) are appended to pState's current output buffer.
28282859
*/
2829
-int Th_output_f_ob( char const * zData, int len, void * pState ){
2830
- Th_Ob_Man * pMan = (Th_Ob_Man*)pState;
2831
- Blob * b = Th_ob_current( pMan );
2860
+int Th_Output_f_ob( char const * zData, int len, void * pState ){
2861
+ Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
2862
+ Blob * b = Th_Ob_GetCurrentBuffer( pMan );
28322863
assert( NULL != pMan );
28332864
assert( b );
28342865
blob_append( b, zData, len );
28352866
return len;
28362867
}
28372868
2838
-static void Th_output_dispose_ob( void * pState ){
2869
+static void Th_Output_dispose_ob( void * pState ){
28392870
/* possible todo: move the cleanup logic from
2840
- Th_ob_pop() to here? */
2841
- /*printf("disposing() ob vtab.\n"); */
2871
+ Th_Ob_Pop() to here? */
28422872
#if 0
2843
- Th_Ob_Man * pMan = (Th_Ob_Man*)pState;
2844
- Blob * b = Th_ob_current( pMan );
2873
+ Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
2874
+ Blob * b = Th_Ob_GetCurrentBuffer( pMan );
28452875
assert( NULL != pMan );
28462876
assert( b );
28472877
#endif
28482878
}
28492879
28502880
28512881
2852
-int Th_ob_push( Th_Ob_Man * pMan,
2853
- Th_Vtab_Output const * pWriter,
2882
+int Th_Ob_Push( Th_Ob_Manager * pMan,
2883
+ Th_Vtab_OutputMethods const * pWriter,
28542884
Blob ** pOut ){
28552885
Blob * pBlob;
28562886
int x, i;
28572887
if( NULL == pWriter ){
2858
- pWriter = &Th_Vtab_Output_ob;
2888
+ pWriter = &Th_Vtab_OutputMethods_ob;
28592889
}
28602890
assert( NULL != pMan->interp );
28612891
pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob));
28622892
*pBlob = empty_blob;
28632893
@@ -2871,17 +2901,17 @@
28712901
void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
28722902
if(NULL==re){
28732903
goto error;
28742904
}
28752905
pMan->aBuf = (Blob **)re;
2876
- re = Th_Realloc( pMan->interp, pMan->aOutput, x * sizeof(Th_Vtab_Output) );
2906
+ re = Th_Realloc( pMan->interp, pMan->aOutput, x * sizeof(Th_Vtab_OutputMethods) );
28772907
if(NULL==re){
28782908
goto error;
28792909
}
2880
- pMan->aOutput = (Th_Vtab_Output*)re;
2910
+ pMan->aOutput = (Th_Vtab_OutputMethods*)re;
28812911
for( i = pMan->nBuf; i < x; ++i ){
2882
- pMan->aOutput[i] = Th_Vtab_Output_empty;
2912
+ pMan->aOutput[i] = Th_Vtab_OutputMethods_empty;
28832913
pMan->aBuf[i] = NULL;
28842914
}
28852915
pMan->nBuf = x;
28862916
}
28872917
assert( pMan->nBuf > pMan->cursor );
@@ -2892,65 +2922,62 @@
28922922
pMan->interp->pVtab->out = *pWriter;
28932923
pMan->interp->pVtab->out.pState = pMan;
28942924
if( pOut ){
28952925
*pOut = pBlob;
28962926
}
2897
- /*printf( "push: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
28982927
return TH_OK;
28992928
error:
29002929
if( pBlob ){
29012930
Th_Free( pMan->interp, pBlob );
29022931
}
29032932
return TH_ERROR;
29042933
}
29052934
2906
-Blob * Th_ob_pop( Th_Ob_Man * pMan ){
2935
+Blob * Th_Ob_Pop( Th_Ob_Manager * pMan ){
29072936
if( pMan->cursor < 0 ){
29082937
return NULL;
29092938
}else{
29102939
Blob * rc;
2911
- Th_Vtab_Output * theOut;
2912
- /*printf( "pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
2940
+ Th_Vtab_OutputMethods * theOut;
29132941
assert( pMan->nBuf > pMan->cursor );
29142942
rc = pMan->aBuf[pMan->cursor];
29152943
pMan->aBuf[pMan->cursor] = NULL;
29162944
theOut = &pMan->aOutput[pMan->cursor];
29172945
if( theOut->dispose ){
29182946
theOut->dispose( theOut->pState );
29192947
}
29202948
pMan->interp->pVtab->out = *theOut;
2921
- pMan->aOutput[pMan->cursor] = Th_Vtab_Output_empty;
2949
+ pMan->aOutput[pMan->cursor] = Th_Vtab_OutputMethods_empty;
29222950
if(-1 == --pMan->cursor){
29232951
Th_Interp * interp = pMan->interp;
29242952
Th_Free( pMan->interp, pMan->aBuf );
29252953
Th_Free( pMan->interp, pMan->aOutput );
29262954
*pMan = Th_Ob_Man_empty;
29272955
pMan->interp = interp;
29282956
assert(-1 == pMan->cursor);
29292957
}
2930
- /*printf( "post-pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
29312958
return rc;
29322959
}
29332960
}
29342961
2935
-int Th_ob_pop_free( Th_Ob_Man * pMan ){
2936
- Blob * b = Th_ob_pop( pMan );
2962
+int Th_Ob_PopAndFree( Th_Ob_Manager * pMan ){
2963
+ Blob * b = Th_Ob_Pop( pMan );
29372964
if(!b) return 1;
29382965
else {
29392966
blob_reset(b);
29402967
Th_Free( pMan->interp, b );
29412968
}
29422969
}
29432970
29442971
2945
-void Th_ob_cleanup( Th_Ob_Man * man ){
2946
- while( 0 == Th_ob_pop_free(man) ){}
2972
+void Th_ob_cleanup( Th_Ob_Manager * man ){
2973
+ while( 0 == Th_Ob_PopAndFree(man) ){}
29472974
}
29482975
29492976
29502977
/*
2951
-** TH Syntax:
2978
+** TH command:
29522979
**
29532980
** ob clean
29542981
**
29552982
** Erases any currently buffered contents but does not modify
29562983
** the buffering level.
@@ -2957,14 +2984,14 @@
29572984
*/
29582985
static int ob_clean_command( Th_Interp *interp, void *ctx,
29592986
int argc, const char **argv, int *argl
29602987
){
29612988
const char doRc = ctx ? 1 : 0;
2962
- Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
2989
+ Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
29632990
Blob * b;
29642991
assert( pMan && (interp == pMan->interp) );
2965
- b = pMan ? Th_ob_current(pMan) : NULL;
2992
+ b = pMan ? Th_Ob_GetCurrentBuffer(pMan) : NULL;
29662993
if(!b){
29672994
Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
29682995
return TH_ERROR;
29692996
}else{
29702997
blob_reset(b);
@@ -2974,24 +3001,24 @@
29743001
return TH_OK;
29753002
}
29763003
}
29773004
29783005
/*
2979
-** TH Syntax:
3006
+** TH command:
29803007
**
29813008
** ob end
29823009
**
29833010
** Erases any currently buffered contents and pops the current buffer
29843011
** from the stack.
29853012
*/
29863013
static int ob_end_command( Th_Interp *interp, void *ctx,
29873014
int argc, const char **argv, int *argl ){
29883015
const char doRc = ctx ? 1 : 0;
2989
- Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
3016
+ Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
29903017
Blob * b;
29913018
assert( pMan && (interp == pMan->interp) );
2992
- b = Th_ob_pop(pMan);
3019
+ b = Th_Ob_Pop(pMan);
29933020
if(!b){
29943021
Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
29953022
return TH_ERROR;
29963023
}else{
29973024
blob_reset(b);
@@ -3002,11 +3029,11 @@
30023029
return TH_OK;
30033030
}
30043031
}
30053032
30063033
/*
3007
-** TH Syntax:
3034
+** TH command:
30083035
**
30093036
** ob flush ?pop|end?
30103037
**
30113038
** Briefly reverts the output layer to the next-lower
30123039
** level, flushes the current buffer to that output layer,
@@ -3015,23 +3042,23 @@
30153042
** it behaves as if "ob end" had been called (after flushing
30163043
** the buffer).
30173044
*/
30183045
static int ob_flush_command( Th_Interp *interp, void *ctx,
30193046
int argc, const char **argv, int *argl ){
3020
- Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3047
+ Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
30213048
Blob * b = NULL;
30223049
Th_Vtab * oldVtab;
30233050
int rc = TH_OK;
30243051
assert( pMan && (interp == pMan->interp) );
3025
- b = Th_ob_current(pMan);
3052
+ b = Th_Ob_GetCurrentBuffer(pMan);
30263053
if( NULL == b ){
30273054
Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
30283055
return TH_ERROR;
30293056
}
30303057
oldVtab = interp->pVtab;
30313058
interp->pVtab->out = pMan->aOutput[pMan->cursor];
3032
- Th_output( interp, blob_str(b), b->nUsed );
3059
+ Th_Output( interp, blob_str(b), b->nUsed );
30333060
interp->pVtab = oldVtab;
30343061
blob_reset(b);
30353062
30363063
if(!rc && argc>2){
30373064
int argPos = 2;
@@ -3047,11 +3074,11 @@
30473074
Th_SetResultInt( interp, 0 );
30483075
return rc;
30493076
}
30503077
30513078
/*
3052
-** TH Syntax:
3079
+** TH command:
30533080
**
30543081
** ob get ?clean|end|pop?
30553082
**
30563083
** Fetches the contents of the current buffer level. If either
30573084
** 'clean' or 'end' are specified then the effect is as if "ob clean"
@@ -3059,14 +3086,14 @@
30593086
** value. Calling "ob get end" is functionality equivalent to "ob get"
30603087
** followed by "ob end".
30613088
*/
30623089
static int ob_get_command( Th_Interp *interp, void *ctx,
30633090
int argc, const char **argv, int *argl){
3064
- Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3091
+ Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
30653092
Blob * b = NULL;
30663093
assert( pMan && (interp == pMan->interp) );
3067
- b = Th_ob_current(pMan);
3094
+ b = Th_Ob_GetCurrentBuffer(pMan);
30683095
if( NULL == b ){
30693096
Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
30703097
return TH_ERROR;
30713098
}else{
30723099
int argPos = 2;
@@ -3090,27 +3117,27 @@
30903117
return rc;
30913118
}
30923119
}
30933120
30943121
/*
3095
-** TH Syntax:
3122
+** TH command:
30963123
**
30973124
** ob level
30983125
**
30993126
** Returns the buffering level, where 0 means no buffering is
31003127
** active, 1 means 1 level is active, etc.
31013128
*/
31023129
static int ob_level_command( Th_Interp *interp, void *ctx,
31033130
int argc, const char **argv, int *argl
31043131
){
3105
- Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3132
+ Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
31063133
Th_SetResultInt( interp, 1 + pMan->cursor );
31073134
return TH_OK;
31083135
}
31093136
31103137
/*
3111
-** TH Syntax:
3138
+** TH command:
31123139
**
31133140
** ob start|push
31143141
**
31153142
** Pushes a new level of buffering onto the buffer stack.
31163143
** Returns the new buffering level (1-based).
@@ -3120,16 +3147,16 @@
31203147
**
31213148
*/
31223149
static int ob_start_command( Th_Interp *interp, void *ctx,
31233150
int argc, const char **argv, int *argl
31243151
){
3125
- Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3152
+ Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
31263153
Blob * b = NULL;
31273154
int rc;
3128
- Th_Vtab_Output const * pWriter = &Th_Vtab_Output_ob;
3155
+ Th_Vtab_OutputMethods const * pWriter = &Th_Vtab_OutputMethods_ob;
31293156
assert( pMan && (interp == pMan->interp) );
3130
- rc = Th_ob_push(pMan, NULL, &b);
3157
+ rc = Th_Ob_Push(pMan, NULL, &b);
31313158
if( TH_OK != rc ){
31323159
assert( NULL == b );
31333160
return rc;
31343161
}
31353162
assert( NULL != b );
@@ -3136,21 +3163,20 @@
31363163
Th_SetResultInt( interp, 1 + pMan->cursor );
31373164
return TH_OK;
31383165
}
31393166
31403167
static void finalizerObMan( Th_Interp * interp, void * p ){
3141
- Th_Ob_Man * man = (Th_Ob_Man*)p;
3142
- /*printf("finalizerObMan(%p,%p)\n", interp, p );*/
3168
+ Th_Ob_Manager * man = (Th_Ob_Manager*)p;
31433169
if(man){
31443170
assert( interp == man->interp );
31453171
Th_ob_cleanup( man );
3146
- Th_Free( interp, man );
3172
+ Th_Free( interp, p );
31473173
}
31483174
}
31493175
31503176
/*
3151
-** TH Syntax:
3177
+** TH command:
31523178
**
31533179
** ob clean|(end|pop)|flush|get|level|(start|push)
31543180
**
31553181
** Runs the given subcommand. Some subcommands have other subcommands
31563182
** (see their docs for details).
@@ -3161,20 +3187,21 @@
31613187
void *ignored,
31623188
int argc,
31633189
const char **argv,
31643190
int *argl
31653191
){
3166
- Th_Ob_Man * pMan = Th_ob_manager(interp);
3192
+ Th_Ob_Manager * pMan = Th_Ob_GetManager(interp);
31673193
Th_SubCommand aSub[] = {
31683194
{ "clean", ob_clean_command },
31693195
{ "end", ob_end_command },
31703196
{ "flush", ob_flush_command },
31713197
{ "get", ob_get_command },
31723198
{ "level", ob_level_command },
31733199
{ "pop", ob_end_command },
31743200
{ "push", ob_start_command },
31753201
{ "start", ob_start_command },
3202
+ /* TODO: enable/disable commands which call Th_OutputEnable(). */
31763203
{ 0, 0 }
31773204
};
31783205
assert(NULL != pMan && pMan->interp==interp);
31793206
return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
31803207
}
@@ -3183,27 +3210,28 @@
31833210
int rc;
31843211
static Th_Command_Reg aCommand[] = {
31853212
{"ob", ob_cmd, 0},
31863213
{0,0,0}
31873214
};
3188
- rc = Th_register_commands( interp, aCommand );
3189
- if(NULL == Th_ob_manager(interp)){
3190
- Th_Ob_Man * pMan;
3191
- pMan = Th_Malloc(interp, sizeof(Th_Ob_Man));
3215
+ rc = Th_RegisterCommands( interp, aCommand );
3216
+ if(NULL == Th_Ob_GetManager(interp)){
3217
+ Th_Ob_Manager * pMan;
3218
+ pMan = Th_Malloc(interp, sizeof(Th_Ob_Manager));
31923219
if(!pMan){
31933220
rc = TH_ERROR;
31943221
}else{
31953222
*pMan = Th_Ob_Man_empty;
31963223
pMan->interp = interp;
31973224
assert( -1 == pMan->cursor );
3198
- Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
3199
- assert( NULL != Th_ob_manager(interp) );
3225
+ Th_SetData( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
3226
+ assert( NULL != Th_Ob_GetManager(interp) );
32003227
}
32013228
}
32023229
return rc;
32033230
}
32043231
32053232
#undef Th_Ob_Man_empty_m
3233
+#undef Th_Vtab_OutputMethods_empty_m
3234
+#undef Th_Vtab_OutputMethods_ob_m
32063235
#undef Th_Ob_Man_KEY
32073236
#endif
3208
-/* end TH_ENABLE_OUTBUF */
3209
-
3237
+/* end TH_ENABLE_OB */
32103238
--- src/th.c
+++ src/th.c
@@ -7,37 +7,30 @@
7 #include "th.h"
8 #include <string.h>
9 #include <assert.h>
10 #include <stdio.h> /* FILE class */
11
12 #ifdef TH_ENABLE_OUTBUF
13 struct Th_Ob_Man {
14 Blob ** aBuf; /* Stack of Blobs */
15 int nBuf; /* Number of blobs */
16 int cursor; /* Current level (-1=not active) */
17 Th_Interp * interp; /* The associated interpreter */
18 Th_Vtab_Output * aOutput
19 /* Stack of output routines corresponding
20 to the current buffering level.
21 Has nBuf entries.
22 */;
23 };
24 #endif
25
26 extern void *fossil_realloc(void *p, size_t n);
27 static void * th_fossil_realloc(void *p, unsigned int n){
28 return fossil_realloc( p, n );
29 }
30 static int Th_output_f_ob( char const * zData, int len, void * pState );
31 static void Th_output_dispose_ob( void * pState );
32 typedef struct Th_Command Th_Command;
33 typedef struct Th_Frame Th_Frame;
34 typedef struct Th_Variable Th_Variable;
35
36 const Th_Vtab_Output Th_Vtab_Output_FILE = {
37 Th_output_f_FILE /* write() */,
38 Th_output_dispose_FILE /* dispose() */,
 
 
 
39 NULL /*pState*/,
40 1/*enabled*/
41 };
42
43 /*
@@ -60,11 +53,11 @@
60 Th_Hash *paCmd; /* Table of registered commands */
61 Th_Frame *pFrame; /* Current execution frame */
62 int isListMode; /* True if thSplitList() should operate in "list" mode */
63 Th_Hash * paGc; /* Holds client-provided data owned by this
64 object. It would be more efficient to store
65 these in a list (we don't expect many
66 entries), but Th_Hash has the strong advantage
67 of being here and working.
68 */
69 };
70
@@ -1449,11 +1442,11 @@
1449 void *p = pInterp->pVtab->xRealloc(z, nByte);
1450 return p;
1451 }
1452
1453
1454 int Th_Vtab_output( Th_Vtab *vTab, char const * zData, int nData ){
1455 if(!vTab->out.write){
1456 return -1;
1457 }else if(!vTab->out.enabled){
1458 return 0;
1459 }else{
@@ -1460,22 +1453,30 @@
1460 return vTab->out.write( zData, nData, vTab->out.pState );
1461 }
1462 }
1463
1464
1465 int Th_output( Th_Interp *pInterp, char const * zData, int nData ){
1466 return Th_Vtab_output( pInterp->pVtab, zData, nData );
 
 
 
 
 
 
 
 
1467 }
1468
1469 int Th_output_f_FILE( char const * zData, int nData, void * pState ){
1470 FILE * dest = pState ? (FILE*)pState : stdout;
1471 int rc = (int)fwrite(zData, 1, nData, dest);
1472 fflush(dest);
1473 return rc;
1474 }
1475
1476 void Th_output_dispose_FILE( void * pState ){
1477 FILE * f = pState ? (FILE*)pState : NULL;
1478 if(f
1479 && (f != stdout)
1480 && (f != stderr)
1481 && (f != stdin)){
@@ -1745,10 +1746,15 @@
1745 if( interp->paGc ){
1746 Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
1747 Th_HashDelete(interp, interp->paGc);
1748 interp->paGc = NULL;
1749 }
 
 
 
 
 
1750
1751 /* Delete the contents of the global frame. */
1752 thPopFrame(interp);
1753
1754 /* Delete any result currently stored in the interpreter. */
@@ -2444,11 +2450,10 @@
2444 }
2445
2446 #ifndef LONGDOUBLE_TYPE
2447 # define LONGDOUBLE_TYPE long double
2448 #endif
2449 /*typedef char u8;*/
2450
2451
2452 /*
2453 ** Return TRUE if z is a pure numeric string. Return FALSE if the
2454 ** string contains any character which is not part of a number. If
@@ -2746,11 +2751,11 @@
2746 *z = '\0';
2747 return Th_SetResult(interp, zBuf, -1);
2748 }
2749
2750
2751 int Th_Data_Set( Th_Interp * interp, char const * key,
2752 void * pData,
2753 void (*finalizer)( Th_Interp *, void * ) ){
2754 Th_HashEntry * pEnt;
2755 Th_GcEntry * pGc;
2756 if(NULL == interp->paGc){
@@ -2769,95 +2774,120 @@
2769 pGc->pData = pData;
2770 pGc->xDel = finalizer;
2771 return 0;
2772
2773 }
2774 void * Th_Data_Get( Th_Interp * interp, char const * key ){
2775 Th_HashEntry * e = interp->paGc
2776 ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
2777 : NULL;
2778 return e ? ((Th_GcEntry*)e->pData)->pData : NULL;
2779 }
2780
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2781
2782
2783 #ifdef TH_ENABLE_OUTBUF
2784 /* Reminder: the ob code "really" belongs in th_lang.c,
2785 but we need access to Th_Interp internals in order to
2786 swap out Th_Vtab parts for purposes of stacking layers
2787 of buffers.
 
2788 */
 
 
 
2789 #define Th_Ob_Man_empty_m { \
2790 NULL/*aBuf*/, \
2791 0/*nBuf*/, \
2792 -1/*cursor*/, \
2793 NULL/*interp*/, \
2794 NULL/*aOutput*/ \
2795 }
2796
2797 /*
2798 ** Vtab impl for the ob buffering layer.
2799 */
2800 #define Th_Vtab_Output_empty_m { \
2801 NULL /* write() */, \
2802 NULL /* dispose() */, \
2803 NULL /*pState*/,\
2804 1/*enabled*/\
2805 }
2806 #define Th_Vtab_Output_ob_m { \
2807 Th_output_f_ob /*write()*/, \
2808 Th_output_dispose_ob /* dispose() */, \
 
2809 NULL /*pState*/,\
2810 1/*enabled*/\
2811 }
2812 static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;
2813 static Th_Vtab_Output Th_Vtab_Output_ob = Th_Vtab_Output_ob_m;
2814 static Th_Vtab_Output Th_Vtab_Output_empty = Th_Vtab_Output_empty_m;
2815 #define Th_Ob_Man_KEY "Th_Ob_Man"
2816 Th_Ob_Man * Th_ob_manager(Th_Interp *interp){
2817 return (Th_Ob_Man*) Th_Data_Get(interp, Th_Ob_Man_KEY );
 
 
 
 
 
 
 
 
2818 }
2819
2820 Blob * Th_ob_current( Th_Ob_Man * pMan ){
2821 return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
2822 }
2823
2824
2825 /*
2826 ** Th_output_f() impl which expects pState to be (Th_Ob_Man*).
2827 ** (zData,len) are appended to pState's current output buffer.
2828 */
2829 int Th_output_f_ob( char const * zData, int len, void * pState ){
2830 Th_Ob_Man * pMan = (Th_Ob_Man*)pState;
2831 Blob * b = Th_ob_current( pMan );
2832 assert( NULL != pMan );
2833 assert( b );
2834 blob_append( b, zData, len );
2835 return len;
2836 }
2837
2838 static void Th_output_dispose_ob( void * pState ){
2839 /* possible todo: move the cleanup logic from
2840 Th_ob_pop() to here? */
2841 /*printf("disposing() ob vtab.\n"); */
2842 #if 0
2843 Th_Ob_Man * pMan = (Th_Ob_Man*)pState;
2844 Blob * b = Th_ob_current( pMan );
2845 assert( NULL != pMan );
2846 assert( b );
2847 #endif
2848 }
2849
2850
2851
2852 int Th_ob_push( Th_Ob_Man * pMan,
2853 Th_Vtab_Output const * pWriter,
2854 Blob ** pOut ){
2855 Blob * pBlob;
2856 int x, i;
2857 if( NULL == pWriter ){
2858 pWriter = &Th_Vtab_Output_ob;
2859 }
2860 assert( NULL != pMan->interp );
2861 pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob));
2862 *pBlob = empty_blob;
2863
@@ -2871,17 +2901,17 @@
2871 void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
2872 if(NULL==re){
2873 goto error;
2874 }
2875 pMan->aBuf = (Blob **)re;
2876 re = Th_Realloc( pMan->interp, pMan->aOutput, x * sizeof(Th_Vtab_Output) );
2877 if(NULL==re){
2878 goto error;
2879 }
2880 pMan->aOutput = (Th_Vtab_Output*)re;
2881 for( i = pMan->nBuf; i < x; ++i ){
2882 pMan->aOutput[i] = Th_Vtab_Output_empty;
2883 pMan->aBuf[i] = NULL;
2884 }
2885 pMan->nBuf = x;
2886 }
2887 assert( pMan->nBuf > pMan->cursor );
@@ -2892,65 +2922,62 @@
2892 pMan->interp->pVtab->out = *pWriter;
2893 pMan->interp->pVtab->out.pState = pMan;
2894 if( pOut ){
2895 *pOut = pBlob;
2896 }
2897 /*printf( "push: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
2898 return TH_OK;
2899 error:
2900 if( pBlob ){
2901 Th_Free( pMan->interp, pBlob );
2902 }
2903 return TH_ERROR;
2904 }
2905
2906 Blob * Th_ob_pop( Th_Ob_Man * pMan ){
2907 if( pMan->cursor < 0 ){
2908 return NULL;
2909 }else{
2910 Blob * rc;
2911 Th_Vtab_Output * theOut;
2912 /*printf( "pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
2913 assert( pMan->nBuf > pMan->cursor );
2914 rc = pMan->aBuf[pMan->cursor];
2915 pMan->aBuf[pMan->cursor] = NULL;
2916 theOut = &pMan->aOutput[pMan->cursor];
2917 if( theOut->dispose ){
2918 theOut->dispose( theOut->pState );
2919 }
2920 pMan->interp->pVtab->out = *theOut;
2921 pMan->aOutput[pMan->cursor] = Th_Vtab_Output_empty;
2922 if(-1 == --pMan->cursor){
2923 Th_Interp * interp = pMan->interp;
2924 Th_Free( pMan->interp, pMan->aBuf );
2925 Th_Free( pMan->interp, pMan->aOutput );
2926 *pMan = Th_Ob_Man_empty;
2927 pMan->interp = interp;
2928 assert(-1 == pMan->cursor);
2929 }
2930 /*printf( "post-pop: pMan->nBuf=%d, pMan->cursor=%d\n", pMan->nBuf, pMan->cursor);*/
2931 return rc;
2932 }
2933 }
2934
2935 int Th_ob_pop_free( Th_Ob_Man * pMan ){
2936 Blob * b = Th_ob_pop( pMan );
2937 if(!b) return 1;
2938 else {
2939 blob_reset(b);
2940 Th_Free( pMan->interp, b );
2941 }
2942 }
2943
2944
2945 void Th_ob_cleanup( Th_Ob_Man * man ){
2946 while( 0 == Th_ob_pop_free(man) ){}
2947 }
2948
2949
2950 /*
2951 ** TH Syntax:
2952 **
2953 ** ob clean
2954 **
2955 ** Erases any currently buffered contents but does not modify
2956 ** the buffering level.
@@ -2957,14 +2984,14 @@
2957 */
2958 static int ob_clean_command( Th_Interp *interp, void *ctx,
2959 int argc, const char **argv, int *argl
2960 ){
2961 const char doRc = ctx ? 1 : 0;
2962 Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
2963 Blob * b;
2964 assert( pMan && (interp == pMan->interp) );
2965 b = pMan ? Th_ob_current(pMan) : NULL;
2966 if(!b){
2967 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2968 return TH_ERROR;
2969 }else{
2970 blob_reset(b);
@@ -2974,24 +3001,24 @@
2974 return TH_OK;
2975 }
2976 }
2977
2978 /*
2979 ** TH Syntax:
2980 **
2981 ** ob end
2982 **
2983 ** Erases any currently buffered contents and pops the current buffer
2984 ** from the stack.
2985 */
2986 static int ob_end_command( Th_Interp *interp, void *ctx,
2987 int argc, const char **argv, int *argl ){
2988 const char doRc = ctx ? 1 : 0;
2989 Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
2990 Blob * b;
2991 assert( pMan && (interp == pMan->interp) );
2992 b = Th_ob_pop(pMan);
2993 if(!b){
2994 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2995 return TH_ERROR;
2996 }else{
2997 blob_reset(b);
@@ -3002,11 +3029,11 @@
3002 return TH_OK;
3003 }
3004 }
3005
3006 /*
3007 ** TH Syntax:
3008 **
3009 ** ob flush ?pop|end?
3010 **
3011 ** Briefly reverts the output layer to the next-lower
3012 ** level, flushes the current buffer to that output layer,
@@ -3015,23 +3042,23 @@
3015 ** it behaves as if "ob end" had been called (after flushing
3016 ** the buffer).
3017 */
3018 static int ob_flush_command( Th_Interp *interp, void *ctx,
3019 int argc, const char **argv, int *argl ){
3020 Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3021 Blob * b = NULL;
3022 Th_Vtab * oldVtab;
3023 int rc = TH_OK;
3024 assert( pMan && (interp == pMan->interp) );
3025 b = Th_ob_current(pMan);
3026 if( NULL == b ){
3027 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
3028 return TH_ERROR;
3029 }
3030 oldVtab = interp->pVtab;
3031 interp->pVtab->out = pMan->aOutput[pMan->cursor];
3032 Th_output( interp, blob_str(b), b->nUsed );
3033 interp->pVtab = oldVtab;
3034 blob_reset(b);
3035
3036 if(!rc && argc>2){
3037 int argPos = 2;
@@ -3047,11 +3074,11 @@
3047 Th_SetResultInt( interp, 0 );
3048 return rc;
3049 }
3050
3051 /*
3052 ** TH Syntax:
3053 **
3054 ** ob get ?clean|end|pop?
3055 **
3056 ** Fetches the contents of the current buffer level. If either
3057 ** 'clean' or 'end' are specified then the effect is as if "ob clean"
@@ -3059,14 +3086,14 @@
3059 ** value. Calling "ob get end" is functionality equivalent to "ob get"
3060 ** followed by "ob end".
3061 */
3062 static int ob_get_command( Th_Interp *interp, void *ctx,
3063 int argc, const char **argv, int *argl){
3064 Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3065 Blob * b = NULL;
3066 assert( pMan && (interp == pMan->interp) );
3067 b = Th_ob_current(pMan);
3068 if( NULL == b ){
3069 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
3070 return TH_ERROR;
3071 }else{
3072 int argPos = 2;
@@ -3090,27 +3117,27 @@
3090 return rc;
3091 }
3092 }
3093
3094 /*
3095 ** TH Syntax:
3096 **
3097 ** ob level
3098 **
3099 ** Returns the buffering level, where 0 means no buffering is
3100 ** active, 1 means 1 level is active, etc.
3101 */
3102 static int ob_level_command( Th_Interp *interp, void *ctx,
3103 int argc, const char **argv, int *argl
3104 ){
3105 Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3106 Th_SetResultInt( interp, 1 + pMan->cursor );
3107 return TH_OK;
3108 }
3109
3110 /*
3111 ** TH Syntax:
3112 **
3113 ** ob start|push
3114 **
3115 ** Pushes a new level of buffering onto the buffer stack.
3116 ** Returns the new buffering level (1-based).
@@ -3120,16 +3147,16 @@
3120 **
3121 */
3122 static int ob_start_command( Th_Interp *interp, void *ctx,
3123 int argc, const char **argv, int *argl
3124 ){
3125 Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
3126 Blob * b = NULL;
3127 int rc;
3128 Th_Vtab_Output const * pWriter = &Th_Vtab_Output_ob;
3129 assert( pMan && (interp == pMan->interp) );
3130 rc = Th_ob_push(pMan, NULL, &b);
3131 if( TH_OK != rc ){
3132 assert( NULL == b );
3133 return rc;
3134 }
3135 assert( NULL != b );
@@ -3136,21 +3163,20 @@
3136 Th_SetResultInt( interp, 1 + pMan->cursor );
3137 return TH_OK;
3138 }
3139
3140 static void finalizerObMan( Th_Interp * interp, void * p ){
3141 Th_Ob_Man * man = (Th_Ob_Man*)p;
3142 /*printf("finalizerObMan(%p,%p)\n", interp, p );*/
3143 if(man){
3144 assert( interp == man->interp );
3145 Th_ob_cleanup( man );
3146 Th_Free( interp, man );
3147 }
3148 }
3149
3150 /*
3151 ** TH Syntax:
3152 **
3153 ** ob clean|(end|pop)|flush|get|level|(start|push)
3154 **
3155 ** Runs the given subcommand. Some subcommands have other subcommands
3156 ** (see their docs for details).
@@ -3161,20 +3187,21 @@
3161 void *ignored,
3162 int argc,
3163 const char **argv,
3164 int *argl
3165 ){
3166 Th_Ob_Man * pMan = Th_ob_manager(interp);
3167 Th_SubCommand aSub[] = {
3168 { "clean", ob_clean_command },
3169 { "end", ob_end_command },
3170 { "flush", ob_flush_command },
3171 { "get", ob_get_command },
3172 { "level", ob_level_command },
3173 { "pop", ob_end_command },
3174 { "push", ob_start_command },
3175 { "start", ob_start_command },
 
3176 { 0, 0 }
3177 };
3178 assert(NULL != pMan && pMan->interp==interp);
3179 return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
3180 }
@@ -3183,27 +3210,28 @@
3183 int rc;
3184 static Th_Command_Reg aCommand[] = {
3185 {"ob", ob_cmd, 0},
3186 {0,0,0}
3187 };
3188 rc = Th_register_commands( interp, aCommand );
3189 if(NULL == Th_ob_manager(interp)){
3190 Th_Ob_Man * pMan;
3191 pMan = Th_Malloc(interp, sizeof(Th_Ob_Man));
3192 if(!pMan){
3193 rc = TH_ERROR;
3194 }else{
3195 *pMan = Th_Ob_Man_empty;
3196 pMan->interp = interp;
3197 assert( -1 == pMan->cursor );
3198 Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
3199 assert( NULL != Th_ob_manager(interp) );
3200 }
3201 }
3202 return rc;
3203 }
3204
3205 #undef Th_Ob_Man_empty_m
 
 
3206 #undef Th_Ob_Man_KEY
3207 #endif
3208 /* end TH_ENABLE_OUTBUF */
3209
3210
--- src/th.c
+++ src/th.c
@@ -7,37 +7,30 @@
7 #include "th.h"
8 #include <string.h>
9 #include <assert.h>
10 #include <stdio.h> /* FILE class */
11
12 /*
13 ** Th_Output_f() impl which redirects output to a Th_Ob_Manager.
14 */
15 static int Th_Output_f_ob( char const * zData, int len, void * pState );
16
17 /*
18 ** Th_Output::dispose() impl which requires pState to be-a Th_Ob_Manager.
19 */
20 static void Th_Output_dispose_ob( void * pState );
21
 
 
 
 
 
 
 
 
 
 
22 typedef struct Th_Command Th_Command;
23 typedef struct Th_Frame Th_Frame;
24 typedef struct Th_Variable Th_Variable;
25
26 /*
27 ** Shared instance. See th.h for the docs.
28 */
29 const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE = {
30 Th_Output_f_FILE /* write() */,
31 Th_Output_dispose_FILE /* dispose() */,
32 NULL /*pState*/,
33 1/*enabled*/
34 };
35
36 /*
@@ -60,11 +53,11 @@
53 Th_Hash *paCmd; /* Table of registered commands */
54 Th_Frame *pFrame; /* Current execution frame */
55 int isListMode; /* True if thSplitList() should operate in "list" mode */
56 Th_Hash * paGc; /* Holds client-provided data owned by this
57 object. It would be more efficient to store
58 these in a list (because we don't expect many
59 entries), but Th_Hash has the strong advantage
60 of being here and working.
61 */
62 };
63
@@ -1449,11 +1442,11 @@
1442 void *p = pInterp->pVtab->xRealloc(z, nByte);
1443 return p;
1444 }
1445
1446
1447 int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int nData ){
1448 if(!vTab->out.write){
1449 return -1;
1450 }else if(!vTab->out.enabled){
1451 return 0;
1452 }else{
@@ -1460,22 +1453,30 @@
1453 return vTab->out.write( zData, nData, vTab->out.pState );
1454 }
1455 }
1456
1457
1458 int Th_Output( Th_Interp *pInterp, char const * zData, int nData ){
1459 return Th_Vtab_Output( pInterp->pVtab, zData, nData );
1460 }
1461
1462 void Th_OutputEnable( Th_Interp *pInterp, char flag ){
1463 pInterp->pVtab->out.enabled = flag;
1464 }
1465
1466 char Th_OutputEnabled( Th_Interp *pInterp ){
1467 return pInterp->pVtab->out.enabled ? 1 : 0;
1468 }
1469
1470 int Th_Output_f_FILE( char const * zData, int nData, void * pState ){
1471 FILE * dest = pState ? (FILE*)pState : stdout;
1472 int rc = (int)fwrite(zData, 1, nData, dest);
1473 fflush(dest);
1474 return rc;
1475 }
1476
1477 void Th_Output_dispose_FILE( void * pState ){
1478 FILE * f = pState ? (FILE*)pState : NULL;
1479 if(f
1480 && (f != stdout)
1481 && (f != stderr)
1482 && (f != stdin)){
@@ -1745,10 +1746,15 @@
1746 if( interp->paGc ){
1747 Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
1748 Th_HashDelete(interp, interp->paGc);
1749 interp->paGc = NULL;
1750 }
1751
1752 /* Clean up the output abstraction. */
1753 if( interp->pVtab && interp->pVtab->out.dispose ){
1754 interp->pVtab->out.dispose( interp->pVtab->out.pState );
1755 }
1756
1757 /* Delete the contents of the global frame. */
1758 thPopFrame(interp);
1759
1760 /* Delete any result currently stored in the interpreter. */
@@ -2444,11 +2450,10 @@
2450 }
2451
2452 #ifndef LONGDOUBLE_TYPE
2453 # define LONGDOUBLE_TYPE long double
2454 #endif
 
2455
2456
2457 /*
2458 ** Return TRUE if z is a pure numeric string. Return FALSE if the
2459 ** string contains any character which is not part of a number. If
@@ -2746,11 +2751,11 @@
2751 *z = '\0';
2752 return Th_SetResult(interp, zBuf, -1);
2753 }
2754
2755
2756 int Th_SetData( Th_Interp * interp, char const * key,
2757 void * pData,
2758 void (*finalizer)( Th_Interp *, void * ) ){
2759 Th_HashEntry * pEnt;
2760 Th_GcEntry * pGc;
2761 if(NULL == interp->paGc){
@@ -2769,95 +2774,120 @@
2774 pGc->pData = pData;
2775 pGc->xDel = finalizer;
2776 return 0;
2777
2778 }
2779 void * Th_GetData( Th_Interp * interp, char const * key ){
2780 Th_HashEntry * e = interp->paGc
2781 ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
2782 : NULL;
2783 return e ? ((Th_GcEntry*)e->pData)->pData : NULL;
2784 }
2785
2786 int Th_RegisterCommands( Th_Interp * interp,
2787 Th_Command_Reg const * aCommand ){
2788 int i;
2789 int rc = TH_OK;
2790 for(i=0; (TH_OK==rc) && aCommand[i].zName; ++i){
2791 if ( !aCommand[i].zName ) break;
2792 else if( !aCommand[i].xProc ) continue;
2793 else{
2794 rc = Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
2795 aCommand[i].pContext, 0);
2796 }
2797 }
2798 return rc;
2799 }
2800
2801
2802
2803 #ifdef TH_ENABLE_OB
2804 /* Reminder: the ob code "really" belongs in th_lang.c or th_main.c,
2805 but it needs access to Th_Interp::pVtab in order to swap out
2806 Th_Vtab_OutputMethods parts for purposes of stacking layers of
2807 buffers. We could add access to it via the public interface,
2808 but that didn't seem appropriate.
2809 */
2810
2811
2812 /* Empty-initialized Th_Ob_Manager instance. */
2813 #define Th_Ob_Man_empty_m { \
2814 NULL/*aBuf*/, \
2815 0/*nBuf*/, \
2816 -1/*cursor*/, \
2817 NULL/*interp*/, \
2818 NULL/*aOutput*/ \
2819 }
2820
2821 /* Empty-initialized Th_Vtab_OutputMethods instance. */
2822 #define Th_Vtab_OutputMethods_empty_m { \
 
 
2823 NULL /* write() */, \
2824 NULL /* dispose() */, \
2825 NULL /*pState*/,\
2826 1/*enabled*/\
2827 }
2828 /* Vtab_OutputMethods instance initialized for OB support. */
2829 #define Th_Vtab_OutputMethods_ob_m { \
2830 Th_Output_f_ob /*write()*/, \
2831 Th_Output_dispose_ob /* dispose() */, \
2832 NULL /*pState*/,\
2833 1/*enabled*/\
2834 }
2835 /* Empty-initialized Th_Vtab_Man instance. */
2836 static const Th_Ob_Manager Th_Ob_Man_empty = Th_Ob_Man_empty_m;
2837 /* Empty-initialized Th_Vtab_OutputMethods instance. */
2838 static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_empty = Th_Vtab_OutputMethods_empty_m;
2839 /* Th_Vtab_OutputMethods instance initialized for OB support. */
2840 static Th_Vtab_OutputMethods Th_Vtab_OutputMethods_ob = Th_Vtab_OutputMethods_ob_m;
2841
2842 /*
2843 ** Internal key for Th_Set/GetData(), for storing a Th_Ob_Manager instance.
2844 */
2845 #define Th_Ob_Man_KEY "Th_Ob_Manager"
2846
2847 Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *interp){
2848 return (Th_Ob_Manager*) Th_GetData(interp, Th_Ob_Man_KEY );
2849 }
2850
2851 Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan ){
2852 return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
2853 }
2854
2855
2856 /*
2857 ** Th_Output_f() impl which expects pState to be (Th_Ob_Manager*).
2858 ** (zData,len) are appended to pState's current output buffer.
2859 */
2860 int Th_Output_f_ob( char const * zData, int len, void * pState ){
2861 Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
2862 Blob * b = Th_Ob_GetCurrentBuffer( pMan );
2863 assert( NULL != pMan );
2864 assert( b );
2865 blob_append( b, zData, len );
2866 return len;
2867 }
2868
2869 static void Th_Output_dispose_ob( void * pState ){
2870 /* possible todo: move the cleanup logic from
2871 Th_Ob_Pop() to here? */
 
2872 #if 0
2873 Th_Ob_Manager * pMan = (Th_Ob_Manager*)pState;
2874 Blob * b = Th_Ob_GetCurrentBuffer( pMan );
2875 assert( NULL != pMan );
2876 assert( b );
2877 #endif
2878 }
2879
2880
2881
2882 int Th_Ob_Push( Th_Ob_Manager * pMan,
2883 Th_Vtab_OutputMethods const * pWriter,
2884 Blob ** pOut ){
2885 Blob * pBlob;
2886 int x, i;
2887 if( NULL == pWriter ){
2888 pWriter = &Th_Vtab_OutputMethods_ob;
2889 }
2890 assert( NULL != pMan->interp );
2891 pBlob = (Blob *)Th_Malloc(pMan->interp, sizeof(Blob));
2892 *pBlob = empty_blob;
2893
@@ -2871,17 +2901,17 @@
2901 void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
2902 if(NULL==re){
2903 goto error;
2904 }
2905 pMan->aBuf = (Blob **)re;
2906 re = Th_Realloc( pMan->interp, pMan->aOutput, x * sizeof(Th_Vtab_OutputMethods) );
2907 if(NULL==re){
2908 goto error;
2909 }
2910 pMan->aOutput = (Th_Vtab_OutputMethods*)re;
2911 for( i = pMan->nBuf; i < x; ++i ){
2912 pMan->aOutput[i] = Th_Vtab_OutputMethods_empty;
2913 pMan->aBuf[i] = NULL;
2914 }
2915 pMan->nBuf = x;
2916 }
2917 assert( pMan->nBuf > pMan->cursor );
@@ -2892,65 +2922,62 @@
2922 pMan->interp->pVtab->out = *pWriter;
2923 pMan->interp->pVtab->out.pState = pMan;
2924 if( pOut ){
2925 *pOut = pBlob;
2926 }
 
2927 return TH_OK;
2928 error:
2929 if( pBlob ){
2930 Th_Free( pMan->interp, pBlob );
2931 }
2932 return TH_ERROR;
2933 }
2934
2935 Blob * Th_Ob_Pop( Th_Ob_Manager * pMan ){
2936 if( pMan->cursor < 0 ){
2937 return NULL;
2938 }else{
2939 Blob * rc;
2940 Th_Vtab_OutputMethods * theOut;
 
2941 assert( pMan->nBuf > pMan->cursor );
2942 rc = pMan->aBuf[pMan->cursor];
2943 pMan->aBuf[pMan->cursor] = NULL;
2944 theOut = &pMan->aOutput[pMan->cursor];
2945 if( theOut->dispose ){
2946 theOut->dispose( theOut->pState );
2947 }
2948 pMan->interp->pVtab->out = *theOut;
2949 pMan->aOutput[pMan->cursor] = Th_Vtab_OutputMethods_empty;
2950 if(-1 == --pMan->cursor){
2951 Th_Interp * interp = pMan->interp;
2952 Th_Free( pMan->interp, pMan->aBuf );
2953 Th_Free( pMan->interp, pMan->aOutput );
2954 *pMan = Th_Ob_Man_empty;
2955 pMan->interp = interp;
2956 assert(-1 == pMan->cursor);
2957 }
 
2958 return rc;
2959 }
2960 }
2961
2962 int Th_Ob_PopAndFree( Th_Ob_Manager * pMan ){
2963 Blob * b = Th_Ob_Pop( pMan );
2964 if(!b) return 1;
2965 else {
2966 blob_reset(b);
2967 Th_Free( pMan->interp, b );
2968 }
2969 }
2970
2971
2972 void Th_ob_cleanup( Th_Ob_Manager * man ){
2973 while( 0 == Th_Ob_PopAndFree(man) ){}
2974 }
2975
2976
2977 /*
2978 ** TH command:
2979 **
2980 ** ob clean
2981 **
2982 ** Erases any currently buffered contents but does not modify
2983 ** the buffering level.
@@ -2957,14 +2984,14 @@
2984 */
2985 static int ob_clean_command( Th_Interp *interp, void *ctx,
2986 int argc, const char **argv, int *argl
2987 ){
2988 const char doRc = ctx ? 1 : 0;
2989 Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
2990 Blob * b;
2991 assert( pMan && (interp == pMan->interp) );
2992 b = pMan ? Th_Ob_GetCurrentBuffer(pMan) : NULL;
2993 if(!b){
2994 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2995 return TH_ERROR;
2996 }else{
2997 blob_reset(b);
@@ -2974,24 +3001,24 @@
3001 return TH_OK;
3002 }
3003 }
3004
3005 /*
3006 ** TH command:
3007 **
3008 ** ob end
3009 **
3010 ** Erases any currently buffered contents and pops the current buffer
3011 ** from the stack.
3012 */
3013 static int ob_end_command( Th_Interp *interp, void *ctx,
3014 int argc, const char **argv, int *argl ){
3015 const char doRc = ctx ? 1 : 0;
3016 Th_Ob_Manager * pMan = ctx ? (Th_Ob_Manager *)ctx : Th_Ob_GetManager(interp);
3017 Blob * b;
3018 assert( pMan && (interp == pMan->interp) );
3019 b = Th_Ob_Pop(pMan);
3020 if(!b){
3021 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
3022 return TH_ERROR;
3023 }else{
3024 blob_reset(b);
@@ -3002,11 +3029,11 @@
3029 return TH_OK;
3030 }
3031 }
3032
3033 /*
3034 ** TH command:
3035 **
3036 ** ob flush ?pop|end?
3037 **
3038 ** Briefly reverts the output layer to the next-lower
3039 ** level, flushes the current buffer to that output layer,
@@ -3015,23 +3042,23 @@
3042 ** it behaves as if "ob end" had been called (after flushing
3043 ** the buffer).
3044 */
3045 static int ob_flush_command( Th_Interp *interp, void *ctx,
3046 int argc, const char **argv, int *argl ){
3047 Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
3048 Blob * b = NULL;
3049 Th_Vtab * oldVtab;
3050 int rc = TH_OK;
3051 assert( pMan && (interp == pMan->interp) );
3052 b = Th_Ob_GetCurrentBuffer(pMan);
3053 if( NULL == b ){
3054 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
3055 return TH_ERROR;
3056 }
3057 oldVtab = interp->pVtab;
3058 interp->pVtab->out = pMan->aOutput[pMan->cursor];
3059 Th_Output( interp, blob_str(b), b->nUsed );
3060 interp->pVtab = oldVtab;
3061 blob_reset(b);
3062
3063 if(!rc && argc>2){
3064 int argPos = 2;
@@ -3047,11 +3074,11 @@
3074 Th_SetResultInt( interp, 0 );
3075 return rc;
3076 }
3077
3078 /*
3079 ** TH command:
3080 **
3081 ** ob get ?clean|end|pop?
3082 **
3083 ** Fetches the contents of the current buffer level. If either
3084 ** 'clean' or 'end' are specified then the effect is as if "ob clean"
@@ -3059,14 +3086,14 @@
3086 ** value. Calling "ob get end" is functionality equivalent to "ob get"
3087 ** followed by "ob end".
3088 */
3089 static int ob_get_command( Th_Interp *interp, void *ctx,
3090 int argc, const char **argv, int *argl){
3091 Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
3092 Blob * b = NULL;
3093 assert( pMan && (interp == pMan->interp) );
3094 b = Th_Ob_GetCurrentBuffer(pMan);
3095 if( NULL == b ){
3096 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
3097 return TH_ERROR;
3098 }else{
3099 int argPos = 2;
@@ -3090,27 +3117,27 @@
3117 return rc;
3118 }
3119 }
3120
3121 /*
3122 ** TH command:
3123 **
3124 ** ob level
3125 **
3126 ** Returns the buffering level, where 0 means no buffering is
3127 ** active, 1 means 1 level is active, etc.
3128 */
3129 static int ob_level_command( Th_Interp *interp, void *ctx,
3130 int argc, const char **argv, int *argl
3131 ){
3132 Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
3133 Th_SetResultInt( interp, 1 + pMan->cursor );
3134 return TH_OK;
3135 }
3136
3137 /*
3138 ** TH command:
3139 **
3140 ** ob start|push
3141 **
3142 ** Pushes a new level of buffering onto the buffer stack.
3143 ** Returns the new buffering level (1-based).
@@ -3120,16 +3147,16 @@
3147 **
3148 */
3149 static int ob_start_command( Th_Interp *interp, void *ctx,
3150 int argc, const char **argv, int *argl
3151 ){
3152 Th_Ob_Manager * pMan = (Th_Ob_Manager *)ctx;
3153 Blob * b = NULL;
3154 int rc;
3155 Th_Vtab_OutputMethods const * pWriter = &Th_Vtab_OutputMethods_ob;
3156 assert( pMan && (interp == pMan->interp) );
3157 rc = Th_Ob_Push(pMan, NULL, &b);
3158 if( TH_OK != rc ){
3159 assert( NULL == b );
3160 return rc;
3161 }
3162 assert( NULL != b );
@@ -3136,21 +3163,20 @@
3163 Th_SetResultInt( interp, 1 + pMan->cursor );
3164 return TH_OK;
3165 }
3166
3167 static void finalizerObMan( Th_Interp * interp, void * p ){
3168 Th_Ob_Manager * man = (Th_Ob_Manager*)p;
 
3169 if(man){
3170 assert( interp == man->interp );
3171 Th_ob_cleanup( man );
3172 Th_Free( interp, p );
3173 }
3174 }
3175
3176 /*
3177 ** TH command:
3178 **
3179 ** ob clean|(end|pop)|flush|get|level|(start|push)
3180 **
3181 ** Runs the given subcommand. Some subcommands have other subcommands
3182 ** (see their docs for details).
@@ -3161,20 +3187,21 @@
3187 void *ignored,
3188 int argc,
3189 const char **argv,
3190 int *argl
3191 ){
3192 Th_Ob_Manager * pMan = Th_Ob_GetManager(interp);
3193 Th_SubCommand aSub[] = {
3194 { "clean", ob_clean_command },
3195 { "end", ob_end_command },
3196 { "flush", ob_flush_command },
3197 { "get", ob_get_command },
3198 { "level", ob_level_command },
3199 { "pop", ob_end_command },
3200 { "push", ob_start_command },
3201 { "start", ob_start_command },
3202 /* TODO: enable/disable commands which call Th_OutputEnable(). */
3203 { 0, 0 }
3204 };
3205 assert(NULL != pMan && pMan->interp==interp);
3206 return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
3207 }
@@ -3183,27 +3210,28 @@
3210 int rc;
3211 static Th_Command_Reg aCommand[] = {
3212 {"ob", ob_cmd, 0},
3213 {0,0,0}
3214 };
3215 rc = Th_RegisterCommands( interp, aCommand );
3216 if(NULL == Th_Ob_GetManager(interp)){
3217 Th_Ob_Manager * pMan;
3218 pMan = Th_Malloc(interp, sizeof(Th_Ob_Manager));
3219 if(!pMan){
3220 rc = TH_ERROR;
3221 }else{
3222 *pMan = Th_Ob_Man_empty;
3223 pMan->interp = interp;
3224 assert( -1 == pMan->cursor );
3225 Th_SetData( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
3226 assert( NULL != Th_Ob_GetManager(interp) );
3227 }
3228 }
3229 return rc;
3230 }
3231
3232 #undef Th_Ob_Man_empty_m
3233 #undef Th_Vtab_OutputMethods_empty_m
3234 #undef Th_Vtab_OutputMethods_ob_m
3235 #undef Th_Ob_Man_KEY
3236 #endif
3237 /* end TH_ENABLE_OB */
 
3238
+216 -73
--- src/th.h
+++ src/th.h
@@ -1,76 +1,94 @@
11
#include "config.h"
22
33
/*
4
-** TH_ENABLE_SQLITE, if defined, enables the "query" family of functions.
4
+** TH_ENABLE_QUERY, if defined, enables the "query" family of functions.
55
** They provide SELECT-only access to the repository db.
66
*/
7
-#define TH_ENABLE_SQLITE
7
+#define TH_ENABLE_QUERY
88
99
/*
10
-** TH_ENABLE_OUTBUF, if defined, enables the "ob" family of functions.
10
+** TH_ENABLE_OB, if defined, enables the "ob" family of functions.
1111
** They are functionally similar to PHP's ob_start(), ob_end(), etc.
1212
** family of functions, providing output capturing/buffering.
1313
*/
14
-#define TH_ENABLE_OUTBUF
14
+#define TH_ENABLE_OB
1515
1616
/*
1717
** TH_ENABLE_ARGV, if defined, enables the "argv" family of functions.
1818
** They provide access to CLI arguments as well as GET/POST arguments.
1919
** They do not provide access to POST data submitted in JSON mode.
2020
*/
2121
#define TH_ENABLE_ARGV
2222
23
-#ifdef TH_ENABLE_OUTBUF
23
+#ifdef TH_ENABLE_OB
2424
#ifndef INTERFACE
25
-#include "blob.h"
25
+#include "blob.h" /* maintenance reminder: also pulls in fossil_realloc() and friends */
2626
#endif
2727
#endif
2828
2929
/* This header file defines the external interface to the custom Scripting
3030
** Language (TH) interpreter. TH is very similar to TCL but is not an
3131
** exact clone.
3232
*/
3333
3434
/*
35
-** Th_output_f() specifies a generic output routine for use by Th_Vtab
36
-** and friends. Its first argument is the data to write, the second is
37
-** the number of bytes to write, and the 3rd is an
38
-** implementation-specific state pointer (may be NULL, depending on
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
3939
** the implementation). The return value is the number of bytes output
4040
** (which may differ from len due to encoding and whatnot). On error
4141
** a negative value must be returned.
4242
*/
43
-typedef int (*Th_output_f)( char const * zData, int len, void * pState );
43
+typedef int (*Th_Output_f)( char const * zData, int len, void * pState );
4444
4545
/*
4646
** This structure defines the output state associated with a
4747
** Th_Vtab. It is intended that a given Vtab be able to swap out
4848
** output back-ends during its lifetime, e.g. to form a stack of
4949
** buffers.
5050
*/
51
-struct Th_Vtab_Output {
52
- Th_output_f write; /* output handler */
53
- void (*dispose)( void * pState );
54
- void * pState; /* final argument for xOut() and dispose()*/
55
- char enabled; /* if 0, Th_output() does nothing. */
51
+struct Th_Vtab_OutputMethods {
52
+ Th_Output_f write; /* output handler */
53
+ void (*dispose)( 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 write() and dispose()*/
57
+ char enabled; /* if 0, Th_Output() does nothing. */
5658
};
57
-typedef struct Th_Vtab_Output Th_Vtab_Output;
59
+typedef struct Th_Vtab_OutputMethods Th_Vtab_OutputMethods;
5860
5961
/*
60
-** Shared Th_Vtab_Output instance used for copy-initialization.
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.
6167
*/
62
-extern const Th_Vtab_Output Th_Vtab_Output_FILE;
68
+extern const Th_Vtab_OutputMethods Th_Vtab_OutputMethods_FILE;
6369
6470
/*
6571
** Before creating an interpreter, the application must allocate and
6672
** populate an instance of the following structure. It must remain valid
6773
** for the lifetime of the interpreter.
6874
*/
6975
struct Th_Vtab {
70
- void *(*xRealloc)(void *, unsigned int); /* Re/deallocation routine. */
71
- Th_Vtab_Output out; /* output implementation */
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
+ */
7290
};
7391
typedef struct Th_Vtab Th_Vtab;
7492
7593
7694
/*
@@ -78,14 +96,25 @@
7896
*/
7997
typedef struct Th_Interp Th_Interp;
8098
8199
82100
/*
83
-** 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.dispose() 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.
84109
*/
85110
Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
86
-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);
87116
88117
/*
89118
** Evaluate an TH program in the stack frame identified by parameter
90119
** iFrame, according to the following rules:
91120
**
@@ -113,19 +142,37 @@
113142
int Th_GetVar(Th_Interp *, const char *, int);
114143
int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
115144
int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
116145
int Th_UnsetVar(Th_Interp *, const char *, int);
117146
118
-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);
119160
120161
/*
121
-** 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.
122170
*/
123171
int Th_CreateCommand(
124172
Th_Interp *interp,
125173
const char *zName,
126
- /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
127174
Th_CommandProc xProc,
128175
void *pContext,
129176
void (*xDel)(Th_Interp *, void *)
130177
);
131178
@@ -212,16 +259,39 @@
212259
213260
/*
214261
** Interfaces to register the language extensions.
215262
*/
216263
int th_register_language(Th_Interp *interp); /* th_lang.c */
217
-int th_register_query(Th_Interp *interp); /* th_main.c */
218
-int th_register_argv(Th_Interp *interp); /* th_main.c */
219264
int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
220265
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
+*/
221271
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
+*/
222290
int th_register_ob(Th_Interp * interp); /* th.c */
291
+#endif
292
+
223293
/*
224294
** General purpose hash table from th_lang.c.
225295
*/
226296
typedef struct Th_Hash Th_Hash;
227297
typedef struct Th_HashEntry Th_HashEntry;
@@ -237,145 +307,218 @@
237307
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
238308
239309
/*
240310
** Useful functions from th_lang.c.
241311
*/
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
+*/
242317
int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
318
+
243319
/*
244320
** Works like Th_WrongNumArgs() but expects (zCmdName,zCmdLen) to be
245321
** the current command's (name,length), i.e. (argv[0],argl[0]).
246322
*/
247323
int Th_WrongNumArgs2(Th_Interp *interp, const char *zCmdName,
248324
int zCmdLen, const char *zMsg);
249325
250326
typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
251327
int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
328
+
252329
/*
253330
** Works similarly to Th_CallSubCommand() but adjusts argc/argv/argl
254
-** by 1 before passing on the call to the subcommand.
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.
255334
*/
256335
int Th_CallSubCommand2(Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl, Th_SubCommand *aSub);
336
+
257337
/*
258338
** Sends the given data through vTab->out.f() if vTab->out.enabled is
259339
** true, otherwise this is a no-op. Returns 0 or higher on success, *
260340
** a negative value if vTab->out.f is NULL.
261341
*/
262
-int Th_Vtab_output( Th_Vtab *vTab, char const * zData, int len );
342
+int Th_Vtab_Output( Th_Vtab *vTab, char const * zData, int len );
263343
264344
/*
265
-** Sends the given output through pInterp's v-table's output
266
-** implementation. See Th_Vtab_output() for the argument and
345
+** Sends the given output through pInterp's vtab's output
346
+** implementation. See Th_Vtab_OutputMethods() for the argument and
267347
** return value semantics.
268348
*/
269
-int Th_output( Th_Interp *pInterp, char const * zData, int len );
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
+
270367
271368
/*
272
-** Th_output_f() implementation which sends its output to either
369
+** A Th_Output_f() implementation which sends its output to either
273370
** pState (which must be NULL or a (FILE*)) or stdout (if pState is
274371
** NULL).
275372
*/
276
-int Th_output_f_FILE( char const * zData, int len, void * pState );
373
+int Th_Output_f_FILE( char const * zData, int len, void * pState );
374
+
277375
/*
278
-** Th_Vtab_Output::dispose impl for FILE handles. If pState is not
279
-** one of the standard streams then it is fclose()d.
376
+** A Th_Vtab_OutputMethods::dispose() impl for FILE handles. If pState is not
377
+** one of the standard streams (stdin, stdout, stderr) then it is
378
+** fclose()d.
280379
*/
281
-void Th_output_dispose_FILE( void * pState );
380
+void Th_Output_dispose_FILE( void * pState );
282381
283
-typedef struct Th_Command_Reg Th_Command_Reg;
284382
/*
285383
** A helper type for holding lists of function registration information.
286
-** For use with Th_register_commands().
384
+** For use with Th_RegisterCommands().
287385
*/
288386
struct Th_Command_Reg {
289387
const char *zName; /* Function name. */
290388
Th_CommandProc xProc; /* Callback function */
291389
void *pContext; /* Arbitrary data for the callback. */
292390
};
391
+typedef struct Th_Command_Reg Th_Command_Reg;
293392
294
-/* mkindex cannot do enums enum Th_Render_Flags { */
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
+*/
295400
#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
+*/
296405
#define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)
297
-/*};*/
298406
299
-int Th_Render(const char *z, int flags);
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);
300413
301414
/*
302415
** Adds a piece of memory to the given interpreter, such that:
303416
**
304417
** a) it will be cleaned up when the interpreter is destroyed, by
305418
** calling finalizer(interp, pData). The finalizer may be NULL.
306419
** Cleanup happens in an unspecified/unpredictable order.
307420
**
308
-** b) it can be fetched via Th_Data_Get().
421
+** b) it can be fetched via Th_GetData().
309422
**
310423
** If a given key is added more than once then any previous
311424
** entry is cleaned up before adding it.
312425
**
313426
** Returns 0 on success, non-0 on allocation error.
314427
*/
315
-int Th_Data_Set( Th_Interp * interp, char const * key,
428
+int Th_SetData( Th_Interp * interp, char const * key,
316429
void * pData,
317430
void (*finalizer)( Th_Interp *, void * ) );
318431
319432
/*
320
-** Fetches data added via Th_Data_Set(), or NULL if no data
433
+** Fetches data added via Th_SetData(), or NULL if no data
321434
** has been associated with the given key.
322435
*/
323
-void * Th_Data_Get( Th_Interp * interp, char const * key );
436
+void * Th_GetData( Th_Interp * interp, char const * key );
324437
325438
326439
/*
327440
** Registers a list of commands with the interpreter. pList must be a non-NULL
328441
** pointer to an array of Th_Command_Reg objects, the last one of which MUST
329442
** have a NULL zName field (that is the end-of-list marker).
330443
** Returns TH_OK on success, "something else" on error.
331444
*/
332
-int Th_register_commands( Th_Interp * interp, Th_Command_Reg const * pList );
445
+int Th_RegisterCommands( Th_Interp * interp, Th_Command_Reg const * pList );
333446
447
+#ifdef TH_ENABLE_OB
448
+/*
449
+** Output buffer stack manager for TH. Used/managed by the Th_ob_xxx() functions.
450
+*/
451
+struct Th_Ob_Manager {
452
+ Blob ** aBuf; /* Stack of Blobs */
453
+ int nBuf; /* Number of blobs */
454
+ int cursor; /* Current level (-1=not active) */
455
+ Th_Interp * interp; /* The associated interpreter */
456
+ Th_Vtab_OutputMethods * aOutput
457
+ /* Stack of output routines corresponding
458
+ to the current buffering level.
459
+ Has nBuf entries.
460
+ */;
461
+};
334462
335
-#ifdef TH_ENABLE_OUTBUF
336463
/*
337
-** Manager of a stack of Blob objects for output buffering.
338
-** See Th_ob_manager().
464
+** Manager of a stack of Th_Vtab_Output objects for output buffering.
465
+** It gets its name ("ob") from the similarly-named PHP functionality.
466
+**
467
+** See Th_Ob_GetManager().
468
+**
469
+** Potential TODO: remove the Blob from the interface and replace it
470
+** with a Th_Output_f (or similar) which clients can pass in to have
471
+** the data transfered from Th_Ob_Manager to them. We would also need to
472
+** add APIs for clearing the buffer.
339473
*/
340
-typedef struct Th_Ob_Man Th_Ob_Man;
474
+typedef struct Th_Ob_Manager Th_Ob_Manager;
341475
342476
/*
343
-** Returns the ob manager for the given interpreter. The manager gets
477
+** Returns the ob manager for the given interpreter. The manager gets
344478
** installed by the th_register_ob(). In Fossil ob support is
345479
** installed automatically if it is available at built time.
346480
*/
347
-Th_Ob_Man * Th_ob_manager(Th_Interp *ignored);
348
-
349
-/*
350
-** Returns the top-most Blob in pMan's stack, or NULL
351
-** if buffering is not active.
352
-*/
353
-Blob * Th_ob_current( Th_Ob_Man * pMan );
354
-
355
-/*
356
-** Pushes a new blob onto pMan's stack. On success
357
-** returns TH_OK and assigns *pOut (if pOut is not NULL)
358
-** to the new blob (which is owned by pMan). On error
359
-** pOut is not modified and non-0 is returned.
360
-*/
361
-int Th_ob_push( Th_Ob_Man * pMan, Th_Vtab_Output const * pWriter, Blob ** pOut );
481
+Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *ignored);
482
+
483
+/*
484
+** Returns the top-most Blob in pMan's stack, or NULL if buffering is
485
+** not active or if the current buffering level does not refer to a
486
+** blob. (Note: the latter will never currently be the case, but may
487
+** be if the API is expanded to offer other output direction options,
488
+** e.g. (ob start file /tmp/foo.out).)
489
+*/
490
+Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan );
491
+
492
+/*
493
+** Pushes a new blob onto pMan's stack. On success returns TH_OK and
494
+** assigns *pOut (if pOut is not NULL) to the new blob (which is owned
495
+** by pMan). On error pOut is not modified and non-0 is returned. The
496
+** new blob can be cleaned up via Th_Ob_Pop() or Th_Ob_PopAndFree()
497
+** (please read both to understand the difference!).
498
+*/
499
+int Th_Ob_Push( Th_Ob_Manager * pMan, Th_Vtab_OutputMethods const * pWriter, Blob ** pOut );
362500
363501
/*
364502
** Pops the top-most output buffer off the stack and returns
365503
** it. Returns NULL if there is no current buffer. When the last
366504
** buffer is popped, pMan's internals are cleaned up (but pMan is not
367505
** freed).
368506
**
369507
** The caller owns the returned object and must eventually clean it up
370508
** by first passing it to blob_reset() and then Th_Free() it.
509
+**
510
+** See also: Th_Ob_PopAndFree().
371511
*/
372
-Blob * Th_ob_pop( Th_Ob_Man * pMan );
512
+Blob * Th_Ob_Pop( Th_Ob_Manager * pMan );
513
+
373514
/*
374
-** Convenience form of Th_ob_pop() which pops and frees the
515
+** Convenience form of Th_Ob_Pop() which pops and frees the
375516
** top-most buffer. Returns 0 on success, non-0 if there is no
376
-** stack to pop.
517
+** stack to pop. Thus is can be used in a loop like:
518
+**
519
+** while( !Th_Ob_PopAndFree(theManager) ) {}
377520
*/
378
-int Th_ob_pop_free( Th_Ob_Man * pMan );
521
+int Th_Ob_PopAndFree( Th_Ob_Manager * pMan );
379522
380523
#endif
381
-/* TH_ENABLE_OUTBUF */
524
+/* end TH_ENABLE_OB */
382525
--- src/th.h
+++ src/th.h
@@ -1,76 +1,94 @@
1 #include "config.h"
2
3 /*
4 ** TH_ENABLE_SQLITE, if defined, enables the "query" family of functions.
5 ** They provide SELECT-only access to the repository db.
6 */
7 #define TH_ENABLE_SQLITE
8
9 /*
10 ** TH_ENABLE_OUTBUF, 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_OUTBUF
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_OUTBUF
24 #ifndef INTERFACE
25 #include "blob.h"
26 #endif
27 #endif
28
29 /* This header file defines the external interface to the custom Scripting
30 ** Language (TH) interpreter. TH is very similar to TCL but is not an
31 ** exact clone.
32 */
33
34 /*
35 ** Th_output_f() specifies a generic output routine for use by Th_Vtab
36 ** and friends. Its first argument is the data to write, the second is
37 ** the number of bytes to write, and the 3rd is an
38 ** 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_Output {
52 Th_output_f write; /* output handler */
53 void (*dispose)( void * pState );
54 void * pState; /* final argument for xOut() and dispose()*/
55 char enabled; /* if 0, Th_output() does nothing. */
 
 
56 };
57 typedef struct Th_Vtab_Output Th_Vtab_Output;
58
59 /*
60 ** Shared Th_Vtab_Output instance used for copy-initialization.
 
 
 
 
61 */
62 extern const Th_Vtab_Output Th_Vtab_Output_FILE;
63
64 /*
65 ** Before creating an interpreter, the application must allocate and
66 ** populate an instance of the following structure. It must remain valid
67 ** for the lifetime of the interpreter.
68 */
69 struct Th_Vtab {
70 void *(*xRealloc)(void *, unsigned int); /* Re/deallocation routine. */
71 Th_Vtab_Output out; /* output implementation */
 
 
 
 
 
 
 
 
 
 
 
 
72 };
73 typedef struct Th_Vtab Th_Vtab;
74
75
76 /*
@@ -78,14 +96,25 @@
78 */
79 typedef struct Th_Interp Th_Interp;
80
81
82 /*
83 ** Create and delete interpreters.
 
 
 
 
 
 
 
84 */
85 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
86 void Th_DeleteInterp(Th_Interp *);
 
 
 
 
87
88 /*
89 ** Evaluate an TH program in the stack frame identified by parameter
90 ** iFrame, according to the following rules:
91 **
@@ -113,19 +142,37 @@
113 int Th_GetVar(Th_Interp *, const char *, int);
114 int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
115 int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
116 int Th_UnsetVar(Th_Interp *, const char *, int);
117
118 typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
 
 
 
 
 
 
 
 
 
 
 
 
119
120 /*
121 ** Register new commands.
 
 
 
 
 
 
 
122 */
123 int Th_CreateCommand(
124 Th_Interp *interp,
125 const char *zName,
126 /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */
127 Th_CommandProc xProc,
128 void *pContext,
129 void (*xDel)(Th_Interp *, void *)
130 );
131
@@ -212,16 +259,39 @@
212
213 /*
214 ** Interfaces to register the language extensions.
215 */
216 int th_register_language(Th_Interp *interp); /* th_lang.c */
217 int th_register_query(Th_Interp *interp); /* th_main.c */
218 int th_register_argv(Th_Interp *interp); /* th_main.c */
219 int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
220 int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */
 
 
 
 
 
221 int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222 int th_register_ob(Th_Interp * interp); /* th.c */
 
 
223 /*
224 ** General purpose hash table from th_lang.c.
225 */
226 typedef struct Th_Hash Th_Hash;
227 typedef struct Th_HashEntry Th_HashEntry;
@@ -237,145 +307,218 @@
237 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
238
239 /*
240 ** Useful functions from th_lang.c.
241 */
 
 
 
 
 
242 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
 
243 /*
244 ** Works like Th_WrongNumArgs() but expects (zCmdName,zCmdLen) to be
245 ** the current command's (name,length), i.e. (argv[0],argl[0]).
246 */
247 int Th_WrongNumArgs2(Th_Interp *interp, const char *zCmdName,
248 int zCmdLen, const char *zMsg);
249
250 typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
251 int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
 
252 /*
253 ** Works similarly to Th_CallSubCommand() but adjusts argc/argv/argl
254 ** by 1 before passing on the call to the subcommand.
 
 
255 */
256 int Th_CallSubCommand2(Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl, Th_SubCommand *aSub);
 
257 /*
258 ** Sends the given data through vTab->out.f() if vTab->out.enabled is
259 ** true, otherwise this is a no-op. Returns 0 or higher on success, *
260 ** a negative value if vTab->out.f is NULL.
261 */
262 int Th_Vtab_output( Th_Vtab *vTab, char const * zData, int len );
263
264 /*
265 ** Sends the given output through pInterp's v-table's output
266 ** implementation. See Th_Vtab_output() for the argument and
267 ** return value semantics.
268 */
269 int Th_output( Th_Interp *pInterp, char const * zData, int len );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
271 /*
272 ** Th_output_f() implementation which sends its output to either
273 ** pState (which must be NULL or a (FILE*)) or stdout (if pState is
274 ** NULL).
275 */
276 int Th_output_f_FILE( char const * zData, int len, void * pState );
 
277 /*
278 ** Th_Vtab_Output::dispose impl for FILE handles. If pState is not
279 ** one of the standard streams then it is fclose()d.
 
280 */
281 void Th_output_dispose_FILE( void * pState );
282
283 typedef struct Th_Command_Reg Th_Command_Reg;
284 /*
285 ** A helper type for holding lists of function registration information.
286 ** For use with Th_register_commands().
287 */
288 struct Th_Command_Reg {
289 const char *zName; /* Function name. */
290 Th_CommandProc xProc; /* Callback function */
291 void *pContext; /* Arbitrary data for the callback. */
292 };
 
293
294 /* mkindex cannot do enums enum Th_Render_Flags { */
 
 
 
 
 
 
295 #define Th_Render_Flags_DEFAULT 0
 
 
 
 
296 #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)
297 /*};*/
298
299 int Th_Render(const char *z, int flags);
 
 
 
 
 
300
301 /*
302 ** Adds a piece of memory to the given interpreter, such that:
303 **
304 ** a) it will be cleaned up when the interpreter is destroyed, by
305 ** calling finalizer(interp, pData). The finalizer may be NULL.
306 ** Cleanup happens in an unspecified/unpredictable order.
307 **
308 ** b) it can be fetched via Th_Data_Get().
309 **
310 ** If a given key is added more than once then any previous
311 ** entry is cleaned up before adding it.
312 **
313 ** Returns 0 on success, non-0 on allocation error.
314 */
315 int Th_Data_Set( Th_Interp * interp, char const * key,
316 void * pData,
317 void (*finalizer)( Th_Interp *, void * ) );
318
319 /*
320 ** Fetches data added via Th_Data_Set(), or NULL if no data
321 ** has been associated with the given key.
322 */
323 void * Th_Data_Get( Th_Interp * interp, char const * key );
324
325
326 /*
327 ** Registers a list of commands with the interpreter. pList must be a non-NULL
328 ** pointer to an array of Th_Command_Reg objects, the last one of which MUST
329 ** have a NULL zName field (that is the end-of-list marker).
330 ** Returns TH_OK on success, "something else" on error.
331 */
332 int Th_register_commands( Th_Interp * interp, Th_Command_Reg const * pList );
333
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
335 #ifdef TH_ENABLE_OUTBUF
336 /*
337 ** Manager of a stack of Blob objects for output buffering.
338 ** See Th_ob_manager().
 
 
 
 
 
 
 
339 */
340 typedef struct Th_Ob_Man Th_Ob_Man;
341
342 /*
343 ** Returns the ob manager for the given interpreter. The manager gets
344 ** installed by the th_register_ob(). In Fossil ob support is
345 ** installed automatically if it is available at built time.
346 */
347 Th_Ob_Man * Th_ob_manager(Th_Interp *ignored);
348
349 /*
350 ** Returns the top-most Blob in pMan's stack, or NULL
351 ** if buffering is not active.
352 */
353 Blob * Th_ob_current( Th_Ob_Man * pMan );
354
355 /*
356 ** Pushes a new blob onto pMan's stack. On success
357 ** returns TH_OK and assigns *pOut (if pOut is not NULL)
358 ** to the new blob (which is owned by pMan). On error
359 ** pOut is not modified and non-0 is returned.
360 */
361 int Th_ob_push( Th_Ob_Man * pMan, Th_Vtab_Output const * pWriter, Blob ** pOut );
 
 
 
 
362
363 /*
364 ** Pops the top-most output buffer off the stack and returns
365 ** it. Returns NULL if there is no current buffer. When the last
366 ** buffer is popped, pMan's internals are cleaned up (but pMan is not
367 ** freed).
368 **
369 ** The caller owns the returned object and must eventually clean it up
370 ** by first passing it to blob_reset() and then Th_Free() it.
 
 
371 */
372 Blob * Th_ob_pop( Th_Ob_Man * pMan );
 
373 /*
374 ** Convenience form of Th_ob_pop() which pops and frees the
375 ** top-most buffer. Returns 0 on success, non-0 if there is no
376 ** stack to pop.
 
 
377 */
378 int Th_ob_pop_free( Th_Ob_Man * pMan );
379
380 #endif
381 /* TH_ENABLE_OUTBUF */
382
--- src/th.h
+++ src/th.h
@@ -1,76 +1,94 @@
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
28
29 /* This header file defines the external interface to the custom Scripting
30 ** Language (TH) interpreter. TH is very similar to TCL but is not an
31 ** exact clone.
32 */
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 write; /* output handler */
53 void (*dispose)( 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 write() and dispose()*/
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
70 /*
71 ** Before creating an interpreter, the application must allocate and
72 ** populate an instance of the following structure. It must remain valid
73 ** for the lifetime of the interpreter.
74 */
75 struct Th_Vtab {
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 */
90 };
91 typedef struct Th_Vtab Th_Vtab;
92
93
94 /*
@@ -78,14 +96,25 @@
96 */
97 typedef struct Th_Interp Th_Interp;
98
99
100 /*
101 ** Creates a new interpreter instance using the given v-table. pVtab
102 ** must outlive the returned object, and pVtab->out.dispose() 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.
109 */
110 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab);
111
112 /*
113 ** Frees up all resources associated with interp then frees interp.
114 */
115 void Th_DeleteInterp(Th_Interp *interp);
116
117 /*
118 ** Evaluate an TH program in the stack frame identified by parameter
119 ** iFrame, according to the following rules:
120 **
@@ -113,19 +142,37 @@
142 int Th_GetVar(Th_Interp *, const char *, int);
143 int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
144 int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
145 int Th_UnsetVar(Th_Interp *, const char *, int);
146
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);
160
161 /*
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.
170 */
171 int Th_CreateCommand(
172 Th_Interp *interp,
173 const char *zName,
 
174 Th_CommandProc xProc,
175 void *pContext,
176 void (*xDel)(Th_Interp *, void *)
177 );
178
@@ -212,16 +259,39 @@
259
260 /*
261 ** Interfaces to register the language extensions.
262 */
263 int th_register_language(Th_Interp *interp); /* th_lang.c */
 
 
264 int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
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 */
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
292
293 /*
294 ** General purpose hash table from th_lang.c.
295 */
296 typedef struct Th_Hash Th_Hash;
297 typedef struct Th_HashEntry Th_HashEntry;
@@ -237,145 +307,218 @@
307 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
308
309 /*
310 ** Useful functions from th_lang.c.
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 */
317 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
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
326 typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
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::dispose() impl for FILE handles. If pState is not
377 ** one of the standard streams (stdin, stdout, stderr) then it is
378 ** fclose()d.
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() functions.
450 */
451 struct Th_Ob_Manager {
452 Blob ** aBuf; /* Stack of Blobs */
453 int nBuf; /* Number of blobs */
454 int cursor; /* Current level (-1=not active) */
455 Th_Interp * interp; /* The associated interpreter */
456 Th_Vtab_OutputMethods * aOutput
457 /* Stack of output routines corresponding
458 to the current buffering level.
459 Has nBuf entries.
460 */;
461 };
462
 
463 /*
464 ** Manager of a stack of Th_Vtab_Output objects for output buffering.
465 ** It gets its name ("ob") from the similarly-named PHP functionality.
466 **
467 ** See Th_Ob_GetManager().
468 **
469 ** Potential TODO: remove the Blob from the interface and replace it
470 ** with a Th_Output_f (or similar) which clients can pass in to have
471 ** the data transfered from Th_Ob_Manager to them. We would also need to
472 ** add APIs for clearing the buffer.
473 */
474 typedef struct Th_Ob_Manager Th_Ob_Manager;
475
476 /*
477 ** Returns the ob manager for the given interpreter. The manager gets
478 ** installed by the th_register_ob(). In Fossil ob support is
479 ** installed automatically if it is available at built time.
480 */
481 Th_Ob_Manager * Th_Ob_GetManager(Th_Interp *ignored);
482
483 /*
484 ** Returns the top-most Blob in pMan's stack, or NULL if buffering is
485 ** not active or if the current buffering level does not refer to a
486 ** blob. (Note: the latter will never currently be the case, but may
487 ** be if the API is expanded to offer other output direction options,
488 ** e.g. (ob start file /tmp/foo.out).)
489 */
490 Blob * Th_Ob_GetCurrentBuffer( Th_Ob_Manager * pMan );
491
492 /*
493 ** Pushes a new blob onto pMan's stack. On success returns TH_OK and
494 ** assigns *pOut (if pOut is not NULL) to the new blob (which is owned
495 ** by pMan). On error pOut is not modified and non-0 is returned. The
496 ** new blob can be cleaned up via Th_Ob_Pop() or Th_Ob_PopAndFree()
497 ** (please read both to understand the difference!).
498 */
499 int Th_Ob_Push( Th_Ob_Manager * pMan, Th_Vtab_OutputMethods const * pWriter, Blob ** pOut );
500
501 /*
502 ** Pops the top-most output buffer off the stack and returns
503 ** it. Returns NULL if there is no current buffer. When the last
504 ** buffer is popped, pMan's internals are cleaned up (but pMan is not
505 ** freed).
506 **
507 ** The caller owns the returned object and must eventually clean it up
508 ** by first passing it to blob_reset() and then Th_Free() it.
509 **
510 ** See also: Th_Ob_PopAndFree().
511 */
512 Blob * Th_Ob_Pop( Th_Ob_Manager * pMan );
513
514 /*
515 ** Convenience form of Th_Ob_Pop() which pops and frees the
516 ** top-most buffer. Returns 0 on success, non-0 if there is no
517 ** stack to pop. Thus is can be used in a loop like:
518 **
519 ** while( !Th_Ob_PopAndFree(theManager) ) {}
520 */
521 int Th_Ob_PopAndFree( Th_Ob_Manager * pMan );
522
523 #endif
524 /* end TH_ENABLE_OB */
525
+1 -1
--- src/th_lang.c
+++ src/th_lang.c
@@ -1088,8 +1088,8 @@
10881088
{"continue", simple_command, (void *)TH_CONTINUE},
10891089
{"error", simple_command, (void *)TH_ERROR},
10901090
10911091
{0, 0, 0}
10921092
};
1093
- rc = Th_register_commands(interp, aCommand);
1093
+ rc = Th_RegisterCommands(interp, aCommand);
10941094
return rc;
10951095
}
10961096
--- src/th_lang.c
+++ src/th_lang.c
@@ -1088,8 +1088,8 @@
1088 {"continue", simple_command, (void *)TH_CONTINUE},
1089 {"error", simple_command, (void *)TH_ERROR},
1090
1091 {0, 0, 0}
1092 };
1093 rc = Th_register_commands(interp, aCommand);
1094 return rc;
1095 }
1096
--- src/th_lang.c
+++ src/th_lang.c
@@ -1088,8 +1088,8 @@
1088 {"continue", simple_command, (void *)TH_CONTINUE},
1089 {"error", simple_command, (void *)TH_ERROR},
1090
1091 {0, 0, 0}
1092 };
1093 rc = Th_RegisterCommands(interp, aCommand);
1094 return rc;
1095 }
1096
+237 -151
--- src/th_main.c
+++ src/th_main.c
@@ -18,18 +18,17 @@
1818
** This file contains an interface between the TH scripting language
1919
** (an independent project) and fossil.
2020
*/
2121
#include "config.h"
2222
#include "th_main.h"
23
+
24
+#ifdef TH_ENABLE_QUERY
2325
#ifndef INTERFACE
24
-#include "blob.h"
25
-#endif
26
-#ifdef TH_ENABLE_SQLITE
2726
#include "sqlite3.h"
2827
#endif
28
+#endif
2929
30
-/*#include "th_main.h"*/
3130
/*
3231
** Global variable counting the number of outstanding calls to malloc()
3332
** made by the th1 implementation. This is used to catch memory leaks
3433
** in the interpreter. Obviously, it also means th1 is not threadsafe.
3534
*/
@@ -50,32 +49,28 @@
5049
nOutstandingMalloc--;
5150
}
5251
fossil_free(p);
5352
}
5453
54
+/*
55
+** Default Th_Vtab::xRealloc() implementation.
56
+*/
5557
static void *xRealloc(void * p, unsigned int n){
58
+ assert(n>=0 && "Invalid memory (re/de)allocation size.");
5659
if(0 == n){
5760
xFree(p);
5861
return NULL;
5962
}else if(NULL == p){
6063
return xMalloc(n);
6164
}else{
6265
return fossil_realloc(p, n)
63
- /* FIXME: try to find some reasonable nOutstandingMalloc
64
- heuristics, e.g. if !p then ++, if !n then --, etc.
66
+ /* In theory nOutstandingMalloc doesn't need to be updated here
67
+ unless xRealloc() is sorely misused.
6568
*/;
6669
}
6770
}
6871
69
-static Th_Vtab vtab = { xRealloc, {
70
- NULL /*write()*/,
71
- NULL/*dispose()*/,
72
- NULL/*pState*/,
73
- 1/*enabled*/
74
- }
75
-};
76
-
7772
/*
7873
** Generate a TH1 trace message if debugging is enabled.
7974
*/
8075
void Th_Trace(const char *zFormat, ...){
8176
va_list ap;
@@ -85,10 +80,18 @@
8580
}
8681
8782
8883
/*
8984
** 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.
9093
*/
9194
static int enableOutput = 1;
9295
9396
/*
9497
** TH command: enable_output BOOLEAN
@@ -106,16 +109,18 @@
106109
return Th_WrongNumArgs2(interp,
107110
argv[0], argl[0],
108111
"BOOLEAN");
109112
}else{
110113
int rc = Th_ToInt(interp, argv[1], argl[1], &enableOutput);
111
- vtab.out.enabled = enableOutput;
112114
return rc;
113115
}
114116
}
115117
116
-int Th_output_f_cgi_content( char const * zData, int nData, void * pState ){
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 ){
117122
cgi_append_content(zData, nData);
118123
return nData;
119124
}
120125
121126
@@ -132,27 +137,39 @@
132137
if( n<0 ) n = strlen(z);
133138
if( encode ){
134139
z = htmlize(z, n);
135140
n = strlen(z);
136141
}
137
- Th_output( pInterp, z, n );
142
+ Th_Output( pInterp, z, n );
138143
if( encode ) fossil_free((char*)z);
139144
}
140145
}
141146
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
+*/
142154
struct PutsCmdData {
143
- char escapeHtml;
144
- char const * sep;
145
- char const * eol;
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. */
146160
};
147161
typedef struct PutsCmdData PutsCmdData;
148162
149163
/*
150164
** TH command: puts STRING
151165
** TH command: html STRING
152166
**
153
-** 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.
154171
*/
155172
static int putsCmd(
156173
Th_Interp *interp,
157174
void *pConvert,
158175
int argc,
@@ -229,11 +246,12 @@
229246
free(zOut);
230247
return TH_OK;
231248
}
232249
233250
#if 0
234
-/* i'm not sure we need this */
251
+/* This is not yet needed, but something like it may become useful for
252
+ custom page/command support, for rendering snippets/templates. */
235253
/*
236254
** TH command: render STRING
237255
**
238256
** Render the input string as TH1.
239257
*/
@@ -247,11 +265,11 @@
247265
if( argc<2 ){
248266
return Th_WrongNumArgs2(interp,
249267
argv[0], argl[0],
250268
"STRING ?STRING...?");
251269
}else{
252
- Th_Ob_Man * man = Th_ob_manager(interp);
270
+ Th_Ob_Manager * man = Th_Ob_GetManager(interp);
253271
Blob * b = NULL;
254272
Blob buf = empty_blob;
255273
int rc, i;
256274
/*FIXME: assert(NULL != man && man->interp==interp);*/
257275
man->interp = interp;
@@ -263,18 +281,18 @@
263281
for( i = 1; TH_OK==rc && i < argc; ++i ){
264282
char const * str = argv[i];
265283
blob_append( &buf, str, argl[i] );
266284
/*rc = Th_Render( str, Th_Render_Flags_NO_DOLLAR_DEREF );*/
267285
}
268
- rc = Th_ob_push( man, &b );
286
+ rc = Th_Ob_Push( man, &b );
269287
if(rc){
270288
blob_reset( &buf );
271289
return rc;
272290
}
273291
rc = Th_Render( buf.aData, Th_Render_Flags_DEFAULT );
274292
blob_reset(&buf);
275
- b = Th_ob_pop( man );
293
+ b = Th_Ob_Pop( man );
276294
if(TH_OK==rc){
277295
Th_SetResult( interp, b->aData, b->nUsed );
278296
}
279297
blob_reset( b );
280298
Th_Free( interp, b );
@@ -544,15 +562,12 @@
544562
return TH_OK;
545563
}
546564
547565
548566
#ifdef TH_ENABLE_ARGV
549
-extern const char *find_option(const char *zLong,
550
- const char *zShort,
551
- int hasArg) /* from main.c */;
552567
/*
553
-** TH Syntax:
568
+** TH command:
554569
**
555570
** argv len
556571
**
557572
** Returns the number of command-line arguments.
558573
*/
@@ -568,11 +583,11 @@
568583
}
569584
570585
571586
572587
/*
573
-** TH Syntax:
588
+** TH command:
574589
**
575590
** argv at Index
576591
**
577592
** Returns the raw argument at the given index, throwing if
578593
** out of bounds.
@@ -607,11 +622,11 @@
607622
return TH_OK;
608623
}
609624
610625
611626
/*
612
-** TH Syntax:
627
+** TH command:
613628
**
614629
** argv getstr longName ??shortName? ?defaultValue??
615630
**
616631
** Functions more or less like Fossil's find_option().
617632
** If the given argument is found then its value is returned,
@@ -678,15 +693,15 @@
678693
Th_SetResult( interp, zVal, zVal ? strlen(zVal) : 0 );
679694
return TH_OK;
680695
}
681696
682697
/*
683
-** TH Syntax:
698
+** TH command:
684699
**
685700
** argv getbool longName ??shortName? ?defaultValue??
686701
**
687
-** Works just like argv_getstr but treats any empty value or one
702
+** Works just like argv getstr but treats any empty value or one
688703
** starting with the digit '0' as a boolean false.
689704
**
690705
** Returns the result as an integer 0 (false) or 1 (true).
691706
*/
692707
static int argvFindOptionBoolCmd(
@@ -751,13 +766,16 @@
751766
Th_SetResultInt( interp, zVal ? 1 : 0 );
752767
return TH_OK;
753768
}
754769
755770
/*
756
-** TH Syntax:
771
+** TH command:
757772
**
758773
** 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).
759777
*/
760778
static int argvFindOptionIntCmd(
761779
Th_Interp *interp,
762780
void *p,
763781
int argc,
@@ -811,10 +829,17 @@
811829
Th_ToInt(interp, zVal, strlen(zVal), &val);
812830
Th_SetResultInt( interp, val );
813831
return TH_OK;
814832
}
815833
834
+/*
835
+** TH command:
836
+**
837
+** argv subcommand
838
+**
839
+** This is the top-level dispatching function.
840
+*/
816841
static int argvTopLevelCmd(
817842
Th_Interp *interp,
818843
void *ctx,
819844
int argc,
820845
const char **argv,
@@ -834,63 +859,58 @@
834859
int th_register_argv(Th_Interp *interp){
835860
static Th_Command_Reg aCommand[] = {
836861
{"argv", argvTopLevelCmd, 0 },
837862
{0, 0, 0}
838863
};
839
- Th_register_commands( interp, aCommand );
864
+ Th_RegisterCommands( interp, aCommand );
840865
}
841866
842867
#endif
843868
/* end TH_ENABLE_ARGV */
844869
845
-#ifdef TH_ENABLE_SQLITE
870
+#ifdef TH_ENABLE_QUERY
846871
847872
/*
848873
** Adds the given prepared statement to the interpreter. Returns the
849874
** statement's opaque identifier (a positive value). Ownerships of
850875
** pStmt is transfered to interp and it must be cleaned up by the
851
-** client by calling Th_FinalizeStmt(), passing it the value returned
876
+** client by calling Th_query_FinalizeStmt(), passing it the value returned
852877
** by this function.
853878
**
854879
** If interp is destroyed before all statements are finalized,
855880
** it will finalize them but may emit a warning message.
856881
*/
857
-static int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
858
-
859
-/*
860
-** Expects stmtId to be a statement identifier returned by
861
-** Th_AddStmt(). On success, finalizes the statement and returns 0.
862
-** On error (statement not found) non-0 is returned. After this
863
-** call, some subsequent call to Th_AddStmt() may return the
864
-** same statement ID.
865
-*/
866
-static int Th_FinalizeStmt(Th_Interp *interp, int stmtId);
867
-static int Th_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt *);
868
-
869
-/*
870
-** Fetches the statement with the given ID, as returned by
871
-** Th_AddStmt(). Returns NULL if stmtId does not refer (or no longer
872
-** refers) to a statement added via Th_AddStmt().
873
-*/
874
-static sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId);
875
-
876
-
877
-struct Th_Sqlite {
878
- sqlite3_stmt ** aStmt;
879
- int nStmt;
880
- int colCmdIndex;
882
+static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
883
+
884
+
885
+/*
886
+** Internal state for the "query" API.
887
+*/
888
+struct Th_Query {
889
+ sqlite3_stmt ** aStmt; /* Array of statement handles. */
890
+ int nStmt; /* number of entries in aStmt. */
891
+ int colCmdIndex; /* column index argument. Set by some top-level dispatchers
892
+ for their subcommands.
893
+ */
881894
};
882
-#define Th_Sqlite_KEY "Th_Sqlite"
883
-typedef struct Th_Sqlite Th_Sqlite;
895
+/*
896
+** Internal key for use with Th_Data_Add().
897
+*/
898
+#define Th_Query_KEY "Th_Query"
899
+typedef struct Th_Query Th_Query;
884900
885
-static Th_Sqlite * Th_sqlite_manager( Th_Interp * interp ){
886
- void * p = Th_Data_Get( interp, Th_Sqlite_KEY );
887
- return p ? (Th_Sqlite*)p : NULL;
901
+/*
902
+** Returns the Th_Query object associated with the given interpreter,
903
+** or 0 if there is not one.
904
+*/
905
+static Th_Query * Th_query_manager( Th_Interp * interp ){
906
+ void * p = Th_GetData( interp, Th_Query_KEY );
907
+ return p ? (Th_Query*)p : NULL;
888908
}
889909
890
-static int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
891
- Th_Sqlite * sq = Th_sqlite_manager(interp);
910
+static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
911
+ Th_Query * sq = Th_query_manager(interp);
892912
int i, x;
893913
sqlite3_stmt * s;
894914
sqlite3_stmt ** list = sq->aStmt;
895915
for( i = 0; i < sq->nStmt; ++i ){
896916
s = list[i];
@@ -910,12 +930,19 @@
910930
sq->aStmt = list;
911931
return x + 1;
912932
}
913933
914934
915
-static int Th_FinalizeStmt(Th_Interp *interp, int stmtId){
916
- Th_Sqlite * sq = Th_sqlite_manager(interp);
935
+/*
936
+** Expects stmtId to be a statement identifier returned by
937
+** Th_query_AddStmt(). On success, finalizes the statement and returns 0.
938
+** On error (statement not found) non-0 is returned. After this
939
+** call, some subsequent call to Th_query_AddStmt() may return the
940
+** same statement ID.
941
+*/
942
+static int Th_query_FinalizeStmt(Th_Interp *interp, int stmtId){
943
+ Th_Query * sq = Th_query_manager(interp);
917944
sqlite3_stmt * st;
918945
int rc = 0;
919946
assert( stmtId>0 && stmtId<=sq->nStmt );
920947
st = sq->aStmt[stmtId-1];
921948
if(NULL != st){
@@ -925,12 +952,16 @@
925952
}else{
926953
return 1;
927954
}
928955
}
929956
930
-static int Th_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt * pSt){
931
- Th_Sqlite * sq = Th_sqlite_manager(interp);
957
+/*
958
+** Works like Th_query_FinalizeStmt() but takes a statement pointer, which
959
+** must have been Th_query_AddStmt()'d to the given interpreter.
960
+*/
961
+static int Th_query_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt * pSt){
962
+ Th_Query * sq = Th_query_manager(interp);
932963
int i = 0;
933964
sqlite3_stmt * st = NULL;
934965
int rc = 0;
935966
for( ; i < sq->nStmt; ++i ){
936967
st = sq->aStmt[i];
@@ -945,42 +976,50 @@
945976
return 1;
946977
}
947978
}
948979
949980
950
-static sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){
951
- Th_Sqlite * sq = Th_sqlite_manager(interp);
952
- return ((stmtId<1) || (stmtId > sq->nStmt))
981
+/*
982
+** Fetches the statement with the given ID, as returned by
983
+** Th_query_AddStmt(). Returns NULL if stmtId does not refer (or no longer
984
+** refers) to a statement added via Th_query_AddStmt().
985
+*/
986
+static sqlite3_stmt * Th_query_GetStmt(Th_Interp *interp, int stmtId){
987
+ Th_Query * sq = Th_query_manager(interp);
988
+ return (!sq || (stmtId<1) || (stmtId > sq->nStmt))
953989
? NULL
954990
: sq->aStmt[stmtId-1];
955991
}
956992
957993
994
+/*
995
+** Th_GCEntry finalizer which requires that p be a (Th_Query*).
996
+*/
958997
static void finalizerSqlite( Th_Interp * interp, void * p ){
959
- Th_Sqlite * sq = (Th_Sqlite *)p;
998
+ Th_Query * sq = (Th_Query *)p;
960999
int i;
9611000
sqlite3_stmt * st = NULL;
9621001
if(!sq) {
963
- fossil_warning("Got a finalizer call for a NULL Th_Sqlite.");
1002
+ fossil_warning("Got a finalizer call for a NULL Th_Query.");
9641003
return;
9651004
}
9661005
for( i = 0; i < sq->nStmt; ++i ){
9671006
st = sq->aStmt[i];
9681007
if(NULL != st){
9691008
fossil_warning("Auto-finalizing unfinalized "
9701009
"statement id #%d: %s",
9711010
i+1, sqlite3_sql(st));
972
- Th_FinalizeStmt( interp, i+1 );
1011
+ Th_query_FinalizeStmt( interp, i+1 );
9731012
}
9741013
}
9751014
Th_Free(interp, sq->aStmt);
9761015
Th_Free(interp, sq);
9771016
}
9781017
9791018
9801019
/*
981
-** TH Syntax:
1020
+** TH command:
9821021
**
9831022
** query prepare SQL
9841023
**
9851024
** Returns an opaque statement identifier.
9861025
*/
@@ -1016,11 +1055,11 @@
10161055
assert(NULL != errMsg);
10171056
assert(NULL == pStmt);
10181057
Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
10191058
return TH_ERROR;
10201059
}
1021
- rc = Th_AddStmt( interp, pStmt );
1060
+ rc = Th_query_AddStmt( interp, pStmt );
10221061
assert( rc >= 0 && "AddStmt failed.");
10231062
Th_SetResultInt( interp, rc );
10241063
return TH_OK;
10251064
}
10261065
@@ -1039,21 +1078,21 @@
10391078
sqlite3_stmt * pStmt = NULL;
10401079
if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
10411080
if(stmtId){
10421081
*stmtId = rc;
10431082
}
1044
- pStmt = Th_GetStmt( interp, rc );
1083
+ pStmt = Th_query_GetStmt( interp, rc );
10451084
if(NULL==pStmt){
10461085
Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
10471086
}
10481087
}
10491088
return pStmt;
10501089
10511090
}
10521091
10531092
/*
1054
-** TH Syntax:
1093
+** TH command:
10551094
**
10561095
** query finalize stmtId
10571096
** query stmtId finalize
10581097
**
10591098
** sqlite3_finalize()s the given statement.
@@ -1083,11 +1122,11 @@
10831122
Th_ErrorMessage(interp, "Not a valid statement handle argument.", NULL, 0);
10841123
return TH_ERROR;
10851124
}
10861125
}
10871126
assert( NULL != pStmt );
1088
- rc = Th_FinalizeStmt2( interp, pStmt );
1127
+ rc = Th_query_FinalizeStmt2( interp, pStmt );
10891128
Th_SetResultInt( interp, rc );
10901129
return TH_OK;
10911130
}
10921131
10931132
/*
@@ -1148,11 +1187,11 @@
11481187
return 0;
11491188
}
11501189
}
11511190
11521191
/*
1153
-** TH Syntax:
1192
+** TH command:
11541193
**
11551194
** query step stmtId
11561195
** query stmtId step
11571196
**
11581197
** Steps the given statement handle. Returns 0 at the end of the set,
@@ -1190,10 +1229,18 @@
11901229
}
11911230
Th_SetResultInt( interp, rc );
11921231
return TH_OK;
11931232
}
11941233
1234
+/*
1235
+** TH command:
1236
+**
1237
+** query StmtId reset
1238
+** query reset StmtId
1239
+**
1240
+** Equivalent to sqlite3_reset().
1241
+*/
11951242
static int queryResetCmd(
11961243
Th_Interp *interp,
11971244
void *p,
11981245
int argc,
11991246
const char **argv,
@@ -1209,11 +1256,11 @@
12091256
}
12101257
}
12111258
12121259
12131260
/*
1214
-** TH Syntax:
1261
+** TH command:
12151262
**
12161263
** query col string stmtId Index
12171264
** query stmtId col string Index
12181265
** query stmtId col Index string
12191266
**
@@ -1224,11 +1271,11 @@
12241271
void *p,
12251272
int argc,
12261273
const char **argv,
12271274
int *argl
12281275
){
1229
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1276
+ Th_Query * sq = Th_query_manager(interp);
12301277
int index = sq->colCmdIndex;
12311278
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
12321279
int requireArgc = pStmt ? 2 : 3;
12331280
char const * val;
12341281
int valLen;
@@ -1251,11 +1298,11 @@
12511298
Th_SetResult( interp, val, valLen );
12521299
return TH_OK;
12531300
}
12541301
12551302
/*
1256
-** TH Syntax:
1303
+** TH command:
12571304
**
12581305
** query col int stmtId Index
12591306
** query stmtId col int Index
12601307
** query stmtId col Index int
12611308
**
@@ -1266,11 +1313,11 @@
12661313
void *p,
12671314
int argc,
12681315
const char **argv,
12691316
int *argl
12701317
){
1271
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1318
+ Th_Query * sq = Th_query_manager(interp);
12721319
int index = sq->colCmdIndex;
12731320
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
12741321
int requireArgc = pStmt ? 2 : 3;
12751322
int rc = 0;
12761323
if( index >= 0 ) --requireArgc;
@@ -1290,11 +1337,11 @@
12901337
Th_SetResultInt( interp, sqlite3_column_int( pStmt, index ) );
12911338
return TH_OK;
12921339
}
12931340
12941341
/*
1295
-** TH Syntax:
1342
+** TH command:
12961343
**
12971344
** query col double stmtId Index
12981345
** query stmtId col double Index
12991346
** query stmtId col Index double
13001347
**
@@ -1305,11 +1352,11 @@
13051352
void *p,
13061353
int argc,
13071354
const char **argv,
13081355
int *argl
13091356
){
1310
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1357
+ Th_Query * sq = Th_query_manager(interp);
13111358
int index = sq->colCmdIndex;
13121359
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
13131360
int requireArgc = pStmt ? 2 : 3;
13141361
double rc = 0;
13151362
if( index >= 0 ) --requireArgc;
@@ -1329,14 +1376,14 @@
13291376
Th_SetResultDouble( interp, sqlite3_column_double( pStmt, index ) );
13301377
return TH_OK;
13311378
}
13321379
13331380
/*
1334
-** TH Syntax:
1381
+** TH command:
13351382
**
13361383
** query col isnull stmtId Index
1337
-** query stmtId col is_null Index
1384
+** query stmtId col isnull Index
13381385
** query stmtId col Index isnull
13391386
**
13401387
** Returns non-0 if the given 0-based result column index contains
13411388
** an SQL NULL value, else returns 0.
13421389
*/
@@ -1345,11 +1392,11 @@
13451392
void *p,
13461393
int argc,
13471394
const char **argv,
13481395
int *argl
13491396
){
1350
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1397
+ Th_Query * sq = Th_query_manager(interp);
13511398
int index = sq->colCmdIndex;
13521399
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
13531400
int requireArgc = pStmt ? 2 : 3;
13541401
if( index >= 0 ) --requireArgc;
13551402
double rc = 0;
@@ -1371,11 +1418,11 @@
13711418
? 1 : 0);
13721419
return TH_OK;
13731420
}
13741421
13751422
/*
1376
-** TH Syntax:
1423
+** TH command:
13771424
**
13781425
** query col type stmtId Index
13791426
** query stmtId col type Index
13801427
** query stmtId col Index type
13811428
**
@@ -1388,11 +1435,11 @@
13881435
void *p,
13891436
int argc,
13901437
const char **argv,
13911438
int *argl
13921439
){
1393
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1440
+ Th_Query * sq = Th_query_manager(interp);
13941441
int index = sq->colCmdIndex;
13951442
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
13961443
int requireArgc = pStmt ? 2 : 3;
13971444
if( index >= 0 ) --requireArgc;
13981445
double rc = 0;
@@ -1412,11 +1459,11 @@
14121459
Th_SetResultInt( interp, sqlite3_column_type( pStmt, index ) );
14131460
return TH_OK;
14141461
}
14151462
14161463
/*
1417
-** TH Syntax:
1464
+** TH command:
14181465
**
14191466
** query col count stmtId
14201467
** query stmtId col count
14211468
**
14221469
** Returns the number of result columns in the query.
@@ -1446,11 +1493,11 @@
14461493
Th_SetResultInt( interp, rc );
14471494
return TH_OK;
14481495
}
14491496
14501497
/*
1451
-** TH Syntax:
1498
+** TH command:
14521499
**
14531500
** query col name stmtId Index
14541501
** query stmtId col name Index
14551502
** query stmtId col Index name
14561503
**
@@ -1461,11 +1508,11 @@
14611508
void *p,
14621509
int argc,
14631510
const char **argv,
14641511
int *argl
14651512
){
1466
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1513
+ Th_Query * sq = Th_query_manager(interp);
14671514
int index = sq->colCmdIndex;
14681515
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
14691516
int requireArgc = pStmt ? 2 : 3;
14701517
char const * val;
14711518
int rc = 0;
@@ -1493,11 +1540,11 @@
14931540
return TH_OK;
14941541
}
14951542
}
14961543
14971544
/*
1498
-** TH Syntax:
1545
+** TH command:
14991546
**
15001547
** query col time stmtId Index format
15011548
** query stmtId col name Index format
15021549
** query stmtId col Index name format
15031550
**
@@ -1508,11 +1555,11 @@
15081555
void *ctx,
15091556
int argc,
15101557
const char **argv,
15111558
int *argl
15121559
){
1513
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1560
+ Th_Query * sq = Th_query_manager(interp);
15141561
int index = sq->colCmdIndex;
15151562
sqlite3_stmt * pStmt = (sqlite3_stmt*)ctx;
15161563
int minArgs = pStmt ? 3 : 4;
15171564
int argPos;
15181565
char const * val;
@@ -1555,10 +1602,17 @@
15551602
Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
15561603
fossil_free(fval);
15571604
return 0;
15581605
}
15591606
1607
+/*
1608
+** TH command:
1609
+**
1610
+** query strftime TimeVal ?Modifiers...?
1611
+**
1612
+** Acts as a proxy to sqlite3's strftime() SQL function.
1613
+*/
15601614
static int queryStrftimeCmd(
15611615
Th_Interp *interp,
15621616
void *ctx,
15631617
int argc,
15641618
const char **argv,
@@ -1592,11 +1646,11 @@
15921646
return 0;
15931647
}
15941648
15951649
15961650
/*
1597
-** TH Syntax:
1651
+** TH command:
15981652
**
15991653
** query bind null stmtId Index
16001654
** query stmtId bind null Index
16011655
**
16021656
** Binds a value to the given 1-based parameter index.
@@ -1606,11 +1660,11 @@
16061660
void *p,
16071661
int argc,
16081662
const char **argv,
16091663
int *argl
16101664
){
1611
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1665
+ Th_Query * sq = Th_query_manager(interp);
16121666
int index = sq->colCmdIndex;
16131667
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
16141668
int requireArgc = pStmt ? 2 : 3;
16151669
if( index > 0 ) --requireArgc;
16161670
int rc;
@@ -1635,11 +1689,11 @@
16351689
return TH_OK;
16361690
}
16371691
16381692
16391693
/*
1640
-** TH Syntax:
1694
+** TH command:
16411695
**
16421696
** query bind string stmtId Index Value
16431697
** query stmtId bind string Index Value
16441698
**
16451699
** Binds a value to the given 1-based parameter index.
@@ -1649,11 +1703,11 @@
16491703
void *p,
16501704
int argc,
16511705
const char **argv,
16521706
int *argl
16531707
){
1654
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1708
+ Th_Query * sq = Th_query_manager(interp);
16551709
int index = sq->colCmdIndex;
16561710
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
16571711
int requireArgc = pStmt ? 3 : 4;
16581712
int rc;
16591713
int argPos;
@@ -1682,11 +1736,11 @@
16821736
Th_SetResultInt( interp, 0 );
16831737
return TH_OK;
16841738
}
16851739
16861740
/*
1687
-** TH Syntax:
1741
+** TH command:
16881742
**
16891743
** query bind int stmtId Index Value
16901744
** query stmtId bind int Index Value
16911745
**
16921746
** Binds a value to the given 1-based parameter index.
@@ -1696,11 +1750,11 @@
16961750
void *p,
16971751
int argc,
16981752
const char **argv,
16991753
int *argl
17001754
){
1701
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1755
+ Th_Query * sq = Th_query_manager(interp);
17021756
int index = sq->colCmdIndex;
17031757
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
17041758
int requireArgc = pStmt ? 3 : 4;
17051759
int rc;
17061760
int argPos;
@@ -1734,11 +1788,11 @@
17341788
Th_SetResultInt( interp, 0 );
17351789
return TH_OK;
17361790
}
17371791
17381792
/*
1739
-** TH Syntax:
1793
+** TH command:
17401794
**
17411795
** query bind double stmtId Index Value
17421796
** query stmtId bind double Index Value
17431797
**
17441798
** Binds a value to the given 1-based parameter index.
@@ -1748,11 +1802,11 @@
17481802
void *p,
17491803
int argc,
17501804
const char **argv,
17511805
int *argl
17521806
){
1753
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1807
+ Th_Query * sq = Th_query_manager(interp);
17541808
int index = sq->colCmdIndex;
17551809
sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
17561810
int requireArgc = pStmt ? 3 : 4;
17571811
int rc;
17581812
int argPos;
@@ -1785,10 +1839,18 @@
17851839
}
17861840
Th_SetResultInt( interp, 0 );
17871841
return TH_OK;
17881842
}
17891843
1844
+/*
1845
+** TH command:
1846
+**
1847
+** bind subcommand StmtId...
1848
+** bind StmtId subcommand...
1849
+**
1850
+** This is the top-level dispatcher for the "bind" family of commands.
1851
+*/
17901852
static int queryBindTopLevelCmd(
17911853
Th_Interp *interp,
17921854
void *ctx,
17931855
int argc,
17941856
const char **argv,
@@ -1800,15 +1862,15 @@
18001862
{"double", queryBindDoubleCmd},
18011863
{"null", queryBindNullCmd},
18021864
{"string", queryBindStringCmd},
18031865
{0, 0}
18041866
};
1805
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1867
+ Th_Query * sq = Th_query_manager(interp);
18061868
assert(NULL != sq);
18071869
if( 1 == argc ){
18081870
Th_WrongNumArgs2( interp, argv[0], argl[0],
1809
- "subcommand");
1871
+ "subcommand: int|double|null|string");
18101872
return TH_ERROR;
18111873
}else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
18121874
if(colIndex <0){
18131875
Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
18141876
return TH_ERROR;
@@ -1820,10 +1882,18 @@
18201882
sq->colCmdIndex = colIndex;
18211883
Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
18221884
18231885
}
18241886
1887
+/*
1888
+** TH command:
1889
+**
1890
+** query col subcommand ...
1891
+** query StmtId col subcommand ...
1892
+**
1893
+** This is the top-level dispatcher for the col subcommands.
1894
+*/
18251895
static int queryColTopLevelCmd(
18261896
Th_Interp *interp,
18271897
void *ctx,
18281898
int argc,
18291899
const char **argv,
@@ -1841,10 +1911,16 @@
18411911
{"time", queryColTimeCmd},
18421912
{"type", queryColTypeCmd},
18431913
{0, 0}
18441914
};
18451915
static Th_SubCommand aSubWithIndex[] = {
1916
+ /*
1917
+ This subset is coded to accept the column index
1918
+ either before the subcommand name or after it.
1919
+ If called like (bind StmtId subcommand) then
1920
+ only these commands will be checked.
1921
+ */
18461922
{"is_null", queryColIsNullCmd},
18471923
{"isnull", queryColIsNullCmd},
18481924
{"name", queryColNameCmd},
18491925
{"double", queryColDoubleCmd},
18501926
{"int", queryColIntCmd},
@@ -1851,15 +1927,17 @@
18511927
{"string", queryColStringCmd},
18521928
{"time", queryColTimeCmd},
18531929
{"type", queryColTypeCmd},
18541930
{0, 0}
18551931
};
1856
- Th_Sqlite * sq = Th_sqlite_manager(interp);
1932
+ Th_Query * sq = Th_query_manager(interp);
18571933
assert(NULL != sq);
18581934
if( 1 == argc ){
18591935
Th_WrongNumArgs2( interp, argv[0], argl[0],
1860
- "subcommand");
1936
+ "subcommand: "
1937
+ "count|is_null|isnull|name|"
1938
+ "double|int|string|time|type");
18611939
return TH_ERROR;
18621940
}else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
18631941
if(colIndex <0){
18641942
Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
18651943
return TH_ERROR;
@@ -1872,10 +1950,18 @@
18721950
Th_CallSubCommand2( interp, ctx, argc, argv, argl,
18731951
(colIndex<0) ? aSub : aSubWithIndex );
18741952
}
18751953
18761954
1955
+/*
1956
+** TH command:
1957
+**
1958
+** query subcommand ...
1959
+** query StmtId subcommand ...
1960
+**
1961
+** This is the top-level dispatcher for the query subcommand.
1962
+*/
18771963
static int queryTopLevelCmd(
18781964
Th_Interp *interp,
18791965
void *ctx,
18801966
int argc,
18811967
const char **argv,
@@ -1884,48 +1970,50 @@
18841970
int stmtId = 0;
18851971
sqlite3_stmt * pStmt = NULL;
18861972
static Th_SubCommand aSubAll[] = {
18871973
{"bind", queryBindTopLevelCmd},
18881974
{"col", queryColTopLevelCmd},
1975
+ {"finalize", queryFinalizeCmd},
1976
+ {"prepare", queryPrepareCmd},
18891977
{"reset", queryResetCmd},
18901978
{"step", queryStepCmd},
1891
- {"finalize", queryFinalizeCmd},
1892
- {"prepare", queryPrepareCmd},
18931979
{"strftime", queryStrftimeCmd},
18941980
{0, 0}
18951981
};
18961982
static Th_SubCommand aSubWithStmt[] = {
1897
- /* These entries are coded to deal with
1898
- being supplied a statement via pStmt
1899
- or via one of their args.
1900
- */
1983
+ /* This subset is coded to deal with being supplied a statement
1984
+ via pStmt or via one of their args. When called like (query
1985
+ StmtId ...) only these subcommands will be checked.*/
19011986
{"bind", queryBindTopLevelCmd},
19021987
{"col", queryColTopLevelCmd},
19031988
{"step", queryStepCmd},
19041989
{"finalize", queryFinalizeCmd},
19051990
{"reset", queryResetCmd},
19061991
{0, 0}
19071992
};
19081993
19091994
1910
- assert( NULL != Th_sqlite_manager(interp) );
1995
+ assert( NULL != Th_query_manager(interp) );
19111996
if( 1 == argc ){
19121997
Th_WrongNumArgs2( interp, argv[0], argl[0],
1913
- "subcommand");
1998
+ "subcommand: bind|col|finalize|prepare|reset|step|strftime");
19141999
return TH_ERROR;
19152000
}else if( 0 == Th_TryInt(interp,argv[1], argl[1], &stmtId) ){
19162001
++argv;
19172002
++argl;
19182003
--argc;
1919
- pStmt = Th_GetStmt( interp, stmtId );
2004
+ pStmt = Th_query_GetStmt( interp, stmtId );
19202005
}
19212006
19222007
Th_CallSubCommand2( interp, pStmt, argc, argv, argl,
19232008
pStmt ? aSubWithStmt : aSubAll );
19242009
}
19252010
1926
-
2011
+/*
2012
+** Registers the "query" API with the given interpreter. Returns TH_OK
2013
+** on success, TH_ERROR on error.
2014
+*/
19272015
int th_register_query(Th_Interp *interp){
19282016
enum { BufLen = 100 };
19292017
char buf[BufLen];
19302018
int i, l;
19312019
#define SET(K) l = snprintf(buf, BufLen, "%d", K); \
@@ -1943,50 +2031,44 @@
19432031
int rc = TH_OK;
19442032
static Th_Command_Reg aCommand[] = {
19452033
{"query", queryTopLevelCmd, 0},
19462034
{0, 0, 0}
19472035
};
1948
- rc = Th_register_commands( interp, aCommand );
2036
+ rc = Th_RegisterCommands( interp, aCommand );
19492037
if(TH_OK==rc){
1950
- Th_Sqlite * sq = Th_Malloc(interp, sizeof(Th_Sqlite));
2038
+ Th_Query * sq = Th_Malloc(interp, sizeof(Th_Query));
19512039
if(!sq){
19522040
rc = TH_ERROR;
19532041
}else{
19542042
assert( NULL == sq->aStmt );
19552043
assert( 0 == sq->nStmt );
1956
- Th_Data_Set( interp, Th_Sqlite_KEY, sq, finalizerSqlite );
1957
- assert( sq == Th_sqlite_manager(interp) );
2044
+ Th_SetData( interp, Th_Query_KEY, sq, finalizerSqlite );
2045
+ assert( sq == Th_query_manager(interp) );
19582046
}
19592047
}
19602048
return rc;
19612049
}
19622050
19632051
#endif
1964
-/* end TH_ENABLE_SQLITE */
1965
-
1966
-int Th_register_commands( Th_Interp * interp,
1967
- Th_Command_Reg const * aCommand ){
1968
- int i;
1969
- int rc = TH_OK;
1970
- for(i=0; (TH_OK==rc) && aCommand[i].zName; ++i){
1971
- if ( !aCommand[i].zName ) break;
1972
- else if( !aCommand[i].xProc ) continue;
1973
- else{
1974
- rc = Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
1975
- aCommand[i].pContext, 0);
1976
- }
1977
- }
1978
- return rc;
1979
-}
2052
+/* end TH_ENABLE_QUERY */
19802053
19812054
/*
19822055
** Make sure the interpreter has been initialized. Initialize it if
19832056
** it has not been already.
19842057
**
19852058
** The interpreter is stored in the g.interp global variable.
19862059
*/
19872060
void Th_FossilInit(void){
2061
+ /* The fossil-internal Th_Vtab instance. */
2062
+ static Th_Vtab vtab = { xRealloc, {/*out*/
2063
+ NULL /*write()*/,
2064
+ NULL/*dispose()*/,
2065
+ NULL/*pState*/,
2066
+ 1/*enabled*/
2067
+ }
2068
+ };
2069
+
19882070
static PutsCmdData puts_Html = {0, 0, 0};
19892071
static PutsCmdData puts_Normal = {1, 0, 0};
19902072
static Th_Command_Reg aCommand[] = {
19912073
{"anycap", anycapCmd, 0},
19922074
{"combobox", comboboxCmd, 0},
@@ -2007,13 +2089,13 @@
20072089
{0, 0, 0}
20082090
};
20092091
if( g.interp==0 ){
20102092
int i;
20112093
if(g.cgiOutput){
2012
- vtab.out.write = Th_output_f_cgi_content;
2094
+ vtab.out.write = Th_Output_f_cgi_content;
20132095
}else{
2014
- vtab.out = Th_Vtab_Output_FILE;
2096
+ vtab.out = Th_Vtab_OutputMethods_FILE;
20152097
vtab.out.pState = stdout;
20162098
}
20172099
vtab.out.enabled = enableOutput;
20182100
g.interp = Th_CreateInterp(&vtab);
20192101
th_register_language(g.interp); /* Basic scripting commands. */
@@ -2020,20 +2102,20 @@
20202102
#ifdef FOSSIL_ENABLE_TCL
20212103
if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
20222104
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
20232105
}
20242106
#endif
2025
-#ifdef TH_ENABLE_OUTBUF
2107
+#ifdef TH_ENABLE_OB
20262108
th_register_ob(g.interp);
20272109
#endif
2028
-#ifdef TH_ENABLE_SQLITE
2110
+#ifdef TH_ENABLE_QUERY
20292111
th_register_query(g.interp);
20302112
#endif
20312113
#ifdef TH_ENABLE_ARGV
20322114
th_register_argv(g.interp);
20332115
#endif
2034
- Th_register_commands( g.interp, aCommand );
2116
+ Th_RegisterCommands( g.interp, aCommand );
20352117
Th_Eval( g.interp, 0, "proc incr {name {step 1}} {\n"
20362118
"upvar $name x\n"
20372119
"set x [expr $x+$step]\n"
20382120
"}", -1 );
20392121
}
@@ -2140,11 +2222,11 @@
21402222
** then TH1 variables are $aaa or $<aaa>. The first form of variable
21412223
** is literal. The second is run through htmlize before being
21422224
** inserted.
21432225
**
21442226
** This routine processes the template and writes the results
2145
-** via Th_output().
2227
+** via Th_Output().
21462228
*/
21472229
int Th_Render(const char *z, int flags){
21482230
int i = 0;
21492231
int n;
21502232
int rc = TH_OK;
@@ -2201,12 +2283,16 @@
22012283
** COMMAND: th1
22022284
**
22032285
** Processes a file provided on the command line as a TH1-capable
22042286
** script/page. Output is sent to stdout or the CGI output buffer, as
22052287
** appropriate. The input file is assumed to be text/wiki/HTML content
2206
-** which may contain TH1 tag blocks. Each block is executed in the
2207
-** same TH1 interpreter instance.
2288
+** which may contain TH1 tag blocks and variables in the form $var or
2289
+** $<var>. Each block is executed in the same TH1 interpreter
2290
+** instance.
2291
+**
2292
+** ACHTUNG: not all of the $variables which are set in CGI mode
2293
+** are available via this (CLI) command.
22082294
**
22092295
*/
22102296
void test_th_render(void){
22112297
Blob in;
22122298
if( g.argc<3 ){
@@ -2213,12 +2299,12 @@
22132299
usage("FILE");
22142300
assert(0 && "usage() does not return");
22152301
}
22162302
blob_zero(&in);
22172303
db_open_config(0); /* Needed for global "tcl" setting. */
2218
-#ifdef TH_ENABLE_SQLITE
2304
+#ifdef TH_ENABLE_QUERY
22192305
db_find_and_open_repository(OPEN_ANY_SCHEMA,0)
22202306
/* required for th1 query API. */;
22212307
#endif
22222308
blob_read_from_file(&in, g.argv[2]);
22232309
Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT);
22242310
}
22252311
--- src/th_main.c
+++ src/th_main.c
@@ -18,18 +18,17 @@
18 ** This file contains an interface between the TH scripting language
19 ** (an independent project) and fossil.
20 */
21 #include "config.h"
22 #include "th_main.h"
 
 
23 #ifndef INTERFACE
24 #include "blob.h"
25 #endif
26 #ifdef TH_ENABLE_SQLITE
27 #include "sqlite3.h"
28 #endif
 
29
30 /*#include "th_main.h"*/
31 /*
32 ** Global variable counting the number of outstanding calls to malloc()
33 ** made by the th1 implementation. This is used to catch memory leaks
34 ** in the interpreter. Obviously, it also means th1 is not threadsafe.
35 */
@@ -50,32 +49,28 @@
50 nOutstandingMalloc--;
51 }
52 fossil_free(p);
53 }
54
 
 
 
55 static void *xRealloc(void * p, unsigned int n){
 
56 if(0 == n){
57 xFree(p);
58 return NULL;
59 }else if(NULL == p){
60 return xMalloc(n);
61 }else{
62 return fossil_realloc(p, n)
63 /* FIXME: try to find some reasonable nOutstandingMalloc
64 heuristics, e.g. if !p then ++, if !n then --, etc.
65 */;
66 }
67 }
68
69 static Th_Vtab vtab = { xRealloc, {
70 NULL /*write()*/,
71 NULL/*dispose()*/,
72 NULL/*pState*/,
73 1/*enabled*/
74 }
75 };
76
77 /*
78 ** Generate a TH1 trace message if debugging is enabled.
79 */
80 void Th_Trace(const char *zFormat, ...){
81 va_list ap;
@@ -85,10 +80,18 @@
85 }
86
87
88 /*
89 ** True if output is enabled. False if disabled.
 
 
 
 
 
 
 
 
90 */
91 static int enableOutput = 1;
92
93 /*
94 ** TH command: enable_output BOOLEAN
@@ -106,16 +109,18 @@
106 return Th_WrongNumArgs2(interp,
107 argv[0], argl[0],
108 "BOOLEAN");
109 }else{
110 int rc = Th_ToInt(interp, argv[1], argl[1], &enableOutput);
111 vtab.out.enabled = enableOutput;
112 return rc;
113 }
114 }
115
116 int Th_output_f_cgi_content( char const * zData, int nData, void * pState ){
 
 
 
117 cgi_append_content(zData, nData);
118 return nData;
119 }
120
121
@@ -132,27 +137,39 @@
132 if( n<0 ) n = strlen(z);
133 if( encode ){
134 z = htmlize(z, n);
135 n = strlen(z);
136 }
137 Th_output( pInterp, z, n );
138 if( encode ) fossil_free((char*)z);
139 }
140 }
141
 
 
 
 
 
 
 
142 struct PutsCmdData {
143 char escapeHtml;
144 char const * sep;
145 char const * eol;
 
 
146 };
147 typedef struct PutsCmdData PutsCmdData;
148
149 /*
150 ** TH command: puts STRING
151 ** TH command: html STRING
152 **
153 ** Output STRING as HTML (html) or unchanged (puts).
 
 
 
154 */
155 static int putsCmd(
156 Th_Interp *interp,
157 void *pConvert,
158 int argc,
@@ -229,11 +246,12 @@
229 free(zOut);
230 return TH_OK;
231 }
232
233 #if 0
234 /* i'm not sure we need this */
 
235 /*
236 ** TH command: render STRING
237 **
238 ** Render the input string as TH1.
239 */
@@ -247,11 +265,11 @@
247 if( argc<2 ){
248 return Th_WrongNumArgs2(interp,
249 argv[0], argl[0],
250 "STRING ?STRING...?");
251 }else{
252 Th_Ob_Man * man = Th_ob_manager(interp);
253 Blob * b = NULL;
254 Blob buf = empty_blob;
255 int rc, i;
256 /*FIXME: assert(NULL != man && man->interp==interp);*/
257 man->interp = interp;
@@ -263,18 +281,18 @@
263 for( i = 1; TH_OK==rc && i < argc; ++i ){
264 char const * str = argv[i];
265 blob_append( &buf, str, argl[i] );
266 /*rc = Th_Render( str, Th_Render_Flags_NO_DOLLAR_DEREF );*/
267 }
268 rc = Th_ob_push( man, &b );
269 if(rc){
270 blob_reset( &buf );
271 return rc;
272 }
273 rc = Th_Render( buf.aData, Th_Render_Flags_DEFAULT );
274 blob_reset(&buf);
275 b = Th_ob_pop( man );
276 if(TH_OK==rc){
277 Th_SetResult( interp, b->aData, b->nUsed );
278 }
279 blob_reset( b );
280 Th_Free( interp, b );
@@ -544,15 +562,12 @@
544 return TH_OK;
545 }
546
547
548 #ifdef TH_ENABLE_ARGV
549 extern const char *find_option(const char *zLong,
550 const char *zShort,
551 int hasArg) /* from main.c */;
552 /*
553 ** TH Syntax:
554 **
555 ** argv len
556 **
557 ** Returns the number of command-line arguments.
558 */
@@ -568,11 +583,11 @@
568 }
569
570
571
572 /*
573 ** TH Syntax:
574 **
575 ** argv at Index
576 **
577 ** Returns the raw argument at the given index, throwing if
578 ** out of bounds.
@@ -607,11 +622,11 @@
607 return TH_OK;
608 }
609
610
611 /*
612 ** TH Syntax:
613 **
614 ** argv getstr longName ??shortName? ?defaultValue??
615 **
616 ** Functions more or less like Fossil's find_option().
617 ** If the given argument is found then its value is returned,
@@ -678,15 +693,15 @@
678 Th_SetResult( interp, zVal, zVal ? strlen(zVal) : 0 );
679 return TH_OK;
680 }
681
682 /*
683 ** TH Syntax:
684 **
685 ** argv getbool longName ??shortName? ?defaultValue??
686 **
687 ** Works just like argv_getstr but treats any empty value or one
688 ** starting with the digit '0' as a boolean false.
689 **
690 ** Returns the result as an integer 0 (false) or 1 (true).
691 */
692 static int argvFindOptionBoolCmd(
@@ -751,13 +766,16 @@
751 Th_SetResultInt( interp, zVal ? 1 : 0 );
752 return TH_OK;
753 }
754
755 /*
756 ** TH Syntax:
757 **
758 ** argv getint longName ?shortName? ?defaultValue?
 
 
 
759 */
760 static int argvFindOptionIntCmd(
761 Th_Interp *interp,
762 void *p,
763 int argc,
@@ -811,10 +829,17 @@
811 Th_ToInt(interp, zVal, strlen(zVal), &val);
812 Th_SetResultInt( interp, val );
813 return TH_OK;
814 }
815
 
 
 
 
 
 
 
816 static int argvTopLevelCmd(
817 Th_Interp *interp,
818 void *ctx,
819 int argc,
820 const char **argv,
@@ -834,63 +859,58 @@
834 int th_register_argv(Th_Interp *interp){
835 static Th_Command_Reg aCommand[] = {
836 {"argv", argvTopLevelCmd, 0 },
837 {0, 0, 0}
838 };
839 Th_register_commands( interp, aCommand );
840 }
841
842 #endif
843 /* end TH_ENABLE_ARGV */
844
845 #ifdef TH_ENABLE_SQLITE
846
847 /*
848 ** Adds the given prepared statement to the interpreter. Returns the
849 ** statement's opaque identifier (a positive value). Ownerships of
850 ** pStmt is transfered to interp and it must be cleaned up by the
851 ** client by calling Th_FinalizeStmt(), passing it the value returned
852 ** by this function.
853 **
854 ** If interp is destroyed before all statements are finalized,
855 ** it will finalize them but may emit a warning message.
856 */
857 static int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
858
859 /*
860 ** Expects stmtId to be a statement identifier returned by
861 ** Th_AddStmt(). On success, finalizes the statement and returns 0.
862 ** On error (statement not found) non-0 is returned. After this
863 ** call, some subsequent call to Th_AddStmt() may return the
864 ** same statement ID.
865 */
866 static int Th_FinalizeStmt(Th_Interp *interp, int stmtId);
867 static int Th_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt *);
868
869 /*
870 ** Fetches the statement with the given ID, as returned by
871 ** Th_AddStmt(). Returns NULL if stmtId does not refer (or no longer
872 ** refers) to a statement added via Th_AddStmt().
873 */
874 static sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId);
875
876
877 struct Th_Sqlite {
878 sqlite3_stmt ** aStmt;
879 int nStmt;
880 int colCmdIndex;
881 };
882 #define Th_Sqlite_KEY "Th_Sqlite"
883 typedef struct Th_Sqlite Th_Sqlite;
 
 
 
884
885 static Th_Sqlite * Th_sqlite_manager( Th_Interp * interp ){
886 void * p = Th_Data_Get( interp, Th_Sqlite_KEY );
887 return p ? (Th_Sqlite*)p : NULL;
 
 
 
 
888 }
889
890 static int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
891 Th_Sqlite * sq = Th_sqlite_manager(interp);
892 int i, x;
893 sqlite3_stmt * s;
894 sqlite3_stmt ** list = sq->aStmt;
895 for( i = 0; i < sq->nStmt; ++i ){
896 s = list[i];
@@ -910,12 +930,19 @@
910 sq->aStmt = list;
911 return x + 1;
912 }
913
914
915 static int Th_FinalizeStmt(Th_Interp *interp, int stmtId){
916 Th_Sqlite * sq = Th_sqlite_manager(interp);
 
 
 
 
 
 
 
917 sqlite3_stmt * st;
918 int rc = 0;
919 assert( stmtId>0 && stmtId<=sq->nStmt );
920 st = sq->aStmt[stmtId-1];
921 if(NULL != st){
@@ -925,12 +952,16 @@
925 }else{
926 return 1;
927 }
928 }
929
930 static int Th_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt * pSt){
931 Th_Sqlite * sq = Th_sqlite_manager(interp);
 
 
 
 
932 int i = 0;
933 sqlite3_stmt * st = NULL;
934 int rc = 0;
935 for( ; i < sq->nStmt; ++i ){
936 st = sq->aStmt[i];
@@ -945,42 +976,50 @@
945 return 1;
946 }
947 }
948
949
950 static sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){
951 Th_Sqlite * sq = Th_sqlite_manager(interp);
952 return ((stmtId<1) || (stmtId > sq->nStmt))
 
 
 
 
 
953 ? NULL
954 : sq->aStmt[stmtId-1];
955 }
956
957
 
 
 
958 static void finalizerSqlite( Th_Interp * interp, void * p ){
959 Th_Sqlite * sq = (Th_Sqlite *)p;
960 int i;
961 sqlite3_stmt * st = NULL;
962 if(!sq) {
963 fossil_warning("Got a finalizer call for a NULL Th_Sqlite.");
964 return;
965 }
966 for( i = 0; i < sq->nStmt; ++i ){
967 st = sq->aStmt[i];
968 if(NULL != st){
969 fossil_warning("Auto-finalizing unfinalized "
970 "statement id #%d: %s",
971 i+1, sqlite3_sql(st));
972 Th_FinalizeStmt( interp, i+1 );
973 }
974 }
975 Th_Free(interp, sq->aStmt);
976 Th_Free(interp, sq);
977 }
978
979
980 /*
981 ** TH Syntax:
982 **
983 ** query prepare SQL
984 **
985 ** Returns an opaque statement identifier.
986 */
@@ -1016,11 +1055,11 @@
1016 assert(NULL != errMsg);
1017 assert(NULL == pStmt);
1018 Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
1019 return TH_ERROR;
1020 }
1021 rc = Th_AddStmt( interp, pStmt );
1022 assert( rc >= 0 && "AddStmt failed.");
1023 Th_SetResultInt( interp, rc );
1024 return TH_OK;
1025 }
1026
@@ -1039,21 +1078,21 @@
1039 sqlite3_stmt * pStmt = NULL;
1040 if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
1041 if(stmtId){
1042 *stmtId = rc;
1043 }
1044 pStmt = Th_GetStmt( interp, rc );
1045 if(NULL==pStmt){
1046 Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
1047 }
1048 }
1049 return pStmt;
1050
1051 }
1052
1053 /*
1054 ** TH Syntax:
1055 **
1056 ** query finalize stmtId
1057 ** query stmtId finalize
1058 **
1059 ** sqlite3_finalize()s the given statement.
@@ -1083,11 +1122,11 @@
1083 Th_ErrorMessage(interp, "Not a valid statement handle argument.", NULL, 0);
1084 return TH_ERROR;
1085 }
1086 }
1087 assert( NULL != pStmt );
1088 rc = Th_FinalizeStmt2( interp, pStmt );
1089 Th_SetResultInt( interp, rc );
1090 return TH_OK;
1091 }
1092
1093 /*
@@ -1148,11 +1187,11 @@
1148 return 0;
1149 }
1150 }
1151
1152 /*
1153 ** TH Syntax:
1154 **
1155 ** query step stmtId
1156 ** query stmtId step
1157 **
1158 ** Steps the given statement handle. Returns 0 at the end of the set,
@@ -1190,10 +1229,18 @@
1190 }
1191 Th_SetResultInt( interp, rc );
1192 return TH_OK;
1193 }
1194
 
 
 
 
 
 
 
 
1195 static int queryResetCmd(
1196 Th_Interp *interp,
1197 void *p,
1198 int argc,
1199 const char **argv,
@@ -1209,11 +1256,11 @@
1209 }
1210 }
1211
1212
1213 /*
1214 ** TH Syntax:
1215 **
1216 ** query col string stmtId Index
1217 ** query stmtId col string Index
1218 ** query stmtId col Index string
1219 **
@@ -1224,11 +1271,11 @@
1224 void *p,
1225 int argc,
1226 const char **argv,
1227 int *argl
1228 ){
1229 Th_Sqlite * sq = Th_sqlite_manager(interp);
1230 int index = sq->colCmdIndex;
1231 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1232 int requireArgc = pStmt ? 2 : 3;
1233 char const * val;
1234 int valLen;
@@ -1251,11 +1298,11 @@
1251 Th_SetResult( interp, val, valLen );
1252 return TH_OK;
1253 }
1254
1255 /*
1256 ** TH Syntax:
1257 **
1258 ** query col int stmtId Index
1259 ** query stmtId col int Index
1260 ** query stmtId col Index int
1261 **
@@ -1266,11 +1313,11 @@
1266 void *p,
1267 int argc,
1268 const char **argv,
1269 int *argl
1270 ){
1271 Th_Sqlite * sq = Th_sqlite_manager(interp);
1272 int index = sq->colCmdIndex;
1273 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1274 int requireArgc = pStmt ? 2 : 3;
1275 int rc = 0;
1276 if( index >= 0 ) --requireArgc;
@@ -1290,11 +1337,11 @@
1290 Th_SetResultInt( interp, sqlite3_column_int( pStmt, index ) );
1291 return TH_OK;
1292 }
1293
1294 /*
1295 ** TH Syntax:
1296 **
1297 ** query col double stmtId Index
1298 ** query stmtId col double Index
1299 ** query stmtId col Index double
1300 **
@@ -1305,11 +1352,11 @@
1305 void *p,
1306 int argc,
1307 const char **argv,
1308 int *argl
1309 ){
1310 Th_Sqlite * sq = Th_sqlite_manager(interp);
1311 int index = sq->colCmdIndex;
1312 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1313 int requireArgc = pStmt ? 2 : 3;
1314 double rc = 0;
1315 if( index >= 0 ) --requireArgc;
@@ -1329,14 +1376,14 @@
1329 Th_SetResultDouble( interp, sqlite3_column_double( pStmt, index ) );
1330 return TH_OK;
1331 }
1332
1333 /*
1334 ** TH Syntax:
1335 **
1336 ** query col isnull stmtId Index
1337 ** query stmtId col is_null Index
1338 ** query stmtId col Index isnull
1339 **
1340 ** Returns non-0 if the given 0-based result column index contains
1341 ** an SQL NULL value, else returns 0.
1342 */
@@ -1345,11 +1392,11 @@
1345 void *p,
1346 int argc,
1347 const char **argv,
1348 int *argl
1349 ){
1350 Th_Sqlite * sq = Th_sqlite_manager(interp);
1351 int index = sq->colCmdIndex;
1352 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1353 int requireArgc = pStmt ? 2 : 3;
1354 if( index >= 0 ) --requireArgc;
1355 double rc = 0;
@@ -1371,11 +1418,11 @@
1371 ? 1 : 0);
1372 return TH_OK;
1373 }
1374
1375 /*
1376 ** TH Syntax:
1377 **
1378 ** query col type stmtId Index
1379 ** query stmtId col type Index
1380 ** query stmtId col Index type
1381 **
@@ -1388,11 +1435,11 @@
1388 void *p,
1389 int argc,
1390 const char **argv,
1391 int *argl
1392 ){
1393 Th_Sqlite * sq = Th_sqlite_manager(interp);
1394 int index = sq->colCmdIndex;
1395 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1396 int requireArgc = pStmt ? 2 : 3;
1397 if( index >= 0 ) --requireArgc;
1398 double rc = 0;
@@ -1412,11 +1459,11 @@
1412 Th_SetResultInt( interp, sqlite3_column_type( pStmt, index ) );
1413 return TH_OK;
1414 }
1415
1416 /*
1417 ** TH Syntax:
1418 **
1419 ** query col count stmtId
1420 ** query stmtId col count
1421 **
1422 ** Returns the number of result columns in the query.
@@ -1446,11 +1493,11 @@
1446 Th_SetResultInt( interp, rc );
1447 return TH_OK;
1448 }
1449
1450 /*
1451 ** TH Syntax:
1452 **
1453 ** query col name stmtId Index
1454 ** query stmtId col name Index
1455 ** query stmtId col Index name
1456 **
@@ -1461,11 +1508,11 @@
1461 void *p,
1462 int argc,
1463 const char **argv,
1464 int *argl
1465 ){
1466 Th_Sqlite * sq = Th_sqlite_manager(interp);
1467 int index = sq->colCmdIndex;
1468 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1469 int requireArgc = pStmt ? 2 : 3;
1470 char const * val;
1471 int rc = 0;
@@ -1493,11 +1540,11 @@
1493 return TH_OK;
1494 }
1495 }
1496
1497 /*
1498 ** TH Syntax:
1499 **
1500 ** query col time stmtId Index format
1501 ** query stmtId col name Index format
1502 ** query stmtId col Index name format
1503 **
@@ -1508,11 +1555,11 @@
1508 void *ctx,
1509 int argc,
1510 const char **argv,
1511 int *argl
1512 ){
1513 Th_Sqlite * sq = Th_sqlite_manager(interp);
1514 int index = sq->colCmdIndex;
1515 sqlite3_stmt * pStmt = (sqlite3_stmt*)ctx;
1516 int minArgs = pStmt ? 3 : 4;
1517 int argPos;
1518 char const * val;
@@ -1555,10 +1602,17 @@
1555 Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
1556 fossil_free(fval);
1557 return 0;
1558 }
1559
 
 
 
 
 
 
 
1560 static int queryStrftimeCmd(
1561 Th_Interp *interp,
1562 void *ctx,
1563 int argc,
1564 const char **argv,
@@ -1592,11 +1646,11 @@
1592 return 0;
1593 }
1594
1595
1596 /*
1597 ** TH Syntax:
1598 **
1599 ** query bind null stmtId Index
1600 ** query stmtId bind null Index
1601 **
1602 ** Binds a value to the given 1-based parameter index.
@@ -1606,11 +1660,11 @@
1606 void *p,
1607 int argc,
1608 const char **argv,
1609 int *argl
1610 ){
1611 Th_Sqlite * sq = Th_sqlite_manager(interp);
1612 int index = sq->colCmdIndex;
1613 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1614 int requireArgc = pStmt ? 2 : 3;
1615 if( index > 0 ) --requireArgc;
1616 int rc;
@@ -1635,11 +1689,11 @@
1635 return TH_OK;
1636 }
1637
1638
1639 /*
1640 ** TH Syntax:
1641 **
1642 ** query bind string stmtId Index Value
1643 ** query stmtId bind string Index Value
1644 **
1645 ** Binds a value to the given 1-based parameter index.
@@ -1649,11 +1703,11 @@
1649 void *p,
1650 int argc,
1651 const char **argv,
1652 int *argl
1653 ){
1654 Th_Sqlite * sq = Th_sqlite_manager(interp);
1655 int index = sq->colCmdIndex;
1656 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1657 int requireArgc = pStmt ? 3 : 4;
1658 int rc;
1659 int argPos;
@@ -1682,11 +1736,11 @@
1682 Th_SetResultInt( interp, 0 );
1683 return TH_OK;
1684 }
1685
1686 /*
1687 ** TH Syntax:
1688 **
1689 ** query bind int stmtId Index Value
1690 ** query stmtId bind int Index Value
1691 **
1692 ** Binds a value to the given 1-based parameter index.
@@ -1696,11 +1750,11 @@
1696 void *p,
1697 int argc,
1698 const char **argv,
1699 int *argl
1700 ){
1701 Th_Sqlite * sq = Th_sqlite_manager(interp);
1702 int index = sq->colCmdIndex;
1703 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1704 int requireArgc = pStmt ? 3 : 4;
1705 int rc;
1706 int argPos;
@@ -1734,11 +1788,11 @@
1734 Th_SetResultInt( interp, 0 );
1735 return TH_OK;
1736 }
1737
1738 /*
1739 ** TH Syntax:
1740 **
1741 ** query bind double stmtId Index Value
1742 ** query stmtId bind double Index Value
1743 **
1744 ** Binds a value to the given 1-based parameter index.
@@ -1748,11 +1802,11 @@
1748 void *p,
1749 int argc,
1750 const char **argv,
1751 int *argl
1752 ){
1753 Th_Sqlite * sq = Th_sqlite_manager(interp);
1754 int index = sq->colCmdIndex;
1755 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1756 int requireArgc = pStmt ? 3 : 4;
1757 int rc;
1758 int argPos;
@@ -1785,10 +1839,18 @@
1785 }
1786 Th_SetResultInt( interp, 0 );
1787 return TH_OK;
1788 }
1789
 
 
 
 
 
 
 
 
1790 static int queryBindTopLevelCmd(
1791 Th_Interp *interp,
1792 void *ctx,
1793 int argc,
1794 const char **argv,
@@ -1800,15 +1862,15 @@
1800 {"double", queryBindDoubleCmd},
1801 {"null", queryBindNullCmd},
1802 {"string", queryBindStringCmd},
1803 {0, 0}
1804 };
1805 Th_Sqlite * sq = Th_sqlite_manager(interp);
1806 assert(NULL != sq);
1807 if( 1 == argc ){
1808 Th_WrongNumArgs2( interp, argv[0], argl[0],
1809 "subcommand");
1810 return TH_ERROR;
1811 }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
1812 if(colIndex <0){
1813 Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
1814 return TH_ERROR;
@@ -1820,10 +1882,18 @@
1820 sq->colCmdIndex = colIndex;
1821 Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
1822
1823 }
1824
 
 
 
 
 
 
 
 
1825 static int queryColTopLevelCmd(
1826 Th_Interp *interp,
1827 void *ctx,
1828 int argc,
1829 const char **argv,
@@ -1841,10 +1911,16 @@
1841 {"time", queryColTimeCmd},
1842 {"type", queryColTypeCmd},
1843 {0, 0}
1844 };
1845 static Th_SubCommand aSubWithIndex[] = {
 
 
 
 
 
 
1846 {"is_null", queryColIsNullCmd},
1847 {"isnull", queryColIsNullCmd},
1848 {"name", queryColNameCmd},
1849 {"double", queryColDoubleCmd},
1850 {"int", queryColIntCmd},
@@ -1851,15 +1927,17 @@
1851 {"string", queryColStringCmd},
1852 {"time", queryColTimeCmd},
1853 {"type", queryColTypeCmd},
1854 {0, 0}
1855 };
1856 Th_Sqlite * sq = Th_sqlite_manager(interp);
1857 assert(NULL != sq);
1858 if( 1 == argc ){
1859 Th_WrongNumArgs2( interp, argv[0], argl[0],
1860 "subcommand");
 
 
1861 return TH_ERROR;
1862 }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
1863 if(colIndex <0){
1864 Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
1865 return TH_ERROR;
@@ -1872,10 +1950,18 @@
1872 Th_CallSubCommand2( interp, ctx, argc, argv, argl,
1873 (colIndex<0) ? aSub : aSubWithIndex );
1874 }
1875
1876
 
 
 
 
 
 
 
 
1877 static int queryTopLevelCmd(
1878 Th_Interp *interp,
1879 void *ctx,
1880 int argc,
1881 const char **argv,
@@ -1884,48 +1970,50 @@
1884 int stmtId = 0;
1885 sqlite3_stmt * pStmt = NULL;
1886 static Th_SubCommand aSubAll[] = {
1887 {"bind", queryBindTopLevelCmd},
1888 {"col", queryColTopLevelCmd},
 
 
1889 {"reset", queryResetCmd},
1890 {"step", queryStepCmd},
1891 {"finalize", queryFinalizeCmd},
1892 {"prepare", queryPrepareCmd},
1893 {"strftime", queryStrftimeCmd},
1894 {0, 0}
1895 };
1896 static Th_SubCommand aSubWithStmt[] = {
1897 /* These entries are coded to deal with
1898 being supplied a statement via pStmt
1899 or via one of their args.
1900 */
1901 {"bind", queryBindTopLevelCmd},
1902 {"col", queryColTopLevelCmd},
1903 {"step", queryStepCmd},
1904 {"finalize", queryFinalizeCmd},
1905 {"reset", queryResetCmd},
1906 {0, 0}
1907 };
1908
1909
1910 assert( NULL != Th_sqlite_manager(interp) );
1911 if( 1 == argc ){
1912 Th_WrongNumArgs2( interp, argv[0], argl[0],
1913 "subcommand");
1914 return TH_ERROR;
1915 }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &stmtId) ){
1916 ++argv;
1917 ++argl;
1918 --argc;
1919 pStmt = Th_GetStmt( interp, stmtId );
1920 }
1921
1922 Th_CallSubCommand2( interp, pStmt, argc, argv, argl,
1923 pStmt ? aSubWithStmt : aSubAll );
1924 }
1925
1926
 
 
 
1927 int th_register_query(Th_Interp *interp){
1928 enum { BufLen = 100 };
1929 char buf[BufLen];
1930 int i, l;
1931 #define SET(K) l = snprintf(buf, BufLen, "%d", K); \
@@ -1943,50 +2031,44 @@
1943 int rc = TH_OK;
1944 static Th_Command_Reg aCommand[] = {
1945 {"query", queryTopLevelCmd, 0},
1946 {0, 0, 0}
1947 };
1948 rc = Th_register_commands( interp, aCommand );
1949 if(TH_OK==rc){
1950 Th_Sqlite * sq = Th_Malloc(interp, sizeof(Th_Sqlite));
1951 if(!sq){
1952 rc = TH_ERROR;
1953 }else{
1954 assert( NULL == sq->aStmt );
1955 assert( 0 == sq->nStmt );
1956 Th_Data_Set( interp, Th_Sqlite_KEY, sq, finalizerSqlite );
1957 assert( sq == Th_sqlite_manager(interp) );
1958 }
1959 }
1960 return rc;
1961 }
1962
1963 #endif
1964 /* end TH_ENABLE_SQLITE */
1965
1966 int Th_register_commands( Th_Interp * interp,
1967 Th_Command_Reg const * aCommand ){
1968 int i;
1969 int rc = TH_OK;
1970 for(i=0; (TH_OK==rc) && aCommand[i].zName; ++i){
1971 if ( !aCommand[i].zName ) break;
1972 else if( !aCommand[i].xProc ) continue;
1973 else{
1974 rc = Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
1975 aCommand[i].pContext, 0);
1976 }
1977 }
1978 return rc;
1979 }
1980
1981 /*
1982 ** Make sure the interpreter has been initialized. Initialize it if
1983 ** it has not been already.
1984 **
1985 ** The interpreter is stored in the g.interp global variable.
1986 */
1987 void Th_FossilInit(void){
 
 
 
 
 
 
 
 
 
1988 static PutsCmdData puts_Html = {0, 0, 0};
1989 static PutsCmdData puts_Normal = {1, 0, 0};
1990 static Th_Command_Reg aCommand[] = {
1991 {"anycap", anycapCmd, 0},
1992 {"combobox", comboboxCmd, 0},
@@ -2007,13 +2089,13 @@
2007 {0, 0, 0}
2008 };
2009 if( g.interp==0 ){
2010 int i;
2011 if(g.cgiOutput){
2012 vtab.out.write = Th_output_f_cgi_content;
2013 }else{
2014 vtab.out = Th_Vtab_Output_FILE;
2015 vtab.out.pState = stdout;
2016 }
2017 vtab.out.enabled = enableOutput;
2018 g.interp = Th_CreateInterp(&vtab);
2019 th_register_language(g.interp); /* Basic scripting commands. */
@@ -2020,20 +2102,20 @@
2020 #ifdef FOSSIL_ENABLE_TCL
2021 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
2022 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
2023 }
2024 #endif
2025 #ifdef TH_ENABLE_OUTBUF
2026 th_register_ob(g.interp);
2027 #endif
2028 #ifdef TH_ENABLE_SQLITE
2029 th_register_query(g.interp);
2030 #endif
2031 #ifdef TH_ENABLE_ARGV
2032 th_register_argv(g.interp);
2033 #endif
2034 Th_register_commands( g.interp, aCommand );
2035 Th_Eval( g.interp, 0, "proc incr {name {step 1}} {\n"
2036 "upvar $name x\n"
2037 "set x [expr $x+$step]\n"
2038 "}", -1 );
2039 }
@@ -2140,11 +2222,11 @@
2140 ** then TH1 variables are $aaa or $<aaa>. The first form of variable
2141 ** is literal. The second is run through htmlize before being
2142 ** inserted.
2143 **
2144 ** This routine processes the template and writes the results
2145 ** via Th_output().
2146 */
2147 int Th_Render(const char *z, int flags){
2148 int i = 0;
2149 int n;
2150 int rc = TH_OK;
@@ -2201,12 +2283,16 @@
2201 ** COMMAND: th1
2202 **
2203 ** Processes a file provided on the command line as a TH1-capable
2204 ** script/page. Output is sent to stdout or the CGI output buffer, as
2205 ** appropriate. The input file is assumed to be text/wiki/HTML content
2206 ** which may contain TH1 tag blocks. Each block is executed in the
2207 ** same TH1 interpreter instance.
 
 
 
 
2208 **
2209 */
2210 void test_th_render(void){
2211 Blob in;
2212 if( g.argc<3 ){
@@ -2213,12 +2299,12 @@
2213 usage("FILE");
2214 assert(0 && "usage() does not return");
2215 }
2216 blob_zero(&in);
2217 db_open_config(0); /* Needed for global "tcl" setting. */
2218 #ifdef TH_ENABLE_SQLITE
2219 db_find_and_open_repository(OPEN_ANY_SCHEMA,0)
2220 /* required for th1 query API. */;
2221 #endif
2222 blob_read_from_file(&in, g.argv[2]);
2223 Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT);
2224 }
2225
--- src/th_main.c
+++ src/th_main.c
@@ -18,18 +18,17 @@
18 ** This file contains an interface between the TH scripting language
19 ** (an independent project) and fossil.
20 */
21 #include "config.h"
22 #include "th_main.h"
23
24 #ifdef TH_ENABLE_QUERY
25 #ifndef INTERFACE
 
 
 
26 #include "sqlite3.h"
27 #endif
28 #endif
29
 
30 /*
31 ** Global variable counting the number of outstanding calls to malloc()
32 ** made by the th1 implementation. This is used to catch memory leaks
33 ** in the interpreter. Obviously, it also means th1 is not threadsafe.
34 */
@@ -50,32 +49,28 @@
49 nOutstandingMalloc--;
50 }
51 fossil_free(p);
52 }
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 }
71
 
 
 
 
 
 
 
 
72 /*
73 ** Generate a TH1 trace message if debugging is enabled.
74 */
75 void Th_Trace(const char *zFormat, ...){
76 va_list ap;
@@ -85,10 +80,18 @@
80 }
81
82
83 /*
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.
93 */
94 static int enableOutput = 1;
95
96 /*
97 ** TH command: enable_output BOOLEAN
@@ -106,16 +109,18 @@
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;
115 }
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
126
@@ -132,27 +137,39 @@
137 if( n<0 ) n = strlen(z);
138 if( encode ){
139 z = htmlize(z, n);
140 n = strlen(z);
141 }
142 Th_Output( pInterp, z, n );
143 if( encode ) fossil_free((char*)z);
144 }
145 }
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
163 /*
164 ** TH command: puts STRING
165 ** TH command: html STRING
166 **
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.
171 */
172 static int putsCmd(
173 Th_Interp *interp,
174 void *pConvert,
175 int argc,
@@ -229,11 +246,12 @@
246 free(zOut);
247 return TH_OK;
248 }
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 */
@@ -247,11 +265,11 @@
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;
@@ -263,18 +281,18 @@
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 );
@@ -544,15 +562,12 @@
562 return TH_OK;
563 }
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 */
@@ -568,11 +583,11 @@
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.
@@ -607,11 +622,11 @@
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,
@@ -678,15 +693,15 @@
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(
@@ -751,13 +766,16 @@
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,
@@ -811,10 +829,17 @@
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,
@@ -834,63 +859,58 @@
859 int th_register_argv(Th_Interp *interp){
860 static Th_Command_Reg aCommand[] = {
861 {"argv", argvTopLevelCmd, 0 },
862 {0, 0, 0}
863 };
864 Th_RegisterCommands( interp, aCommand );
865 }
866
867 #endif
868 /* end TH_ENABLE_ARGV */
869
870 #ifdef TH_ENABLE_QUERY
871
872 /*
873 ** Adds the given prepared statement to the interpreter. Returns the
874 ** statement's opaque identifier (a positive value). Ownerships of
875 ** pStmt is transfered to interp and it must be cleaned up by the
876 ** client by calling Th_query_FinalizeStmt(), passing it the value returned
877 ** by this function.
878 **
879 ** If interp is destroyed before all statements are finalized,
880 ** it will finalize them but may emit a warning message.
881 */
882 static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
883
884
885 /*
886 ** Internal state for the "query" API.
887 */
888 struct Th_Query {
889 sqlite3_stmt ** aStmt; /* Array of statement handles. */
890 int nStmt; /* number of entries in aStmt. */
891 int colCmdIndex; /* column index argument. Set by some top-level dispatchers
892 for their subcommands.
893 */
 
 
 
 
 
 
 
 
 
 
 
 
894 };
895 /*
896 ** Internal key for use with Th_Data_Add().
897 */
898 #define Th_Query_KEY "Th_Query"
899 typedef struct Th_Query Th_Query;
900
901 /*
902 ** Returns the Th_Query object associated with the given interpreter,
903 ** or 0 if there is not one.
904 */
905 static Th_Query * Th_query_manager( Th_Interp * interp ){
906 void * p = Th_GetData( interp, Th_Query_KEY );
907 return p ? (Th_Query*)p : NULL;
908 }
909
910 static int Th_query_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
911 Th_Query * sq = Th_query_manager(interp);
912 int i, x;
913 sqlite3_stmt * s;
914 sqlite3_stmt ** list = sq->aStmt;
915 for( i = 0; i < sq->nStmt; ++i ){
916 s = list[i];
@@ -910,12 +930,19 @@
930 sq->aStmt = list;
931 return x + 1;
932 }
933
934
935 /*
936 ** Expects stmtId to be a statement identifier returned by
937 ** Th_query_AddStmt(). On success, finalizes the statement and returns 0.
938 ** On error (statement not found) non-0 is returned. After this
939 ** call, some subsequent call to Th_query_AddStmt() may return the
940 ** same statement ID.
941 */
942 static int Th_query_FinalizeStmt(Th_Interp *interp, int stmtId){
943 Th_Query * sq = Th_query_manager(interp);
944 sqlite3_stmt * st;
945 int rc = 0;
946 assert( stmtId>0 && stmtId<=sq->nStmt );
947 st = sq->aStmt[stmtId-1];
948 if(NULL != st){
@@ -925,12 +952,16 @@
952 }else{
953 return 1;
954 }
955 }
956
957 /*
958 ** Works like Th_query_FinalizeStmt() but takes a statement pointer, which
959 ** must have been Th_query_AddStmt()'d to the given interpreter.
960 */
961 static int Th_query_FinalizeStmt2(Th_Interp *interp, sqlite3_stmt * pSt){
962 Th_Query * sq = Th_query_manager(interp);
963 int i = 0;
964 sqlite3_stmt * st = NULL;
965 int rc = 0;
966 for( ; i < sq->nStmt; ++i ){
967 st = sq->aStmt[i];
@@ -945,42 +976,50 @@
976 return 1;
977 }
978 }
979
980
981 /*
982 ** Fetches the statement with the given ID, as returned by
983 ** Th_query_AddStmt(). Returns NULL if stmtId does not refer (or no longer
984 ** refers) to a statement added via Th_query_AddStmt().
985 */
986 static sqlite3_stmt * Th_query_GetStmt(Th_Interp *interp, int stmtId){
987 Th_Query * sq = Th_query_manager(interp);
988 return (!sq || (stmtId<1) || (stmtId > sq->nStmt))
989 ? NULL
990 : sq->aStmt[stmtId-1];
991 }
992
993
994 /*
995 ** Th_GCEntry finalizer which requires that p be a (Th_Query*).
996 */
997 static void finalizerSqlite( Th_Interp * interp, void * p ){
998 Th_Query * sq = (Th_Query *)p;
999 int i;
1000 sqlite3_stmt * st = NULL;
1001 if(!sq) {
1002 fossil_warning("Got a finalizer call for a NULL Th_Query.");
1003 return;
1004 }
1005 for( i = 0; i < sq->nStmt; ++i ){
1006 st = sq->aStmt[i];
1007 if(NULL != st){
1008 fossil_warning("Auto-finalizing unfinalized "
1009 "statement id #%d: %s",
1010 i+1, sqlite3_sql(st));
1011 Th_query_FinalizeStmt( interp, i+1 );
1012 }
1013 }
1014 Th_Free(interp, sq->aStmt);
1015 Th_Free(interp, sq);
1016 }
1017
1018
1019 /*
1020 ** TH command:
1021 **
1022 ** query prepare SQL
1023 **
1024 ** Returns an opaque statement identifier.
1025 */
@@ -1016,11 +1055,11 @@
1055 assert(NULL != errMsg);
1056 assert(NULL == pStmt);
1057 Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
1058 return TH_ERROR;
1059 }
1060 rc = Th_query_AddStmt( interp, pStmt );
1061 assert( rc >= 0 && "AddStmt failed.");
1062 Th_SetResultInt( interp, rc );
1063 return TH_OK;
1064 }
1065
@@ -1039,21 +1078,21 @@
1078 sqlite3_stmt * pStmt = NULL;
1079 if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
1080 if(stmtId){
1081 *stmtId = rc;
1082 }
1083 pStmt = Th_query_GetStmt( interp, rc );
1084 if(NULL==pStmt){
1085 Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
1086 }
1087 }
1088 return pStmt;
1089
1090 }
1091
1092 /*
1093 ** TH command:
1094 **
1095 ** query finalize stmtId
1096 ** query stmtId finalize
1097 **
1098 ** sqlite3_finalize()s the given statement.
@@ -1083,11 +1122,11 @@
1122 Th_ErrorMessage(interp, "Not a valid statement handle argument.", NULL, 0);
1123 return TH_ERROR;
1124 }
1125 }
1126 assert( NULL != pStmt );
1127 rc = Th_query_FinalizeStmt2( interp, pStmt );
1128 Th_SetResultInt( interp, rc );
1129 return TH_OK;
1130 }
1131
1132 /*
@@ -1148,11 +1187,11 @@
1187 return 0;
1188 }
1189 }
1190
1191 /*
1192 ** TH command:
1193 **
1194 ** query step stmtId
1195 ** query stmtId step
1196 **
1197 ** Steps the given statement handle. Returns 0 at the end of the set,
@@ -1190,10 +1229,18 @@
1229 }
1230 Th_SetResultInt( interp, rc );
1231 return TH_OK;
1232 }
1233
1234 /*
1235 ** TH command:
1236 **
1237 ** query StmtId reset
1238 ** query reset StmtId
1239 **
1240 ** Equivalent to sqlite3_reset().
1241 */
1242 static int queryResetCmd(
1243 Th_Interp *interp,
1244 void *p,
1245 int argc,
1246 const char **argv,
@@ -1209,11 +1256,11 @@
1256 }
1257 }
1258
1259
1260 /*
1261 ** TH command:
1262 **
1263 ** query col string stmtId Index
1264 ** query stmtId col string Index
1265 ** query stmtId col Index string
1266 **
@@ -1224,11 +1271,11 @@
1271 void *p,
1272 int argc,
1273 const char **argv,
1274 int *argl
1275 ){
1276 Th_Query * sq = Th_query_manager(interp);
1277 int index = sq->colCmdIndex;
1278 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1279 int requireArgc = pStmt ? 2 : 3;
1280 char const * val;
1281 int valLen;
@@ -1251,11 +1298,11 @@
1298 Th_SetResult( interp, val, valLen );
1299 return TH_OK;
1300 }
1301
1302 /*
1303 ** TH command:
1304 **
1305 ** query col int stmtId Index
1306 ** query stmtId col int Index
1307 ** query stmtId col Index int
1308 **
@@ -1266,11 +1313,11 @@
1313 void *p,
1314 int argc,
1315 const char **argv,
1316 int *argl
1317 ){
1318 Th_Query * sq = Th_query_manager(interp);
1319 int index = sq->colCmdIndex;
1320 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1321 int requireArgc = pStmt ? 2 : 3;
1322 int rc = 0;
1323 if( index >= 0 ) --requireArgc;
@@ -1290,11 +1337,11 @@
1337 Th_SetResultInt( interp, sqlite3_column_int( pStmt, index ) );
1338 return TH_OK;
1339 }
1340
1341 /*
1342 ** TH command:
1343 **
1344 ** query col double stmtId Index
1345 ** query stmtId col double Index
1346 ** query stmtId col Index double
1347 **
@@ -1305,11 +1352,11 @@
1352 void *p,
1353 int argc,
1354 const char **argv,
1355 int *argl
1356 ){
1357 Th_Query * sq = Th_query_manager(interp);
1358 int index = sq->colCmdIndex;
1359 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1360 int requireArgc = pStmt ? 2 : 3;
1361 double rc = 0;
1362 if( index >= 0 ) --requireArgc;
@@ -1329,14 +1376,14 @@
1376 Th_SetResultDouble( interp, sqlite3_column_double( pStmt, index ) );
1377 return TH_OK;
1378 }
1379
1380 /*
1381 ** TH command:
1382 **
1383 ** query col isnull stmtId Index
1384 ** query stmtId col isnull Index
1385 ** query stmtId col Index isnull
1386 **
1387 ** Returns non-0 if the given 0-based result column index contains
1388 ** an SQL NULL value, else returns 0.
1389 */
@@ -1345,11 +1392,11 @@
1392 void *p,
1393 int argc,
1394 const char **argv,
1395 int *argl
1396 ){
1397 Th_Query * sq = Th_query_manager(interp);
1398 int index = sq->colCmdIndex;
1399 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1400 int requireArgc = pStmt ? 2 : 3;
1401 if( index >= 0 ) --requireArgc;
1402 double rc = 0;
@@ -1371,11 +1418,11 @@
1418 ? 1 : 0);
1419 return TH_OK;
1420 }
1421
1422 /*
1423 ** TH command:
1424 **
1425 ** query col type stmtId Index
1426 ** query stmtId col type Index
1427 ** query stmtId col Index type
1428 **
@@ -1388,11 +1435,11 @@
1435 void *p,
1436 int argc,
1437 const char **argv,
1438 int *argl
1439 ){
1440 Th_Query * sq = Th_query_manager(interp);
1441 int index = sq->colCmdIndex;
1442 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1443 int requireArgc = pStmt ? 2 : 3;
1444 if( index >= 0 ) --requireArgc;
1445 double rc = 0;
@@ -1412,11 +1459,11 @@
1459 Th_SetResultInt( interp, sqlite3_column_type( pStmt, index ) );
1460 return TH_OK;
1461 }
1462
1463 /*
1464 ** TH command:
1465 **
1466 ** query col count stmtId
1467 ** query stmtId col count
1468 **
1469 ** Returns the number of result columns in the query.
@@ -1446,11 +1493,11 @@
1493 Th_SetResultInt( interp, rc );
1494 return TH_OK;
1495 }
1496
1497 /*
1498 ** TH command:
1499 **
1500 ** query col name stmtId Index
1501 ** query stmtId col name Index
1502 ** query stmtId col Index name
1503 **
@@ -1461,11 +1508,11 @@
1508 void *p,
1509 int argc,
1510 const char **argv,
1511 int *argl
1512 ){
1513 Th_Query * sq = Th_query_manager(interp);
1514 int index = sq->colCmdIndex;
1515 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1516 int requireArgc = pStmt ? 2 : 3;
1517 char const * val;
1518 int rc = 0;
@@ -1493,11 +1540,11 @@
1540 return TH_OK;
1541 }
1542 }
1543
1544 /*
1545 ** TH command:
1546 **
1547 ** query col time stmtId Index format
1548 ** query stmtId col name Index format
1549 ** query stmtId col Index name format
1550 **
@@ -1508,11 +1555,11 @@
1555 void *ctx,
1556 int argc,
1557 const char **argv,
1558 int *argl
1559 ){
1560 Th_Query * sq = Th_query_manager(interp);
1561 int index = sq->colCmdIndex;
1562 sqlite3_stmt * pStmt = (sqlite3_stmt*)ctx;
1563 int minArgs = pStmt ? 3 : 4;
1564 int argPos;
1565 char const * val;
@@ -1555,10 +1602,17 @@
1602 Th_SetResult( interp, fval, fval ? strlen(fval) : 0 );
1603 fossil_free(fval);
1604 return 0;
1605 }
1606
1607 /*
1608 ** TH command:
1609 **
1610 ** query strftime TimeVal ?Modifiers...?
1611 **
1612 ** Acts as a proxy to sqlite3's strftime() SQL function.
1613 */
1614 static int queryStrftimeCmd(
1615 Th_Interp *interp,
1616 void *ctx,
1617 int argc,
1618 const char **argv,
@@ -1592,11 +1646,11 @@
1646 return 0;
1647 }
1648
1649
1650 /*
1651 ** TH command:
1652 **
1653 ** query bind null stmtId Index
1654 ** query stmtId bind null Index
1655 **
1656 ** Binds a value to the given 1-based parameter index.
@@ -1606,11 +1660,11 @@
1660 void *p,
1661 int argc,
1662 const char **argv,
1663 int *argl
1664 ){
1665 Th_Query * sq = Th_query_manager(interp);
1666 int index = sq->colCmdIndex;
1667 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1668 int requireArgc = pStmt ? 2 : 3;
1669 if( index > 0 ) --requireArgc;
1670 int rc;
@@ -1635,11 +1689,11 @@
1689 return TH_OK;
1690 }
1691
1692
1693 /*
1694 ** TH command:
1695 **
1696 ** query bind string stmtId Index Value
1697 ** query stmtId bind string Index Value
1698 **
1699 ** Binds a value to the given 1-based parameter index.
@@ -1649,11 +1703,11 @@
1703 void *p,
1704 int argc,
1705 const char **argv,
1706 int *argl
1707 ){
1708 Th_Query * sq = Th_query_manager(interp);
1709 int index = sq->colCmdIndex;
1710 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1711 int requireArgc = pStmt ? 3 : 4;
1712 int rc;
1713 int argPos;
@@ -1682,11 +1736,11 @@
1736 Th_SetResultInt( interp, 0 );
1737 return TH_OK;
1738 }
1739
1740 /*
1741 ** TH command:
1742 **
1743 ** query bind int stmtId Index Value
1744 ** query stmtId bind int Index Value
1745 **
1746 ** Binds a value to the given 1-based parameter index.
@@ -1696,11 +1750,11 @@
1750 void *p,
1751 int argc,
1752 const char **argv,
1753 int *argl
1754 ){
1755 Th_Query * sq = Th_query_manager(interp);
1756 int index = sq->colCmdIndex;
1757 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1758 int requireArgc = pStmt ? 3 : 4;
1759 int rc;
1760 int argPos;
@@ -1734,11 +1788,11 @@
1788 Th_SetResultInt( interp, 0 );
1789 return TH_OK;
1790 }
1791
1792 /*
1793 ** TH command:
1794 **
1795 ** query bind double stmtId Index Value
1796 ** query stmtId bind double Index Value
1797 **
1798 ** Binds a value to the given 1-based parameter index.
@@ -1748,11 +1802,11 @@
1802 void *p,
1803 int argc,
1804 const char **argv,
1805 int *argl
1806 ){
1807 Th_Query * sq = Th_query_manager(interp);
1808 int index = sq->colCmdIndex;
1809 sqlite3_stmt * pStmt = (sqlite3_stmt*)p;
1810 int requireArgc = pStmt ? 3 : 4;
1811 int rc;
1812 int argPos;
@@ -1785,10 +1839,18 @@
1839 }
1840 Th_SetResultInt( interp, 0 );
1841 return TH_OK;
1842 }
1843
1844 /*
1845 ** TH command:
1846 **
1847 ** bind subcommand StmtId...
1848 ** bind StmtId subcommand...
1849 **
1850 ** This is the top-level dispatcher for the "bind" family of commands.
1851 */
1852 static int queryBindTopLevelCmd(
1853 Th_Interp *interp,
1854 void *ctx,
1855 int argc,
1856 const char **argv,
@@ -1800,15 +1862,15 @@
1862 {"double", queryBindDoubleCmd},
1863 {"null", queryBindNullCmd},
1864 {"string", queryBindStringCmd},
1865 {0, 0}
1866 };
1867 Th_Query * sq = Th_query_manager(interp);
1868 assert(NULL != sq);
1869 if( 1 == argc ){
1870 Th_WrongNumArgs2( interp, argv[0], argl[0],
1871 "subcommand: int|double|null|string");
1872 return TH_ERROR;
1873 }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
1874 if(colIndex <0){
1875 Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
1876 return TH_ERROR;
@@ -1820,10 +1882,18 @@
1882 sq->colCmdIndex = colIndex;
1883 Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub );
1884
1885 }
1886
1887 /*
1888 ** TH command:
1889 **
1890 ** query col subcommand ...
1891 ** query StmtId col subcommand ...
1892 **
1893 ** This is the top-level dispatcher for the col subcommands.
1894 */
1895 static int queryColTopLevelCmd(
1896 Th_Interp *interp,
1897 void *ctx,
1898 int argc,
1899 const char **argv,
@@ -1841,10 +1911,16 @@
1911 {"time", queryColTimeCmd},
1912 {"type", queryColTypeCmd},
1913 {0, 0}
1914 };
1915 static Th_SubCommand aSubWithIndex[] = {
1916 /*
1917 This subset is coded to accept the column index
1918 either before the subcommand name or after it.
1919 If called like (bind StmtId subcommand) then
1920 only these commands will be checked.
1921 */
1922 {"is_null", queryColIsNullCmd},
1923 {"isnull", queryColIsNullCmd},
1924 {"name", queryColNameCmd},
1925 {"double", queryColDoubleCmd},
1926 {"int", queryColIntCmd},
@@ -1851,15 +1927,17 @@
1927 {"string", queryColStringCmd},
1928 {"time", queryColTimeCmd},
1929 {"type", queryColTypeCmd},
1930 {0, 0}
1931 };
1932 Th_Query * sq = Th_query_manager(interp);
1933 assert(NULL != sq);
1934 if( 1 == argc ){
1935 Th_WrongNumArgs2( interp, argv[0], argl[0],
1936 "subcommand: "
1937 "count|is_null|isnull|name|"
1938 "double|int|string|time|type");
1939 return TH_ERROR;
1940 }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &colIndex) ){
1941 if(colIndex <0){
1942 Th_ErrorMessage( interp, "Invalid column index.", NULL, 0);
1943 return TH_ERROR;
@@ -1872,10 +1950,18 @@
1950 Th_CallSubCommand2( interp, ctx, argc, argv, argl,
1951 (colIndex<0) ? aSub : aSubWithIndex );
1952 }
1953
1954
1955 /*
1956 ** TH command:
1957 **
1958 ** query subcommand ...
1959 ** query StmtId subcommand ...
1960 **
1961 ** This is the top-level dispatcher for the query subcommand.
1962 */
1963 static int queryTopLevelCmd(
1964 Th_Interp *interp,
1965 void *ctx,
1966 int argc,
1967 const char **argv,
@@ -1884,48 +1970,50 @@
1970 int stmtId = 0;
1971 sqlite3_stmt * pStmt = NULL;
1972 static Th_SubCommand aSubAll[] = {
1973 {"bind", queryBindTopLevelCmd},
1974 {"col", queryColTopLevelCmd},
1975 {"finalize", queryFinalizeCmd},
1976 {"prepare", queryPrepareCmd},
1977 {"reset", queryResetCmd},
1978 {"step", queryStepCmd},
 
 
1979 {"strftime", queryStrftimeCmd},
1980 {0, 0}
1981 };
1982 static Th_SubCommand aSubWithStmt[] = {
1983 /* This subset is coded to deal with being supplied a statement
1984 via pStmt or via one of their args. When called like (query
1985 StmtId ...) only these subcommands will be checked.*/
 
1986 {"bind", queryBindTopLevelCmd},
1987 {"col", queryColTopLevelCmd},
1988 {"step", queryStepCmd},
1989 {"finalize", queryFinalizeCmd},
1990 {"reset", queryResetCmd},
1991 {0, 0}
1992 };
1993
1994
1995 assert( NULL != Th_query_manager(interp) );
1996 if( 1 == argc ){
1997 Th_WrongNumArgs2( interp, argv[0], argl[0],
1998 "subcommand: bind|col|finalize|prepare|reset|step|strftime");
1999 return TH_ERROR;
2000 }else if( 0 == Th_TryInt(interp,argv[1], argl[1], &stmtId) ){
2001 ++argv;
2002 ++argl;
2003 --argc;
2004 pStmt = Th_query_GetStmt( interp, stmtId );
2005 }
2006
2007 Th_CallSubCommand2( interp, pStmt, argc, argv, argl,
2008 pStmt ? aSubWithStmt : aSubAll );
2009 }
2010
2011 /*
2012 ** Registers the "query" API with the given interpreter. Returns TH_OK
2013 ** on success, TH_ERROR on error.
2014 */
2015 int th_register_query(Th_Interp *interp){
2016 enum { BufLen = 100 };
2017 char buf[BufLen];
2018 int i, l;
2019 #define SET(K) l = snprintf(buf, BufLen, "%d", K); \
@@ -1943,50 +2031,44 @@
2031 int rc = TH_OK;
2032 static Th_Command_Reg aCommand[] = {
2033 {"query", queryTopLevelCmd, 0},
2034 {0, 0, 0}
2035 };
2036 rc = Th_RegisterCommands( interp, aCommand );
2037 if(TH_OK==rc){
2038 Th_Query * sq = Th_Malloc(interp, sizeof(Th_Query));
2039 if(!sq){
2040 rc = TH_ERROR;
2041 }else{
2042 assert( NULL == sq->aStmt );
2043 assert( 0 == sq->nStmt );
2044 Th_SetData( interp, Th_Query_KEY, sq, finalizerSqlite );
2045 assert( sq == Th_query_manager(interp) );
2046 }
2047 }
2048 return rc;
2049 }
2050
2051 #endif
2052 /* end TH_ENABLE_QUERY */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2053
2054 /*
2055 ** Make sure the interpreter has been initialized. Initialize it if
2056 ** it has not been already.
2057 **
2058 ** The interpreter is stored in the g.interp global variable.
2059 */
2060 void Th_FossilInit(void){
2061 /* The fossil-internal Th_Vtab instance. */
2062 static Th_Vtab vtab = { xRealloc, {/*out*/
2063 NULL /*write()*/,
2064 NULL/*dispose()*/,
2065 NULL/*pState*/,
2066 1/*enabled*/
2067 }
2068 };
2069
2070 static PutsCmdData puts_Html = {0, 0, 0};
2071 static PutsCmdData puts_Normal = {1, 0, 0};
2072 static Th_Command_Reg aCommand[] = {
2073 {"anycap", anycapCmd, 0},
2074 {"combobox", comboboxCmd, 0},
@@ -2007,13 +2089,13 @@
2089 {0, 0, 0}
2090 };
2091 if( g.interp==0 ){
2092 int i;
2093 if(g.cgiOutput){
2094 vtab.out.write = Th_Output_f_cgi_content;
2095 }else{
2096 vtab.out = Th_Vtab_OutputMethods_FILE;
2097 vtab.out.pState = stdout;
2098 }
2099 vtab.out.enabled = enableOutput;
2100 g.interp = Th_CreateInterp(&vtab);
2101 th_register_language(g.interp); /* Basic scripting commands. */
@@ -2020,20 +2102,20 @@
2102 #ifdef FOSSIL_ENABLE_TCL
2103 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
2104 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
2105 }
2106 #endif
2107 #ifdef TH_ENABLE_OB
2108 th_register_ob(g.interp);
2109 #endif
2110 #ifdef TH_ENABLE_QUERY
2111 th_register_query(g.interp);
2112 #endif
2113 #ifdef TH_ENABLE_ARGV
2114 th_register_argv(g.interp);
2115 #endif
2116 Th_RegisterCommands( g.interp, aCommand );
2117 Th_Eval( g.interp, 0, "proc incr {name {step 1}} {\n"
2118 "upvar $name x\n"
2119 "set x [expr $x+$step]\n"
2120 "}", -1 );
2121 }
@@ -2140,11 +2222,11 @@
2222 ** then TH1 variables are $aaa or $<aaa>. The first form of variable
2223 ** is literal. The second is run through htmlize before being
2224 ** inserted.
2225 **
2226 ** This routine processes the template and writes the results
2227 ** via Th_Output().
2228 */
2229 int Th_Render(const char *z, int flags){
2230 int i = 0;
2231 int n;
2232 int rc = TH_OK;
@@ -2201,12 +2283,16 @@
2283 ** COMMAND: th1
2284 **
2285 ** Processes a file provided on the command line as a TH1-capable
2286 ** script/page. Output is sent to stdout or the CGI output buffer, as
2287 ** appropriate. The input file is assumed to be text/wiki/HTML content
2288 ** which may contain TH1 tag blocks and variables in the form $var or
2289 ** $<var>. Each block is executed in the same TH1 interpreter
2290 ** instance.
2291 **
2292 ** ACHTUNG: not all of the $variables which are set in CGI mode
2293 ** are available via this (CLI) command.
2294 **
2295 */
2296 void test_th_render(void){
2297 Blob in;
2298 if( g.argc<3 ){
@@ -2213,12 +2299,12 @@
2299 usage("FILE");
2300 assert(0 && "usage() does not return");
2301 }
2302 blob_zero(&in);
2303 db_open_config(0); /* Needed for global "tcl" setting. */
2304 #ifdef TH_ENABLE_QUERY
2305 db_find_and_open_repository(OPEN_ANY_SCHEMA,0)
2306 /* required for th1 query API. */;
2307 #endif
2308 blob_read_from_file(&in, g.argv[2]);
2309 Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT);
2310 }
2311

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button