Tcl Source Code

Artifact [76cf64e232]
Login

Artifact 76cf64e232e330206f36533d36859f7e19443968:

Attachment "link.diff" to ticket [1881048fff] added by dkf 2008-01-28 17:58:16.
diff -Nupr tcl-8.5/generic/tclBasic.c tclTIP/generic/tclBasic.c
--- tcl-8.5/generic/tclBasic.c	2007-12-13 16:23:14.000000000 +0100
+++ tclTIP/generic/tclBasic.c	2008-01-26 19:54:22.000000000 +0100
@@ -146,6 +146,7 @@ static const CmdInfo builtInCmds[] = {
     {"lappend",		Tcl_LappendObjCmd,	TclCompileLappendCmd,	1},
     {"lassign",		Tcl_LassignObjCmd,	TclCompileLassignCmd,	1},
     {"lindex",		Tcl_LindexObjCmd,	TclCompileLindexCmd,	1},
+    {"link",		Tcl_LinkObjCmd,		NULL,			0},
     {"linsert",		Tcl_LinsertObjCmd,	NULL,			1},
     {"list",		Tcl_ListObjCmd,		TclCompileListCmd,	1},
     {"llength",		Tcl_LlengthObjCmd,	TclCompileLlengthCmd,	1},
diff -Nupr tcl-8.5/generic/tcl.decls tclTIP/generic/tcl.decls
--- tcl-8.5/generic/tcl.decls	2007-12-13 16:23:14.000000000 +0100
+++ tclTIP/generic/tcl.decls	2008-01-26 19:52:14.000000000 +0100
@@ -2099,6 +2099,12 @@ declare 579 generic {
     void Tcl_AppendPrintfToObj(Tcl_Obj *objPtr, CONST char *format, ...)
 }
 
+# TIP#TODO Create ''link'' command and add more link types
+declare 580 generic {
+    int Tcl_LinkVar(Tcl_Interp *interp, CONST char *varName, char *addr,
+            int type, int size)
+}
+
 ##############################################################################
 
 # Define the platform specific public Tcl interface.  These functions are
diff -Nupr tcl-8.5/generic/tcl.h tclTIP/generic/tcl.h
--- tcl-8.5/generic/tcl.h	2007-12-13 16:23:14.000000000 +0100
+++ tclTIP/generic/tcl.h	2008-01-26 19:49:50.000000000 +0100
@@ -1065,6 +1065,8 @@ typedef struct Tcl_DString {
 #define TCL_LINK_ULONG		12
 #define TCL_LINK_FLOAT		13
 #define TCL_LINK_WIDE_UINT	14
+#define TCL_LINK_CHARS          15
+#define TCL_LINK_BINARY         16
 #define TCL_LINK_READ_ONLY	0x80
 
 /*
diff -Nupr tcl-8.5/generic/tclInt.h tclTIP/generic/tclInt.h
--- tcl-8.5/generic/tclInt.h	2007-12-17 16:28:27.000000000 +0100
+++ tclTIP/generic/tclInt.h	2008-01-26 20:10:58.000000000 +0100
@@ -2826,6 +2826,9 @@ MODULE_SCOPE int	Tcl_LassignObjCmd(Clien
 MODULE_SCOPE int	Tcl_LindexObjCmd(ClientData clientData,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *const objv[]);
+MODULE_SCOPE int	Tcl_LinkObjCmd(ClientData clientData,
+			    Tcl_Interp *interp, int objc,
+			    Tcl_Obj *const objv[]);
 MODULE_SCOPE int	Tcl_LinsertObjCmd(ClientData clientData,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *const objv[]);
diff -Nupr tcl-8.5/generic/tclLink.c tclTIP/generic/tclLink.c
--- tcl-8.5/generic/tclLink.c	2007-12-13 16:23:18.000000000 +0100
+++ tclTIP/generic/tclLink.c	2008-01-26 20:13:51.000000000 +0100
@@ -30,6 +30,9 @@ typedef struct Link {
 				 * actual variable may be aliased at that time
 				 * via upvar. */
     char *addr;			/* Location of C variable. */
+    int bytes;			/* Size of C variable array.
+				 * 0 when single variables
+				 * >0 used for array variables */
     int type;			/* Type of link (TCL_LINK_INT, etc.). */
     union {
 	char c;
@@ -44,6 +47,18 @@ typedef struct Link {
 	Tcl_WideUInt uw;
 	float f;
 	double d;
+	char *pc;
+	unsigned char *puc;
+	int *pi;
+	unsigned int *pui;
+	short *ps;
+	unsigned short *pus;
+	long *pl;
+	unsigned long *pul;
+	Tcl_WideInt *pw;
+	Tcl_WideUInt *puw;
+	float *pf;
+	double *pd;
     } lastValue;		/* Last known value of C variable; used to
 				 * avoid string conversions. */
     int flags;			/* Miscellaneous one-bit values; see below for
@@ -57,10 +72,14 @@ typedef struct Link {
  * LINK_BEING_UPDATED -		1 means that a call to Tcl_UpdateLinkedVar is
  *				in progress for this variable, so trace
  *				callbacks on the variable should be ignored.
+ * LINK_ALLOC_ADDR -		1 means linkPtr->addr was allocated on the heap
+ * LINK_ALLOC_LAST -		1 means linkPtr->valueLast.p was allocated on the heap
  */
 

 #define LINK_READ_ONLY		1
 #define LINK_BEING_UPDATED	2
+#define LINK_ALLOC_ADDR		4
+#define LINK_ALLOC_LAST		8
 
 /*
  * Forward references to functions defined later in this file:
@@ -69,6 +88,7 @@ typedef struct Link {
 static char *		LinkTraceProc(ClientData clientData,Tcl_Interp *interp,
 			    CONST char *name1, CONST char *name2, int flags);
 static Tcl_Obj *	ObjValue(Link *linkPtr);
+static void		LinkFree(Link *linkPtr);
 
 /*
  * Convenience macro for accessing the value of the C variable pointed to by a
@@ -125,11 +145,12 @@ Tcl_LinkVar(
     } else {
 	linkPtr->flags = 0;
     }
+    linkPtr->bytes = 0;
     objPtr = ObjValue(linkPtr);
     if (Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr,
 	    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
 	Tcl_DecrRefCount(linkPtr->varName);
-	ckfree((char *) linkPtr);
+	LinkFree(linkPtr);
 	return TCL_ERROR;
     }
     code = Tcl_TraceVar(interp, varName, TCL_GLOBAL_ONLY|TCL_TRACE_READS
@@ -137,7 +158,149 @@ Tcl_LinkVar(
 	    (ClientData) linkPtr);
     if (code != TCL_OK) {
 	Tcl_DecrRefCount(linkPtr->varName);
-	ckfree((char *) linkPtr);
+	LinkFree(linkPtr);
+    }
+    return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_LinkArray --
+ *
+ *	Link a C variable array to a Tcl variable so that changes to either one
+ *	causes the other to change.
+ *
+ * Results:
+ *	The return value is TCL_OK if everything went well or TCL_ERROR if an
+ *	error occurred (the interp's result is also set after errors).
+ *
+ * Side effects:
+ *	The value at *addr is linked to the Tcl variable "varName", using
+ *	"type" to convert between string values for Tcl and binary values for
+ *	*addr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_LinkArray(
+    Tcl_Interp *interp,		/* Interpreter in which varName exists. */
+    CONST char *varName,	/* Name of a global variable in interp. */
+    char *addr,			/* Address of a C variable to be linked to
+				 * varName. If NULL then the necessary space
+				 * will be allocated and returned as the
+				 * interpreter result. */
+    int type,			/* Type of C variable: TCL_LINK_INT, etc. Also
+				 * may have TCL_LINK_READ_ONLY and
+				 * TCL_LINK_ALLOC OR'ed in. */
+    int size)			/* Size of C variable array, >1 if array */
+{
+    Tcl_Obj *objPtr;
+    Link *linkPtr;
+    int code;
+
+    if (size < 1) {
+	Tcl_SetResult(interp, "wrong array size given", TCL_STATIC);
+	return TCL_ERROR;
+    }
+
+    linkPtr = (Link *) ckalloc(sizeof(Link));
+    linkPtr->type = type & ~TCL_LINK_READ_ONLY;
+    if (type & TCL_LINK_READ_ONLY) {
+	linkPtr->flags = LINK_READ_ONLY;
+    } else {
+	linkPtr->flags = 0;
+    }
+    switch (linkPtr->type) {
+    case TCL_LINK_INT:
+    case TCL_LINK_BOOLEAN:
+	linkPtr->bytes = size * sizeof(int);
+	break;
+    case TCL_LINK_DOUBLE:
+	linkPtr->bytes = size * sizeof(double);
+	break;
+    case TCL_LINK_WIDE_INT:
+	linkPtr->bytes = size * sizeof(Tcl_WideInt);
+	break;
+    case TCL_LINK_WIDE_UINT:
+	linkPtr->bytes = size * sizeof(Tcl_WideUInt);
+	break;
+    case TCL_LINK_CHAR:
+	linkPtr->bytes = size * sizeof(char);
+	break;
+    case TCL_LINK_UCHAR:
+	linkPtr->bytes = size * sizeof(unsigned char);
+	break;
+    case TCL_LINK_SHORT:
+	linkPtr->bytes = size * sizeof(short);
+	break;
+    case TCL_LINK_USHORT:
+	linkPtr->bytes = size * sizeof(unsigned short);
+	break;
+    case TCL_LINK_UINT:
+	linkPtr->bytes = size * sizeof(unsigned int);
+	break;
+    case TCL_LINK_LONG:
+	linkPtr->bytes = size * sizeof(long);
+	break;
+    case TCL_LINK_ULONG:
+	linkPtr->bytes = size * sizeof(unsigned long);
+	break;
+    case TCL_LINK_FLOAT:
+	linkPtr->bytes = size * sizeof(float);
+	break;
+    case TCL_LINK_STRING:
+	linkPtr->bytes = size * sizeof(char);
+	size = 1;/* this is a variable length string, no need to check last value */
+	/* if no address is given create one and use as address the
+         * not needed linkPtr->lastValue */
+	if (addr == NULL) {
+	    linkPtr->lastValue.pc = ckalloc(linkPtr->bytes);
+	    linkPtr->flags |= LINK_ALLOC_LAST;
+	    addr = (char *)&linkPtr->lastValue.pc;
+	}
+	break;
+    case TCL_LINK_CHARS:
+    case TCL_LINK_BINARY:
+	linkPtr->bytes = size * sizeof(char);
+	break;
+    default:
+	LinkFree(linkPtr);
+	Tcl_SetResult(interp, "bad linked array variable type", TCL_STATIC);
+	return TCL_ERROR;
+    }
+    /* allocate C variable space in case no address is given */
+    if (addr==NULL) {
+	linkPtr->addr = ckalloc(linkPtr->bytes);
+	linkPtr->flags |= LINK_ALLOC_ADDR;
+    } else {
+	linkPtr->addr = addr;
+    }
+    /* if necessary create space for last used value */
+    if (size > 1) {
+	linkPtr->lastValue.pc = ckalloc(linkPtr->bytes);
+	linkPtr->flags |= LINK_ALLOC_LAST;
+    }
+    /* set common structure values */
+    linkPtr->interp = interp;
+    linkPtr->varName = Tcl_NewStringObj(varName, -1);
+    Tcl_IncrRefCount(linkPtr->varName);
+    objPtr = ObjValue(linkPtr);
+    if (Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, objPtr,
+	    TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+	Tcl_DecrRefCount(linkPtr->varName);
+	LinkFree(linkPtr);
+	return TCL_ERROR;
+    }
+    code = Tcl_TraceVar(interp, varName, TCL_GLOBAL_ONLY|TCL_TRACE_READS
+	    |TCL_TRACE_WRITES|TCL_TRACE_UNSETS, LinkTraceProc,
+	    (ClientData) linkPtr);
+    if (code != TCL_OK) {
+	Tcl_DecrRefCount(linkPtr->varName);
+	LinkFree(linkPtr);
+    } else {
+	Tcl_SetObjResult(interp,Tcl_NewIntObj((int)linkPtr->addr));
     }
     return code;
 }
@@ -176,7 +339,7 @@ Tcl_UnlinkVar(
 	    TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 	    LinkTraceProc, (ClientData) linkPtr);
     Tcl_DecrRefCount(linkPtr->varName);
-    ckfree((char *) linkPtr);
+    LinkFree(linkPtr);
 }
 
 /*
@@ -228,6 +391,118 @@ Tcl_UpdateLinkedVar(
 /*
  *----------------------------------------------------------------------
  *
+ * Tcl_LinkObjCmd --
+ *
+ *      This function is invoked to process the "link" Tcl command. See the
+ *      user documentation for details on what it does.
+ *
+ * Results:
+ *      A standard Tcl result.
+ *
+ * Side effects:
+ *      See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_LinkObjCmd(
+    ClientData dummy,           /* Not used. */
+    Tcl_Interp *interp,         /* Current interpreter. */
+    int objc,                   /* Number of arguments. */
+    Tcl_Obj *const objv[])      /* Argument objects. */
+{
+    static const char *LinkOption[] = {
+    "update", "remove", "create", NULL
+    };
+    enum LinkOption { LINK_UPDATE, LINK_REMOVE, LINK_CREATE };
+    static const char *LinkType[] = {
+    "char", "uchar", "short", "ushort", "int", "uint", "long", "ulong",
+    "wide", "uwide", "float", "double", "string", "chars", "binary", NULL
+    };
+    /* all values after TCL_LINK_CHARS_ARRAY are used as arrays (see below) */
+    static int LinkTypes[] = { TCL_LINK_CHAR, TCL_LINK_UCHAR,
+    TCL_LINK_SHORT, TCL_LINK_USHORT, TCL_LINK_INT, TCL_LINK_UINT,
+    TCL_LINK_LONG, TCL_LINK_ULONG, TCL_LINK_WIDE_INT, TCL_LINK_WIDE_UINT,
+    TCL_LINK_FLOAT, TCL_LINK_DOUBLE, TCL_LINK_STRING, TCL_LINK_CHARS, TCL_LINK_BINARY
+    };
+    int optionIndex;
+    int typeIndex;
+    int readonly;
+    int i;
+    char *name;
+    int addr;
+    int size;
+    char *arg;
+    int length;
+
+    if (objc < 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "option args");
+	return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObj(interp, objv[1], LinkOption, "option", 0,
+	&optionIndex) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    switch ((enum LinkOption) optionIndex) {
+    case LINK_UPDATE:
+	for (i=2;i<objc;i++) {
+	    Tcl_UpdateLinkedVar(interp, Tcl_GetString(objv[i]));
+	}
+	return TCL_OK;
+    case LINK_REMOVE:
+	for (i=2;i<objc;i++) {
+	    Tcl_UnlinkVar(interp, Tcl_GetString(objv[i]));
+	}
+	return TCL_OK;
+    case LINK_CREATE:
+	if (objc < 4) {
+	    goto wrongArgs;
+	}
+	readonly = 0;
+	i = 2;
+	/* test on switch -r... */
+	arg = Tcl_GetStringFromObj(objv[i],&length);
+	if (length < 2) {
+	    goto wrongArgs;
+	}
+	if (arg[0] == '-') {
+	    if (arg[1] != 'r') {
+	        goto wrongArgs;
+	    }
+	    readonly = TCL_LINK_READ_ONLY;
+	    i++;
+	}
+	if (Tcl_GetIndexFromObj(interp, objv[i++], LinkType, "type", 0,
+	    &typeIndex) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if (Tcl_GetIntFromObj(interp, objv[i++], &size) == TCL_ERROR) {
+	    Tcl_SetResult(interp, "wrong size value", NULL);
+	    return TCL_ERROR;
+	}
+	name = Tcl_GetString(objv[i++]);
+	/* if no address is given request one in the underlying function */
+	if (i <objc) {
+	    if (Tcl_GetIntFromObj(interp, objv[i], &addr) == TCL_ERROR) {
+	        Tcl_SetResult(interp, "wrong address value", NULL);
+	        return TCL_ERROR;
+	    }
+	} else {
+	    addr = 0;
+	}
+	return Tcl_LinkArray(interp,name,(char *)addr,LinkTypes[typeIndex] | readonly,size);
+    }
+    return TCL_OK;
+
+  wrongArgs:
+    Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? type size name ?address?");
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * LinkTraceProc --
  *
  *	This function is invoked when a linked Tcl variable is read, written,
@@ -262,6 +537,9 @@ LinkTraceProc(
     int valueInt;
     Tcl_WideInt valueWide;
     double valueDouble;
+    int objc;
+    Tcl_Obj **objv;
+    int i;
 
     /*
      * If the variable is being unset, then just re-create it (with a trace)
@@ -271,7 +549,7 @@ LinkTraceProc(
     if (flags & TCL_TRACE_UNSETS) {
 	if (Tcl_InterpDeleted(interp)) {
 	    Tcl_DecrRefCount(linkPtr->varName);
-	    ckfree((char *) linkPtr);
+	    LinkFree(linkPtr);
 	} else if (flags & TCL_TRACE_DESTROYED) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
 		    TCL_GLOBAL_ONLY);
@@ -298,6 +576,11 @@ LinkTraceProc(
      */
 
     if (flags & TCL_TRACE_READS) {
+	/* variable arrays */
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    changed = memcmp(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	/* single variables */
+	} else {
 	switch (linkPtr->type) {
 	case TCL_LINK_INT:
 	case TCL_LINK_BOOLEAN:
@@ -337,10 +620,13 @@ LinkTraceProc(
 	    changed = (LinkedVar(float) != linkPtr->lastValue.f);
 	    break;
 	case TCL_LINK_STRING:
+	case TCL_LINK_CHARS:
+	case TCL_LINK_BINARY:
 	    changed = 1;
 	    break;
 	default:
-	    return "internal error: bad linked variable type";
+	    changed = 0;
+	}
 	}
 	if (changed) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -352,7 +638,7 @@ LinkTraceProc(
     /*
      * For writes, first make sure that the variable is writable. Then convert
      * the Tcl value to C if possible. If the variable isn't writable or can't
-     * be converted, then restore the varaible's old value and return an
+     * be converted, then restore the variable's old value and return an
      * error. Another tricky thing: we have to save and restore the interp's
      * result, since the variable access could occur when the result has been
      * partially set.
@@ -374,6 +660,21 @@ LinkTraceProc(
 
     switch (linkPtr->type) {
     case TCL_LINK_INT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(int)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetIntFromObj(NULL,objv[i],&linkPtr->lastValue.pi[i])!= TCL_OK) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have integer value";
+		}
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetIntFromObj(NULL, valueObj, &linkPtr->lastValue.i)
 		!= TCL_OK) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -384,16 +685,53 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_WIDE_INT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(Tcl_WideInt)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetWideIntFromObj(NULL,objv[i],&linkPtr->lastValue.pw[i])!= TCL_OK) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have wide integer value";
+		}
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetWideIntFromObj(NULL, valueObj, &linkPtr->lastValue.w)
 		!= TCL_OK) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
 		    TCL_GLOBAL_ONLY);
-	    return "variable must have integer value";
+	    return "variable must have wide integer value";
 	}
 	LinkedVar(Tcl_WideInt) = linkPtr->lastValue.w;
 	break;
 
     case TCL_LINK_DOUBLE:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(double)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetDoubleFromObj(NULL,objv[i],&linkPtr->lastValue.pd[i])!= TCL_OK) {
+#ifdef ACCEPT_NAN
+	    if (valueObj->typePtr != &tclDoubleType) {
+#endif
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have real value";
+#ifdef ACCEPT_NAN
+	    }
+	    linkPtr->lastValue.pdi[i] = valueObj->internalRep.doubleValue;
+#endif
+		}
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetDoubleFromObj(NULL, valueObj, &linkPtr->lastValue.d)
 		!= TCL_OK) {
 #ifdef ACCEPT_NAN
@@ -411,6 +749,21 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_BOOLEAN:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(int)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetBooleanFromObj(NULL,objv[i],&linkPtr->lastValue.pi[i])!= TCL_OK) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have boolean value";
+		}
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetBooleanFromObj(NULL, valueObj, &linkPtr->lastValue.i)
 		!= TCL_OK) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -421,6 +774,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_CHAR:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(char)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetIntFromObj(NULL,objv[i],&valueInt)!= TCL_OK
+			|| valueInt < SCHAR_MIN || valueInt > SCHAR_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have char value";
+		}
+		linkPtr->lastValue.pc[i] = (char)valueInt;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK
 		|| valueInt < SCHAR_MIN || valueInt > SCHAR_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -432,6 +802,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_UCHAR:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(unsigned char)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetIntFromObj(NULL,objv[i],&valueInt)!= TCL_OK
+			|| valueInt < 0 || valueInt > UCHAR_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have unsigned char value";
+		}
+		linkPtr->lastValue.puc[i] = (unsigned char)valueInt;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK
 		|| valueInt < 0 || valueInt > UCHAR_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -443,6 +830,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_SHORT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(short)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetIntFromObj(NULL,objv[i],&valueInt)!= TCL_OK
+			|| valueInt < SHRT_MIN || valueInt > SHRT_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have short value";
+		}
+		linkPtr->lastValue.ps[i] = (short)valueInt;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK
 		|| valueInt < SHRT_MIN || valueInt > SHRT_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -454,6 +858,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_USHORT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(unsigned short)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetIntFromObj(NULL,objv[i],&valueInt)!= TCL_OK
+			|| valueInt < 0 || valueInt > USHRT_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have unsigned short value";
+		}
+		linkPtr->lastValue.pus[i] = (unsigned short)valueInt;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetIntFromObj(interp, valueObj, &valueInt) != TCL_OK
 		|| valueInt < 0 || valueInt > USHRT_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -465,6 +886,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_UINT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(unsigned int)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetWideIntFromObj(NULL,objv[i],&valueWide)!= TCL_OK
+			|| valueWide < 0 || valueWide > UINT_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have unsigned int value";
+		}
+		linkPtr->lastValue.pui[i] = (unsigned int)valueWide;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK
 		|| valueWide < 0 || valueWide > UINT_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -476,6 +914,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_LONG:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(long)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetWideIntFromObj(NULL,objv[i],&valueWide)!= TCL_OK
+			|| valueWide < LONG_MIN || valueWide > LONG_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have long value";
+		}
+		linkPtr->lastValue.pl[i] = (long)valueWide;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK
 		|| valueWide < LONG_MIN || valueWide > LONG_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -487,6 +942,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_ULONG:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(unsigned long)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetWideIntFromObj(NULL,objv[i],&valueWide)!= TCL_OK
+			|| valueWide < 0 || valueWide > ULONG_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have unsigned long value";
+		}
+		linkPtr->lastValue.pul[i] = (unsigned long)valueWide;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK
 		|| valueWide < 0 || (Tcl_WideUInt) valueWide > ULONG_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -501,7 +973,25 @@ LinkTraceProc(
 	/*
 	 * FIXME: represent as a bignum.
 	 */
-	if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK) {
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(Tcl_WideUInt)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetWideIntFromObj(NULL,objv[i],&valueWide)!= TCL_OK
+			|| valueWide < 0) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have unsigned wide int value";
+		}
+		linkPtr->lastValue.puw[i] = (Tcl_WideUInt)valueWide;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
+	if (Tcl_GetWideIntFromObj(interp, valueObj, &valueWide) != TCL_OK
+		|| valueWide < 0) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
 		    TCL_GLOBAL_ONLY);
 	    return "variable must have unsigned wide int value";
@@ -511,6 +1001,23 @@ LinkTraceProc(
 	break;
 
     case TCL_LINK_FLOAT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    if (Tcl_ListObjGetElements(interp,valueObj,&objc,&objv)==TCL_ERROR
+		|| objc != linkPtr->bytes/sizeof(float)) {
+		return "wrong dimension";
+	    }
+	    for (i=0; i < objc; i++) {
+		if (Tcl_GetDoubleFromObj(interp,objv[i],&valueDouble)!= TCL_OK
+			|| valueDouble < FLT_MIN || valueDouble > FLT_MAX) {
+	        Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
+		    TCL_GLOBAL_ONLY);
+	            return "variable array must have float value";
+		}
+		linkPtr->lastValue.pf[i] = (float)valueDouble;
+	    }
+	    memcpy(linkPtr->addr,linkPtr->lastValue.pc,linkPtr->bytes);
+	    break;
+	}
 	if (Tcl_GetDoubleFromObj(interp, valueObj, &valueDouble) != TCL_OK
 		|| valueDouble < -FLT_MAX || valueDouble > FLT_MAX) {
 	    Tcl_ObjSetVar2(interp, linkPtr->varName, NULL, ObjValue(linkPtr),
@@ -525,11 +1032,39 @@ LinkTraceProc(
 	value = Tcl_GetStringFromObj(valueObj, &valueLength);
 	valueLength++;
 	pp = (char **) linkPtr->addr;
-
 	*pp = ckrealloc(*pp, valueLength);
 	memcpy(*pp, value, (unsigned) valueLength);
 	break;
 
+    case TCL_LINK_CHARS:
+	value = (char *)Tcl_GetStringFromObj(valueObj, &valueLength);
+	valueLength++;/* include end of string char */
+	if (valueLength > linkPtr->bytes) {
+	    return "wrong size of chars value";
+	}
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, value, (unsigned) valueLength);
+	    memcpy(linkPtr->addr, value, (unsigned) valueLength);
+	} else {
+	    linkPtr->lastValue.c = '\0';
+	    LinkedVar(char) = linkPtr->lastValue.c;
+	}
+	break;
+
+    case TCL_LINK_BINARY:
+	value = (char *)Tcl_GetByteArrayFromObj(valueObj, &valueLength);
+	if (valueLength != linkPtr->bytes) {
+	    return "wrong size of binary value";
+	}
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, value, (unsigned) valueLength);
+	    memcpy(linkPtr->addr, value, (unsigned) valueLength);
+	} else {
+	    linkPtr->lastValue.uc = (unsigned char) *value;
+	    LinkedVar(unsigned char) = linkPtr->lastValue.uc;
+	}
+	break;
+
     default:
 	return "internal error: bad linked variable type";
     }
