Fossil SCM

Cherrypick MSVC/TH1 related fixes [42316a14e2], [354288db9c], [9dc0877d91], [fb29094a8f], [e0f22dda7b], [1aeb2726b0], [95292a13fa], [5e368e911d], [dd8d317670], and [f61958b183] from trunk for review.

mistachkin 2014-01-16 10:01 pending-review
Commit 76442af7e13267bd164a2a681dc4ef2757ebcd4f
+6 -5
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378378
if(g.db){
379379
db_close(0);
380380
}
381381
/*
382382
** FIXME: The next two lines cannot always be enabled; however, they
383
- ** are useful for tracking down TH1 memory leaks.
383
+ ** are very useful for tracking down TH1 memory leaks.
384384
*/
385
- /*
386
- if( g.interp ){
387
- Th_DeleteInterp(g.interp); g.interp = 0;
385
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386
+ if( g.interp ){
387
+ Th_DeleteInterp(g.interp); g.interp = 0;
388
+ }
389
+ assert( Th_GetOutstandingMalloc()==0 );
388390
}
389
- assert( Th_GetOutstandingMalloc()==0 ); */
390391
}
391392
392393
/*
393394
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394395
** search g.argv for arguments "--args FILENAME". If found, then
395396
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are useful for tracking down TH1 memory leaks.
384 */
385 /*
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
 
 
388 }
389 assert( Th_GetOutstandingMalloc()==0 ); */
390 }
391
392 /*
393 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394 ** search g.argv for arguments "--args FILENAME". If found, then
395
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are very useful for tracking down TH1 memory leaks.
384 */
385 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
388 }
389 assert( Th_GetOutstandingMalloc()==0 );
390 }
 
391 }
392
393 /*
394 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
395 ** search g.argv for arguments "--args FILENAME". If found, then
396
+6 -5
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378378
if(g.db){
379379
db_close(0);
380380
}
381381
/*
382382
** FIXME: The next two lines cannot always be enabled; however, they
383
- ** are useful for tracking down TH1 memory leaks.
383
+ ** are very useful for tracking down TH1 memory leaks.
384384
*/
385
- /*
386
- if( g.interp ){
387
- Th_DeleteInterp(g.interp); g.interp = 0;
385
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386
+ if( g.interp ){
387
+ Th_DeleteInterp(g.interp); g.interp = 0;
388
+ }
389
+ assert( Th_GetOutstandingMalloc()==0 );
388390
}
389
- assert( Th_GetOutstandingMalloc()==0 ); */
390391
}
391392
392393
/*
393394
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394395
** search g.argv for arguments "--args FILENAME". If found, then
395396
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are useful for tracking down TH1 memory leaks.
384 */
385 /*
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
 
 
388 }
389 assert( Th_GetOutstandingMalloc()==0 ); */
390 }
391
392 /*
393 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394 ** search g.argv for arguments "--args FILENAME". If found, then
395
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are very useful for tracking down TH1 memory leaks.
384 */
385 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
388 }
389 assert( Th_GetOutstandingMalloc()==0 );
390 }
 
391 }
392
393 /*
394 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
395 ** search g.argv for arguments "--args FILENAME". If found, then
396
+6 -5
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378378
if(g.db){
379379
db_close(0);
380380
}
381381
/*
382382
** FIXME: The next two lines cannot always be enabled; however, they
383
- ** are useful for tracking down TH1 memory leaks.
383
+ ** are very useful for tracking down TH1 memory leaks.
384384
*/
385
- /*
386
- if( g.interp ){
387
- Th_DeleteInterp(g.interp); g.interp = 0;
385
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386
+ if( g.interp ){
387
+ Th_DeleteInterp(g.interp); g.interp = 0;
388
+ }
389
+ assert( Th_GetOutstandingMalloc()==0 );
388390
}
389
- assert( Th_GetOutstandingMalloc()==0 ); */
390391
}
391392
392393
/*
393394
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394395
** search g.argv for arguments "--args FILENAME". If found, then
395396
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are useful for tracking down TH1 memory leaks.
384 */
385 /*
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
 
 
388 }
389 assert( Th_GetOutstandingMalloc()==0 ); */
390 }
391
392 /*
393 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394 ** search g.argv for arguments "--args FILENAME". If found, then
395
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are very useful for tracking down TH1 memory leaks.
384 */
385 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
388 }
389 assert( Th_GetOutstandingMalloc()==0 );
390 }
 
391 }
392
393 /*
394 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
395 ** search g.argv for arguments "--args FILENAME". If found, then
396
+6 -5
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378378
if(g.db){
379379
db_close(0);
380380
}
381381
/*
382382
** FIXME: The next two lines cannot always be enabled; however, they
383
- ** are useful for tracking down TH1 memory leaks.
383
+ ** are very useful for tracking down TH1 memory leaks.
384384
*/
385
- /*
386
- if( g.interp ){
387
- Th_DeleteInterp(g.interp); g.interp = 0;
385
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386
+ if( g.interp ){
387
+ Th_DeleteInterp(g.interp); g.interp = 0;
388
+ }
389
+ assert( Th_GetOutstandingMalloc()==0 );
388390
}
389
- assert( Th_GetOutstandingMalloc()==0 ); */
390391
}
391392
392393
/*
393394
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394395
** search g.argv for arguments "--args FILENAME". If found, then
395396
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are useful for tracking down TH1 memory leaks.
384 */
385 /*
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
 
 
388 }
389 assert( Th_GetOutstandingMalloc()==0 ); */
390 }
391
392 /*
393 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394 ** search g.argv for arguments "--args FILENAME". If found, then
395
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are very useful for tracking down TH1 memory leaks.
384 */
385 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
388 }
389 assert( Th_GetOutstandingMalloc()==0 );
390 }
 
391 }
392
393 /*
394 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
395 ** search g.argv for arguments "--args FILENAME". If found, then
396
+6 -5
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378378
if(g.db){
379379
db_close(0);
380380
}
381381
/*
382382
** FIXME: The next two lines cannot always be enabled; however, they
383
- ** are useful for tracking down TH1 memory leaks.
383
+ ** are very useful for tracking down TH1 memory leaks.
384384
*/
385
- /*
386
- if( g.interp ){
387
- Th_DeleteInterp(g.interp); g.interp = 0;
385
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386
+ if( g.interp ){
387
+ Th_DeleteInterp(g.interp); g.interp = 0;
388
+ }
389
+ assert( Th_GetOutstandingMalloc()==0 );
388390
}
389
- assert( Th_GetOutstandingMalloc()==0 ); */
390391
}
391392
392393
/*
393394
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394395
** search g.argv for arguments "--args FILENAME". If found, then
395396
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are useful for tracking down TH1 memory leaks.
384 */
385 /*
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
 
 
388 }
389 assert( Th_GetOutstandingMalloc()==0 ); */
390 }
391
392 /*
393 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
394 ** search g.argv for arguments "--args FILENAME". If found, then
395
--- src/main.c
+++ src/main.c
@@ -378,17 +378,18 @@
378 if(g.db){
379 db_close(0);
380 }
381 /*
382 ** FIXME: The next two lines cannot always be enabled; however, they
383 ** are very useful for tracking down TH1 memory leaks.
384 */
385 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
386 if( g.interp ){
387 Th_DeleteInterp(g.interp); g.interp = 0;
388 }
389 assert( Th_GetOutstandingMalloc()==0 );
390 }
 
