Fossil SCM

Added ability to tie client data/finalizer to th1, allowing a refactoring of OB manager to use per-interpreter-instance state instead of global state.

stephan 2012-07-15 10:59 th1-query-api
Commit 147c602d935c272b1f754581427fab0b326cbdba
2 files changed +139 -37 +25
+139 -37
--- src/th.c
+++ src/th.c
@@ -18,26 +18,32 @@
1818
1919
typedef struct Th_Command Th_Command;
2020
typedef struct Th_Frame Th_Frame;
2121
typedef struct Th_Variable Th_Variable;
2222
23
+struct Th_GcEntry {
24
+ void * pData;
25
+ void (*xDel)( Th_Interp *, void * );
26
+};
27
+typedef struct Th_GcEntry Th_GcEntry;
28
+
2329
/*
2430
** Interpreter structure.
2531
*/
2632
struct Th_Interp {
27
- Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
33
+ Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
2834
char *zResult; /* Current interpreter result (Th_Malloc()ed) */
29
- int nResult; /* number of bytes in zResult */
30
- Th_Hash *paCmd; /* Table of registered commands */
31
- Th_Frame *pFrame; /* Current execution frame */
32
- int isListMode; /* True if thSplitList() should operate in "list" mode */
35
+ int nResult; /* number of bytes in zResult */
36
+ Th_Hash *paCmd; /* Table of registered commands */
37
+ Th_Frame *pFrame; /* Current execution frame */
38
+ int isListMode; /* True if thSplitList() should operate in "list" mode */
39
+ Th_Hash * paGc; /* holds client-provided data owned by this object */
3340
#ifdef TH_USE_SQLITE
3441
struct {
3542
sqlite3_stmt ** aStmt;
3643
int nStmt;
37
- int rc;
38
- } stmt;
44
+ } stmt; /* list of prepared statements */
3945
#endif
4046
};
4147
4248
/*
4349
** Each TH command registered using Th_CreateCommand() is represented
@@ -121,10 +127,11 @@
121127
static int thPushFrame(Th_Interp*, Th_Frame*);
122128
static void thPopFrame(Th_Interp*);
123129
124130
static void thFreeVariable(Th_HashEntry*, void*);
125131
static void thFreeCommand(Th_HashEntry*, void*);
132
+static void thFreeGc(Th_HashEntry*, void*);
126133
127134
/*
128135
** The following are used by both the expression and language parsers.
129136
** Given that the start of the input string (z, n) is a language
130137
** construct of the relevant type (a command enclosed in [], an escape
@@ -303,10 +310,26 @@
303310
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
304311
}
305312
Th_Free((Th_Interp *)pContext, pEntry->pData);
306313
pEntry->pData = 0;
307314
}
315
+
316
+/*
317
+** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees
318
+** pEntry->pData but not pEntry.
319
+*/
320
+static void thFreeGc(Th_HashEntry *pEntry, void *pContext){
321
+ Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData;
322
+ if(gc){
323
+ if( gc->xDel ){
324
+ gc->xDel( (Th_Interp*)pContext, gc->pData );
325
+ }
326
+ Th_Free((Th_Interp *)pContext, pEntry->pData);
327
+ pEntry->pData = 0;
328
+ }
329
+}
330
+
308331
309332
/*
310333
** Push a new frame onto the stack.
311334
*/
312335
static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){
@@ -1682,10 +1705,17 @@
16821705
*/
16831706
void Th_DeleteInterp(Th_Interp *interp){
16841707
assert(interp->pFrame);
16851708
assert(0==interp->pFrame->pCaller);
16861709
1710
+ /* Delete any client-side gc entries first. */
1711
+ if( interp->paGc ){
1712
+ Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
1713
+ Th_HashDelete(interp, interp->paGc);
1714
+ interp->paGc = NULL;
1715
+ }
1716
+
16871717
/* Delete the contents of the global frame. */
16881718
thPopFrame(interp);
16891719
16901720
/* Delete any result currently stored in the interpreter. */
16911721
Th_SetResult(interp, 0, 0);
@@ -2332,11 +2362,11 @@
23322362
** a null pointer.
23332363
*/
23342364
int th_strlen(const char *zStr){
23352365
int n = 0;
23362366
if( zStr ){
2337
- while( zStr[n] ) n++;
2367
+ while( zStr[n] ) ++n;
23382368
}
23392369
return n;
23402370
}
23412371
23422372
/* Whitespace characters:
@@ -2697,10 +2727,40 @@
26972727
26982728
*z = '\0';
26992729
return Th_SetResult(interp, zBuf, -1);
27002730
}
27012731
2732
+
2733
+int Th_Data_Set( Th_Interp * interp, char const * key,
2734
+ void * pData,
2735
+ void (*finalizer)( Th_Interp *, void * ) ){
2736
+ Th_HashEntry * pEnt;
2737
+ Th_GcEntry * pGc;
2738
+ if(NULL == interp->paGc){
2739
+ interp->paGc = Th_HashNew(interp);
2740
+ assert(NULL != interp->paGc);
2741
+ if(!interp->paGc){
2742
+ return TH_ERROR;
2743
+ }
2744
+ }
2745
+ pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1);
2746
+ if( pEnt->pData ){
2747
+ thFreeGc( pEnt, interp );
2748
+ }
2749
+ assert( NULL == pEnt->pData );
2750
+ pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry));
2751
+ pGc->pData = pData;
2752
+ pGc->xDel = finalizer;
2753
+ return 0;
2754
+
2755
+}
2756
+void * Th_Data_Get( Th_Interp * interp, char const * key ){
2757
+ Th_HashEntry * e = interp->paGc
2758
+ ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
2759
+ : NULL;
2760
+ return e ? e->pData : NULL;
2761
+}
27022762
27032763
#ifdef TH_USE_SQLITE
27042764
int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
27052765
int i, x;
27062766
sqlite3_stmt * s;
@@ -2753,18 +2813,27 @@
27532813
/* Reminder: the ob code "really" belongs in th_lang.c,
27542814
but we need access to Th_Interp internals in order to
27552815
swap out Th_Vtab parts for purposes of stacking layers
27562816
of buffers.
27572817
*/
2758
-#define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL }
2818
+#define Th_Ob_Man_empty_m { \
2819
+ NULL/*aBuf*/, \
2820
+ 0/*nBuf*/, \
2821
+ -1/*cursor*/, \
2822
+ NULL/*interp*/, \
2823
+ NULL/*aVtab*/ \
2824
+}
27592825
static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;
27602826
static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m;
2761
-
2762
-Th_Ob_Man * Th_ob_manager(Th_Interp *ignored){
2763
- return &Th_Ob_Man_instance;
2827
+#define Th_Ob_Man_KEY "Th_Ob_Man"
2828
+Th_Ob_Man * Th_ob_manager(Th_Interp *interp){
2829
+ void * rc = Th_Data_Get(interp, Th_Ob_Man_KEY );
2830
+ return rc
2831
+ ? ((Th_GcEntry*)rc)->pData
2832
+ : NULL;
27642833
}
2765
-
2834
+
27662835
27672836
Blob * Th_ob_current( Th_Ob_Man * pMan ){
27682837
return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
27692838
}
27702839
@@ -2805,11 +2874,10 @@
28052874
x = pMan->nBuf + 5;
28062875
if( pMan->cursor >= x ) {
28072876
assert( 0 && "This really should not happen." );
28082877
x = pMan->cursor + 5;
28092878
}
2810
- /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/
28112879
void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
28122880
if(NULL==re){
28132881
goto error;
28142882
}
28152883
pMan->aBuf = (Blob **)re;
@@ -2832,18 +2900,19 @@
28322900
pMan->interp->pVtab = &Th_Vtab_Ob;
28332901
Th_Vtab_Ob.out.pState = pMan;
28342902
if( pOut ){
28352903
*pOut = pBlob;
28362904
}
2837
- /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/
28382905
return TH_OK;
28392906
error:
28402907
if( pBlob ){
28412908
Th_Free( pMan->interp, pBlob );
28422909
}
28432910
return TH_ERROR;
28442911
}
2912
+
2913
+
28452914
28462915
Blob * Th_ob_pop( Th_Ob_Man * pMan ){
28472916
if( pMan->cursor < 0 ){
28482917
return NULL;
28492918
}else{
@@ -2852,18 +2921,28 @@
28522921
rc = pMan->aBuf[pMan->cursor];
28532922
pMan->aBuf[pMan->cursor] = NULL;
28542923
pMan->interp->pVtab = pMan->aVtab[pMan->cursor];
28552924
pMan->aVtab[pMan->cursor] = NULL;
28562925
if(-1 == --pMan->cursor){
2926
+ Th_Interp * interp = pMan->interp;
28572927
Th_Free( pMan->interp, pMan->aBuf );
28582928
Th_Free( pMan->interp, pMan->aVtab );
28592929
*pMan = Th_Ob_Man_empty;
2930
+ pMan->interp = interp;
28602931
}
2861
- /*fprintf(stderr,"OB pop: %p level=%d\n", rc, pMan->cursor-1);*/
28622932
return rc;
28632933
}
28642934
}
2935
+
2936
+void Th_ob_cleanup( Th_Ob_Man * man ){
2937
+ Blob * b;
2938
+ while( (b = Th_ob_pop(man)) ){
2939
+ blob_reset(b);
2940
+ Th_Free( man->interp, b );
2941
+ }
2942
+}
2943
+
28652944
28662945
/*
28672946
** TH Syntax:
28682947
**
28692948
** ob clean
@@ -2872,20 +2951,23 @@
28722951
** the buffering level.
28732952
*/
28742953
static int ob_clean_command( Th_Interp *interp, void *ctx,
28752954
int argc, const char **argv, int *argl
28762955
){
2877
- Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
2956
+ const char doRc = ctx ? 1 : 0;
2957
+ Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
28782958
Blob * b;
28792959
assert( pMan && (interp == pMan->interp) );
28802960
b = pMan ? Th_ob_current(pMan) : NULL;
28812961
if(!b){
28822962
Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
28832963
return TH_ERROR;
28842964
}else{
28852965
blob_reset(b);
2886
- Th_SetResultInt( interp, 0 );
2966
+ if( doRc ) {
2967
+ Th_SetResultInt( interp, 0 );
2968
+ }
28872969
return TH_OK;
28882970
}
28892971
}
28902972
28912973
/*
@@ -2896,21 +2978,24 @@
28962978
** Erases any currently buffered contents and pops the current buffer
28972979
** from the stack.
28982980
*/
28992981
static int ob_end_command( Th_Interp *interp, void *ctx,
29002982
int argc, const char **argv, int *argl ){
2901
- Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
2983
+ const char doRc = ctx ? 1 : 0;
2984
+ Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
29022985
Blob * b;
29032986
assert( pMan && (interp == pMan->interp) );
29042987
b = Th_ob_pop(pMan);
29052988
if(!b){
29062989
Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
29072990
return TH_ERROR;
29082991
}else{
29092992
blob_reset(b);
29102993
Th_Free( interp, b );
2911
- Th_SetResultInt( interp, 0 );
2994
+ if(doRc){
2995
+ Th_SetResultInt( interp, 0 );
2996
+ }
29122997
return TH_OK;
29132998
}
29142999
}
29153000
29163001
/*
@@ -2949,11 +3034,11 @@
29493034
int subL = argl[argPos];
29503035
/* "flush end" */
29513036
if(th_strlen(sub)==3 &&
29523037
((0==memcmp("end", sub, subL)
29533038
|| (0==memcmp("pop", sub, subL))))){
2954
- rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1);
3039
+ rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
29553040
}
29563041
}
29573042
Th_SetResultInt( interp, 0 );
29583043
return rc;
29593044
}
@@ -2987,16 +3072,16 @@
29873072
if(argc>argPos){
29883073
sub = argv[argPos];
29893074
subL = argl[argPos];
29903075
/* "ob get clean" */
29913076
if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){
2992
- rc |= ob_clean_command(interp, ctx, argc-1, argv+1, argl+1);
3077
+ rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1);
29933078
}/* "ob get end" */
29943079
else if(!rc && th_strlen(sub)==3 &&
29953080
((0==memcmp("end", sub, subL))
29963081
|| (0==memcmp("pop", sub, subL)))){
2997
- rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1);
3082
+ rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
29983083
}
29993084
}
30003085
return rc;
30013086
}
30023087
}
@@ -3036,31 +3121,43 @@
30363121
if( TH_OK != rc ){
30373122
assert( NULL == b );
30383123
return rc;
30393124
}
30403125
assert( NULL != b );
3041
- /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/
30423126
Th_SetResultInt( interp, 1 + pMan->cursor );
30433127
return TH_OK;
30443128
}
3129
+
3130
+static void finalizerObMan( Th_Interp * interp, void * p ){
3131
+ Th_Ob_Man * man = (Th_Ob_Man*)p;
3132
+ /*printf("finalizerObMan(%p,%p)\n", interp, p );*/
3133
+ if(man){
3134
+ assert( interp == man->interp );
3135
+ Th_ob_cleanup( man );
3136
+ if( man != &Th_Ob_Man_instance ){
3137
+ Th_Free( interp, man );
3138
+ }
3139
+ }
3140
+}
30453141
30463142
/*
30473143
** TH Syntax:
30483144
**
3049
-** ob clean|end|flush|get|level|start
3145
+** ob clean|(end|pop)|flush|get|level|(start|push)
30503146
**
3051
-** Runs the given subcommand.
3147
+** Runs the given subcommand. Some subcommands have other subcommands
3148
+** (see their docs for details).
30523149
**
30533150
*/
30543151
static int ob_cmd(
30553152
Th_Interp *interp,
30563153
void *ignored,
30573154
int argc,
30583155
const char **argv,
30593156
int *argl
30603157
){
3061
- static Th_Ob_Man * pMan = &Th_Ob_Man_instance;
3158
+ Th_Ob_Man * pMan = Th_ob_manager(interp);
30623159
Th_SubCommand aSub[] = {
30633160
{ "clean", ob_clean_command },
30643161
{ "end", ob_end_command },
30653162
{ "flush", ob_flush_command },
30663163
{ "get", ob_get_command },
@@ -3068,29 +3165,34 @@
30683165
{ "pop", ob_end_command },
30693166
{ "push", ob_start_command },
30703167
{ "start", ob_start_command },
30713168
{ 0, 0 }
30723169
};
3073
- if(NULL == pMan->interp){
3074
- pMan->interp = interp;
3075
- /*
3076
- FIXME: add rudamentary at-finalization GC to Th_Interp and clean
3077
- this up there. We currently leak only if the client does not
3078
- close all buffering levels properly.
3079
- */
3080
- }
3170
+ assert(NULL != pMan && pMan->interp==interp);
30813171
return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
3082
-
30833172
}
30843173
30853174
int th_register_ob(Th_Interp * interp){
3175
+ int rc;
30863176
static Th_Command_Reg aCommand[] = {
30873177
{"ob", ob_cmd, 0},
30883178
{0,0,0}
30893179
};
3090
- return Th_register_commands( interp, aCommand );
3180
+ rc = Th_register_commands( interp, aCommand );
3181
+ if(NULL == Th_ob_manager(interp)){
3182
+ Th_Ob_Man * pMan;
3183
+ pMan = 1
3184
+ ? &Th_Ob_Man_instance
3185
+ : Th_Malloc(interp, sizeof(Th_Ob_Man));
3186
+ /* *pMan = Th_Ob_Man_empty;*/
3187
+ pMan->interp = interp;
3188
+ Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
3189
+ assert( NULL != Th_ob_manager(interp) );
3190
+ }
3191
+ return rc;
30913192
}
30923193
30933194
#undef Th_Ob_Man_empty_m
3195
+#undef Th_Ob_Man_KEY
30943196
#endif
30953197
/* end TH_USE_OUTBUF */
30963198
30973199
--- src/th.c
+++ src/th.c
@@ -18,26 +18,32 @@
18
19 typedef struct Th_Command Th_Command;
20 typedef struct Th_Frame Th_Frame;
21 typedef struct Th_Variable Th_Variable;
22
 
 
 
 
 
 
23 /*
24 ** Interpreter structure.
25 */
26 struct Th_Interp {
27 Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
28 char *zResult; /* Current interpreter result (Th_Malloc()ed) */
29 int nResult; /* number of bytes in zResult */
30 Th_Hash *paCmd; /* Table of registered commands */
31 Th_Frame *pFrame; /* Current execution frame */
32 int isListMode; /* True if thSplitList() should operate in "list" mode */
 
33 #ifdef TH_USE_SQLITE
34 struct {
35 sqlite3_stmt ** aStmt;
36 int nStmt;
37 int rc;
38 } stmt;
39 #endif
40 };
41
42 /*
43 ** Each TH command registered using Th_CreateCommand() is represented
@@ -121,10 +127,11 @@
121 static int thPushFrame(Th_Interp*, Th_Frame*);
122 static void thPopFrame(Th_Interp*);
123
124 static void thFreeVariable(Th_HashEntry*, void*);
125 static void thFreeCommand(Th_HashEntry*, void*);
 
126
127 /*
128 ** The following are used by both the expression and language parsers.
129 ** Given that the start of the input string (z, n) is a language
130 ** construct of the relevant type (a command enclosed in [], an escape
@@ -303,10 +310,26 @@
303 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
304 }
305 Th_Free((Th_Interp *)pContext, pEntry->pData);
306 pEntry->pData = 0;
307 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
309 /*
310 ** Push a new frame onto the stack.
311 */
312 static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){
@@ -1682,10 +1705,17 @@
1682 */
1683 void Th_DeleteInterp(Th_Interp *interp){
1684 assert(interp->pFrame);
1685 assert(0==interp->pFrame->pCaller);
1686
 
 
 
 
 
 
 
1687 /* Delete the contents of the global frame. */
1688 thPopFrame(interp);
1689
1690 /* Delete any result currently stored in the interpreter. */
1691 Th_SetResult(interp, 0, 0);
@@ -2332,11 +2362,11 @@
2332 ** a null pointer.
2333 */
2334 int th_strlen(const char *zStr){
2335 int n = 0;
2336 if( zStr ){
2337 while( zStr[n] ) n++;
2338 }
2339 return n;
2340 }
2341
2342 /* Whitespace characters:
@@ -2697,10 +2727,40 @@
2697
2698 *z = '\0';
2699 return Th_SetResult(interp, zBuf, -1);
2700 }
2701
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2702
2703 #ifdef TH_USE_SQLITE
2704 int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
2705 int i, x;
2706 sqlite3_stmt * s;
@@ -2753,18 +2813,27 @@
2753 /* Reminder: the ob code "really" belongs in th_lang.c,
2754 but we need access to Th_Interp internals in order to
2755 swap out Th_Vtab parts for purposes of stacking layers
2756 of buffers.
2757 */
2758 #define Th_Ob_Man_empty_m { NULL, 0, -1, NULL, NULL }
 
 
 
 
 
 
2759 static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;
2760 static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m;
2761
2762 Th_Ob_Man * Th_ob_manager(Th_Interp *ignored){
2763 return &Th_Ob_Man_instance;
 
 
 
2764 }
2765
2766
2767 Blob * Th_ob_current( Th_Ob_Man * pMan ){
2768 return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
2769 }
2770
@@ -2805,11 +2874,10 @@
2805 x = pMan->nBuf + 5;
2806 if( pMan->cursor >= x ) {
2807 assert( 0 && "This really should not happen." );
2808 x = pMan->cursor + 5;
2809 }
2810 /*fprintf(stderr,"OB EXPAND x=%d\n",x);*/
2811 void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
2812 if(NULL==re){
2813 goto error;
2814 }
2815 pMan->aBuf = (Blob **)re;
@@ -2832,18 +2900,19 @@
2832 pMan->interp->pVtab = &Th_Vtab_Ob;
2833 Th_Vtab_Ob.out.pState = pMan;
2834 if( pOut ){
2835 *pOut = pBlob;
2836 }
2837 /*fprintf(stderr,"OB PUSH: %p\n", pBlob);*/
2838 return TH_OK;
2839 error:
2840 if( pBlob ){
2841 Th_Free( pMan->interp, pBlob );
2842 }
2843 return TH_ERROR;
2844 }
 
 
2845
2846 Blob * Th_ob_pop( Th_Ob_Man * pMan ){
2847 if( pMan->cursor < 0 ){
2848 return NULL;
2849 }else{
@@ -2852,18 +2921,28 @@
2852 rc = pMan->aBuf[pMan->cursor];
2853 pMan->aBuf[pMan->cursor] = NULL;
2854 pMan->interp->pVtab = pMan->aVtab[pMan->cursor];
2855 pMan->aVtab[pMan->cursor] = NULL;
2856 if(-1 == --pMan->cursor){
 
2857 Th_Free( pMan->interp, pMan->aBuf );
2858 Th_Free( pMan->interp, pMan->aVtab );
2859 *pMan = Th_Ob_Man_empty;
 
2860 }
2861 /*fprintf(stderr,"OB pop: %p level=%d\n", rc, pMan->cursor-1);*/
2862 return rc;
2863 }
2864 }
 
 
 
 
 
 
 
 
 
2865
2866 /*
2867 ** TH Syntax:
2868 **
2869 ** ob clean
@@ -2872,20 +2951,23 @@
2872 ** the buffering level.
2873 */
2874 static int ob_clean_command( Th_Interp *interp, void *ctx,
2875 int argc, const char **argv, int *argl
2876 ){
2877 Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
 
2878 Blob * b;
2879 assert( pMan && (interp == pMan->interp) );
2880 b = pMan ? Th_ob_current(pMan) : NULL;
2881 if(!b){
2882 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2883 return TH_ERROR;
2884 }else{
2885 blob_reset(b);
2886 Th_SetResultInt( interp, 0 );
 
 
2887 return TH_OK;
2888 }
2889 }
2890
2891 /*
@@ -2896,21 +2978,24 @@
2896 ** Erases any currently buffered contents and pops the current buffer
2897 ** from the stack.
2898 */
2899 static int ob_end_command( Th_Interp *interp, void *ctx,
2900 int argc, const char **argv, int *argl ){
2901 Th_Ob_Man * pMan = (Th_Ob_Man *)ctx;
 
2902 Blob * b;
2903 assert( pMan && (interp == pMan->interp) );
2904 b = Th_ob_pop(pMan);
2905 if(!b){
2906 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2907 return TH_ERROR;
2908 }else{
2909 blob_reset(b);
2910 Th_Free( interp, b );
2911 Th_SetResultInt( interp, 0 );
 
 
2912 return TH_OK;
2913 }
2914 }
2915
2916 /*
@@ -2949,11 +3034,11 @@
2949 int subL = argl[argPos];
2950 /* "flush end" */
2951 if(th_strlen(sub)==3 &&
2952 ((0==memcmp("end", sub, subL)
2953 || (0==memcmp("pop", sub, subL))))){
2954 rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1);
2955 }
2956 }
2957 Th_SetResultInt( interp, 0 );
2958 return rc;
2959 }
@@ -2987,16 +3072,16 @@
2987 if(argc>argPos){
2988 sub = argv[argPos];
2989 subL = argl[argPos];
2990 /* "ob get clean" */
2991 if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){
2992 rc |= ob_clean_command(interp, ctx, argc-1, argv+1, argl+1);
2993 }/* "ob get end" */
2994 else if(!rc && th_strlen(sub)==3 &&
2995 ((0==memcmp("end", sub, subL))
2996 || (0==memcmp("pop", sub, subL)))){
2997 rc |= ob_end_command(interp, ctx, argc-1, argv+1, argl+1);
2998 }
2999 }
3000 return rc;
3001 }
3002 }
@@ -3036,31 +3121,43 @@
3036 if( TH_OK != rc ){
3037 assert( NULL == b );
3038 return rc;
3039 }
3040 assert( NULL != b );
3041 /*fprintf(stderr,"OB STARTED: %p level=%d\n", b, pMan->cursor);*/
3042 Th_SetResultInt( interp, 1 + pMan->cursor );
3043 return TH_OK;
3044 }
 
 
 
 
 
 
 
 
 
 
 
 
3045
3046 /*
3047 ** TH Syntax:
3048 **
3049 ** ob clean|end|flush|get|level|start
3050 **
3051 ** Runs the given subcommand.
 
3052 **
3053 */
3054 static int ob_cmd(
3055 Th_Interp *interp,
3056 void *ignored,
3057 int argc,
3058 const char **argv,
3059 int *argl
3060 ){
3061 static Th_Ob_Man * pMan = &Th_Ob_Man_instance;
3062 Th_SubCommand aSub[] = {
3063 { "clean", ob_clean_command },
3064 { "end", ob_end_command },
3065 { "flush", ob_flush_command },
3066 { "get", ob_get_command },
@@ -3068,29 +3165,34 @@
3068 { "pop", ob_end_command },
3069 { "push", ob_start_command },
3070 { "start", ob_start_command },
3071 { 0, 0 }
3072 };
3073 if(NULL == pMan->interp){
3074 pMan->interp = interp;
3075 /*
3076 FIXME: add rudamentary at-finalization GC to Th_Interp and clean
3077 this up there. We currently leak only if the client does not
3078 close all buffering levels properly.
3079 */
3080 }
3081 return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
3082
3083 }
3084
3085 int th_register_ob(Th_Interp * interp){
 
3086 static Th_Command_Reg aCommand[] = {
3087 {"ob", ob_cmd, 0},
3088 {0,0,0}
3089 };
3090 return Th_register_commands( interp, aCommand );
 
 
 
 
 
 
 
 
 
 
 
3091 }
3092
3093 #undef Th_Ob_Man_empty_m
 
