rem: Tcl_SetVar2Ex(interp, name1, name2, Tcl_DuplicateObj(objPtr), 0);
add: Tcl_SetVar2Ex(interp, name, name2, Tcl_DuplicateObj(objPtr), 0);
context: ShareTraceProc(clientData, interp, name1, name2, flags)    ClientData clientData;	/* Not used. */    Tcl_Interp *interp;		/* Interpreter whose share variable is				 * being modified. */    char *name1;		/* Name of the shared variable. */    char *name2;		/* Name of variable being modified, or NULL				 * if whole array is being deleted (UTF-8). */    int flags;			/* Indicates what's happening. */{    NsShareVar *valuePtr;	/* The shared value */    Tcl_HashEntry *hPtr;	/* Current hash table item */    Tcl_HashEntry *nextPtr;	/* Next hash table item */    Tcl_HashSearch search;	/* For iterating through shared arrays */    Tcl_Obj *objPtr;		/* The value in the variable */    Tcl_Obj *oldObjPtr;		/* The previous shared value */    Tcl_Obj *newObjPtr;		/* The new shared value */    int new;			/* For CreateHashEntry */    int destroyed = 0;		/* True if share value is destroyed */    int bail = 0;		/* True if this is a recursive trace */    char* string;               /* String form of shared value */    int length;                 /* Length of string */    Tcl_DString ds;		/* Buffer for globalized name */    NsInterp *itPtr = NsGetInterp(interp);    NsServer *servPtr = itPtr->servPtr;    name1 = GetGlobalizedName(&ds, name1);    Ns_CsEnter(&servPtr->share.cs);    hPtr = Tcl_FindHashEntry(&servPtr->share.vars, name1);    if (hPtr == NULL) {	/*	 * This trace is firing on an upvar alias to the shared variable.	 * Punt because there is no exported Tcl API to get the real	 * variable name.  Also lets us cheat and unset the shared	 * variable in the interpreter without reflecting the unset	 * down into the shared value.  HACK ALERT.	 */	Ns_CsLeave(&servPtr->share.cs);	goto done;    }    valuePtr = Tcl_GetHashValue(hPtr);    /*     * Shared variables are persistent until the interpreter is destroyed.     * When the last interpreter sharing the value goes away, so     * does the shared value.     *     * Don't unset shared values (i.e., bail out) when the interpreter is     * being destroyed as that is a nasty side effect on other interpreters     * still using the shared value.     */    if (flags & TCL_INTERP_DESTROYED) {	valuePtr->shareCount--;	if (valuePtr->shareCount == 0) {	    destroyed = 1;	    Tcl_DeleteHashEntry(hPtr);	} else {	    bail = 1;	}    }    /*     * The Tcl_UnsetVar calls in this procedure will trigger     * recursive unset traces, so if we detect this we just bail     */        if (valuePtr->flags & SHARE_TRACE) {	bail = 1;    }    Ns_CsLeave(&servPtr->share.cs);    if (bail) {	goto done;    }	        Ns_CsEnter(&valuePtr->lock);    if ((flags & TCL_TRACE_ARRAY) && (valuePtr->flags & SHARE_ARRAY)) {	/*	 * The easiest way to ensure our copy is up-to-date is just	 * to delete it and recreate it from scratch.  This makes	 * the array names and array get operations weighty.	 */	ShareUnsetVar(interp, name1, valuePtr);	hPtr = Tcl_FirstHashEntry(&valuePtr->array, &search);	while (hPtr != NULL) {	    name2 = Tcl_GetHashKey(&valuePtr->array, hPtr);	    objPtr = Tcl_GetHashValue(hPtr);	    Tcl_SetVar2Ex(interp, name1, name2, Tcl_DuplicateObj(objPtr), 0);	    hPtr = Tcl_NextHashEntry(&search);	}    }    if (flags & TCL_TRACE_WRITES) {	/*	 * Get a copy of the variable value for the shared value.	 */	objPtr = Tcl_GetVar2Ex(interp, name1, name2, 0);	string = Tcl_GetStringFromObj(objPtr, &length);	newObjPtr = Tcl_NewStringObj(string, length);	Tcl_IncrRefCount(newObjPtr);	if (name2 != NULL) {	    /*	     * Update the shared value.	     */	    if (valuePtr->flags == SHARE_UNDEFINED) {		Tcl_InitHashTable(&valuePtr->array, TCL_STRING_KEYS);		valuePtr->flags = SHARE_ARRAY;	    }	    hPtr = Tcl_CreateHashEntry(&valuePtr->array, name2, &new);	    oldObjPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr);	    Tcl_SetHashValue(hPtr, (char *) newObjPtr);	} else {	    oldObjPtr = valuePtr->objPtr;	    valuePtr->objPtr = newObjPtr;	}	/*	 * Discard the old shared value.	 */	if (oldObjPtr != NULL) {	    Tcl_DecrRefCount(oldObjPtr);	}    }    if (flags & TCL_TRACE_READS) {	objPtr = NULL;	if (name2 != NULL) {	    hPtr = Tcl_FindHashEntry(&valuePtr->array, name2);	    if (hPtr != NULL) {		objPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr);	    }	} else if (valuePtr->objPtr != NULL) {	    objPtr = valuePtr->objPtr;	}	if (objPtr != NULL) {	    newObjPtr = Tcl_DuplicateObj(objPtr);	    Tcl_SetVar2Ex(interp, name1, name2, newObjPtr, 0);	}    }    if (flags & TCL_TRACE_UNSETS) {	/*	 * Unset the corresponding shared value.	 */	if (name2 != NULL) {	    hPtr = Tcl_FindHashEntry(&valuePtr->array, name2);	    if (hPtr != NULL) {		objPtr = Tcl_GetHashValue(hPtr);		Tcl_DecrRefCount(objPtr);		Tcl_DeleteHashEntry(hPtr);	    }	} else if (valuePtr->flags & SHARE_ARRAY) {	    hPtr = Tcl_FirstHashEntry(&valuePtr->array, &search);	    while (hPtr != NULL) {		nextPtr = Tcl_NextHashEntry(&search);		objPtr = Tcl_GetHashValue(hPtr);		Tcl_DecrRefCount(objPtr);		Tcl_DeleteHashEntry(hPtr);		hPtr = nextPtr;	    }	    Tcl_DeleteHashTable(&valuePtr->array);	    valuePtr->flags &= ~SHARE_ARRAY;	} else if (valuePtr->objPtr != NULL) {	    Tcl_DecrRefCount(valuePtr->objPtr);	    valuePtr->objPtr = NULL;	    valuePtr->flags &= ~SHARE_SCALAR;	}	if (!destroyed) {	    /*	     * This makes the shared property of the variable "sticky"	     * across unsets.	     */	    	    if (Tcl_TraceVar2(interp, name1, (char *) NULL,		    TCL_TRACE_WRITES | TCL_TRACE_UNSETS |		    TCL_TRACE_READS | TCL_TRACE_ARRAY,  ShareTraceProc,		    (ClientData) NULL) != TCL_OK) {		Ns_Fatal("Cannot set trace on share");	    }	}    }    Ns_CsLeave(&valuePtr->lock);    /*     * Assert we are the only thread with a reference to this     * valuePtr, so we can delete it without holding its lock.     */    if (destroyed) {	Ns_CsDestroy(&valuePtr->lock);	Tcl_Free((char *) valuePtr );    }done:    Tcl_DStringFree(&ds);    return NULL;}