391 }
392
393 /*
394 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
395 ** search g.argv for arguments "--args FILENAME". If found, then
396
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
10331033
10341034
!ifdef FOSSIL_ENABLE_SSL
10351035
INCL = $(INCL) -I$(SSLINCDIR)
10361036
!endif
10371037
1038
-CFLAGS = -nologo -MT -O2
1038
+CFLAGS = -nologo
10391039
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
10401040
10411041
!ifdef DEBUG
1042
-CFLAGS = $(CFLAGS) -Zi
1042
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
10431043
LDFLAGS = $(LDFLAGS) /DEBUG
1044
+!else
1045
+CFLAGS = $(CFLAGS) -MT -O2
10441046
!endif
10451047
10461048
BCC = $(CC) $(CFLAGS)
10471049
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
10481050
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
11281130
$(BCC) $**
11291131
11301132
mkversion$E: $B\src\mkversion.c
11311133
$(BCC) $**
11321134
1133
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1135
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
11341136
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
11351137
1136
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1138
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
11371139
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
11381140
11391141
$(OX)\th$O : $(SRCDIR)\th.c
11401142
$(TCC) /Fo$@ -c $**
11411143
11421144
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo -MT -O2
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi
1043 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
1044 !endif
1045
1046 BCC = $(CC) $(CFLAGS)
1047 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1048 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1128 $(BCC) $**
1129
1130 mkversion$E: $B\src\mkversion.c
1131 $(BCC) $**
1132
1133 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
1134 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1135
1136 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
1137 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1138
1139 $(OX)\th$O : $(SRCDIR)\th.c
1140 $(TCC) /Fo$@ -c $**
1141
1142
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1033,16 +1033,18 @@
1033
1034 !ifdef FOSSIL_ENABLE_SSL
1035 INCL = $(INCL) -I$(SSLINCDIR)
1036 !endif
1037
1038 CFLAGS = -nologo
1039 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
1040
1041 !ifdef DEBUG
1042 CFLAGS = $(CFLAGS) -Zi -MTd -Od
1043 LDFLAGS = $(LDFLAGS) /DEBUG
1044 !else
1045 CFLAGS = $(CFLAGS) -MT -O2
1046 !endif
1047
1048 BCC = $(CC) $(CFLAGS)
1049 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
1050 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -1128,14 +1130,14 @@
1130 $(BCC) $**
1131
1132 mkversion$E: $B\src\mkversion.c
1133 $(BCC) $**
1134
1135 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
1136 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
1137
1138 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
1139 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1140
1141 $(OX)\th$O : $(SRCDIR)\th.c
1142 $(TCC) /Fo$@ -c $**
1143
1144
+27 -11
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
14941494
#endif /* LOCAL_INTERFACE */
14951495
14961496
/*
14971497
** Finish up a sequence of manifest_crosslink calls.
14981498
*/
1499
-void manifest_crosslink_end(void){
1499
+int manifest_crosslink_end(int flags){
15001500
Stmt q, u;
15011501
int i;
1502
+ int rc = TH_OK;
1503
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
1504
+ const char *zScript = 0;
15021505
assert( manifest_crosslink_busy==1 );
1506
+ if( permitHooks ){
1507
+ rc = xfer_run_common_script();
1508
+ if( rc==TH_OK ){
1509
+ zScript = xfer_ticket_code();
1510
+ }
1511
+ }
15031512
db_prepare(&q, "SELECT uuid FROM pending_tkt");
15041513
while( db_step(&q)==SQLITE_ROW ){
15051514
const char *zUuid = db_column_text(&q, 0);
15061515
ticket_rebuild_entry(zUuid);
1516
+ if( permitHooks && rc==TH_OK ){
1517
+ rc = xfer_run_script(zScript, zUuid);
1518
+ }
15071519
}
15081520
db_finalize(&q);
15091521
db_multi_exec("DROP TABLE pending_tkt");
15101522
15111523
/* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
15361548
"DROP TABLE time_fudge;"
15371549
);
15381550
15391551
db_end_transaction(0);
15401552
manifest_crosslink_busy = 0;
1553
+ return ( rc!=TH_ERROR );
15411554
}
15421555
15431556
/*
15441557
** Make an entry in the event table for a ticket change artifact.
15451558
*/
@@ -1657,14 +1670,15 @@
16571670
** Processing for other control artifacts was added later. The name
16581671
** of the routine, "manifest_crosslink", and the name of this source
16591672
** file, is a legacy of its original use.
16601673
*/
16611674
int manifest_crosslink(int rid, Blob *pContent, int flags){
1662
- int i, result = TH_OK;
1675
+ int i, rc = TH_OK;
16631676
Manifest *p;
16641677
Stmt q;
16651678
int parentid = 0;
1679
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
16661680
const char *zScript = 0;
16671681
const char *zUuid = 0;
16681682
16691683
if( (p = manifest_cache_find(rid))!=0 ){
16701684
blob_reset(pContent);
@@ -1685,11 +1699,13 @@
16851699
fossil_error(1, "cannot fetch baseline manifest");
16861700
return 0;
16871701
}
16881702
db_begin_transaction();
16891703
if( p->type==CFTYPE_MANIFEST ){
1690
- zScript = xfer_commit_code();
1704
+ if( permitHooks ){
1705
+ zScript = xfer_commit_code();
1706
+ }
16911707
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
16921708
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
16931709
char *zCom;
16941710
for(i=0; i<p->nParent; i++){
16951711
int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
18831899
}
18841900
}
18851901
if( p->type==CFTYPE_TICKET ){
18861902
char *zTag;
18871903
1888
- zScript = xfer_ticket_code();
1889
- zUuid = p->zTicketUuid;
18901904
assert( manifest_crosslink_busy==1 );
18911905
zTag = mprintf("tkt-%s", p->zTicketUuid);
18921906
tag_insert(zTag, 1, 0, rid, p->rDate, rid);
18931907
free(zTag);
18941908
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
19681982
zTagUuid);
19691983
branchMove = 0;
19701984
if( db_exists("SELECT 1 FROM event, blob"
19711985
" WHERE event.type='ci' AND event.objid=blob.rid"
19721986
" AND blob.uuid='%s'", zTagUuid) ){
1973
- zScript = xfer_commit_code();
1987
+ if( permitHooks ){
1988
+ zScript = xfer_commit_code();
1989
+ }
19741990
zUuid = zTagUuid;
19751991
}
19761992
}
19771993
zName = p->aTag[i].zName;
19781994
zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
20422058
p->rDate, rid, p->zUser, blob_str(&comment)+1
20432059
);
20442060
blob_reset(&comment);
20452061
}
20462062
db_end_transaction(0);
2047
- if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048
- result = xfer_run_common_script();
2049
- if( result==TH_OK ){
2050
- result = xfer_run_script(zScript, zUuid);
2063
+ if( permitHooks ){
2064
+ rc = xfer_run_common_script();
2065
+ if( rc==TH_OK ){
2066
+ rc = xfer_run_script(zScript, zUuid);
20512067
}
20522068
}
20532069
if( p->type==CFTYPE_MANIFEST ){
20542070
manifest_cache_insert(p);
20552071
}else{
20562072
manifest_destroy(p);
20572073
}
20582074
assert( blob_is_reset(pContent) );
2059
- return ( result!=TH_ERROR );
2075
+ return ( rc!=TH_ERROR );
20602076
}
20612077
20622078
/*
20632079
** COMMAND: test-crosslink
20642080
**
20652081
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 void manifest_crosslink_end(void){
1500 Stmt q, u;
1501 int i;
 
 
 
1502 assert( manifest_crosslink_busy==1 );
 
 
 
 
 
 
1503 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1504 while( db_step(&q)==SQLITE_ROW ){
1505 const char *zUuid = db_column_text(&q, 0);
1506 ticket_rebuild_entry(zUuid);
 
 
 
1507 }
1508 db_finalize(&q);
1509 db_multi_exec("DROP TABLE pending_tkt");
1510
1511 /* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
1536 "DROP TABLE time_fudge;"
1537 );
1538
1539 db_end_transaction(0);
1540 manifest_crosslink_busy = 0;
 
1541 }
1542
1543 /*
1544 ** Make an entry in the event table for a ticket change artifact.
1545 */
@@ -1657,14 +1670,15 @@
1657 ** Processing for other control artifacts was added later. The name
1658 ** of the routine, "manifest_crosslink", and the name of this source
1659 ** file, is a legacy of its original use.
1660 */
1661 int manifest_crosslink(int rid, Blob *pContent, int flags){
1662 int i, result = TH_OK;
1663 Manifest *p;
1664 Stmt q;
1665 int parentid = 0;
 
1666 const char *zScript = 0;
1667 const char *zUuid = 0;
1668
1669 if( (p = manifest_cache_find(rid))!=0 ){
1670 blob_reset(pContent);
@@ -1685,11 +1699,13 @@
1685 fossil_error(1, "cannot fetch baseline manifest");
1686 return 0;
1687 }
1688 db_begin_transaction();
1689 if( p->type==CFTYPE_MANIFEST ){
1690 zScript = xfer_commit_code();
 
 
1691 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1692 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1693 char *zCom;
1694 for(i=0; i<p->nParent; i++){
1695 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
1883 }
1884 }
1885 if( p->type==CFTYPE_TICKET ){
1886 char *zTag;
1887
1888 zScript = xfer_ticket_code();
1889 zUuid = p->zTicketUuid;
1890 assert( manifest_crosslink_busy==1 );
1891 zTag = mprintf("tkt-%s", p->zTicketUuid);
1892 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1893 free(zTag);
1894 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
1968 zTagUuid);
1969 branchMove = 0;
1970 if( db_exists("SELECT 1 FROM event, blob"
1971 " WHERE event.type='ci' AND event.objid=blob.rid"
1972 " AND blob.uuid='%s'", zTagUuid) ){
1973 zScript = xfer_commit_code();
 
 
1974 zUuid = zTagUuid;
1975 }
1976 }
1977 zName = p->aTag[i].zName;
1978 zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
2042 p->rDate, rid, p->zUser, blob_str(&comment)+1
2043 );
2044 blob_reset(&comment);
2045 }
2046 db_end_transaction(0);
2047 if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048 result = xfer_run_common_script();
2049 if( result==TH_OK ){
2050 result = xfer_run_script(zScript, zUuid);
2051 }
2052 }
2053 if( p->type==CFTYPE_MANIFEST ){
2054 manifest_cache_insert(p);
2055 }else{
2056 manifest_destroy(p);
2057 }
2058 assert( blob_is_reset(pContent) );
2059 return ( result!=TH_ERROR );
2060 }
2061
2062 /*
2063 ** COMMAND: test-crosslink
2064 **
2065
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 int manifest_crosslink_end(int flags){
1500 Stmt q, u;
1501 int i;
1502 int rc = TH_OK;
1503 int permitHooks = (flags & MC_PERMIT_HOOKS);
1504 const char *zScript = 0;
1505 assert( manifest_crosslink_busy==1 );
1506 if( permitHooks ){
1507 rc = xfer_run_common_script();
1508 if( rc==TH_OK ){
1509 zScript = xfer_ticket_code();
1510 }
1511 }
1512 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1513 while( db_step(&q)==SQLITE_ROW ){
1514 const char *zUuid = db_column_text(&q, 0);
1515 ticket_rebuild_entry(zUuid);
1516 if( permitHooks && rc==TH_OK ){
1517 rc = xfer_run_script(zScript, zUuid);
1518 }
1519 }
1520 db_finalize(&q);
1521 db_multi_exec("DROP TABLE pending_tkt");
1522
1523 /* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
1548 "DROP TABLE time_fudge;"
1549 );
1550
1551 db_end_transaction(0);
1552 manifest_crosslink_busy = 0;
1553 return ( rc!=TH_ERROR );
1554 }
1555
1556 /*
1557 ** Make an entry in the event table for a ticket change artifact.
1558 */
@@ -1657,14 +1670,15 @@
1670 ** Processing for other control artifacts was added later. The name
1671 ** of the routine, "manifest_crosslink", and the name of this source
1672 ** file, is a legacy of its original use.
1673 */
1674 int manifest_crosslink(int rid, Blob *pContent, int flags){
1675 int i, rc = TH_OK;
1676 Manifest *p;
1677 Stmt q;
1678 int parentid = 0;
1679 int permitHooks = (flags & MC_PERMIT_HOOKS);
1680 const char *zScript = 0;
1681 const char *zUuid = 0;
1682
1683 if( (p = manifest_cache_find(rid))!=0 ){
1684 blob_reset(pContent);
@@ -1685,11 +1699,13 @@
1699 fossil_error(1, "cannot fetch baseline manifest");
1700 return 0;
1701 }
1702 db_begin_transaction();
1703 if( p->type==CFTYPE_MANIFEST ){
1704 if( permitHooks ){
1705 zScript = xfer_commit_code();
1706 }
1707 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1708 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1709 char *zCom;
1710 for(i=0; i<p->nParent; i++){
1711 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
1899 }
1900 }
1901 if( p->type==CFTYPE_TICKET ){
1902 char *zTag;
1903
 
 
1904 assert( manifest_crosslink_busy==1 );
1905 zTag = mprintf("tkt-%s", p->zTicketUuid);
1906 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1907 free(zTag);
1908 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
1982 zTagUuid);
1983 branchMove = 0;
1984 if( db_exists("SELECT 1 FROM event, blob"
1985 " WHERE event.type='ci' AND event.objid=blob.rid"
1986 " AND blob.uuid='%s'", zTagUuid) ){
1987 if( permitHooks ){
1988 zScript = xfer_commit_code();
1989 }
1990 zUuid = zTagUuid;
1991 }
1992 }
1993 zName = p->aTag[i].zName;
1994 zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
2058 p->rDate, rid, p->zUser, blob_str(&comment)+1
2059 );
2060 blob_reset(&comment);
2061 }
2062 db_end_transaction(0);
2063 if( permitHooks ){
2064 rc = xfer_run_common_script();
2065 if( rc==TH_OK ){
2066 rc = xfer_run_script(zScript, zUuid);
2067 }
2068 }
2069 if( p->type==CFTYPE_MANIFEST ){
2070 manifest_cache_insert(p);
2071 }else{
2072 manifest_destroy(p);
2073 }
2074 assert( blob_is_reset(pContent) );
2075 return ( rc!=TH_ERROR );
2076 }
2077
2078 /*
2079 ** COMMAND: test-crosslink
2080 **
2081
+27 -11
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
14941494
#endif /* LOCAL_INTERFACE */
14951495
14961496
/*
14971497
** Finish up a sequence of manifest_crosslink calls.
14981498
*/
1499
-void manifest_crosslink_end(void){
1499
+int manifest_crosslink_end(int flags){
15001500
Stmt q, u;
15011501
int i;
1502
+ int rc = TH_OK;
1503
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
1504
+ const char *zScript = 0;
15021505
assert( manifest_crosslink_busy==1 );
1506
+ if( permitHooks ){
1507
+ rc = xfer_run_common_script();
1508
+ if( rc==TH_OK ){
1509
+ zScript = xfer_ticket_code();
1510
+ }
1511
+ }
15031512
db_prepare(&q, "SELECT uuid FROM pending_tkt");
15041513
while( db_step(&q)==SQLITE_ROW ){
15051514
const char *zUuid = db_column_text(&q, 0);
15061515
ticket_rebuild_entry(zUuid);
1516
+ if( permitHooks && rc==TH_OK ){
1517
+ rc = xfer_run_script(zScript, zUuid);
1518
+ }
15071519
}
15081520
db_finalize(&q);
15091521
db_multi_exec("DROP TABLE pending_tkt");
15101522
15111523
/* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
15361548
"DROP TABLE time_fudge;"
15371549
);
15381550
15391551
db_end_transaction(0);
15401552
manifest_crosslink_busy = 0;
1553
+ return ( rc!=TH_ERROR );
15411554
}
15421555
15431556
/*
15441557
** Make an entry in the event table for a ticket change artifact.
15451558
*/
@@ -1657,14 +1670,15 @@
16571670
** Processing for other control artifacts was added later. The name
16581671
** of the routine, "manifest_crosslink", and the name of this source
16591672
** file, is a legacy of its original use.
16601673
*/
16611674
int manifest_crosslink(int rid, Blob *pContent, int flags){
1662
- int i, result = TH_OK;
1675
+ int i, rc = TH_OK;
16631676
Manifest *p;
16641677
Stmt q;
16651678
int parentid = 0;
1679
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
16661680
const char *zScript = 0;
16671681
const char *zUuid = 0;
16681682
16691683
if( (p = manifest_cache_find(rid))!=0 ){
16701684
blob_reset(pContent);
@@ -1685,11 +1699,13 @@
16851699
fossil_error(1, "cannot fetch baseline manifest");
16861700
return 0;
16871701
}
16881702
db_begin_transaction();
16891703
if( p->type==CFTYPE_MANIFEST ){
1690
- zScript = xfer_commit_code();
1704
+ if( permitHooks ){
1705
+ zScript = xfer_commit_code();
1706
+ }
16911707
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
16921708
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
16931709
char *zCom;
16941710
for(i=0; i<p->nParent; i++){
16951711
int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
18831899
}
18841900
}
18851901
if( p->type==CFTYPE_TICKET ){
18861902
char *zTag;
18871903
1888
- zScript = xfer_ticket_code();
1889
- zUuid = p->zTicketUuid;
18901904
assert( manifest_crosslink_busy==1 );
18911905
zTag = mprintf("tkt-%s", p->zTicketUuid);
18921906
tag_insert(zTag, 1, 0, rid, p->rDate, rid);
18931907
free(zTag);
18941908
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
19681982
zTagUuid);
19691983
branchMove = 0;
19701984
if( db_exists("SELECT 1 FROM event, blob"
19711985
" WHERE event.type='ci' AND event.objid=blob.rid"
19721986
" AND blob.uuid='%s'", zTagUuid) ){
1973
- zScript = xfer_commit_code();
1987
+ if( permitHooks ){
1988
+ zScript = xfer_commit_code();
1989
+ }
19741990
zUuid = zTagUuid;
19751991
}
19761992
}
19771993
zName = p->aTag[i].zName;
19781994
zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
20422058
p->rDate, rid, p->zUser, blob_str(&comment)+1
20432059
);
20442060
blob_reset(&comment);
20452061
}
20462062
db_end_transaction(0);
2047
- if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048
- result = xfer_run_common_script();
2049
- if( result==TH_OK ){
2050
- result = xfer_run_script(zScript, zUuid);
2063
+ if( permitHooks ){
2064
+ rc = xfer_run_common_script();
2065
+ if( rc==TH_OK ){
2066
+ rc = xfer_run_script(zScript, zUuid);
20512067
}
20522068
}
20532069
if( p->type==CFTYPE_MANIFEST ){
20542070
manifest_cache_insert(p);
20552071
}else{
20562072
manifest_destroy(p);
20572073
}
20582074
assert( blob_is_reset(pContent) );
2059
- return ( result!=TH_ERROR );
2075
+ return ( rc!=TH_ERROR );
20602076
}
20612077
20622078
/*
20632079
** COMMAND: test-crosslink
20642080
**
20652081
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 void manifest_crosslink_end(void){
1500 Stmt q, u;
1501 int i;
 
 
 
1502 assert( manifest_crosslink_busy==1 );
 
 
 
 
 
 
1503 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1504 while( db_step(&q)==SQLITE_ROW ){
1505 const char *zUuid = db_column_text(&q, 0);
1506 ticket_rebuild_entry(zUuid);
 
 
 
1507 }
1508 db_finalize(&q);
1509 db_multi_exec("DROP TABLE pending_tkt");
1510
1511 /* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
1536 "DROP TABLE time_fudge;"
1537 );
1538
1539 db_end_transaction(0);
1540 manifest_crosslink_busy = 0;
 
1541 }
1542
1543 /*
1544 ** Make an entry in the event table for a ticket change artifact.
1545 */
@@ -1657,14 +1670,15 @@
1657 ** Processing for other control artifacts was added later. The name
1658 ** of the routine, "manifest_crosslink", and the name of this source
1659 ** file, is a legacy of its original use.
1660 */
1661 int manifest_crosslink(int rid, Blob *pContent, int flags){
1662 int i, result = TH_OK;
1663 Manifest *p;
1664 Stmt q;
1665 int parentid = 0;
 
1666 const char *zScript = 0;
1667 const char *zUuid = 0;
1668
1669 if( (p = manifest_cache_find(rid))!=0 ){
1670 blob_reset(pContent);
@@ -1685,11 +1699,13 @@
1685 fossil_error(1, "cannot fetch baseline manifest");
1686 return 0;
1687 }
1688 db_begin_transaction();
1689 if( p->type==CFTYPE_MANIFEST ){
1690 zScript = xfer_commit_code();
 
 
1691 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1692 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1693 char *zCom;
1694 for(i=0; i<p->nParent; i++){
1695 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
1883 }
1884 }
1885 if( p->type==CFTYPE_TICKET ){
1886 char *zTag;
1887
1888 zScript = xfer_ticket_code();
1889 zUuid = p->zTicketUuid;
1890 assert( manifest_crosslink_busy==1 );
1891 zTag = mprintf("tkt-%s", p->zTicketUuid);
1892 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1893 free(zTag);
1894 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
1968 zTagUuid);
1969 branchMove = 0;
1970 if( db_exists("SELECT 1 FROM event, blob"
1971 " WHERE event.type='ci' AND event.objid=blob.rid"
1972 " AND blob.uuid='%s'", zTagUuid) ){
1973 zScript = xfer_commit_code();
 
 
1974 zUuid = zTagUuid;
1975 }
1976 }
1977 zName = p->aTag[i].zName;
1978 zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
2042 p->rDate, rid, p->zUser, blob_str(&comment)+1
2043 );
2044 blob_reset(&comment);
2045 }
2046 db_end_transaction(0);
2047 if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048 result = xfer_run_common_script();
2049 if( result==TH_OK ){
2050 result = xfer_run_script(zScript, zUuid);
2051 }
2052 }
2053 if( p->type==CFTYPE_MANIFEST ){
2054 manifest_cache_insert(p);
2055 }else{
2056 manifest_destroy(p);
2057 }
2058 assert( blob_is_reset(pContent) );
2059 return ( result!=TH_ERROR );
2060 }
2061
2062 /*
2063 ** COMMAND: test-crosslink
2064 **
2065
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 int manifest_crosslink_end(int flags){
1500 Stmt q, u;
1501 int i;
1502 int rc = TH_OK;
1503 int permitHooks = (flags & MC_PERMIT_HOOKS);
1504 const char *zScript = 0;
1505 assert( manifest_crosslink_busy==1 );
1506 if( permitHooks ){
1507 rc = xfer_run_common_script();
1508 if( rc==TH_OK ){
1509 zScript = xfer_ticket_code();
1510 }
1511 }
1512 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1513 while( db_step(&q)==SQLITE_ROW ){
1514 const char *zUuid = db_column_text(&q, 0);
1515 ticket_rebuild_entry(zUuid);
1516 if( permitHooks && rc==TH_OK ){
1517 rc = xfer_run_script(zScript, zUuid);
1518 }
1519 }
1520 db_finalize(&q);
1521 db_multi_exec("DROP TABLE pending_tkt");
1522
1523 /* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
1548 "DROP TABLE time_fudge;"
1549 );
1550
1551 db_end_transaction(0);
1552 manifest_crosslink_busy = 0;
1553 return ( rc!=TH_ERROR );
1554 }
1555
1556 /*
1557 ** Make an entry in the event table for a ticket change artifact.
1558 */
@@ -1657,14 +1670,15 @@
1670 ** Processing for other control artifacts was added later. The name
1671 ** of the routine, "manifest_crosslink", and the name of this source
1672 ** file, is a legacy of its original use.
1673 */
1674 int manifest_crosslink(int rid, Blob *pContent, int flags){
1675 int i, rc = TH_OK;
1676 Manifest *p;
1677 Stmt q;
1678 int parentid = 0;
1679 int permitHooks = (flags & MC_PERMIT_HOOKS);
1680 const char *zScript = 0;
1681 const char *zUuid = 0;
1682
1683 if( (p = manifest_cache_find(rid))!=0 ){
1684 blob_reset(pContent);
@@ -1685,11 +1699,13 @@
1699 fossil_error(1, "cannot fetch baseline manifest");
1700 return 0;
1701 }
1702 db_begin_transaction();
1703 if( p->type==CFTYPE_MANIFEST ){
1704 if( permitHooks ){
1705 zScript = xfer_commit_code();
1706 }
1707 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1708 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1709 char *zCom;
1710 for(i=0; i<p->nParent; i++){
1711 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
1899 }
1900 }
1901 if( p->type==CFTYPE_TICKET ){
1902 char *zTag;
1903
 
 
1904 assert( manifest_crosslink_busy==1 );
1905 zTag = mprintf("tkt-%s", p->zTicketUuid);
1906 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1907 free(zTag);
1908 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
1982 zTagUuid);
1983 branchMove = 0;
1984 if( db_exists("SELECT 1 FROM event, blob"
1985 " WHERE event.type='ci' AND event.objid=blob.rid"
1986 " AND blob.uuid='%s'", zTagUuid) ){
1987 if( permitHooks ){
1988 zScript = xfer_commit_code();
1989 }
1990 zUuid = zTagUuid;
1991 }
1992 }
1993 zName = p->aTag[i].zName;
1994 zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
2058 p->rDate, rid, p->zUser, blob_str(&comment)+1
2059 );
2060 blob_reset(&comment);
2061 }
2062 db_end_transaction(0);
2063 if( permitHooks ){
2064 rc = xfer_run_common_script();
2065 if( rc==TH_OK ){
2066 rc = xfer_run_script(zScript, zUuid);
2067 }
2068 }
2069 if( p->type==CFTYPE_MANIFEST ){
2070 manifest_cache_insert(p);
2071 }else{
2072 manifest_destroy(p);
2073 }
2074 assert( blob_is_reset(pContent) );
2075 return ( rc!=TH_ERROR );
2076 }
2077
2078 /*
2079 ** COMMAND: test-crosslink
2080 **
2081
+27 -11
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
14941494
#endif /* LOCAL_INTERFACE */
14951495
14961496
/*
14971497
** Finish up a sequence of manifest_crosslink calls.
14981498
*/
1499
-void manifest_crosslink_end(void){
1499
+int manifest_crosslink_end(int flags){
15001500
Stmt q, u;
15011501
int i;
1502
+ int rc = TH_OK;
1503
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
1504
+ const char *zScript = 0;
15021505
assert( manifest_crosslink_busy==1 );
1506
+ if( permitHooks ){
1507
+ rc = xfer_run_common_script();
1508
+ if( rc==TH_OK ){
1509
+ zScript = xfer_ticket_code();
1510
+ }
1511
+ }
15031512
db_prepare(&q, "SELECT uuid FROM pending_tkt");
15041513
while( db_step(&q)==SQLITE_ROW ){
15051514
const char *zUuid = db_column_text(&q, 0);
15061515
ticket_rebuild_entry(zUuid);
1516
+ if( permitHooks && rc==TH_OK ){
1517
+ rc = xfer_run_script(zScript, zUuid);
1518
+ }
15071519
}
15081520
db_finalize(&q);
15091521
db_multi_exec("DROP TABLE pending_tkt");
15101522
15111523
/* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
15361548
"DROP TABLE time_fudge;"
15371549
);
15381550
15391551
db_end_transaction(0);
15401552
manifest_crosslink_busy = 0;
1553
+ return ( rc!=TH_ERROR );
15411554
}
15421555
15431556
/*
15441557
** Make an entry in the event table for a ticket change artifact.
15451558
*/
@@ -1657,14 +1670,15 @@
16571670
** Processing for other control artifacts was added later. The name
16581671
** of the routine, "manifest_crosslink", and the name of this source
16591672
** file, is a legacy of its original use.
16601673
*/
16611674
int manifest_crosslink(int rid, Blob *pContent, int flags){
1662
- int i, result = TH_OK;
1675
+ int i, rc = TH_OK;
16631676
Manifest *p;
16641677
Stmt q;
16651678
int parentid = 0;
1679
+ int permitHooks = (flags & MC_PERMIT_HOOKS);
16661680
const char *zScript = 0;
16671681
const char *zUuid = 0;
16681682
16691683
if( (p = manifest_cache_find(rid))!=0 ){
16701684
blob_reset(pContent);
@@ -1685,11 +1699,13 @@
16851699
fossil_error(1, "cannot fetch baseline manifest");
16861700
return 0;
16871701
}
16881702
db_begin_transaction();
16891703
if( p->type==CFTYPE_MANIFEST ){
1690
- zScript = xfer_commit_code();
1704
+ if( permitHooks ){
1705
+ zScript = xfer_commit_code();
1706
+ }
16911707
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
16921708
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
16931709
char *zCom;
16941710
for(i=0; i<p->nParent; i++){
16951711
int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
18831899
}
18841900
}
18851901
if( p->type==CFTYPE_TICKET ){
18861902
char *zTag;
18871903
1888
- zScript = xfer_ticket_code();
1889
- zUuid = p->zTicketUuid;
18901904
assert( manifest_crosslink_busy==1 );
18911905
zTag = mprintf("tkt-%s", p->zTicketUuid);
18921906
tag_insert(zTag, 1, 0, rid, p->rDate, rid);
18931907
free(zTag);
18941908
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
19681982
zTagUuid);
19691983
branchMove = 0;
19701984
if( db_exists("SELECT 1 FROM event, blob"
19711985
" WHERE event.type='ci' AND event.objid=blob.rid"
19721986
" AND blob.uuid='%s'", zTagUuid) ){
1973
- zScript = xfer_commit_code();
1987
+ if( permitHooks ){
1988
+ zScript = xfer_commit_code();
1989
+ }
19741990
zUuid = zTagUuid;
19751991
}
19761992
}
19771993
zName = p->aTag[i].zName;
19781994
zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
20422058
p->rDate, rid, p->zUser, blob_str(&comment)+1
20432059
);
20442060
blob_reset(&comment);
20452061
}
20462062
db_end_transaction(0);
2047
- if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048
- result = xfer_run_common_script();
2049
- if( result==TH_OK ){
2050
- result = xfer_run_script(zScript, zUuid);
2063
+ if( permitHooks ){
2064
+ rc = xfer_run_common_script();
2065
+ if( rc==TH_OK ){
2066
+ rc = xfer_run_script(zScript, zUuid);
20512067
}
20522068
}
20532069
if( p->type==CFTYPE_MANIFEST ){
20542070
manifest_cache_insert(p);
20552071
}else{
20562072
manifest_destroy(p);
20572073
}
20582074
assert( blob_is_reset(pContent) );
2059
- return ( result!=TH_ERROR );
2075
+ return ( rc!=TH_ERROR );
20602076
}
20612077
20622078
/*
20632079
** COMMAND: test-crosslink
20642080
**
20652081
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 void manifest_crosslink_end(void){
1500 Stmt q, u;
1501 int i;
 
 
 
1502 assert( manifest_crosslink_busy==1 );
 
 
 
 
 
 
1503 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1504 while( db_step(&q)==SQLITE_ROW ){
1505 const char *zUuid = db_column_text(&q, 0);
1506 ticket_rebuild_entry(zUuid);
 
 
 
1507 }
1508 db_finalize(&q);
1509 db_multi_exec("DROP TABLE pending_tkt");
1510
1511 /* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
1536 "DROP TABLE time_fudge;"
1537 );
1538
1539 db_end_transaction(0);
1540 manifest_crosslink_busy = 0;
 
1541 }
1542
1543 /*
1544 ** Make an entry in the event table for a ticket change artifact.
1545 */
@@ -1657,14 +1670,15 @@
1657 ** Processing for other control artifacts was added later. The name
1658 ** of the routine, "manifest_crosslink", and the name of this source
1659 ** file, is a legacy of its original use.
1660 */
1661 int manifest_crosslink(int rid, Blob *pContent, int flags){
1662 int i, result = TH_OK;
1663 Manifest *p;
1664 Stmt q;
1665 int parentid = 0;
 
1666 const char *zScript = 0;
1667 const char *zUuid = 0;
1668
1669 if( (p = manifest_cache_find(rid))!=0 ){
1670 blob_reset(pContent);
@@ -1685,11 +1699,13 @@
1685 fossil_error(1, "cannot fetch baseline manifest");
1686 return 0;
1687 }
1688 db_begin_transaction();
1689 if( p->type==CFTYPE_MANIFEST ){
1690 zScript = xfer_commit_code();
 
 
1691 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1692 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1693 char *zCom;
1694 for(i=0; i<p->nParent; i++){
1695 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
1883 }
1884 }
1885 if( p->type==CFTYPE_TICKET ){
1886 char *zTag;
1887
1888 zScript = xfer_ticket_code();
1889 zUuid = p->zTicketUuid;
1890 assert( manifest_crosslink_busy==1 );
1891 zTag = mprintf("tkt-%s", p->zTicketUuid);
1892 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1893 free(zTag);
1894 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
1968 zTagUuid);
1969 branchMove = 0;
1970 if( db_exists("SELECT 1 FROM event, blob"
1971 " WHERE event.type='ci' AND event.objid=blob.rid"
1972 " AND blob.uuid='%s'", zTagUuid) ){
1973 zScript = xfer_commit_code();
 
 
1974 zUuid = zTagUuid;
1975 }
1976 }
1977 zName = p->aTag[i].zName;
1978 zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
2042 p->rDate, rid, p->zUser, blob_str(&comment)+1
2043 );
2044 blob_reset(&comment);
2045 }
2046 db_end_transaction(0);
2047 if( zScript && (flags & MC_PERMIT_HOOKS) ){
2048 result = xfer_run_common_script();
2049 if( result==TH_OK ){
2050 result = xfer_run_script(zScript, zUuid);
2051 }
2052 }
2053 if( p->type==CFTYPE_MANIFEST ){
2054 manifest_cache_insert(p);
2055 }else{
2056 manifest_destroy(p);
2057 }
2058 assert( blob_is_reset(pContent) );
2059 return ( result!=TH_ERROR );
2060 }
2061
2062 /*
2063 ** COMMAND: test-crosslink
2064 **
2065
--- src/manifest.c
+++ src/manifest.c
@@ -1494,18 +1494,30 @@
1494 #endif /* LOCAL_INTERFACE */
1495
1496 /*
1497 ** Finish up a sequence of manifest_crosslink calls.
1498 */
1499 int manifest_crosslink_end(int flags){
1500 Stmt q, u;
1501 int i;
1502 int rc = TH_OK;
1503 int permitHooks = (flags & MC_PERMIT_HOOKS);
1504 const char *zScript = 0;
1505 assert( manifest_crosslink_busy==1 );
1506 if( permitHooks ){
1507 rc = xfer_run_common_script();
1508 if( rc==TH_OK ){
1509 zScript = xfer_ticket_code();
1510 }
1511 }
1512 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1513 while( db_step(&q)==SQLITE_ROW ){
1514 const char *zUuid = db_column_text(&q, 0);
1515 ticket_rebuild_entry(zUuid);
1516 if( permitHooks && rc==TH_OK ){
1517 rc = xfer_run_script(zScript, zUuid);
1518 }
1519 }
1520 db_finalize(&q);
1521 db_multi_exec("DROP TABLE pending_tkt");
1522
1523 /* If multiple check-ins happen close together in time, adjust their
@@ -1536,10 +1548,11 @@
1548 "DROP TABLE time_fudge;"
1549 );
1550
1551 db_end_transaction(0);
1552 manifest_crosslink_busy = 0;
1553 return ( rc!=TH_ERROR );
1554 }
1555
1556 /*
1557 ** Make an entry in the event table for a ticket change artifact.
1558 */
@@ -1657,14 +1670,15 @@
1670 ** Processing for other control artifacts was added later. The name
1671 ** of the routine, "manifest_crosslink", and the name of this source
1672 ** file, is a legacy of its original use.
1673 */
1674 int manifest_crosslink(int rid, Blob *pContent, int flags){
1675 int i, rc = TH_OK;
1676 Manifest *p;
1677 Stmt q;
1678 int parentid = 0;
1679 int permitHooks = (flags & MC_PERMIT_HOOKS);
1680 const char *zScript = 0;
1681 const char *zUuid = 0;
1682
1683 if( (p = manifest_cache_find(rid))!=0 ){
1684 blob_reset(pContent);
@@ -1685,11 +1699,13 @@
1699 fossil_error(1, "cannot fetch baseline manifest");
1700 return 0;
1701 }
1702 db_begin_transaction();
1703 if( p->type==CFTYPE_MANIFEST ){
1704 if( permitHooks ){
1705 zScript = xfer_commit_code();
1706 }
1707 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
1708 if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
1709 char *zCom;
1710 for(i=0; i<p->nParent; i++){
1711 int pid = uuid_to_rid(p->azParent[i], 1);
@@ -1883,12 +1899,10 @@
1899 }
1900 }
1901 if( p->type==CFTYPE_TICKET ){
1902 char *zTag;
1903
 
 
1904 assert( manifest_crosslink_busy==1 );
1905 zTag = mprintf("tkt-%s", p->zTicketUuid);
1906 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
1907 free(zTag);
1908 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
@@ -1968,11 +1982,13 @@
1982 zTagUuid);
1983 branchMove = 0;
1984 if( db_exists("SELECT 1 FROM event, blob"
1985 " WHERE event.type='ci' AND event.objid=blob.rid"
1986 " AND blob.uuid='%s'", zTagUuid) ){
1987 if( permitHooks ){
1988 zScript = xfer_commit_code();
1989 }
1990 zUuid = zTagUuid;
1991 }
1992 }
1993 zName = p->aTag[i].zName;
1994 zValue = p->aTag[i].zValue;
@@ -2042,23 +2058,23 @@
2058 p->rDate, rid, p->zUser, blob_str(&comment)+1
2059 );
2060 blob_reset(&comment);
2061 }
2062 db_end_transaction(0);
2063 if( permitHooks ){
2064 rc = xfer_run_common_script();
2065 if( rc==TH_OK ){
2066 rc = xfer_run_script(zScript, zUuid);
2067 }
2068 }
2069 if( p->type==CFTYPE_MANIFEST ){
2070 manifest_cache_insert(p);
2071 }else{
2072 manifest_destroy(p);
2073 }
2074 assert( blob_is_reset(pContent) );
2075 return ( rc!=TH_ERROR );
2076 }
2077
2078 /*
2079 ** COMMAND: test-crosslink
2080 **
2081
+1 -1
--- src/rebuild.c
+++ src/rebuild.c
@@ -412,11 +412,11 @@
412412
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
413413
rebuild_step_done(rid);
414414
}
415415
}
416416
db_finalize(&s);
417
- manifest_crosslink_end();
417
+ manifest_crosslink_end(MC_NONE);
418418
rebuild_tag_trunk();
419419
if( ttyOutput && !g.fQuiet && totalSize>0 ){
420420
processCnt += incrSize;
421421
percent_complete((processCnt*1000)/totalSize);
422422
}
423423
--- src/rebuild.c
+++ src/rebuild.c
@@ -412,11 +412,11 @@
412 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
413 rebuild_step_done(rid);
414 }
415 }
416 db_finalize(&s);
417 manifest_crosslink_end();
418 rebuild_tag_trunk();
419 if( ttyOutput && !g.fQuiet && totalSize>0 ){
420 processCnt += incrSize;
421 percent_complete((processCnt*1000)/totalSize);
422 }
423
--- src/rebuild.c
+++ src/rebuild.c
@@ -412,11 +412,11 @@
412 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
413 rebuild_step_done(rid);
414 }
415 }
416 db_finalize(&s);
417 manifest_crosslink_end(MC_NONE);
418 rebuild_tag_trunk();
419 if( ttyOutput && !g.fQuiet && totalSize>0 ){
420 processCnt += incrSize;
421 percent_complete((processCnt*1000)/totalSize);
422 }
423
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */
1065 const char *zElem; /* Name of array element, if applicable */
1066 int nElem; /* Length of array element name, if applicable */
1067 };
1068 typedef struct Find Find;
1069
1070 /*
1071 ** Input string (zVar, nVar) contains a variable name. This function locates
1072 ** the Th_Variable structure associated with the named variable. The
1073 ** variable name may be a global or local scalar or array variable
1074 **
@@ -1055,17 +1078,19 @@
1078 **
1079 ** If the arrayok argument is false and the named variable is an array,
1080 ** an error is left in the interpreter result and NULL returned. If
1081 ** arrayok is true an array name is Ok.
1082 */
1083
1084 static Th_Variable *thFindValue(
1085 Th_Interp *interp,
1086 const char *zVar, /* Pointer to variable name */
1087 int nVar, /* Number of bytes at nVar */
1088 int create, /* If true, create the variable if not found */
1089 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090 int noerror, /* If false, set interpreter result to error */
1091 Find *pFind /* If non-zero, place output here */
1092 ){
1093 const char *zOuter;
1094 int nOuter;
1095 const char *zInner;
1096 int nInner;
@@ -1074,16 +1099,24 @@
1099 Th_HashEntry *pEntry;
1100 Th_Frame *pFrame = interp->pFrame;
1101 Th_Variable *pValue;
1102
1103 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104 if( pFind ){
1105 memset(pFind, 0, sizeof(Find));
1106 pFind->zElem = zInner;
1107 pFind->nElem = nInner;
1108 }
1109 if( isGlobal ){
1110 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1111 }
1112
1113 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1114 assert(pEntry || create<=0);
1115 if( pFind ){
1116 pFind->pValueEntry = pEntry;
1117 }
1118 if( !pEntry ){
1119 goto no_such_var;
1120 }
1121
1122 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1139 goto no_such_var;
1140 }
1141 pValue->pHash = Th_HashNew(interp);
1142 }
1143 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144 assert(pEntry || create<=0);
1145 if( pFind ){
1146 pFind->pElemEntry = pEntry;
1147 }
1148 if( !pEntry ){
1149 goto no_such_var;
1150 }
1151 pValue = (Th_Variable *)pEntry->pData;
1152 if( !pValue ){
@@ -1145,11 +1182,11 @@
1182 ** an error message in the interpreter result.
1183 */
1184 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191 if( !pValue->zData ){
1192 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1198
1199 /*
1200 ** Return true if variable (zVar, nVar) exists.
1201 */
1202 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1203 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204 return pValue && (pValue->zData || pValue->pHash);
1205 }
1206
1207 /*
1208 ** String (zVar, nVar) must contain the name of a scalar variable or
1209 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1219 const char *zValue,
1220 int nValue
1221 ){
1222 Th_Variable *pValue;
1223
1224 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1225 if( !pValue ){
1226 return TH_ERROR;
1227 }
1228
1229 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1262 if( !pFrame ){
1263 return TH_ERROR;
1264 }
1265 pSavedFrame = interp->pFrame;
1266 interp->pFrame = pFrame;
1267 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1268 interp->pFrame = pSavedFrame;
1269
1270 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1271 if( pEntry->pData ){
1272 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1283 ** an array, or an array member. If the identified variable exists, it
1284 ** is deleted and TH_OK returned. Otherwise, an error message is left
1285 ** in the interpreter result and TH_ERROR is returned.
1286 */
1287 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288 Find find;
1289 Th_Variable *pValue;
1290 Th_HashEntry *pEntry;
1291 int rc = TH_ERROR;
1292
1293 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1294 if( !pValue ){
1295 return rc;
1296 }
1297
1298 if( pValue->zData || pValue->pHash ){
1299 rc = TH_OK;
1300 }else {
1301 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302 }
1303
1304 /*
1305 ** The variable may be shared by more than one frame; therefore, make sure
1306 ** it is actually freed prior to freeing the parent structure. The values
1307 ** for the variable must be freed now so the variable appears undefined in
1308 ** all frames. The hash entry in the current frame must also be deleted
1309 ** now; otherwise, if the current stack frame is later popped, it will try
1310 ** to delete a variable which has already been freed.
1311 */
1312 if( find.zElem ){
1313 pEntry = find.pElemEntry;
1314 }else{
1315 pEntry = find.pValueEntry;
1316 }
1317 assert( pEntry );
1318 assert( pValue );
1319 if( thFreeVariable(pEntry, (void *)interp) ){
1320 if( find.zElem ){
1321 Th_Variable *pValue2 = find.pValueEntry->pData;
1322 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323 }else if( pEntry->pData ){
1324 Th_Free(interp, pEntry->pData);
1325 pEntry->pData = 0;
1326 }
1327 }else{
1328 if( pValue->zData ){
1329 Th_Free(interp, pValue->zData);
1330 pValue->zData = 0;
1331 }
1332 if( pValue->pHash ){
1333 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334 Th_HashDelete(interp, pValue->pHash);
1335 pValue->pHash = 0;
1336 }
1337 if( find.zElem ){
1338 Th_Variable *pValue2 = find.pValueEntry->pData;
1339 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340 }
1341 }
1342 if( !find.zElem ){
1343 Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+21 -19
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650650
** string first NEEDLE HAYSTACK
651651
*/
652652
static int string_first_command(
653653
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654654
){
655
- const char *zNeedle;
656655
int nNeedle;
657
- const char *zHaystack;
658656
int nHaystack;
659
- int i;
660657
int iRes = -1;
661658
662659
if( argc!=4 ){
663660
return Th_WrongNumArgs(interp, "string first needle haystack");
664661
}
665662
666
- zNeedle = argv[2];
667663
nNeedle = argl[2];
668
- zHaystack = argv[3];
669664
nHaystack = argl[3];
670665
671
- for(i=0; i<(nHaystack-nNeedle); i++){
672
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
- iRes = i;
674
- break;
666
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667
+ const char *zNeedle = argv[2];
668
+ const char *zHaystack = argv[3];
669
+ int i;
670
+
671
+ for(i=0; i<=(nHaystack-nNeedle); i++){
672
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673
+ iRes = i;
674
+ break;
675
+ }
675676
}
676677
}
677678
678679
return Th_SetResultInt(interp, iRes);
679680
}
@@ -711,30 +712,31 @@
711712
** string last NEEDLE HAYSTACK
712713
*/
713714
static int string_last_command(
714715
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715716
){
716
- const char *zNeedle;
717717
int nNeedle;
718
- const char *zHaystack;
719718
int nHaystack;
720
- int i;
721719
int iRes = -1;
722720
723721
if( argc!=4 ){
724
- return Th_WrongNumArgs(interp, "string first needle haystack");
722
+ return Th_WrongNumArgs(interp, "string last needle haystack");
725723
}
726724
727
- zNeedle = argv[2];
728725
nNeedle = argl[2];
729
- zHaystack = argv[3];
730726
nHaystack = argl[3];
731727
732
- for(i=nHaystack-nNeedle-1; i>=0; i--){
733
- if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734
- iRes = i;
735
- break;
728
+ if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729
+ const char *zNeedle = argv[2];
730
+ const char *zHaystack = argv[3];
731
+ int i;
732
+
733
+ for(i=nHaystack-nNeedle; i>=0; i--){
734
+ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735
+ iRes = i;
736
+ break;
737
+ }
736738
}
737739
}
738740
739741
return Th_SetResultInt(interp, iRes);
740742
}
741743
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
655 const char *zNeedle;
656 int nNeedle;
657 const char *zHaystack;
658 int nHaystack;
659 int i;
660 int iRes = -1;
661
662 if( argc!=4 ){
663 return Th_WrongNumArgs(interp, "string first needle haystack");
664 }
665
666 zNeedle = argv[2];
667 nNeedle = argl[2];
668 zHaystack = argv[3];
669 nHaystack = argl[3];
670
671 for(i=0; i<(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
 
 
 
 
 
 
675 }
676 }
677
678 return Th_SetResultInt(interp, iRes);
679 }
@@ -711,30 +712,31 @@
711 ** string last NEEDLE HAYSTACK
712 */
713 static int string_last_command(
714 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
715 ){
716 const char *zNeedle;
717 int nNeedle;
718 const char *zHaystack;
719 int nHaystack;
720 int i;
721 int iRes = -1;
722
723 if( argc!=4 ){
724 return Th_WrongNumArgs(interp, "string first needle haystack");
725 }
726
727 zNeedle = argv[2];
728 nNeedle = argl[2];
729 zHaystack = argv[3];
730 nHaystack = argl[3];
731
732 for(i=nHaystack-nNeedle-1; i>=0; i--){
733 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
734 iRes = i;
735 break;
 
 
 
 
 
 
736 }
737 }
738
739 return Th_SetResultInt(interp, iRes);
740 }
741
--- src/th_lang.c
+++ src/th_lang.c
@@ -650,30 +650,31 @@
650 ** string first NEEDLE HAYSTACK
651 */
652 static int string_first_command(
653 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
654 ){
 
655 int nNeedle;
 
656 int nHaystack;
 
657 int iRes = -1;
658
659 if( argc!=4 ){
660 return Th_WrongNumArgs(interp, "string first needle haystack");
661 }
662
 
663 nNeedle = argl[2];
 
664 nHaystack = argl[3];
665
666 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
667 const char *zNeedle = argv[2];
668 const char *zHaystack = argv[3];
669 int i;
670
671 for(i=0; i<=(nHaystack-nNeedle); i++){
672 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
673 iRes = i;
674 break;
675 }
676 }
677 }
678
679 return Th_SetResultInt(interp, iRes);
680 }
@@ -711,30 +712,31 @@
712 ** string last NEEDLE HAYSTACK
713 */
714 static int string_last_command(
715 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
716 ){
 
717 int nNeedle;
 
718 int nHaystack;
 
719 int iRes = -1;
720
721 if( argc!=4 ){
722 return Th_WrongNumArgs(interp, "string last needle haystack");
723 }
724
 
725 nNeedle = argl[2];
 
726 nHaystack = argl[3];
727
728 if( nNeedle && nHaystack && nNeedle<=nHaystack ){
729 const char *zNeedle = argv[2];
730 const char *zHaystack = argv[3];
731 int i;
732
733 for(i=nHaystack-nNeedle; i>=0; i--){
734 if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){
735 iRes = i;
736 break;
737 }
738 }
739 }
740
741 return Th_SetResultInt(interp, iRes);
742 }
743
+2 -1
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
10111011
}
10121012
if( forceReset || created ){
10131013
th_register_language(g.interp); /* Basic scripting commands. */
10141014
}
10151015
#ifdef FOSSIL_ENABLE_TCL
1016
- if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
1016
+ if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017
+ db_get_boolean("tcl", 0) ){
10171018
if( !g.tcl.setup ){
10181019
g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
10191020
}
10201021
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
10211022
}
10221023
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
1017 if( !g.tcl.setup ){
1018 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1019 }
1020 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1021 }
1022
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017 db_get_boolean("tcl", 0) ){
1018 if( !g.tcl.setup ){
1019 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1020 }
1021 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1022 }
1023
+2 -1
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
10111011
}
10121012
if( forceReset || created ){
10131013
th_register_language(g.interp); /* Basic scripting commands. */
10141014
}
10151015
#ifdef FOSSIL_ENABLE_TCL
1016
- if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
1016
+ if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017
+ db_get_boolean("tcl", 0) ){
10171018
if( !g.tcl.setup ){
10181019
g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
10191020
}
10201021
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
10211022
}
10221023
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
1017 if( !g.tcl.setup ){
1018 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1019 }
1020 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1021 }
1022
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017 db_get_boolean("tcl", 0) ){
1018 if( !g.tcl.setup ){
1019 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1020 }
1021 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1022 }
1023
+2 -1
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
10111011
}
10121012
if( forceReset || created ){
10131013
th_register_language(g.interp); /* Basic scripting commands. */
10141014
}
10151015
#ifdef FOSSIL_ENABLE_TCL
1016
- if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
1016
+ if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017
+ db_get_boolean("tcl", 0) ){
10171018
if( !g.tcl.setup ){
10181019
g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
10191020
}
10201021
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
10211022
}
10221023
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
1017 if( !g.tcl.setup ){
1018 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1019 }
1020 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1021 }
1022
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017 db_get_boolean("tcl", 0) ){
1018 if( !g.tcl.setup ){
1019 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1020 }
1021 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1022 }
1023
+2 -1
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
10111011
}
10121012
if( forceReset || created ){
10131013
th_register_language(g.interp); /* Basic scripting commands. */
10141014
}
10151015
#ifdef FOSSIL_ENABLE_TCL
1016
- if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
1016
+ if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017
+ db_get_boolean("tcl", 0) ){
10171018
if( !g.tcl.setup ){
10181019
g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
10191020
}
10201021
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
10211022
}
10221023
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
1017 if( !g.tcl.setup ){
1018 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1019 }
1020 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1021 }
1022
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017 db_get_boolean("tcl", 0) ){
1018 if( !g.tcl.setup ){
1019 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1020 }
1021 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1022 }
1023
+2 -1
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
10111011
}
10121012
if( forceReset || created ){
10131013
th_register_language(g.interp); /* Basic scripting commands. */
10141014
}
10151015
#ifdef FOSSIL_ENABLE_TCL
1016
- if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
1016
+ if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017
+ db_get_boolean("tcl", 0) ){
10171018
if( !g.tcl.setup ){
10181019
g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
10191020
}
10201021
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
10211022
}
10221023
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
1017 if( !g.tcl.setup ){
1018 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1019 }
1020 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1021 }
1022
--- src/th_main.c
+++ src/th_main.c
@@ -1011,11 +1011,12 @@
1011 }
1012 if( forceReset || created ){
1013 th_register_language(g.interp); /* Basic scripting commands. */
1014 }
1015 #ifdef FOSSIL_ENABLE_TCL
1016 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
1017 db_get_boolean("tcl", 0) ){
1018 if( !g.tcl.setup ){
1019 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
1020 }
1021 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
1022 }
1023
+6 -2
--- src/tkt.c
+++ src/tkt.c
@@ -535,13 +535,17 @@
535535
}else{
536536
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
537537
db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
538538
}
539539
manifest_crosslink_begin();
540
- result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0);
540
+ result = (manifest_crosslink(rid, pTicket, MC_NONE)==0);
541541
assert( blob_is_reset(pTicket) );
542
- manifest_crosslink_end();
542
+ if( !result ){
543
+ result = manifest_crosslink_end(MC_PERMIT_HOOKS);
544
+ }else{
545
+ manifest_crosslink_end(MC_NONE);
546
+ }
543547
return result;
544548
}
545549
546550
/*
547551
** Subscript command: submit_ticket
548552
--- src/tkt.c
+++ src/tkt.c
@@ -535,13 +535,17 @@
535 }else{
536 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
537 db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
538 }
539 manifest_crosslink_begin();
540 result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0);
541 assert( blob_is_reset(pTicket) );
542 manifest_crosslink_end();
 
 
 
 
543 return result;
544 }
545
546 /*
547 ** Subscript command: submit_ticket
548
--- src/tkt.c
+++ src/tkt.c
@@ -535,13 +535,17 @@
535 }else{
536 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
537 db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
538 }
539 manifest_crosslink_begin();
540 result = (manifest_crosslink(rid, pTicket, MC_NONE)==0);
541 assert( blob_is_reset(pTicket) );
542 if( !result ){
543 result = manifest_crosslink_end(MC_PERMIT_HOOKS);
544 }else{
545 manifest_crosslink_end(MC_NONE);
546 }
547 return result;
548 }
549
550 /*
551 ** Subscript command: submit_ticket
552
+15 -15
--- src/xfer.c
+++ src/xfer.c
@@ -852,25 +852,25 @@
852852
853853
/*
854854
** Run the specified TH1 script, if any, and returns 1 on error.
855855
*/
856856
int xfer_run_script(const char *zScript, const char *zUuid){
857
- int result;
857
+ int rc;
858858
if( !zScript ) return TH_OK;
859859
Th_FossilInit(TH_INIT_DEFAULT);
860860
if( zUuid ){
861
- result = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862
- if( result!=TH_OK ){
861
+ rc = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862
+ if( rc!=TH_OK ){
863863
fossil_error(1, "%s", Th_GetResult(g.interp, 0));
864
- return result;
864
+ return rc;
865865
}
866866
}
867
- result = Th_Eval(g.interp, 0, zScript, -1);
868
- if( result!=TH_OK ){
867
+ rc = Th_Eval(g.interp, 0, zScript, -1);
868
+ if( rc!=TH_OK ){
869869
fossil_error(1, "%s", Th_GetResult(g.interp, 0));
870870
}
871
- return result;
871
+ return rc;
872872
}
873873
874874
/*
875875
** Runs the pre-transfer TH1 script, if any, and returns its return code.
876876
** This script may be run multiple times. If the script performs actions
@@ -914,11 +914,11 @@
914914
int isClone = 0;
915915
int nGimme = 0;
916916
int size;
917917
int recvConfig = 0;
918918
char *zNow;
919
- int result;
919
+ int rc;
920920
921921
if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
922922
fossil_redirect_home();
923923
}
924924
g.zLogin = "anonymous";
@@ -944,12 +944,12 @@
944944
db_begin_transaction();
945945
db_multi_exec(
946946
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
947947
);
948948
manifest_crosslink_begin();
949
- result = xfer_run_common_script();
950
- if( result==TH_ERROR ){
949
+ rc = xfer_run_common_script();
950
+ if( rc==TH_ERROR ){
951951
cgi_reset_content();
952952
@ error common\sscript\sfailed:\s%F(g.zErrMsg)
953953
nErr++;
954954
}
955955
while( blob_line(xfer.pIn, &xfer.line) ){
@@ -1272,13 +1272,13 @@
12721272
}
12731273
blobarray_reset(xfer.aToken, xfer.nToken);
12741274
blob_reset(&xfer.line);
12751275
}
12761276
if( isPush ){
1277
- if( result==TH_OK ){
1278
- result = xfer_run_script(xfer_push_code(), 0);
1279
- if( result==TH_ERROR ){
1277
+ if( rc==TH_OK ){
1278
+ rc = xfer_run_script(xfer_push_code(), 0);
1279
+ if( rc==TH_ERROR ){
12801280
cgi_reset_content();
12811281
@ error push\sscript\sfailed:\s%F(g.zErrMsg)
12821282
nErr++;
12831283
}
12841284
}
@@ -1301,11 +1301,11 @@
13011301
}
13021302
if( recvConfig ){
13031303
configure_finalize_receive();
13041304
}
13051305
db_multi_exec("DROP TABLE onremote");
1306
- manifest_crosslink_end();
1306
+ manifest_crosslink_end(MC_PERMIT_HOOKS);
13071307
13081308
/* Send the server timestamp last, in case prior processing happened
13091309
** to use up a significant fraction of our time window.
13101310
*/
13111311
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@@ -1928,10 +1928,10 @@
19281928
"%s finished with %lld bytes sent, %lld bytes received\n",
19291929
zOpType, nSent, nRcvd);
19301930
transport_close(GLOBAL_URL());
19311931
transport_global_shutdown(GLOBAL_URL());
19321932
db_multi_exec("DROP TABLE onremote");
1933
- manifest_crosslink_end();
1933
+ manifest_crosslink_end(MC_PERMIT_HOOKS);
19341934
content_enable_dephantomize(1);
19351935
db_end_transaction(0);
19361936
return nErr;
19371937
}
19381938
--- src/xfer.c
+++ src/xfer.c
@@ -852,25 +852,25 @@
852
853 /*
854 ** Run the specified TH1 script, if any, and returns 1 on error.
855 */
856 int xfer_run_script(const char *zScript, const char *zUuid){
857 int result;
858 if( !zScript ) return TH_OK;
859 Th_FossilInit(TH_INIT_DEFAULT);
860 if( zUuid ){
861 result = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862 if( result!=TH_OK ){
863 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
864 return result;
865 }
866 }
867 result = Th_Eval(g.interp, 0, zScript, -1);
868 if( result!=TH_OK ){
869 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
870 }
871 return result;
872 }
873
874 /*
875 ** Runs the pre-transfer TH1 script, if any, and returns its return code.
876 ** This script may be run multiple times. If the script performs actions
@@ -914,11 +914,11 @@
914 int isClone = 0;
915 int nGimme = 0;
916 int size;
917 int recvConfig = 0;
918 char *zNow;
919 int result;
920
921 if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
922 fossil_redirect_home();
923 }
924 g.zLogin = "anonymous";
@@ -944,12 +944,12 @@
944 db_begin_transaction();
945 db_multi_exec(
946 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
947 );
948 manifest_crosslink_begin();
949 result = xfer_run_common_script();
950 if( result==TH_ERROR ){
951 cgi_reset_content();
952 @ error common\sscript\sfailed:\s%F(g.zErrMsg)
953 nErr++;
954 }
955 while( blob_line(xfer.pIn, &xfer.line) ){
@@ -1272,13 +1272,13 @@
1272 }
1273 blobarray_reset(xfer.aToken, xfer.nToken);
1274 blob_reset(&xfer.line);
1275 }
1276 if( isPush ){
1277 if( result==TH_OK ){
1278 result = xfer_run_script(xfer_push_code(), 0);
1279 if( result==TH_ERROR ){
1280 cgi_reset_content();
1281 @ error push\sscript\sfailed:\s%F(g.zErrMsg)
1282 nErr++;
1283 }
1284 }
@@ -1301,11 +1301,11 @@
1301 }
1302 if( recvConfig ){
1303 configure_finalize_receive();
1304 }
1305 db_multi_exec("DROP TABLE onremote");
1306 manifest_crosslink_end();
1307
1308 /* Send the server timestamp last, in case prior processing happened
1309 ** to use up a significant fraction of our time window.
1310 */
1311 zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@@ -1928,10 +1928,10 @@
1928 "%s finished with %lld bytes sent, %lld bytes received\n",
1929 zOpType, nSent, nRcvd);
1930 transport_close(GLOBAL_URL());
1931 transport_global_shutdown(GLOBAL_URL());
1932 db_multi_exec("DROP TABLE onremote");
1933 manifest_crosslink_end();
1934 content_enable_dephantomize(1);
1935 db_end_transaction(0);
1936 return nErr;
1937 }
1938
--- src/xfer.c
+++ src/xfer.c
@@ -852,25 +852,25 @@
852
853 /*
854 ** Run the specified TH1 script, if any, and returns 1 on error.
855 */
856 int xfer_run_script(const char *zScript, const char *zUuid){
857 int rc;
858 if( !zScript ) return TH_OK;
859 Th_FossilInit(TH_INIT_DEFAULT);
860 if( zUuid ){
861 rc = Th_SetVar(g.interp, "uuid", -1, zUuid, -1);
862 if( rc!=TH_OK ){
863 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
864 return rc;
865 }
866 }
867 rc = Th_Eval(g.interp, 0, zScript, -1);
868 if( rc!=TH_OK ){
869 fossil_error(1, "%s", Th_GetResult(g.interp, 0));
870 }
871 return rc;
872 }
873
874 /*
875 ** Runs the pre-transfer TH1 script, if any, and returns its return code.
876 ** This script may be run multiple times. If the script performs actions
@@ -914,11 +914,11 @@
914 int isClone = 0;
915 int nGimme = 0;
916 int size;
917 int recvConfig = 0;
918 char *zNow;
919 int rc;
920
921 if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
922 fossil_redirect_home();
923 }
924 g.zLogin = "anonymous";
@@ -944,12 +944,12 @@
944 db_begin_transaction();
945 db_multi_exec(
946 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
947 );
948 manifest_crosslink_begin();
949 rc = xfer_run_common_script();
950 if( rc==TH_ERROR ){
951 cgi_reset_content();
952 @ error common\sscript\sfailed:\s%F(g.zErrMsg)
953 nErr++;
954 }
955 while( blob_line(xfer.pIn, &xfer.line) ){
@@ -1272,13 +1272,13 @@
1272 }
1273 blobarray_reset(xfer.aToken, xfer.nToken);
1274 blob_reset(&xfer.line);
1275 }
1276 if( isPush ){
1277 if( rc==TH_OK ){
1278 rc = xfer_run_script(xfer_push_code(), 0);
1279 if( rc==TH_ERROR ){
1280 cgi_reset_content();
1281 @ error push\sscript\sfailed:\s%F(g.zErrMsg)
1282 nErr++;
1283 }
1284 }
@@ -1301,11 +1301,11 @@
1301 }
1302 if( recvConfig ){
1303 configure_finalize_receive();
1304 }
1305 db_multi_exec("DROP TABLE onremote");
1306 manifest_crosslink_end(MC_PERMIT_HOOKS);
1307
1308 /* Send the server timestamp last, in case prior processing happened
1309 ** to use up a significant fraction of our time window.
1310 */
1311 zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@@ -1928,10 +1928,10 @@
1928 "%s finished with %lld bytes sent, %lld bytes received\n",
1929 zOpType, nSent, nRcvd);
1930 transport_close(GLOBAL_URL());
1931 transport_global_shutdown(GLOBAL_URL());
1932 db_multi_exec("DROP TABLE onremote");
1933 manifest_crosslink_end(MC_PERMIT_HOOKS);
1934 content_enable_dephantomize(1);
1935 db_end_transaction(0);
1936 return nErr;
1937 }
1938
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
+145
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,120 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
+
213
+###############################################################################
214
+
215
+fossil test-th-eval "string first {} {}"
216
+test th1-string-first-1 {$RESULT eq {-1}}
217
+
218
+###############################################################################
219
+
220
+fossil test-th-eval "string first {} {a}"
221
+test th1-string-first-2 {$RESULT eq {-1}}
222
+
223
+###############################################################################
224
+
225
+fossil test-th-eval "string first {a} {}"
226
+test th1-string-first-3 {$RESULT eq {-1}}
227
+
228
+###############################################################################
229
+
230
+fossil test-th-eval "string first {a} {a}"
231
+test th1-string-first-4 {$RESULT eq {0}}
232
+
233
+###############################################################################
234
+
235
+fossil test-th-eval "string first {a} {aa}"
236
+test th1-string-first-5 {$RESULT eq {0}}
237
+
238
+###############################################################################
239
+
240
+fossil test-th-eval "string first {aa} {a}"
241
+test th1-string-first-6 {$RESULT eq {-1}}
242
+
243
+###############################################################################
244
+
245
+fossil test-th-eval "string first {ab} {abc}"
246
+test th1-string-first-7 {$RESULT eq {0}}
247
+
248
+###############################################################################
249
+
250
+fossil test-th-eval "string first {bc} {abc}"
251
+test th1-string-first-8 {$RESULT eq {1}}
252
+
253
+###############################################################################
254
+
255
+fossil test-th-eval "string first {AB} {abc}"
256
+test th1-string-first-9 {$RESULT eq {-1}}
257
+
258
+###############################################################################
259
+
260
+fossil test-th-eval "string last {} {}"
261
+test th1-string-last-1 {$RESULT eq {-1}}
262
+
263
+###############################################################################
264
+
265
+fossil test-th-eval "string last {} {a}"
266
+test th1-string-last-2 {$RESULT eq {-1}}
267
+
268
+###############################################################################
269
+
270
+fossil test-th-eval "string last {a} {}"
271
+test th1-string-last-3 {$RESULT eq {-1}}
272
+
273
+###############################################################################
274
+
275
+fossil test-th-eval "string last {a} {a}"
276
+test th1-string-last-4 {$RESULT eq {0}}
277
+
278
+###############################################################################
279
+
280
+fossil test-th-eval "string last {a} {aa}"
281
+test th1-string-last-5 {$RESULT eq {1}}
282
+
283
+###############################################################################
284
+
285
+fossil test-th-eval "string last {aa} {a}"
286
+test th1-string-last-6 {$RESULT eq {-1}}
287
+
288
+###############################################################################
289
+
290
+fossil test-th-eval "string last {ab} {abc}"
291
+test th1-string-last-7 {$RESULT eq {0}}
292
+
293
+###############################################################################
294
+
295
+fossil test-th-eval "string last {bc} {abc}"
296
+test th1-string-last-8 {$RESULT eq {1}}
297
+
298
+###############################################################################
299
+
300
+fossil test-th-eval "string last {AB} {abc}"
301
+test th1-string-last-9 {$RESULT eq {-1}}
157302
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145 fossil test-th-eval "set var 1; unset var"
146 test th1-unset-1 {$RESULT eq {var}}
147
148 ###############################################################################
149
@@ -152,5 +182,120 @@
152
153 ###############################################################################
154
155 fossil test-th-eval "set var 1; unset var; unset var"
156 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140 fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141 test th1-info-exists-6 {$RESULT eq {bad}}
142
143 ###############################################################################
144
145 fossil test-th-eval "set var(1) 1; info exists var"
146 test th1-info-exists-7 {$RESULT eq {1}}
147
148 ###############################################################################
149
150 fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151 test th1-info-exists-8 {$RESULT eq {1}}
152
153 ###############################################################################
154
155 fossil test-th-eval "set var(1) 1; unset var; info exists var"
156 test th1-info-exists-9 {$RESULT eq {0}}
157
158 ###############################################################################
159
160 fossil test-th-eval "set var(1) 1; info exists var(1)"
161 test th1-info-exists-10 {$RESULT eq {1}}
162
163 ###############################################################################
164
165 fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166 test th1-info-exists-11 {$RESULT eq {0}}
167
168 ###############################################################################
169
170 fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171 test th1-info-exists-12 {$RESULT eq {0}}
172
173 ###############################################################################
174
175 fossil test-th-eval "set var 1; unset var"
176 test th1-unset-1 {$RESULT eq {var}}
177
178 ###############################################################################
179
@@ -152,5 +182,120 @@
182
183 ###############################################################################
184
185 fossil test-th-eval "set var 1; unset var; unset var"
186 test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
188 ###############################################################################
189
190 fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191 test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
193 ###############################################################################
194
195 fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196 test th1-unset-5 {$RESULT eq {1}}
197
198 ###############################################################################
199
200 fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201 test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
203 ###############################################################################
204
205 fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206 test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
208 ###############################################################################
209
210 fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211 test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
212
213 ###############################################################################
214
215 fossil test-th-eval "string first {} {}"
216 test th1-string-first-1 {$RESULT eq {-1}}
217
218 ###############################################################################
219
220 fossil test-th-eval "string first {} {a}"
221 test th1-string-first-2 {$RESULT eq {-1}}
222
223 ###############################################################################
224
225 fossil test-th-eval "string first {a} {}"
226 test th1-string-first-3 {$RESULT eq {-1}}
227
228 ###############################################################################
229
230 fossil test-th-eval "string first {a} {a}"
231 test th1-string-first-4 {$RESULT eq {0}}
232
233 ###############################################################################
234
235 fossil test-th-eval "string first {a} {aa}"
236 test th1-string-first-5 {$RESULT eq {0}}
237
238 ###############################################################################
239
240 fossil test-th-eval "string first {aa} {a}"
241 test th1-string-first-6 {$RESULT eq {-1}}
242
243 ###############################################################################
244
245 fossil test-th-eval "string first {ab} {abc}"
246 test th1-string-first-7 {$RESULT eq {0}}
247
248 ###############################################################################
249
250 fossil test-th-eval "string first {bc} {abc}"
251 test th1-string-first-8 {$RESULT eq {1}}
252
253 ###############################################################################
254
255 fossil test-th-eval "string first {AB} {abc}"
256 test th1-string-first-9 {$RESULT eq {-1}}
257
258 ###############################################################################
259
260 fossil test-th-eval "string last {} {}"
261 test th1-string-last-1 {$RESULT eq {-1}}
262
263 ###############################################################################
264
265 fossil test-th-eval "string last {} {a}"
266 test th1-string-last-2 {$RESULT eq {-1}}
267
268 ###############################################################################
269
270 fossil test-th-eval "string last {a} {}"
271 test th1-string-last-3 {$RESULT eq {-1}}
272
273 ###############################################################################
274
275 fossil test-th-eval "string last {a} {a}"
276 test th1-string-last-4 {$RESULT eq {0}}
277
278 ###############################################################################
279
280 fossil test-th-eval "string last {a} {aa}"
281 test th1-string-last-5 {$RESULT eq {1}}
282
283 ###############################################################################
284
285 fossil test-th-eval "string last {aa} {a}"
286 test th1-string-last-6 {$RESULT eq {-1}}
287
288 ###############################################################################
289
290 fossil test-th-eval "string last {ab} {abc}"
291 test th1-string-last-7 {$RESULT eq {0}}
292
293 ###############################################################################
294
295 fossil test-th-eval "string last {bc} {abc}"
296 test th1-string-last-8 {$RESULT eq {1}}
297
298 ###############################################################################
299
300 fossil test-th-eval "string last {AB} {abc}"
301 test th1-string-last-9 {$RESULT eq {-1}}
302
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
3939
4040
!ifdef FOSSIL_ENABLE_SSL
4141
INCL = $(INCL) -I$(SSLINCDIR)
4242
!endif
4343
44
-CFLAGS = -nologo -MT -O2
44
+CFLAGS = -nologo
4545
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
4646
4747
!ifdef DEBUG
48
-CFLAGS = $(CFLAGS) -Zi
48
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
4949
LDFLAGS = $(LDFLAGS) /DEBUG
50
+!else
51
+CFLAGS = $(CFLAGS) -MT -O2
5052
!endif
5153
5254
BCC = $(CC) $(CFLAGS)
5355
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
5456
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448450
$(BCC) $**
449451
450452
mkversion$E: $B\src\mkversion.c
451453
$(BCC) $**
452454
453
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
455
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
454456
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455457
456
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
458
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
457459
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458460
459461
$(OX)\th$O : $(SRCDIR)\th.c
460462
$(TCC) /Fo$@ -c $**
461463
462464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo -MT -O2
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi
49 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
50 !endif
51
52 BCC = $(CC) $(CFLAGS)
53 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
54 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448 $(BCC) $**
449
450 mkversion$E: $B\src\mkversion.c
451 $(BCC) $**
452
453 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
454 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455
456 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
457 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458
459 $(OX)\th$O : $(SRCDIR)\th.c
460 $(TCC) /Fo$@ -c $**
461
462
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi -MTd -Od
49 LDFLAGS = $(LDFLAGS) /DEBUG
50 !else
51 CFLAGS = $(CFLAGS) -MT -O2
52 !endif
53
54 BCC = $(CC) $(CFLAGS)
55 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
56 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
450 $(BCC) $**
451
452 mkversion$E: $B\src\mkversion.c
453 $(BCC) $**
454
455 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
456 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457
458 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
459 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
460
461 $(OX)\th$O : $(SRCDIR)\th.c
462 $(TCC) /Fo$@ -c $**
463
464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
3939
4040
!ifdef FOSSIL_ENABLE_SSL
4141
INCL = $(INCL) -I$(SSLINCDIR)
4242
!endif
4343
44
-CFLAGS = -nologo -MT -O2
44
+CFLAGS = -nologo
4545
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
4646
4747
!ifdef DEBUG
48
-CFLAGS = $(CFLAGS) -Zi
48
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
4949
LDFLAGS = $(LDFLAGS) /DEBUG
50
+!else
51
+CFLAGS = $(CFLAGS) -MT -O2
5052
!endif
5153
5254
BCC = $(CC) $(CFLAGS)
5355
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
5456
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448450
$(BCC) $**
449451
450452
mkversion$E: $B\src\mkversion.c
451453
$(BCC) $**
452454
453
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
455
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
454456
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455457
456
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
458
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
457459
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458460
459461
$(OX)\th$O : $(SRCDIR)\th.c
460462
$(TCC) /Fo$@ -c $**
461463
462464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo -MT -O2
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi
49 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
50 !endif
51
52 BCC = $(CC) $(CFLAGS)
53 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
54 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448 $(BCC) $**
449
450 mkversion$E: $B\src\mkversion.c
451 $(BCC) $**
452
453 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
454 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455
456 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
457 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458
459 $(OX)\th$O : $(SRCDIR)\th.c
460 $(TCC) /Fo$@ -c $**
461
462
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi -MTd -Od
49 LDFLAGS = $(LDFLAGS) /DEBUG
50 !else
51 CFLAGS = $(CFLAGS) -MT -O2
52 !endif
53
54 BCC = $(CC) $(CFLAGS)
55 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
56 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
450 $(BCC) $**
451
452 mkversion$E: $B\src\mkversion.c
453 $(BCC) $**
454
455 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
456 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457
458 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
459 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
460
461 $(OX)\th$O : $(SRCDIR)\th.c
462 $(TCC) /Fo$@ -c $**
463
464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
3939
4040
!ifdef FOSSIL_ENABLE_SSL
4141
INCL = $(INCL) -I$(SSLINCDIR)
4242
!endif
4343
44
-CFLAGS = -nologo -MT -O2
44
+CFLAGS = -nologo
4545
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
4646
4747
!ifdef DEBUG
48
-CFLAGS = $(CFLAGS) -Zi
48
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
4949
LDFLAGS = $(LDFLAGS) /DEBUG
50
+!else
51
+CFLAGS = $(CFLAGS) -MT -O2
5052
!endif
5153
5254
BCC = $(CC) $(CFLAGS)
5355
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
5456
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448450
$(BCC) $**
449451
450452
mkversion$E: $B\src\mkversion.c
451453
$(BCC) $**
452454
453
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
455
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
454456
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455457
456
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
458
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
457459
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458460
459461
$(OX)\th$O : $(SRCDIR)\th.c
460462
$(TCC) /Fo$@ -c $**
461463
462464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo -MT -O2
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi
49 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
50 !endif
51
52 BCC = $(CC) $(CFLAGS)
53 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
54 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448 $(BCC) $**
449
450 mkversion$E: $B\src\mkversion.c
451 $(BCC) $**
452
453 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
454 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455
456 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
457 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458
459 $(OX)\th$O : $(SRCDIR)\th.c
460 $(TCC) /Fo$@ -c $**
461
462
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi -MTd -Od
49 LDFLAGS = $(LDFLAGS) /DEBUG
50 !else
51 CFLAGS = $(CFLAGS) -MT -O2
52 !endif
53
54 BCC = $(CC) $(CFLAGS)
55 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
56 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
450 $(BCC) $**
451
452 mkversion$E: $B\src\mkversion.c
453 $(BCC) $**
454
455 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
456 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457
458 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
459 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
460
461 $(OX)\th$O : $(SRCDIR)\th.c
462 $(TCC) /Fo$@ -c $**
463
464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
3939
4040
!ifdef FOSSIL_ENABLE_SSL
4141
INCL = $(INCL) -I$(SSLINCDIR)
4242
!endif
4343
44
-CFLAGS = -nologo -MT -O2
44
+CFLAGS = -nologo
4545
LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
4646
4747
!ifdef DEBUG
48
-CFLAGS = $(CFLAGS) -Zi
48
+CFLAGS = $(CFLAGS) -Zi -MTd -Od
4949
LDFLAGS = $(LDFLAGS) /DEBUG
50
+!else
51
+CFLAGS = $(CFLAGS) -MT -O2
5052
!endif
5153
5254
BCC = $(CC) $(CFLAGS)
5355
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
5456
RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448450
$(BCC) $**
449451
450452
mkversion$E: $B\src\mkversion.c
451453
$(BCC) $**
452454
453
-$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
455
+$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
454456
$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455457
456
-$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
458
+$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
457459
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458460
459461
$(OX)\th$O : $(SRCDIR)\th.c
460462
$(TCC) /Fo$@ -c $**
461463
462464
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo -MT -O2
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi
49 LDFLAGS = $(LDFLAGS) /DEBUG
 
 
50 !endif
51
52 BCC = $(CC) $(CFLAGS)
53 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
54 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
448 $(BCC) $**
449
450 mkversion$E: $B\src\mkversion.c
451 $(BCC) $**
452
453 $(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc
454 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
455
456 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc
457 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
458
459 $(OX)\th$O : $(SRCDIR)\th.c
460 $(TCC) /Fo$@ -c $**
461
462
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -39,16 +39,18 @@
39
40 !ifdef FOSSIL_ENABLE_SSL
41 INCL = $(INCL) -I$(SSLINCDIR)
42 !endif
43
44 CFLAGS = -nologo
45 LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO
46
47 !ifdef DEBUG
48 CFLAGS = $(CFLAGS) -Zi -MTd -Od
49 LDFLAGS = $(LDFLAGS) /DEBUG
50 !else
51 CFLAGS = $(CFLAGS) -MT -O2
52 !endif
53
54 BCC = $(CC) $(CFLAGS)
55 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL)
56 RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL)
@@ -448,14 +450,14 @@
450 $(BCC) $**
451
452 mkversion$E: $B\src\mkversion.c
453 $(BCC) $**
454
455 $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
456 $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c
457
458 $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
459 $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
460
461 $(OX)\th$O : $(SRCDIR)\th.c
462 $(TCC) /Fo$@ -c $**
463
464

Keyboard Shortcuts

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