3094 #endif
3095 /* end TH_USE_OUTBUF */
3096
3097
--- src/th.c
+++ src/th.c
@@ -18,26 +18,32 @@
18
19 typedef struct Th_Command Th_Command;
20 typedef struct Th_Frame Th_Frame;
21 typedef struct Th_Variable Th_Variable;
22
23 struct Th_GcEntry {
24 void * pData;
25 void (*xDel)( Th_Interp *, void * );
26 };
27 typedef struct Th_GcEntry Th_GcEntry;
28
29 /*
30 ** Interpreter structure.
31 */
32 struct Th_Interp {
33 Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */
34 char *zResult; /* Current interpreter result (Th_Malloc()ed) */
35 int nResult; /* number of bytes in zResult */
36 Th_Hash *paCmd; /* Table of registered commands */
37 Th_Frame *pFrame; /* Current execution frame */
38 int isListMode; /* True if thSplitList() should operate in "list" mode */
39 Th_Hash * paGc; /* holds client-provided data owned by this object */
40 #ifdef TH_USE_SQLITE
41 struct {
42 sqlite3_stmt ** aStmt;
43 int nStmt;
44 } stmt; /* list of prepared statements */
 
45 #endif
46 };
47
48 /*
49 ** Each TH command registered using Th_CreateCommand() is represented
@@ -121,10 +127,11 @@
127 static int thPushFrame(Th_Interp*, Th_Frame*);
128 static void thPopFrame(Th_Interp*);
129
130 static void thFreeVariable(Th_HashEntry*, void*);
131 static void thFreeCommand(Th_HashEntry*, void*);
132 static void thFreeGc(Th_HashEntry*, void*);
133
134 /*
135 ** The following are used by both the expression and language parsers.
136 ** Given that the start of the input string (z, n) is a language
137 ** construct of the relevant type (a command enclosed in [], an escape
@@ -303,10 +310,26 @@
310 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
311 }
312 Th_Free((Th_Interp *)pContext, pEntry->pData);
313 pEntry->pData = 0;
314 }
315
316 /*
317 ** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees
318 ** pEntry->pData but not pEntry.
319 */
320 static void thFreeGc(Th_HashEntry *pEntry, void *pContext){
321 Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData;
322 if(gc){
323 if( gc->xDel ){
324 gc->xDel( (Th_Interp*)pContext, gc->pData );
325 }
326 Th_Free((Th_Interp *)pContext, pEntry->pData);
327 pEntry->pData = 0;
328 }
329 }
330
331
332 /*
333 ** Push a new frame onto the stack.
334 */
335 static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){
@@ -1682,10 +1705,17 @@
1705 */
1706 void Th_DeleteInterp(Th_Interp *interp){
1707 assert(interp->pFrame);
1708 assert(0==interp->pFrame->pCaller);
1709
1710 /* Delete any client-side gc entries first. */
1711 if( interp->paGc ){
1712 Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp);
1713 Th_HashDelete(interp, interp->paGc);
1714 interp->paGc = NULL;
1715 }
1716
1717 /* Delete the contents of the global frame. */
1718 thPopFrame(interp);
1719
1720 /* Delete any result currently stored in the interpreter. */
1721 Th_SetResult(interp, 0, 0);
@@ -2332,11 +2362,11 @@
2362 ** a null pointer.
2363 */
2364 int th_strlen(const char *zStr){
2365 int n = 0;
2366 if( zStr ){
2367 while( zStr[n] ) ++n;
2368 }
2369 return n;
2370 }
2371
2372 /* Whitespace characters:
@@ -2697,10 +2727,40 @@
2727
2728 *z = '\0';
2729 return Th_SetResult(interp, zBuf, -1);
2730 }
2731
2732
2733 int Th_Data_Set( Th_Interp * interp, char const * key,
2734 void * pData,
2735 void (*finalizer)( Th_Interp *, void * ) ){
2736 Th_HashEntry * pEnt;
2737 Th_GcEntry * pGc;
2738 if(NULL == interp->paGc){
2739 interp->paGc = Th_HashNew(interp);
2740 assert(NULL != interp->paGc);
2741 if(!interp->paGc){
2742 return TH_ERROR;
2743 }
2744 }
2745 pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1);
2746 if( pEnt->pData ){
2747 thFreeGc( pEnt, interp );
2748 }
2749 assert( NULL == pEnt->pData );
2750 pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry));
2751 pGc->pData = pData;
2752 pGc->xDel = finalizer;
2753 return 0;
2754
2755 }
2756 void * Th_Data_Get( Th_Interp * interp, char const * key ){
2757 Th_HashEntry * e = interp->paGc
2758 ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0)
2759 : NULL;
2760 return e ? e->pData : NULL;
2761 }
2762
2763 #ifdef TH_USE_SQLITE
2764 int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
2765 int i, x;
2766 sqlite3_stmt * s;
@@ -2753,18 +2813,27 @@
2813 /* Reminder: the ob code "really" belongs in th_lang.c,
2814 but we need access to Th_Interp internals in order to
2815 swap out Th_Vtab parts for purposes of stacking layers
2816 of buffers.
2817 */
2818 #define Th_Ob_Man_empty_m { \
2819 NULL/*aBuf*/, \
2820 0/*nBuf*/, \
2821 -1/*cursor*/, \
2822 NULL/*interp*/, \
2823 NULL/*aVtab*/ \
2824 }
2825 static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m;
2826 static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m;
2827 #define Th_Ob_Man_KEY "Th_Ob_Man"
2828 Th_Ob_Man * Th_ob_manager(Th_Interp *interp){
2829 void * rc = Th_Data_Get(interp, Th_Ob_Man_KEY );
2830 return rc
2831 ? ((Th_GcEntry*)rc)->pData
2832 : NULL;
2833 }
2834
2835
2836 Blob * Th_ob_current( Th_Ob_Man * pMan ){
2837 return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0;
2838 }
2839
@@ -2805,11 +2874,10 @@
2874 x = pMan->nBuf + 5;
2875 if( pMan->cursor >= x ) {
2876 assert( 0 && "This really should not happen." );
2877 x = pMan->cursor + 5;
2878 }
 
