📄 tclvar.c
字号:
argv += 2;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TclDeleteVars --
*
* This procedure is called to recycle all the storage space
* associated with a table of variables. For this procedure
* to work correctly, it must not be possible for any of the
* variable in the table to be accessed from Tcl commands
* (e.g. from trace procedures).
*
* Results:
* None.
*
* Side effects:
* Variables are deleted and trace procedures are invoked, if
* any are declared.
*
*----------------------------------------------------------------------
*/
void
TclDeleteVars(iPtr, tablePtr)
Interp *iPtr; /* Interpreter to which variables belong. */
Tcl_HashTable *tablePtr; /* Hash table containing variables to
* delete. */
{
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
register Var *varPtr;
int flags, globalFlag;
flags = TCL_TRACE_UNSETS;
if (tablePtr == &iPtr->globalTable) {
flags |= TCL_INTERP_DESTROYED | TCL_GLOBAL_ONLY;
}
for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
hPtr = Tcl_NextHashEntry(&search)) {
varPtr = (Var *) Tcl_GetHashValue(hPtr);
/*
* For global/upvar variables referenced in procedures, free up the
* local space and then decrement the reference count on the
* variable referred to. If there are no more references to the
* global/upvar and it is undefined and has no traces set, then
* follow on and delete the referenced variable too.
*/
globalFlag = 0;
if (varPtr->flags & VAR_UPVAR) {
hPtr = varPtr->value.upvarPtr;
ckfree((char *) varPtr);
varPtr = (Var *) Tcl_GetHashValue(hPtr);
varPtr->upvarUses--;
if ((varPtr->upvarUses != 0) || !(varPtr->flags & VAR_UNDEFINED)
|| (varPtr->tracePtr != NULL)) {
continue;
}
globalFlag = TCL_GLOBAL_ONLY;
}
/*
* Invoke traces on the variable that is being deleted, then
* free up the variable's space (no need to free the hash entry
* here, unless we're dealing with a global variable: the
* hash entries will be deleted automatically when the whole
* table is deleted).
*/
if (varPtr->tracePtr != NULL) {
(void) CallTraces(iPtr, (Var *) NULL, hPtr,
Tcl_GetHashKey(tablePtr, hPtr), (char *) NULL,
flags | globalFlag);
while (varPtr->tracePtr != NULL) {
VarTrace *tracePtr = varPtr->tracePtr;
varPtr->tracePtr = tracePtr->nextPtr;
ckfree((char *) tracePtr);
}
}
if (varPtr->flags & VAR_ARRAY) {
DeleteArray(iPtr, Tcl_GetHashKey(tablePtr, hPtr), varPtr,
flags | globalFlag);
}
if (globalFlag) {
Tcl_DeleteHashEntry(hPtr);
}
ckfree((char *) varPtr);
}
Tcl_DeleteHashTable(tablePtr);
}
/*
*----------------------------------------------------------------------
*
* CallTraces --
*
* This procedure is invoked to find and invoke relevant
* trace procedures associated with a particular operation on
* a variable. This procedure invokes traces both on the
* variable and on its containing array (where relevant).
*
* Results:
* The return value is NULL if no trace procedures were invoked, or
* if all the invoked trace procedures returned successfully.
* The return value is non-zero if a trace procedure returned an
* error (in this case no more trace procedures were invoked after
* the error was returned). In this case the return value is a
* pointer to a static string describing the error.
*
* Side effects:
* Almost anything can happen, depending on trace; this procedure
* itself doesn't have any side effects.
*
*----------------------------------------------------------------------
*/
static char *
CallTraces(iPtr, arrayPtr, hPtr, part1, part2, flags)
Interp *iPtr; /* Interpreter containing variable. */
register Var *arrayPtr; /* Pointer to array variable that
* contains the variable, or NULL if
* the variable isn't an element of an
* array. */
Tcl_HashEntry *hPtr; /* Hash table entry corresponding to
* variable whose traces are to be
* invoked. */
char *part1, *part2; /* Variable's two-part name. */
int flags; /* Flags to pass to trace procedures:
* indicates what's happening to
* variable, plus other stuff like
* TCL_GLOBAL_ONLY and
* TCL_INTERP_DESTROYED. */
{
Var *varPtr;
register VarTrace *tracePtr;
ActiveVarTrace active;
char *result;
int savedArrayFlags = 0; /* (Initialization not needed except
* to prevent compiler warning) */
/*
* If there are already similar trace procedures active for the
* variable, don't call them again.
*/
varPtr = (Var *) Tcl_GetHashValue(hPtr);
if (varPtr->flags & VAR_TRACE_ACTIVE) {
return NULL;
}
varPtr->flags |= VAR_TRACE_ACTIVE;
/*
* Invoke traces on the array containing the variable, if relevant.
*/
result = NULL;
active.nextPtr = iPtr->activeTracePtr;
iPtr->activeTracePtr = &active;
if (arrayPtr != NULL) {
savedArrayFlags = arrayPtr->flags;
arrayPtr->flags |= VAR_ELEMENT_ACTIVE;
for (tracePtr = arrayPtr->tracePtr; tracePtr != NULL;
tracePtr = active.nextTracePtr) {
active.nextTracePtr = tracePtr->nextPtr;
if (!(tracePtr->flags & flags)) {
continue;
}
result = (*tracePtr->traceProc)(tracePtr->clientData,
(Tcl_Interp *) iPtr, part1, part2, flags);
if (result != NULL) {
if (flags & TCL_TRACE_UNSETS) {
result = NULL;
} else {
goto done;
}
}
}
}
/*
* Invoke traces on the variable itself.
*/
if (flags & TCL_TRACE_UNSETS) {
flags |= TCL_TRACE_DESTROYED;
}
for (tracePtr = varPtr->tracePtr; tracePtr != NULL;
tracePtr = active.nextTracePtr) {
active.nextTracePtr = tracePtr->nextPtr;
if (!(tracePtr->flags & flags)) {
continue;
}
result = (*tracePtr->traceProc)(tracePtr->clientData,
(Tcl_Interp *) iPtr, part1, part2, flags);
if (result != NULL) {
if (flags & TCL_TRACE_UNSETS) {
result = NULL;
} else {
goto done;
}
}
}
/*
* Restore the variable's flags, remove the record of our active
* traces, and then return. Remember that the variable could have
* been re-allocated during the traces, but its hash entry won't
* change.
*/
done:
if (arrayPtr != NULL) {
arrayPtr->flags = savedArrayFlags;
}
varPtr = (Var *) Tcl_GetHashValue(hPtr);
varPtr->flags &= ~VAR_TRACE_ACTIVE;
iPtr->activeTracePtr = active.nextPtr;
return result;
}
/*
*----------------------------------------------------------------------
*
* NewVar --
*
* Create a new variable with a given initial value.
*
* Results:
* The return value is a pointer to the new variable structure.
* The variable will not be part of any hash table yet, and its
* upvarUses count is initialized to 0. Its initial value will
* be empty, but "space" bytes will be available in the value
* area.
*
* Side effects:
* Storage gets allocated.
*
*----------------------------------------------------------------------
*/
static Var *
NewVar(space)
int space; /* Minimum amount of space to allocate
* for variable's value. */
{
int extra;
register Var *varPtr;
extra = space - sizeof(varPtr->value);
if (extra < 0) {
extra = 0;
space = sizeof(varPtr->value);
}
varPtr = (Var *) ckalloc((unsigned) (sizeof(Var) + extra));
varPtr->valueLength = 0;
varPtr->valueSpace = space;
varPtr->upvarUses = 0;
varPtr->tracePtr = NULL;
varPtr->searchPtr = NULL;
varPtr->flags = 0;
varPtr->value.string[0] = 0;
return varPtr;
}
/*
*----------------------------------------------------------------------
*
* ParseSearchId --
*
* This procedure translates from a string to a pointer to an
* active array search (if there is one that matches the string).
*
* Results:
* The return value is a pointer to the array search indicated
* by string, or NULL if there isn't one. If NULL is returned,
* interp->result contains an error message.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ArraySearch *
ParseSearchId(interp, varPtr, varName, string)
Tcl_Interp *interp; /* Interpreter containing variable. */
Var *varPtr; /* Array variable search is for. */
char *varName; /* Name of array variable that search is
* supposed to be for. */
char *string; /* String containing id of search. Must have
* form "search-num-var" where "num" is a
* decimal number and "var" is a variable
* name. */
{
char *end;
int id;
ArraySearch *searchPtr;
/*
* Parse the id into the three parts separated by dashes.
*/
if ((string[0] != 's') || (string[1] != '-')) {
syntax:
Tcl_AppendResult(interp, "illegal search identifier \"", string,
"\"", (char *) NULL);
return NULL;
}
id = strtoul(string+2, &end, 10);
if ((end == (string+2)) || (*end != '-')) {
goto syntax;
}
if (strcmp(end+1, varName) != 0) {
Tcl_AppendResult(interp, "search identifier \"", string,
"\" isn't for variable \"", varName, "\"", (char *) NULL);
return NULL;
}
/*
* Search through the list of active searches on the interpreter
* to see if the desired one exists.
*/
for (searchPtr = varPtr->searchPtr; searchPtr != NULL;
searchPtr = searchPtr->nextPtr) {
if (searchPtr->id == id) {
return searchPtr;
}
}
Tcl_AppendResult(interp, "couldn't find search \"", string, "\"",
(char *) NULL);
return NULL;
}
/*
*----------------------------------------------------------------------
*
* DeleteSearches --
*
* This procedure is called to free up all of the searches
* associated with an array variable.
*
* Results:
* None.
*
* Side effects:
* Memory is released to the storage allocator.
*
*----------------------------------------------------------------------
*/
static void
DeleteSearches(arrayVarPtr)
register Var *arrayVarPtr; /* Variable whose searches are
* to be deleted. */
{
ArraySearch *searchPtr;
while (arrayVarPtr->searchPtr != NULL) {
searchPtr = arrayVarPtr->searchPtr;
arrayVarPtr->searchPtr = searchPtr->nextPtr;
ckfree((char *) searchPtr);
}
}
/*
*----------------------------------------------------------------------
*
* DeleteArray --
*
* This procedure is called to free up everything in an array
* variable. It's the caller's responsibility to make sure
* that the array is no longer accessible before this procedure
* is called.
*
* Results:
* None.
*
* Side effects:
* All storage associated with varPtr's array elements is deleted
* (including the hash table). Any delete trace procedures for
* array elements are invoked.
*
*----------------------------------------------------------------------
*/
static void
DeleteArray(iPtr, arrayName, varPtr, flags)
Interp *iPtr; /* Interpreter containing array. */
char *arrayName; /* Name of array (used for trace
* callbacks). */
Var *varPtr; /* Pointer to variable structure. */
int flags; /* Flags to pass to CallTraces:
* TCL_TRACE_UNSETS and sometimes
* TCL_INTERP_DESTROYED and/or
* TCL_GLOBAL_ONLY. */
{
Tcl_HashSearch search;
register Tcl_HashEntry *hPtr;
register Var *elPtr;
DeleteSearches(varPtr);
for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
elPtr = (Var *) Tcl_GetHashValue(hPtr);
if (elPtr->tracePtr != NULL) {
(void) CallTraces(iPtr, (Var *) NULL, hPtr, arrayName,
Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), flags);
while (elPtr->tracePtr != NULL) {
VarTrace *tracePtr = elPtr->tracePtr;
elPtr->tracePtr = tracePtr->nextPtr;
ckfree((char *) tracePtr);
}
}
if (elPtr->flags & VAR_SEARCHES_POSSIBLE) {
panic("DeleteArray found searches on array alement!");
}
ckfree((char *) elPtr);
}
Tcl_DeleteHashTable(varPtr->value.tablePtr);
ckfree((char *) varPtr->value.tablePtr);
}
/*
*----------------------------------------------------------------------
*
* VarErrMsg --
*
* Generate a reasonable error message describing why a variable
* operation failed.
*
* Results:
* None.
*
* Side effects:
* Interp->result is reset to hold a message identifying the
* variable given by part1 and part2 and describing why the
* variable operation failed.
*
*----------------------------------------------------------------------
*/
static void
VarErrMsg(interp, part1, part2, operation, reason)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -