OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / blt2.5 / generic / bltVector.c
diff --git a/util/src/TclTk/blt2.5/generic/bltVector.c b/util/src/TclTk/blt2.5/generic/bltVector.c
new file mode 100644 (file)
index 0000000..f2769a3
--- /dev/null
@@ -0,0 +1,2842 @@
+/*
+ * bltVector.c --
+ *
+ *     This module implements vector data objects.
+ *
+ * Copyright 1995-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness.  In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * TODO:
+ *     o Add H. Kirsch's vector binary read operation
+ *             x binread file0
+ *             x binread -file file0
+ *
+ *     o Add ASCII/binary file reader
+ *             x read fileName
+ *
+ *     o Allow Tcl-based client notifications.
+ *             vector x
+ *             x notify call Display
+ *             x notify delete Display
+ *             x notify reorder #1 #2
+ */
+
+#include "bltVecInt.h"
+#include <bltMath.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif /* HAVE_SYS_TIME_H */
+#endif /* TIME_WITH_SYS_TIME */
+
+#ifndef TCL_NAMESPACE_ONLY
+#define TCL_NAMESPACE_ONLY TCL_GLOBAL_ONLY
+#endif
+
+#define DEF_ARRAY_SIZE         64
+#define VECFLAGS(v)    \
+       (((v)->varNsPtr != NULL) ? (TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY) : 0);
+#define TRACE_ALL  (TCL_TRACE_WRITES | TCL_TRACE_READS | TCL_TRACE_UNSETS)
+
+
+#define VECTOR_CHAR(c) ((isalnum(UCHAR(c))) || \
+       (c == '_') || (c == ':') || (c == '@') || (c == '.'))
+
+
+/*
+ * VectorClient --
+ *
+ *     A vector can be shared by several clients.  Each client
+ *     allocates this structure that acts as its key for using the
+ *     vector.  Clients can also designate a callback routine that is
+ *     executed whenever the vector is updated or destroyed.
+ *
+ */
+typedef struct {
+    unsigned int magic;                /* Magic value designating whether this
+                                * really is a vector token or not */
+
+    VectorObject *serverPtr;   /* Pointer to the master record of the
+                                * vector.  If NULL, indicates that the
+                                * vector has been destroyed but as of
+                                * yet, this client hasn't recognized
+                                * it. */
+
+    Blt_VectorChangedProc *proc;/* Routine to call when the contents
+                                * of the vector change or the vector
+                                * is deleted. */
+
+    ClientData clientData;     /* Data passed whenever the vector
+                                * change procedure is called. */
+
+    Blt_ChainLink *linkPtr;    /* Used to quickly remove this entry from
+                                * its server's client chain. */
+} VectorClient;
+
+static Tcl_CmdDeleteProc VectorInstDeleteProc;
+static Tcl_InterpDeleteProc VectorInterpDeleteProc;
+
+#if (TCL_MAJOR_VERSION == 7)
+static Tcl_CmdProc VectorCmd;
+#endif
+
+#if defined(HAVE_SRAND48) && defined(NO_DECL_SRAND48)
+extern void srand48 _ANSI_ARGS_((long int seed));
+#endif
+
+static VectorObject *
+FindVectorInNamespace(dataPtr, nsPtr, vecName)
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    Tcl_Namespace *nsPtr;
+    CONST char *vecName;
+{
+    Tcl_DString dString;
+    CONST char *name;
+    Blt_HashEntry *hPtr;
+
+    name = Blt_GetQualifiedName(nsPtr, vecName, &dString);
+    hPtr = Blt_FindHashEntry(&(dataPtr->vectorTable), name);
+    Tcl_DStringFree(&dString);
+    if (hPtr != NULL) {
+       return (VectorObject *)Blt_GetHashValue(hPtr);
+    }
+    return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetVectorObject --
+ *
+ *     Searches for the vector associated with the name given.
+ *     Allow for a range specification.
+ *
+ * Results:
+ *     Returns a pointer to the vector if found, otherwise NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static VectorObject *
+GetVectorObject(dataPtr, name, flags)
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    CONST char *name;
+    int flags;
+{
+    CONST char *vecName;
+    Tcl_Namespace *nsPtr;
+    VectorObject *vPtr;
+
+    nsPtr = NULL;
+    vecName = name;
+    if (Blt_ParseQualifiedName(dataPtr->interp, name, &nsPtr, &vecName) 
+       != TCL_OK) {
+       return NULL;            /* Can't find namespace. */
+    } 
+    vPtr = NULL;
+    if (nsPtr != NULL) {
+       vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+    } else {
+       if (flags & NS_SEARCH_CURRENT) {
+           nsPtr = Tcl_GetCurrentNamespace(dataPtr->interp);
+           vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+       }
+       if ((vPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+           nsPtr = Tcl_GetGlobalNamespace(dataPtr->interp);
+           vPtr = FindVectorInNamespace(dataPtr, nsPtr, vecName);
+       }
+    }
+    return vPtr;
+}
+
+void
+Blt_VectorUpdateRange(vPtr)
+    VectorObject *vPtr;
+{
+    double min, max;
+    register int i;
+
+    min = DBL_MAX, max = -DBL_MAX;
+    for (i = 0; i < vPtr->length; i++) {
+       if (FINITE(vPtr->valueArr[i])) {
+           min = max = vPtr->valueArr[i];
+           break;
+       }
+    }
+    for (/* empty */; i < vPtr->length; i++) {
+       if (FINITE(vPtr->valueArr[i])) {
+           if (min > vPtr->valueArr[i]) {
+               min = vPtr->valueArr[i]; 
+           } else if (max < vPtr->valueArr[i]) { 
+               max = vPtr->valueArr[i]; 
+           } 
+       } 
+    } 
+    vPtr->min = min;
+    vPtr->max = max;
+    vPtr->notifyFlags &= ~UPDATE_RANGE;
+}
+
+static int
+GetIntEnd(interp, string, iPtr, endVal)
+    Tcl_Interp *interp;
+    char *string;
+    int *iPtr;
+    int endVal;
+{
+    char *cp = string;
+    int add = 0;
+    
+    if (strncmp("end", cp, 3) == 0) {
+        if (*cp == 0) {
+            *iPtr = endVal;
+            return TCL_OK;
+        }
+        add = endVal;
+        cp += 3;
+    }
+    if (Tcl_GetInt(interp, cp, iPtr) != TCL_OK) {
+        long l;
+        Tcl_ResetResult(interp);
+        if (Tcl_ExprLong(interp, cp, &l) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        *iPtr = (int)l;
+    }
+    *iPtr += add;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorGetIndex --
+ *
+ *     Converts the string representing an index in the vector, to
+ *     its numeric value.  A valid index may be an numeric string of
+ *     the string "end" (indicating the last element in the string).
+ *
+ * Results:
+ *     A standard Tcl result.  If the string is a valid index, TCL_OK
+ *     is returned.  Otherwise TCL_ERROR is returned and interp->result
+ *     will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorGetIndex(interp, vPtr, string, indexPtr, flags, procPtrPtr)
+    Tcl_Interp *interp;
+    VectorObject *vPtr;
+    CONST char *string;
+    int *indexPtr;
+    int flags;
+    Blt_VectorIndexProc **procPtrPtr;
+{
+    char c;
+    int value = -1, value2 = -1, sz, result = TCL_OK;
+    char *comma, *zBuf;
+    char zStatic[201];
+    
+    /* Need a static copy of index. */
+    sz = strlen(string);
+    if (sz>=200) {
+        zBuf = Blt_Malloc(sz+1);
+        strcpy(zBuf, string);
+        string = zBuf;
+    } else {
+        strcpy(zStatic, string);
+        string = zStatic;
+    }
+
+    c = string[0];
+
+    /* Treat the index "end" like a numeric index.  */
+
+    if ((c == 'e') && (strcmp(string, "end") == 0)) {
+       if (vPtr->length < 1) {
+           if (interp != NULL) {
+               Tcl_AppendResult(interp, "bad index \"end\": vector is empty", 
+                                (char *)NULL);
+           }
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+       *indexPtr = vPtr->length - 1;
+       goto cleanup;
+    } else if ((c == '+') && (strcmp(string, "++end") == 0)) {
+       *indexPtr = vPtr->length;
+       goto cleanup;
+    }
+    if (procPtrPtr != NULL) {
+       Blt_HashEntry *hPtr;
+
+       hPtr = Blt_FindHashEntry(&(vPtr->dataPtr->indexProcTable), string);
+       if (hPtr != NULL) {
+           *indexPtr = SPECIAL_INDEX;
+           *procPtrPtr = (Blt_VectorIndexProc *) Blt_GetHashValue(hPtr);
+           goto cleanup;
+       }
+    }
+    if ((comma=strchr(string, ','))) {
+        char *ccp;
+        *comma = 0;
+        ccp = (char*)comma+1;
+        if ((GetIntEnd(interp, ccp, &value2, vPtr->numcols-1) == TCL_OK) &&
+            (GetIntEnd(interp, (char *)string, &value, vPtr->numcols-1) ==
+             TCL_OK)) {
+                if (value2<0 || value<0) {
+                    if (flags & INDEX_VAR_TRACE) {
+                        *indexPtr = SPECIAL_INDEX;
+                        if (procPtrPtr != NULL) {
+                            *procPtrPtr = NULL;
+                        }
+                        goto cleanup;
+                    } else {
+                        Tcl_AppendResult(interp, "negative index not allowed", 0);
+                        result = TCL_ERROR;
+                        goto cleanup;
+                    }
+                }
+                if (value2>=vPtr->numcols) {
+                    Tcl_AppendResult(interp, "col index is > numcols", 0);
+                    result = TCL_ERROR;
+                    goto cleanup;
+                }
+                value = (value*vPtr->numcols)+value2; 
+                *comma = ',';
+        } else {
+            *comma = ',';
+            result = TCL_ERROR;
+            goto cleanup;
+        }
+        
+    } else if (Tcl_GetInt(interp, (char *)string, &value) != TCL_OK) {
+       long int lvalue;
+       /*   
+        * Unlike Tcl_GetInt, Tcl_ExprLong needs a valid interpreter,
+        * but the interp passed in may be NULL.  So we have to use
+        * vPtr->interp and then reset the result.  
+        */
+       if (Tcl_ExprLong(vPtr->interp, (char *)string, &lvalue) != TCL_OK) {
+           Tcl_ResetResult(vPtr->interp);
+           if (interp != NULL) {
+               Tcl_AppendResult(interp, "bad index \"", string, "\"", 
+                                (char *)NULL);
+           }
+             result = TCL_ERROR;
+             goto cleanup;
+         }
+       value = lvalue;
+    }
+    /*
+     * Correct the index by the current value of the offset. This makes
+     * all the numeric indices non-negative, which is how we distinguish
+     * the special non-numeric indices.
+     */
+    value -= vPtr->offset;
+
+    if ((value < 0) || ((flags & INDEX_CHECK) && (value >= vPtr->length))) {
+       if (interp != NULL) {
+           Tcl_AppendResult(interp, "index \"", string, "\" is out of range", 
+                        (char *)NULL);
+       }
+         result = TCL_ERROR;
+         goto cleanup;
+     }
+    *indexPtr = (int)value;
+cleanup:
+    if (string != zStatic) {
+        Blt_Free( string );
+    }
+    return result;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorGetIndexRange --
+ *
+ *     Converts the string representing an index in the vector, to
+ *     its numeric value.  A valid index may be an numeric string of
+ *     the string "end" (indicating the last element in the string).
+ *
+ * Results:
+ *     A standard Tcl result.  If the string is a valid index, TCL_OK
+ *     is returned.  Otherwise TCL_ERROR is returned and interp->result
+ *     will contain an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorGetIndexRange(interp, vPtr, string, flags, procPtrPtr)
+    Tcl_Interp *interp;
+    VectorObject *vPtr;
+    CONST char *string;
+    int flags;
+    Blt_VectorIndexProc **procPtrPtr;
+{
+    int ielem;
+    char *colon;
+
+    colon = NULL;
+    if (flags & INDEX_COLON) {
+       colon = strchr(string, ':');
+    }
+    if (colon != NULL) {
+       if (string == colon) {
+           vPtr->first = 0;    /* Default to the first index */
+       } else {
+           int result;
+
+           *colon = '\0';
+           result = Blt_VectorGetIndex(interp, vPtr, string, &ielem, flags,
+               (Blt_VectorIndexProc **) NULL);
+           *colon = ':';
+           if (result != TCL_OK) {
+               return TCL_ERROR;
+           }
+           vPtr->first = ielem;
+       }
+       if (*(colon + 1) == '\0') {
+           /* Default to the last index */
+           vPtr->last = (vPtr->length > 0) ? vPtr->length - 1 : 0;
+       } else {
+           if (Blt_VectorGetIndex(interp, vPtr, colon + 1, &ielem, flags,
+                   (Blt_VectorIndexProc **) NULL) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           vPtr->last = ielem;
+       }
+       if (vPtr->first > vPtr->last) {
+           if (interp != NULL) {
+               Tcl_AppendResult(interp, "bad range \"", string,
+                        "\" (first > last)", (char *)NULL);
+           }
+           return TCL_ERROR;
+       }
+    } else {
+       if (Blt_VectorGetIndex(interp, vPtr, string, &ielem, flags, 
+                      procPtrPtr) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       vPtr->last = vPtr->first = ielem;
+    }
+    return TCL_OK;
+}
+
+VectorObject *
+Blt_VectorParseElement(interp, dataPtr, start, endPtr, flags)
+    Tcl_Interp *interp;
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    CONST char *start;
+    char **endPtr;
+    int flags;
+{
+    register char *p;
+    char saved;
+    VectorObject *vPtr;
+
+    p = (char *)start;
+    /* Find the end of the vector name */
+    while (VECTOR_CHAR(*p)) {
+       p++;
+    }
+    saved = *p;
+    *p = '\0';
+
+    vPtr = GetVectorObject(dataPtr, start, flags);
+    if (vPtr == NULL) {
+       if (interp != NULL) {
+           Tcl_AppendResult(interp, "can't find vector \"", start, "\"", 
+                            (char *)NULL);
+       }
+       *p = saved;
+       return NULL;
+    }
+    *p = saved;
+    vPtr->first = 0;
+    vPtr->last = vPtr->length - 1;
+    if (*p == '(') {
+       int count, result;
+
+       start = p + 1;
+       p++;
+
+       /* Find the matching right parenthesis */
+       count = 1;
+       while (*p != '\0') {
+           if (*p == ')') {
+               count--;
+               if (count == 0) {
+                   break;
+               }
+           } else if (*p == '(') {
+               count++;
+           }
+           p++;
+       }
+       if (count > 0) {
+           if (interp != NULL) {
+               Tcl_AppendResult(interp, "unbalanced parentheses \"", start, 
+                       "\"", (char *)NULL);
+           }
+           return NULL;
+       }
+       *p = '\0';
+       result = Blt_VectorGetIndexRange(interp, vPtr, start, 
+               (INDEX_COLON | INDEX_CHECK), (Blt_VectorIndexProc **) NULL);
+       *p = ')';
+       if (result != TCL_OK) {
+           return NULL;
+       }
+       p++;
+    }
+    if (endPtr != NULL) {
+      *endPtr = p;
+    }
+    return vPtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorNotifyClients --
+ *
+ *     Notifies each client of the vector that the vector has changed
+ *     (updated or destroyed) by calling the provided function back.
+ *     The function pointer may be NULL, in that case the client is
+ *     not notified.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The results depend upon what actions the client callbacks
+ *     take.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorNotifyClients(clientData)
+    ClientData clientData;
+{
+    VectorObject *vPtr = clientData;
+    Blt_ChainLink *linkPtr;
+    VectorClient *clientPtr;
+    Blt_VectorNotify notify;
+
+    notify = (vPtr->notifyFlags & NOTIFY_DESTROYED)
+       ? BLT_VECTOR_NOTIFY_DESTROY : BLT_VECTOR_NOTIFY_UPDATE;
+    vPtr->notifyFlags &= ~(NOTIFY_UPDATED | NOTIFY_DESTROYED | NOTIFY_PENDING);
+
+    for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+       linkPtr = Blt_ChainNextLink(linkPtr)) {
+       clientPtr = Blt_ChainGetValue(linkPtr);
+       if (clientPtr->proc != NULL) {
+           (*clientPtr->proc) (vPtr->interp, clientPtr->clientData, notify);
+       }
+    }
+    /*
+     * Some clients may not handle the "destroy" callback properly
+     * (they should call Blt_FreeVectorId to release the client
+     * identifier), so mark any remaining clients to indicate that
+     * vector's server has gone away.
+     */
+    if (notify == BLT_VECTOR_NOTIFY_DESTROY) {
+       for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+           linkPtr = Blt_ChainNextLink(linkPtr)) {
+           clientPtr = Blt_ChainGetValue(linkPtr);
+           clientPtr->serverPtr = NULL;
+       }
+    }
+    if (vPtr->callback) {
+        Tcl_EvalObjEx( vPtr->interp, vPtr->callback, TCL_GLOBAL_ONLY);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorUpdateClients --
+ *
+ *     Notifies each client of the vector that the vector has changed
+ *     (updated or destroyed) by calling the provided function back.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The individual client callbacks are eventually invoked.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorUpdateClients(vPtr)
+    VectorObject *vPtr;
+{
+    vPtr->dirty++;
+    vPtr->max = vPtr->min = bltNaN;
+    if (vPtr->notifyFlags & NOTIFY_NEVER) {
+       return;
+    }
+    vPtr->notifyFlags |= NOTIFY_UPDATED;
+    if (vPtr->notifyFlags & NOTIFY_ALWAYS) {
+       Blt_VectorNotifyClients(vPtr);
+       return;
+    }
+    if (!(vPtr->notifyFlags & NOTIFY_PENDING)) {
+       vPtr->notifyFlags |= NOTIFY_PENDING;
+       Tcl_DoWhenIdle(Blt_VectorNotifyClients, vPtr);
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorFlushCache --
+ *
+ *     Unsets all the elements of the Tcl array variable associated
+ *     with the vector, freeing memory associated with the variable.
+ *     This includes both the hash table and the hash keys.  The down
+ *     side is that this effectively flushes the caching of vector
+ *     elements in the array.  This means that the subsequent reads
+ *     of the array will require a decimal to string conversion.
+ *
+ *     This is needed when the vector changes its values, making
+ *     the array variable out-of-sync.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     All elements of array variable (except one) are unset, freeing
+ *     the memory associated with the variable.
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorFlushCache(vPtr)
+    VectorObject *vPtr;
+{
+    Tcl_CallFrame *framePtr;
+    Tcl_Interp *interp = vPtr->interp;
+
+    if (vPtr->arrayName == NULL) {
+       return;                 /* Doesn't use the variable API */
+    }
+    framePtr = NULL;
+    if (vPtr->varNsPtr != NULL) {
+       framePtr = Blt_EnterNamespace(interp, vPtr->varNsPtr);
+    }
+    /* Turn off the trace temporarily so that we can unset all the
+     * elements in the array.  */
+
+    Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+       TRACE_ALL | vPtr->varFlags, Blt_VectorVarTrace, vPtr);
+
+    if (vPtr->numcols>1) {
+        Tcl_Interp *i = vPtr->interp;
+        Tcl_Obj *o = Tcl_NewListObj(0,0);
+        /* Preserve non-numeric datat that Tktable stores. */
+        
+        Tcl_ListObjAppendElement(i, o, Tcl_NewStringObj("::array",-1));
+        Tcl_ListObjAppendElement(i, o, Tcl_NewStringObj("unset",-1));
+        Tcl_ListObjAppendElement(i, o, Tcl_NewStringObj(vPtr->arrayName,-1));
+        Tcl_ListObjAppendElement(i, o, Tcl_NewStringObj("[0-9]*,[0-9]*",-1));
+        Tcl_IncrRefCount(o);
+        if (Tcl_EvalObjEx(i, o, 0) != TCL_OK) {
+            /* printf("FAIL: %s\n", Tcl_GetStringResult(interp)); */
+        }
+        Tcl_DecrRefCount(o);
+    } else {
+        /* Clear all the element entries from the entire array */
+        Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+        /* Restore the "end" index by default and the trace on the entire array */
+        Tcl_SetVar2(interp, vPtr->arrayName, "end", "", vPtr->varFlags);
+    }
+
+    Tcl_TraceVar2(interp, vPtr->arrayName, (char *)NULL,
+       TRACE_ALL | vPtr->varFlags, Blt_VectorVarTrace, vPtr);
+
+    if ((vPtr->varNsPtr != NULL) && (framePtr != NULL)) {
+       Blt_LeaveNamespace(interp, framePtr);   /* Go back to current */
+    }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorLookupName --
+ *
+ *     Searches for the vector associated with the name given.  Allow
+ *     for a range specification.
+ *
+ * Results:
+ *     Returns a pointer to the vector if found, otherwise NULL.
+ *     If the name is not associated with a vector and the
+ *     TCL_LEAVE_ERR_MSG flag is set, and interp->result will contain
+ *     an error message.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorLookupName(dataPtr, vecName, vPtrPtr)
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    char *vecName;
+    VectorObject **vPtrPtr;
+{
+    VectorObject *vPtr;
+    char *endPtr;
+
+    vPtr = Blt_VectorParseElement(dataPtr->interp, dataPtr, vecName, &endPtr, 
+       NS_SEARCH_BOTH);
+    if (vPtr == NULL) {
+       return TCL_ERROR;
+    }
+    if (*endPtr != '\0') {
+       Tcl_AppendResult(dataPtr->interp, 
+                        "extra characters after vector name", (char *)NULL);
+       return TCL_ERROR;
+    }
+    *vPtrPtr = vPtr;
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * DeleteCommand --
+ *
+ *     Deletes the Tcl command associated with the vector, without
+ *     triggering a callback to "VectorInstDeleteProc".
+ *
+ * Results:
+ *     None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+DeleteCommand(vPtr)
+    VectorObject *vPtr;                /* Vector associated with the Tcl command. */
+{
+    Tcl_Interp *interp = vPtr->interp;
+    char *qualName;            /* Name of Tcl command. */
+    Tcl_CmdInfo cmdInfo;
+    Tcl_DString dString;
+
+    Tcl_DStringInit(&dString);
+    qualName = Blt_GetQualifiedName(
+       Blt_GetCommandNamespace(interp, vPtr->cmdToken), 
+       Tcl_GetCommandName(interp, vPtr->cmdToken), &dString);
+    if (Tcl_GetCommandInfo(interp, qualName, &cmdInfo)) {
+       cmdInfo.deleteProc = NULL;      /* Disable the callback before
+                                        * deleting the Tcl command.*/
+       Tcl_SetCommandInfo(interp, qualName, &cmdInfo);
+       Tcl_DeleteCommandFromToken(interp, vPtr->cmdToken);
+    }
+    Tcl_DStringFree(&dString);
+    vPtr->cmdToken = 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * UnmapVariable --
+ *
+ *     Destroys the trace on the current Tcl variable designated
+ *     to access the vector.
+ *
+ * Results:
+ *     None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+UnmapVariable(vPtr)
+    VectorObject *vPtr;
+{
+    Tcl_Interp *interp = vPtr->interp;
+    Tcl_CallFrame *framePtr;
+
+    framePtr = NULL;
+    if (vPtr->varNsPtr != NULL) {      /* Activate namespace */
+       framePtr = Blt_EnterNamespace(interp, vPtr->varNsPtr);
+    }
+    /* Unset the entire array */
+    Tcl_UntraceVar2(interp, vPtr->arrayName, (char *)NULL,
+       (TRACE_ALL | vPtr->varFlags), Blt_VectorVarTrace, vPtr);
+    Tcl_UnsetVar2(interp, vPtr->arrayName, (char *)NULL, vPtr->varFlags);
+
+    if ((vPtr->varNsPtr != NULL) && (framePtr != NULL)) {
+       /* Go back to current namespace */
+       Blt_LeaveNamespace(interp, framePtr);
+    }
+    if (vPtr->arrayName != NULL) {
+       Blt_Free(vPtr->arrayName);
+       vPtr->arrayName = NULL;
+    }
+    vPtr->varNsPtr = NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorMapVariable --
+ *
+ *     Sets up traces on a Tcl variable to access the vector.
+ *
+ *     If another variable is already mapped, it's first untraced and
+ *     removed.  Don't do anything else for variables named "" (even
+ *     though Tcl allows this pathology). Saves the name of the new
+ *     array variable.
+ *
+ * Results:
+ *     A standard Tcl result. If an error occurs setting the variable
+ *     TCL_ERROR is returned and an error message is left in the
+ *     interpreter.
+ *
+ * Side effects:
+ *     Traces are set for the new variable. The new variable name is
+ *     saved in a malloc'ed string in vPtr->arrayName.  If this
+ *     variable is non-NULL, it indicates that a Tcl variable has
+ *     been mapped to this vector.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorMapVariable(interp, vPtr, name)
+    Tcl_Interp *interp;
+    VectorObject *vPtr;
+    CONST char *name;
+{
+    Tcl_Namespace *nsPtr;
+    Tcl_CallFrame *framePtr;
+    CONST char *varName;
+    CONST char *result;
+
+    if (vPtr->arrayName != NULL) {
+       UnmapVariable(vPtr);
+    }
+    if ((name == NULL) || (name[0] == '\0')) {
+       return TCL_OK;          /* If the variable name is the empty
+                                * string, simply return after
+                                * removing any existing variable. */
+    }
+    framePtr = NULL;
+
+    /* Get the variable name (without the namespace qualifier). */
+    if (Blt_ParseQualifiedName(interp, name, &nsPtr, &varName) != TCL_OK) {
+       Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+           (char *)NULL);
+       return TCL_ERROR;
+    }
+    /* We need to use fully qualified names. */
+    varName = name;
+    if (nsPtr != NULL) {
+       /* [incr Tcl] 2.x doesn't like qualifiers with variable names,
+        * so we need to enter the namespace if one was designated. */
+       framePtr = Blt_EnterNamespace(interp, nsPtr);
+    }
+    /*
+     * To play it safe, delete the variable first.  This has
+     * side-effect of unmapping the variable from any vector that may
+     * be currently using it.
+     */
+    Tcl_UnsetVar2(interp, (char *)varName, (char *)NULL, 0);
+
+    /* Set the index "end" in the array.  This will create the
+     * variable immediately so that we can check its namespace
+     * context.  */
+    result = Tcl_SetVar2(interp, (char *)varName, "end", "", TCL_LEAVE_ERR_MSG);
+
+    /* Determine if the variable is global or not.  If there wasn't a
+     * namespace qualifier, it still may be global.  We need to look
+     * inside the Var structure to see what it's namespace field says.
+     * NULL indicates that it's local.  */
+
+    vPtr->varNsPtr = Blt_GetVariableNamespace(interp, varName);
+    vPtr->varFlags = (vPtr->varNsPtr != NULL) ?
+       (TCL_NAMESPACE_ONLY | TCL_GLOBAL_ONLY) : 0;
+
+    if (result != NULL) {
+       /* Trace the array on reads, writes, and unsets */
+       Tcl_TraceVar2(interp, (char *)varName, (char *)NULL, 
+               (TRACE_ALL | vPtr->varFlags), Blt_VectorVarTrace, vPtr);
+    }
+    if ((nsPtr != NULL) && (framePtr != NULL)) {
+       Blt_LeaveNamespace(interp, framePtr);   /* Go back to current */
+    }
+    vPtr->arrayName = Blt_Strdup(varName);
+    return (result == NULL) ? TCL_ERROR : TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorChangeLength --
+ *
+ *     Resizes the vector to the new size.
+ *
+ *     The new size of the vector is computed by doubling the
+ *     size of the vector until it fits the number of slots needed
+ *     (designated by *length*).
+ *
+ *     If the new size is the same as the old, simply adjust the
+ *     length of the vector.  Otherwise we're copying the data from
+ *     one memory location to another. The trailing elements of the 
+ *     vector need to be reset to zero.
+ *
+ *     If the storage changed memory locations, free up the old
+ *     location if it was dynamically allocated.
+ *
+ * Results:
+ *     A standard Tcl result.  If the reallocation is successful,
+ *     TCL_OK is returned, otherwise TCL_ERROR.
+ *
+ * Side effects:
+ *     Memory for the array is reallocated.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+int
+Blt_VectorChangeLength(vPtr, length)
+    VectorObject *vPtr;
+    int length;
+{
+    int newSize;               /* Size of array in elements */
+    double *newArr;
+    Tcl_FreeProc *freeProc;
+    VectorInterpData *dataPtr = vPtr->dataPtr;
+
+
+    newArr = NULL;
+    newSize = 0;
+    freeProc = TCL_STATIC;
+
+    if (length > 0) {
+       int wanted, used;
+
+         if (vPtr->numcols>1 && length%vPtr->numcols) {
+            length += (vPtr->numcols-(length%vPtr->numcols));
+        }
+        if (dataPtr->bltMaxSize>0 && length>dataPtr->bltMaxSize) {
+            Tcl_AppendResult(vPtr->interp, "vector size too large",0);
+            return TCL_ERROR;
+        }
+        wanted = length;
+       used = vPtr->length;
+
+       /* Compute the new size by doubling old size until it's big enough */
+       newSize = DEF_ARRAY_SIZE;
+       if (wanted > DEF_ARRAY_SIZE) {
+           while (newSize < wanted) {
+               newSize += newSize;
+           }
+       }
+       freeProc = vPtr->freeProc;
+       if (newSize == vPtr->size) {
+           newArr = vPtr->valueArr; /* Same size, use current array. */
+       } else {
+           /* Dynamically allocate memory for the new array. */
+            newArr = Blt_Malloc(newSize * sizeof(double));
+           if (newArr == NULL) {
+               Tcl_AppendResult(vPtr->interp, "can't allocate ", 
+               Blt_Itoa(newSize), " elements for vector \"", vPtr->name, 
+                                "\"", (char *)NULL); return TCL_ERROR;
+           }
+           if (used > wanted) {
+               used = wanted;
+           }
+           /* Copy any previous data */
+           if (used > 0) {
+               memcpy(newArr, vPtr->valueArr, used * sizeof(double));
+           }
+           freeProc = TCL_DYNAMIC;
+       }
+       /* Clear any new slots that we're now using in the array */
+       if (wanted > used) {
+           memset(newArr + used, 0, (wanted - used) * sizeof(double));
+       }
+    }
+    if ((newArr != vPtr->valueArr) && (vPtr->valueArr != NULL)) {
+       /* 
+        * We're not using the old storage anymore, so free it if it's
+        * not static.  It's static because the user previously reset
+        * the vector with a statically allocated array (setting freeProc 
+        * to TCL_STATIC).  
+        */
+       if (vPtr->freeProc != TCL_STATIC) {
+           if (vPtr->freeProc == TCL_DYNAMIC) {
+               Blt_Free(vPtr->valueArr);
+           } else {
+               (*vPtr->freeProc) ((char *)vPtr->valueArr);
+           }
+       }
+    }
+    vPtr->valueArr = newArr;
+    vPtr->size = newSize;
+    vPtr->length = length;
+    vPtr->first = 0;
+    vPtr->last = length - 1;
+    vPtr->freeProc = freeProc; /* Set the type of the new storage */
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ *     Resets the vector data.  This is called by a client to
+ *     indicate that the vector data has changed.  The vector does
+ *     not need to point to different memory.  Any clients of the
+ *     vector will be notified of the change.
+ *
+ * Results:
+ *     A standard Tcl result.  If the new array size is invalid,
+ *     TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *     new vector data is recorded.
+ *
+ * Side Effects:
+ *     Any client designated callbacks will be posted.  Memory may
+ *     be changed for the vector array.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_VectorReset(vPtr, valueArr, length, size, freeProc)
+    VectorObject *vPtr;
+    double *valueArr;          /* Array containing the elements of the
+                                * vector. If NULL, indicates to reset the 
+                                * vector.*/
+    int length;                        /* The number of elements that the vector 
+                                * currently holds. */
+    int size;                  /* The maximum number of elements that the
+                                * array can hold. */
+    Tcl_FreeProc *freeProc;    /* Address of memory deallocation routine
+                                * for the array of values.  Can also be
+                                * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+    if (vPtr->valueArr != valueArr) {  /* New array of values resides
+                                        * in different memory than
+                                        * the current vector.  */
+       if ((valueArr == NULL) || (size == 0)) {
+           /* Empty array. Set up default values */
+           freeProc = TCL_STATIC;
+           valueArr = NULL;
+           size = length = 0;
+       } else if (freeProc == TCL_VOLATILE) {
+           double *newArr;
+           /* Data is volatile. Make a copy of the value array.  */
+           newArr = Blt_Malloc(size * sizeof(double));
+           if (newArr == NULL) {
+               Tcl_AppendResult(vPtr->interp, "can't allocate ", 
+                       Blt_Itoa(size), " elements for vector \"", 
+                       vPtr->name, "\"", (char *)NULL);
+               return TCL_ERROR;
+           }
+           memcpy((char *)newArr, (char *)valueArr, 
+                  sizeof(double) * length);
+           valueArr = newArr;
+           freeProc = TCL_DYNAMIC;
+       } 
+
+       if (vPtr->freeProc != TCL_STATIC) {
+           /* Old data was dynamically allocated. Free it before
+            * attaching new data.  */
+           if (vPtr->freeProc == TCL_DYNAMIC) {
+               Blt_Free(vPtr->valueArr);
+           } else {
+               (*freeProc) ((char *)vPtr->valueArr);
+           }
+       }
+       vPtr->freeProc = freeProc;
+       vPtr->valueArr = valueArr;
+       vPtr->size = size;
+    }
+
+    vPtr->length = length;
+    if (vPtr->flush) {
+       Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+VectorObject *
+Blt_VectorNew(dataPtr)
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+{
+    VectorObject *vPtr;
+
+    vPtr = Blt_Calloc(1, sizeof(VectorObject));
+    assert(vPtr);
+    vPtr->notifyFlags = NOTIFY_WHENIDLE;
+    vPtr->freeProc = TCL_STATIC;
+    vPtr->dataPtr = dataPtr;
+    vPtr->valueArr = NULL;
+    vPtr->length = vPtr->size = 0;
+    vPtr->interp = dataPtr->interp;
+    vPtr->hashPtr = NULL;
+    vPtr->chainPtr = Blt_ChainCreate();
+    vPtr->flush = FALSE;
+    vPtr->numcols = 1;
+    vPtr->min = vPtr->max = bltNaN;
+    return vPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorFree --
+ *
+ *     Removes the memory and frees resources associated with the
+ *     vector.
+ *
+ *     o Removes the trace and the Tcl array variable and unsets
+ *       the variable.
+ *     o Notifies clients of the vector that the vector is being
+ *       destroyed.
+ *     o Removes any clients that are left after notification.
+ *     o Frees the memory (if necessary) allocated for the array.
+ *     o Removes the entry from the hash table of vectors.
+ *     o Frees the memory allocated for the name.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *
+ * ----------------------------------------------------------------------
+ */
+void
+Blt_VectorFree(vPtr)
+    VectorObject *vPtr;
+{
+    Blt_ChainLink *linkPtr;
+    VectorClient *clientPtr;
+
+    if (vPtr->cmdToken != 0) {
+       DeleteCommand(vPtr);
+    }
+    if (vPtr->arrayName != NULL) {
+       UnmapVariable(vPtr);
+    }
+    vPtr->length = 0;
+
+    /* Immediately notify clients that vector is going away */
+    if (vPtr->notifyFlags & NOTIFY_PENDING) {
+       vPtr->notifyFlags &= ~NOTIFY_PENDING;
+       Tcl_CancelIdleCall(Blt_VectorNotifyClients, vPtr);
+    }
+    vPtr->notifyFlags |= NOTIFY_DESTROYED;
+    Blt_VectorNotifyClients(vPtr);
+
+    for (linkPtr = Blt_ChainFirstLink(vPtr->chainPtr); linkPtr != NULL;
+       linkPtr = Blt_ChainNextLink(linkPtr)) {
+       clientPtr = Blt_ChainGetValue(linkPtr);
+       Blt_Free(clientPtr);
+    }
+    Blt_ChainDestroy(vPtr->chainPtr);
+    if ((vPtr->valueArr != NULL) && (vPtr->freeProc != TCL_STATIC)) {
+       if (vPtr->freeProc == TCL_DYNAMIC) {
+           Blt_Free(vPtr->valueArr);
+       } else {
+           (*vPtr->freeProc) ((char *)vPtr->valueArr);
+       }
+    }
+    if (vPtr->hashPtr != NULL) {
+       Blt_DeleteHashEntry(&(vPtr->dataPtr->vectorTable), vPtr->hashPtr);
+    }
+#ifdef NAMESPACE_DELETE_NOTIFY
+    if (vPtr->nsPtr != NULL) {
+       Blt_DestroyNsDeleteNotify(vPtr->interp, vPtr->nsPtr, vPtr);
+    }
+#endif /* NAMESPACE_DELETE_NOTIFY */
+    Blt_Free(vPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * VectorInstDeleteProc --
+ *
+ *     Deletes the command associated with the vector.  This is
+ *     called only when the command associated with the vector is
+ *     destroyed.
+ *
+ * Results:
+ *     None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+VectorInstDeleteProc(clientData)
+    ClientData clientData;
+{
+    VectorObject *vPtr = clientData;
+
+    vPtr->cmdToken = 0;
+    Blt_VectorFree(vPtr);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorCreate --
+ *
+ *     Creates a vector structure and the following items:
+ *
+ *     o Tcl command
+ *     o Tcl array variable and establishes traces on the variable
+ *     o Adds a  new entry in the vector hash table
+ *
+ * Results:
+ *     A pointer to the new vector structure.  If an error occurred
+ *     NULL is returned and an error message is left in
+ *     interp->result.
+ *
+ * Side effects:
+ *     A new Tcl command and array variable is added to the
+ *     interpreter.
+ *
+ * ---------------------------------------------------------------------- */
+VectorObject *
+Blt_VectorCreate(dataPtr, vecName, cmdName, varName, newPtr)
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    CONST char *vecName;       /* Namespace-qualified name of the vector */
+    CONST char *cmdName;       /* Name of the Tcl command mapped to
+                                * the vector */
+    CONST char *varName;       /* Name of the Tcl array mapped to the
+                                * vector */
+    int *newPtr;
+{
+    Tcl_DString dString;
+    VectorObject *vPtr;
+    int isNew;
+    CONST char *name;
+    char *qualName;
+    Tcl_Namespace *nsPtr;
+    Blt_HashEntry *hPtr;
+    Tcl_Interp *interp = dataPtr->interp;
+
+    isNew = 0;
+    nsPtr = NULL;
+    vPtr = NULL;
+
+    if (Blt_ParseQualifiedName(interp, vecName, &nsPtr, &name) != TCL_OK) {
+       Tcl_AppendResult(interp, "can't find namespace in \"", vecName, "\"",
+           (char *)NULL);
+       return NULL;
+    }
+    if (nsPtr == NULL) {
+       nsPtr = Tcl_GetCurrentNamespace(interp);
+    }
+    Tcl_DStringInit(&dString);
+    if ((name[0] == '#') && (strcmp(name, "#auto") == 0)) {
+       char string[200];
+
+       do {    /* Generate a unique vector name. */
+           sprintf(string, "vector%d", dataPtr->nextId++);
+           qualName = Blt_GetQualifiedName(nsPtr, string, &dString);
+           hPtr = Blt_FindHashEntry(&(dataPtr->vectorTable), qualName);
+       } while (hPtr != NULL);
+    } else {
+       register CONST char *p;
+
+       for (p = name; *p != '\0'; p++) {
+           if (!VECTOR_CHAR(*p)) {
+               Tcl_AppendResult(interp, "bad vector name \"", name,
+                   "\": must contain digits, letters, underscore, or period",
+                   (char *)NULL);
+               goto error;
+           }
+       }
+       qualName = Blt_GetQualifiedName(nsPtr, name, &dString);
+       vPtr = Blt_VectorParseElement((Tcl_Interp *)NULL, dataPtr, qualName, 
+               (char **)NULL, NS_SEARCH_CURRENT);
+    }
+    if (vPtr == NULL) {
+       hPtr = Blt_CreateHashEntry(&(dataPtr->vectorTable), qualName, &isNew);
+       vPtr = Blt_VectorNew(dataPtr);
+       vPtr->hashPtr = hPtr;
+       vPtr->nsPtr = nsPtr;
+
+       vPtr->name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+#ifdef NAMESPACE_DELETE_NOTIFY
+       Blt_CreateNsDeleteNotify(interp, nsPtr, vPtr, VectorInstDeleteProc);
+#endif /* NAMESPACE_DELETE_NOTIFY */
+       Blt_SetHashValue(hPtr, vPtr);
+    }
+    if (cmdName != NULL && cmdName[0]) {
+       Tcl_CmdInfo cmdInfo;
+
+       if ((cmdName == vecName) ||
+           ((name[0] == '#') && (strcmp(name, "#auto") == 0))) {
+           cmdName = qualName;
+       } 
+       if (Tcl_GetCommandInfo(interp, (char *)cmdName, &cmdInfo)) {
+#if TCL_MAJOR_VERSION > 7
+           if (vPtr != cmdInfo.objClientData) {
+#else
+           if (vPtr != cmdInfo.clientData) {
+#endif
+               Tcl_AppendResult(interp, "command \"", cmdName,
+                        "\" already exists", (char *)NULL);
+               goto error;
+           }
+           /* We get here only if the old name is the same as the new. */
+           goto checkVariable;
+       }
+    }
+    if (vPtr->cmdToken != 0 && cmdName[0]) {
+       DeleteCommand(vPtr);    /* Command already exists, delete old first */
+    }
+    if (cmdName != NULL) {
+#if (TCL_MAJOR_VERSION == 7)
+       vPtr->cmdToken = Blt_CreateCommand(interp, cmdName, Blt_VectorInstCmd, 
+               vPtr, VectorInstDeleteProc);
+#else
+       Tcl_DString dString2;
+       
+       Tcl_DStringInit(&dString2);
+       if (cmdName != qualName) {
+           if (Blt_ParseQualifiedName(interp, cmdName, &nsPtr, &name) 
+               != TCL_OK) {
+               Tcl_AppendResult(interp, "can't find namespace in \"", cmdName,
+                                "\"", (char *)NULL);
+               goto error;
+           }
+           if (nsPtr == NULL) {
+               nsPtr = Tcl_GetCurrentNamespace(interp);
+           }
+           cmdName = Blt_GetQualifiedName(nsPtr, name, &dString2);
+       }
+       vPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)cmdName, 
+               Blt_VectorInstCmd, vPtr, VectorInstDeleteProc);
+       Tcl_DStringFree(&dString2);
+#endif
+    }
+  checkVariable:
+  if (varName != NULL&& varName[0]) {
+       if ((varName[0] == '#') && (strcmp(varName, "#auto") == 0)) {
+           varName = qualName;
+       }
+         varName = qualName;
+         if (Blt_VectorMapVariable(interp, vPtr, varName) != TCL_OK) {
+           goto error;
+       }
+    }
+
+    Tcl_DStringFree(&dString);
+    *newPtr = isNew;
+    return vPtr;
+
+  error:
+    Tcl_DStringFree(&dString);
+    if (vPtr != NULL) {
+       Blt_VectorFree(vPtr);
+    }
+    return NULL;
+}
+
+
+int
+Blt_VectorDuplicate(destPtr, srcPtr)
+    VectorObject *destPtr, *srcPtr;
+{
+    int nBytes;
+    int length;
+
+    if (destPtr == srcPtr) {
+       /* Copying the same vector. */
+    }
+    length = srcPtr->last - srcPtr->first + 1;
+    if (Blt_VectorChangeLength(destPtr, length) != TCL_OK) {
+       return TCL_ERROR;
+    }
+    nBytes = length * sizeof(double);
+    memcpy(destPtr->valueArr, srcPtr->valueArr + srcPtr->first, nBytes);
+    destPtr->offset = srcPtr->offset;
+    return TCL_OK;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorNamesOp --
+ *
+ *     Reports the names of all the current vectors in the interpreter.
+ *
+ * Results:
+ *     A standard Tcl result.  interp->result will contain a list of
+ *     all the names of the vector instances.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+#if (TCL_MAJOR_VERSION == 7)
+
+static int
+VectorNamesOp(clientData, interp, argc, argv)
+    ClientData clientData;     /* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    char *name;
+    Blt_HashSearch cursor;
+
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+       hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+       name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+       if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
+           Tcl_AppendElement(interp, name);
+       }
+    }
+    return TCL_OK;
+}
+
+#else
+
+static int
+VectorNamesObjOp(clientData, interp, objc, objv)
+ClientData clientData; /* Interpreter-specific data. */
+Tcl_Interp *interp;
+int objc;
+Tcl_Obj *CONST *objv;
+{
+    VectorInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    char *name;
+    Blt_HashSearch cursor;
+
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+    hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+        name = Blt_GetHashKey(&(dataPtr->vectorTable), hPtr);
+        if ((objc == 2) || (Tcl_StringMatch(name, Tcl_GetString(objv[2])))) {
+            Tcl_AppendElement(interp, name);
+        }
+    }
+    return TCL_OK;
+}
+
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ *     Creates a Tcl command, and array variable representing an
+ *     instance of a vector.
+ *
+ *     vector a
+ *     vector b(20)
+ *     vector c(-5:14)
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreate2(clientData, interp, argStart, argc, argv)
+    ClientData clientData;     /* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argStart;
+    int argc;
+    char **argv;
+{
+    VectorInterpData *dataPtr = clientData;
+    VectorObject *vPtr;
+    char *leftParen, *rightParen;
+    int isNew, size, first, last;
+    char *cmdName, *varName;
+    int length;
+    int inspectFlags, freeOnUnset, flush;
+    char **nameArr;
+    int count, numcols = 1;
+    register int i;
+
+    /*
+     * Handle switches to the vector command and collect the vector
+     * name arguments into an array.
+     */
+    varName = cmdName = NULL;
+    freeOnUnset = dataPtr->bltFreeOnUnset;
+    nameArr = Blt_Malloc(sizeof(char *) * argc);
+    assert(nameArr);
+
+    inspectFlags = TRUE;
+    flush = dataPtr->bltFlushArray;
+    count = 0;
+    vPtr = NULL;
+    for (i = argStart; i < argc; i++) {
+       if ((inspectFlags) && (argv[i][0] == '-')) {
+           length = strlen(argv[i]);
+           if ((length > 1) &&
+               (strncmp(argv[i], "-variable", length) == 0)) {
+               if ((i + 1) == argc) {
+                   Tcl_AppendResult(interp,
+                       "no variable name supplied with \"",
+                       argv[i], "\" switch", (char *)NULL);
+                   goto error;
+               }
+               i++;
+               varName = argv[i];
+           } else if ((length > 1) &&
+               (strncmp(argv[i], "-command", length) == 0)) {
+               if ((i + 1) == argc) {
+                   Tcl_AppendResult(interp,
+                       "no command name supplied with \"",
+                       argv[i], "\" switch", (char *)NULL);
+                   goto error;
+               }
+               i++;
+               cmdName = argv[i];
+           } else if ((length > 1) &&
+               (strncmp(argv[i], "-watchunset", length) == 0)) {
+               int bool;
+
+               if ((i + 1) == argc) {
+                   Tcl_AppendResult(interp, "no value name supplied with \"",
+                       argv[i], "\" switch", (char *)NULL);
+                   goto error;
+               }
+               i++;
+               if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+                   goto error;
+               }
+               freeOnUnset = bool;
+           } else if ((length > 1) && (strncmp(argv[i], "-flush", length) == 0)) {
+               int bool;
+
+               if ((i + 1) == argc) {
+                   Tcl_AppendResult(interp, "no value name supplied with \"",
+                       argv[i], "\" switch", (char *)NULL);
+                   goto error;
+               }
+               i++;
+               if (Tcl_GetBoolean(interp, argv[i], &bool) != TCL_OK) {
+                   goto error;
+               }
+               flush = bool;
+           } else if ((length > 1) && (argv[i][1] == '-') &&
+               (argv[i][2] == '\0')) {
+               inspectFlags = FALSE;   /* Allow vector names to start with - */
+           } else {
+               Tcl_AppendResult(interp, "bad vector switch \"", argv[i], "\"",
+                   (char *)NULL);
+               goto error;
+           }
+       } else {
+           nameArr[count++] = argv[i];
+       }
+    }
+    if (varName == NULL && dataPtr->bltNoVariable) {
+        varName = "";
+    }
+    if (cmdName == NULL && dataPtr->bltNoCommand) {
+        cmdName = "";
+    }
+    if (count == 0) {
+       Tcl_AppendResult(interp, "no vector names supplied", (char *)NULL);
+       goto error;
+    }
+    if (count > 1) {
+       if ((cmdName != NULL) && (cmdName[0] != '\0')) {
+           Tcl_AppendResult(interp,
+               "can't specify more than one vector with \"-command\" switch",
+               (char *)NULL);
+           goto error;
+       }
+       if ((varName != NULL) && (varName[0] != '\0')) {
+           Tcl_AppendResult(interp,
+               "can't specify more than one vector with \"-variable\" switch",
+               (char *)NULL);
+           goto error;
+       }
+    }
+    for (i = 0; i < count; i++) {
+       size = first = last = 0;
+       leftParen = strchr(nameArr[i], '(');
+       rightParen = strchr(nameArr[i], ')');
+       if (((leftParen != NULL) && (rightParen == NULL)) ||
+           ((leftParen == NULL) && (rightParen != NULL)) ||
+           (leftParen > rightParen)) {
+           Tcl_AppendResult(interp, "bad vector specification \"", nameArr[i],
+               "\"", (char *)NULL);
+           goto error;
+       }
+       if (leftParen != NULL) {
+           int result;
+           char *colon, *comma;
+
+           *rightParen = '\0';
+           comma = strchr(leftParen + 1, ',');
+           colon = strchr(leftParen + 1, ':');
+           if (comma != NULL) {
+               int rows;
+                *comma = '\0';
+                result = Tcl_GetInt(interp, leftParen + 1, &rows);
+               if ((*(comma + 1) != '\0') && (result == TCL_OK)) {
+                   result = Tcl_GetInt(interp, comma + 1, &numcols);
+               }
+               size = rows*numcols;
+                if (result != TCL_OK || rows<=0 || numcols<=0) {
+                    Tcl_AppendResult(interp, "bad matrix vector \"",
+                       nameArr[i], "\"", (char *)NULL);
+                       result = TCL_ERROR;
+               }
+            } else if (colon != NULL) {
+
+               /* Specification is in the form vecName(first:last) */
+               *colon = '\0';
+               result = Tcl_GetInt(interp, leftParen + 1, &first);
+               if ((*(colon + 1) != '\0') && (result == TCL_OK)) {
+                   result = Tcl_GetInt(interp, colon + 1, &last);
+                   if (first > last) {
+                       Tcl_AppendResult(interp, "bad vector range \"",
+                           nameArr[i], "\"", (char *)NULL);
+                       result = TCL_ERROR;
+                   }
+                   size = (last - first) + 1;
+               }
+               *colon = ':';
+           } else {
+               /* Specification is in the form vecName(size) */
+               result = Tcl_GetInt(interp, leftParen + 1, &size);
+           }
+           *rightParen = ')';
+           if (result != TCL_OK) {
+               goto error;
+           }
+           if (size < 0) {
+               Tcl_AppendResult(interp, "bad vector size \"", nameArr[i], "\"",
+                   (char *)NULL);
+               goto error;
+           }
+       }
+       if (leftParen != NULL) {
+           *leftParen = '\0';
+       }
+       if (dataPtr->bltMaxSize>0 && size>dataPtr->bltMaxSize) {
+             Tcl_AppendResult(interp, "vector size too large\"", nameArr[i], "\"",
+                (char *)NULL);
+                goto error;
+       }
+       /*
+        * By default, we create a Tcl command by the name of the vector.
+        */
+       vPtr = Blt_VectorCreate(dataPtr, nameArr[i],
+           (cmdName == NULL) ? nameArr[i] : cmdName,
+           (varName == NULL) ? nameArr[i] : varName,
+           &isNew);
+       if (leftParen != NULL) {
+           *leftParen = '(';
+       }
+       if (vPtr == NULL) {
+           goto error;
+       }
+       vPtr->freeOnUnset = freeOnUnset;
+       vPtr->flush = flush;
+       vPtr->offset = first;
+        if (first && first%vPtr->numcols) {
+            first += (vPtr->numcols-(first%vPtr->numcols));
+        }
+       vPtr->numcols = numcols;
+       if (size > 0) {
+           if (Blt_VectorChangeLength(vPtr, size) != TCL_OK) {
+               goto error;
+           }
+       }
+       if (!isNew) {
+           if (vPtr->flush) {
+               Blt_VectorFlushCache(vPtr);
+           }
+           Blt_VectorUpdateClients(vPtr);
+       }
+    }
+    Blt_Free(nameArr);
+    if (vPtr != NULL) {
+       /* Return the name of the last vector created  */
+       Tcl_SetResult(interp, vPtr->name, TCL_VOLATILE);
+    }
+    return TCL_OK;
+  error:
+    Blt_Free(nameArr);
+    return TCL_ERROR;
+}
+
+static int
+VectorObjCallOp(clientData, interp, objc, objv, proc)
+ClientData clientData;
+Tcl_Interp *interp;
+int objc;
+Tcl_Obj *CONST *objv;
+Blt_Op proc;
+{
+    int i, result;
+    const char **argv;
+    argv = (const char **) ckalloc((unsigned)(objc + 1) * sizeof(char *));
+
+    for (i = 0;  i < objc;  i++) {
+        argv[i] = Tcl_GetString(objv[i]);
+    }
+    argv[objc] = 0;
+    result = (*proc)(clientData, interp, objc, argv);
+    ckfree((char*) argv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorCreateOp --
+ *
+ *     Creates a Tcl command, and array variable representing an
+ *     instance of a vector.
+ *
+ *     vector a
+ *     vector b(20)
+ *     vector c(-5:14)
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorCreateOp(clientData, interp, argc, argv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return VectorCreate2(clientData, interp, 2, argc, argv);
+}
+
+static int
+VectorCreateObjOp(clientData, interp, objc, objv)
+ClientData clientData;
+Tcl_Interp *interp;
+int objc;
+Tcl_Obj *CONST *objv;
+{
+    return VectorObjCallOp(clientData, interp, objc, objv, VectorCreateOp);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorDestroyOp --
+ *
+ *     Destroys the vector and its related Tcl command and array
+ *     variable (if they exist).
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     Deletes the vector.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorDestroyOp(clientData, interp, argc, argv)
+    ClientData clientData;     /* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    VectorInterpData *dataPtr = clientData;
+    VectorObject *vPtr;
+    register int i;
+
+    for (i = 2; i < argc; i++) {
+       if (Blt_VectorLookupName(dataPtr, argv[i], &vPtr) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       Blt_VectorFree(vPtr);
+    }
+    return TCL_OK;
+}
+
+static int
+VectorDestroyObjOp(clientData, interp, objc, objv)
+ClientData clientData;
+Tcl_Interp *interp;
+int objc;
+Tcl_Obj *CONST *objv;
+{
+    return VectorObjCallOp(clientData, interp, objc, objv, VectorDestroyOp);
+}
+
+#if (TCL_MAJOR_VERSION == 7)
+
+static int
+VectorOpOp(clientData, interp, argc, argv)
+ClientData clientData; /* Interpreter-specific data. */
+Tcl_Interp *interp;
+int argc;
+char **argv;
+{
+    int i, result;
+    const char **nargv;
+    Blt_Vector *vecPtr;
+    
+    result = Blt_GetVector(interp, Tcl_GetString(argv[3]), &vecPtr);
+    if (result != TCL_OK) {
+        return result;
+    }
+    nargv = (const char **) ckalloc((unsigned)(argc) * sizeof(char *));
+
+    nargv[0] = argv[3];
+    nargv[1] = argv[2];
+    for (i = 2;  i < (argc-2);  i++) {
+        nargv[i] = argv[i+2];
+    }
+    nargv[argc-2] = 0;
+    result = Blt_VectorInstCmd((ClientData)vecPtr, interp, argc-2, nargv);
+    ckfree((char*) nargv);
+    return result;
+}
+
+#else
+
+
+/* 
+ * Set/get various options.  These include:
+ *
+ *    -oldcreate : compatibility mode of implicit 'create'.
+ *    -flush     : flush array cells
+ *    -watchunset: delete on var deletion.
+ *    -maxsize   : max allocation.
+ *
+ * These are global settings to all interps.
+ */
+
+static int
+VectorConfigureObjOp(clientData, interp, objc, objv)
+ClientData clientData;
+Tcl_Interp *interp;
+int objc;
+Tcl_Obj *CONST *objv;
+{
+    enum optionIndices {
+            OPTION_FLUSH, OPTION_OLDCREATE, OPTION_WATCHUNSET, OPTION_MAXSIZE,
+            OPTION_NOCOMMAND, OPTION_NOVARIABLE
+    };
+    static char *optionArr[] = {
+        "-flush", "-oldcreate", "-watchunset", "-maxsize",
+        "-nocommand", "-novariable", 0
+    };
+    int i;
+    VectorInterpData *d = clientData;
+    
+    if (objc%2) {
+        Tcl_WrongNumArgs(interp, 2, objv, "?-flush bool? ?-watchunset bool? ?-oldcreate bool?");
+        return TCL_ERROR;
+    }
+    if (objc == 2) {
+        Tcl_Obj * o = Tcl_NewListObj(0,0);
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewStringObj("-flush", -1));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewBooleanObj(d->bltFlushArray));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewStringObj("-watchunset", -1));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewBooleanObj(d->bltFreeOnUnset));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewStringObj("-oldcreate", -1));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewBooleanObj(d->bltOldCreate));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewStringObj("-nocommand", -1));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewBooleanObj(d->bltNoCommand));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewStringObj("-novariable", -1));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewBooleanObj(d->bltNoVariable));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewStringObj("-maxsize", -1));
+        Tcl_ListObjAppendElement(interp, o, Tcl_NewIntObj(d->bltMaxSize));
+        Tcl_SetObjResult(interp, o);
+        return TCL_OK;
+    }
+    for (i=2; i<objc; i+=2) {
+        int option, *ovar;
+        if (Tcl_GetIndexFromObj(interp, objv[i], optionArr, "option",
+            0, &option) != TCL_OK) {
+                return TCL_OK;
+        }
+        
+        switch (option) {
+            case OPTION_FLUSH:      ovar=&d->bltFlushArray; break;
+            case OPTION_OLDCREATE:  ovar=&d->bltOldCreate; break;
+            case OPTION_WATCHUNSET: ovar=&d->bltFreeOnUnset; break;
+            case OPTION_NOCOMMAND:  ovar=&d->bltNoCommand; break;
+            case OPTION_NOVARIABLE: ovar=&d->bltNoVariable; break;
+            case OPTION_MAXSIZE:
+                if (Tcl_GetIntFromObj(interp, objv[i+1], &d->bltMaxSize) != TCL_OK) {
+                    return TCL_ERROR;
+                }
+                return TCL_OK;
+        }
+
+        if (Tcl_GetBooleanFromObj(interp, objv[i+1], ovar) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+VectorOpObjOp(clientData, interp, objc, objv)
+    ClientData clientData;
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    int i, j, o1 = 2, result;
+    Tcl_Obj **nobjv;
+    Blt_Vector *vecPtr;
+    char *string;
+    
+    string = Tcl_GetString(objv[2]);
+    if (!strncmp(string, "ma", 2)) {
+        /* The "vector op matrix rowize VEC ?N?" command. */
+        o1 = 3;
+    }
+    result = Blt_GetVector(interp, Tcl_GetString(objv[o1+1]), &vecPtr);
+    if (result != TCL_OK) {
+        return result;
+    }
+    nobjv = (Tcl_Obj **) ckalloc((unsigned)(objc) * sizeof(Tcl_Obj *));
+
+    j = 0;
+    nobjv[j++] = objv[o1+1];
+    nobjv[j++] = objv[2];
+    i = 4;
+    if (o1 != 2) {
+        nobjv[j++] = objv[3];
+        i = 5;
+    }        
+    for (;  i < objc;  j++, i++) {
+        nobjv[j] = objv[i];
+    }
+    nobjv[j++] = 0;
+    /* TODO: fix error messages in this call duplicating the arg. */
+    result = Blt_VectorInstCmd((ClientData)vecPtr, interp, objc-2, nobjv);
+    ckfree((char*) nobjv);
+    return result;
+}
+
+
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * VectorExprOp --
+ *
+ *     Computes the result of the expression which may be
+ *     either a scalar (single value) or vector (list of values).
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+VectorExprOp(clientData, interp, argc, argv)
+    ClientData clientData;     /* Not Used. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    return Blt_ExprVector(interp, argv[2], (Blt_Vector *) NULL);
+}
+
+static int
+VectorExprObjOp(clientData, interp, objc, objv)
+ClientData clientData;
+Tcl_Interp *interp;
+int objc;
+Tcl_Obj *CONST *objv;
+{
+    return VectorObjCallOp(clientData, interp, objc, objv, VectorExprOp);
+}
+
+#if (TCL_MAJOR_VERSION == 7)
+
+static Blt_OpSpec vectorCmdOps[] =
+{
+    {"create", 1, (Blt_Op)VectorCreateOp, 3, 0,
+       "vecName ?vecName...? ?switches...?",},
+    {"destroy", 1, (Blt_Op)VectorDestroyOp, 3, 0,
+       "vecName ?vecName...?",},
+    {"expr", 1, (Blt_Op)VectorExprOp, 3, 3, "expression",},
+    {"names", 1, (Blt_Op)VectorNamesOp, 2, 3, "?pattern?",},
+    {"op", 1, (Blt_Op)VectorOpOp, 4, 0, "cmd ...",},
+    };
+
+static int nCmdOps = sizeof(vectorCmdOps) / sizeof(Blt_OpSpec);
+
+/*ARGSUSED*/
+static int
+VectorCmd(clientData, interp, argc, argv)
+    ClientData clientData;     /* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int argc;
+    char **argv;
+{
+    Blt_Op proc;
+
+    /*
+     * Try to replicate the old vector command's behavior:
+     */
+    if (argc > 1) {
+       char c;
+       register int i;
+       register Blt_OpSpec *specPtr;
+
+       c = argv[1][0];
+       for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) {
+           if ((c == specPtr->name[0]) &&
+               (strcmp(argv[1], specPtr->name) == 0)) {
+               goto doOp;
+           }
+       }
+       /*
+        * The first argument is not an operation, so assume that its
+        * actually the name of a vector to be created
+        */
+       return VectorCreate2(clientData, interp, 1, argc, argv);
+    }
+  doOp:
+    /* Do the usual vector operation lookup now. */
+    proc = Blt_GetOp(interp, nCmdOps, vectorCmdOps, BLT_OP_ARG1, argc, argv,0);
+    if (proc == NULL) {
+       return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, argc, argv);
+}
+
+#else
+
+static Blt_OpSpec vectorCmdOps[] =
+{
+    {"configure", 1, (Blt_Op)VectorConfigureObjOp, 2, 0, "?-flush bool? ?-watchunset bool? ?-oldcreate bool? ?-maxsize N? ?-nocommand bool? ?-novariable bool?",},
+    {"create", 1, (Blt_Op)VectorCreateObjOp, 3, 0,
+        "vecName ?vecName...? ?switches...?",},
+    {"destroy", 1, (Blt_Op)VectorDestroyObjOp, 3, 0,
+        "vecName ?vecName...?",},
+    {"expr", 1, (Blt_Op)VectorExprObjOp, 3, 3, "expression",},
+    {"names", 1, (Blt_Op)VectorNamesObjOp, 2, 3, "?pattern?",},
+    {"op", 1, (Blt_Op)VectorOpObjOp, 4, 0, "cmd vecName ...",},
+};
+
+static int nCmdOps = sizeof(vectorCmdOps) / sizeof(Blt_OpSpec);
+
+static int
+VectorObjCmd(clientData, interp, objc, objv)
+    ClientData clientData;     /* Interpreter-specific data. */
+    Tcl_Interp *interp;
+    int objc;
+    Tcl_Obj *CONST *objv;
+{
+    Blt_Op proc;
+    VectorInterpData *dataPtr = clientData;
+
+    /*
+    * Try to replicate the old vector command's behavior:
+    */
+    if (objc > 1 && dataPtr->bltOldCreate) {
+        char c, *str;
+        register int i;
+        register Blt_OpSpec *specPtr;
+        int result;
+        int argc;
+        char **argv;
+        Tcl_Obj *listPtr;
+    
+        str = Tcl_GetString(objv[1]);
+        c = str[0];
+        for (specPtr = vectorCmdOps, i = 0; i < nCmdOps; i++, specPtr++) {
+            if ((c == specPtr->name[0]) &&
+            (strcmp(str, specPtr->name) == 0)) {
+                goto doOp;
+            }
+        }
+        /*
+        * The first argument is not an operation, so assume that its
+        * actually the name of a vector to be created
+        */
+        listPtr = Tcl_NewListObj(objc, objv);
+        result = Tcl_SplitList( interp, Tcl_GetString(listPtr), &argc, &argv);
+        if (result == TCL_OK) {
+            result = VectorCreate2(clientData, interp, 1, argc, argv);
+            ckfree((char*) argv);
+        }
+        Tcl_DecrRefCount(listPtr);
+        return result;
+    }
+    doOp:
+    /* Do the usual vector operation lookup now. */
+    proc = Blt_GetOpFromObj(interp, nCmdOps, vectorCmdOps, BLT_OP_ARG1, objc, objv,0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+#endif
+
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * VectorInterpDeleteProc --
+ *
+ *     This is called when the interpreter hosting the "vector" command
+ *     is deleted.  
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Destroys the math and index hash tables.  In addition removes
+ *     the hash table managing all vector names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+VectorInterpDeleteProc(clientData, interp)
+    ClientData clientData;     /* Interpreter-specific data. */
+    Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr = clientData;
+    Blt_HashEntry *hPtr;
+    Blt_HashSearch cursor;
+    VectorObject *vPtr;
+    
+    for (hPtr = Blt_FirstHashEntry(&(dataPtr->vectorTable), &cursor);
+        hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+       vPtr = (VectorObject *)Blt_GetHashValue(hPtr);
+       vPtr->hashPtr = NULL;
+       Blt_VectorFree(vPtr);
+    }
+    Blt_DeleteHashTable(&(dataPtr->vectorTable));
+
+    /* If any user-defined math functions were installed, remove them.  */
+    Blt_VectorUninstallMathFunctions(&(dataPtr->mathProcTable));
+    Blt_DeleteHashTable(&(dataPtr->mathProcTable));
+
+    Blt_DeleteHashTable(&(dataPtr->indexProcTable));
+    Tcl_DeleteAssocData(interp, VECTOR_THREAD_KEY);
+    Blt_Free(dataPtr);
+}
+
+VectorInterpData *
+Blt_VectorGetInterpData(interp)
+    Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr;
+    Tcl_InterpDeleteProc *proc;
+
+    dataPtr = (VectorInterpData *)
+       Tcl_GetAssocData(interp, VECTOR_THREAD_KEY, &proc);
+    if (dataPtr == NULL) {
+       dataPtr = Blt_Calloc(sizeof(VectorInterpData), 1);
+       assert(dataPtr);
+       dataPtr->interp = interp;
+       dataPtr->nextId = 0;
+       Tcl_SetAssocData(interp, VECTOR_THREAD_KEY, VectorInterpDeleteProc,
+                dataPtr);
+       Blt_InitHashTable(&(dataPtr->vectorTable), BLT_STRING_KEYS);
+       Blt_InitHashTable(&(dataPtr->mathProcTable), BLT_STRING_KEYS);
+       Blt_InitHashTable(&(dataPtr->indexProcTable), BLT_STRING_KEYS);
+       Blt_VectorInstallMathFunctions(&(dataPtr->mathProcTable));
+       Blt_VectorInstallSpecialIndices(&(dataPtr->indexProcTable));
+#ifdef HAVE_SRAND48
+       srand48(time((time_t *) NULL));
+#endif
+    }
+    return dataPtr;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_VectorInit --
+ *
+ *     This procedure is invoked to initialize the "vector" command.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Creates the new command and adds a new entry into a global Tcl
+ *     associative array.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#if (TCL_MAJOR_VERSION == 7)
+
+int
+Blt_VectorInit(interp)
+    Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    static Blt_CmdSpec cmdSpec = {"vector", VectorCmd, };
+    
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /* 
+     * This routine may be run several times in the same interpreter. 
+     * For example, if someone tries to initial the BLT commands from 
+     * another namespace. Keep a reference count, so we know when it's 
+     * safe to clean up.
+     */
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+#else
+
+int
+Blt_VectorInit(interp)
+Tcl_Interp *interp;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    static Blt_ObjCmdSpec cmdSpec = {"vector", VectorObjCmd, };
+    
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /* 
+    * This routine may be run several times in the same interpreter. 
+    * For example, if someone tries to initial the BLT commands from 
+    * another namespace. Keep a reference count, so we know when it's 
+    * safe to clean up.
+    */
+    cmdSpec.clientData = dataPtr;
+    if (Blt_InitObjCmd(interp, "blt", &cmdSpec) == NULL) {
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+#endif
+
+\f
+/* C Application interface to vectors */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_CreateVector --
+ *
+ *     Creates a new vector by the name and size.
+ *
+ * Results:
+ *     A standard Tcl result.  If the new array size is invalid or a
+ *     vector already exists by that name, TCL_ERROR is returned.
+ *     Otherwise TCL_OK is returned and the new vector is created.
+ *
+ * Side Effects:
+ *     Memory will be allocated for the new vector.  A new Tcl command
+ *     and Tcl array variable will be created.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+/*LINTLIBRARY*/
+int
+Blt_CreateVector2(interp, vecName, cmdName, varName, initialSize, vecPtrPtr)
+    Tcl_Interp *interp;
+    char *vecName;
+    char *cmdName, *varName;
+    int initialSize;
+    Blt_Vector **vecPtrPtr;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    VectorObject *vPtr;
+    int isNew;
+    char *nameCopy;
+
+    if (initialSize < 0) {
+       Tcl_AppendResult(interp, "bad vector size \"", Blt_Itoa(initialSize),
+           "\"", (char *)NULL);
+       return TCL_ERROR;
+    }
+    dataPtr = Blt_VectorGetInterpData(interp);
+
+    nameCopy = Blt_Strdup(vecName);
+    vPtr = Blt_VectorCreate(dataPtr, nameCopy, cmdName, varName, &isNew);
+    Blt_Free(nameCopy);
+
+    if (vPtr == NULL) {
+       return TCL_ERROR;
+    }
+    if (initialSize > 0) {
+       if (Blt_VectorChangeLength(vPtr, initialSize) != TCL_OK) {
+           return TCL_ERROR;
+       }
+    }
+    if (vecPtrPtr != NULL) {
+       *vecPtrPtr = (Blt_Vector *) vPtr;
+    }
+    return TCL_OK;
+}
+
+int
+Blt_CreateVector(interp, name, size, vecPtrPtr)
+    Tcl_Interp *interp;
+    char *name;
+    int size;
+    Blt_Vector **vecPtrPtr;
+{
+    return Blt_CreateVector2(interp, name, name, name, size, vecPtrPtr);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_DeleteVector --
+ *
+ *     Deletes the vector of the given name.  All clients with
+ *     designated callback routines will be notified.
+ *
+ * Results:
+ *     A standard Tcl result.  If no vector exists by that name,
+ *     TCL_ERROR is returned.  Otherwise TCL_OK is returned and
+ *     vector is deleted.
+ *
+ * Side Effects:
+ *     Memory will be released for the new vector.  Both the Tcl
+ *     command and array variable will be deleted.  All clients which
+ *     set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVector(vecPtr)
+    Blt_Vector *vecPtr;
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+    Blt_VectorFree(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_DeleteVectorByName --
+ *
+ *     Deletes the vector of the given name.  All clients with
+ *     designated callback routines will be notified.
+ *
+ * Results:
+ *     A standard Tcl result.  If no vector exists by that name,
+ *     TCL_ERROR is returned.  Otherwise TCL_OK is returned and
+ *     vector is deleted.
+ *
+ * Side Effects:
+ *     Memory will be released for the new vector.  Both the Tcl
+ *     command and array variable will be deleted.  All clients which
+ *     set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+/*LINTLIBRARY*/
+int
+Blt_DeleteVectorByName(interp, name)
+    Tcl_Interp *interp;
+    char *name;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    VectorObject *vPtr;
+    char *nameCopy;
+    int result;
+
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseElement routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(name);
+    dataPtr = Blt_VectorGetInterpData(interp);
+    result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+    Blt_Free(nameCopy);
+
+    if (result != TCL_OK) {
+       return TCL_ERROR;
+    }
+    Blt_VectorFree(vPtr);
+    return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorExists2 --
+ *
+ *     Returns whether the vector associated with the client token
+ *     still exists.
+ *
+ * Results:
+ *     Returns 1 is the vector still exists, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorExists2(interp, vecName)
+    Tcl_Interp *interp;
+    char *vecName;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    if (GetVectorObject(dataPtr, vecName, NS_SEARCH_BOTH) != NULL) {
+       return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Blt_VectorExists --
+ *
+ *     Returns whether the vector associated with the client token
+ *     still exists.
+ *
+ * Results:
+ *     Returns 1 is the vector still exists, 0 otherwise.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+Blt_VectorExists(interp, vecName)
+    Tcl_Interp *interp;
+    char *vecName;
+{
+    char *nameCopy;
+    int result;
+
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseName routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(vecName);
+    result = Blt_VectorExists2(interp, nameCopy);
+    Blt_Free(nameCopy);
+    return result;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_GetVector --
+ *
+ *     Returns a pointer to the vector associated with the given name.
+ *
+ * Results:
+ *     A standard Tcl result.  If there is no vector "name", TCL_ERROR
+ *     is returned.  Otherwise TCL_OK is returned and vecPtrPtr will
+ *     point to the vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_GetVector(interp, name, vecPtrPtr)
+    Tcl_Interp *interp;
+    char *name;
+    Blt_Vector **vecPtrPtr;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    VectorObject *vPtr;
+    char *nameCopy;
+    int result;
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseName routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(name);
+    result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+    Blt_Free(nameCopy);
+    if (result != TCL_OK) {
+       return TCL_ERROR;
+    }
+    Blt_VectorUpdateRange(vPtr);
+    *vecPtrPtr = (Blt_Vector *) vPtr;
+    return TCL_OK;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResetVector --
+ *
+ *     Resets the vector data.  This is called by a client to
+ *     indicate that the vector data has changed.  The vector does
+ *     not need to point to different memory.  Any clients of the
+ *     vector will be notified of the change.
+ *
+ * Results:
+ *     A standard Tcl result.  If the new array size is invalid,
+ *     TCL_ERROR is returned.  Otherwise TCL_OK is returned and the
+ *     new vector data is recorded.
+ *
+ * Side Effects:
+ *     Any client designated callbacks will be posted.  Memory may
+ *     be changed for the vector array.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_ResetVector(vecPtr, valueArr, length, size, freeProc)
+    Blt_Vector *vecPtr;
+    double *valueArr;          /* Array containing the elements of the
+                                * vector. If NULL, indicates to reset the 
+                                * vector.*/
+    int length;                        /* The number of elements that the vector 
+                                * currently holds. */
+    int size;                  /* The maximum number of elements that the
+                                * array can hold. */
+    Tcl_FreeProc *freeProc;    /* Address of memory deallocation routine
+                                * for the array of values.  Can also be
+                                * TCL_STATIC, TCL_DYNAMIC, or TCL_VOLATILE. */
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    if (size < 0) {
+       Tcl_AppendResult(vPtr->interp, "bad array size", (char *)NULL);
+       return TCL_ERROR;
+    }
+    return Blt_VectorReset(vPtr, valueArr, length, size, freeProc);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_ResizeVector --
+ *
+ *     Changes the size of the vector.  All clients with designated
+ *     callback routines will be notified of the size change.
+ *
+ * Results:
+ *     A standard Tcl result.  If no vector exists by that name,
+ *     TCL_ERROR is returned.  Otherwise TCL_OK is returned and
+ *     vector is resized.
+ *
+ * Side Effects:
+ *     Memory may be reallocated for the new vector size.  All clients
+ *     which set call back procedures will be notified.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_ResizeVector(vecPtr, length)
+    Blt_Vector *vecPtr;
+    int length;
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+
+    if (Blt_VectorChangeLength(vPtr, length) != TCL_OK) {
+       Tcl_AppendResult(vPtr->interp, "can't resize vector \"", vPtr->name,
+           "\"", (char *)NULL);
+       return TCL_ERROR;
+    }
+    if (vPtr->flush) {
+       Blt_VectorFlushCache(vPtr);
+    }
+    Blt_VectorUpdateClients(vPtr);
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_AllocVectorId --
+ *
+ *     Creates an identifier token for an existing vector.  The
+ *     identifier is used by the client routines to get call backs
+ *     when (and if) the vector changes.
+ *
+ * Results:
+ *     A standard Tcl result.  If "vecName" is not associated with
+ *     a vector, TCL_ERROR is returned and interp->result is filled
+ *     with an error message.
+ *
+ *--------------------------------------------------------------
+ */
+Blt_VectorId
+Blt_AllocVectorId(interp, name)
+    Tcl_Interp *interp;
+    char *name;
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    VectorObject *vPtr;
+    VectorClient *clientPtr;
+    Blt_VectorId clientId;
+    int result;
+    char *nameCopy;
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    /*
+     * If the vector name was passed via a read-only string (e.g. "x"),
+     * the Blt_VectorParseName routine will segfault when it tries to write
+     * into the string.  Therefore make a writable copy and free it
+     * when we're done.
+     */
+    nameCopy = Blt_Strdup(name);
+    result = Blt_VectorLookupName(dataPtr, nameCopy, &vPtr);
+    Blt_Free(nameCopy);
+
+    if (result != TCL_OK) {
+       return (Blt_VectorId) 0;
+    }
+    /* Allocate a new client structure */
+    clientPtr = Blt_Calloc(1, sizeof(VectorClient));
+    assert(clientPtr);
+    clientPtr->magic = VECTOR_MAGIC;
+
+    /* Add the new client to the server's list of clients */
+    clientPtr->linkPtr = Blt_ChainAppend(vPtr->chainPtr, clientPtr);
+    clientPtr->serverPtr = vPtr;
+    clientId = (Blt_VectorId) clientPtr;
+    return clientId;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_SetVectorChangedProc --
+ *
+ *     Sets the routine to be called back when the vector is changed
+ *     or deleted.  *clientData* will be provided as an argument. If
+ *     *proc* is NULL, no callback will be made.
+ *
+ * Results:
+ *     None.
+ *
+ * Side Effects:
+ *     The designated routine will be called when the vector is changed
+ *     or deleted.
+ *
+ * -----------------------------------------------------------------------
+ */
+void
+Blt_SetVectorChangedProc(clientId, proc, clientData)
+    Blt_VectorId clientId;     /* Client token identifying the vector */
+    Blt_VectorChangedProc *proc;/* Address of routine to call when the contents
+                                * of the vector change. If NULL, no routine
+                                * will be called */
+    ClientData clientData;     /* One word of information to pass along when
+                                * the above routine is called */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if (clientPtr->magic != VECTOR_MAGIC) {
+       return;                 /* Not a valid token */
+    }
+    clientPtr->clientData = clientData;
+    clientPtr->proc = proc;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_FreeVectorId --
+ *
+ *     Releases the token for an existing vector.  This indicates
+ *     that the client is no longer interested the vector.  Any
+ *     previously specified callback routine will no longer be
+ *     invoked when (and if) the vector changes.
+ *
+ * Results:
+ *     None.
+ *
+ * Side Effects:
+ *     Any previously specified callback routine will no longer be
+ *     invoked when (and if) the vector changes.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Blt_FreeVectorId(clientId)
+    Blt_VectorId clientId;     /* Client token identifying the vector */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if (clientPtr->magic != VECTOR_MAGIC) {
+       return;                 /* Not a valid token */
+    }
+    if (clientPtr->serverPtr != NULL) {
+       /* Remove the client from the server's list */
+       Blt_ChainDeleteLink(clientPtr->serverPtr->chainPtr, clientPtr->linkPtr);
+    }
+    Blt_Free(clientPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_NameOfVectorId --
+ *
+ *     Returns the name of the vector (and array variable).
+ *
+ * Results:
+ *     The name of the array variable is returned.
+ *
+ *--------------------------------------------------------------
+ */
+char *
+Blt_NameOfVectorId(clientId)
+    Blt_VectorId clientId;     /* Client token identifying the vector */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if ((clientPtr->magic != VECTOR_MAGIC) || (clientPtr->serverPtr == NULL)) {
+       return NULL;
+    }
+    return clientPtr->serverPtr->name;
+}
+
+char *
+Blt_NameOfVector(vecPtr)
+    Blt_Vector *vecPtr;                /* Vector to query. */
+{
+    VectorObject *vPtr = (VectorObject *)vecPtr;
+    return vPtr->name;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Blt_VectorNotifyPending --
+ *
+ *     Returns the name of the vector (and array variable).
+ *
+ * Results:
+ *     The name of the array variable is returned.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Blt_VectorNotifyPending(clientId)
+    Blt_VectorId clientId;     /* Client token identifying the vector */
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if ((clientPtr == NULL) || (clientPtr->magic != VECTOR_MAGIC) || 
+       (clientPtr->serverPtr == NULL)) {
+       return 0;
+    }
+    return (clientPtr->serverPtr->notifyFlags & NOTIFY_PENDING);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Blt_GetVectorById --
+ *
+ *     Returns a pointer to the vector associated with the client
+ *     token.
+ *
+ * Results:
+ *     A standard Tcl result.  If the client token is not associated
+ *     with a vector any longer, TCL_ERROR is returned. Otherwise,
+ *     TCL_OK is returned and vecPtrPtr will point to vector.
+ *
+ * -----------------------------------------------------------------------
+ */
+int
+Blt_GetVectorById(interp, clientId, vecPtrPtr)
+    Tcl_Interp *interp;
+    Blt_VectorId clientId;     /* Client token identifying the vector */
+    Blt_Vector **vecPtrPtr;
+{
+    VectorClient *clientPtr = (VectorClient *)clientId;
+
+    if (clientPtr->magic != VECTOR_MAGIC) {
+       Tcl_AppendResult(interp, "bad vector token", (char *)NULL);
+       return TCL_ERROR;
+    }
+    if (clientPtr->serverPtr == NULL) {
+       Tcl_AppendResult(interp, "vector no longer exists", (char *)NULL);
+       return TCL_ERROR;
+    }
+    Blt_VectorUpdateRange(clientPtr->serverPtr);
+    *vecPtrPtr = (Blt_Vector *) clientPtr->serverPtr;
+    return TCL_OK;
+}
+
+/*LINTLIBRARY*/
+void
+Blt_InstallIndexProc(interp, string, procPtr)
+    Tcl_Interp *interp;
+    char *string;
+    Blt_VectorIndexProc *procPtr; /* Pointer to function to be called
+                                  * when the vector finds the named index.
+                                  * If NULL, this indicates to remove
+                                  * the index from the table.
+                                  */
+{
+    VectorInterpData *dataPtr; /* Interpreter-specific data. */
+    Blt_HashEntry *hPtr;
+    int isNew;
+
+    dataPtr = Blt_VectorGetInterpData(interp);
+    hPtr = Blt_CreateHashEntry(&(dataPtr->indexProcTable), string, &isNew);
+    if (procPtr == NULL) {
+       Blt_DeleteHashEntry(&(dataPtr->indexProcTable), hPtr);
+    } else {
+       Blt_SetHashValue(hPtr, procPtr);
+    }
+}