2879 void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) );
2880 if(NULL==re){
2881 goto error;
2882 }
2883 pMan->aBuf = (Blob **)re;
@@ -2832,18 +2900,19 @@
2900 pMan->interp->pVtab = &Th_Vtab_Ob;
2901 Th_Vtab_Ob.out.pState = pMan;
2902 if( pOut ){
2903 *pOut = pBlob;
2904 }
 
2905 return TH_OK;
2906 error:
2907 if( pBlob ){
2908 Th_Free( pMan->interp, pBlob );
2909 }
2910 return TH_ERROR;
2911 }
2912
2913
2914
2915 Blob * Th_ob_pop( Th_Ob_Man * pMan ){
2916 if( pMan->cursor < 0 ){
2917 return NULL;
2918 }else{
@@ -2852,18 +2921,28 @@
2921 rc = pMan->aBuf[pMan->cursor];
2922 pMan->aBuf[pMan->cursor] = NULL;
2923 pMan->interp->pVtab = pMan->aVtab[pMan->cursor];
2924 pMan->aVtab[pMan->cursor] = NULL;
2925 if(-1 == --pMan->cursor){
2926 Th_Interp * interp = pMan->interp;
2927 Th_Free( pMan->interp, pMan->aBuf );
2928 Th_Free( pMan->interp, pMan->aVtab );
2929 *pMan = Th_Ob_Man_empty;
2930 pMan->interp = interp;
2931 }
 
2932 return rc;
2933 }
2934 }
2935
2936 void Th_ob_cleanup( Th_Ob_Man * man ){
2937 Blob * b;
2938 while( (b = Th_ob_pop(man)) ){
2939 blob_reset(b);
2940 Th_Free( man->interp, b );
2941 }
2942 }
2943
2944
2945 /*
2946 ** TH Syntax:
2947 **
2948 ** ob clean
@@ -2872,20 +2951,23 @@
2951 ** the buffering level.
2952 */
2953 static int ob_clean_command( Th_Interp *interp, void *ctx,
2954 int argc, const char **argv, int *argl
2955 ){
2956 const char doRc = ctx ? 1 : 0;
2957 Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
2958 Blob * b;
2959 assert( pMan && (interp == pMan->interp) );
2960 b = pMan ? Th_ob_current(pMan) : NULL;
2961 if(!b){
2962 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2963 return TH_ERROR;
2964 }else{
2965 blob_reset(b);
2966 if( doRc ) {
2967 Th_SetResultInt( interp, 0 );
2968 }
2969 return TH_OK;
2970 }
2971 }
2972
2973 /*
@@ -2896,21 +2978,24 @@
2978 ** Erases any currently buffered contents and pops the current buffer
2979 ** from the stack.
2980 */
2981 static int ob_end_command( Th_Interp *interp, void *ctx,
2982 int argc, const char **argv, int *argl ){
2983 const char doRc = ctx ? 1 : 0;
2984 Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp);
2985 Blob * b;
2986 assert( pMan && (interp == pMan->interp) );
2987 b = Th_ob_pop(pMan);
2988 if(!b){
2989 Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 );
2990 return TH_ERROR;
2991 }else{
2992 blob_reset(b);
2993 Th_Free( interp, b );
2994 if(doRc){
2995 Th_SetResultInt( interp, 0 );
2996 }
2997 return TH_OK;
2998 }
2999 }
3000
3001 /*
@@ -2949,11 +3034,11 @@
3034 int subL = argl[argPos];
3035 /* "flush end" */
3036 if(th_strlen(sub)==3 &&
3037 ((0==memcmp("end", sub, subL)
3038 || (0==memcmp("pop", sub, subL))))){
3039 rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
3040 }
3041 }
3042 Th_SetResultInt( interp, 0 );
3043 return rc;
3044 }
@@ -2987,16 +3072,16 @@
3072 if(argc>argPos){
3073 sub = argv[argPos];
3074 subL = argl[argPos];
3075 /* "ob get clean" */
3076 if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){
3077 rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1);
3078 }/* "ob get end" */
3079 else if(!rc && th_strlen(sub)==3 &&
3080 ((0==memcmp("end", sub, subL))
3081 || (0==memcmp("pop", sub, subL)))){
3082 rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1);
3083 }
3084 }
3085 return rc;
3086 }
3087 }
@@ -3036,31 +3121,43 @@
3121 if( TH_OK != rc ){
3122 assert( NULL == b );
3123 return rc;
3124 }
3125 assert( NULL != b );
 
