Tcl Source Code

Artifact [319d40d545]
Login

Artifact 319d40d5458269697c5f0bb45669cb00effe755f:

Attachment "1992824.patch" to ticket [1992824fff] added by dgp 2008-09-17 23:48:52.
Index: generic/tcl.decls
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tcl.decls,v
retrieving revision 1.143
diff -u -r1.143 tcl.decls
--- generic/tcl.decls	21 Aug 2008 21:35:37 -0000	1.143
+++ generic/tcl.decls	17 Sep 2008 16:45:58 -0000
@@ -2146,6 +2146,13 @@
 	                     Tcl_Obj *CONST objv[])
 }
 
+# TIP#312 New Tcl_LinkArray() function
+declare 589 generic {
+    int Tcl_LinkArray(Tcl_Interp *interp, CONST char *varName, char *addr,
+            int type, int size)
+}
+
+
 ##############################################################################
 
 # Define the platform specific public Tcl interface.  These functions are
Index: generic/tcl.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tcl.h,v
retrieving revision 1.270
diff -u -r1.270 tcl.h
--- generic/tcl.h	3 Sep 2008 05:43:31 -0000	1.270
+++ generic/tcl.h	17 Sep 2008 16:45:58 -0000
@@ -1078,6 +1078,8 @@
 #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
 
 /*
Index: generic/tclDecls.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclDecls.h,v
retrieving revision 1.145
diff -u -r1.145 tclDecls.h
--- generic/tclDecls.h	21 Aug 2008 21:35:38 -0000	1.145
+++ generic/tclDecls.h	17 Sep 2008 16:45:59 -0000
@@ -3566,6 +3566,13 @@
 				ClientData clientData, int objc, 
 				Tcl_Obj *CONST objv[]);
 #endif
+#ifndef Tcl_LinkArray_TCL_DECLARED
+#define Tcl_LinkArray_TCL_DECLARED
+/* 589 */
+EXTERN int		Tcl_LinkArray (Tcl_Interp * interp, 
+				CONST char * varName, char * addr, int type, 
+				int size);
+#endif
 
 typedef struct TclStubHooks {
     CONST struct TclPlatStubs *tclPlatStubs;
@@ -4214,6 +4221,7 @@
     int (*tcl_NRCmdSwap) (Tcl_Interp * interp, Tcl_Command cmd, int objc, Tcl_Obj *CONST objv[], int flags); /* 586 */
     void (*tcl_NRAddCallback) (Tcl_Interp * interp, Tcl_NRPostProc * postProcPtr, ClientData data0, ClientData data1, ClientData data2, ClientData data3); /* 587 */
     int (*tcl_NRCallObjProc) (Tcl_Interp * interp, Tcl_ObjCmdProc * objProc, ClientData clientData, int objc, Tcl_Obj *CONST objv[]); /* 588 */
+    int (*tcl_LinkArray) (Tcl_Interp * interp, CONST char * varName, char * addr, int type, int size); /* 589 */
 } TclStubs;
 
 #if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
@@ -6642,6 +6650,10 @@
 #define Tcl_NRCallObjProc \
 	(tclStubsPtr->tcl_NRCallObjProc) /* 588 */
 #endif
+#ifndef Tcl_LinkArray
+#define Tcl_LinkArray \
+	(tclStubsPtr->tcl_LinkArray) /* 589 */
+#endif
 
 #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
 
Index: generic/tclLink.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclLink.c,v
retrieving revision 1.25
diff -u -r1.25 tclLink.c
--- generic/tclLink.c	27 Apr 2008 22:21:30 -0000	1.25
+++ generic/tclLink.c	17 Sep 2008 16:45:59 -0000
@@ -30,6 +30,9 @@
 				 * 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 @@
 	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 @@
  * 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 @@
 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,18 +145,161 @@
     } 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
 	    |TCL_TRACE_WRITES|TCL_TRACE_UNSETS, LinkTraceProc, 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;
 }
