1487 lines
45 KiB
C
1487 lines
45 KiB
C
|
/*
|
|||
|
* tclXkeylist.c --
|
|||
|
*
|
|||
|
* Extended Tcl keyed list commands and interfaces.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
* Copyright 1991-1999 Karl Lehenbauer and Mark Diekhans.
|
|||
|
*
|
|||
|
* 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. Karl Lehenbauer and
|
|||
|
* Mark Diekhans make no representations about the suitability of this
|
|||
|
* software for any purpose. It is provided "as is" without express or
|
|||
|
* implied warranty.
|
|||
|
*
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*
|
|||
|
* This file was synthetized from the TclX distribution and made
|
|||
|
* self-containing in order to encapsulate the keyed list datatype
|
|||
|
* for the inclusion in the Tcl threading extension. I have made
|
|||
|
* some minor changes to it in order to get internal object handling
|
|||
|
* thread-safe and allow for this datatype to be used from within
|
|||
|
* the thread shared variables implementation.
|
|||
|
*
|
|||
|
* For any questions, contant Zoran Vasiljevic (zoran@archiware.com)
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
#include "tclThreadInt.h"
|
|||
|
#include "threadSvCmd.h"
|
|||
|
#include "tclXkeylist.h"
|
|||
|
#include <stdarg.h>
|
|||
|
|
|||
|
#ifdef STATIC_BUILD
|
|||
|
#if TCL_MAJOR_VERSION >= 9
|
|||
|
/*
|
|||
|
* Static build, Tcl >= 9, compile-time decision to disable T_ROT calls.
|
|||
|
*/
|
|||
|
#undef Tcl_RegisterObjType
|
|||
|
#define Tcl_RegisterObjType(typePtr) (typePtr)->setFromAnyProc = NULL
|
|||
|
#else
|
|||
|
/*
|
|||
|
* Static build, Tcl <= 9 --> T_ROT is directly linked, no stubs
|
|||
|
* Nothing needs to be done
|
|||
|
*/
|
|||
|
#endif
|
|||
|
#else /* !STATIC_BUILD */
|
|||
|
/*
|
|||
|
* Dynamic build. Assume building with stubs (xx) and make a run-time
|
|||
|
* decision regarding T_ROT.
|
|||
|
* (Ad xx): Should be checked. Without stubs we have to go like static.
|
|||
|
*/
|
|||
|
#undef Tcl_RegisterObjType
|
|||
|
#define Tcl_RegisterObjType(typePtr) if (threadTclVersion<90) { \
|
|||
|
((void (*)(const Tcl_ObjType *))(void *)((&(tclStubsPtr->tcl_PkgProvideEx))[211]))(typePtr); \
|
|||
|
} else { \
|
|||
|
(typePtr)->setFromAnyProc = NULL; \
|
|||
|
}
|
|||
|
#endif /* eof STATIC_BUILD */
|
|||
|
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
/* Stuff copied verbatim from the rest of TclX to avoid dependencies */
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/*
|
|||
|
* Assert macro for use in TclX. Some GCCs libraries are missing a function
|
|||
|
* used by their macro, so we define out own.
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef TCLX_DEBUG
|
|||
|
# define TclX_Assert(expr) ((expr) ? NULL : \
|
|||
|
panic("TclX assertion failure: %s:%d \"%s\"\n",\
|
|||
|
__FILE__, __LINE__, "expr"))
|
|||
|
#else
|
|||
|
# define TclX_Assert(expr)
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Macro that behaves like strdup, only uses ckalloc. Also macro that does the
|
|||
|
* same with a string that might contain zero bytes,
|
|||
|
*/
|
|||
|
|
|||
|
#define ckstrdup(sourceStr) \
|
|||
|
(strcpy ((char *)ckalloc (strlen (sourceStr) + 1), sourceStr))
|
|||
|
|
|||
|
#define ckbinstrdup(sourceStr, length) \
|
|||
|
((char *) memcpy ((char *)ckalloc (length + 1), sourceStr, length + 1))
|
|||
|
|
|||
|
/*
|
|||
|
* Used to return argument messages by most commands.
|
|||
|
*/
|
|||
|
static const char *tclXWrongArgs = "wrong # args: ";
|
|||
|
|
|||
|
static const Tcl_ObjType *listType;
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_IsNullObj --
|
|||
|
*
|
|||
|
* Check if an object is {}, either in list or zero-lemngth string form, with
|
|||
|
* out forcing a conversion.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o objPtr - Object to check.
|
|||
|
* Returns:
|
|||
|
* 1 if NULL, 0 if not.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
TclX_IsNullObj (
|
|||
|
Tcl_Obj *objPtr
|
|||
|
) {
|
|||
|
if (objPtr->typePtr == NULL) {
|
|||
|
return (objPtr->length == 0);
|
|||
|
} else if (objPtr->typePtr == listType) {
|
|||
|
int length;
|
|||
|
Tcl_ListObjLength(NULL, objPtr, &length);
|
|||
|
return (length == 0);
|
|||
|
}
|
|||
|
(void)Tcl_GetString(objPtr);
|
|||
|
return (objPtr->length == 0);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_AppendObjResult --
|
|||
|
*
|
|||
|
* Append a variable number of strings onto the object result already
|
|||
|
* present for an interpreter. If the object is shared, the current contents
|
|||
|
* are discarded.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Interpreter to set the result in.
|
|||
|
* o args - Strings to append, terminated by a NULL.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
TclX_AppendObjResult(Tcl_Interp *interp, ...)
|
|||
|
{
|
|||
|
Tcl_Obj *resultPtr;
|
|||
|
va_list argList;
|
|||
|
char *string;
|
|||
|
|
|||
|
va_start(argList, interp);
|
|||
|
resultPtr = Tcl_GetObjResult (interp);
|
|||
|
|
|||
|
if (Tcl_IsShared(resultPtr)) {
|
|||
|
resultPtr = Tcl_NewStringObj(NULL, 0);
|
|||
|
Tcl_SetObjResult(interp, resultPtr);
|
|||
|
}
|
|||
|
|
|||
|
while (1) {
|
|||
|
string = va_arg(argList, char *);
|
|||
|
if (string == NULL) {
|
|||
|
break;
|
|||
|
}
|
|||
|
Tcl_AppendToObj (resultPtr, string, -1);
|
|||
|
}
|
|||
|
va_end(argList);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_WrongArgs --
|
|||
|
*
|
|||
|
* Easily create "wrong # args" error messages.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o commandNameObj - Object containing name of command (objv[0])
|
|||
|
* o string - Text message to append.
|
|||
|
* Returns:
|
|||
|
* TCL_ERROR
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
TclX_WrongArgs(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *commandNameObj,
|
|||
|
const char *string
|
|||
|
) {
|
|||
|
const char *commandName = Tcl_GetString(commandNameObj);
|
|||
|
Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
|
|||
|
|
|||
|
Tcl_ResetResult(interp);
|
|||
|
Tcl_AppendStringsToObj (resultPtr,
|
|||
|
tclXWrongArgs,
|
|||
|
commandName,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (*string != '\0') {
|
|||
|
Tcl_AppendStringsToObj (resultPtr, " ", string, NULL);
|
|||
|
}
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
/* Here is where the original file begins */
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
/*---------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/*
|
|||
|
* Keyed lists are stored as arrays recursively defined objects. The data
|
|||
|
* portion of a keyed list entry is a Tcl_Obj which may be a keyed list object
|
|||
|
* or any other Tcl object. Since determine the structure of a keyed list is
|
|||
|
* lazy (you don't know if an element is data or another keyed list) until it
|
|||
|
* is accessed, the object can be transformed into a keyed list from a Tcl
|
|||
|
* string or list.
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* An entry in a keyed list array. (FIX: Should key be object?)
|
|||
|
*/
|
|||
|
typedef struct {
|
|||
|
char *key;
|
|||
|
Tcl_Obj *valuePtr;
|
|||
|
} keylEntry_t;
|
|||
|
|
|||
|
/*
|
|||
|
* Internal representation of a keyed list object.
|
|||
|
*/
|
|||
|
typedef struct {
|
|||
|
int arraySize; /* Current slots available in the array. */
|
|||
|
int numEntries; /* Number of actual entries in the array. */
|
|||
|
keylEntry_t *entries; /* Array of keyed list entries. */
|
|||
|
} keylIntObj_t;
|
|||
|
|
|||
|
/*
|
|||
|
* Amount to increment array size by when it needs to grow.
|
|||
|
*/
|
|||
|
#define KEYEDLIST_ARRAY_INCR_SIZE 16
|
|||
|
|
|||
|
/*
|
|||
|
* Macro to duplicate a child entry of a keyed list if it is share by more
|
|||
|
* than the parent.
|
|||
|
*/
|
|||
|
#define DupSharedKeyListChild(keylIntPtr, idx) \
|
|||
|
if (Tcl_IsShared (keylIntPtr->entries [idx].valuePtr)) { \
|
|||
|
keylIntPtr->entries [idx].valuePtr = \
|
|||
|
Tcl_DuplicateObj (keylIntPtr->entries [idx].valuePtr); \
|
|||
|
Tcl_IncrRefCount (keylIntPtr->entries [idx].valuePtr); \
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Macros to validate an keyed list object or internal representation
|
|||
|
*/
|
|||
|
#ifdef TCLX_DEBUG
|
|||
|
# define KEYL_OBJ_ASSERT(keylAPtr) {\
|
|||
|
TclX_Assert (keylAPtr->typePtr == &keyedListType); \
|
|||
|
ValidateKeyedList (keylAIntPtr); \
|
|||
|
}
|
|||
|
# define KEYL_REP_ASSERT(keylAIntPtr) \
|
|||
|
ValidateKeyedList (keylAIntPtr)
|
|||
|
#else
|
|||
|
# define KEYL_REP_ASSERT(keylAIntPtr)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Prototypes of internal functions.
|
|||
|
*/
|
|||
|
#ifdef TCLX_DEBUG
|
|||
|
static void
|
|||
|
ValidateKeyedList(keylIntObj_t *keylIntPtr);
|
|||
|
#endif
|
|||
|
|
|||
|
static int
|
|||
|
ValidateKey(Tcl_Interp *interp,
|
|||
|
const char *key,
|
|||
|
size_t keyLen,
|
|||
|
int isPath);
|
|||
|
|
|||
|
static keylIntObj_t *
|
|||
|
AllocKeyedListIntRep(void);
|
|||
|
|
|||
|
static void
|
|||
|
FreeKeyedListData(keylIntObj_t *keylIntPtr);
|
|||
|
|
|||
|
static void
|
|||
|
EnsureKeyedListSpace(keylIntObj_t *keylIntPtr,
|
|||
|
int newNumEntries);
|
|||
|
|
|||
|
static void
|
|||
|
DeleteKeyedListEntry(keylIntObj_t *keylIntPtr,
|
|||
|
int entryIdx);
|
|||
|
|
|||
|
static int
|
|||
|
FindKeyedListEntry(keylIntObj_t *keylIntPtr,
|
|||
|
const char *key,
|
|||
|
size_t *keyLenPtr,
|
|||
|
const char **nextSubKeyPtr);
|
|||
|
|
|||
|
static int
|
|||
|
ObjToKeyedListEntry(Tcl_Interp *interp,
|
|||
|
Tcl_Obj *objPtr,
|
|||
|
keylEntry_t *entryPtr);
|
|||
|
|
|||
|
static void
|
|||
|
DupKeyedListInternalRep(Tcl_Obj *srcPtr,
|
|||
|
Tcl_Obj *copyPtr);
|
|||
|
|
|||
|
static void
|
|||
|
FreeKeyedListInternalRep(Tcl_Obj *keylPtr);
|
|||
|
|
|||
|
static int
|
|||
|
SetKeyedListFromAny(Tcl_Interp *interp,
|
|||
|
Tcl_Obj *objPtr);
|
|||
|
|
|||
|
static void
|
|||
|
UpdateStringOfKeyedList(Tcl_Obj *keylPtr);
|
|||
|
|
|||
|
static int
|
|||
|
Tcl_KeylgetObjCmd(void *clientData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]);
|
|||
|
|
|||
|
static int
|
|||
|
Tcl_KeylsetObjCmd(void *clientData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]);
|
|||
|
|
|||
|
static int
|
|||
|
Tcl_KeyldelObjCmd(void *clientData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]);
|
|||
|
|
|||
|
static int
|
|||
|
Tcl_KeylkeysObjCmd(void *clientData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]);
|
|||
|
|
|||
|
/*
|
|||
|
* Type definition.
|
|||
|
*/
|
|||
|
Tcl_ObjType keyedListType = {
|
|||
|
"keyedList", /* name */
|
|||
|
FreeKeyedListInternalRep, /* freeIntRepProc */
|
|||
|
DupKeyedListInternalRep, /* dupIntRepProc */
|
|||
|
UpdateStringOfKeyedList, /* updateStringProc */
|
|||
|
SetKeyedListFromAny /* setFromAnyProc */
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* ValidateKeyedList --
|
|||
|
* Validate a keyed list (only when TCLX_DEBUG is enabled).
|
|||
|
* Parameters:
|
|||
|
* o keylIntPtr - Keyed list internal representation.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
#ifdef TCLX_DEBUG
|
|||
|
static void
|
|||
|
ValidateKeyedList (keylIntPtr)
|
|||
|
keylIntObj_t *keylIntPtr;
|
|||
|
{
|
|||
|
int idx;
|
|||
|
|
|||
|
TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
|
|||
|
TclX_Assert (keylIntPtr->arraySize >= 0);
|
|||
|
TclX_Assert (keylIntPtr->numEntries >= 0);
|
|||
|
TclX_Assert ((keylIntPtr->arraySize > 0) ?
|
|||
|
(keylIntPtr->entries != NULL) : 1);
|
|||
|
TclX_Assert ((keylIntPtr->numEntries > 0) ?
|
|||
|
(keylIntPtr->entries != NULL) : 1);
|
|||
|
|
|||
|
for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
|
|||
|
keylEntry_t *entryPtr = &(keylIntPtr->entries [idx]);
|
|||
|
TclX_Assert (entryPtr->key != NULL);
|
|||
|
TclX_Assert (entryPtr->valuePtr->refCount >= 1);
|
|||
|
if (entryPtr->valuePtr->typePtr == &keyedListType) {
|
|||
|
ValidateKeyedList (entryPtr->valuePtr->internalRep.twoPtrValue.ptr1);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* ValidateKey --
|
|||
|
* Check that a key or keypath string is a valid value.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Used to return error messages.
|
|||
|
* o key - Key string to check.
|
|||
|
* o keyLen - Length of the string, used to check for binary data.
|
|||
|
* o isPath - 1 if this is a key path, 0 if its a simple key and
|
|||
|
* thus "." is illegal.
|
|||
|
* Returns:
|
|||
|
* TCL_OK or TCL_ERROR.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
ValidateKey(
|
|||
|
Tcl_Interp *interp,
|
|||
|
const char *key,
|
|||
|
size_t keyLen,
|
|||
|
int isPath
|
|||
|
) {
|
|||
|
const char *keyp;
|
|||
|
|
|||
|
if (strlen(key) != keyLen) {
|
|||
|
Tcl_ResetResult(interp);
|
|||
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
|
|||
|
"keyed list key may not be a ",
|
|||
|
"binary string", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (key[0] == '\0') {
|
|||
|
Tcl_ResetResult(interp);
|
|||
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
|
|||
|
"keyed list key may not be an ",
|
|||
|
"empty string", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
for (keyp = key; *keyp != '\0'; keyp++) {
|
|||
|
if ((!isPath) && (*keyp == '.')) {
|
|||
|
Tcl_ResetResult(interp);
|
|||
|
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
|
|||
|
"keyed list key may not contain a \".\"; ",
|
|||
|
"it is used as a separator in key paths",
|
|||
|
(char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* AllocKeyedListIntRep --
|
|||
|
* Allocate an and initialize the keyed list internal representation.
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
* A pointer to the keyed list internal structure.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static keylIntObj_t *
|
|||
|
AllocKeyedListIntRep(void)
|
|||
|
{
|
|||
|
keylIntObj_t *keylIntPtr;
|
|||
|
|
|||
|
keylIntPtr = (keylIntObj_t *) ckalloc (sizeof (keylIntObj_t));
|
|||
|
|
|||
|
keylIntPtr->arraySize = 0;
|
|||
|
keylIntPtr->numEntries = 0;
|
|||
|
keylIntPtr->entries = NULL;
|
|||
|
|
|||
|
return keylIntPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* FreeKeyedListData --
|
|||
|
* Free the internal representation of a keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o keylIntPtr - Keyed list internal structure to free.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
FreeKeyedListData(
|
|||
|
keylIntObj_t *keylIntPtr
|
|||
|
) {
|
|||
|
int idx;
|
|||
|
|
|||
|
for (idx = 0; idx < keylIntPtr->numEntries ; idx++) {
|
|||
|
ckfree (keylIntPtr->entries [idx].key);
|
|||
|
Tcl_DecrRefCount (keylIntPtr->entries [idx].valuePtr);
|
|||
|
}
|
|||
|
if (keylIntPtr->entries != NULL)
|
|||
|
ckfree ((char *) keylIntPtr->entries);
|
|||
|
ckfree ((char *) keylIntPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* EnsureKeyedListSpace --
|
|||
|
* Ensure there is enough room in a keyed list array for a certain number
|
|||
|
* of entries, expanding if necessary.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o keylIntPtr - Keyed list internal representation.
|
|||
|
* o newNumEntries - The number of entries that are going to be added to
|
|||
|
* the keyed list.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
EnsureKeyedListSpace(
|
|||
|
keylIntObj_t *keylIntPtr,
|
|||
|
int newNumEntries
|
|||
|
) {
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
|
|||
|
if ((keylIntPtr->arraySize - keylIntPtr->numEntries) < newNumEntries) {
|
|||
|
int newSize = keylIntPtr->arraySize + newNumEntries +
|
|||
|
KEYEDLIST_ARRAY_INCR_SIZE;
|
|||
|
if (keylIntPtr->entries == NULL) {
|
|||
|
keylIntPtr->entries = (keylEntry_t *)
|
|||
|
ckalloc (newSize * sizeof (keylEntry_t));
|
|||
|
} else {
|
|||
|
keylIntPtr->entries = (keylEntry_t *)
|
|||
|
ckrealloc ((void *) keylIntPtr->entries,
|
|||
|
newSize * sizeof (keylEntry_t));
|
|||
|
}
|
|||
|
keylIntPtr->arraySize = newSize;
|
|||
|
}
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* DeleteKeyedListEntry --
|
|||
|
* Delete an entry from a keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o keylIntPtr - Keyed list internal representation.
|
|||
|
* o entryIdx - Index of entry to delete.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
DeleteKeyedListEntry (
|
|||
|
keylIntObj_t *keylIntPtr,
|
|||
|
int entryIdx
|
|||
|
) {
|
|||
|
int idx;
|
|||
|
|
|||
|
ckfree (keylIntPtr->entries [entryIdx].key);
|
|||
|
Tcl_DecrRefCount (keylIntPtr->entries [entryIdx].valuePtr);
|
|||
|
|
|||
|
for (idx = entryIdx; idx < keylIntPtr->numEntries - 1; idx++)
|
|||
|
keylIntPtr->entries [idx] = keylIntPtr->entries [idx + 1];
|
|||
|
keylIntPtr->numEntries--;
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* FindKeyedListEntry --
|
|||
|
* Find an entry in keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o keylIntPtr - Keyed list internal representation.
|
|||
|
* o key - Name of key to search for.
|
|||
|
* o keyLenPtr - In not NULL, the length of the key for this
|
|||
|
* level is returned here. This excludes subkeys and the `.' delimiters.
|
|||
|
* o nextSubKeyPtr - If not NULL, the start of the name of the next
|
|||
|
* sub-key within key is returned.
|
|||
|
* Returns:
|
|||
|
* Index of the entry or -1 if not found.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
FindKeyedListEntry(
|
|||
|
keylIntObj_t *keylIntPtr,
|
|||
|
const char *key,
|
|||
|
size_t *keyLenPtr,
|
|||
|
const char **nextSubKeyPtr
|
|||
|
) {
|
|||
|
const char *keySeparPtr;
|
|||
|
size_t keyLen;
|
|||
|
int findIdx;
|
|||
|
|
|||
|
keySeparPtr = strchr(key, '.');
|
|||
|
if (keySeparPtr != NULL) {
|
|||
|
keyLen = keySeparPtr - key;
|
|||
|
} else {
|
|||
|
keyLen = strlen (key);
|
|||
|
}
|
|||
|
|
|||
|
for (findIdx = 0; findIdx < keylIntPtr->numEntries; findIdx++) {
|
|||
|
if ((strncmp (keylIntPtr->entries [findIdx].key, key, keyLen) == 0) &&
|
|||
|
(keylIntPtr->entries [findIdx].key [keyLen] == '\0'))
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (nextSubKeyPtr != NULL) {
|
|||
|
if (keySeparPtr == NULL) {
|
|||
|
*nextSubKeyPtr = NULL;
|
|||
|
} else {
|
|||
|
*nextSubKeyPtr = keySeparPtr + 1;
|
|||
|
}
|
|||
|
}
|
|||
|
if (keyLenPtr != NULL) {
|
|||
|
*keyLenPtr = keyLen;
|
|||
|
}
|
|||
|
|
|||
|
if (findIdx >= keylIntPtr->numEntries) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
return findIdx;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* ObjToKeyedListEntry --
|
|||
|
* Convert an object to a keyed list entry. (Keyword/value pair).
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Used to return error messages, if not NULL.
|
|||
|
* o objPtr - Object to convert. Each entry must be a two element list,
|
|||
|
* with the first element being the key and the second being the
|
|||
|
* value.
|
|||
|
* o entryPtr - The keyed list entry to initialize from the object.
|
|||
|
* Returns:
|
|||
|
* TCL_OK or TCL_ERROR.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
ObjToKeyedListEntry(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *objPtr,
|
|||
|
keylEntry_t *entryPtr
|
|||
|
) {
|
|||
|
int objc;
|
|||
|
Tcl_Obj **objv;
|
|||
|
const char *key;
|
|||
|
|
|||
|
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
|
|||
|
Tcl_ResetResult (interp);
|
|||
|
Tcl_AppendStringsToObj(Tcl_GetObjResult (interp),
|
|||
|
"keyed list entry not a valid list, ",
|
|||
|
"found \"",
|
|||
|
Tcl_GetString(objPtr),
|
|||
|
"\"", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
if (objc != 2) {
|
|||
|
Tcl_AppendStringsToObj(Tcl_GetObjResult (interp),
|
|||
|
"keyed list entry must be a two ",
|
|||
|
"element list, found \"",
|
|||
|
Tcl_GetString(objPtr),
|
|||
|
"\"", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
key = Tcl_GetString(objv[0]);
|
|||
|
if (ValidateKey(interp, key, objv[0]->length, 0) == TCL_ERROR) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
entryPtr->key = ckstrdup(key);
|
|||
|
entryPtr->valuePtr = Tcl_DuplicateObj(objv [1]);
|
|||
|
Tcl_IncrRefCount(entryPtr->valuePtr);
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* FreeKeyedListInternalRep --
|
|||
|
* Free the internal representation of a keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o keylPtr - Keyed list object being deleted.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
FreeKeyedListInternalRep(
|
|||
|
Tcl_Obj *keylPtr
|
|||
|
) {
|
|||
|
FreeKeyedListData((keylIntObj_t *)keylPtr->internalRep.twoPtrValue.ptr1);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* DupKeyedListInternalRep --
|
|||
|
* Duplicate the internal representation of a keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o srcPtr - Keyed list object to copy.
|
|||
|
* o copyPtr - Target object to copy internal representation to.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
DupKeyedListInternalRep(
|
|||
|
Tcl_Obj *srcPtr,
|
|||
|
Tcl_Obj *copyPtr
|
|||
|
) {
|
|||
|
keylIntObj_t *srcIntPtr = (keylIntObj_t *)
|
|||
|
srcPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
keylIntObj_t *copyIntPtr;
|
|||
|
int idx;
|
|||
|
|
|||
|
KEYL_REP_ASSERT (srcIntPtr);
|
|||
|
|
|||
|
copyIntPtr = (keylIntObj_t *) ckalloc (sizeof (keylIntObj_t));
|
|||
|
copyIntPtr->arraySize = srcIntPtr->arraySize;
|
|||
|
copyIntPtr->numEntries = srcIntPtr->numEntries;
|
|||
|
copyIntPtr->entries = (keylEntry_t *)
|
|||
|
ckalloc (copyIntPtr->arraySize * sizeof (keylEntry_t));
|
|||
|
|
|||
|
for (idx = 0; idx < srcIntPtr->numEntries ; idx++) {
|
|||
|
copyIntPtr->entries [idx].key =
|
|||
|
ckstrdup (srcIntPtr->entries [idx].key);
|
|||
|
copyIntPtr->entries [idx].valuePtr = srcIntPtr->entries [idx].valuePtr;
|
|||
|
Tcl_IncrRefCount (copyIntPtr->entries [idx].valuePtr);
|
|||
|
}
|
|||
|
|
|||
|
copyPtr->internalRep.twoPtrValue.ptr1 = copyIntPtr;
|
|||
|
copyPtr->typePtr = &keyedListType;
|
|||
|
|
|||
|
KEYL_REP_ASSERT (copyIntPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* DupKeyedListInternalRepShared --
|
|||
|
* Same as DupKeyedListInternalRepbut does not reference objects
|
|||
|
* from the srcPtr list. It duplicates them and stores the copy
|
|||
|
* in the list-copy object.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o srcPtr - Keyed list object to copy.
|
|||
|
* o copyPtr - Target object to copy internal representation to.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
void
|
|||
|
DupKeyedListInternalRepShared (
|
|||
|
Tcl_Obj *srcPtr,
|
|||
|
Tcl_Obj *copyPtr
|
|||
|
) {
|
|||
|
keylIntObj_t *srcIntPtr = (keylIntObj_t *)
|
|||
|
srcPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
keylIntObj_t *copyIntPtr;
|
|||
|
int idx;
|
|||
|
|
|||
|
KEYL_REP_ASSERT (srcIntPtr);
|
|||
|
|
|||
|
copyIntPtr = (keylIntObj_t *) ckalloc (sizeof (keylIntObj_t));
|
|||
|
copyIntPtr->arraySize = srcIntPtr->arraySize;
|
|||
|
copyIntPtr->numEntries = srcIntPtr->numEntries;
|
|||
|
copyIntPtr->entries = (keylEntry_t *)
|
|||
|
ckalloc (copyIntPtr->arraySize * sizeof (keylEntry_t));
|
|||
|
|
|||
|
for (idx = 0; idx < srcIntPtr->numEntries ; idx++) {
|
|||
|
copyIntPtr->entries [idx].key =
|
|||
|
ckstrdup (srcIntPtr->entries [idx].key);
|
|||
|
copyIntPtr->entries [idx].valuePtr =
|
|||
|
Sv_DuplicateObj (srcIntPtr->entries [idx].valuePtr);
|
|||
|
Tcl_IncrRefCount(copyIntPtr->entries [idx].valuePtr);
|
|||
|
}
|
|||
|
|
|||
|
copyPtr->internalRep.twoPtrValue.ptr1 = copyIntPtr;
|
|||
|
copyPtr->typePtr = &keyedListType;
|
|||
|
|
|||
|
KEYL_REP_ASSERT (copyIntPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* SetKeyedListFromAny --
|
|||
|
* Convert an object to a keyed list from its string representation. Only
|
|||
|
* the first level is converted, as there is no way of knowing how far down
|
|||
|
* the keyed list recurses until lower levels are accessed.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o objPtr - Object to convert to a keyed list.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
SetKeyedListFromAny(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *objPtr
|
|||
|
) {
|
|||
|
keylIntObj_t *keylIntPtr;
|
|||
|
int idx, objc;
|
|||
|
Tcl_Obj **objv;
|
|||
|
|
|||
|
if (Tcl_ListObjGetElements (interp, objPtr, &objc, &objv) != TCL_OK)
|
|||
|
return TCL_ERROR;
|
|||
|
|
|||
|
keylIntPtr = AllocKeyedListIntRep ();
|
|||
|
|
|||
|
EnsureKeyedListSpace (keylIntPtr, objc);
|
|||
|
|
|||
|
for (idx = 0; idx < objc; idx++) {
|
|||
|
if (ObjToKeyedListEntry (interp, objv [idx],
|
|||
|
&(keylIntPtr->entries [keylIntPtr->numEntries])) != TCL_OK)
|
|||
|
goto errorExit;
|
|||
|
keylIntPtr->numEntries++;
|
|||
|
}
|
|||
|
|
|||
|
if ((objPtr->typePtr != NULL) &&
|
|||
|
(objPtr->typePtr->freeIntRepProc != NULL)) {
|
|||
|
(*objPtr->typePtr->freeIntRepProc) (objPtr);
|
|||
|
}
|
|||
|
objPtr->internalRep.twoPtrValue.ptr1 = keylIntPtr;
|
|||
|
objPtr->typePtr = &keyedListType;
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return TCL_OK;
|
|||
|
|
|||
|
errorExit:
|
|||
|
FreeKeyedListData (keylIntPtr);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* UpdateStringOfKeyedList --
|
|||
|
* Update the string representation of a keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o objPtr - Object to convert to a keyed list.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static void
|
|||
|
UpdateStringOfKeyedList(
|
|||
|
Tcl_Obj *keylPtr
|
|||
|
) {
|
|||
|
#define UPDATE_STATIC_SIZE 32
|
|||
|
int idx;
|
|||
|
Tcl_Obj **listObjv, *entryObjv [2], *tmpListObj;
|
|||
|
Tcl_Obj *staticListObjv [UPDATE_STATIC_SIZE];
|
|||
|
char *listStr;
|
|||
|
keylIntObj_t *keylIntPtr = (keylIntObj_t *)
|
|||
|
keylPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
|
|||
|
/*
|
|||
|
* Conversion to strings is done via list objects to support binary data.
|
|||
|
*/
|
|||
|
if (keylIntPtr->numEntries > UPDATE_STATIC_SIZE) {
|
|||
|
listObjv =
|
|||
|
(Tcl_Obj **) ckalloc (keylIntPtr->numEntries * sizeof (Tcl_Obj *));
|
|||
|
} else {
|
|||
|
listObjv = staticListObjv;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Convert each keyed list entry to a two element list object. No
|
|||
|
* need to incr/decr ref counts, the list objects will take care of that.
|
|||
|
* FIX: Keeping key as string object will speed this up.
|
|||
|
*/
|
|||
|
for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
|
|||
|
entryObjv [0] =
|
|||
|
Tcl_NewStringObj(keylIntPtr->entries [idx].key,
|
|||
|
strlen (keylIntPtr->entries [idx].key));
|
|||
|
entryObjv [1] = keylIntPtr->entries [idx].valuePtr;
|
|||
|
listObjv [idx] = Tcl_NewListObj (2, entryObjv);
|
|||
|
}
|
|||
|
|
|||
|
tmpListObj = Tcl_NewListObj (keylIntPtr->numEntries, listObjv);
|
|||
|
listStr = Tcl_GetString(tmpListObj);
|
|||
|
keylPtr->bytes = ckbinstrdup(listStr, tmpListObj->length);
|
|||
|
keylPtr->length = tmpListObj->length;
|
|||
|
|
|||
|
Tcl_DecrRefCount (tmpListObj);
|
|||
|
if (listObjv != staticListObjv)
|
|||
|
ckfree ((void*) listObjv);
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_NewKeyedListObj --
|
|||
|
* Create and initialize a new keyed list object.
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
* A pointer to the object.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
Tcl_Obj *
|
|||
|
TclX_NewKeyedListObj(void)
|
|||
|
{
|
|||
|
Tcl_Obj *keylPtr = Tcl_NewObj ();
|
|||
|
keylIntObj_t *keylIntPtr = AllocKeyedListIntRep ();
|
|||
|
|
|||
|
keylPtr->internalRep.twoPtrValue.ptr1 = keylIntPtr;
|
|||
|
keylPtr->typePtr = &keyedListType;
|
|||
|
return keylPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_KeyedListGet --
|
|||
|
* Retrieve a key value from a keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Error message will be return in result if there is an error.
|
|||
|
* o keylPtr - Keyed list object to get key from.
|
|||
|
* o key - The name of the key to extract. Will recusively process sub-keys
|
|||
|
* seperated by `.'.
|
|||
|
* o valueObjPtrPtr - If the key is found, a pointer to the key object
|
|||
|
* is returned here. NULL is returned if the key is not present.
|
|||
|
* Returns:
|
|||
|
* o TCL_OK - If the key value was returned.
|
|||
|
* o TCL_BREAK - If the key was not found.
|
|||
|
* o TCL_ERROR - If an error occured.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
int
|
|||
|
TclX_KeyedListGet(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *keylPtr,
|
|||
|
const char *key,
|
|||
|
Tcl_Obj **valuePtrPtr
|
|||
|
) {
|
|||
|
keylIntObj_t *keylIntPtr;
|
|||
|
const char *nextSubKey;
|
|||
|
int findIdx;
|
|||
|
|
|||
|
if (keylPtr->typePtr != &keyedListType) {
|
|||
|
if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
keylIntPtr = (keylIntObj_t *)keylPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
|
|||
|
findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);
|
|||
|
|
|||
|
/*
|
|||
|
* If not found, return status.
|
|||
|
*/
|
|||
|
if (findIdx < 0) {
|
|||
|
*valuePtrPtr = NULL;
|
|||
|
return TCL_BREAK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If we are at the last subkey, return the entry, otherwise recurse
|
|||
|
* down looking for the entry.
|
|||
|
*/
|
|||
|
if (nextSubKey == NULL) {
|
|||
|
*valuePtrPtr = keylIntPtr->entries [findIdx].valuePtr;
|
|||
|
return TCL_OK;
|
|||
|
} else {
|
|||
|
return TclX_KeyedListGet (interp,
|
|||
|
keylIntPtr->entries [findIdx].valuePtr,
|
|||
|
nextSubKey,
|
|||
|
valuePtrPtr);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_KeyedListSet --
|
|||
|
* Set a key value in keyed list object.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Error message will be return in result object.
|
|||
|
* o keylPtr - Keyed list object to update.
|
|||
|
* o key - The name of the key to extract. Will recusively process
|
|||
|
* sub-key seperated by `.'.
|
|||
|
* o valueObjPtr - The value to set for the key.
|
|||
|
* Returns:
|
|||
|
* TCL_OK or TCL_ERROR.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
int
|
|||
|
TclX_KeyedListSet(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *keylPtr,
|
|||
|
const char *key,
|
|||
|
Tcl_Obj *valuePtr
|
|||
|
) {
|
|||
|
keylIntObj_t *keylIntPtr;
|
|||
|
const char *nextSubKey;
|
|||
|
int findIdx, status;
|
|||
|
size_t keyLen;
|
|||
|
Tcl_Obj *newKeylPtr;
|
|||
|
|
|||
|
if (keylPtr->typePtr != &keyedListType) {
|
|||
|
if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
keylIntPtr = (keylIntObj_t *)keylPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
|
|||
|
findIdx = FindKeyedListEntry (keylIntPtr, key,
|
|||
|
&keyLen, &nextSubKey);
|
|||
|
|
|||
|
/*
|
|||
|
* If we are at the last subkey, either update or add an entry.
|
|||
|
*/
|
|||
|
if (nextSubKey == NULL) {
|
|||
|
if (findIdx < 0) {
|
|||
|
EnsureKeyedListSpace (keylIntPtr, 1);
|
|||
|
findIdx = keylIntPtr->numEntries;
|
|||
|
keylIntPtr->numEntries++;
|
|||
|
} else {
|
|||
|
ckfree (keylIntPtr->entries [findIdx].key);
|
|||
|
Tcl_DecrRefCount (keylIntPtr->entries [findIdx].valuePtr);
|
|||
|
}
|
|||
|
keylIntPtr->entries [findIdx].key =
|
|||
|
(char *) ckalloc (keyLen + 1);
|
|||
|
strncpy (keylIntPtr->entries [findIdx].key, key, keyLen);
|
|||
|
keylIntPtr->entries [findIdx].key [keyLen] = '\0';
|
|||
|
keylIntPtr->entries [findIdx].valuePtr = valuePtr;
|
|||
|
Tcl_IncrRefCount (valuePtr);
|
|||
|
Tcl_InvalidateStringRep (keylPtr);
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If we are not at the last subkey, recurse down, creating new
|
|||
|
* entries if neccessary. If this level key was not found, it
|
|||
|
* means we must build new subtree. Don't insert the new tree until we
|
|||
|
* come back without error.
|
|||
|
*/
|
|||
|
if (findIdx >= 0) {
|
|||
|
DupSharedKeyListChild (keylIntPtr, findIdx);
|
|||
|
status =
|
|||
|
TclX_KeyedListSet (interp,
|
|||
|
keylIntPtr->entries [findIdx].valuePtr,
|
|||
|
nextSubKey, valuePtr);
|
|||
|
if (status == TCL_OK) {
|
|||
|
Tcl_InvalidateStringRep (keylPtr);
|
|||
|
}
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return status;
|
|||
|
} else {
|
|||
|
newKeylPtr = TclX_NewKeyedListObj ();
|
|||
|
if (TclX_KeyedListSet (interp, newKeylPtr,
|
|||
|
nextSubKey, valuePtr) != TCL_OK) {
|
|||
|
Tcl_DecrRefCount (newKeylPtr);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
EnsureKeyedListSpace (keylIntPtr, 1);
|
|||
|
findIdx = keylIntPtr->numEntries++;
|
|||
|
keylIntPtr->entries [findIdx].key =
|
|||
|
(char *) ckalloc (keyLen + 1);
|
|||
|
strncpy (keylIntPtr->entries [findIdx].key, key, keyLen);
|
|||
|
keylIntPtr->entries [findIdx].key [keyLen] = '\0';
|
|||
|
keylIntPtr->entries [findIdx].valuePtr = newKeylPtr;
|
|||
|
Tcl_IncrRefCount (newKeylPtr);
|
|||
|
Tcl_InvalidateStringRep (keylPtr);
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_KeyedListDelete --
|
|||
|
* Delete a key value from keyed list.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Error message will be return in result if there is an error.
|
|||
|
* o keylPtr - Keyed list object to update.
|
|||
|
* o key - The name of the key to extract. Will recusively process
|
|||
|
* sub-key seperated by `.'.
|
|||
|
* Returns:
|
|||
|
* o TCL_OK - If the key was deleted.
|
|||
|
* o TCL_BREAK - If the key was not found.
|
|||
|
* o TCL_ERROR - If an error occured.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
int
|
|||
|
TclX_KeyedListDelete(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *keylPtr,
|
|||
|
const char *key
|
|||
|
) {
|
|||
|
keylIntObj_t *keylIntPtr, *subKeylIntPtr;
|
|||
|
const char *nextSubKey;
|
|||
|
int findIdx, status;
|
|||
|
|
|||
|
if (keylPtr->typePtr != &keyedListType) {
|
|||
|
if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
keylIntPtr = (keylIntObj_t *)keylPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
|
|||
|
findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);
|
|||
|
|
|||
|
/*
|
|||
|
* If not found, return status.
|
|||
|
*/
|
|||
|
if (findIdx < 0) {
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return TCL_BREAK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If we are at the last subkey, delete the entry.
|
|||
|
*/
|
|||
|
if (nextSubKey == NULL) {
|
|||
|
DeleteKeyedListEntry (keylIntPtr, findIdx);
|
|||
|
Tcl_InvalidateStringRep (keylPtr);
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If we are not at the last subkey, recurse down. If the entry is
|
|||
|
* deleted and the sub-keyed list is empty, delete it as well. Must
|
|||
|
* invalidate string, as it caches all representations below it.
|
|||
|
*/
|
|||
|
DupSharedKeyListChild (keylIntPtr, findIdx);
|
|||
|
|
|||
|
status = TclX_KeyedListDelete (interp,
|
|||
|
keylIntPtr->entries [findIdx].valuePtr,
|
|||
|
nextSubKey);
|
|||
|
if (status == TCL_OK) {
|
|||
|
subKeylIntPtr = (keylIntObj_t *)
|
|||
|
keylIntPtr->entries [findIdx].valuePtr->internalRep.twoPtrValue.ptr1;
|
|||
|
if (subKeylIntPtr->numEntries == 0) {
|
|||
|
DeleteKeyedListEntry (keylIntPtr, findIdx);
|
|||
|
}
|
|||
|
Tcl_InvalidateStringRep (keylPtr);
|
|||
|
}
|
|||
|
|
|||
|
KEYL_REP_ASSERT (keylIntPtr);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_KeyedListGetKeys --
|
|||
|
* Retrieve a list of keyed list keys.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Error message will be return in result if there is an error.
|
|||
|
* o keylPtr - Keyed list object to get key from.
|
|||
|
* o key - The name of the key to get the sub keys for. NULL or empty
|
|||
|
* to retrieve all top level keys.
|
|||
|
* o listObjPtrPtr - List object is returned here with key as values.
|
|||
|
* Returns:
|
|||
|
* o TCL_OK - If the zero or more key where returned.
|
|||
|
* o TCL_BREAK - If the key was not found.
|
|||
|
* o TCL_ERROR - If an error occured.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
int
|
|||
|
TclX_KeyedListGetKeys(
|
|||
|
Tcl_Interp *interp,
|
|||
|
Tcl_Obj *keylPtr,
|
|||
|
const char *key,
|
|||
|
Tcl_Obj **listObjPtrPtr
|
|||
|
) {
|
|||
|
keylIntObj_t *keylIntPtr;
|
|||
|
Tcl_Obj *nameObjPtr, *listObjPtr;
|
|||
|
const char *nextSubKey;
|
|||
|
int idx, findIdx;
|
|||
|
|
|||
|
if (keylPtr->typePtr != &keyedListType) {
|
|||
|
if (SetKeyedListFromAny(interp, keylPtr) != TCL_OK) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
keylIntPtr = (keylIntObj_t *)keylPtr->internalRep.twoPtrValue.ptr1;
|
|||
|
|
|||
|
/*
|
|||
|
* If key is not NULL or empty, then recurse down until we go past
|
|||
|
* the end of all of the elements of the key.
|
|||
|
*/
|
|||
|
if ((key != NULL) && (key [0] != '\0')) {
|
|||
|
findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);
|
|||
|
if (findIdx < 0) {
|
|||
|
TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
|
|||
|
return TCL_BREAK;
|
|||
|
}
|
|||
|
TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
|
|||
|
return TclX_KeyedListGetKeys (interp,
|
|||
|
keylIntPtr->entries [findIdx].valuePtr,
|
|||
|
nextSubKey,
|
|||
|
listObjPtrPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Reached the end of the full key, return all keys at this level.
|
|||
|
*/
|
|||
|
listObjPtr = Tcl_NewListObj (0, NULL);
|
|||
|
for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
|
|||
|
nameObjPtr = Tcl_NewStringObj (keylIntPtr->entries [idx].key,
|
|||
|
-1);
|
|||
|
if (Tcl_ListObjAppendElement (interp, listObjPtr,
|
|||
|
nameObjPtr) != TCL_OK) {
|
|||
|
Tcl_DecrRefCount (nameObjPtr);
|
|||
|
Tcl_DecrRefCount (listObjPtr);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
*listObjPtrPtr = listObjPtr;
|
|||
|
TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* Tcl_KeylgetObjCmd --
|
|||
|
* Implements the TCL keylget command:
|
|||
|
* keylget listvar ?key? ?retvar | {}?
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
Tcl_KeylgetObjCmd(
|
|||
|
void *clientData,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]
|
|||
|
) {
|
|||
|
Tcl_Obj *keylPtr, *valuePtr;
|
|||
|
const char *key;
|
|||
|
int status;
|
|||
|
|
|||
|
if ((objc < 2) || (objc > 4)) {
|
|||
|
return TclX_WrongArgs (interp, objv [0],
|
|||
|
"listvar ?key? ?retvar | {}?");
|
|||
|
}
|
|||
|
/*
|
|||
|
* Handle request for list of keys, use keylkeys command.
|
|||
|
*/
|
|||
|
if (objc == 2)
|
|||
|
return Tcl_KeylkeysObjCmd (clientData, interp, objc, objv);
|
|||
|
|
|||
|
keylPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
|
|||
|
if (keylPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Handle retrieving a value for a specified key.
|
|||
|
*/
|
|||
|
key = Tcl_GetString(objv[2]);
|
|||
|
if (ValidateKey(interp, key, objv[2]->length, 1) == TCL_ERROR) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
status = TclX_KeyedListGet (interp, keylPtr, key, &valuePtr);
|
|||
|
if (status == TCL_ERROR)
|
|||
|
return TCL_ERROR;
|
|||
|
|
|||
|
/*
|
|||
|
* Handle key not found.
|
|||
|
*/
|
|||
|
if (status == TCL_BREAK) {
|
|||
|
if (objc == 3) {
|
|||
|
TclX_AppendObjResult (interp, "key \"", key,
|
|||
|
"\" not found in keyed list",
|
|||
|
(char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
} else {
|
|||
|
Tcl_ResetResult(interp);
|
|||
|
Tcl_SetIntObj(Tcl_GetObjResult (interp), 0);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* No variable specified, so return value in the result.
|
|||
|
*/
|
|||
|
if (objc == 3) {
|
|||
|
Tcl_SetObjResult (interp, valuePtr);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Variable (or empty variable name) specified.
|
|||
|
*/
|
|||
|
if (!TclX_IsNullObj(objv [3])) {
|
|||
|
if (Tcl_ObjSetVar2(interp, objv[3], NULL,
|
|||
|
valuePtr, TCL_LEAVE_ERR_MSG) == NULL)
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
Tcl_ResetResult(interp);
|
|||
|
Tcl_SetIntObj(Tcl_GetObjResult (interp), 1);
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* Tcl_KeylsetObjCmd --
|
|||
|
* Implements the TCL keylset command:
|
|||
|
* keylset listvar key value ?key value...?
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
Tcl_KeylsetObjCmd(
|
|||
|
void *dummy,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]
|
|||
|
) {
|
|||
|
Tcl_Obj *keylVarPtr, *newVarObj;
|
|||
|
const char *key;
|
|||
|
int idx;
|
|||
|
(void)dummy;
|
|||
|
|
|||
|
if ((objc < 4) || ((objc % 2) != 0)) {
|
|||
|
return TclX_WrongArgs (interp, objv [0],
|
|||
|
"listvar key value ?key value...?");
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Get the variable that we are going to update. If the var doesn't exist,
|
|||
|
* create it. If it is shared by more than being a variable, duplicated
|
|||
|
* it.
|
|||
|
*/
|
|||
|
keylVarPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
|
|||
|
if ((keylVarPtr == NULL) || (Tcl_IsShared (keylVarPtr))) {
|
|||
|
if (keylVarPtr == NULL) {
|
|||
|
keylVarPtr = TclX_NewKeyedListObj ();
|
|||
|
} else {
|
|||
|
keylVarPtr = Tcl_DuplicateObj (keylVarPtr);
|
|||
|
}
|
|||
|
newVarObj = keylVarPtr;
|
|||
|
} else {
|
|||
|
newVarObj = NULL;
|
|||
|
}
|
|||
|
|
|||
|
for (idx = 2; idx < objc; idx += 2) {
|
|||
|
key = Tcl_GetString(objv[idx]);
|
|||
|
if (ValidateKey(interp, key, objv[idx]->length, 1) == TCL_ERROR) {
|
|||
|
goto errorExit;
|
|||
|
}
|
|||
|
if (TclX_KeyedListSet (interp, keylVarPtr, key, objv [idx+1]) != TCL_OK) {
|
|||
|
goto errorExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (Tcl_ObjSetVar2(interp, objv[1], NULL, keylVarPtr,
|
|||
|
TCL_LEAVE_ERR_MSG) == NULL) {
|
|||
|
goto errorExit;
|
|||
|
}
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
|
|||
|
errorExit:
|
|||
|
if (newVarObj != NULL) {
|
|||
|
Tcl_DecrRefCount (newVarObj);
|
|||
|
}
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* Tcl_KeyldelObjCmd --
|
|||
|
* Implements the TCL keyldel command:
|
|||
|
* keyldel listvar key ?key ...?
|
|||
|
*----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
Tcl_KeyldelObjCmd(
|
|||
|
void *dummy,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]
|
|||
|
) {
|
|||
|
Tcl_Obj *keylVarPtr, *keylPtr;
|
|||
|
const char *key;
|
|||
|
int idx, status;
|
|||
|
(void)dummy;
|
|||
|
|
|||
|
if (objc < 3) {
|
|||
|
return TclX_WrongArgs (interp, objv [0], "listvar key ?key ...?");
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Get the variable that we are going to update. If it is shared by more
|
|||
|
* than being a variable, duplicated it.
|
|||
|
*/
|
|||
|
keylVarPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
|
|||
|
if (keylVarPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (Tcl_IsShared (keylVarPtr)) {
|
|||
|
keylPtr = Tcl_DuplicateObj (keylVarPtr);
|
|||
|
keylVarPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, keylPtr, TCL_LEAVE_ERR_MSG);
|
|||
|
if (keylVarPtr == NULL) {
|
|||
|
Tcl_DecrRefCount (keylPtr);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (keylVarPtr != keylPtr) {
|
|||
|
Tcl_DecrRefCount (keylPtr);
|
|||
|
}
|
|||
|
}
|
|||
|
keylPtr = keylVarPtr;
|
|||
|
|
|||
|
for (idx = 2; idx < objc; idx++) {
|
|||
|
key = Tcl_GetString(objv[idx]);
|
|||
|
if (ValidateKey(interp, key, objv[idx]->length, 1) == TCL_ERROR) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
status = TclX_KeyedListDelete (interp, keylPtr, key);
|
|||
|
switch (status) {
|
|||
|
case TCL_BREAK:
|
|||
|
TclX_AppendObjResult (interp, "key not found: \"",
|
|||
|
key, "\"", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
case TCL_ERROR:
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* Tcl_KeylkeysObjCmd --
|
|||
|
* Implements the TCL keylkeys command:
|
|||
|
* keylkeys listvar ?key?
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
static int
|
|||
|
Tcl_KeylkeysObjCmd(
|
|||
|
void *dummy,
|
|||
|
Tcl_Interp *interp,
|
|||
|
int objc,
|
|||
|
Tcl_Obj *const objv[]
|
|||
|
) {
|
|||
|
Tcl_Obj *keylPtr, *listObjPtr;
|
|||
|
const char *key;
|
|||
|
int status;
|
|||
|
(void)dummy;
|
|||
|
|
|||
|
if ((objc < 2) || (objc > 3)) {
|
|||
|
return TclX_WrongArgs(interp, objv [0], "listvar ?key?");
|
|||
|
}
|
|||
|
|
|||
|
keylPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
|
|||
|
if (keylPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If key argument is not specified, then objv [2] is NULL or empty,
|
|||
|
* meaning get top level keys.
|
|||
|
*/
|
|||
|
if (objc < 3) {
|
|||
|
key = NULL;
|
|||
|
} else {
|
|||
|
key = Tcl_GetString(objv[2]);
|
|||
|
if (ValidateKey(interp, key, objv[2]->length, 1) == TCL_ERROR) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = TclX_KeyedListGetKeys (interp, keylPtr, key, &listObjPtr);
|
|||
|
switch (status) {
|
|||
|
case TCL_BREAK:
|
|||
|
TclX_AppendObjResult (interp, "key not found: \"", key, "\"",
|
|||
|
(char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
case TCL_ERROR:
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
Tcl_SetObjResult (interp, listObjPtr);
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*-----------------------------------------------------------------------------
|
|||
|
* TclX_KeyedListInit --
|
|||
|
* Initialize the keyed list commands for this interpreter.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* o interp - Interpreter to add commands to.
|
|||
|
*-----------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
void
|
|||
|
TclX_KeyedListInit(
|
|||
|
Tcl_Interp *interp
|
|||
|
) {
|
|||
|
Tcl_Obj *listobj;
|
|||
|
Tcl_RegisterObjType(&keyedListType);
|
|||
|
|
|||
|
listobj = Tcl_NewObj();
|
|||
|
listobj = Tcl_NewListObj(1, &listobj);
|
|||
|
listType = listobj->typePtr;
|
|||
|
Tcl_DecrRefCount(listobj);
|
|||
|
|
|||
|
if (0) {
|
|||
|
Tcl_CreateObjCommand (interp,
|
|||
|
"keylget",
|
|||
|
Tcl_KeylgetObjCmd,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
Tcl_CreateObjCommand (interp,
|
|||
|
"keylset",
|
|||
|
Tcl_KeylsetObjCmd,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
Tcl_CreateObjCommand (interp,
|
|||
|
"keyldel",
|
|||
|
Tcl_KeyldelObjCmd,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
Tcl_CreateObjCommand (interp,
|
|||
|
"keylkeys",
|
|||
|
Tcl_KeylkeysObjCmd,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|