3126 Th_SetResultInt( interp, 1 + pMan->cursor );
3127 return TH_OK;
3128 }
3129
3130 static void finalizerObMan( Th_Interp * interp, void * p ){
3131 Th_Ob_Man * man = (Th_Ob_Man*)p;
3132 /*printf("finalizerObMan(%p,%p)\n", interp, p );*/
3133 if(man){
3134 assert( interp == man->interp );
3135 Th_ob_cleanup( man );
3136 if( man != &Th_Ob_Man_instance ){
3137 Th_Free( interp, man );
3138 }
3139 }
3140 }
3141
3142 /*
3143 ** TH Syntax:
3144 **
3145 ** ob clean|(end|pop)|flush|get|level|(start|push)
3146 **
3147 ** Runs the given subcommand. Some subcommands have other subcommands
3148 ** (see their docs for details).
3149 **
3150 */
3151 static int ob_cmd(
3152 Th_Interp *interp,
3153 void *ignored,
3154 int argc,
3155 const char **argv,
3156 int *argl
3157 ){
3158 Th_Ob_Man * pMan = Th_ob_manager(interp);
3159 Th_SubCommand aSub[] = {
3160 { "clean", ob_clean_command },
3161 { "end", ob_end_command },
3162 { "flush", ob_flush_command },
3163 { "get", ob_get_command },
@@ -3068,29 +3165,34 @@
3165 { "pop", ob_end_command },
3166 { "push", ob_start_command },
3167 { "start", ob_start_command },
3168 { 0, 0 }
3169 };
3170 assert(NULL != pMan && pMan->interp==interp);
 
 
 
 
 
 
 
3171 return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub);
 
