tclvar.c

来自「tcl是工具命令语言」· C语言 代码 · 共 1,908 行 · 第 1/5 页

C
1,908
字号
    CONST char *part1;		/* Name of an array (if part2 is non-NULL)				 * or the name of a variable. */    CONST char *part2;		/* If non-NULL, gives the name of an element				 * in the array part1. */    Tcl_Obj *newValuePtr;	/* New value for variable. */    CONST int flags;			/* OR-ed combination of TCL_GLOBAL_ONLY,				 * and TCL_LEAVE_ERR_MSG bits. */{    Interp *iPtr = (Interp *) interp;    Tcl_Obj *oldValuePtr;    Tcl_Obj *resultPtr = NULL;    int result;    /*     * If the variable is in a hashtable and its hPtr field is NULL, then we     * may have an upvar to an array element where the array was deleted     * or an upvar to a namespace variable whose namespace was deleted.     * Generate an error (allowing the variable to be reset would screw up     * our storage allocation and is meaningless anyway).     */    if ((varPtr->flags & VAR_IN_HASHTABLE) && (varPtr->hPtr == NULL)) {	if (flags & TCL_LEAVE_ERR_MSG) {	    if (TclIsVarArrayElement(varPtr)) {		VarErrMsg(interp, part1, part2, "set", danglingElement);	    } else {		VarErrMsg(interp, part1, part2, "set", danglingVar);	    }	}	return NULL;    }    /*     * It's an error to try to set an array variable itself.     */    if (TclIsVarArray(varPtr) && !TclIsVarUndefined(varPtr)) {	if (flags & TCL_LEAVE_ERR_MSG) {	    VarErrMsg(interp, part1, part2, "set", isArray);	}	return NULL;    }    /*     * Invoke any read traces that have been set for the variable if it     * is requested; this is only done in the core when lappending.     */    if ((flags & TCL_TRACE_READS) && ((varPtr->tracePtr != NULL) 	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL)))) {	if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,		TCL_TRACE_READS, (flags & TCL_LEAVE_ERR_MSG))) {	    return NULL;	}    }    /*     * Set the variable's new value. If appending, append the new value to     * the variable, either as a list element or as a string. Also, if     * appending, then if the variable's old value is unshared we can modify     * it directly, otherwise we must create a new copy to modify: this is     * "copy on write".     */    oldValuePtr = varPtr->value.objPtr;    if (flags & TCL_APPEND_VALUE) {	if (TclIsVarUndefined(varPtr) && (oldValuePtr != NULL)) {	    Tcl_DecrRefCount(oldValuePtr);     /* discard old value */	    varPtr->value.objPtr = NULL;	    oldValuePtr = NULL;	}	if (flags & TCL_LIST_ELEMENT) {	       /* append list element */	    if (oldValuePtr == NULL) {		TclNewObj(oldValuePtr);		varPtr->value.objPtr = oldValuePtr;		Tcl_IncrRefCount(oldValuePtr); /* since var is referenced */	    } else if (Tcl_IsShared(oldValuePtr)) {		varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr);		Tcl_DecrRefCount(oldValuePtr);		oldValuePtr = varPtr->value.objPtr;		Tcl_IncrRefCount(oldValuePtr); /* since var is referenced */	    }	    result = Tcl_ListObjAppendElement(interp, oldValuePtr,		    newValuePtr);	    if (result != TCL_OK) {		return NULL;	    }	} else {		               /* append string */	    /*	     * We append newValuePtr's bytes but don't change its ref count.	     */	    if (oldValuePtr == NULL) {		varPtr->value.objPtr = newValuePtr;		Tcl_IncrRefCount(newValuePtr);	    } else {		if (Tcl_IsShared(oldValuePtr)) {   /* append to copy */		    varPtr->value.objPtr = Tcl_DuplicateObj(oldValuePtr);		    TclDecrRefCount(oldValuePtr);		    oldValuePtr = varPtr->value.objPtr;		    Tcl_IncrRefCount(oldValuePtr); /* since var is ref */		}		Tcl_AppendObjToObj(oldValuePtr, newValuePtr);	    }	}    } else if (newValuePtr != oldValuePtr) {	/*	 * In this case we are replacing the value, so we don't need to	 * do more than swap the objects.	 */	varPtr->value.objPtr = newValuePtr;	Tcl_IncrRefCount(newValuePtr);      /* var is another ref */	if (oldValuePtr != NULL) {	    TclDecrRefCount(oldValuePtr);   /* discard old value */	}    }    TclSetVarScalar(varPtr);    TclClearVarUndefined(varPtr);    if (arrayPtr != NULL) {	TclClearVarUndefined(arrayPtr);    }    /*     * Invoke any write traces for the variable.     */    if ((varPtr->tracePtr != NULL)	    || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {	if (TCL_ERROR == CallVarTraces(iPtr, arrayPtr, varPtr, part1, part2,	        (flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY))		| TCL_TRACE_WRITES, (flags & TCL_LEAVE_ERR_MSG))) {	    goto cleanup;	}    }    /*     * Return the variable's value unless the variable was changed in some     * gross way by a trace (e.g. it was unset and then recreated as an     * array).      */    if (TclIsVarScalar(varPtr) && !TclIsVarUndefined(varPtr)) {	return varPtr->value.objPtr;    }    /*     * A trace changed the value in some gross way. Return an empty string     * object.     */        resultPtr = iPtr->emptyObjPtr;    /*     * If the variable doesn't exist anymore and no-one's using it, then     * free up the relevant structures and hash table entries.     */    cleanup:    if (TclIsVarUndefined(varPtr)) {	CleanupVar(varPtr, arrayPtr);    }    return resultPtr;}/* *---------------------------------------------------------------------- * * TclIncrVar2 -- * *	Given a two-part variable name, which may refer either to a scalar *	variable or an element of an array, increment the Tcl object value *	of the variable by a specified amount. * * Results: *	Returns a pointer to the Tcl_Obj holding the new value of the *	variable. If the specified variable doesn't exist, or there is a *	clash in array usage, or an error occurs while executing variable *	traces, then NULL is returned and a message will be left in *	the interpreter's result. * * Side effects: *	The value of the given variable is incremented by the specified *	amount. If either the array or the entry didn't exist then a new *	variable is created. The ref count for the returned object is _not_ *	incremented to reflect the returned reference; if you want to keep a *	reference to the object you must increment its ref count yourself. * *---------------------------------------------------------------------- */Tcl_Obj *TclIncrVar2(interp, part1Ptr, part2Ptr, incrAmount, flags)    Tcl_Interp *interp;		/* Command interpreter in which variable is				 * to be found. */    Tcl_Obj *part1Ptr;		/* Points to an object holding the name of				 * an array (if part2 is non-NULL) or the				 * name of a variable. */    Tcl_Obj *part2Ptr;		/* If non-null, points to an object holding				 * the name of an element in the array				 * part1Ptr. */    long incrAmount;		/* Amount to be added to variable. */    int flags;                  /* Various flags that tell how to incr value:				 * any of TCL_GLOBAL_ONLY,				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,				 * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */{    Var *varPtr, *arrayPtr;    char *part1, *part2;    part1 = TclGetString(part1Ptr);    part2 = ((part2Ptr == NULL)? NULL : TclGetString(part2Ptr));    varPtr = TclObjLookupVar(interp, part1Ptr, part2, flags, "read",	    0, 1, &arrayPtr);    if (varPtr == NULL) {	Tcl_AddObjErrorInfo(interp,		"\n    (reading value of variable to increment)", -1);	return NULL;    }    return TclPtrIncrVar(interp, varPtr, arrayPtr, part1, part2,	    incrAmount, flags);}/* *---------------------------------------------------------------------- * * TclPtrIncrVar -- * *	Given the pointers to a variable and possible containing array,  *      increment the Tcl object value of the variable by a specified  *      amount. * * Results: *	Returns a pointer to the Tcl_Obj holding the new value of the *	variable. If the specified variable doesn't exist, or there is a *	clash in array usage, or an error occurs while executing variable *	traces, then NULL is returned and a message will be left in *	the interpreter's result. * * Side effects: *	The value of the given variable is incremented by the specified *	amount. If either the array or the entry didn't exist then a new *	variable is created. The ref count for the returned object is _not_ *	incremented to reflect the returned reference; if you want to keep a *	reference to the object you must increment its ref count yourself. * *---------------------------------------------------------------------- */Tcl_Obj *TclPtrIncrVar(interp, varPtr, arrayPtr, part1, part2, incrAmount, flags)    Tcl_Interp *interp;		/* Command interpreter in which variable is				 * to be found. */    Var *varPtr;    Var *arrayPtr;    CONST char *part1;		/* Points to an object holding the name of				 * an array (if part2 is non-NULL) or the				 * name of a variable. */    CONST char *part2;		/* If non-null, points to an object holding				 * the name of an element in the array				 * part1Ptr. */    CONST long incrAmount;	/* Amount to be added to variable. */    CONST int flags;            /* Various flags that tell how to incr value:				 * any of TCL_GLOBAL_ONLY,				 * TCL_NAMESPACE_ONLY, TCL_APPEND_VALUE,				 * TCL_LIST_ELEMENT, TCL_LEAVE_ERR_MSG. */{    register Tcl_Obj *varValuePtr;    int createdNewObj;		/* Set 1 if var's value object is shared				 * so we must increment a copy (i.e. copy				 * on write). */    long i;    varValuePtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1, part2, flags);    if (varValuePtr == NULL) {	Tcl_AddObjErrorInfo(interp,		"\n    (reading value of variable to increment)", -1);	return NULL;    }    /*     * Increment the variable's value. If the object is unshared we can     * modify it directly, otherwise we must create a new copy to modify:     * this is "copy on write". Then free the variable's old string     * representation, if any, since it will no longer be valid.     */    createdNewObj = 0;    if (Tcl_IsShared(varValuePtr)) {	varValuePtr = Tcl_DuplicateObj(varValuePtr);	createdNewObj = 1;    }#ifdef TCL_WIDE_INT_IS_LONG    if (Tcl_GetLongFromObj(interp, varValuePtr, &i) != TCL_OK) {	if (createdNewObj) {	    Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */	}	return NULL;    }    Tcl_SetLongObj(varValuePtr, (i + incrAmount));#else    if (varValuePtr->typePtr == &tclWideIntType) {	Tcl_WideInt wide = varValuePtr->internalRep.wideValue;	Tcl_SetWideIntObj(varValuePtr, wide + Tcl_LongAsWide(incrAmount));    } else if (varValuePtr->typePtr == &tclIntType) {	i = varValuePtr->internalRep.longValue;	Tcl_SetIntObj(varValuePtr, i + incrAmount);    } else {	/*	 * Not an integer or wide internal-rep...	 */	Tcl_WideInt wide;	if (Tcl_GetWideIntFromObj(interp, varValuePtr, &wide) != TCL_OK) {	    if (createdNewObj) {		Tcl_DecrRefCount(varValuePtr); /* free unneeded copy */	    }	    return NULL;	}	if (wide <= Tcl_LongAsWide(LONG_MAX)		&& wide >= Tcl_LongAsWide(LONG_MIN)) {	    Tcl_SetLongObj(varValuePtr, Tcl_WideAsLong(wide) + incrAmount);	} else {	    Tcl_SetWideIntObj(varValuePtr, wide + Tcl_LongAsWide(incrAmount));	}    }#endif    /*     * Store the variable's new value and run any write traces.     */        return TclPtrSetVar(interp, varPtr, arrayPtr, part1, part2,	    varValuePtr, flags);}/* *---------------------------------------------------------------------- * * Tcl_UnsetVar -- * *	Delete a variable, so that it may not be accessed anymore. * * Results: *	Returns TCL_OK if the variable was successfully deleted, TCL_ERROR *	if the variable can't be unset.  In the event of an error, *	if the TCL_LEAVE_ERR_MSG flag is set then an error message *	is left in the interp's result. * * Side effects: *	If varName is defined as a local or global variable in interp, *	it is deleted. * *---------------------------------------------------------------------- */intTcl_UnsetVar(interp, varName, flags)    Tcl_Interp *interp;		/* Command interpreter in which varName is				 * to be looked up. */    CONST char *varName;	/* Name of a variable in interp.  May be				 * either a scalar name or an array name				 * or an element in an array. */    int flags;			/* OR-ed combination of any of				 * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY or				 * TCL_LEAVE_ERR_MSG. */{    return Tcl_UnsetVar2(interp, varName, (char *) NULL, flags);}/* *---------------------------------------------------------------------- * * Tcl_UnsetVar2 -- * *	Delete a variable, given a 2-part name. * * Results: *	Returns TCL_OK if the variable was 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?