Fossil SCM

Fix several issues with the TH1 unset command, including a memory leak. Add more tests. Keep the original branch open in case further changes are needed.

drh 2014-01-14 16:08 trunk merge
Commit 1aeb2726b02778874ff21a2e9cb54d1a7ebe9710
3 files changed +112 -34 +1 -1 +55
+112 -34
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105105
static int thEndOfLine(const char *, int);
106106
107107
static int thPushFrame(Th_Interp*, Th_Frame*);
108108
static void thPopFrame(Th_Interp*);
109109
110
-static void thFreeVariable(Th_HashEntry*, void*);
111
-static void thFreeCommand(Th_HashEntry*, void*);
110
+static int thFreeVariable(Th_HashEntry*, void*);
111
+static int thFreeCommand(Th_HashEntry*, void*);
112112
113113
/*
114114
** The following are used by both the expression and language parsers.
115115
** Given that the start of the input string (z, n) is a language
116116
** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258258
** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259259
** structure that the entry points to. Free the Th_Variable if its
260260
** reference count reaches 0.
261261
**
262262
** Argument pContext is a pointer to the interpreter structure.
263
+**
264
+** Returns non-zero if the Th_Variable was actually freed.
263265
*/
264
-static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
266
+static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265267
Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266268
pValue->nRef--;
267269
assert( pValue->nRef>=0 );
268270
if( pValue->nRef==0 ){
269271
Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271273
if( pValue->pHash ){
272274
Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273275
Th_HashDelete(interp, pValue->pHash);
274276
}
275277
Th_Free(interp, pValue);
278
+ pEntry->pData = 0;
279
+ return 1;
276280
}
281
+ return 0;
277282
}
278283
279284
/*
280285
** Argument pEntry points to an entry in the command hash table
281286
** (Th_Interp.paCmd). Delete the Th_Command structure that the
282287
** entry points to.
283288
**
284289
** Argument pContext is a pointer to the interpreter structure.
290
+**
291
+** Always returns non-zero.
285292
*/
286
-static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
293
+static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287294
Th_Command *pCommand = (Th_Command *)pEntry->pData;
288295
if( pCommand->xDel ){
289296
pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290297
}
291298
Th_Free((Th_Interp *)pContext, pEntry->pData);
292299
pEntry->pData = 0;
300
+ return 1;
293301
}
294302
295303
/*
296304
** Push a new frame onto the stack.
297305
*/
@@ -1042,10 +1050,25 @@
10421050
*pnInner = nInner;
10431051
*pisGlobal = isGlobal;
10441052
return TH_OK;
10451053
}
10461054
1055
+/*
1056
+** The Find structure is used to return extra information to callers of the
1057
+** thFindValue function. The fields within it are populated by thFindValue
1058
+** as soon as the necessary information is available. Callers should check
1059
+** each field of interest upon return.
1060
+*/
1061
+
1062
+struct Find {
1063
+ Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064
+ Th_HashEntry *pElemEntry; /* Pointer to the element hash entry, if any */
1065
+ const char *zElem; /* Name of array element, if applicable */
1066
+ int nElem; /* Length of array element name, if applicable */
1067
+};
1068
+typedef struct Find Find;
1069
+
10471070
/*
10481071
** Input string (zVar, nVar) contains a variable name. This function locates
10491072
** the Th_Variable structure associated with the named variable. The
10501073
** variable name may be a global or local scalar or array variable
10511074
**
@@ -1055,17 +1078,19 @@
10551078
**
10561079
** If the arrayok argument is false and the named variable is an array,
10571080
** an error is left in the interpreter result and NULL returned. If
10581081
** arrayok is true an array name is Ok.
10591082
*/
1083
+
10601084
static Th_Variable *thFindValue(
10611085
Th_Interp *interp,
1062
- const char *zVar, /* Pointer to variable name */
1063
- int nVar, /* Number of bytes at nVar */
1064
- int create, /* If true, create the variable if not found */
1065
- int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066
- int noerror /* If false, set interpreter result to error message */
1086
+ const char *zVar, /* Pointer to variable name */
1087
+ int nVar, /* Number of bytes at nVar */
1088
+ int create, /* If true, create the variable if not found */
1089
+ int arrayok, /* If true, an array is Ok. Otherwise array==error */
1090
+ int noerror, /* If false, set interpreter result to error */
1091
+ Find *pFind /* If non-zero, place output here */
10671092
){
10681093
const char *zOuter;
10691094
int nOuter;
10701095
const char *zInner;
10711096
int nInner;
@@ -1074,16 +1099,24 @@
10741099
Th_HashEntry *pEntry;
10751100
Th_Frame *pFrame = interp->pFrame;
10761101
Th_Variable *pValue;
10771102
10781103
thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
1104
+ if( pFind ){
1105
+ memset(pFind, 0, sizeof(Find));
1106
+ pFind->zElem = zInner;
1107
+ pFind->nElem = nInner;
1108
+ }
10791109
if( isGlobal ){
10801110
while( pFrame->pCaller ) pFrame = pFrame->pCaller;
10811111
}
10821112
10831113
pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
10841114
assert(pEntry || create<=0);
1115
+ if( pFind ){
1116
+ pFind->pValueEntry = pEntry;
1117
+ }
10851118
if( !pEntry ){
10861119
goto no_such_var;
10871120
}
10881121
10891122
pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
11061139
goto no_such_var;
11071140
}
11081141
pValue->pHash = Th_HashNew(interp);
11091142
}
11101143
pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
1144
+ assert(pEntry || create<=0);
1145
+ if( pFind ){
1146
+ pFind->pElemEntry = pEntry;
1147
+ }
11111148
if( !pEntry ){
11121149
goto no_such_var;
11131150
}
11141151
pValue = (Th_Variable *)pEntry->pData;
11151152
if( !pValue ){
@@ -1145,11 +1182,11 @@
11451182
** an error message in the interpreter result.
11461183
*/
11471184
int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
11481185
Th_Variable *pValue;
11491186
1150
- pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1187
+ pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0);
11511188
if( !pValue ){
11521189
return TH_ERROR;
11531190
}
11541191
if( !pValue->zData ){
11551192
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
11611198
11621199
/*
11631200
** Return true if variable (zVar, nVar) exists.
11641201
*/
11651202
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166
- Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167
- return pValue && pValue->zData;
1203
+ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
1204
+ return pValue && (pValue->zData || pValue->pHash);
11681205
}
11691206
11701207
/*
11711208
** String (zVar, nVar) must contain the name of a scalar variable or
11721209
** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
11821219
const char *zValue,
11831220
int nValue
11841221
){
11851222
Th_Variable *pValue;
11861223
1187
- pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1224
+ pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
11881225
if( !pValue ){
11891226
return TH_ERROR;
11901227
}
11911228
11921229
if( nValue<0 ){
@@ -1225,11 +1262,11 @@
12251262
if( !pFrame ){
12261263
return TH_ERROR;
12271264
}
12281265
pSavedFrame = interp->pFrame;
12291266
interp->pFrame = pFrame;
1230
- pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1267
+ pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0);
12311268
interp->pFrame = pSavedFrame;
12321269
12331270
pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
12341271
if( pEntry->pData ){
12351272
Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
12461283
** an array, or an array member. If the identified variable exists, it
12471284
** is deleted and TH_OK returned. Otherwise, an error message is left
12481285
** in the interpreter result and TH_ERROR is returned.
12491286
*/
12501287
int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
1288
+ Find find;
12511289
Th_Variable *pValue;
1290
+ Th_HashEntry *pEntry;
1291
+ int rc = TH_ERROR;
12521292
1253
- pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1293
+ pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find);
12541294
if( !pValue ){
1255
- return TH_ERROR;
1256
- }
1257
-
1258
- if( pValue->zData ){
1259
- Th_Free(interp, pValue->zData);
1260
- pValue->zData = 0;
1261
- }
1262
- if( pValue->pHash ){
1263
- Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264
- Th_HashDelete(interp, pValue->pHash);
1265
- pValue->pHash = 0;
1266
- }
1267
-
1268
- thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269
- return TH_OK;
1295
+ return rc;
1296
+ }
1297
+
1298
+ if( pValue->zData || pValue->pHash ){
1299
+ rc = TH_OK;
1300
+ }else {
1301
+ Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
1302
+ }
1303
+
1304
+ /*
1305
+ ** The variable may be shared by more than one frame; therefore, make sure
1306
+ ** it is actually freed prior to freeing the parent structure. The values
1307
+ ** for the variable must be freed now so the variable appears undefined in
1308
+ ** all frames. The hash entry in the current frame must also be deleted
1309
+ ** now; otherwise, if the current stack frame is later popped, it will try
1310
+ ** to delete a variable which has already been freed.
1311
+ */
1312
+ if( find.zElem ){
1313
+ pEntry = find.pElemEntry;
1314
+ }else{
1315
+ pEntry = find.pValueEntry;
1316
+ }
1317
+ assert( pEntry );
1318
+ assert( pValue );
1319
+ if( thFreeVariable(pEntry, (void *)interp) ){
1320
+ if( find.zElem ){
1321
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1322
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1323
+ }else if( pEntry->pData ){
1324
+ Th_Free(interp, pEntry->pData);
1325
+ pEntry->pData = 0;
1326
+ }
1327
+ }else{
1328
+ if( pValue->zData ){
1329
+ Th_Free(interp, pValue->zData);
1330
+ pValue->zData = 0;
1331
+ }
1332
+ if( pValue->pHash ){
1333
+ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1334
+ Th_HashDelete(interp, pValue->pHash);
1335
+ pValue->pHash = 0;
1336
+ }
1337
+ if( find.zElem ){
1338
+ Th_Variable *pValue2 = find.pValueEntry->pData;
1339
+ Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1);
1340
+ }
1341
+ }
1342
+ if( !find.zElem ){
1343
+ thFindValue(interp, zVar, nVar, -1, 1, 1, 0);
1344
+ }
1345
+ return rc;
12701346
}
12711347
12721348
/*
12731349
** Return an allocated buffer containing a copy of string (z, n). The
12741350
** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
22112287
22122288
/*
22132289
** Iterate through all values currently stored in the hash table. Invoke
22142290
** the callback function xCallback for each entry. The second argument
22152291
** passed to xCallback is a copy of the fourth argument passed to this
2216
-** function.
2292
+** function. The return value from the callback function xCallback is
2293
+** ignored.
22172294
*/
22182295
void Th_HashIterate(
22192296
Th_Interp *interp,
22202297
Th_Hash *pHash,
2221
- void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2298
+ int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
22222299
void *pContext
22232300
){
22242301
int i;
22252302
for(i=0; i<TH_HASHSIZE; i++){
22262303
Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
22312308
}
22322309
}
22332310
}
22342311
22352312
/*
2236
-** Helper function for Th_HashDelete().
2313
+** Helper function for Th_HashDelete(). Always returns non-zero.
22372314
*/
2238
-static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2315
+static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
22392316
Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317
+ return 1;
22402318
}
22412319
22422320
/*
22432321
** Free a hash-table previously allocated by Th_HashNew().
22442322
*/
22452323
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static void thFreeVariable(Th_HashEntry*, void*);
111 static void thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
 
 
263 */
264 static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){
265 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
266 pValue->nRef--;
267 assert( pValue->nRef>=0 );
268 if( pValue->nRef==0 ){
269 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
271 if( pValue->pHash ){
272 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
273 Th_HashDelete(interp, pValue->pHash);
274 }
275 Th_Free(interp, pValue);
 
 
276 }
 
277 }
278
279 /*
280 ** Argument pEntry points to an entry in the command hash table
281 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
282 ** entry points to.
283 **
284 ** Argument pContext is a pointer to the interpreter structure.
 
 
285 */
286 static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){
287 Th_Command *pCommand = (Th_Command *)pEntry->pData;
288 if( pCommand->xDel ){
289 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
290 }
291 Th_Free((Th_Interp *)pContext, pEntry->pData);
292 pEntry->pData = 0;
 
293 }
294
295 /*
296 ** Push a new frame onto the stack.
297 */
@@ -1042,10 +1050,25 @@
1042 *pnInner = nInner;
1043 *pisGlobal = isGlobal;
1044 return TH_OK;
1045 }
1046
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1047 /*
1048 ** Input string (zVar, nVar) contains a variable name. This function locates
1049 ** the Th_Variable structure associated with the named variable. The
1050 ** variable name may be a global or local scalar or array variable
1051 **
@@ -1055,17 +1078,19 @@
1055 **
1056 ** If the arrayok argument is false and the named variable is an array,
1057 ** an error is left in the interpreter result and NULL returned. If
1058 ** arrayok is true an array name is Ok.
1059 */
 
1060 static Th_Variable *thFindValue(
1061 Th_Interp *interp,
1062 const char *zVar, /* Pointer to variable name */
1063 int nVar, /* Number of bytes at nVar */
1064 int create, /* If true, create the variable if not found */
1065 int arrayok, /* If true, an array is Ok. Otherwise array==error */
1066 int noerror /* If false, set interpreter result to error message */
 
1067 ){
1068 const char *zOuter;
1069 int nOuter;
1070 const char *zInner;
1071 int nInner;
@@ -1074,16 +1099,24 @@
1074 Th_HashEntry *pEntry;
1075 Th_Frame *pFrame = interp->pFrame;
1076 Th_Variable *pValue;
1077
1078 thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal);
 
 
 
 
 
1079 if( isGlobal ){
1080 while( pFrame->pCaller ) pFrame = pFrame->pCaller;
1081 }
1082
1083 pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create);
1084 assert(pEntry || create<=0);
 
 
 
1085 if( !pEntry ){
1086 goto no_such_var;
1087 }
1088
1089 pValue = (Th_Variable *)pEntry->pData;
@@ -1106,10 +1139,14 @@
1106 goto no_such_var;
1107 }
1108 pValue->pHash = Th_HashNew(interp);
1109 }
1110 pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create);
 
 
 
 
1111 if( !pEntry ){
1112 goto no_such_var;
1113 }
1114 pValue = (Th_Variable *)pEntry->pData;
1115 if( !pValue ){
@@ -1145,11 +1182,11 @@
1145 ** an error message in the interpreter result.
1146 */
1147 int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){
1148 Th_Variable *pValue;
1149
1150 pValue = thFindValue(interp, zVar, nVar, 0, 0, 0);
1151 if( !pValue ){
1152 return TH_ERROR;
1153 }
1154 if( !pValue->zData ){
1155 Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
@@ -1161,12 +1198,12 @@
1161
1162 /*
1163 ** Return true if variable (zVar, nVar) exists.
1164 */
1165 int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
1166 Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 0, 1);
1167 return pValue && pValue->zData;
1168 }
1169
1170 /*
1171 ** String (zVar, nVar) must contain the name of a scalar variable or
1172 ** array member. If the variable does not exist it is created. The
@@ -1182,11 +1219,11 @@
1182 const char *zValue,
1183 int nValue
1184 ){
1185 Th_Variable *pValue;
1186
1187 pValue = thFindValue(interp, zVar, nVar, 1, 0, 0);
1188 if( !pValue ){
1189 return TH_ERROR;
1190 }
1191
1192 if( nValue<0 ){
@@ -1225,11 +1262,11 @@
1225 if( !pFrame ){
1226 return TH_ERROR;
1227 }
1228 pSavedFrame = interp->pFrame;
1229 interp->pFrame = pFrame;
1230 pValue = thFindValue(interp, zLink, nLink, 1, 1, 0);
1231 interp->pFrame = pSavedFrame;
1232
1233 pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1);
1234 if( pEntry->pData ){
1235 Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal);
@@ -1246,29 +1283,68 @@
1246 ** an array, or an array member. If the identified variable exists, it
1247 ** is deleted and TH_OK returned. Otherwise, an error message is left
1248 ** in the interpreter result and TH_ERROR is returned.
1249 */
1250 int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){
 
1251 Th_Variable *pValue;
 
 
1252
1253 pValue = thFindValue(interp, zVar, nVar, 0, 1, 0);
1254 if( !pValue ){
1255 return TH_ERROR;
1256 }
1257
1258 if( pValue->zData ){
1259 Th_Free(interp, pValue->zData);
1260 pValue->zData = 0;
1261 }
1262 if( pValue->pHash ){
1263 Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp);
1264 Th_HashDelete(interp, pValue->pHash);
1265 pValue->pHash = 0;
1266 }
1267
1268 thFindValue(interp, zVar, nVar, -1, 1, 1); /* Finally, delete from frame */
1269 return TH_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270 }
1271
1272 /*
1273 ** Return an allocated buffer containing a copy of string (z, n). The
1274 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2211
2212 /*
2213 ** Iterate through all values currently stored in the hash table. Invoke
2214 ** the callback function xCallback for each entry. The second argument
2215 ** passed to xCallback is a copy of the fourth argument passed to this
2216 ** function.
 
2217 */
2218 void Th_HashIterate(
2219 Th_Interp *interp,
2220 Th_Hash *pHash,
2221 void (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2222 void *pContext
2223 ){
2224 int i;
2225 for(i=0; i<TH_HASHSIZE; i++){
2226 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2231 }
2232 }
2233 }
2234
2235 /*
2236 ** Helper function for Th_HashDelete().
2237 */
2238 static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2239 Th_Free((Th_Interp *)pContext, (void *)pEntry);
 