3172 }
3173
3174 int th_register_ob(Th_Interp * interp){
3175 int rc;
3176 static Th_Command_Reg aCommand[] = {
3177 {"ob", ob_cmd, 0},
3178 {0,0,0}
3179 };
3180 rc = Th_register_commands( interp, aCommand );
3181 if(NULL == Th_ob_manager(interp)){
3182 Th_Ob_Man * pMan;
3183 pMan = 1
3184 ? &Th_Ob_Man_instance
3185 : Th_Malloc(interp, sizeof(Th_Ob_Man));
3186 /* *pMan = Th_Ob_Man_empty;*/
3187 pMan->interp = interp;
3188 Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan );
3189 assert( NULL != Th_ob_manager(interp) );
3190 }
3191 return rc;
3192 }
3193
3194 #undef Th_Ob_Man_empty_m
3195 #undef Th_Ob_Man_KEY
3196 #endif
3197 /* end TH_USE_OUTBUF */
3198
3199
+25
--- src/th.h
+++ src/th.h
@@ -269,10 +269,35 @@
269269
#define Th_Render_Flags_DEFAULT 0
270270
#define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)
271271
/*};*/
272272
273273
int Th_Render(const char *z, int flags);
274
+
275
+/*
276
+** Adds a piece of memory to the given interpreter, such that:
277
+**
278
+** a) it will be cleaned up when the interpreter is destroyed, by
279
+** calling finalizer(interp, pData). The finalizer may be NULL.
280
+** Cleanup happens in an unspecified/unpredictable order.
281
+**
282
+** b) it can be fetched via Th_Data_Get().
283
+**
284
+** If a given key is added more than once then any previous
285
+** entry is cleaned up before adding it.
286
+**
287
+** Returns 0 on success, non-0 on allocation error.
288
+*/
289
+int Th_Data_Set( Th_Interp * interp, char const * key,
290
+ void * pData,
291
+ void (*finalizer)( Th_Interp *, void * ) );
292
+
293
+/*
294
+** Fetches data added via Th_Data_Set(), or NULL if no data
295
+** has been associated with the given key.
296
+*/
297
+void * Th_Data_Get( Th_Interp * interp, char const * key );
298
+
274299
275300
/*
276301
** Registers a list of commands with the interpreter. pList must be a non-NULL
277302
** pointer to an array of Th_Command_Reg objects, the last one of which MUST
278303
** have a NULL zName field (that is the end-of-list marker).
279304
--- src/th.h
+++ src/th.h
@@ -269,10 +269,35 @@
269 #define Th_Render_Flags_DEFAULT 0
270 #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)
271 /*};*/
272
273 int Th_Render(const char *z, int flags);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
275 /*
276 ** Registers a list of commands with the interpreter. pList must be a non-NULL
277 ** pointer to an array of Th_Command_Reg objects, the last one of which MUST
278 ** have a NULL zName field (that is the end-of-list marker).
279
--- src/th.h
+++ src/th.h
@@ -269,10 +269,35 @@
269 #define Th_Render_Flags_DEFAULT 0
270 #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1)
271 /*};*/
272
273 int Th_Render(const char *z, int flags);
274
275 /*
276 ** Adds a piece of memory to the given interpreter, such that:
277 **
278 ** a) it will be cleaned up when the interpreter is destroyed, by
279 ** calling finalizer(interp, pData). The finalizer may be NULL.
280 ** Cleanup happens in an unspecified/unpredictable order.
281 **
282 ** b) it can be fetched via Th_Data_Get().
283 **
284 ** If a given key is added more than once then any previous
285 ** entry is cleaned up before adding it.
286 **
287 ** Returns 0 on success, non-0 on allocation error.
288 */
289 int Th_Data_Set( Th_Interp * interp, char const * key,
290 void * pData,
291 void (*finalizer)( Th_Interp *, void * ) );
292
293 /*
294 ** Fetches data added via Th_Data_Set(), or NULL if no data
295 ** has been associated with the given key.
296 */
297 void * Th_Data_Get( Th_Interp * interp, char const * key );
298
299
300 /*
301 ** Registers a list of commands with the interpreter. pList must be a non-NULL
302 ** pointer to an array of Th_Command_Reg objects, the last one of which MUST
303 ** have a NULL zName field (that is the end-of-list marker).
304

Keyboard Shortcuts

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