@@ -560,49 +1095,169 @@ ObjValue(
 {
     char *p;
     Tcl_Obj *resultObj;
+    int objc;
+    static Tcl_Obj **objv = NULL;
+    int i;
 
     switch (linkPtr->type) {
     case TCL_LINK_INT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(int);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewIntObj(linkPtr->lastValue.pi[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.i = LinkedVar(int);
 	return Tcl_NewIntObj(linkPtr->lastValue.i);
     case TCL_LINK_WIDE_INT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(Tcl_WideInt);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewWideIntObj(linkPtr->lastValue.pw[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.w = LinkedVar(Tcl_WideInt);
 	return Tcl_NewWideIntObj(linkPtr->lastValue.w);
     case TCL_LINK_DOUBLE:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(double);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewDoubleObj(linkPtr->lastValue.pd[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.d = LinkedVar(double);
 	return Tcl_NewDoubleObj(linkPtr->lastValue.d);
     case TCL_LINK_BOOLEAN:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(int);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewBooleanObj(linkPtr->lastValue.pi[i] != 0);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.i = LinkedVar(int);
 	return Tcl_NewBooleanObj(linkPtr->lastValue.i != 0);
     case TCL_LINK_CHAR:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(char);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewIntObj(linkPtr->lastValue.pc[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.c = LinkedVar(char);
 	return Tcl_NewIntObj(linkPtr->lastValue.c);
     case TCL_LINK_UCHAR:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(unsigned char);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewIntObj(linkPtr->lastValue.puc[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.uc = LinkedVar(unsigned char);
 	return Tcl_NewIntObj(linkPtr->lastValue.uc);
     case TCL_LINK_SHORT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(short);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewIntObj(linkPtr->lastValue.ps[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.s = LinkedVar(short);
 	return Tcl_NewIntObj(linkPtr->lastValue.s);
     case TCL_LINK_USHORT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(unsigned short);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewIntObj(linkPtr->lastValue.pus[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.us = LinkedVar(unsigned short);
 	return Tcl_NewIntObj(linkPtr->lastValue.us);
     case TCL_LINK_UINT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(unsigned int);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewWideIntObj(linkPtr->lastValue.pui[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.ui = LinkedVar(unsigned int);
 	return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.ui);
     case TCL_LINK_LONG:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(long);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewWideIntObj(linkPtr->lastValue.pl[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.l = LinkedVar(long);
 	return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.l);
     case TCL_LINK_ULONG:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(unsigned long);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewWideIntObj(linkPtr->lastValue.pul[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.ul = LinkedVar(unsigned long);
 	return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.ul);
     case TCL_LINK_FLOAT:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(float);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewDoubleObj(linkPtr->lastValue.pf[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
 	linkPtr->lastValue.f = LinkedVar(float);
 	return Tcl_NewDoubleObj(linkPtr->lastValue.f);
     case TCL_LINK_WIDE_UINT:
-	linkPtr->lastValue.uw = LinkedVar(Tcl_WideUInt);
 	/*
 	 * FIXME: represent as a bignum.
 	 */
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    objc = linkPtr->bytes/sizeof(Tcl_WideUInt);
+	    objv = (Tcl_Obj **) ckrealloc((char *)objv, objc*sizeof(Tcl_Obj*));
+	    for (i=0; i < objc; i++) {
+		objv[i] = Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.puw[i]);
+	    }
+	    return Tcl_NewListObj(objc,objv);
+	}
+	linkPtr->lastValue.uw = LinkedVar(Tcl_WideUInt);
 	return Tcl_NewWideIntObj((Tcl_WideInt) linkPtr->lastValue.uw);
     case TCL_LINK_STRING:
 	p = LinkedVar(char *);
@@ -612,6 +1267,23 @@ ObjValue(
 	}
 	return Tcl_NewStringObj(p, -1);
 
+    case TCL_LINK_CHARS:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    linkPtr->lastValue.pc[linkPtr->bytes-1] = '\0';/* take care of proper string end */
+	    return Tcl_NewStringObj(linkPtr->lastValue.pc,linkPtr->bytes);
+	}
+	linkPtr->lastValue.c = '\0';
+	return Tcl_NewStringObj(&linkPtr->lastValue.c,1);
+
+    case TCL_LINK_BINARY:
+	if (linkPtr->flags & LINK_ALLOC_LAST) {
+	    memcpy(linkPtr->lastValue.pc, linkPtr->addr, linkPtr->bytes);
+	    return Tcl_NewByteArrayObj((unsigned char *)linkPtr->addr,linkPtr->bytes);
+	}
+	linkPtr->lastValue.uc = LinkedVar(unsigned char);
+	return Tcl_NewByteArrayObj(&linkPtr->lastValue.uc,1);
+
     /*
      * This code only gets executed if the link type is unknown (shouldn't
      * ever happen).
@@ -624,6 +1296,31 @@ ObjValue(
 }
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * LinkFree --
+ *
+ *	Free's allocated space of given link and link structure.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+LinkFree(
+    Link *linkPtr)		/* Structure describing linked variable. */
+{
+    if (linkPtr->flags & LINK_ALLOC_ADDR) ckfree(linkPtr->addr);
+    if (linkPtr->flags & LINK_ALLOC_LAST) ckfree(linkPtr->lastValue.pc);
+    ckfree((char *) linkPtr);
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nupr tcl-8.5/tests/link.test tclTIP/tests/link.test
--- tcl-8.5/tests/link.test	2007-12-13 16:26:06.000000000 +0100
+++ tclTIP/tests/link.test	2008-01-26 20:26:32.000000000 +0100
@@ -79,7 +79,7 @@ test link-2.5 {writing bad values into v
     testlink set 43 1.23 4 - 56785678 64 250 30000 60000 0xbeefbabe 12321 32123 3.25 1231231234
     testlink create 1 1 1 1 1 1 1 1 1 1 1 1 1 1
     list [catch {set wide gorp} msg] $msg $bool
-} {1 {can't set "wide": variable must have integer value} 1}
+} {1 {can't set "wide": variable must have wide integer value} 1}
 
 test link-3.1 {read-only variables} {testlink} {
     testlink delete
@@ -234,7 +234,7 @@ test link-7.7 {access to linked variable
     testlink create 1 1 1 1 1 1 1 1 1 1 1 1 1 1
     testlink set -4 16.3 1 {} 778899 {} {} {} {} {} {} {} {} {}
     list [catch x msg] $msg $wide
-} {1 {can't set "y": variable must have integer value} 778899}
+} {1 {can't set "y": variable must have wide integer value} 778899}
 
 test link-8.1 {Tcl_UpdateLinkedVar procedure} {testlink} {
     proc x args {
@@ -275,6 +275,296 @@ catch {testlink delete}
 foreach i {int real bool string wide} {
     catch {unset $i}
 }
+test link-9.1 {link usage messages} {
+    set mylist [list]
+    catch {link} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {link x} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {link update} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {link remove} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {link create} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {link create xx 1 my} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {link create chars 0 my} my(msg); lappend mylist $my(msg);unset my(msg)
+    join $mylist "\n"
+} {wrong # args: should be "link option args"
+bad option "x": must be update, remove, or create
+
+
+wrong # args: should be "link create ?-readonly? type size name ?address?"
+bad type "xx": must be char, uchar, short, ushort, int, uint, long, ulong, wide, uwide, float, double, string, chars, or binary
+wrong array size given}
+test link-10.1 {link chars} {
+    set mylist [list]
+    link create chars 1 ::my(var)
+    lappend mylist [set ::my(var) ""]
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create chars 4 ::my(var)
+    set ::my(var) x
+    catch {set ::my(var) xyzz} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create -r chars 4 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {
+can't set "::my(var)": wrong size of chars value
+can't set "::my(var)": wrong size of chars value
+can't set "::my(var)": linked variable is read-only}
+test link-11.1 {link char} {
+    set mylist [list]
+    link create char 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1234} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create char 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r char 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have char value
+120
+can't set "::my(var)": variable must have char value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-12.1 {link unsigned char} {
+    set mylist [list]
+    link create uchar 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1234} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {set ::my(var) -1} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create uchar 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r uchar 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have unsigned char value
+120
+can't set "::my(var)": variable must have unsigned char value
+can't set "::my(var)": variable must have unsigned char value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-13.1 {link short} {
+    set mylist [list]
+    link create short 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 123456} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create short 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r short 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have short value
+120
+can't set "::my(var)": variable must have short value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-14.1 {link unsigned short} {
+    set mylist [list]
+    link create ushort 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 123456} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {set ::my(var) -1} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create ushort 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r ushort 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have unsigned short value
+120
+can't set "::my(var)": variable must have unsigned short value
+can't set "::my(var)": variable must have unsigned short value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-15.1 {link int} {
+    set mylist [list]
+    link create int 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1e3} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create int 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r int 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have integer value
+120
+can't set "::my(var)": variable must have integer value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-16.1 {link unsigned int} {
+    set mylist [list]
+    link create uint 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1e33} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {set ::my(var) -1} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create uint 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r uint 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have unsigned int value
+120
+can't set "::my(var)": variable must have unsigned int value
+can't set "::my(var)": variable must have unsigned int value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-17.1 {link long} {
+    set mylist [list]
+    link create long 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1e33} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create long 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r long 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have long value
+120
+can't set "::my(var)": variable must have long value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-18.1 {link unsigned long} {
+    set mylist [list]
+    link create ulong 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1e33} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {set ::my(var) -1} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create ulong 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r ulong 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have unsigned long value
+120
+can't set "::my(var)": variable must have unsigned long value
+can't set "::my(var)": variable must have unsigned long value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-19.1 {link wide} {
+    set mylist [list]
+    link create wide 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1e33} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create wide 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r wide 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have wide integer value
+120
+can't set "::my(var)": variable must have wide integer value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-20.1 {link unsigned wide} {
+    set mylist [list]
+    link create uwide 1 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist [set ::my(var) 120]
+    catch {set ::my(var) 1e33} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {set ::my(var) -1} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var)
+    link create uwide 4 ::my(var)
+    catch {set ::my(var) {1 2 3}} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) {1 2 3 4}
+    lappend mylist $my(var)
+    link remove ::my(var)
+    link create -r uwide 2 ::my(var)
+    catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": variable must have unsigned wide int value
+120
+can't set "::my(var)": variable must have unsigned wide int value
+can't set "::my(var)": variable must have unsigned wide int value
+can't set "::my(var)": wrong dimension
+1 2 3 4
+can't set "::my(var)": linked variable is read-only}
+test link-21.1 {link string} {
+    set mylist [list]
+    link create string 1 ::my(var)
+    lappend mylist [set ::my(var) ""]
+    lappend mylist [set ::my(var) "xyz"]
+    lappend mylist $::my(var)
+    link remove ::my(var)
+    link create -r string 4 ::my(var)
+    catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {
+xyz
+xyz
+can't set "::my(var)": linked variable is read-only}
+test link-22.1 {link binary} {
+    set mylist [list]
+    link create binary 1 ::my(var)
+    set ::my(var) x
+    catch {set ::my(var) xy} my(msg); lappend mylist $my(msg);unset my(msg)
+    lappend mylist $::my(var)
+    link remove ::my(var)
+    link create binary 4 ::my(var)
+    catch {set ::my(var) abc} my(msg); lappend mylist $my(msg);unset my(msg)
+    catch {set ::my(var) abcde} my(msg); lappend mylist $my(msg);unset my(msg)
+    set ::my(var) abcd
+    lappend mylist $::my(var)
+    link remove ::my(var)
+    link create -r binary 4 ::my(var)
+    catch {set ::my(var) xyzv} my(msg); lappend mylist $my(msg);unset my(msg)
+    link remove ::my(var); unset my; join $mylist "\n"
+} {can't set "::my(var)": wrong size of binary value
+x
+can't set "::my(var)": wrong size of binary value
+can't set "::my(var)": wrong size of binary value
+abcd
+can't set "::my(var)": linked variable is read-only}
 
 # cleanup
 ::tcltest::cleanupTests