2240 }
2241
2242 /*
2243 ** Free a hash-table previously allocated by Th_HashNew().
2244 */
2245
--- src/th.c
+++ src/th.c
@@ -105,12 +105,12 @@
105 static int thEndOfLine(const char *, int);
106
107 static int thPushFrame(Th_Interp*, Th_Frame*);
108 static void thPopFrame(Th_Interp*);
109
110 static int thFreeVariable(Th_HashEntry*, void*);
111 static int thFreeCommand(Th_HashEntry*, void*);
112
113 /*
114 ** The following are used by both the expression and language parsers.
115 ** Given that the start of the input string (z, n) is a language
116 ** construct of the relevant type (a command enclosed in [], an escape
@@ -258,12 +258,14 @@
258 ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable
259 ** structure that the entry points to. Free the Th_Variable if its
260 ** reference count reaches 0.
261 **
262 ** Argument pContext is a pointer to the interpreter structure.
263 **
264 ** Returns non-zero if the Th_Variable was actually freed.
265 */
266 static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){
267 Th_Variable *pValue = (Th_Variable *)pEntry->pData;
268 pValue->nRef--;
269 assert( pValue->nRef>=0 );
270 if( pValue->nRef==0 ){
271 Th_Interp *interp = (Th_Interp *)pContext;
@@ -271,27 +273,33 @@
273 if( pValue->pHash ){
274 Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext);
275 Th_HashDelete(interp, pValue->pHash);
276 }
277 Th_Free(interp, pValue);
278 pEntry->pData = 0;
279 return 1;
280 }
281 return 0;
282 }
283
284 /*
285 ** Argument pEntry points to an entry in the command hash table
286 ** (Th_Interp.paCmd). Delete the Th_Command structure that the
287 ** entry points to.
288 **
289 ** Argument pContext is a pointer to the interpreter structure.
290 **
291 ** Always returns non-zero.
292 */
293 static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){
294 Th_Command *pCommand = (Th_Command *)pEntry->pData;
295 if( pCommand->xDel ){
296 pCommand->xDel((Th_Interp *)pContext, pCommand->pContext);
297 }
298 Th_Free((Th_Interp *)pContext, pEntry->pData);
299 pEntry->pData = 0;
300 return 1;
301 }
302
303 /*
304 ** Push a new frame onto the stack.
305 */
@@ -1042,10 +1050,25 @@
1050 *pnInner = nInner;
1051 *pisGlobal = isGlobal;
1052 return TH_OK;
1053 }
1054
1055 /*
1056 ** The Find structure is used to return extra information to callers of the
1057 ** thFindValue function. The fields within it are populated by thFindValue
1058 ** as soon as the necessary information is available. Callers should check
1059 ** each field of interest upon return.
1060 */
1061
1062 struct Find {
1063 Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */
1064 Th_HashEntry *pElemEntry; /* Pointer to the 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 thFindValue(interp, zVar, nVar, -1, 1, 1, 0);
1344 }
1345 return rc;
1346 }
1347
1348 /*
1349 ** Return an allocated buffer containing a copy of string (z, n). The
1350 ** caller is responsible for eventually calling Th_Free() to free
@@ -2211,16 +2287,17 @@
2287
2288 /*
2289 ** Iterate through all values currently stored in the hash table. Invoke
2290 ** the callback function xCallback for each entry. The second argument
2291 ** passed to xCallback is a copy of the fourth argument passed to this
2292 ** function. The return value from the callback function xCallback is
2293 ** ignored.
2294 */
2295 void Th_HashIterate(
2296 Th_Interp *interp,
2297 Th_Hash *pHash,
2298 int (*xCallback)(Th_HashEntry *pEntry, void *pContext),
2299 void *pContext
2300 ){
2301 int i;
2302 for(i=0; i<TH_HASHSIZE; i++){
2303 Th_HashEntry *pEntry;
@@ -2231,14 +2308,15 @@
2308 }
2309 }
2310 }
2311
2312 /*
2313 ** Helper function for Th_HashDelete(). Always returns non-zero.
2314 */
2315 static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){
2316 Th_Free((Th_Interp *)pContext, (void *)pEntry);
2317 return 1;
2318 }
2319
2320 /*
2321 ** Free a hash-table previously allocated by Th_HashNew().
2322 */
2323
+1 -1
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174174
int nKey;
175175
Th_HashEntry *pNext; /* Internal use only */
176176
};
177177
Th_Hash *Th_HashNew(Th_Interp *);
178178
void Th_HashDelete(Th_Interp *, Th_Hash *);
179
-void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
179
+void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180180
Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181181
182182
/*
183183
** Useful functions from th_lang.c.
184184
*/
185185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- src/th.h
+++ src/th.h
@@ -174,11 +174,11 @@
174 int nKey;
175 Th_HashEntry *pNext; /* Internal use only */
176 };
177 Th_Hash *Th_HashNew(Th_Interp *);
178 void Th_HashDelete(Th_Interp *, Th_Hash *);
179 void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*);
180 Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int);
181
182 /*
183 ** Useful functions from th_lang.c.
184 */
185
--- 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,30 @@
152182
153183
###############################################################################
154184
155185
fossil test-th-eval "set var 1; unset var; unset var"
156186
test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}}
187
+
188
+###############################################################################
189
+
190
+fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv"
191
+test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}}
192
+
193
+###############################################################################
194
+
195
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2"
196
+test th1-unset-5 {$RESULT eq {1}}
197
+
198
+###############################################################################
199
+
200
+fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2"
201
+test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}}
202
+
203
+###############################################################################
204
+
205
+fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)"
206
+test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}}
207
+
208
+###############################################################################
209
+
210
+fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2"
211
+test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}}
157212
--- 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,30 @@
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,30 @@
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

Keyboard Shortcuts

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