Fossil SCM
Cherrypick MSVC/TH1 related fixes [42316a14e2], [354288db9c], [9dc0877d91], [fb29094a8f], [e0f22dda7b], [1aeb2726b0], [95292a13fa], [5e368e911d], [dd8d317670], and [f61958b183] from trunk for review.
Commit
76442af7e13267bd164a2a681dc4ef2757ebcd4f
Parent
2447b2d7ed9353a…
65 files changed
+6
-5
+6
-5
+6
-5
+6
-5
+6
-5
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+6
-4
+27
-11
+27
-11
+27
-11
+1
-1
+112
-34
+112
-34
+112
-34
+112
-34
+112
-34
+112
-34
+112
-34
+112
-34
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+21
-19
+21
-19
+21
-19
+21
-19
+21
-19
+21
-19
+21
-19
+21
-19
+21
-19
+21
-19
+2
-1
+2
-1
+2
-1
+2
-1
+2
-1
+6
-2
+15
-15
+145
+145
+145
+145
+145
+145
+145
+145
+145
+145
+6
-4
+6
-4
+6
-4
+6
-4
~
src/main.c
~
src/main.c
~
src/main.c
~
src/main.c
~
src/main.c
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/makemake.tcl
~
src/manifest.c
~
src/manifest.c
~
src/manifest.c
~
src/rebuild.c
~
src/th.c
~
src/th.c
~
src/th.c
~
src/th.c
~
src/th.c
~
src/th.c
~
src/th.c
~
src/th.c
~
src/th.h
~
src/th.h
~
src/th.h
~
src/th.h
~
src/th.h
~
src/th.h
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_lang.c
~
src/th_main.c
~
src/th_main.c
~
src/th_main.c
~
src/th_main.c
~
src/th_main.c
~
src/tkt.c
~
src/xfer.c
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
test/th1.test
~
win/Makefile.msc
~
win/Makefile.msc
~
win/Makefile.msc
~
win/Makefile.msc
+6
-5
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -378,17 +378,18 @@ | ||
| 378 | 378 | if(g.db){ |
| 379 | 379 | db_close(0); |
| 380 | 380 | } |
| 381 | 381 | /* |
| 382 | 382 | ** 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. | |
| 384 | 384 | */ |
| 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 ); | |
| 388 | 390 | } |
| 389 | - assert( Th_GetOutstandingMalloc()==0 ); */ | |
| 390 | 391 | } |
| 391 | 392 | |
| 392 | 393 | /* |
| 393 | 394 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 394 | 395 | ** search g.argv for arguments "--args FILENAME". If found, then |
| 395 | 396 |
| --- 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 @@ | ||
| 378 | 378 | if(g.db){ |
| 379 | 379 | db_close(0); |
| 380 | 380 | } |
| 381 | 381 | /* |
| 382 | 382 | ** 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. | |
| 384 | 384 | */ |
| 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 ); | |
| 388 | 390 | } |
| 389 | - assert( Th_GetOutstandingMalloc()==0 ); */ | |
| 390 | 391 | } |
| 391 | 392 | |
| 392 | 393 | /* |
| 393 | 394 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 394 | 395 | ** search g.argv for arguments "--args FILENAME". If found, then |
| 395 | 396 |
| --- 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 @@ | ||
| 378 | 378 | if(g.db){ |
| 379 | 379 | db_close(0); |
| 380 | 380 | } |
| 381 | 381 | /* |
| 382 | 382 | ** 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. | |
| 384 | 384 | */ |
| 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 ); | |
| 388 | 390 | } |
| 389 | - assert( Th_GetOutstandingMalloc()==0 ); */ | |
| 390 | 391 | } |
| 391 | 392 | |
| 392 | 393 | /* |
| 393 | 394 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 394 | 395 | ** search g.argv for arguments "--args FILENAME". If found, then |
| 395 | 396 |
| --- 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 @@ | ||
| 378 | 378 | if(g.db){ |
| 379 | 379 | db_close(0); |
| 380 | 380 | } |
| 381 | 381 | /* |
| 382 | 382 | ** 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. | |
| 384 | 384 | */ |
| 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 ); | |
| 388 | 390 | } |
| 389 | - assert( Th_GetOutstandingMalloc()==0 ); */ | |
| 390 | 391 | } |
| 391 | 392 | |
| 392 | 393 | /* |
| 393 | 394 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 394 | 395 | ** search g.argv for arguments "--args FILENAME". If found, then |
| 395 | 396 |
| --- 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 @@ | ||
| 378 | 378 | if(g.db){ |
| 379 | 379 | db_close(0); |
| 380 | 380 | } |
| 381 | 381 | /* |
| 382 | 382 | ** 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. | |
| 384 | 384 | */ |
| 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 ); | |
| 388 | 390 | } |
| 389 | - assert( Th_GetOutstandingMalloc()==0 ); */ | |
| 390 | 391 | } |
| 391 | 392 | |
| 392 | 393 | /* |
| 393 | 394 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 394 | 395 | ** search g.argv for arguments "--args FILENAME". If found, then |
| 395 | 396 |
| --- 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
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 |
+6
-4
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1033,16 +1033,18 @@ | ||
| 1033 | 1033 | |
| 1034 | 1034 | !ifdef FOSSIL_ENABLE_SSL |
| 1035 | 1035 | INCL = $(INCL) -I$(SSLINCDIR) |
| 1036 | 1036 | !endif |
| 1037 | 1037 | |
| 1038 | -CFLAGS = -nologo -MT -O2 | |
| 1038 | +CFLAGS = -nologo | |
| 1039 | 1039 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 1040 | 1040 | |
| 1041 | 1041 | !ifdef DEBUG |
| 1042 | -CFLAGS = $(CFLAGS) -Zi | |
| 1042 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 1043 | 1043 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1044 | +!else | |
| 1045 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 1044 | 1046 | !endif |
| 1045 | 1047 | |
| 1046 | 1048 | BCC = $(CC) $(CFLAGS) |
| 1047 | 1049 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1048 | 1050 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -1128,14 +1130,14 @@ | ||
| 1128 | 1130 | $(BCC) $** |
| 1129 | 1131 | |
| 1130 | 1132 | mkversion$E: $B\src\mkversion.c |
| 1131 | 1133 | $(BCC) $** |
| 1132 | 1134 | |
| 1133 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 1135 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 1134 | 1136 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 1135 | 1137 | |
| 1136 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 1138 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 1137 | 1139 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 1138 | 1140 | |
| 1139 | 1141 | $(OX)\th$O : $(SRCDIR)\th.c |
| 1140 | 1142 | $(TCC) /Fo$@ -c $** |
| 1141 | 1143 | |
| 1142 | 1144 |
| --- 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 @@ | ||
| 1494 | 1494 | #endif /* LOCAL_INTERFACE */ |
| 1495 | 1495 | |
| 1496 | 1496 | /* |
| 1497 | 1497 | ** Finish up a sequence of manifest_crosslink calls. |
| 1498 | 1498 | */ |
| 1499 | -void manifest_crosslink_end(void){ | |
| 1499 | +int manifest_crosslink_end(int flags){ | |
| 1500 | 1500 | Stmt q, u; |
| 1501 | 1501 | int i; |
| 1502 | + int rc = TH_OK; | |
| 1503 | + int permitHooks = (flags & MC_PERMIT_HOOKS); | |
| 1504 | + const char *zScript = 0; | |
| 1502 | 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 | + } | |
| 1503 | 1512 | db_prepare(&q, "SELECT uuid FROM pending_tkt"); |
| 1504 | 1513 | while( db_step(&q)==SQLITE_ROW ){ |
| 1505 | 1514 | const char *zUuid = db_column_text(&q, 0); |
| 1506 | 1515 | ticket_rebuild_entry(zUuid); |
| 1516 | + if( permitHooks && rc==TH_OK ){ | |
| 1517 | + rc = xfer_run_script(zScript, zUuid); | |
| 1518 | + } | |
| 1507 | 1519 | } |
| 1508 | 1520 | db_finalize(&q); |
| 1509 | 1521 | db_multi_exec("DROP TABLE pending_tkt"); |
| 1510 | 1522 | |
| 1511 | 1523 | /* If multiple check-ins happen close together in time, adjust their |
| @@ -1536,10 +1548,11 @@ | ||
| 1536 | 1548 | "DROP TABLE time_fudge;" |
| 1537 | 1549 | ); |
| 1538 | 1550 | |
| 1539 | 1551 | db_end_transaction(0); |
| 1540 | 1552 | manifest_crosslink_busy = 0; |
| 1553 | + return ( rc!=TH_ERROR ); | |
| 1541 | 1554 | } |
| 1542 | 1555 | |
| 1543 | 1556 | /* |
| 1544 | 1557 | ** Make an entry in the event table for a ticket change artifact. |
| 1545 | 1558 | */ |
| @@ -1657,14 +1670,15 @@ | ||
| 1657 | 1670 | ** Processing for other control artifacts was added later. The name |
| 1658 | 1671 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1659 | 1672 | ** file, is a legacy of its original use. |
| 1660 | 1673 | */ |
| 1661 | 1674 | int manifest_crosslink(int rid, Blob *pContent, int flags){ |
| 1662 | - int i, result = TH_OK; | |
| 1675 | + int i, rc = TH_OK; | |
| 1663 | 1676 | Manifest *p; |
| 1664 | 1677 | Stmt q; |
| 1665 | 1678 | int parentid = 0; |
| 1679 | + int permitHooks = (flags & MC_PERMIT_HOOKS); | |
| 1666 | 1680 | const char *zScript = 0; |
| 1667 | 1681 | const char *zUuid = 0; |
| 1668 | 1682 | |
| 1669 | 1683 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1670 | 1684 | blob_reset(pContent); |
| @@ -1685,11 +1699,13 @@ | ||
| 1685 | 1699 | fossil_error(1, "cannot fetch baseline manifest"); |
| 1686 | 1700 | return 0; |
| 1687 | 1701 | } |
| 1688 | 1702 | db_begin_transaction(); |
| 1689 | 1703 | if( p->type==CFTYPE_MANIFEST ){ |
| 1690 | - zScript = xfer_commit_code(); | |
| 1704 | + if( permitHooks ){ | |
| 1705 | + zScript = xfer_commit_code(); | |
| 1706 | + } | |
| 1691 | 1707 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1692 | 1708 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1693 | 1709 | char *zCom; |
| 1694 | 1710 | for(i=0; i<p->nParent; i++){ |
| 1695 | 1711 | int pid = uuid_to_rid(p->azParent[i], 1); |
| @@ -1883,12 +1899,10 @@ | ||
| 1883 | 1899 | } |
| 1884 | 1900 | } |
| 1885 | 1901 | if( p->type==CFTYPE_TICKET ){ |
| 1886 | 1902 | char *zTag; |
| 1887 | 1903 | |
| 1888 | - zScript = xfer_ticket_code(); | |
| 1889 | - zUuid = p->zTicketUuid; | |
| 1890 | 1904 | assert( manifest_crosslink_busy==1 ); |
| 1891 | 1905 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1892 | 1906 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1893 | 1907 | free(zTag); |
| 1894 | 1908 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1968,11 +1982,13 @@ | ||
| 1968 | 1982 | zTagUuid); |
| 1969 | 1983 | branchMove = 0; |
| 1970 | 1984 | if( db_exists("SELECT 1 FROM event, blob" |
| 1971 | 1985 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 1972 | 1986 | " AND blob.uuid='%s'", zTagUuid) ){ |
| 1973 | - zScript = xfer_commit_code(); | |
| 1987 | + if( permitHooks ){ | |
| 1988 | + zScript = xfer_commit_code(); | |
| 1989 | + } | |
| 1974 | 1990 | zUuid = zTagUuid; |
| 1975 | 1991 | } |
| 1976 | 1992 | } |
| 1977 | 1993 | zName = p->aTag[i].zName; |
| 1978 | 1994 | zValue = p->aTag[i].zValue; |
| @@ -2042,23 +2058,23 @@ | ||
| 2042 | 2058 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2043 | 2059 | ); |
| 2044 | 2060 | blob_reset(&comment); |
| 2045 | 2061 | } |
| 2046 | 2062 | 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); | |
| 2051 | 2067 | } |
| 2052 | 2068 | } |
| 2053 | 2069 | if( p->type==CFTYPE_MANIFEST ){ |
| 2054 | 2070 | manifest_cache_insert(p); |
| 2055 | 2071 | }else{ |
| 2056 | 2072 | manifest_destroy(p); |
| 2057 | 2073 | } |
| 2058 | 2074 | assert( blob_is_reset(pContent) ); |
| 2059 | - return ( result!=TH_ERROR ); | |
| 2075 | + return ( rc!=TH_ERROR ); | |
| 2060 | 2076 | } |
| 2061 | 2077 | |
| 2062 | 2078 | /* |
| 2063 | 2079 | ** COMMAND: test-crosslink |
| 2064 | 2080 | ** |
| 2065 | 2081 |
| --- 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 @@ | ||
| 1494 | 1494 | #endif /* LOCAL_INTERFACE */ |
| 1495 | 1495 | |
| 1496 | 1496 | /* |
| 1497 | 1497 | ** Finish up a sequence of manifest_crosslink calls. |
| 1498 | 1498 | */ |
| 1499 | -void manifest_crosslink_end(void){ | |
| 1499 | +int manifest_crosslink_end(int flags){ | |
| 1500 | 1500 | Stmt q, u; |
| 1501 | 1501 | int i; |
| 1502 | + int rc = TH_OK; | |
| 1503 | + int permitHooks = (flags & MC_PERMIT_HOOKS); | |
| 1504 | + const char *zScript = 0; | |
| 1502 | 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 | + } | |
| 1503 | 1512 | db_prepare(&q, "SELECT uuid FROM pending_tkt"); |
| 1504 | 1513 | while( db_step(&q)==SQLITE_ROW ){ |
| 1505 | 1514 | const char *zUuid = db_column_text(&q, 0); |
| 1506 | 1515 | ticket_rebuild_entry(zUuid); |
| 1516 | + if( permitHooks && rc==TH_OK ){ | |
| 1517 | + rc = xfer_run_script(zScript, zUuid); | |
| 1518 | + } | |
| 1507 | 1519 | } |
| 1508 | 1520 | db_finalize(&q); |
| 1509 | 1521 | db_multi_exec("DROP TABLE pending_tkt"); |
| 1510 | 1522 | |
| 1511 | 1523 | /* If multiple check-ins happen close together in time, adjust their |
| @@ -1536,10 +1548,11 @@ | ||
| 1536 | 1548 | "DROP TABLE time_fudge;" |
| 1537 | 1549 | ); |
| 1538 | 1550 | |
| 1539 | 1551 | db_end_transaction(0); |
| 1540 | 1552 | manifest_crosslink_busy = 0; |
| 1553 | + return ( rc!=TH_ERROR ); | |
| 1541 | 1554 | } |
| 1542 | 1555 | |
| 1543 | 1556 | /* |
| 1544 | 1557 | ** Make an entry in the event table for a ticket change artifact. |
| 1545 | 1558 | */ |
| @@ -1657,14 +1670,15 @@ | ||
| 1657 | 1670 | ** Processing for other control artifacts was added later. The name |
| 1658 | 1671 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1659 | 1672 | ** file, is a legacy of its original use. |
| 1660 | 1673 | */ |
| 1661 | 1674 | int manifest_crosslink(int rid, Blob *pContent, int flags){ |
| 1662 | - int i, result = TH_OK; | |
| 1675 | + int i, rc = TH_OK; | |
| 1663 | 1676 | Manifest *p; |
| 1664 | 1677 | Stmt q; |
| 1665 | 1678 | int parentid = 0; |
| 1679 | + int permitHooks = (flags & MC_PERMIT_HOOKS); | |
| 1666 | 1680 | const char *zScript = 0; |
| 1667 | 1681 | const char *zUuid = 0; |
| 1668 | 1682 | |
| 1669 | 1683 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1670 | 1684 | blob_reset(pContent); |
| @@ -1685,11 +1699,13 @@ | ||
| 1685 | 1699 | fossil_error(1, "cannot fetch baseline manifest"); |
| 1686 | 1700 | return 0; |
| 1687 | 1701 | } |
| 1688 | 1702 | db_begin_transaction(); |
| 1689 | 1703 | if( p->type==CFTYPE_MANIFEST ){ |
| 1690 | - zScript = xfer_commit_code(); | |
| 1704 | + if( permitHooks ){ | |
| 1705 | + zScript = xfer_commit_code(); | |
| 1706 | + } | |
| 1691 | 1707 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1692 | 1708 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1693 | 1709 | char *zCom; |
| 1694 | 1710 | for(i=0; i<p->nParent; i++){ |
| 1695 | 1711 | int pid = uuid_to_rid(p->azParent[i], 1); |
| @@ -1883,12 +1899,10 @@ | ||
| 1883 | 1899 | } |
| 1884 | 1900 | } |
| 1885 | 1901 | if( p->type==CFTYPE_TICKET ){ |
| 1886 | 1902 | char *zTag; |
| 1887 | 1903 | |
| 1888 | - zScript = xfer_ticket_code(); | |
| 1889 | - zUuid = p->zTicketUuid; | |
| 1890 | 1904 | assert( manifest_crosslink_busy==1 ); |
| 1891 | 1905 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1892 | 1906 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1893 | 1907 | free(zTag); |
| 1894 | 1908 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1968,11 +1982,13 @@ | ||
| 1968 | 1982 | zTagUuid); |
| 1969 | 1983 | branchMove = 0; |
| 1970 | 1984 | if( db_exists("SELECT 1 FROM event, blob" |
| 1971 | 1985 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 1972 | 1986 | " AND blob.uuid='%s'", zTagUuid) ){ |
| 1973 | - zScript = xfer_commit_code(); | |
| 1987 | + if( permitHooks ){ | |
| 1988 | + zScript = xfer_commit_code(); | |
| 1989 | + } | |
| 1974 | 1990 | zUuid = zTagUuid; |
| 1975 | 1991 | } |
| 1976 | 1992 | } |
| 1977 | 1993 | zName = p->aTag[i].zName; |
| 1978 | 1994 | zValue = p->aTag[i].zValue; |
| @@ -2042,23 +2058,23 @@ | ||
| 2042 | 2058 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2043 | 2059 | ); |
| 2044 | 2060 | blob_reset(&comment); |
| 2045 | 2061 | } |
| 2046 | 2062 | 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); | |
| 2051 | 2067 | } |
| 2052 | 2068 | } |
| 2053 | 2069 | if( p->type==CFTYPE_MANIFEST ){ |
| 2054 | 2070 | manifest_cache_insert(p); |
| 2055 | 2071 | }else{ |
| 2056 | 2072 | manifest_destroy(p); |
| 2057 | 2073 | } |
| 2058 | 2074 | assert( blob_is_reset(pContent) ); |
| 2059 | - return ( result!=TH_ERROR ); | |
| 2075 | + return ( rc!=TH_ERROR ); | |
| 2060 | 2076 | } |
| 2061 | 2077 | |
| 2062 | 2078 | /* |
| 2063 | 2079 | ** COMMAND: test-crosslink |
| 2064 | 2080 | ** |
| 2065 | 2081 |
| --- 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 @@ | ||
| 1494 | 1494 | #endif /* LOCAL_INTERFACE */ |
| 1495 | 1495 | |
| 1496 | 1496 | /* |
| 1497 | 1497 | ** Finish up a sequence of manifest_crosslink calls. |
| 1498 | 1498 | */ |
| 1499 | -void manifest_crosslink_end(void){ | |
| 1499 | +int manifest_crosslink_end(int flags){ | |
| 1500 | 1500 | Stmt q, u; |
| 1501 | 1501 | int i; |
| 1502 | + int rc = TH_OK; | |
| 1503 | + int permitHooks = (flags & MC_PERMIT_HOOKS); | |
| 1504 | + const char *zScript = 0; | |
| 1502 | 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 | + } | |
| 1503 | 1512 | db_prepare(&q, "SELECT uuid FROM pending_tkt"); |
| 1504 | 1513 | while( db_step(&q)==SQLITE_ROW ){ |
| 1505 | 1514 | const char *zUuid = db_column_text(&q, 0); |
| 1506 | 1515 | ticket_rebuild_entry(zUuid); |
| 1516 | + if( permitHooks && rc==TH_OK ){ | |
| 1517 | + rc = xfer_run_script(zScript, zUuid); | |
| 1518 | + } | |
| 1507 | 1519 | } |
| 1508 | 1520 | db_finalize(&q); |
| 1509 | 1521 | db_multi_exec("DROP TABLE pending_tkt"); |
| 1510 | 1522 | |
| 1511 | 1523 | /* If multiple check-ins happen close together in time, adjust their |
| @@ -1536,10 +1548,11 @@ | ||
| 1536 | 1548 | "DROP TABLE time_fudge;" |
| 1537 | 1549 | ); |
| 1538 | 1550 | |
| 1539 | 1551 | db_end_transaction(0); |
| 1540 | 1552 | manifest_crosslink_busy = 0; |
| 1553 | + return ( rc!=TH_ERROR ); | |
| 1541 | 1554 | } |
| 1542 | 1555 | |
| 1543 | 1556 | /* |
| 1544 | 1557 | ** Make an entry in the event table for a ticket change artifact. |
| 1545 | 1558 | */ |
| @@ -1657,14 +1670,15 @@ | ||
| 1657 | 1670 | ** Processing for other control artifacts was added later. The name |
| 1658 | 1671 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1659 | 1672 | ** file, is a legacy of its original use. |
| 1660 | 1673 | */ |
| 1661 | 1674 | int manifest_crosslink(int rid, Blob *pContent, int flags){ |
| 1662 | - int i, result = TH_OK; | |
| 1675 | + int i, rc = TH_OK; | |
| 1663 | 1676 | Manifest *p; |
| 1664 | 1677 | Stmt q; |
| 1665 | 1678 | int parentid = 0; |
| 1679 | + int permitHooks = (flags & MC_PERMIT_HOOKS); | |
| 1666 | 1680 | const char *zScript = 0; |
| 1667 | 1681 | const char *zUuid = 0; |
| 1668 | 1682 | |
| 1669 | 1683 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1670 | 1684 | blob_reset(pContent); |
| @@ -1685,11 +1699,13 @@ | ||
| 1685 | 1699 | fossil_error(1, "cannot fetch baseline manifest"); |
| 1686 | 1700 | return 0; |
| 1687 | 1701 | } |
| 1688 | 1702 | db_begin_transaction(); |
| 1689 | 1703 | if( p->type==CFTYPE_MANIFEST ){ |
| 1690 | - zScript = xfer_commit_code(); | |
| 1704 | + if( permitHooks ){ | |
| 1705 | + zScript = xfer_commit_code(); | |
| 1706 | + } | |
| 1691 | 1707 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1692 | 1708 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1693 | 1709 | char *zCom; |
| 1694 | 1710 | for(i=0; i<p->nParent; i++){ |
| 1695 | 1711 | int pid = uuid_to_rid(p->azParent[i], 1); |
| @@ -1883,12 +1899,10 @@ | ||
| 1883 | 1899 | } |
| 1884 | 1900 | } |
| 1885 | 1901 | if( p->type==CFTYPE_TICKET ){ |
| 1886 | 1902 | char *zTag; |
| 1887 | 1903 | |
| 1888 | - zScript = xfer_ticket_code(); | |
| 1889 | - zUuid = p->zTicketUuid; | |
| 1890 | 1904 | assert( manifest_crosslink_busy==1 ); |
| 1891 | 1905 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1892 | 1906 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1893 | 1907 | free(zTag); |
| 1894 | 1908 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1968,11 +1982,13 @@ | ||
| 1968 | 1982 | zTagUuid); |
| 1969 | 1983 | branchMove = 0; |
| 1970 | 1984 | if( db_exists("SELECT 1 FROM event, blob" |
| 1971 | 1985 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 1972 | 1986 | " AND blob.uuid='%s'", zTagUuid) ){ |
| 1973 | - zScript = xfer_commit_code(); | |
| 1987 | + if( permitHooks ){ | |
| 1988 | + zScript = xfer_commit_code(); | |
| 1989 | + } | |
| 1974 | 1990 | zUuid = zTagUuid; |
| 1975 | 1991 | } |
| 1976 | 1992 | } |
| 1977 | 1993 | zName = p->aTag[i].zName; |
| 1978 | 1994 | zValue = p->aTag[i].zValue; |
| @@ -2042,23 +2058,23 @@ | ||
| 2042 | 2058 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2043 | 2059 | ); |
| 2044 | 2060 | blob_reset(&comment); |
| 2045 | 2061 | } |
| 2046 | 2062 | 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); | |
| 2051 | 2067 | } |
| 2052 | 2068 | } |
| 2053 | 2069 | if( p->type==CFTYPE_MANIFEST ){ |
| 2054 | 2070 | manifest_cache_insert(p); |
| 2055 | 2071 | }else{ |
| 2056 | 2072 | manifest_destroy(p); |
| 2057 | 2073 | } |
| 2058 | 2074 | assert( blob_is_reset(pContent) ); |
| 2059 | - return ( result!=TH_ERROR ); | |
| 2075 | + return ( rc!=TH_ERROR ); | |
| 2060 | 2076 | } |
| 2061 | 2077 | |
| 2062 | 2078 | /* |
| 2063 | 2079 | ** COMMAND: test-crosslink |
| 2064 | 2080 | ** |
| 2065 | 2081 |
| --- 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 @@ | ||
| 412 | 412 | db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); |
| 413 | 413 | rebuild_step_done(rid); |
| 414 | 414 | } |
| 415 | 415 | } |
| 416 | 416 | db_finalize(&s); |
| 417 | - manifest_crosslink_end(); | |
| 417 | + manifest_crosslink_end(MC_NONE); | |
| 418 | 418 | rebuild_tag_trunk(); |
| 419 | 419 | if( ttyOutput && !g.fQuiet && totalSize>0 ){ |
| 420 | 420 | processCnt += incrSize; |
| 421 | 421 | percent_complete((processCnt*1000)/totalSize); |
| 422 | 422 | } |
| 423 | 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(); |
| 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.c
+112
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | static int thEndOfLine(const char *, int); |
| 106 | 106 | |
| 107 | 107 | static int thPushFrame(Th_Interp*, Th_Frame*); |
| 108 | 108 | static void thPopFrame(Th_Interp*); |
| 109 | 109 | |
| 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*); | |
| 112 | 112 | |
| 113 | 113 | /* |
| 114 | 114 | ** The following are used by both the expression and language parsers. |
| 115 | 115 | ** Given that the start of the input string (z, n) is a language |
| 116 | 116 | ** construct of the relevant type (a command enclosed in [], an escape |
| @@ -258,12 +258,14 @@ | ||
| 258 | 258 | ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable |
| 259 | 259 | ** structure that the entry points to. Free the Th_Variable if its |
| 260 | 260 | ** reference count reaches 0. |
| 261 | 261 | ** |
| 262 | 262 | ** Argument pContext is a pointer to the interpreter structure. |
| 263 | +** | |
| 264 | +** Returns non-zero if the Th_Variable was actually freed. | |
| 263 | 265 | */ |
| 264 | -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 266 | +static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ | |
| 265 | 267 | Th_Variable *pValue = (Th_Variable *)pEntry->pData; |
| 266 | 268 | pValue->nRef--; |
| 267 | 269 | assert( pValue->nRef>=0 ); |
| 268 | 270 | if( pValue->nRef==0 ){ |
| 269 | 271 | Th_Interp *interp = (Th_Interp *)pContext; |
| @@ -271,27 +273,33 @@ | ||
| 271 | 273 | if( pValue->pHash ){ |
| 272 | 274 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); |
| 273 | 275 | Th_HashDelete(interp, pValue->pHash); |
| 274 | 276 | } |
| 275 | 277 | Th_Free(interp, pValue); |
| 278 | + pEntry->pData = 0; | |
| 279 | + return 1; | |
| 276 | 280 | } |
| 281 | + return 0; | |
| 277 | 282 | } |
| 278 | 283 | |
| 279 | 284 | /* |
| 280 | 285 | ** Argument pEntry points to an entry in the command hash table |
| 281 | 286 | ** (Th_Interp.paCmd). Delete the Th_Command structure that the |
| 282 | 287 | ** entry points to. |
| 283 | 288 | ** |
| 284 | 289 | ** Argument pContext is a pointer to the interpreter structure. |
| 290 | +** | |
| 291 | +** Always returns non-zero. | |
| 285 | 292 | */ |
| 286 | -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 293 | +static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ | |
| 287 | 294 | Th_Command *pCommand = (Th_Command *)pEntry->pData; |
| 288 | 295 | if( pCommand->xDel ){ |
| 289 | 296 | pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); |
| 290 | 297 | } |
| 291 | 298 | Th_Free((Th_Interp *)pContext, pEntry->pData); |
| 292 | 299 | pEntry->pData = 0; |
| 300 | + return 1; | |
| 293 | 301 | } |
| 294 | 302 | |
| 295 | 303 | /* |
| 296 | 304 | ** Push a new frame onto the stack. |
| 297 | 305 | */ |
| @@ -1042,10 +1050,25 @@ | ||
| 1042 | 1050 | *pnInner = nInner; |
| 1043 | 1051 | *pisGlobal = isGlobal; |
| 1044 | 1052 | return TH_OK; |
| 1045 | 1053 | } |
| 1046 | 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 | + | |
| 1047 | 1070 | /* |
| 1048 | 1071 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1049 | 1072 | ** the Th_Variable structure associated with the named variable. The |
| 1050 | 1073 | ** variable name may be a global or local scalar or array variable |
| 1051 | 1074 | ** |
| @@ -1055,17 +1078,19 @@ | ||
| 1055 | 1078 | ** |
| 1056 | 1079 | ** If the arrayok argument is false and the named variable is an array, |
| 1057 | 1080 | ** an error is left in the interpreter result and NULL returned. If |
| 1058 | 1081 | ** arrayok is true an array name is Ok. |
| 1059 | 1082 | */ |
| 1083 | + | |
| 1060 | 1084 | static Th_Variable *thFindValue( |
| 1061 | 1085 | 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 */ | |
| 1067 | 1092 | ){ |
| 1068 | 1093 | const char *zOuter; |
| 1069 | 1094 | int nOuter; |
| 1070 | 1095 | const char *zInner; |
| 1071 | 1096 | int nInner; |
| @@ -1074,16 +1099,24 @@ | ||
| 1074 | 1099 | Th_HashEntry *pEntry; |
| 1075 | 1100 | Th_Frame *pFrame = interp->pFrame; |
| 1076 | 1101 | Th_Variable *pValue; |
| 1077 | 1102 | |
| 1078 | 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 | + } | |
| 1079 | 1109 | if( isGlobal ){ |
| 1080 | 1110 | while( pFrame->pCaller ) pFrame = pFrame->pCaller; |
| 1081 | 1111 | } |
| 1082 | 1112 | |
| 1083 | 1113 | pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); |
| 1084 | 1114 | assert(pEntry || create<=0); |
| 1115 | + if( pFind ){ | |
| 1116 | + pFind->pValueEntry = pEntry; | |
| 1117 | + } | |
| 1085 | 1118 | if( !pEntry ){ |
| 1086 | 1119 | goto no_such_var; |
| 1087 | 1120 | } |
| 1088 | 1121 | |
| 1089 | 1122 | pValue = (Th_Variable *)pEntry->pData; |
| @@ -1106,10 +1139,14 @@ | ||
| 1106 | 1139 | goto no_such_var; |
| 1107 | 1140 | } |
| 1108 | 1141 | pValue->pHash = Th_HashNew(interp); |
| 1109 | 1142 | } |
| 1110 | 1143 | pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); |
| 1144 | + assert(pEntry || create<=0); | |
| 1145 | + if( pFind ){ | |
| 1146 | + pFind->pElemEntry = pEntry; | |
| 1147 | + } | |
| 1111 | 1148 | if( !pEntry ){ |
| 1112 | 1149 | goto no_such_var; |
| 1113 | 1150 | } |
| 1114 | 1151 | pValue = (Th_Variable *)pEntry->pData; |
| 1115 | 1152 | if( !pValue ){ |
| @@ -1145,11 +1182,11 @@ | ||
| 1145 | 1182 | ** an error message in the interpreter result. |
| 1146 | 1183 | */ |
| 1147 | 1184 | int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1148 | 1185 | Th_Variable *pValue; |
| 1149 | 1186 | |
| 1150 | - pValue = thFindValue(interp, zVar, nVar, 0, 0, 0); | |
| 1187 | + pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); | |
| 1151 | 1188 | if( !pValue ){ |
| 1152 | 1189 | return TH_ERROR; |
| 1153 | 1190 | } |
| 1154 | 1191 | if( !pValue->zData ){ |
| 1155 | 1192 | Th_ErrorMessage(interp, "no such variable:", zVar, nVar); |
| @@ -1161,12 +1198,12 @@ | ||
| 1161 | 1198 | |
| 1162 | 1199 | /* |
| 1163 | 1200 | ** Return true if variable (zVar, nVar) exists. |
| 1164 | 1201 | */ |
| 1165 | 1202 | 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); | |
| 1168 | 1205 | } |
| 1169 | 1206 | |
| 1170 | 1207 | /* |
| 1171 | 1208 | ** String (zVar, nVar) must contain the name of a scalar variable or |
| 1172 | 1209 | ** array member. If the variable does not exist it is created. The |
| @@ -1182,11 +1219,11 @@ | ||
| 1182 | 1219 | const char *zValue, |
| 1183 | 1220 | int nValue |
| 1184 | 1221 | ){ |
| 1185 | 1222 | Th_Variable *pValue; |
| 1186 | 1223 | |
| 1187 | - pValue = thFindValue(interp, zVar, nVar, 1, 0, 0); | |
| 1224 | + pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); | |
| 1188 | 1225 | if( !pValue ){ |
| 1189 | 1226 | return TH_ERROR; |
| 1190 | 1227 | } |
| 1191 | 1228 | |
| 1192 | 1229 | if( nValue<0 ){ |
| @@ -1225,11 +1262,11 @@ | ||
| 1225 | 1262 | if( !pFrame ){ |
| 1226 | 1263 | return TH_ERROR; |
| 1227 | 1264 | } |
| 1228 | 1265 | pSavedFrame = interp->pFrame; |
| 1229 | 1266 | interp->pFrame = pFrame; |
| 1230 | - pValue = thFindValue(interp, zLink, nLink, 1, 1, 0); | |
| 1267 | + pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); | |
| 1231 | 1268 | interp->pFrame = pSavedFrame; |
| 1232 | 1269 | |
| 1233 | 1270 | pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); |
| 1234 | 1271 | if( pEntry->pData ){ |
| 1235 | 1272 | Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); |
| @@ -1246,29 +1283,68 @@ | ||
| 1246 | 1283 | ** an array, or an array member. If the identified variable exists, it |
| 1247 | 1284 | ** is deleted and TH_OK returned. Otherwise, an error message is left |
| 1248 | 1285 | ** in the interpreter result and TH_ERROR is returned. |
| 1249 | 1286 | */ |
| 1250 | 1287 | int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1288 | + Find find; | |
| 1251 | 1289 | Th_Variable *pValue; |
| 1290 | + Th_HashEntry *pEntry; | |
| 1291 | + int rc = TH_ERROR; | |
| 1252 | 1292 | |
| 1253 | - pValue = thFindValue(interp, zVar, nVar, 0, 1, 0); | |
| 1293 | + pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); | |
| 1254 | 1294 | 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; | |
| 1270 | 1346 | } |
| 1271 | 1347 | |
| 1272 | 1348 | /* |
| 1273 | 1349 | ** Return an allocated buffer containing a copy of string (z, n). The |
| 1274 | 1350 | ** caller is responsible for eventually calling Th_Free() to free |
| @@ -2211,16 +2287,17 @@ | ||
| 2211 | 2287 | |
| 2212 | 2288 | /* |
| 2213 | 2289 | ** Iterate through all values currently stored in the hash table. Invoke |
| 2214 | 2290 | ** the callback function xCallback for each entry. The second argument |
| 2215 | 2291 | ** 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. | |
| 2217 | 2294 | */ |
| 2218 | 2295 | void Th_HashIterate( |
| 2219 | 2296 | Th_Interp *interp, |
| 2220 | 2297 | Th_Hash *pHash, |
| 2221 | - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2298 | + int (*xCallback)(Th_HashEntry *pEntry, void *pContext), | |
| 2222 | 2299 | void *pContext |
| 2223 | 2300 | ){ |
| 2224 | 2301 | int i; |
| 2225 | 2302 | for(i=0; i<TH_HASHSIZE; i++){ |
| 2226 | 2303 | Th_HashEntry *pEntry; |
| @@ -2231,14 +2308,15 @@ | ||
| 2231 | 2308 | } |
| 2232 | 2309 | } |
| 2233 | 2310 | } |
| 2234 | 2311 | |
| 2235 | 2312 | /* |
| 2236 | -** Helper function for Th_HashDelete(). | |
| 2313 | +** Helper function for Th_HashDelete(). Always returns non-zero. | |
| 2237 | 2314 | */ |
| 2238 | -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2315 | +static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ | |
| 2239 | 2316 | Th_Free((Th_Interp *)pContext, (void *)pEntry); |
| 2317 | + return 1; | |
| 2240 | 2318 | } |
| 2241 | 2319 | |
| 2242 | 2320 | /* |
| 2243 | 2321 | ** Free a hash-table previously allocated by Th_HashNew(). |
| 2244 | 2322 | */ |
| 2245 | 2323 |
| --- 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 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | int nKey; |
| 175 | 175 | Th_HashEntry *pNext; /* Internal use only */ |
| 176 | 176 | }; |
| 177 | 177 | Th_Hash *Th_HashNew(Th_Interp *); |
| 178 | 178 | 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*); | |
| 180 | 180 | Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); |
| 181 | 181 | |
| 182 | 182 | /* |
| 183 | 183 | ** Useful functions from th_lang.c. |
| 184 | 184 | */ |
| 185 | 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*,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 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | int nKey; |
| 175 | 175 | Th_HashEntry *pNext; /* Internal use only */ |
| 176 | 176 | }; |
| 177 | 177 | Th_Hash *Th_HashNew(Th_Interp *); |
| 178 | 178 | 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*); | |
| 180 | 180 | Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); |
| 181 | 181 | |
| 182 | 182 | /* |
| 183 | 183 | ** Useful functions from th_lang.c. |
| 184 | 184 | */ |
| 185 | 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*,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 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | int nKey; |
| 175 | 175 | Th_HashEntry *pNext; /* Internal use only */ |
| 176 | 176 | }; |
| 177 | 177 | Th_Hash *Th_HashNew(Th_Interp *); |
| 178 | 178 | 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*); | |
| 180 | 180 | Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); |
| 181 | 181 | |
| 182 | 182 | /* |
| 183 | 183 | ** Useful functions from th_lang.c. |
| 184 | 184 | */ |
| 185 | 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*,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 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | int nKey; |
| 175 | 175 | Th_HashEntry *pNext; /* Internal use only */ |
| 176 | 176 | }; |
| 177 | 177 | Th_Hash *Th_HashNew(Th_Interp *); |
| 178 | 178 | 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*); | |
| 180 | 180 | Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); |
| 181 | 181 | |
| 182 | 182 | /* |
| 183 | 183 | ** Useful functions from th_lang.c. |
| 184 | 184 | */ |
| 185 | 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*,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 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | int nKey; |
| 175 | 175 | Th_HashEntry *pNext; /* Internal use only */ |
| 176 | 176 | }; |
| 177 | 177 | Th_Hash *Th_HashNew(Th_Interp *); |
| 178 | 178 | 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*); | |
| 180 | 180 | Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); |
| 181 | 181 | |
| 182 | 182 | /* |
| 183 | 183 | ** Useful functions from th_lang.c. |
| 184 | 184 | */ |
| 185 | 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*,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 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | int nKey; |
| 175 | 175 | Th_HashEntry *pNext; /* Internal use only */ |
| 176 | 176 | }; |
| 177 | 177 | Th_Hash *Th_HashNew(Th_Interp *); |
| 178 | 178 | 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*); | |
| 180 | 180 | Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); |
| 181 | 181 | |
| 182 | 182 | /* |
| 183 | 183 | ** Useful functions from th_lang.c. |
| 184 | 184 | */ |
| 185 | 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*,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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 650 | 650 | ** string first NEEDLE HAYSTACK |
| 651 | 651 | */ |
| 652 | 652 | static int string_first_command( |
| 653 | 653 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 654 | 654 | ){ |
| 655 | - const char *zNeedle; | |
| 656 | 655 | int nNeedle; |
| 657 | - const char *zHaystack; | |
| 658 | 656 | int nHaystack; |
| 659 | - int i; | |
| 660 | 657 | int iRes = -1; |
| 661 | 658 | |
| 662 | 659 | if( argc!=4 ){ |
| 663 | 660 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 664 | 661 | } |
| 665 | 662 | |
| 666 | - zNeedle = argv[2]; | |
| 667 | 663 | nNeedle = argl[2]; |
| 668 | - zHaystack = argv[3]; | |
| 669 | 664 | nHaystack = argl[3]; |
| 670 | 665 | |
| 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 | + } | |
| 675 | 676 | } |
| 676 | 677 | } |
| 677 | 678 | |
| 678 | 679 | return Th_SetResultInt(interp, iRes); |
| 679 | 680 | } |
| @@ -711,30 +712,31 @@ | ||
| 711 | 712 | ** string last NEEDLE HAYSTACK |
| 712 | 713 | */ |
| 713 | 714 | static int string_last_command( |
| 714 | 715 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 715 | 716 | ){ |
| 716 | - const char *zNeedle; | |
| 717 | 717 | int nNeedle; |
| 718 | - const char *zHaystack; | |
| 719 | 718 | int nHaystack; |
| 720 | - int i; | |
| 721 | 719 | int iRes = -1; |
| 722 | 720 | |
| 723 | 721 | if( argc!=4 ){ |
| 724 | - return Th_WrongNumArgs(interp, "string first needle haystack"); | |
| 722 | + return Th_WrongNumArgs(interp, "string last needle haystack"); | |
| 725 | 723 | } |
| 726 | 724 | |
| 727 | - zNeedle = argv[2]; | |
| 728 | 725 | nNeedle = argl[2]; |
| 729 | - zHaystack = argv[3]; | |
| 730 | 726 | nHaystack = argl[3]; |
| 731 | 727 | |
| 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 | + } | |
| 736 | 738 | } |
| 737 | 739 | } |
| 738 | 740 | |
| 739 | 741 | return Th_SetResultInt(interp, iRes); |
| 740 | 742 | } |
| 741 | 743 |
| --- 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 @@ | ||
| 1011 | 1011 | } |
| 1012 | 1012 | if( forceReset || created ){ |
| 1013 | 1013 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 1014 | 1014 | } |
| 1015 | 1015 | #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) ){ | |
| 1017 | 1018 | if( !g.tcl.setup ){ |
| 1018 | 1019 | g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */ |
| 1019 | 1020 | } |
| 1020 | 1021 | th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ |
| 1021 | 1022 | } |
| 1022 | 1023 |
| --- 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 @@ | ||
| 1011 | 1011 | } |
| 1012 | 1012 | if( forceReset || created ){ |
| 1013 | 1013 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 1014 | 1014 | } |
| 1015 | 1015 | #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) ){ | |
| 1017 | 1018 | if( !g.tcl.setup ){ |
| 1018 | 1019 | g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */ |
| 1019 | 1020 | } |
| 1020 | 1021 | th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ |
| 1021 | 1022 | } |
| 1022 | 1023 |
| --- 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 @@ | ||
| 1011 | 1011 | } |
| 1012 | 1012 | if( forceReset || created ){ |
| 1013 | 1013 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 1014 | 1014 | } |
| 1015 | 1015 | #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) ){ | |
| 1017 | 1018 | if( !g.tcl.setup ){ |
| 1018 | 1019 | g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */ |
| 1019 | 1020 | } |
| 1020 | 1021 | th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ |
| 1021 | 1022 | } |
| 1022 | 1023 |
| --- 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 @@ | ||
| 1011 | 1011 | } |
| 1012 | 1012 | if( forceReset || created ){ |
| 1013 | 1013 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 1014 | 1014 | } |
| 1015 | 1015 | #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) ){ | |
| 1017 | 1018 | if( !g.tcl.setup ){ |
| 1018 | 1019 | g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */ |
| 1019 | 1020 | } |
| 1020 | 1021 | th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ |
| 1021 | 1022 | } |
| 1022 | 1023 |
| --- 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 @@ | ||
| 1011 | 1011 | } |
| 1012 | 1012 | if( forceReset || created ){ |
| 1013 | 1013 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 1014 | 1014 | } |
| 1015 | 1015 | #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) ){ | |
| 1017 | 1018 | if( !g.tcl.setup ){ |
| 1018 | 1019 | g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */ |
| 1019 | 1020 | } |
| 1020 | 1021 | th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ |
| 1021 | 1022 | } |
| 1022 | 1023 |
| --- 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 @@ | ||
| 535 | 535 | }else{ |
| 536 | 536 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 537 | 537 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 538 | 538 | } |
| 539 | 539 | manifest_crosslink_begin(); |
| 540 | - result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0); | |
| 540 | + result = (manifest_crosslink(rid, pTicket, MC_NONE)==0); | |
| 541 | 541 | 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 | + } | |
| 543 | 547 | return result; |
| 544 | 548 | } |
| 545 | 549 | |
| 546 | 550 | /* |
| 547 | 551 | ** Subscript command: submit_ticket |
| 548 | 552 |
| --- 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 @@ | ||
| 852 | 852 | |
| 853 | 853 | /* |
| 854 | 854 | ** Run the specified TH1 script, if any, and returns 1 on error. |
| 855 | 855 | */ |
| 856 | 856 | int xfer_run_script(const char *zScript, const char *zUuid){ |
| 857 | - int result; | |
| 857 | + int rc; | |
| 858 | 858 | if( !zScript ) return TH_OK; |
| 859 | 859 | Th_FossilInit(TH_INIT_DEFAULT); |
| 860 | 860 | 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 ){ | |
| 863 | 863 | fossil_error(1, "%s", Th_GetResult(g.interp, 0)); |
| 864 | - return result; | |
| 864 | + return rc; | |
| 865 | 865 | } |
| 866 | 866 | } |
| 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 ){ | |
| 869 | 869 | fossil_error(1, "%s", Th_GetResult(g.interp, 0)); |
| 870 | 870 | } |
| 871 | - return result; | |
| 871 | + return rc; | |
| 872 | 872 | } |
| 873 | 873 | |
| 874 | 874 | /* |
| 875 | 875 | ** Runs the pre-transfer TH1 script, if any, and returns its return code. |
| 876 | 876 | ** This script may be run multiple times. If the script performs actions |
| @@ -914,11 +914,11 @@ | ||
| 914 | 914 | int isClone = 0; |
| 915 | 915 | int nGimme = 0; |
| 916 | 916 | int size; |
| 917 | 917 | int recvConfig = 0; |
| 918 | 918 | char *zNow; |
| 919 | - int result; | |
| 919 | + int rc; | |
| 920 | 920 | |
| 921 | 921 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 922 | 922 | fossil_redirect_home(); |
| 923 | 923 | } |
| 924 | 924 | g.zLogin = "anonymous"; |
| @@ -944,12 +944,12 @@ | ||
| 944 | 944 | db_begin_transaction(); |
| 945 | 945 | db_multi_exec( |
| 946 | 946 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 947 | 947 | ); |
| 948 | 948 | 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 ){ | |
| 951 | 951 | cgi_reset_content(); |
| 952 | 952 | @ error common\sscript\sfailed:\s%F(g.zErrMsg) |
| 953 | 953 | nErr++; |
| 954 | 954 | } |
| 955 | 955 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| @@ -1272,13 +1272,13 @@ | ||
| 1272 | 1272 | } |
| 1273 | 1273 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1274 | 1274 | blob_reset(&xfer.line); |
| 1275 | 1275 | } |
| 1276 | 1276 | 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 ){ | |
| 1280 | 1280 | cgi_reset_content(); |
| 1281 | 1281 | @ error push\sscript\sfailed:\s%F(g.zErrMsg) |
| 1282 | 1282 | nErr++; |
| 1283 | 1283 | } |
| 1284 | 1284 | } |
| @@ -1301,11 +1301,11 @@ | ||
| 1301 | 1301 | } |
| 1302 | 1302 | if( recvConfig ){ |
| 1303 | 1303 | configure_finalize_receive(); |
| 1304 | 1304 | } |
| 1305 | 1305 | db_multi_exec("DROP TABLE onremote"); |
| 1306 | - manifest_crosslink_end(); | |
| 1306 | + manifest_crosslink_end(MC_PERMIT_HOOKS); | |
| 1307 | 1307 | |
| 1308 | 1308 | /* Send the server timestamp last, in case prior processing happened |
| 1309 | 1309 | ** to use up a significant fraction of our time window. |
| 1310 | 1310 | */ |
| 1311 | 1311 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| @@ -1928,10 +1928,10 @@ | ||
| 1928 | 1928 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1929 | 1929 | zOpType, nSent, nRcvd); |
| 1930 | 1930 | transport_close(GLOBAL_URL()); |
| 1931 | 1931 | transport_global_shutdown(GLOBAL_URL()); |
| 1932 | 1932 | db_multi_exec("DROP TABLE onremote"); |
| 1933 | - manifest_crosslink_end(); | |
| 1933 | + manifest_crosslink_end(MC_PERMIT_HOOKS); | |
| 1934 | 1934 | content_enable_dephantomize(1); |
| 1935 | 1935 | db_end_transaction(0); |
| 1936 | 1936 | return nErr; |
| 1937 | 1937 | } |
| 1938 | 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 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 @@ | ||
| 140 | 140 | fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" |
| 141 | 141 | test th1-info-exists-6 {$RESULT eq {bad}} |
| 142 | 142 | |
| 143 | 143 | ############################################################################### |
| 144 | 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 | + | |
| 145 | 175 | fossil test-th-eval "set var 1; unset var" |
| 146 | 176 | test th1-unset-1 {$RESULT eq {var}} |
| 147 | 177 | |
| 148 | 178 | ############################################################################### |
| 149 | 179 | |
| @@ -152,5 +182,120 @@ | ||
| 152 | 182 | |
| 153 | 183 | ############################################################################### |
| 154 | 184 | |
| 155 | 185 | fossil test-th-eval "set var 1; unset var; unset var" |
| 156 | 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}} | |
| 157 | 302 |
| --- 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 |
+6
-4
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -39,16 +39,18 @@ | ||
| 39 | 39 | |
| 40 | 40 | !ifdef FOSSIL_ENABLE_SSL |
| 41 | 41 | INCL = $(INCL) -I$(SSLINCDIR) |
| 42 | 42 | !endif |
| 43 | 43 | |
| 44 | -CFLAGS = -nologo -MT -O2 | |
| 44 | +CFLAGS = -nologo | |
| 45 | 45 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 46 | 46 | |
| 47 | 47 | !ifdef DEBUG |
| 48 | -CFLAGS = $(CFLAGS) -Zi | |
| 48 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 49 | 49 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 50 | +!else | |
| 51 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 50 | 52 | !endif |
| 51 | 53 | |
| 52 | 54 | BCC = $(CC) $(CFLAGS) |
| 53 | 55 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 54 | 56 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -448,14 +450,14 @@ | ||
| 448 | 450 | $(BCC) $** |
| 449 | 451 | |
| 450 | 452 | mkversion$E: $B\src\mkversion.c |
| 451 | 453 | $(BCC) $** |
| 452 | 454 | |
| 453 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 455 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 454 | 456 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 455 | 457 | |
| 456 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 458 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 457 | 459 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 458 | 460 | |
| 459 | 461 | $(OX)\th$O : $(SRCDIR)\th.c |
| 460 | 462 | $(TCC) /Fo$@ -c $** |
| 461 | 463 | |
| 462 | 464 |
| --- 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 |
+6
-4
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -39,16 +39,18 @@ | ||
| 39 | 39 | |
| 40 | 40 | !ifdef FOSSIL_ENABLE_SSL |
| 41 | 41 | INCL = $(INCL) -I$(SSLINCDIR) |
| 42 | 42 | !endif |
| 43 | 43 | |
| 44 | -CFLAGS = -nologo -MT -O2 | |
| 44 | +CFLAGS = -nologo | |
| 45 | 45 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 46 | 46 | |
| 47 | 47 | !ifdef DEBUG |
| 48 | -CFLAGS = $(CFLAGS) -Zi | |
| 48 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 49 | 49 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 50 | +!else | |
| 51 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 50 | 52 | !endif |
| 51 | 53 | |
| 52 | 54 | BCC = $(CC) $(CFLAGS) |
| 53 | 55 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 54 | 56 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -448,14 +450,14 @@ | ||
| 448 | 450 | $(BCC) $** |
| 449 | 451 | |
| 450 | 452 | mkversion$E: $B\src\mkversion.c |
| 451 | 453 | $(BCC) $** |
| 452 | 454 | |
| 453 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 455 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 454 | 456 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 455 | 457 | |
| 456 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 458 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 457 | 459 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 458 | 460 | |
| 459 | 461 | $(OX)\th$O : $(SRCDIR)\th.c |
| 460 | 462 | $(TCC) /Fo$@ -c $** |
| 461 | 463 | |
| 462 | 464 |
| --- 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 |
+6
-4
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -39,16 +39,18 @@ | ||
| 39 | 39 | |
| 40 | 40 | !ifdef FOSSIL_ENABLE_SSL |
| 41 | 41 | INCL = $(INCL) -I$(SSLINCDIR) |
| 42 | 42 | !endif |
| 43 | 43 | |
| 44 | -CFLAGS = -nologo -MT -O2 | |
| 44 | +CFLAGS = -nologo | |
| 45 | 45 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 46 | 46 | |
| 47 | 47 | !ifdef DEBUG |
| 48 | -CFLAGS = $(CFLAGS) -Zi | |
| 48 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 49 | 49 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 50 | +!else | |
| 51 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 50 | 52 | !endif |
| 51 | 53 | |
| 52 | 54 | BCC = $(CC) $(CFLAGS) |
| 53 | 55 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 54 | 56 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -448,14 +450,14 @@ | ||
| 448 | 450 | $(BCC) $** |
| 449 | 451 | |
| 450 | 452 | mkversion$E: $B\src\mkversion.c |
| 451 | 453 | $(BCC) $** |
| 452 | 454 | |
| 453 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 455 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 454 | 456 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 455 | 457 | |
| 456 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 458 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 457 | 459 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 458 | 460 | |
| 459 | 461 | $(OX)\th$O : $(SRCDIR)\th.c |
| 460 | 462 | $(TCC) /Fo$@ -c $** |
| 461 | 463 | |
| 462 | 464 |
| --- 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 |
+6
-4
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -39,16 +39,18 @@ | ||
| 39 | 39 | |
| 40 | 40 | !ifdef FOSSIL_ENABLE_SSL |
| 41 | 41 | INCL = $(INCL) -I$(SSLINCDIR) |
| 42 | 42 | !endif |
| 43 | 43 | |
| 44 | -CFLAGS = -nologo -MT -O2 | |
| 44 | +CFLAGS = -nologo | |
| 45 | 45 | LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO |
| 46 | 46 | |
| 47 | 47 | !ifdef DEBUG |
| 48 | -CFLAGS = $(CFLAGS) -Zi | |
| 48 | +CFLAGS = $(CFLAGS) -Zi -MTd -Od | |
| 49 | 49 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 50 | +!else | |
| 51 | +CFLAGS = $(CFLAGS) -MT -O2 | |
| 50 | 52 | !endif |
| 51 | 53 | |
| 52 | 54 | BCC = $(CC) $(CFLAGS) |
| 53 | 55 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(INCL) |
| 54 | 56 | RCC = rc -D_WIN32 -D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -448,14 +450,14 @@ | ||
| 448 | 450 | $(BCC) $** |
| 449 | 451 | |
| 450 | 452 | mkversion$E: $B\src\mkversion.c |
| 451 | 453 | $(BCC) $** |
| 452 | 454 | |
| 453 | -$(OX)\shell$O : $(SRCDIR)\shell.c Makefile.msc | |
| 455 | +$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc | |
| 454 | 456 | $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c |
| 455 | 457 | |
| 456 | -$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c Makefile.msc | |
| 458 | +$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc | |
| 457 | 459 | $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c |
| 458 | 460 | |
| 459 | 461 | $(OX)\th$O : $(SRCDIR)\th.c |
| 460 | 462 | $(TCC) /Fo$@ -c $** |
| 461 | 463 | |
| 462 | 464 |
| --- 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 |