Fossil SCM

Fix several issues with the TH1 unset command, including a memory leak. Add more tests.

mistachkin 2014-01-14 12:50 UTC trunk
Commit e4047acb76d90c643fd36c6a13377cc938d30179
3 files changed +100 -34 +1 -1 +35
+100 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1055,17 +1063,26 @@
10551063
**
10561064
** If the arrayok argument is false and the named variable is an array,
10571065
** an error is left in the interpreter result and NULL returned. If
10581066
** arrayok is true an array name is Ok.
10591067
*/
1068
+struct Find {
1069
+ Th_HashEntry *pValueEntry;
1070
+ Th_HashEntry *pElemEntry;
1071
+ const char *zElem;
1072
+ int nElem;
1073
+};
1074
+typedef struct Find Find;
1075
+
10601076
static Th_Variable *thFindValue(
10611077
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 */
1078
+ const char *zVar, /* Pointer to variable name */
1079
+ int nVar, /* Number of bytes at nVar */
1080
+ int create, /* If true, create the variable if not found */
1081
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1082
+ int noerror, /* If false, set interpreter result to error */
1083
+ Find *pFind /* If non-zero, place output here */
10671084
){
10681085
const char *zOuter;
10691086
int nOuter;
10701087
const char *zInner;
10711088
int nInner;
@@ -1074,16 +1091,23 @@
10741091
Th_HashEntry *pEntry;
10751092
Th_Frame *pFrame = interp->pFrame;
10761093
Th_Variable *pValue;
10771094
10781095
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1096
+ if( pFind ){
1097
+ pFind->zElem = zInner;
1098
+ pFind->nElem = nInner;
1099
+ }
10791100
if( isGlobal ){
10801101
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811102
}
10821103
10831104
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841105
assert(pEntry || create<=0);
1106
+ if( pFind ){
1107
+ pFind->pValueEntry = pEntry;
1108
+ }
10851109
if( !pEntry ){
10861110
goto no_such_var;
10871111
}
10881112
10891113
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1130,14 @@
11061130
goto no_such_var;
11071131
}
11081132
pValue->pHash = Th_HashNew(interp);
11091133
}
11101134
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1135
+ assert(pEntry || create<=0);
1136
+ if( pFind ){
1137
+ pFind->pElemEntry = pEntry;
1138
+ }
11111139
if( !pEntry ){
11121140
goto no_such_var;
11131141
}
11141142
pValue = (Th_Variable *)pEntry->pData;
11151143
if( !pValue ){
@@ -1145,11 +1173,11 @@
11451173
** an error message in the interpreter result.
11461174
*/
11471175
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481176
Th_Variable *pValue;
11491177
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1178
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511179
if( !pValue ){
11521180
return TH_ERROR;
11531181
}
11541182
if( !pValue->zData ){
11551183
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1189,12 @@
11611189
11621190
/*
11631191
** Return true if variable (zVar, nVar) exists.
11641192
*/
11651193
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;
1194
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1195
+ return pValue && (pValue->zData || pValue->pHash);
11681196
}
11691197
11701198
/*
11711199
** String (zVar, nVar) must contain the name of a scalar variable or
11721200
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1210,11 @@
11821210
const char *zValue,
11831211
int nValue
11841212
){
11851213
Th_Variable *pValue;
11861214
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1215
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881216
if( !pValue ){
11891217
return TH_ERROR;
11901218
}
11911219
11921220
if( nValue<0 ){
@@ -1225,11 +1253,11 @@
12251253
if( !pFrame ){
12261254
return TH_ERROR;
12271255
}
12281256
pSavedFrame = interp->pFrame;
12291257
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1258
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311259
interp->pFrame = pSavedFrame;
12321260
12331261
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341262
if( pEntry->pData ){
12351263
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1274,65 @@
12461274
** an array, or an array member. If the identified variable exists, it
12471275
** is deleted and TH_OK returned. Otherwise, an error message is left
12481276
** in the interpreter result and TH_ERROR is returned.
12491277
*/
12501278
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1279
+ Find find;
12511280
Th_Variable *pValue;
1281
+ Th_HashEntry *pEntry;
1282
+ int rc = TH_ERROR;
12521283
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1284
+ memset(&find, 0, sizeof(Find));
1285
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541286
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;
1287
+ return rc;
1288
+ }
1289
+
1290
+ if( pValue->zData || pValue->pHash ){
1291
+ rc = TH_OK;
1292
+ }else {
1293
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1294
+ }
1295
+
1296
+ /*
1297
+ ** The variable may be shared by more than one frame; therefore, make sure
1298
+ ** it is actually freed prior to freeing the parent structure. The values
1299
+ ** for the variable must be freed now so the variable appears undefined in
1300
+ ** all frames. The hash entry in the current frame must also be deleted
1301
+ ** now; otherwise, if the current stack frame is later popped, it will try
1302
+ ** to delete a variable which has already been freed.
1303
+ */
1304
+ if( find.zElem ){
1305
+ pEntry = find.pElemEntry;
1306
+ }else{
1307
+ pEntry = find.pValueEntry;
1308
+ }
1309
+ assert( pEntry );
1310
+ assert( pValue );
1311
+ if( thFreeVariable(pEntry, (void *)interp) ){
1312
+ if( find.zElem ){
1313
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1314
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1315
+ }else{
1316
+ Th_Free(interp, pEntry->pData);
1317
+ pEntry->pData = 0;
1318
+ }
1319
+ }else{
1320
+ if( pValue->zData ){
1321
+ Th_Free(interp, pValue->zData);
1322
+ pValue->zData = 0;
1323
+ }
1324
+ if( pValue->pHash ){
1325
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1326
+ Th_HashDelete(interp, pValue->pHash);
1327
+ pValue->pHash = 0;
1328
+ }
1329
+ }
1330
+ if( !find.zElem ){
1331
+ thFindValue(interp, zVar, nVar, -1, 1, 1, 0);
1332
+ }
1333
+ return rc;
12701334
}
12711335
12721336
/*
12731337
** Return an allocated buffer containing a copy of string (z, n). The
12741338
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2275,17 @@
22112275
22122276
/*
22132277
** Iterate through all values currently stored in the hash table. Invoke
22142278
** the callback function xCallback for each entry. The second argument
22152279
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2280
+** function. The return value from the callback function xCallback is
2281
+** ignored.
22172282
*/
22182283
void Th_HashIterate(
22192284
Th_Interp *interp,
22202285
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2286
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222287
void *pContext
22232288
){
22242289
int i;
22252290
for(i=0; i<TH_HASHSIZE; i++){
22262291
Th_HashEntry *pEntry;
@@ -2231,14 +2296,15 @@
22312296
}
22322297
}
22332298
}
22342299
22352300
/*
2236
-** Helper function for Th_HashDelete().
2301
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372302
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2303
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392304
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2305
+ return 1;
22402306
}
22412307
22422308
/*
22432309
** Free a hash-table previously allocated by Th_HashNew().
22442310
*/
22452311
--- 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 */
@@ -1055,17 +1063,26 @@
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 +1091,23 @@
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 +1130,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 +1173,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 +1189,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 +1210,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 +1253,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 +1274,65 @@
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 +2275,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 +2296,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 */
@@ -1055,17 +1063,26 @@
1063 **
1064 ** If the arrayok argument is false and the named variable is an array,
1065 ** an error is left in the interpreter result and NULL returned. If
1066 ** arrayok is true an array name is Ok.
1067 */
1068 struct Find {
1069 Th_HashEntry *pValueEntry;
1070 Th_HashEntry *pElemEntry;
1071 const char *zElem;
1072 int nElem;
1073 };
1074 typedef struct Find Find;
1075
1076 static Th_Variable *thFindValue(
1077 Th_Interp *interp,
1078 const char *zVar, /* Pointer to variable name */
1079 int nVar, /* Number of bytes at nVar */
1080 int create, /* If true, create the variable if not found */
1081 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1082 int noerror, /* If false, set interpreter result to error */
1083 Find *pFind /* If non-zero, place output here */
1084 ){
1085 const char *zOuter;
1086 int nOuter;
1087 const char *zInner;
1088 int nInner;
@@ -1074,16 +1091,23 @@
1091 Th_HashEntry *pEntry;
1092 Th_Frame *pFrame = interp->pFrame;
1093 Th_Variable *pValue;
1094
1095 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1096 if( pFind ){
1097 pFind->zElem = zInner;
1098 pFind->nElem = nInner;
1099 }
1100 if( isGlobal ){
1101 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1102 }
1103
1104 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1105 assert(pEntry || create<=0);
1106 if( pFind ){
1107 pFind->pValueEntry = pEntry;
1108 }
1109 if( !pEntry ){
1110 goto no_such_var;
1111 }
1112
1113 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1130,14 @@
1130 goto no_such_var;
1131 }
1132 pValue->pHash = Th_HashNew(interp);
1133 }
1134 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1135 assert(pEntry || create<=0);
1136 if( pFind ){
1137 pFind->pElemEntry = pEntry;
1138 }
1139 if( !pEntry ){
1140 goto no_such_var;
1141 }
1142 pValue = (Th_Variable *)pEntry->pData;
1143 if( !pValue ){
@@ -1145,11 +1173,11 @@
1173 ** an error message in the interpreter result.
1174 */
1175 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1176 Th_Variable *pValue;
1177
1178 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
1179 if( !pValue ){
1180 return TH_ERROR;
1181 }
1182 if( !pValue->zData ){
1183 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1189,12 @@
1189
1190 /*
1191 ** Return true if variable (zVar, nVar) exists.
1192 */
1193 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1194 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1195 return pValue && (pValue->zData || pValue->pHash);
1196 }
1197
1198 /*
1199 ** String (zVar, nVar) must contain the name of a scalar variable or
1200 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1210,11 @@
1210 const char *zValue,
1211 int nValue
1212 ){
1213 Th_Variable *pValue;
1214
1215 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
1216 if( !pValue ){
1217 return TH_ERROR;
1218 }
1219
1220 if( nValue<0 ){
@@ -1225,11 +1253,11 @@
1253 if( !pFrame ){
1254 return TH_ERROR;
1255 }
1256 pSavedFrame = interp->pFrame;
1257 interp->pFrame = pFrame;
1258 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
1259 interp->pFrame = pSavedFrame;
1260
1261 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1262 if( pEntry->pData ){
1263 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1274,65 @@
1274 ** an array, or an array member. If the identified variable exists, it
1275 ** is deleted and TH_OK returned. Otherwise, an error message is left
1276 ** in the interpreter result and TH_ERROR is returned.
1277 */
1278 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1279 Find find;
1280 Th_Variable *pValue;
1281 Th_HashEntry *pEntry;
1282 int rc = TH_ERROR;
1283
1284 memset(&find, 0, sizeof(Find));
1285 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
1286 if( !pValue ){
1287 return rc;
1288 }
1289
1290 if( pValue->zData || pValue->pHash ){
1291 rc = TH_OK;
1292 }else {
1293 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1294 }
1295
1296 /*
1297 ** The variable may be shared by more than one frame; therefore, make sure
1298 ** it is actually freed prior to freeing the parent structure. The values
1299 ** for the variable must be freed now so the variable appears undefined in
1300 ** all frames. The hash entry in the current frame must also be deleted
1301 ** now; otherwise, if the current stack frame is later popped, it will try
1302 ** to delete a variable which has already been freed.
1303 */
1304 if( find.zElem ){
1305 pEntry = find.pElemEntry;
1306 }else{
1307 pEntry = find.pValueEntry;
1308 }
1309 assert( pEntry );
1310 assert( pValue );
1311 if( thFreeVariable(pEntry, (void *)interp) ){
1312 if( find.zElem ){
1313 Th_Variable *pValue2 = find.pValueEntry->pData;
1314 Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1315 }else{
1316 Th_Free(interp, pEntry->pData);
1317 pEntry->pData = 0;
1318 }
1319 }else{
1320 if( pValue->zData ){
1321 Th_Free(interp, pValue->zData);
1322 pValue->zData = 0;
1323 }
1324 if( pValue->pHash ){
1325 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1326 Th_HashDelete(interp, pValue->pHash);
1327 pValue->pHash = 0;
1328 }
1329 }
1330 if( !find.zElem ){
1331 thFindValue(interp, zVar, nVar, -1, 1, 1, 0);
1332 }
1333 return rc;
1334 }
1335
1336 /*
1337 ** Return an allocated buffer containing a copy of string (z, n). The
1338 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2275,17 @@
2275
2276 /*
2277 ** Iterate through all values currently stored in the hash table. Invoke
2278 ** the callback function xCallback for each entry. The second argument
2279 ** passed to xCallback is a copy of the fourth argument passed to this
2280 ** function. The return value from the callback function xCallback is
2281 ** ignored.
2282 */
2283 void Th_HashIterate(
2284 Th_Interp *interp,
2285 Th_Hash *pHash,
2286 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2287 void *pContext
2288 ){
2289 int i;
2290 for(i=0; i<TH_HASHSIZE; i++){
2291 Th_HashEntry *pEntry;
@@ -2231,14 +2296,15 @@
2296 }
2297 }
2298 }
2299
2300 /*
2301 ** Helper function for Th_HashDelete(). Always returns non-zero.
2302 */
2303 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2304 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2305 return 1;
2306 }
2307
2308 /*
2309 ** Free a hash-table previously allocated by Th_HashNew().
2310 */
2311
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- test/th1.test
+++ test/th1.test
@@ -140,10 +140,40 @@
140140
fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace"
141141
test th1-info-exists-6 {$RESULT eq {bad}}
142142
143143
###############################################################################
144144
145
+fossil test-th-eval "set var(1) 1; info exists var"
146
+test th1-info-exists-7 {$RESULT eq {1}}
147
+
148
+###############################################################################
149
+
150
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var"
151
+test th1-info-exists-8 {$RESULT eq {1}}
152
+
153
+###############################################################################
154
+
155
+fossil test-th-eval "set var(1) 1; unset var; info exists var"
156
+test th1-info-exists-9 {$RESULT eq {0}}
157
+
158
+###############################################################################
159
+
160
+fossil test-th-eval "set var(1) 1; info exists var(1)"
161
+test th1-info-exists-10 {$RESULT eq {1}}
162
+
163
+###############################################################################
164
+
165
+fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)"
166
+test th1-info-exists-11 {$RESULT eq {0}}
167
+
168
+###############################################################################
169
+
170
+fossil test-th-eval "set var(1) 1; unset var; info exists var(1)"
171
+test th1-info-exists-12 {$RESULT eq {0}}
172
+
173
+###############################################################################
174
+
145175
fossil test-th-eval "set var 1; unset var"
146176
test th1-unset-1 {$RESULT eq {var}}
147177
148178
###############################################################################
149179
@@ -152,5 +182,10 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
157192
--- 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,10 @@
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,10 @@
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

Keyboard Shortcuts

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