@@ -174,7 +337,7 @@
 	    TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 	    LinkTraceProc, linkPtr);
     Tcl_DecrRefCount(linkPtr->varName);
-    ckfree((char *) linkPtr);
+    LinkFree(linkPtr);
 }
 
 /*
@@ -259,6 +422,9 @@
     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)
@@ -268,7 +434,7 @@
     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);
@@ -295,6 +461,11 @@
      */
 
     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:
@@ -334,10 +505,13 @@
 	    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),
@@ -349,7 +523,7 @@
     /*
      * 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.
@@ -371,6 +545,21 @@
 
     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),
@@ -381,16 +570,53 @@
 	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
@@ -408,6 +634,21 @@
 	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),
@@ -418,6 +659,23 @@
 	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),
@@ -429,6 +687,23 @@
 	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),
@@ -440,6 +715,23 @@
 	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),
@@ -451,6 +743,23 @@
 	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),
@@ -462,6 +771,23 @@
 	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),
@@ -473,6 +799,23 @@
 	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),
@@ -484,6 +827,23 @@
 	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),
@@ -498,7 +858,25 @@
 	/*
 	 * 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";
@@ -508,6 +886,23 @@
 	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),
@@ -522,11 +917,39 @@
 	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 char* 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";
     }
@@ -557,49 +980,169 @@
 {
     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 *);
@@ -609,6 +1152,23 @@
 	}
 	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).
@@ -621,6 +1181,31 @@
 }
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * 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
Index: generic/tclStubInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclStubInit.c,v
retrieving revision 1.162
diff -u -r1.162 tclStubInit.c
--- generic/tclStubInit.c	29 Jul 2008 05:30:38 -0000	1.162
+++ generic/tclStubInit.c	17 Sep 2008 16:45:59 -0000
@@ -1114,6 +1114,7 @@
     Tcl_NRCmdSwap, /* 586 */
     Tcl_NRAddCallback, /* 587 */
     Tcl_NRCallObjProc, /* 588 */
+    Tcl_LinkArray, /* 589 */
 };
 
 /* !END!: Do not edit above this line. */
Index: generic/tclTest.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclTest.c,v
retrieving revision 1.124
diff -u -r1.124 tclTest.c
--- generic/tclTest.c	20 Aug 2008 13:14:41 -0000	1.124
+++ generic/tclTest.c	17 Sep 2008 16:45:59 -0000
@@ -273,6 +273,9 @@
 			    Tcl_Interp *interp, int argc, const char **argv);
 static int		TestlinkCmd(ClientData dummy,
 			    Tcl_Interp *interp, int argc, const char **argv);
+static int		TestlinkarrayCmd(ClientData dummy,
+                            Tcl_Interp *interp, int objc,
+                            Tcl_Obj *const objv[]);
 static int		TestlocaleCmd(ClientData dummy,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *const objv[]);
@@ -616,6 +619,8 @@
     Tcl_CreateCommand(interp, "testinterpdelete", TestinterpdeleteCmd,
 	    (ClientData) 0, NULL);
     Tcl_CreateCommand(interp, "testlink", TestlinkCmd, (ClientData) 0, NULL);
+    Tcl_CreateObjCommand(interp, "testlinkarray", TestlinkarrayCmd,
+            (ClientData) 0, NULL);
     Tcl_CreateObjCommand(interp, "testlocale", TestlocaleCmd, (ClientData) 0,
 	    NULL);
     Tcl_CreateCommand(interp, "testpanic", TestpanicCmd, (ClientData) 0, NULL);
