#include #include /* * array or row pointers * _ * |_---> [row index * |_ [#columns array of Tcl_Objs * |_ [tuple pointer ---> [ . . . . . ] * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * |_ * */ enum TagTypes { TAG_TYPE_NONE, TAG_TYPE_ALL, TAG_TYPE_TAG }; #define TUPLE_THREAD_KEY "BLT Tuple Data" #define TUPLE_MAGIC ((unsigned int) 0x46170277) #define TUPLE_DESTROYED (1<<0) #define TRACE_IGNORE (0) #define TRACE_OK (1) #define VALUE(r,c) \ ((((c)->index < (r)->nColumns) && ((r)->data != NULL)) \ ? (r)->data[(c)->index] : NULL) typedef struct Blt_TupleColumnStruct Column; typedef struct Blt_TupleRowStruct Row; typedef struct Blt_TupleTagTableStruct TagTable; typedef struct Blt_TupleTraceStruct Trace; typedef struct Blt_TupleClientStruct TupleClient; typedef struct Blt_TupleNotifierStruct Notifier; struct Blt_TupleRowStruct { Tcl_Obj **data; /* Array of Tcl_Objs represents the tuple's * data. */ unsigned short nColumns; /* # columns allocated for this tuple. */ unsigned short flags; /* Special flags for this tuple. */ unsigned int index; /* Index of the tuple in the array. */ }; struct Blt_TupleColumnStruct { char *key; /* Name of column. */ int type; /* Type of column: * TYPE_INTEGER * TYPE_NUMBER * TYPE_FLOAT * TYPE_STRING * TYPE_BOOLEAN * TYPE_LIST * TYPE_ARRAY */ int nz; /* Number of (non-NULL) defined values * in the column. */ unsigned int index; /* Location of column in tuple. */ unsigned int flags; /* Special flags for this column. */ Blt_HashEntry *hashPtr; /* Vector linkage */ }; struct Blt_TupleTagTableStruct { Blt_HashTable tagTable; int refCount; }; typedef struct { Blt_HashTable instTable; /* Table of tuple objects. */ unsigned int nextId; Tcl_Interp *interp; } TupleInterpData; typedef struct { Row *rowPtr; Column *columnPtr; } BusyKey; /* Blt_TupleRemoveColumn(table, key); Blt_TupleAddColumn(table, key); Blt_TupleRenameColumn(table, key, newKey); Blt_TupleSetColumnType(table, key, type); Blt_TupleGetColumnType(table, key, typePtr); unsigned int Blt_TupleColumnLength(table, key, flags); Tcl_Obj **Blt_TupleGetColumnVector(tuple, key); unsigned int Blt_TupleLength(table); unsigned int Blt_TupleSize(table); Blt_TupleTuple Blt_TableGetTuple(table, index); Blt_TupleSetTuple(table, index, tuple); Blt_TupleTuple Blt_TableAppendTuple(tuple); Blt_TableTuple Blt_TupleInsertTuple(table, newPosition); Blt_TupleDeleteTuple(table, tuple); Blt_TupleMoveTuple(table, tuple, newPosition); */ /* * Blt_TupleObject -- * * Structure representing a tuple object. * * Tuple objects are uniquely identified by a combination of * their name and the originating namespace. Two objects in the * same interpreter can have similar names but must reside in * different namespaces. * * The tuple object is an array of tuples. A tuple itself is an * array of Tcl_Objs representing data for each column (or key). * Value are identified by their column name. A tuple does not * need to contain values for all columns. Undefined values * (Tcl_Objs) are NULL. * * A tuple object can be shared by several clients. When a * client wants to use a tuple object, it is given a token that * represents the table. The object tracks its clients by its * token. When all clients have released their tokens, the tuple * object is automatically destroyed. * */ typedef struct { Tcl_Interp *interp; /* Interpreter associated with this * object. */ char *name; /* Name of tuple object. */ Tcl_Namespace *nsPtr; Blt_HashEntry *hashPtr; /* Pointer to this object in object * hash table. */ Blt_HashTable *tablePtr; unsigned int columnsAllocated; unsigned int rowsAllocated; unsigned int nColumns; /* Number columns in each tuple. */ unsigned int nRows; /* Number of rows (tuples). */ Row **rows; Column **columns; /* Array of column pointers. Supports * mappings of numeric indices back to * columns. */ Blt_HashTable columnTable; /* Hash table of columns. Supports * mappings of string names back to * columns. */ Blt_Pool rowPool; /* Pool that allocates row containers. */ char *sortCmd; /* Tcl command to invoke to sort * entries. */ Blt_Chain *clients; /* List of clients using this table */ unsigned int flags; /* Internal flags. See definitions * below. */ unsigned int notifyFlags; /* Notification flags. See definitions * below. */ Blt_Chain *notifiers; /* Chain of node event handlers. */ Blt_Chain *traces; /* Chain of callbacks for value changes. */ Blt_HashTable busyTable; /* Hash table tracking the cells that * have traces currently active. */ int notifyHold; } TupleObject; /* * Blt_TupleClientStruct -- * * A tuple object can be shared by several clients. Each client * allocates this structure which acts as a ticket for using the * table. Clients can designate notifier routines that are * automatically invoked by the tuple object whenever the object * is changed is specific ways by other clients. */ struct Blt_TupleClientStruct { unsigned int magic; /* Magic value indicating whether a * generic pointer is really a tuple * token or not. */ Blt_ChainLink *linkPtr; /* Pointer into the server's chain of * clients. */ Tcl_Interp *interp; TupleObject *tupleObjPtr; /* Pointer to the structure containing * the master information about the * table used by the client. If NULL, * this indicates that the table has * been destroyed (but as of yet, this * client hasn't recognized it). */ TagTable *tagTablePtr; }; #define Blt_TupleTableLength(table) ((table)->nRows) #define Blt_TupleTableWidth(table) ((table)->nColumns) static Tcl_InterpDeleteProc TupleInterpDeleteProc; static Row * NewRow(TupleObject *tupleObjPtr, unsigned int rowIndex) { Row *rowPtr; rowPtr = Blt_PoolAllocItem(tupleObjPtr->rowPool, sizeof(Row)); rowPtr->data = NULL; rowPtr->nColumns = 0; rowPtr->index = rowIndex; return rowPtr; } static void DeleteRow(TupleObject *tupleObjPtr, Row *rowPtr) { if (rowPtr->data != NULL) { int i; Tcl_Obj *objPtr; for(i = 0; i < rowPtr->nColumns; i++) { objPtr = rowPtr->data[i]; if (objPtr != NULL) { Tcl_DecrRefCount(objPtr); } } Blt_Free(rowPtr->data); } Blt_PoolFreeItem(tupleObjPtr->rowPool, (char *)rowPtr); } /* * -------------------------------------------------------------- * * NewTupleObject -- * * Creates and initializes a new tuple object. * * Results: * Returns a pointer to the new object is successful, NULL * otherwise. If a tuple object can't be generated, * interp->result will contain an error message. * * -------------------------------------------------------------- */ static TupleObject * NewTupleObject( TupleInterpData *dataPtr, Tcl_Interp *interp, CONST char *name) { TupleObject *tupleObjPtr; int isNew; Blt_HashEntry *hPtr; tupleObjPtr = Blt_Calloc(1, sizeof(TupleObject)); if (tupleObjPtr == NULL) { Tcl_AppendResult(interp, "can't allocate tuple object", (char *)NULL); return NULL; } tupleObjPtr->interp = interp; tupleObjPtr->clients = Blt_ChainCreate(); tupleObjPtr->traces = Blt_ChainCreate(); tupleObjPtr->notifiers = Blt_ChainCreate(); tupleObjPtr->notifyFlags = 0; Blt_InitHashTableWithPool(&tupleObjPtr->busyTable, BLT_ONE_WORD_KEYS); Blt_InitHashTable(&tupleObjPtr->columnTable, BLT_STRING_KEYS); tupleObjPtr->tablePtr = &dataPtr->instTable; hPtr = Blt_CreateHashEntry(tupleObjPtr->tablePtr, name, &isNew); tupleObjPtr->hashPtr = hPtr; tupleObjPtr->name = Blt_GetHashKey(tupleObjPtr->tablePtr, hPtr); Blt_SetHashValue(hPtr, tupleObjPtr); return tupleObjPtr; } /* * ---------------------------------------------------------------------- * * DestroyTupleObject -- * * Destroys the tuple object. This is the final clean up of the * object. The object's entry is removed from the hash table of * tuples. * * Results: * None. * * ---------------------------------------------------------------------- */ static void DestroyTupleObject(TupleObject *tupleObjPtr) { Blt_ChainLink *linkPtr; Trace *tracePtr; struct Blt_TupleNotifierStruct *notifyPtr; tupleObjPtr->flags |= TUPLE_DESTROYED; for (linkPtr = Blt_ChainFirstLink(tupleObjPtr->traces); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tracePtr = Blt_ChainGetValue(linkPtr); tracePtr->linkPtr = NULL; Blt_TupleDeleteTrace(tracePtr); } for (linkPtr = Blt_ChainFirstLink(tupleObjPtr->notifiers); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { notifyPtr = Blt_ChainGetValue(linkPtr); notifyPtr->linkPtr = NULL; Blt_TupleDeleteNotifier(notifyPtr); } Blt_ChainDestroy(tupleObjPtr->traces); Blt_ChainDestroy(tupleObjPtr->notifiers); Blt_ChainDestroy(tupleObjPtr->clients); if (tupleObjPtr->rows != NULL) { int i; for (i = 0; i < tupleObjPtr->nRows; i++) { DeleteRow(tupleObjPtr, tupleObjPtr->rows[i]); } Blt_Free(tupleObjPtr->rows); } tupleObjPtr->nRows = 0; Blt_PoolDestroy(tupleObjPtr->rowPool); Blt_DeleteHashTable(&tupleObjPtr->columnTable); Blt_DeleteHashTable(&tupleObjPtr->busyTable); if (tupleObjPtr->hashPtr != NULL) { Blt_DeleteHashEntry(tupleObjPtr->tablePtr, tupleObjPtr->hashPtr); } Blt_Free(tupleObjPtr); } /* * ----------------------------------------------------------------------- * * TupleInterpDeleteProc -- * * This is called when the interpreter hosting the tree object * is deleted from the interpreter. * * Results: * None. * * Side effects: * Destroys all remaining trees and removes the hash table * used to register tree names. * * ------------------------------------------------------------------------ */ /* ARGSUSED */ static void TupleInterpDeleteProc( ClientData clientData, /* Interpreter-specific data. */ Tcl_Interp *interp) { TupleInterpData *dataPtr = clientData; Blt_HashEntry *hPtr; Blt_HashSearch cursor; TupleObject *tupleObjPtr; for (hPtr = Blt_FirstHashEntry(&dataPtr->instTable, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tupleObjPtr = (TupleObject *)Blt_GetHashValue(hPtr); tupleObjPtr->hashPtr = NULL; DestroyTupleObject(tupleObjPtr); } Blt_DeleteHashTable(&dataPtr->instTable); Tcl_DeleteAssocData(interp, TUPLE_THREAD_KEY); Blt_Free(dataPtr); } /* * -------------------------------------------------------------- * * GetTupleInterpData -- * * Creates or retrieves data associated with tuple data objects * for a particular thread. We're using Tcl_GetAssocData rather * than the Tcl thread routines so BLT can work with pre-8.0 * Tcl versions that don't have thread support. * * Results: * Returns a pointer to the tuple interpreter data. * * -------------------------------------------------------------- */ static TupleInterpData * GetTupleInterpData(Tcl_Interp *interp) { Tcl_InterpDeleteProc *proc; TupleInterpData *dataPtr; dataPtr = (TupleInterpData *) Tcl_GetAssocData(interp, TUPLE_THREAD_KEY, &proc); if (dataPtr == NULL) { dataPtr = Blt_Malloc(sizeof(TupleInterpData)); assert(dataPtr); dataPtr->interp = interp; Tcl_SetAssocData(interp, TUPLE_THREAD_KEY, TupleInterpDeleteProc, dataPtr); Blt_InitHashTable(&dataPtr->instTable, BLT_STRING_KEYS); } return dataPtr; } /* *---------------------------------------------------------------------- * * NewTagTable -- * *---------------------------------------------------------------------- */ static TagTable * NewTagTable(void) { TagTable *tablePtr; tablePtr = Blt_Malloc(sizeof(TagTable)); Blt_InitHashTable(&tablePtr->tagTable, BLT_STRING_KEYS); tablePtr->refCount = 1; return tablePtr; } static void ReleaseTagTable(TagTable *tablePtr) { tablePtr->refCount--; if (tablePtr->refCount <= 0) { Blt_HashEntry *hPtr; Blt_HashSearch cursor; Blt_TupleTagEntry *tPtr; for(hPtr = Blt_FirstHashEntry(&tablePtr->tagTable, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tPtr = Blt_GetHashValue(hPtr); Blt_DeleteHashTable(&tPtr->rowTable); Blt_Free(tPtr); } Blt_DeleteHashTable(&tablePtr->tagTable); Blt_Free(tablePtr); } } static int ParseParentheses( Tcl_Interp *interp, CONST char *string, char **leftPtr, char **rightPtr) { register char *p; char *left, *right; left = right = NULL; for (p = (char *)string; *p != '\0'; p++) { if (*p == '(') { left = p; } else if (*p == ')') { right = p; } } if (left != right) { if (((left != NULL) && (right == NULL)) || ((left == NULL) && (right != NULL)) || (left > right) || (right != (p - 1))) { if (interp != NULL) { Tcl_AppendResult(interp, "bad array specification \"", string, "\"", (char *)NULL); } return TCL_ERROR; } } *leftPtr = left; *rightPtr = right; return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_TupleGetColumnIndex-- * * Returns the index of the column by the name given. Translates * between column names and indices. Column indices are a direct * lookup while names must be hashed. * * Results: * A standard Tcl result. Returns the index of via *indexPtr. * If no column can be found, then TCL_ERROR is returned and * an error message is left in the interpreter result. * *---------------------------------------------------------------------- */ int Blt_TupleGetColumnIndex( Tcl_Interp *interp, TupleObject *tupleObjPtr, CONST char *key, unsigned int *indexPtr) { char *left, *right; Column *columnPtr; Blt_HashEntry *hPtr; if (ParseParentheses(interp, key, &left, &right) != TCL_OK) { return TCL_ERROR; } if (left != NULL) { *left = '\0'; hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); *left = '('; } else { hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); } if (hPtr == NULL) { if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } return TCL_ERROR; } columnPtr = Blt_GetHashValue(hPtr); *indexPtr = columnPtr->index; return TCL_OK; } /* *---------------------------------------------------------------------- * * CallTraces -- * * Examines the traces set for each client of the tuple object * and fires any matching trace. * * Traces can match on * row if the trace was specified by index. * tag if the trace was specified by tag. * key if the trace was specified with a list of * one or more keys. * flag type of trace (read, write, unset, create) * * If no matching criteria is specified (no tag, key, or tuple * address) then only the bit flag has to match. * * If the TUPLE_TRACE_FOREIGN_ONLY is set in the handler, it * means to ignore actions that are initiated by that client of * the object. Only actions by other clients are handled. * * Results: * Always returns TCL_OK. * * Side Effects: * Traces on the tuple table location may be fired. * *---------------------------------------------------------------------- */ static void CallTraces( Tcl_Interp *interp, TupleClient *clientPtr, /* Client holding a reference to the * tuple object. */ Row *rowPtr, /* Tuple being modified. */ Column *columnPtr, unsigned int flags) { Blt_ChainLink *linkPtr; Trace *tracePtr; TupleObject *tupleObjPtr; /* Tuple that was changed. */ tupleObjPtr = clientPtr->tupleObjPtr; /* For each client, examine its trace handlers. */ for(linkPtr = Blt_ChainFirstLink(tupleObjPtr->traces); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tracePtr = Blt_ChainGetValue(linkPtr); if ((tracePtr->tuple != NULL) && (tracePtr->tuple != rowPtr)) { continue; /* Row doesn't match. */ } if ((tracePtr->mask & flags) == 0) { continue; /* Missing the required trace flags. */ } if ((tracePtr->table == clientPtr) && (tracePtr->mask & TUPLE_TRACE_FOREIGN_ONLY)) { continue; /* This client initiated the action. */ } if ((tracePtr->withTag != NULL) && (!Blt_TupleHasTag(tracePtr->table, rowPtr, tracePtr->withTag))){ continue; /* Tag doesn't match. */ } if (tracePtr->keys != NULL) { register CONST char **p; for (p = tracePtr->keys; *p != NULL; p++) { if (strcmp(*p, columnPtr->key) == 0) { break; } } if (*p == NULL) { continue; /* No keys match. */ } } if ((*tracePtr->proc) (tracePtr->clientData, tupleObjPtr->interp, rowPtr->index, columnPtr->index, flags) != TCL_OK) { if (interp != NULL) { Tcl_BackgroundError(interp); } } } } /* * -------------------------------------------------------------- * * GetValue -- * * Gets a scalar Tcl_Obj value from the table at the designated * row and column. Create and read traces may be fired. * * Results: * Always returns TCL_OK. * * -------------------------------------------------------------- */ static int GetValue( Tcl_Interp *interp, /* Interpreter to reports result to. */ TupleClient *clientPtr, /* Table. */ Row *rowPtr, /* Row. */ Column *columnPtr, /* Column. */ Tcl_Obj **objPtrPtr) /* (out) Value from table. */ { Blt_HashEntry *hPtr; BusyKey busy; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; int isNew; busy.rowPtr = rowPtr; busy.columnPtr = columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->busyTable, &busy, &isNew); if (isNew) { CallTraces(interp, clientPtr, rowPtr, columnPtr, TUPLE_TRACE_READ); } Blt_DeleteHashEntry(&tupleObjPtr->busyTable, hPtr); /* Access the data value after traces have been called. */ *objPtrPtr = VALUE(rowPtr, columnPtr); return TCL_OK; } /* * -------------------------------------------------------------- * * SetValue -- * * Sets a scalar Tcl_Obj value in the table at the designated row * and column. Write traces may be fired. * * Results: * Always returns TCL_OK. * * -------------------------------------------------------------- */ static int SetValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, Column *columnPtr, Tcl_Obj *objPtr) { Tcl_Obj *oldObjPtr; unsigned int flags; Blt_HashEntry *hPtr; int isNew; BusyKey busy; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; if ((rowPtr->nColumns < columnPtr->index) && (rowPtr->data != NULL)) { oldObjPtr = rowPtr->data[columnPtr->index]; } else { AllocateRows(tupleObjPtr, rowPtr); objObjPtr = NULL; } if (objPtr != NULL) { Tcl_IncrRefCount(objPtr); } if (oldObjPtr != NULL) { Tcl_DecrRefCount(oldObjPtr); } rowPtr->data[columnPtr->index] = objPtr; flags = TUPLE_TRACE_WRITE; if (oldObjPtr == NULL) { flags |= TUPLE_TRACE_CREATE; } busy.rowPtr = rowPtr; busy.columnPtr = columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->busyTable, &busy, &isNew); if (isNew) { CallTraces(interp, clientPtr, rowPtr, columnPtr, flags); } Blt_DeleteHashEntry(&tupleObjPtr->busyTable, hPtr); return TCL_OK; } /* * -------------------------------------------------------------- * * UnsetValue -- * * Unsets a scalar Tcl_Obj value in the table at the designated * row and column. Unset traces may be fired. * * Results: * Always returns TCL_OK. * * -------------------------------------------------------------- */ static int UnsetValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, Column *columnPtr) { Tcl_Obj *objPtr; objPtr = VALUE(rowPtr, columnPtr); if (objPtr != NULL) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Blt_HashEntry *hPtr; BusyKey busy; int isNew; Tcl_DecrRefCount(objPtr); rowPtr->data[columnPtr->index] = NULL; busy.rowPtr = rowPtr; busy.columnPtr = columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->busyTable, &busy, &isNew); if (isNew) { CallTraces(interp, clientPtr, rowPtr, columnPtr, TUPLE_TRACE_UNSET); } Blt_DeleteHashEntry(&tupleObjPtr->busyTable, hPtr); } return TCL_OK; } static int GetArrayValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, Column *columnPtr, CONST char *elemName, Tcl_Obj **objPtrPtr, int doTrace) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Tcl_Obj *objPtr; Blt_HashTable *tablePtr; Blt_HashEntry *hPtr; if (doTrace) { BusyKey busy; int isNew; busy.rowPtr = rowPtr; busy.columnPtr = columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->busyTable, &busy, &isNew); if (isNew) { CallTraces(interp, clientPtr, rowPtr, columnPtr, TUPLE_TRACE_READ); } Blt_DeleteHashEntry(&tupleObjPtr->busyTable, hPtr); } /* Access the data value after traces have been called. */ objPtr = VALUE(rowPtr, columnPtr); if (objPtr != NULL) { if (Tcl_IsShared(objPtr)) { Tcl_DecrRefCount(objPtr); objPtr = Tcl_DuplicateObj(objPtr); Tcl_IncrRefCount(objPtr); rowPtr->data[columnPtr->index] = objPtr; } if (Blt_GetArrayFromObj(interp, objPtr, &tablePtr) != TCL_OK) { return TCL_ERROR; } hPtr = Blt_FindHashEntry(tablePtr, elemName); if (hPtr == NULL) { if (interp != NULL) { Tcl_AppendResult(interp, "can't find \"", columnPtr->key, "(", elemName, ")\"", (char *)NULL); } return TCL_ERROR; } objPtr = Blt_GetHashValue(hPtr); } *objPtrPtr = objPtr; return TCL_OK; } static int SetArrayValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, Column *columnPtr, CONST char *elemName, Tcl_Obj *valueObjPtr) { Blt_HashTable *tablePtr; Tcl_Obj *arrayObjPtr; unsigned int flags; Blt_HashEntry *hPtr; int isNew; BusyKey busy; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; flags = TUPLE_TRACE_WRITE; arrayObjPtr = VALUE(rowPtr, columnPtr); if (arrayObjPtr == NULL) { arrayObjPtr = Blt_NewArrayObj(0, (Tcl_Obj **)NULL); Tcl_IncrRefCount(arrayObjPtr); flags |= TUPLE_TRACE_CREATE; AllocateRows(tupleObjPtr, rowPtr); } else if (Tcl_IsShared(arrayObjPtr)) { Tcl_DecrRefCount(arrayObjPtr); arrayObjPtr = Tcl_DuplicateObj(arrayObjPtr); Tcl_IncrRefCount(arrayObjPtr); } rowPtr->data[columnPtr->index] = arrayObjPtr; if (Blt_GetArrayFromObj(interp, arrayObjPtr, &tablePtr) != TCL_OK) { return TCL_ERROR; } Tcl_InvalidateStringRep(arrayObjPtr); hPtr = Blt_CreateHashEntry(tablePtr, elemName, &isNew); assert(hPtr); Tcl_IncrRefCount(valueObjPtr); if (!isNew) { Tcl_Obj *oldValueObjPtr; /* An element by the same name already exists. Decrement the * reference count of the old value. */ oldValueObjPtr = Blt_GetHashValue(hPtr); if (oldValueObjPtr != NULL) { Tcl_DecrRefCount(oldValueObjPtr); } } Blt_SetHashValue(hPtr, valueObjPtr); /* * We don't handle traces on a per array element basis. Setting * any element can fire traces for the value. */ busy.rowPtr = rowPtr; busy.columnPtr = columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->busyTable, &busy, &isNew); if (isNew) { CallTraces(interp, clientPtr, rowPtr, columnPtr, flags); } Blt_DeleteHashEntry(&tupleObjPtr->busyTable, hPtr); return TCL_OK; } static int UnsetArrayValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, Column *columnPtr, CONST char *elemName) { Blt_HashEntry *hPtr; Blt_HashTable *tablePtr; BusyKey busy; Tcl_Obj *arrayObjPtr, *valueObjPtr; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; int isNew; arrayObjPtr = VALUE(rowPtr, columnPtr); if (arrayObjPtr == NULL) { return TCL_OK; } if (Tcl_IsShared(arrayObjPtr)) { Tcl_DecrRefCount(arrayObjPtr); arrayObjPtr = Tcl_DuplicateObj(arrayObjPtr); Tcl_IncrRefCount(arrayObjPtr); } if (Blt_GetArrayFromObj(interp, arrayObjPtr, &tablePtr) != TCL_OK) { return TCL_ERROR; } hPtr = Blt_FindHashEntry(tablePtr, elemName); if (hPtr == NULL) { return TCL_OK; /* Element doesn't exist, Ok. */ } valueObjPtr = Blt_GetHashValue(hPtr); Tcl_DecrRefCount(valueObjPtr); Blt_DeleteHashEntry(tablePtr, hPtr); rowPtr->data[columnPtr->index] = NULL; /* * Un-setting any element in the array can cause the trace on the value * to fire. */ busy.rowPtr = rowPtr; busy.columnPtr = columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->busyTable, &busy, &isNew); if (isNew) { CallTraces(interp, clientPtr, rowPtr, columnPtr, TUPLE_TRACE_UNSET); } Blt_DeleteHashEntry(&tupleObjPtr->busyTable, hPtr); return TCL_OK; } static int ExtendRows(TupleObject *tupleObjPtr, int extra) { int nRows; int i; nRows = tupleObjPtr->nRows + extra; if (tupleObjPtr->rowsAllocated < nRows) { Row **rows; if (tupleObjPtr->rowsAllocated == 0) { tupleObjPtr->rowsAllocated = 32; } while (tupleObjPtr->rowsAllocated < nRows) { tupleObjPtr->rowsAllocated += tupleObjPtr->rowsAllocated; } rows = Blt_Realloc(tupleObjPtr->rows, tupleObjPtr->rowsAllocated * sizeof(Row *)); if (rows == NULL) { return -1; } tupleObjPtr->rows = rows; } for (i = tupleObjPtr->nRows; i < nRows; i++) { tupleObjPtr->rows[i] = NewRow(tupleObjPtr, i); } tupleObjPtr->nRows = nRows; return 0; } static int ExtendColumns(TupleObject *tupleObjPtr, Row *rowPtr) { if (tupleObjPtr->columnsAllocated < rowPtr->nColumns) { Tcl_Obj **data; int i; data = Blt_Realloc(rowPtr->data, tupleObjPtr->columnsAllocated * sizeof(Tcl_Obj *)); if (data == NULL) { return TCL_ERROR; } for(i = rowPtr->nColumns; i < tupleObjPtr->columnsAllocated; i++) { data[i] = NULL; } rowPtr->data = data; rowPtr->nColumns = tupleObjPtr->columnsAllocated; } return TCL_OK; } /* * ---------------------------------------------------------------------- * * FindTupleObjectInNamespace -- * * Searches for the tuple object in a given namespace. * * Results: * Returns a pointer to the tuple object if found, otherwise NULL. * * ---------------------------------------------------------------------- */ static TupleObject * FindTupleObjectInNamespace( TupleInterpData *dataPtr, /* Interpreter-specific data. */ Tcl_Namespace *nsPtr, CONST char *objName) { Tcl_DString dString; char *name; Blt_HashEntry *hPtr; name = Blt_GetQualifiedName(nsPtr, objName, &dString); hPtr = Blt_FindHashEntry(&dataPtr->instTable, name); Tcl_DStringFree(&dString); if (hPtr != NULL) { return Blt_GetHashValue(hPtr); } return NULL; } /* * ---------------------------------------------------------------------- * * GetTupleObject -- * * Searches for the tuple object associated by the name given. * * Results: * Returns a pointer to the tuple object if found, otherwise NULL. * * ---------------------------------------------------------------------- */ static TupleObject * GetTupleObject(Tcl_Interp *interp, CONST char *name, unsigned int flags) { CONST char *tupleName; Tcl_Namespace *nsPtr; /* Namespace associated with the tuple object. * If NULL, indicates to look in first the * current namespace and then the global * for the object. */ TupleInterpData *dataPtr; /* Interpreter-specific data. */ TupleObject *tupleObjPtr; tupleObjPtr = NULL; if (Blt_ParseQualifiedName(interp, name, &nsPtr, &tupleName) != TCL_OK) { Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"", (char *)NULL); return NULL; } dataPtr = GetTupleInterpData(interp); if (nsPtr != NULL) { tupleObjPtr = FindTupleObjectInNamespace(dataPtr, nsPtr, tupleName); } else { if (flags & NS_SEARCH_CURRENT) { /* Look first in the current namespace. */ nsPtr = Tcl_GetCurrentNamespace(interp); tupleObjPtr = FindTupleObjectInNamespace(dataPtr, nsPtr, tupleName); } if ((tupleObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) { nsPtr = Tcl_GetGlobalNamespace(interp); tupleObjPtr = FindTupleObjectInNamespace(dataPtr, nsPtr, tupleName); } } return tupleObjPtr; } /* *---------------------------------------------------------------------- * * NewTupleClient -- * * Creates a new tuple client. Clients shared a tuple data * object. They individually manage traces and events on tuple * objects. Returns a pointer to the malloc'ed structure. This * is passed to the client as a tuple token. * * Results: * A pointer to a TupleClient is returned. If one can't * be allocated, NULL is returned. * *---------------------------------------------------------------------- */ static TupleClient * NewTupleClient(TupleObject *tupleObjPtr) { TupleClient *clientPtr; clientPtr = Blt_Calloc(1, sizeof(TupleClient)); if (clientPtr != NULL) { clientPtr->magic = TUPLE_MAGIC; clientPtr->linkPtr = Blt_ChainAppend(tupleObjPtr->clients, clientPtr); clientPtr->tupleObjPtr = tupleObjPtr; clientPtr->tagTablePtr = NewTagTable(); } return clientPtr; } /* *---------------------------------------------------------------------- * * NotifyIdleProc -- * * Used to invoke event handler routines at some idle point. * This routine is called from the Tcl event loop. Errors * generated by the event handler routines are backgrounded. * * Results: * None. * *---------------------------------------------------------------------- */ static void NotifyIdleProc(ClientData clientData) { Notifier *notifyPtr = clientData; int result; notifyPtr->notifyPending = FALSE; notifyPtr->mask |= TUPLE_NOTIFY_ACTIVE; result = (*notifyPtr->proc)(notifyPtr->clientData, ¬ifyPtr->event); notifyPtr->mask &= ~TUPLE_NOTIFY_ACTIVE; if (result != TCL_OK) { Tcl_BackgroundError(notifyPtr->interp); } } /* *---------------------------------------------------------------------- * * NotifyClients -- * * Traverses the list of event callbacks and checks if one * matches the given event. A client may trigger an action that * causes the tuple object to notify it. This can be prevented * by setting the TUPLE_NOTIFY_FOREIGN_ONLY bit in the event * handler. * * If a matching handler is found, a callback may be called either * immediately or at the next idle time depending upon the * TUPLE_NOTIFY_WHENIDLE bit. * * Since a handler routine may trigger yet another call to * itself, callbacks are ignored while the event handler is * executing. * *---------------------------------------------------------------------- */ static void NotifyClients( TupleClient *sourcePtr, TupleObject *tupleObjPtr, unsigned int eventFlag) { Blt_ChainLink *linkPtr; Blt_TupleNotifyEvent event; Notifier *notifyPtr; event.type = eventFlag; /* Issue callbacks indicating that the structure of the tuple * table has changed. */ for (linkPtr = Blt_ChainFirstLink(tupleObjPtr->notifiers); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { notifyPtr = Blt_ChainGetValue(linkPtr); if ((notifyPtr->mask & TUPLE_NOTIFY_ACTIVE) || (notifyPtr->mask & eventFlag) == 0) { continue; /* Ignore callbacks that are generated * inside of a notify handler routine. */ } if ((notifyPtr->table == sourcePtr) && (notifyPtr->mask & TUPLE_NOTIFY_FOREIGN_ONLY)) { continue; /* Don't notify yourself. */ } event.table = notifyPtr->table; if (notifyPtr->mask & TUPLE_NOTIFY_WHENIDLE) { if (!notifyPtr->notifyPending) { notifyPtr->notifyPending = TRUE; notifyPtr->event = event; Tcl_DoWhenIdle(NotifyIdleProc, notifyPtr); } } else { int result; notifyPtr->mask |= TUPLE_NOTIFY_ACTIVE; result = (*notifyPtr->proc) (notifyPtr->clientData, &event); notifyPtr->mask &= ~TUPLE_NOTIFY_ACTIVE; if (result != TCL_OK) { Tcl_BackgroundError(notifyPtr->interp); } } } } /* *---------------------------------------------------------------------- * * Blt_TupleCreateObject -- * * Creates a tuple object by the designated name. It's an error * if a tuple object already exists by that name. After * successfully creating the object, the caller must then call * Blt_TupleGetToken to allocate a token to share and manipulate * the object. * * Results: * A standard Tcl result. If successful, a new tuple object is * created and TCL_OK is returned. If an object already exists * or the tuple object can't be allocated, then TCL_ERROR is * returned and an error message is left in the interpreter. * * Side Effects: * A new tuple object is created. * *---------------------------------------------------------------------- */ int Blt_TupleCreateTable( Tcl_Interp *interp, /* Interpreter to report errors back to. */ CONST char *name, /* Name of tuple in namespace. Object * must not already exist. */ TupleClient **clientPtrPtr) /* (out) Client token of newly created * tuple object. Releasing the token * will free the tuple. If NULL, no * token is generated. */ { Tcl_DString dString; Tcl_Namespace *nsPtr; TupleInterpData *dataPtr; TupleObject *tupleObjPtr; CONST char *tupleName; char string[200]; dataPtr = GetTupleInterpData(interp); if (name != NULL) { /* Does an object by this name already exist in the current * namespace? */ tupleObjPtr = GetTupleObject(interp, name, NS_SEARCH_CURRENT); if (tupleObjPtr != NULL) { Tcl_AppendResult(interp, "a tuple object \"", name, "\" already exists", (char *)NULL); return TCL_ERROR; } } else { /* Generate a unique name in the current namespace. */ do { sprintf(string, "tuple%d", dataPtr->nextId++); } while (GetTupleObject(interp, name, NS_SEARCH_CURRENT) != NULL); name = string; } /* * Tear apart and put back together the namespace-qualified name * of the object. This is to ensure that naming is consistent. */ tupleName = name; if (Blt_ParseQualifiedName(interp, name, &nsPtr, &tupleName) != TCL_OK) { Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"", (char *)NULL); return TCL_ERROR; } if (nsPtr == NULL) { /* * Note that unlike Tcl_CreateCommand, an unqualified name * doesn't imply the global namespace, but the current one. */ nsPtr = Tcl_GetCurrentNamespace(interp); } name = Blt_GetQualifiedName(nsPtr, tupleName, &dString); tupleObjPtr = NewTupleObject(dataPtr, interp, name); if (tupleObjPtr == NULL) { Tcl_AppendResult(interp, "can't allocate tuple object \"", name, "\"", (char *)NULL); Tcl_DStringFree(&dString); return TCL_ERROR; } Tcl_DStringFree(&dString); if (clientPtrPtr != NULL) { TupleClient *clientPtr; clientPtr = NewTupleClient(tupleObjPtr); if (clientPtr == NULL) { Tcl_AppendResult(interp, "can't allocate tuple token", (char *)NULL); return TCL_ERROR; } *clientPtrPtr = clientPtr; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_TupleGetToken -- * * Allocates a token for the tuple object designated by name. * It's an error if no tuple object exists by that name. The * token returned is passed to various routines to manipulate the * object. Traces and event notifications are also made through * the token. * * Results: * A new token is returned representing the tuple object. * * Side Effects: * If this is the remaining client, then the tuple object itself * is freed. * *---------------------------------------------------------------------- */ int Blt_TupleGetTable( Tcl_Interp *interp, /* Interpreter to report errors back to. */ CONST char *name, /* Name of tuple object in namespace. */ TupleClient **clientPtrPtr) { TupleClient *clientPtr; TupleObject *tupleObjPtr; tupleObjPtr = GetTupleObject(interp, name, NS_SEARCH_BOTH); if (tupleObjPtr == NULL) { Tcl_AppendResult(interp, "can't find a tuple object \"", name, "\"", (char *)NULL); return TCL_ERROR; } clientPtr = NewTupleClient(tupleObjPtr); if (clientPtr == NULL) { Tcl_AppendResult(interp, "can't allocate token for tuple \"", name, "\"", (char *)NULL); return TCL_ERROR; } *clientPtrPtr = clientPtr; return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_TupleReleaseToken -- * * Releases the tuple token, indicating this the client is no * longer using the object. The client is removed from the tuple * object's client list. If this is the last client, then the * object itself is destroyed and memory is freed. * * Results: * None. * * Side Effects: * If this is the remaining client, then the tuple object itself * is freed. * *---------------------------------------------------------------------- */ void Blt_TupleReleaseTable(TupleClient *clientPtr) { TupleObject *tupleObjPtr; Blt_ChainLink *linkPtr; Notifier *notifyPtr; Trace *tracePtr; if (clientPtr->magic != TUPLE_MAGIC) { fprintf(stderr, "invalid tuple object token 0x%lx\n", (unsigned long)clientPtr); return; } /* Remove any traces that were set by this client. */ for (linkPtr = Blt_ChainFirstLink(tupleObjPtr->traces); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { tracePtr = Blt_ChainGetValue(linkPtr); if (tracePtr->table == clientPtr) { Blt_TupleDeleteTrace(tracePtr); } } /* Also remove all event handlers created by this client. */ for(linkPtr = Blt_ChainFirstLink(tupleObjPtr->notifiers); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { notifyPtr = Blt_ChainGetValue(linkPtr); if (notifyPtr->table == clientPtr) { Blt_TupleDeleteNotifier(notifyPtr); } } if (clientPtr->tagTablePtr != NULL) { ReleaseTagTable(clientPtr->tagTablePtr); } tupleObjPtr = clientPtr->tupleObjPtr; if (tupleObjPtr != NULL) { /* Remove the client from the server's list */ Blt_ChainDeleteLink(tupleObjPtr->clients, clientPtr->linkPtr); if (Blt_ChainGetLength(tupleObjPtr->clients) == 0) { DestroyTupleObject(tupleObjPtr); } } clientPtr->magic = 0; Blt_Free(clientPtr); } /* *---------------------------------------------------------------------- * * Blt_TupleTableExists -- * * Indicates if a tuple object by the given name exists in either * the current or global namespace. * * Results: * Returns 1 if a tuple object exists and 0 otherwise. * *---------------------------------------------------------------------- */ int Blt_TupleTableExists( Tcl_Interp *interp, /* Interpreter to report errors back to. */ CONST char *name) /* Name of tuple object in the * designated namespace. */ { TupleObject *tupleObjPtr; tupleObjPtr = GetTupleObject(interp, name, NS_SEARCH_BOTH); if (tupleObjPtr == NULL) { Tcl_ResetResult(interp); return 0; } return 1; } /* *---------------------------------------------------------------------- * * Blt_TupleCreateNotifier -- * * Creates an notifier using the following three pieces of * information: * 1. C function pointer, * 2. one-word of data passed on each call, and * 3. event mask indicating which events are of interest. * If an event already exists matching all of the above criteria, * it is repositioned on the end of the event handler list. This * means that it will be the last to fire. * * Results: * Returns a pointer to the event handler. * * Side Effects: * Memory for the event handler is possibly allocated. * *---------------------------------------------------------------------- */ Blt_TupleNotifier Blt_TupleCreateNotifier( TupleClient *clientPtr, unsigned int mask, Blt_TupleNotifyEventProc *proc, ClientData clientData) { Notifier *notifyPtr; notifyPtr = Blt_Malloc(sizeof (Notifier)); assert(notifyPtr); notifyPtr->proc = proc; notifyPtr->clientData = clientData; notifyPtr->mask = mask; notifyPtr->notifyPending = FALSE; notifyPtr->interp = clientPtr->tupleObjPtr->interp; notifyPtr->linkPtr = Blt_ChainAppend(clientPtr->tupleObjPtr->notifiers, notifyPtr); return notifyPtr; } /* *---------------------------------------------------------------------- * * Blt_TupleDeleteNotifier -- * * Removes the event handler designated by following three pieces * of information: 1. C function pointer, 2. one-word of data * passed on each call, and 3. event mask indicating which events * are of interest. * * Results: * Nothing. * * Side Effects: * Memory for the event handler is freed. * *---------------------------------------------------------------------- */ void Blt_TupleDeleteNotifier(Notifier *notifyPtr) { if (notifyPtr->notifyPending) { Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr); } if (notifyPtr->linkPtr != NULL){ Blt_ChainDeleteLink(notifyPtr->table->tupleObjPtr->notifiers, notifyPtr->linkPtr); Blt_Free(notifyPtr); } } /* *---------------------------------------------------------------------- * * Blt_TupleRowIndex -- * * Returns the index of the tuple. * * Results: * Returns the row index of the tuple. * *---------------------------------------------------------------------- */ unsigned int Blt_TupleRowIndex(Row *rowPtr) { return rowPtr->index; } /* *---------------------------------------------------------------------- * * Blt_TupleGetTupleByIndex -- * * Returns the tuple at the row designated int the tuple table. * * Results: * Returns the tuple at the row. If the index is out of range * then NULL is returned. * *---------------------------------------------------------------------- */ Blt_Tuple Blt_TupleGetTupleByIndex(TupleClient *clientPtr, unsigned int row) { if (row >= clientPtr->tupleObjPtr->nRows) { return NULL; } return clientPtr->tupleObjPtr->rows[row]; } Blt_Tuple Blt_TupleNextTuple(TupleClient *clientPtr, Row *rowPtr) { return Blt_TupleGetTupleByIndex(clientPtr, rowPtr->index + 1); } /* *---------------------------------------------------------------------- * * Blt_TupleGetColumnKey -- * * Returns the key of the column with the given index. This is a * helper routine to translate between column indices and keys. * * Results: * Returns the key of the column with the given index. If the * index is invalid, NULL is returned. * *---------------------------------------------------------------------- */ CONST char * Blt_TupleGetColumnKey(TupleClient *clientPtr, int columnIndex) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Column *columnPtr; if ((columnIndex < 0) || (columnIndex >= tupleObjPtr->nColumns)) { return NULL; } columnPtr = tupleObjPtr->columns[columnIndex]; return columnPtr->key; } /* *---------------------------------------------------------------------- * * Blt_TupleAddColumn -- * * Add a new column to the tuple object. Rows are slots in an * array of tuples. The array grows by doubling its size, so * there may be more slots than needed (# rows). * * Results: * Returns TCL_OK is the table is resized and TCL_ERROR if an * not enough memory was available. * * Side Effects: * If more rows are needed, the array which holds the tuples is * reallocated by doubling its size. Storage for the tuples * isn't allocated until the tuple is needed. * *---------------------------------------------------------------------- */ unsigned int Blt_TupleAddColumn( TupleClient *clientPtr, CONST char *key, int *isNewPtr) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Blt_HashEntry *hPtr; Column *columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->columnTable, key, isNewPtr); if (!*isNewPtr) { columnPtr = Blt_GetHashValue(hPtr); } else { columnPtr = Blt_Calloc(1, sizeof(Column)); columnPtr->key = Blt_GetHashKey(&tupleObjPtr->columnTable, hPtr); columnPtr->type = 0; columnPtr->hashPtr = hPtr; columnPtr->index = tupleObjPtr->nColumns; tupleObjPtr->columns[columnPtr->index] = columnPtr; tupleObjPtr->nColumns++; NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_CREATE_COLUMN); } return columnPtr->index; } /* *---------------------------------------------------------------------- * * Blt_TupleAddColumns -- * * Adds one or more columns to the tuple table. This creates a * new column. The tuples are individually resized when new data * is added to them. * * Results: * Returns TCL_OK is the tuple is resized and TCL_ERROR if an * not enough memory was available. * * Side Effects: * If more rows are needed, the array which holds the tuples is * reallocated by doubling its size. Storage for the tuples * isn't allocated until the tuple is needed. * *---------------------------------------------------------------------- */ int Blt_TupleAddColumns( Tcl_Interp *interp, TupleClient *clientPtr, char **keys) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; int i; int nColumns, extra; char **p; Blt_HashEntry *hPtr; int isNew; extra = 0; for (p = keys; *p != NULL; p++) { hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, *p); if (hPtr != NULL) { Tcl_AppendResult(interp, "a column \"", *p, "\" already exists in \"", tupleObjPtr->name, "\"", (char *)NULL); return TCL_ERROR; } extra++; /* Count the number of new columns. */ } nColumns = tupleObjPtr->nColumns + extra; if (nColumns >= tupleObjPtr->columnsAllocated) { Column **columns; int columnsAllocated; columnsAllocated = tupleObjPtr->columnsAllocated; while (nColumns >= columnsAllocated) { columnsAllocated += columnsAllocated; } columns = Blt_Realloc(tupleObjPtr->columns, sizeof(Column *) * columnsAllocated); if (columns == NULL) { return TCL_ERROR; } tupleObjPtr->columns = columns; tupleObjPtr->columnsAllocated = columnsAllocated; } for (p = keys; *p != NULL; p++) { Column *columnPtr; hPtr = Blt_CreateHashEntry(&tupleObjPtr->columnTable, *p, &isNew); columnPtr = Blt_Calloc(1, sizeof(Column)); columnPtr->key = Blt_GetHashKey(&tupleObjPtr->columnTable, hPtr); columnPtr->index = 0; columnPtr->type = 0; columnPtr->hashPtr = hPtr; columnPtr->index = tupleObjPtr->nColumns; tupleObjPtr->columns[columnPtr->index] = columnPtr; tupleObjPtr->nColumns++; } for(i = tupleObjPtr->nColumns; i < tupleObjPtr->columnsAllocated; i++) { tupleObjPtr->columns[i] = NULL; } NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_CREATE_COLUMN); return TCL_OK; } int Blt_TupleDeleteColumnByIndex( Tcl_Interp *interp, TupleClient *clientPtr, unsigned int column) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Column *columnPtr; columnPtr = tupleObjPtr->columns[column]; if (columnPtr != NULL) { Row *rowPtr; int i; int j, k; if (columnPtr->hashPtr != NULL) { Blt_DeleteHashEntry(&tupleObjPtr->columnTable, columnPtr->hashPtr); } for (i = 0; i < tupleObjPtr->nRows; i++) { rowPtr = tupleObjPtr->rows[i]; if ((column < rowPtr->nColumns) && (rowPtr->data != NULL)) { Tcl_Obj *objPtr; /* Release the Tcl_Obj associated with this column and * compress the tuple. */ objPtr = rowPtr->data[column]; if (objPtr != NULL) { Tcl_DecrRefCount(objPtr); rowPtr->data[column] = NULL; } for (j = column, k = column + 1; j < rowPtr->nColumns; j++, k++) { rowPtr->data[k] = rowPtr->data[j]; } rowPtr->nColumns--; } } for (j = column, k = column + 1; j < tupleObjPtr->nColumns; j++, k++) { tupleObjPtr->columns[k] = tupleObjPtr->columns[j]; } tupleObjPtr->columns[k] = NULL; tupleObjPtr->nColumns--; Blt_Free(columnPtr); NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_DELETE_COLUMN); } return TCL_OK; } int Blt_TupleDeleteColumn( Tcl_Interp *interp, TupleClient *clientPtr, CONST char *key) { unsigned int column; if (Blt_TupleGetColumnIndex(interp, clientPtr->tupleObjPtr, key, &column) != TCL_OK) { return TCL_ERROR; } return Blt_TupleDeleteColumnByIndex(interp, clientPtr, column); } /* *---------------------------------------------------------------------- * * Blt_TupleValueExists -- * * Indicates if a value exists for a given row,column index in * the tuple. Note that this routine does not fire read traces. * * Results: * Returns 1 is a value exists, 0 otherwise. * *---------------------------------------------------------------------- */ int Blt_TupleValueExists( TupleClient *clientPtr, Row *rowPtr, CONST char *key) { Blt_HashEntry *hPtr; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; char *left, *right; int result; if (rowPtr->index < tupleObjPtr->nRows) { return FALSE; } if (ParseParentheses((Tcl_Interp *)NULL, key, &left, &right) != TCL_OK) { return TCL_ERROR; } result = FALSE; if (left != NULL) { *left = *right = '\0'; hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr != NULL) { Tcl_Obj *objPtr; Column *columnPtr; columnPtr = Blt_GetHashValue(hPtr); if (GetArrayValue((Tcl_Interp *)NULL, clientPtr, rowPtr, columnPtr, left + 1, &objPtr, TRACE_IGNORE) == TCL_OK) { result = (objPtr != NULL); } } *left = '(', *right = ')'; } else { hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr != NULL) { Tcl_Obj *objPtr; Column *columnPtr; columnPtr = Blt_GetHashValue(hPtr); objPtr = VALUE(rowPtr, columnPtr); result = (objPtr != NULL); } } return result; } /* *---------------------------------------------------------------------- * * Blt_TupleGetValue -- * * Returns the value at the given row, column index, if one * exists. If no traces are currently active on the the slot, * then a call to check if any trace should fire is called. * * Results: * A standard Tcl result. If the column key isn't valid TCL_ERROR * is returned and an error message is left as the interpreter * result. * * Side Effects: * Traces on the table location may be fired. * *---------------------------------------------------------------------- */ int Blt_TupleGetValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, CONST char *key, Tcl_Obj **objPtrPtr) { Blt_HashEntry *hPtr; Column *columnPtr; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; char *left, *right; int result; if (ParseParentheses(interp, key, &left, &right) != TCL_OK) { return TCL_ERROR; } if (left != NULL) { *left = *right = '\0'; hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr != NULL) { columnPtr = Blt_GetHashValue(hPtr); result = GetArrayValue(interp, clientPtr, rowPtr, columnPtr, left + 1, objPtrPtr, TRACE_OK); } else if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } *left = '(', *right = ')'; if (hPtr == NULL) { return TCL_ERROR; } } else { hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr == NULL) { if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } return TCL_ERROR; } columnPtr = Blt_GetHashValue(hPtr); result = GetValue(interp, clientPtr, rowPtr, columnPtr, objPtrPtr); } return result; } /* *---------------------------------------------------------------------- * * Blt_TupleSetValue -- * * Sets the value at the given row, column index. If the slot * is currently filled, the old Tcl_Obj is decremented. The * new Tcl_Obj is incremented. If no traces are currently active * on the the slot, then a call to check if any trace should * fire is called. There are two type of traces that may fire: * * TUPLE_TRACE_WRITE indicates that the slot was * written to. * TUPLE_TRACE_CREATE indicates there was no former * value set. * * Results: * Always returns TCL_OK. * * Side Effects: * Traces on the table location may be fired. * *---------------------------------------------------------------------- */ int Blt_TupleSetValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, CONST char *key, Tcl_Obj *objPtr) { Blt_HashEntry *hPtr; Column *columnPtr; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; char *left, *right; int result; if (ParseParentheses(interp, key, &left, &right) != TCL_OK) { return TCL_ERROR; } if (left != NULL) { *left = *right = '\0'; hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr != NULL) { columnPtr = Blt_GetHashValue(hPtr); result = SetArrayValue(interp, clientPtr, rowPtr, columnPtr, left + 1, objPtr); } else if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } *left = '(', *right = ')'; if (hPtr == NULL) { return TCL_ERROR; } } else { hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr == NULL) { if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } return TCL_ERROR; } columnPtr = Blt_GetHashValue(hPtr); result = SetValue(interp, clientPtr, rowPtr, columnPtr, objPtr); } return result; } /* *---------------------------------------------------------------------- * * Blt_TupleUnsetValue -- * * Removes the value at the given tuple and column key, if one * exists. The Tcl_Obj is decremented and the slot is set to * NULL. If no traces are currently active on the the slot, then * a call to check if any trace should fire is called. * * The row, column indices are assumed to be valid. * * Results: * Always returns TCL_OK. * * Side Effects: * Traces on the table location may be fired. * *---------------------------------------------------------------------- */ int Blt_TupleUnsetValue( Tcl_Interp *interp, TupleClient *clientPtr, Row *rowPtr, CONST char *key) { Blt_HashEntry *hPtr; Column *columnPtr; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; char *left, *right; int result; if (ParseParentheses(interp, key, &left, &right) != TCL_OK) { return TCL_ERROR; } if (left != NULL) { *left = *right = '\0'; hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr != NULL) { columnPtr = Blt_GetHashValue(hPtr); result = UnsetArrayValue(interp, clientPtr, rowPtr, columnPtr, left + 1); } else if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } *left = '(', *right = ')'; if (hPtr == NULL) { return TCL_ERROR; } } else { hPtr = Blt_FindHashEntry(&tupleObjPtr->columnTable, key); if (hPtr == NULL) { if (interp != NULL) { Tcl_AppendResult(interp, "can't find column \"", key, "\" in \"", tupleObjPtr->name, "\"", (char *)NULL); } return TCL_ERROR; } columnPtr = Blt_GetHashValue(hPtr); result = UnsetValue(interp, clientPtr, rowPtr, columnPtr); } return result; } /* *---------------------------------------------------------------------- * * Blt_TupleGetValueByIndex -- * * Returns the value at the given row, column index, if one * exists. If no traces are currently active on the the slot, * then a call to check if any trace should fire is called. * * The row, column indices are assumed to be valid. * * Results: * Always returns TCL_OK. * * Side Effects: * Traces on the table location may be fired. * *---------------------------------------------------------------------- */ int Blt_TupleGetValueByIndex( Tcl_Interp *interp, TupleClient *clientPtr, unsigned int row, unsigned int column, Tcl_Obj **objPtrPtr) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Row *rowPtr; Column *columnPtr; assert((row >= 0) && (row < tupleObjPtr->nRows)); assert((column >= 0) && (column < tupleObjPtr->nColumns)); rowPtr = tupleObjPtr->rows[row]; columnPtr = tupleObjPtr->columns[column]; return GetValue(interp, clientPtr, rowPtr, columnPtr, objPtrPtr); } /* *---------------------------------------------------------------------- * * Blt_TupleSetValueByIndex -- * * Sets the value at the given row, column index. If the slot * is currently filled, the old Tcl_Obj is decremented. The * new Tcl_Obj is incremented. If no traces are currently active * on the the slot, then a call to check if any trace should * fire is called. There are two type of traces that may fire: * * TUPLE_TRACE_WRITE indicates that the slot was * written to. * TUPLE_TRACE_CREATE indicates there was no former * value set. * * The row, column indices are assumed to be valid. * * Results: * Always returns TCL_OK. * * Side Effects: * Traces on the table location may be fired. * *---------------------------------------------------------------------- */ int Blt_TupleSetValueByIndex( Tcl_Interp *interp, TupleClient *clientPtr, unsigned int row, unsigned int column, Tcl_Obj *objPtr) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Row *rowPtr; Column *columnPtr; assert(row < tupleObjPtr->nRows); assert(column < tupleObjPtr->nColumns); rowPtr = tupleObjPtr->rows[row]; columnPtr = tupleObjPtr->columns[column]; return SetValue(interp, clientPtr, rowPtr, columnPtr, objPtr); } /* *---------------------------------------------------------------------- * * Blt_TupleUnsetValueByIndex -- * * Removes the value at the given row, column index, if one * exists. The Tcl_Obj is decremented and the slot is set * to NULL. If no traces are currently active on the the slot, * then a call to check if any trace should fire is called. * * It's not an error to "unset" nonexistent table locations. * * Results: * Always returns TCL_OK. * * Side Effects: * Traces on the table location may be fired. * *---------------------------------------------------------------------- */ int Blt_TupleUnsetValueByIndex( Tcl_Interp *interp, TupleClient *clientPtr, unsigned int row, unsigned int column) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Row *rowPtr; Column *columnPtr; assert(row < tupleObjPtr->nRows); assert(column < tupleObjPtr->nColumns); rowPtr = tupleObjPtr->rows[row]; columnPtr = tupleObjPtr->columns[column]; return UnsetValue(interp, clientPtr, rowPtr, columnPtr); } /* *---------------------------------------------------------------------- * * Blt_TupleInsertRows -- * * Creates a new rows in the table. The name and position in * the parent are also provided. * *---------------------------------------------------------------------- */ int Blt_TupleInsertRows( TupleClient *clientPtr, /* The tuple client that is creating * this node. If NULL, indicates to * trigger notify events on behalf of * the initiating client also. */ unsigned int insertRow, /* Position in the parent's list of children * where to insert the new node. */ unsigned int nRows) /* Number of rows tuples to insert. */ { TupleObject *tupleObjPtr; int oldRows; tupleObjPtr = clientPtr->tupleObjPtr; if (insertRow >= tupleObjPtr->nRows) { insertRow = tupleObjPtr->nRows; } oldRows = tupleObjPtr->nRows; if (!ExtendRows(tupleObjPtr, nRows)) { return TCL_ERROR; } if (insertRow < oldRows) { int i, j; /* Slide the rows down, creating new tuples in its place. */ for (i = insertRow, j = insertRow + nRows; i < oldRows; i++, j++) { tupleObjPtr->rows[j] = tupleObjPtr->rows[i]; tupleObjPtr->rows[j]->index = j; tupleObjPtr->rows[i] = NewRow(tupleObjPtr, i); } } else { int i; for (i = oldRows; i < tupleObjPtr->nRows; i++) { tupleObjPtr->rows[i] = NewRow(tupleObjPtr, i); } } /* * Issue callbacks to each client indicating that a new node has * been created. */ NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_CREATE); return insertRow; } /* *---------------------------------------------------------------------- * * Blt_TupleMoveRows -- * * Move one of more rows to a new location in the tuple. * *---------------------------------------------------------------------- */ /*ARGSUSED*/ int Blt_TupleMoveRows( TupleClient *clientPtr, int fromRow, int toRow, int nRows) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; Row *fromPtr; int i; /* [ | |a| | | | | | | | |b| | | ] */ fromPtr = tupleObjPtr->rows[fromRow]; for (i = fromRow; i < tupleObjPtr->nRows; i++) { /* Compress the array. */ } /* * Issue callbacks to each client indicating that a node has * been moved. */ NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_MOVE); return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_TupleDeleteTuple -- * * Deletes one or more rows starting at the given index. * This will cause a single delete event. * * Results: * Returns the row at the given index. If the index is out * of range, then NULL is returned. * *---------------------------------------------------------------------- */ void Blt_TupleDeleteTuple( TupleClient *clientPtr, Row *rowPtr) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; int i, j; if (!tupleObjPtr->notifyHold) { /* * Issue callbacks to each client indicating that a tuple will be * removed. */ NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_DELETE_ROW); for (j = tupleObjPtr->nRows - 1, i = j - 1; i >= rowPtr->index; i--, j--) { tupleObjPtr->rows[i] = tupleObjPtr->rows[j]; tupleObjPtr->rows[i]->index = i; } tupleObjPtr->nRows--; } if (Blt_ChainGetLength(tupleObjPtr->clients) < 2) { Blt_TupleClearTags(clientPtr, rowPtr); } DeleteRow(tupleObjPtr, rowPtr); } /* *---------------------------------------------------------------------- * * Blt_TupleStartDeleteRows -- * * Deletes one or more rows starting at the given index. * This will cause a single delete event. * * Results: * Returns the row at the given index. If the index is out * of range, then NULL is returned. * *---------------------------------------------------------------------- */ void Blt_TupleStartDeleteRows(TupleClient *clientPtr) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; tupleObjPtr->notifyHold = TRUE; } /* *---------------------------------------------------------------------- * * Blt_TupleEndDeleteRows -- * * Deletes one or more rows starting at the given index. * This will cause a single delete event. * * Results: * Returns the row at the given index. If the index is out * of range, then NULL is returned. * *---------------------------------------------------------------------- */ void Blt_TupleEndDeleteRows(TupleClient *clientPtr) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; int first; int count; int i; /* * Issue callbacks to each client indicating that a tuple will be * removed. */ tupleObjPtr->notifyHold = FALSE; count = 0; for (i = 0; i < tupleObjPtr->nRows; i++) { if (tupleObjPtr->rows[i] == NULL) { if (first == -1) { first = i; } continue; } if (count < i) { tupleObjPtr->rows[count] = tupleObjPtr->rows[i]; tupleObjPtr->rows[count]->index = count; } count++; } tupleObjPtr->nRows = count++; NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_DELETE_ROW); } /* *---------------------------------------------------------------------- * * Blt_TupleCreateTrace -- * * Creates a trace for one or more tuples with one or more column * keys. Whenever a matching action occurs in the tuple object, * the specified procedure is executed. * * Results: * Returns a token for the trace. * * Side Effects: * Memory is allocated for the trace. * *---------------------------------------------------------------------- */ Blt_TupleTrace Blt_TupleCreateTrace( TupleClient *clientPtr, Row *rowPtr, /* Tuple to be traced. May be NULL, * indicating no row. */ CONST char *tagName, /* Tag of tuple(s) to be traced. May * be NULL. */ CONST char *keyList, /* Tcl list of keys. Split and freed * by Blt_TupleDeleteTrace. May be * NULL indicating all keys match. */ unsigned int mask, /* Bit mask indicating what actions to trace. */ Blt_TupleTraceProc *proc, /* Callback procedure for the trace. */ ClientData clientData) /* One-word of data passed along when * the callback is executed. */ { Trace *tracePtr; TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; tracePtr = Blt_Calloc(1, sizeof (Trace)); assert(tracePtr); if (keyList != NULL) { CONST char **keys; int nKeys; if (Tcl_SplitList(clientPtr->interp, keyList, &nKeys, (char ***)&keys) != TCL_OK) { Blt_Free(tracePtr); return NULL; } if (nKeys == 0) { Blt_Free(keys); } else { tracePtr->keys = keys; } } if (tagName != NULL) { tracePtr->withTag = Blt_Strdup(tagName); } tracePtr->table = clientPtr; tracePtr->proc = proc; tracePtr->clientData = clientData; tracePtr->mask = mask; tracePtr->tuple = rowPtr; tracePtr->withTag = NULL; tracePtr->linkPtr = Blt_ChainAppend(tupleObjPtr->traces, tracePtr); return tracePtr; } /* *---------------------------------------------------------------------- * * Blt_TupleDeleteTrace -- * * Deletes a trace. * * Results: * None. * * Side Effects: * Memory is de-allocated for the trace. * *---------------------------------------------------------------------- */ void Blt_TupleDeleteTrace(Trace *tracePtr) { Blt_ChainDeleteLink(tracePtr->table->tupleObjPtr->traces, tracePtr->linkPtr); if (tracePtr->keys != NULL) { Blt_Free(tracePtr->keys); } if (tracePtr->withTag != NULL) { Blt_Free(tracePtr->withTag); } Blt_Free(tracePtr); } /* *---------------------------------------------------------------------- * * Blt_TupleExtendRows -- * * Adds new rows to the table. Rows are slots in an array * of Rows. The array grows by doubling its size, so there * may be more slots than needed (# rows). * * Results: * Returns TCL_OK is the tuple is resized and TCL_ERROR if an * not enough memory was available. * * Side Effects: * If more rows are needed, the array which holds the tuples is * reallocated by doubling its size. * *---------------------------------------------------------------------- */ int Blt_TupleExtendRows(TupleClient *clientPtr, unsigned int nRows) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; int oldSize; int i; oldSize = tupleObjPtr->nRows; if (!ExtendRows(tupleObjPtr, nRows)) { return TCL_ERROR; } for (i = oldSize; i < tupleObjPtr->nRows; i++) { tupleObjPtr->rows[i] = NewRow(tupleObjPtr, i); } tupleObjPtr->nRows = nRows; NotifyClients(clientPtr, tupleObjPtr, TUPLE_NOTIFY_CREATE); return TCL_OK; } int Blt_TupleReduceRows(TupleClient *clientPtr, int nRows) { TupleObject *tupleObjPtr = clientPtr->tupleObjPtr; if (nRows < tupleObjPtr->nRows) { if (tupleObjPtr->rowsAllocated < nRows) { Row **rows; while (tupleObjPtr->rowsAllocated < nRows) { tupleObjPtr->rowsAllocated += tupleObjPtr->rowsAllocated; } rows = Blt_Realloc(tupleObjPtr->rows, tupleObjPtr->rowsAllocated * sizeof(Row *)); if (rows == NULL) { return TCL_ERROR; } tupleObjPtr->rows = rows; } tupleObjPtr->nRows = nRows; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Blt_TupleAddTag -- * *---------------------------------------------------------------------- */ void Blt_TupleAddTag( TupleClient *clientPtr, Row *rowPtr, CONST char *tagName) { if (strcmp(tagName, "all") != 0) { Blt_HashEntry *hPtr; Blt_HashTable *tablePtr; Blt_TupleTagEntry *tPtr; int isNew; tablePtr = &clientPtr->tagTablePtr->tagTable; hPtr = Blt_CreateHashEntry(tablePtr, tagName, &isNew); assert(hPtr); if (isNew) { tPtr = Blt_Malloc(sizeof(Blt_TupleTagEntry)); Blt_InitHashTable(&tPtr->rowTable, BLT_ONE_WORD_KEYS); Blt_SetHashValue(hPtr, tPtr); tPtr->hashPtr = hPtr; tPtr->tagName = Blt_GetHashKey(tablePtr, hPtr); } else { tPtr = Blt_GetHashValue(hPtr); } hPtr = Blt_CreateHashEntry(&tPtr->rowTable, (char *)rowPtr, &isNew); assert(hPtr); if (isNew) { Blt_SetHashValue(hPtr, rowPtr); } } } /* *---------------------------------------------------------------------- * * Blt_TupleClearTags -- * *---------------------------------------------------------------------- */ void Blt_TupleClearTags(TupleClient *clientPtr, Row *rowPtr) { Blt_HashEntry *hPtr, *h2Ptr; Blt_HashSearch cursor; Blt_TupleTagEntry *tPtr; for (hPtr = Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tPtr = Blt_GetHashValue(hPtr); h2Ptr = Blt_FindHashEntry(&tPtr->rowTable, (char *)rowPtr); if (h2Ptr != NULL) { Blt_DeleteHashEntry(&tPtr->rowTable, h2Ptr); } } } /* *---------------------------------------------------------------------- * * Blt_TupleFirstTag -- * *---------------------------------------------------------------------- */ Blt_HashEntry * Blt_TupleFirstTag( TupleClient *clientPtr, Blt_HashSearch *cursorPtr) { return Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, cursorPtr); } /* *---------------------------------------------------------------------- * * Blt_TupleForgetTag -- * *---------------------------------------------------------------------- */ void Blt_TupleForgetTag( TupleClient *clientPtr, CONST char *tagName) { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName); if (hPtr != NULL) { Blt_TupleTagEntry *tPtr; tPtr = Blt_GetHashValue(hPtr); Blt_DeleteHashEntry(&clientPtr->tagTablePtr->tagTable, hPtr); Blt_DeleteHashTable(&tPtr->rowTable); Blt_Free(tPtr); } } /* *---------------------------------------------------------------------- * * Blt_TupleHasTag -- * *---------------------------------------------------------------------- */ int Blt_TupleHasTag( TupleClient *clientPtr, Row *rowPtr, CONST char *tagName) { Blt_HashEntry *hPtr; if (strcmp(tagName, "all") == 0) { return TRUE; } hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName); if (hPtr != NULL) { Blt_TupleTagEntry *tPtr; tPtr = Blt_GetHashValue(hPtr); hPtr = Blt_FindHashEntry(&tPtr->rowTable, (char *)rowPtr); if (hPtr != NULL) { return TRUE; } } return FALSE; } /* *---------------------------------------------------------------------- * * Blt_TupleRemoveTag -- * *---------------------------------------------------------------------- */ void Blt_TupleRemoveTag( TupleClient *clientPtr, Row *rowPtr, CONST char *tagName) { if (strcmp(tagName, "all") != 0) { Blt_HashEntry *hPtr; Blt_HashTable *tablePtr; tablePtr = &clientPtr->tagTablePtr->tagTable; hPtr = Blt_FindHashEntry(tablePtr, tagName); if (hPtr != NULL) { Blt_TupleTagEntry *tPtr; tPtr = Blt_GetHashValue(hPtr); hPtr = Blt_FindHashEntry(&tPtr->rowTable, (char *)rowPtr); if (hPtr != NULL) { Blt_DeleteHashEntry(&tPtr->rowTable, hPtr); } } } } /* *---------------------------------------------------------------------- * * Blt_TupleTagSetHashTable -- * *---------------------------------------------------------------------- */ Blt_HashTable * Blt_TupleTagTableHashTable( TupleClient *clientPtr, CONST char *tagName) { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName); if (hPtr != NULL) { Blt_TupleTagEntry *tPtr; tPtr = Blt_GetHashValue(hPtr); return &tPtr->rowTable; } return NULL; } int Blt_TupleTagTableIsShared(TupleClient *clientPtr) { return (clientPtr->tagTablePtr->refCount > 1); } /* *---------------------------------------------------------------------- * * Blt_TupleFirstTagged -- * * Returns the id of the first tuple tagged by the given tag in * objPtr. * *---------------------------------------------------------------------- */ Blt_Tuple Blt_TupleFirstTagged( Tcl_Interp *interp, TupleClient *clientPtr, Tcl_Obj *objPtr, Blt_TupleTagSearch *cursorPtr) { char *tagName; cursorPtr->tagType = TAG_TYPE_NONE; tagName = Tcl_GetString(objPtr); /* Process strings that start with digits as indices, not tags. */ if (isdigit(UCHAR(*tagName))) { int row; if (Tcl_GetIntFromObj(interp, objPtr, &row) != TCL_OK) { return NULL; } if ((row < 0) || (row >= clientPtr->tupleObjPtr->nRows)) { Tcl_AppendResult(interp, "row index \"", tagName, "\" is out of range", (char *)NULL); return NULL; } return clientPtr->tupleObjPtr->rows[row]; } cursorPtr->nRows = clientPtr->tupleObjPtr->nRows; if (strcmp(tagName, "all") == 0) { cursorPtr->tagType = TAG_TYPE_ALL; cursorPtr->row = 0; return clientPtr->tupleObjPtr->rows[0]; } else { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName); if (hPtr != NULL) { Blt_TupleTagEntry *tPtr; Row *rowPtr; tPtr = Blt_GetHashValue(hPtr); cursorPtr->rowTablePtr = &tPtr->rowTable; cursorPtr->tagType = TAG_TYPE_TAG; hPtr = Blt_FirstHashEntry(cursorPtr->rowTablePtr, &cursorPtr->cursor); if (hPtr == NULL) { return NULL; } rowPtr = Blt_GetHashValue(hPtr); return rowPtr; } } Tcl_AppendResult(interp, "can't find tag or id \"", tagName, "\" in ", clientPtr->tupleObjPtr->name, (char *)NULL); return NULL; } /* *---------------------------------------------------------------------- * * Blt_TupleNextTagged -- * *---------------------------------------------------------------------- */ Blt_Tuple Blt_TupleNextTagged(Blt_TupleTagSearch *cursorPtr) { if (cursorPtr->tagType == TAG_TYPE_ALL) { TupleClient *clientPtr = cursorPtr->table; cursorPtr->row++; if (cursorPtr->row >= clientPtr->tupleObjPtr->nRows) { return NULL; } return clientPtr->tupleObjPtr->rows[cursorPtr->row]; } if (cursorPtr->tagType == TAG_TYPE_TAG) { Blt_HashEntry *hPtr; Row *rowPtr; hPtr = Blt_NextHashEntry(&cursorPtr->cursor); if (hPtr == NULL) { return NULL; } rowPtr = Blt_GetHashValue(hPtr); return rowPtr; } return NULL; }