@@ -3057,6 +3062,118 @@
 /*
  *----------------------------------------------------------------------
  *
+ * TestlinkarrayCmd --
+ *
+ *      This function is invoked to process the "testlinkarray" Tcl command.
+ *      It is used to test the 'Tcl_LinkArray' function.
+ *
+ * Results:
+ *      A standard Tcl result.
+ *
+ * Side effects:
+ *	Creates, deletes, and invokes variable links.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TestlinkarrayCmd(
+    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", "char*", "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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * TestlocaleCmd --
  *
  *	This procedure implements the "testlocale" command.  It is used
Index: tests/link.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/link.test,v
retrieving revision 1.17
diff -u -r1.17 link.test
--- tests/link.test	13 Dec 2007 15:26:06 -0000	1.17
+++ tests/link.test	17 Sep 2008 16:46:00 -0000
@@ -19,6 +19,7 @@
 }
 
 testConstraint testlink [llength [info commands testlink]]
+testConstraint testlinkarray [llength [info commands testlinkarray]]
 
 foreach i {int real bool string} {
     catch {unset $i}
@@ -79,7 +80,7 @@
     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 +235,7 @@
     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 {
@@ -269,6 +270,296 @@
 	testlink update 47 {} {} {} {} {} {} {} {} {} {} {} {} {}
     } msg] $msg $int
 } {0 {} 47}
+test link-9.1 {linkarray usage messages} {
+  set mylist [list]
+  catch {testlinkarray} my(msg); lappend mylist $my(msg);unset my(msg)
+  catch {testlinkarray x} my(msg); lappend mylist $my(msg);unset my(msg)
+  catch {testlinkarray update} my(msg); lappend mylist $my(msg);unset my(msg)
+  catch {testlinkarray remove} my(msg); lappend mylist $my(msg);unset my(msg)
+  catch {testlinkarray create} my(msg); lappend mylist $my(msg);unset my(msg)
+  catch {testlinkarray create xx 1 my} my(msg); lappend mylist $my(msg);unset my(msg)
+  catch {testlinkarray create char* 0 my} my(msg); lappend mylist $my(msg);unset my(msg)
+  join $mylist "\n"
+} {wrong # args: should be "testlinkarray option args"
+bad option "x": must be update, remove, or create
+
+
+wrong # args: should be "testlinkarray create ?-readonly? type size name ?address?"
+bad type "xx": must be char, uchar, short, ushort, int, uint, long, ulong, wide, uwide, float, double, string, char*, or binary
+wrong array size given}
+test link-10.1 {linkarray char*} {
+  set mylist [list]
+  testlinkarray create char* 1 ::my(var)
+  lappend mylist [set ::my(var) ""]
+  catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray remove ::my(var)
+  testlinkarray create char* 4 ::my(var)
+  set ::my(var) x
+  catch {set ::my(var) xyzz} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r char* 4 ::my(var)
+  catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray remove ::my(var); unset my; join $mylist "\n"
+} {
+can't set "::my(var)": wrong size of char* value
+can't set "::my(var)": wrong size of char* value
+can't set "::my(var)": linked variable is read-only}
+test link-11.1 {linkarray char} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r char 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray unsigned char} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r uchar 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray short} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r short 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray unsigned short} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r ushort 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray int} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r int 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray unsigned int} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r uint 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray long} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r long 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray unsigned long} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r ulong 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray wide} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r wide 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray unsigned wide} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r uwide 2 ::my(var)
+  catch {set ::my(var) {1 2}} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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 {linkarray string} {
+  set mylist [list]
+  testlinkarray create string 1 ::my(var)
+  lappend mylist [set ::my(var) ""]
+  lappend mylist [set ::my(var) "xyz"]
+  lappend mylist $::my(var)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r string 4 ::my(var)
+  catch {set ::my(var) x} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray remove ::my(var); unset my; join $mylist "\n"
+} {
+xyz
+xyz
+can't set "::my(var)": linked variable is read-only}
+test link-22.1 {linkarray binary} {
+  set mylist [list]
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray 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)
+  testlinkarray remove ::my(var)
+  testlinkarray create -r binary 4 ::my(var)
+  catch {set ::my(var) xyzv} my(msg); lappend mylist $my(msg);unset my(msg)
+  testlinkarray 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}
 
 catch {testlink set 0 0 0 - 0 0 0 0 0 0 0 0 0 0}
 catch {testlink delete}