diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-12-21 22:13:18 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-12-21 22:13:18 (GMT) |
commit | 07e464099b99459d0a37757771791598ef3395d9 (patch) | |
tree | 4ba7d8aad13735e52f59bdce7ca5ba3151ebd7e3 /tcl8.6/generic/tclListObj.c | |
parent | deb3650e37f26f651f280e480c4df3d7dde87bae (diff) | |
download | blt-07e464099b99459d0a37757771791598ef3395d9.zip blt-07e464099b99459d0a37757771791598ef3395d9.tar.gz blt-07e464099b99459d0a37757771791598ef3395d9.tar.bz2 |
new subtree for tcl/tk
Diffstat (limited to 'tcl8.6/generic/tclListObj.c')
-rw-r--r-- | tcl8.6/generic/tclListObj.c | 2040 |
1 files changed, 0 insertions, 2040 deletions
diff --git a/tcl8.6/generic/tclListObj.c b/tcl8.6/generic/tclListObj.c deleted file mode 100644 index 14b8a14..0000000 --- a/tcl8.6/generic/tclListObj.c +++ /dev/null @@ -1,2040 +0,0 @@ -/* - * tclListObj.c -- - * - * This file contains functions that implement the Tcl list object type. - * - * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright (c) 1998 by Scriptics Corporation. - * Copyright (c) 2001 by Kevin B. Kenny. All rights reserved. - * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - */ - -#include "tclInt.h" - -/* - * Prototypes for functions defined later in this file: - */ - -static List * AttemptNewList(Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[]); -static List * NewListIntRep(int objc, Tcl_Obj *const objv[], int p); -static void DupListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); -static void FreeListInternalRep(Tcl_Obj *listPtr); -static int SetListFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); -static void UpdateStringOfList(Tcl_Obj *listPtr); - -/* - * The structure below defines the list Tcl object type by means of functions - * that can be invoked by generic object code. - * - * The internal representation of a list object is a two-pointer - * representation. The first pointer designates a List structure that contains - * an array of pointers to the element objects, together with integers that - * represent the current element count and the allocated size of the array. - * The second pointer is normally NULL; during execution of functions in this - * file that operate on nested sublists, it is occasionally used as working - * storage to avoid an auxiliary stack. - */ - -const Tcl_ObjType tclListType = { - "list", /* name */ - FreeListInternalRep, /* freeIntRepProc */ - DupListInternalRep, /* dupIntRepProc */ - UpdateStringOfList, /* updateStringProc */ - SetListFromAny /* setFromAnyProc */ -}; - -#ifndef TCL_MIN_ELEMENT_GROWTH -#define TCL_MIN_ELEMENT_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Obj *) -#endif - -/* - *---------------------------------------------------------------------- - * - * NewListIntRep -- - * - * Creates a list internal rep with space for objc elements. objc - * must be > 0. If objv!=NULL, initializes with the first objc values - * in that array. If objv==NULL, initalize list internal rep to have - * 0 elements, with space to add objc more. Flag value "p" indicates - * how to behave on failure. - * - * Results: - * A new List struct with refCount 0 is returned. If some failure - * prevents this then if p=0, NULL is returned and otherwise the - * routine panics. - * - * Side effects: - * The ref counts of the elements in objv are incremented since the - * resulting list now refers to them. - * - *---------------------------------------------------------------------- - */ - -static List * -NewListIntRep( - int objc, - Tcl_Obj *const objv[], - int p) -{ - List *listRepPtr; - - if (objc <= 0) { - Tcl_Panic("NewListIntRep: expects postive element count"); - } - - /* - * First check to see if we'd overflow and try to allocate an object - * larger than our memory allocator allows. Note that this is actually a - * fairly small value when you're on a serious 64-bit machine, but that - * requires API changes to fix. See [Bug 219196] for a discussion. - */ - - if ((size_t)objc > LIST_MAX) { - if (p) { - Tcl_Panic("max length of a Tcl list (%d elements) exceeded", - LIST_MAX); - } - return NULL; - } - - listRepPtr = attemptckalloc(LIST_SIZE(objc)); - if (listRepPtr == NULL) { - if (p) { - Tcl_Panic("list creation failed: unable to alloc %u bytes", - LIST_SIZE(objc)); - } - return NULL; - } - - listRepPtr->canonicalFlag = 0; - listRepPtr->refCount = 0; - listRepPtr->maxElemCount = objc; - - if (objv) { - Tcl_Obj **elemPtrs; - int i; - - listRepPtr->elemCount = objc; - elemPtrs = &listRepPtr->elements; - for (i = 0; i < objc; i++) { - elemPtrs[i] = objv[i]; - Tcl_IncrRefCount(elemPtrs[i]); - } - } else { - listRepPtr->elemCount = 0; - } - return listRepPtr; -} - -/* - *---------------------------------------------------------------------- - * - * AttemptNewList -- - * - * Creates a list internal rep with space for objc elements. objc - * must be > 0. If objv!=NULL, initializes with the first objc values - * in that array. If objv==NULL, initalize list internal rep to have - * 0 elements, with space to add objc more. - * - * Results: - * A new List struct with refCount 0 is returned. If some failure - * prevents this then NULL is returned, and an error message is left - * in the interp result, unless interp is NULL. - * - * Side effects: - * The ref counts of the elements in objv are incremented since the - * resulting list now refers to them. - * - *---------------------------------------------------------------------- - */ - -static List * -AttemptNewList( - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - List *listRepPtr = NewListIntRep(objc, objv, 0); - - if (interp != NULL && listRepPtr == NULL) { - if (objc > LIST_MAX) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "max length of a Tcl list (%d elements) exceeded", - LIST_MAX)); - } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "list creation failed: unable to alloc %u bytes", - LIST_SIZE(objc))); - } - Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); - } - return listRepPtr; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_NewListObj -- - * - * This function is normally called when not debugging: i.e., when - * TCL_MEM_DEBUG is not defined. It creates a new list object from an - * (objc,objv) array: that is, each of the objc elements of the array - * referenced by objv is inserted as an element into a new Tcl object. - * - * When TCL_MEM_DEBUG is defined, this function just returns the result - * of calling the debugging version Tcl_DbNewListObj. - * - * Results: - * A new list object is returned that is initialized from the object - * pointers in objv. If objc is less than or equal to zero, an empty - * object is returned. The new object's string representation is left - * NULL. The resulting new list object has ref count 0. - * - * Side effects: - * The ref counts of the elements in objv are incremented since the - * resulting list now refers to them. - * - *---------------------------------------------------------------------- - */ - -#ifdef TCL_MEM_DEBUG -#undef Tcl_NewListObj - -Tcl_Obj * -Tcl_NewListObj( - int objc, /* Count of objects referenced by objv. */ - Tcl_Obj *const objv[]) /* An array of pointers to Tcl objects. */ -{ - return Tcl_DbNewListObj(objc, objv, "unknown", 0); -} - -#else /* if not TCL_MEM_DEBUG */ - -Tcl_Obj * -Tcl_NewListObj( - int objc, /* Count of objects referenced by objv. */ - Tcl_Obj *const objv[]) /* An array of pointers to Tcl objects. */ -{ - List *listRepPtr; - Tcl_Obj *listPtr; - - TclNewObj(listPtr); - - if (objc <= 0) { - return listPtr; - } - - /* - * Create the internal rep. - */ - - listRepPtr = NewListIntRep(objc, objv, 1); - - /* - * Now create the object. - */ - - TclInvalidateStringRep(listPtr); - ListSetIntRep(listPtr, listRepPtr); - return listPtr; -} -#endif /* if TCL_MEM_DEBUG */ - -/* - *---------------------------------------------------------------------- - * - * Tcl_DbNewListObj -- - * - * This function is normally called when debugging: i.e., when - * TCL_MEM_DEBUG is defined. It creates new list objects. It is the same - * as the Tcl_NewListObj function above except that it calls - * Tcl_DbCkalloc directly with the file name and line number from its - * caller. This simplifies debugging since then the [memory active] - * command will report the correct file name and line number when - * reporting objects that haven't been freed. - * - * When TCL_MEM_DEBUG is not defined, this function just returns the - * result of calling Tcl_NewListObj. - * - * Results: - * A new list object is returned that is initialized from the object - * pointers in objv. If objc is less than or equal to zero, an empty - * object is returned. The new object's string representation is left - * NULL. The new list object has ref count 0. - * - * Side effects: - * The ref counts of the elements in objv are incremented since the - * resulting list now refers to them. - * - *---------------------------------------------------------------------- - */ - -#ifdef TCL_MEM_DEBUG - -Tcl_Obj * -Tcl_DbNewListObj( - int objc, /* Count of objects referenced by objv. */ - Tcl_Obj *const objv[], /* An array of pointers to Tcl objects. */ - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ -{ - Tcl_Obj *listPtr; - List *listRepPtr; - - TclDbNewObj(listPtr, file, line); - - if (objc <= 0) { - return listPtr; - } - - /* - * Create the internal rep. - */ - - listRepPtr = NewListIntRep(objc, objv, 1); - - /* - * Now create the object. - */ - - TclInvalidateStringRep(listPtr); - ListSetIntRep(listPtr, listRepPtr); - - return listPtr; -} - -#else /* if not TCL_MEM_DEBUG */ - -Tcl_Obj * -Tcl_DbNewListObj( - int objc, /* Count of objects referenced by objv. */ - Tcl_Obj *const objv[], /* An array of pointers to Tcl objects. */ - const char *file, /* The name of the source file calling this - * function; used for debugging. */ - int line) /* Line number in the source file; used for - * debugging. */ -{ - return Tcl_NewListObj(objc, objv); -} -#endif /* TCL_MEM_DEBUG */ - -/* - *---------------------------------------------------------------------- - * - * Tcl_SetListObj -- - * - * Modify an object to be a list containing each of the objc elements of - * the object array referenced by objv. - * - * Results: - * None. - * - * Side effects: - * The object is made a list object and is initialized from the object - * pointers in objv. If objc is less than or equal to zero, an empty - * object is returned. The new object's string representation is left - * NULL. The ref counts of the elements in objv are incremented since the - * list now refers to them. The object's old string and internal - * representations are freed and its type is set NULL. - * - *---------------------------------------------------------------------- - */ - -void -Tcl_SetListObj( - Tcl_Obj *objPtr, /* Object whose internal rep to init. */ - int objc, /* Count of objects referenced by objv. */ - Tcl_Obj *const objv[]) /* An array of pointers to Tcl objects. */ -{ - List *listRepPtr; - - if (Tcl_IsShared(objPtr)) { - Tcl_Panic("%s called with shared object", "Tcl_SetListObj"); - } - - /* - * Free any old string rep and any internal rep for the old type. - */ - - TclFreeIntRep(objPtr); - TclInvalidateStringRep(objPtr); - - /* - * Set the object's type to "list" and initialize the internal rep. - * However, if there are no elements to put in the list, just give the - * object an empty string rep and a NULL type. - */ - - if (objc > 0) { - listRepPtr = NewListIntRep(objc, objv, 1); - ListSetIntRep(objPtr, listRepPtr); - } else { - objPtr->bytes = tclEmptyStringRep; - objPtr->length = 0; - } -} - -/* - *---------------------------------------------------------------------- - * - * TclListObjCopy -- - * - * Makes a "pure list" copy of a list value. This provides for the C - * level a counterpart of the [lrange $list 0 end] command, while using - * internals details to be as efficient as possible. - * - * Results: - * Normally returns a pointer to a new Tcl_Obj, that contains the same - * list value as *listPtr does. The returned Tcl_Obj has a refCount of - * zero. If *listPtr does not hold a list, NULL is returned, and if - * interp is non-NULL, an error message is recorded there. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclListObjCopy( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - Tcl_Obj *listPtr) /* List object for which an element array is - * to be returned. */ -{ - Tcl_Obj *copyPtr; - - if (listPtr->typePtr != &tclListType) { - if (SetListFromAny(interp, listPtr) != TCL_OK) { - return NULL; - } - } - - TclNewObj(copyPtr); - TclInvalidateStringRep(copyPtr); - DupListInternalRep(listPtr, copyPtr); - return copyPtr; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ListObjGetElements -- - * - * This function returns an (objc,objv) array of the elements in a list - * object. - * - * Results: - * The return value is normally TCL_OK; in this case *objcPtr is set to - * the count of list elements and *objvPtr is set to a pointer to an - * array of (*objcPtr) pointers to each list element. If listPtr does not - * refer to a list object and the object can not be converted to one, - * TCL_ERROR is returned and an error message will be left in the - * interpreter's result if interp is not NULL. - * - * The objects referenced by the returned array should be treated as - * readonly and their ref counts are _not_ incremented; the caller must - * do that if it holds on to a reference. Furthermore, the pointer and - * length returned by this function may change as soon as any function is - * called on the list object; be careful about retaining the pointer in a - * local data structure. - * - * Side effects: - * The possible conversion of the object referenced by listPtr - * to a list object. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ListObjGetElements( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - register Tcl_Obj *listPtr, /* List object for which an element array is - * to be returned. */ - int *objcPtr, /* Where to store the count of objects - * referenced by objv. */ - Tcl_Obj ***objvPtr) /* Where to store the pointer to an array of - * pointers to the list's objects. */ -{ - register List *listRepPtr; - - if (listPtr->typePtr != &tclListType) { - int result; - - if (listPtr->bytes == tclEmptyStringRep) { - *objcPtr = 0; - *objvPtr = NULL; - return TCL_OK; - } - result = SetListFromAny(interp, listPtr); - if (result != TCL_OK) { - return result; - } - } - listRepPtr = ListRepPtr(listPtr); - *objcPtr = listRepPtr->elemCount; - *objvPtr = &listRepPtr->elements; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ListObjAppendList -- - * - * This function appends the elements in the list value referenced by - * elemListPtr to the list value referenced by listPtr. - * - * Results: - * The return value is normally TCL_OK. If listPtr or elemListPtr do not - * refer to list values, TCL_ERROR is returned and an error message is - * left in the interpreter's result if interp is not NULL. - * - * Side effects: - * The reference counts of the elements in elemListPtr are incremented - * since the list now refers to them. listPtr and elemListPtr are - * converted, if necessary, to list objects. Also, appending the new - * elements may cause listObj's array of element pointers to grow. - * listPtr's old string representation, if any, is invalidated. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ListObjAppendList( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - register Tcl_Obj *listPtr, /* List object to append elements to. */ - Tcl_Obj *elemListPtr) /* List obj with elements to append. */ -{ - int objc; - Tcl_Obj **objv; - - if (Tcl_IsShared(listPtr)) { - Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendList"); - } - - /* - * Pull the elements to append from elemListPtr. - */ - - if (TCL_OK != TclListObjGetElements(interp, elemListPtr, &objc, &objv)) { - return TCL_ERROR; - } - - /* - * Insert the new elements starting after the lists's last element. - * Delete zero existing elements. - */ - - return Tcl_ListObjReplace(interp, listPtr, LIST_MAX, 0, objc, objv); -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ListObjAppendElement -- - * - * This function is a special purpose version of Tcl_ListObjAppendList: - * it appends a single object referenced by objPtr to the list object - * referenced by listPtr. If listPtr is not already a list object, an - * attempt will be made to convert it to one. - * - * Results: - * The return value is normally TCL_OK; in this case objPtr is added to - * the end of listPtr's list. If listPtr does not refer to a list object - * and the object can not be converted to one, TCL_ERROR is returned and - * an error message will be left in the interpreter's result if interp is - * not NULL. - * - * Side effects: - * The ref count of objPtr is incremented since the list now refers to - * it. listPtr will be converted, if necessary, to a list object. Also, - * appending the new element may cause listObj's array of element - * pointers to grow. listPtr's old string representation, if any, is - * invalidated. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ListObjAppendElement( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - Tcl_Obj *listPtr, /* List object to append objPtr to. */ - Tcl_Obj *objPtr) /* Object to append to listPtr's list. */ -{ - register List *listRepPtr, *newPtr = NULL; - int numElems, numRequired, needGrow, isShared, attempt; - - if (Tcl_IsShared(listPtr)) { - Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendElement"); - } - if (listPtr->typePtr != &tclListType) { - int result; - - if (listPtr->bytes == tclEmptyStringRep) { - Tcl_SetListObj(listPtr, 1, &objPtr); - return TCL_OK; - } - result = SetListFromAny(interp, listPtr); - if (result != TCL_OK) { - return result; - } - } - - listRepPtr = ListRepPtr(listPtr); - numElems = listRepPtr->elemCount; - numRequired = numElems + 1 ; - needGrow = (numRequired > listRepPtr->maxElemCount); - isShared = (listRepPtr->refCount > 1); - - if (numRequired > LIST_MAX) { - if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "max length of a Tcl list (%d elements) exceeded", - LIST_MAX)); - Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); - } - return TCL_ERROR; - } - - if (needGrow && !isShared) { - /* - * Need to grow + unshared intrep => try to realloc - */ - - attempt = 2 * numRequired; - if (attempt <= LIST_MAX) { - newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); - } - if (newPtr == NULL) { - attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; - if (attempt > LIST_MAX) { - attempt = LIST_MAX; - } - newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); - } - if (newPtr == NULL) { - attempt = numRequired; - newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); - } - if (newPtr) { - listRepPtr = newPtr; - listRepPtr->maxElemCount = attempt; - needGrow = 0; - } - } - if (isShared || needGrow) { - Tcl_Obj **dst, **src = &listRepPtr->elements; - - /* - * Either we have a shared intrep and we must copy to write, or we - * need to grow and realloc attempts failed. Attempt intrep copy. - */ - - attempt = 2 * numRequired; - newPtr = AttemptNewList(NULL, attempt, NULL); - if (newPtr == NULL) { - attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; - if (attempt > LIST_MAX) { - attempt = LIST_MAX; - } - newPtr = AttemptNewList(NULL, attempt, NULL); - } - if (newPtr == NULL) { - attempt = numRequired; - newPtr = AttemptNewList(interp, attempt, NULL); - } - if (newPtr == NULL) { - /* - * All growth attempts failed; throw the error. - */ - - return TCL_ERROR; - } - - dst = &newPtr->elements; - newPtr->refCount++; - newPtr->canonicalFlag = listRepPtr->canonicalFlag; - newPtr->elemCount = listRepPtr->elemCount; - - if (isShared) { - /* - * The original intrep must remain undisturbed. Copy into the new - * one and bump refcounts - */ - while (numElems--) { - *dst = *src++; - Tcl_IncrRefCount(*dst++); - } - listRepPtr->refCount--; - } else { - /* - * Old intrep to be freed, re-use refCounts. - */ - - memcpy(dst, src, (size_t) numElems * sizeof(Tcl_Obj *)); - ckfree(listRepPtr); - } - listRepPtr = newPtr; - } - listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr; - - /* - * Add objPtr to the end of listPtr's array of element pointers. Increment - * the ref count for the (now shared) objPtr. - */ - - *(&listRepPtr->elements + listRepPtr->elemCount) = objPtr; - Tcl_IncrRefCount(objPtr); - listRepPtr->elemCount++; - - /* - * Invalidate any old string representation since the list's internal - * representation has changed. - */ - - TclInvalidateStringRep(listPtr); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ListObjIndex -- - * - * This function returns a pointer to the index'th object from the list - * referenced by listPtr. The first element has index 0. If index is - * negative or greater than or equal to the number of elements in the - * list, a NULL is returned. If listPtr is not a list object, an attempt - * will be made to convert it to a list. - * - * Results: - * The return value is normally TCL_OK; in this case objPtrPtr is set to - * the Tcl_Obj pointer for the index'th list element or NULL if index is - * out of range. This object should be treated as readonly and its ref - * count is _not_ incremented; the caller must do that if it holds on to - * the reference. If listPtr does not refer to a list and can't be - * converted to one, TCL_ERROR is returned and an error message is left - * in the interpreter's result if interp is not NULL. - * - * Side effects: - * listPtr will be converted, if necessary, to a list object. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ListObjIndex( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - register Tcl_Obj *listPtr, /* List object to index into. */ - register int index, /* Index of element to return. */ - Tcl_Obj **objPtrPtr) /* The resulting Tcl_Obj* is stored here. */ -{ - register List *listRepPtr; - - if (listPtr->typePtr != &tclListType) { - int result; - - if (listPtr->bytes == tclEmptyStringRep) { - *objPtrPtr = NULL; - return TCL_OK; - } - result = SetListFromAny(interp, listPtr); - if (result != TCL_OK) { - return result; - } - } - - listRepPtr = ListRepPtr(listPtr); - if ((index < 0) || (index >= listRepPtr->elemCount)) { - *objPtrPtr = NULL; - } else { - *objPtrPtr = (&listRepPtr->elements)[index]; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ListObjLength -- - * - * This function returns the number of elements in a list object. If the - * object is not already a list object, an attempt will be made to - * convert it to one. - * - * Results: - * The return value is normally TCL_OK; in this case *intPtr will be set - * to the integer count of list elements. If listPtr does not refer to a - * list object and the object can not be converted to one, TCL_ERROR is - * returned and an error message will be left in the interpreter's result - * if interp is not NULL. - * - * Side effects: - * The possible conversion of the argument object to a list object. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ListObjLength( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - register Tcl_Obj *listPtr, /* List object whose #elements to return. */ - register int *intPtr) /* The resulting int is stored here. */ -{ - register List *listRepPtr; - - if (listPtr->typePtr != &tclListType) { - int result; - - if (listPtr->bytes == tclEmptyStringRep) { - *intPtr = 0; - return TCL_OK; - } - result = SetListFromAny(interp, listPtr); - if (result != TCL_OK) { - return result; - } - } - - listRepPtr = ListRepPtr(listPtr); - *intPtr = listRepPtr->elemCount; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ListObjReplace -- - * - * This function replaces zero or more elements of the list referenced by - * listPtr with the objects from an (objc,objv) array. The objc elements - * of the array referenced by objv replace the count elements in listPtr - * starting at first. - * - * If the argument first is zero or negative, it refers to the first - * element. If first is greater than or equal to the number of elements - * in the list, then no elements are deleted; the new elements are - * appended to the list. Count gives the number of elements to replace. - * If count is zero or negative then no elements are deleted; the new - * elements are simply inserted before first. - * - * The argument objv refers to an array of objc pointers to the new - * elements to be added to listPtr in place of those that were deleted. - * If objv is NULL, no new elements are added. If listPtr is not a list - * object, an attempt will be made to convert it to one. - * - * Results: - * The return value is normally TCL_OK. If listPtr does not refer to a - * list object and can not be converted to one, TCL_ERROR is returned and - * an error message will be left in the interpreter's result if interp is - * not NULL. - * - * Side effects: - * The ref counts of the objc elements in objv are incremented since the - * resulting list now refers to them. Similarly, the ref counts for - * replaced objects are decremented. listPtr is converted, if necessary, - * to a list object. listPtr's old string representation, if any, is - * freed. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ListObjReplace( - Tcl_Interp *interp, /* Used for error reporting if not NULL. */ - Tcl_Obj *listPtr, /* List object whose elements to replace. */ - int first, /* Index of first element to replace. */ - int count, /* Number of elements to replace. */ - int objc, /* Number of objects to insert. */ - Tcl_Obj *const objv[]) /* An array of objc pointers to Tcl objects to - * insert. */ -{ - List *listRepPtr; - register Tcl_Obj **elemPtrs; - int needGrow, numElems, numRequired, numAfterLast, start, i, j, isShared; - - if (Tcl_IsShared(listPtr)) { - Tcl_Panic("%s called with shared object", "Tcl_ListObjReplace"); - } - if (listPtr->typePtr != &tclListType) { - if (listPtr->bytes == tclEmptyStringRep) { - if (!objc) { - return TCL_OK; - } - Tcl_SetListObj(listPtr, objc, NULL); - } else { - int result = SetListFromAny(interp, listPtr); - - if (result != TCL_OK) { - return result; - } - } - } - - /* - * Note that when count == 0 and objc == 0, this routine is logically a - * no-op, removing and adding no elements to the list. However, by flowing - * through this routine anyway, we get the important side effect that the - * resulting listPtr is a list in canoncial form. This is important. - * Resist any temptation to optimize this case. - */ - - listRepPtr = ListRepPtr(listPtr); - elemPtrs = &listRepPtr->elements; - numElems = listRepPtr->elemCount; - - if (first < 0) { - first = 0; - } - if (first >= numElems) { - first = numElems; /* So we'll insert after last element. */ - } - if (count < 0) { - count = 0; - } else if (numElems < first+count || first+count < 0) { - /* - * The 'first+count < 0' condition here guards agains integer - * overflow in determining 'first+count'. - */ - - count = numElems - first; - } - - if (objc > LIST_MAX - (numElems - count)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "max length of a Tcl list (%d elements) exceeded", LIST_MAX)); - return TCL_ERROR; - } - isShared = (listRepPtr->refCount > 1); - numRequired = numElems - count + objc; /* Known <= LIST_MAX */ - needGrow = numRequired > listRepPtr->maxElemCount; - - for (i = 0; i < objc; i++) { - Tcl_IncrRefCount(objv[i]); - } - - if (needGrow && !isShared) { - /* Try to use realloc */ - List *newPtr = NULL; - int attempt = 2 * numRequired; - if (attempt <= LIST_MAX) { - newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); - } - if (newPtr == NULL) { - attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; - if (attempt > LIST_MAX) { - attempt = LIST_MAX; - } - newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); - } - if (newPtr == NULL) { - attempt = numRequired; - newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); - } - if (newPtr) { - listRepPtr = newPtr; - listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr; - elemPtrs = &listRepPtr->elements; - listRepPtr->maxElemCount = attempt; - needGrow = numRequired > listRepPtr->maxElemCount; - } - } - if (!needGrow && !isShared) { - int shift; - - /* - * Can use the current List struct. First "delete" count elements - * starting at first. - */ - - for (j = first; j < first + count; j++) { - Tcl_Obj *victimPtr = elemPtrs[j]; - - TclDecrRefCount(victimPtr); - } - - /* - * Shift the elements after the last one removed to their new - * locations. - */ - - start = first + count; - numAfterLast = numElems - start; - shift = objc - count; /* numNewElems - numDeleted */ - if ((numAfterLast > 0) && (shift != 0)) { - Tcl_Obj **src = elemPtrs + start; - - memmove(src+shift, src, (size_t) numAfterLast * sizeof(Tcl_Obj*)); - } - } else { - /* - * Cannot use the current List struct; it is shared, too small, or - * both. Allocate a new struct and insert elements into it. - */ - - List *oldListRepPtr = listRepPtr; - Tcl_Obj **oldPtrs = elemPtrs; - int newMax; - - if (needGrow){ - newMax = 2 * numRequired; - } else { - newMax = listRepPtr->maxElemCount; - } - - listRepPtr = AttemptNewList(NULL, newMax, NULL); - if (listRepPtr == NULL) { - unsigned int limit = LIST_MAX - numRequired; - unsigned int extra = numRequired - numElems - + TCL_MIN_ELEMENT_GROWTH; - int growth = (int) ((extra > limit) ? limit : extra); - - listRepPtr = AttemptNewList(NULL, numRequired + growth, NULL); - if (listRepPtr == NULL) { - listRepPtr = AttemptNewList(interp, numRequired, NULL); - if (listRepPtr == NULL) { - for (i = 0; i < objc; i++) { - /* See bug 3598580 */ -#if TCL_MAJOR_VERSION > 8 - Tcl_DecrRefCount(objv[i]); -#else - objv[i]->refCount--; -#endif - } - return TCL_ERROR; - } - } - } - - listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr; - listRepPtr->refCount++; - - elemPtrs = &listRepPtr->elements; - - if (isShared) { - /* - * The old struct will remain in place; need new refCounts for the - * new List struct references. Copy over only the surviving - * elements. - */ - - for (i=0; i < first; i++) { - elemPtrs[i] = oldPtrs[i]; - Tcl_IncrRefCount(elemPtrs[i]); - } - for (i = first + count, j = first + objc; - j < numRequired; i++, j++) { - elemPtrs[j] = oldPtrs[i]; - Tcl_IncrRefCount(elemPtrs[j]); - } - - oldListRepPtr->refCount--; - } else { - /* - * The old struct will be removed; use its inherited refCounts. - */ - - if (first > 0) { - memcpy(elemPtrs, oldPtrs, (size_t) first * sizeof(Tcl_Obj *)); - } - - /* - * "Delete" count elements starting at first. - */ - - for (j = first; j < first + count; j++) { - Tcl_Obj *victimPtr = oldPtrs[j]; - - TclDecrRefCount(victimPtr); - } - - /* - * Copy the elements after the last one removed, shifted to their - * new locations. - */ - - start = first + count; - numAfterLast = numElems - start; - if (numAfterLast > 0) { - memcpy(elemPtrs + first + objc, oldPtrs + start, - (size_t) numAfterLast * sizeof(Tcl_Obj *)); - } - - ckfree(oldListRepPtr); - } - } - - /* - * Insert the new elements into elemPtrs before "first". - */ - - for (i=0,j=first ; i<objc ; i++,j++) { - elemPtrs[j] = objv[i]; - } - - /* - * Update the count of elements. - */ - - listRepPtr->elemCount = numRequired; - - /* - * Invalidate and free any old string representation since it no longer - * reflects the list's internal representation. - */ - - TclInvalidateStringRep(listPtr); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclLindexList -- - * - * This procedure handles the 'lindex' command when objc==3. - * - * Results: - * Returns a pointer to the object extracted, or NULL if an error - * occurred. The returned object already includes one reference count for - * the pointer returned. - * - * Side effects: - * None. - * - * Notes: - * This procedure is implemented entirely as a wrapper around - * TclLindexFlat. All it does is reconfigure the argument format into the - * form required by TclLindexFlat, while taking care to manage shimmering - * in such a way that we tend to keep the most useful intreps and/or - * avoid the most expensive conversions. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclLindexList( - Tcl_Interp *interp, /* Tcl interpreter. */ - Tcl_Obj *listPtr, /* List being unpacked. */ - Tcl_Obj *argPtr) /* Index or index list. */ -{ - - int index; /* Index into the list. */ - Tcl_Obj *indexListCopy; - - /* - * Determine whether argPtr designates a list or a single index. We have - * to be careful about the order of the checks to avoid repeated - * shimmering; see TIP#22 and TIP#33 for the details. - */ - - if (argPtr->typePtr != &tclListType - && TclGetIntForIndexM(NULL , argPtr, 0, &index) == TCL_OK) { - /* - * argPtr designates a single index. - */ - - return TclLindexFlat(interp, listPtr, 1, &argPtr); - } - - /* - * Here we make a private copy of the index list argument to avoid any - * shimmering issues that might invalidate the indices array below while - * we are still using it. This is probably unnecessary. It does not appear - * that any damaging shimmering is possible, and no test has been devised - * to show any error when this private copy is not made. But it's cheap, - * and it offers some future-proofing insurance in case the TclLindexFlat - * implementation changes in some unexpected way, or some new form of - * trace or callback permits things to happen that the current - * implementation does not. - */ - - indexListCopy = TclListObjCopy(NULL, argPtr); - if (indexListCopy == NULL) { - /* - * argPtr designates something that is neither an index nor a - * well-formed list. Report the error via TclLindexFlat. - */ - - return TclLindexFlat(interp, listPtr, 1, &argPtr); - } - - if (indexListCopy->typePtr == &tclListType) { - List *listRepPtr = ListRepPtr(indexListCopy); - - listPtr = TclLindexFlat(interp, listPtr, listRepPtr->elemCount, - &listRepPtr->elements); - } else { - int indexCount = -1; /* Size of the array of list indices. */ - Tcl_Obj **indices = NULL; - /* Array of list indices. */ - - Tcl_ListObjGetElements(NULL, indexListCopy, &indexCount, &indices); - listPtr = TclLindexFlat(interp, listPtr, indexCount, indices); - } - Tcl_DecrRefCount(indexListCopy); - return listPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclLindexFlat -- - * - * This procedure is the core of the 'lindex' command, with all index - * arguments presented as a flat list. - * - * Results: - * Returns a pointer to the object extracted, or NULL if an error - * occurred. The returned object already includes one reference count for - * the pointer returned. - * - * Side effects: - * None. - * - * Notes: - * The reference count of the returned object includes one reference - * corresponding to the pointer returned. Thus, the calling code will - * usually do something like: - * Tcl_SetObjResult(interp, result); - * Tcl_DecrRefCount(result); - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclLindexFlat( - Tcl_Interp *interp, /* Tcl interpreter. */ - Tcl_Obj *listPtr, /* Tcl object representing the list. */ - int indexCount, /* Count of indices. */ - Tcl_Obj *const indexArray[])/* Array of pointers to Tcl objects that - * represent the indices in the list. */ -{ - int i; - - Tcl_IncrRefCount(listPtr); - - for (i=0 ; i<indexCount && listPtr ; i++) { - int index, listLen = 0; - Tcl_Obj **elemPtrs = NULL, *sublistCopy; - - /* - * Here we make a private copy of the current sublist, so we avoid any - * shimmering issues that might invalidate the elemPtr array below - * while we are still using it. See test lindex-8.4. - */ - - sublistCopy = TclListObjCopy(interp, listPtr); - Tcl_DecrRefCount(listPtr); - listPtr = NULL; - - if (sublistCopy == NULL) { - /* - * The sublist is not a list at all => error. - */ - - break; - } - TclListObjGetElements(NULL, sublistCopy, &listLen, &elemPtrs); - - if (TclGetIntForIndexM(interp, indexArray[i], /*endValue*/ listLen-1, - &index) == TCL_OK) { - if (index<0 || index>=listLen) { - /* - * Index is out of range. Break out of loop with empty result. - * First check remaining indices for validity - */ - - while (++i < indexCount) { - if (TclGetIntForIndexM(interp, indexArray[i], -1, &index) - != TCL_OK) { - Tcl_DecrRefCount(sublistCopy); - return NULL; - } - } - listPtr = Tcl_NewObj(); - } else { - /* - * Extract the pointer to the appropriate element. - */ - - listPtr = elemPtrs[index]; - } - Tcl_IncrRefCount(listPtr); - } - Tcl_DecrRefCount(sublistCopy); - } - - return listPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclLsetList -- - * - * Core of the 'lset' command when objc == 4. Objv[2] may be either a - * scalar index or a list of indices. - * - * Results: - * Returns the new value of the list variable, or NULL if there was an - * error. The returned object includes one reference count for the - * pointer returned. - * - * Side effects: - * None. - * - * Notes: - * This procedure is implemented entirely as a wrapper around - * TclLsetFlat. All it does is reconfigure the argument format into the - * form required by TclLsetFlat, while taking care to manage shimmering - * in such a way that we tend to keep the most useful intreps and/or - * avoid the most expensive conversions. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclLsetList( - Tcl_Interp *interp, /* Tcl interpreter. */ - Tcl_Obj *listPtr, /* Pointer to the list being modified. */ - Tcl_Obj *indexArgPtr, /* Index or index-list arg to 'lset'. */ - Tcl_Obj *valuePtr) /* Value arg to 'lset'. */ -{ - int indexCount = 0; /* Number of indices in the index list. */ - Tcl_Obj **indices = NULL; /* Vector of indices in the index list. */ - Tcl_Obj *retValuePtr; /* Pointer to the list to be returned. */ - int index; /* Current index in the list - discarded. */ - Tcl_Obj *indexListCopy; - - /* - * Determine whether the index arg designates a list or a single index. - * We have to be careful about the order of the checks to avoid repeated - * shimmering; see TIP #22 and #23 for details. - */ - - if (indexArgPtr->typePtr != &tclListType - && TclGetIntForIndexM(NULL, indexArgPtr, 0, &index) == TCL_OK) { - /* - * indexArgPtr designates a single index. - */ - - return TclLsetFlat(interp, listPtr, 1, &indexArgPtr, valuePtr); - - } - - indexListCopy = TclListObjCopy(NULL, indexArgPtr); - if (indexListCopy == NULL) { - /* - * indexArgPtr designates something that is neither an index nor a - * well formed list. Report the error via TclLsetFlat. - */ - - return TclLsetFlat(interp, listPtr, 1, &indexArgPtr, valuePtr); - } - TclListObjGetElements(NULL, indexArgPtr, &indexCount, &indices); - - /* - * Let TclLsetFlat handle the actual lset'ting. - */ - - retValuePtr = TclLsetFlat(interp, listPtr, indexCount, indices, valuePtr); - - Tcl_DecrRefCount(indexListCopy); - return retValuePtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclLsetFlat -- - * - * Core engine of the 'lset' command. - * - * Results: - * Returns the new value of the list variable, or NULL if an error - * occurred. The returned object includes one reference count for the - * pointer returned. - * - * Side effects: - * On entry, the reference count of the variable value does not reflect - * any references held on the stack. The first action of this function is - * to determine whether the object is shared, and to duplicate it if it - * is. The reference count of the duplicate is incremented. At this - * point, the reference count will be 1 for either case, so that the - * object will appear to be unshared. - * - * If an error occurs, and the object has been duplicated, the reference - * count on the duplicate is decremented so that it is now 0: this - * dismisses any memory that was allocated by this function. - * - * If no error occurs, the reference count of the original object is - * incremented if the object has not been duplicated, and nothing is done - * to a reference count of the duplicate. Now the reference count of an - * unduplicated object is 2 (the returned pointer, plus the one stored in - * the variable). The reference count of a duplicate object is 1, - * reflecting that the returned pointer is the only active reference. The - * caller is expected to store the returned value back in the variable - * and decrement its reference count. (INST_STORE_* does exactly this.) - * - * Surgery is performed on the unshared list value to produce the result. - * TclLsetFlat maintains a linked list of Tcl_Obj's whose string - * representations must be spoilt by threading via 'ptr2' of the - * two-pointer internal representation. On entry to TclLsetFlat, the - * values of 'ptr2' are immaterial; on exit, the 'ptr2' field of any - * Tcl_Obj that has been modified is set to NULL. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclLsetFlat( - Tcl_Interp *interp, /* Tcl interpreter. */ - Tcl_Obj *listPtr, /* Pointer to the list being modified. */ - int indexCount, /* Number of index args. */ - Tcl_Obj *const indexArray[], - /* Index args. */ - Tcl_Obj *valuePtr) /* Value arg to 'lset'. */ -{ - int index, result, len; - Tcl_Obj *subListPtr, *retValuePtr, *chainPtr; - - /* - * If there are no indices, simply return the new value. (Without - * indices, [lset] is a synonym for [set]. - */ - - if (indexCount == 0) { - Tcl_IncrRefCount(valuePtr); - return valuePtr; - } - - /* - * If the list is shared, make a copy we can modify (copy-on-write). We - * use Tcl_DuplicateObj() instead of TclListObjCopy() for a few reasons: - * 1) we have not yet confirmed listPtr is actually a list; 2) We make a - * verbatim copy of any existing string rep, and when we combine that with - * the delayed invalidation of string reps of modified Tcl_Obj's - * implemented below, the outcome is that any error condition that causes - * this routine to return NULL, will leave the string rep of listPtr and - * all elements to be unchanged. - */ - - subListPtr = Tcl_IsShared(listPtr) ? Tcl_DuplicateObj(listPtr) : listPtr; - - /* - * Anchor the linked list of Tcl_Obj's whose string reps must be - * invalidated if the operation succeeds. - */ - - retValuePtr = subListPtr; - chainPtr = NULL; - result = TCL_OK; - - /* - * Loop through all the index arguments, and for each one dive into the - * appropriate sublist. - */ - - do { - int elemCount; - Tcl_Obj *parentList, **elemPtrs; - - /* - * Check for the possible error conditions... - */ - - if (TclListObjGetElements(interp, subListPtr, &elemCount, &elemPtrs) - != TCL_OK) { - /* ...the sublist we're indexing into isn't a list at all. */ - result = TCL_ERROR; - break; - } - - /* - * WARNING: the macro TclGetIntForIndexM is not safe for - * post-increments, avoid '*indexArray++' here. - */ - - if (TclGetIntForIndexM(interp, *indexArray, elemCount - 1, &index) - != TCL_OK) { - /* ...the index we're trying to use isn't an index at all. */ - result = TCL_ERROR; - indexArray++; - break; - } - indexArray++; - - if (index < 0 || index > elemCount) { - /* ...the index points outside the sublist. */ - if (interp != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("list index out of range", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", - "BADINDEX", NULL); - } - result = TCL_ERROR; - break; - } - - /* - * No error conditions. As long as we're not yet on the last index, - * determine the next sublist for the next pass through the loop, and - * take steps to make sure it is an unshared copy, as we intend to - * modify it. - */ - - if (--indexCount) { - parentList = subListPtr; - if (index == elemCount) { - subListPtr = Tcl_NewObj(); - } else { - subListPtr = elemPtrs[index]; - } - if (Tcl_IsShared(subListPtr)) { - subListPtr = Tcl_DuplicateObj(subListPtr); - } - - /* - * Replace the original elemPtr[index] in parentList with a copy - * we know to be unshared. This call will also deal with the - * situation where parentList shares its intrep with other - * Tcl_Obj's. Dealing with the shared intrep case can cause - * subListPtr to become shared again, so detect that case and make - * and store another copy. - */ - - if (index == elemCount) { - Tcl_ListObjAppendElement(NULL, parentList, subListPtr); - } else { - TclListObjSetElement(NULL, parentList, index, subListPtr); - } - if (Tcl_IsShared(subListPtr)) { - subListPtr = Tcl_DuplicateObj(subListPtr); - TclListObjSetElement(NULL, parentList, index, subListPtr); - } - - /* - * The TclListObjSetElement() calls do not spoil the string rep of - * parentList, and that's fine for now, since all we've done so - * far is replace a list element with an unshared copy. The list - * value remains the same, so the string rep. is still valid, and - * unchanged, which is good because if this whole routine returns - * NULL, we'd like to leave no change to the value of the lset - * variable. Later on, when we set valuePtr in its proper place, - * then all containing lists will have their values changed, and - * will need their string reps spoiled. We maintain a list of all - * those Tcl_Obj's (via a little intrep surgery) so we can spoil - * them at that time. - */ - - parentList->internalRep.twoPtrValue.ptr2 = chainPtr; - chainPtr = parentList; - } - } while (indexCount > 0); - - /* - * Either we've detected and error condition, and exited the loop with - * result == TCL_ERROR, or we've successfully reached the last index, and - * we're ready to store valuePtr. In either case, we need to clean up our - * string spoiling list of Tcl_Obj's. - */ - - while (chainPtr) { - Tcl_Obj *objPtr = chainPtr; - - if (result == TCL_OK) { - /* - * We're going to store valuePtr, so spoil string reps of all - * containing lists. - */ - - TclInvalidateStringRep(objPtr); - } - - /* - * Clear away our intrep surgery mess. - */ - - chainPtr = objPtr->internalRep.twoPtrValue.ptr2; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - } - - if (result != TCL_OK) { - /* - * Error return; message is already in interp. Clean up any excess - * memory. - */ - - if (retValuePtr != listPtr) { - Tcl_DecrRefCount(retValuePtr); - } - return NULL; - } - - /* - * Store valuePtr in proper sublist and return. The -1 is to avoid a - * compiler warning (not a problem because we checked that we have a - * proper list - or something convertible to one - above). - */ - - len = -1; - TclListObjLength(NULL, subListPtr, &len); - if (index == len) { - Tcl_ListObjAppendElement(NULL, subListPtr, valuePtr); - } else { - TclListObjSetElement(NULL, subListPtr, index, valuePtr); - } - TclInvalidateStringRep(subListPtr); - Tcl_IncrRefCount(retValuePtr); - return retValuePtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclListObjSetElement -- - * - * Set a single element of a list to a specified value - * - * Results: - * The return value is normally TCL_OK. If listPtr does not refer to a - * list object and cannot be converted to one, TCL_ERROR is returned and - * an error message will be left in the interpreter result if interp is - * not NULL. Similarly, if index designates an element outside the range - * [0..listLength-1], where listLength is the count of elements in the - * list object designated by listPtr, TCL_ERROR is returned and an error - * message is left in the interpreter result. - * - * Side effects: - * Tcl_Panic if listPtr designates a shared object. Otherwise, attempts - * to convert it to a list with a non-shared internal rep. Decrements the - * ref count of the object at the specified index within the list, - * replaces with the object designated by valuePtr, and increments the - * ref count of the replacement object. - * - * It is the caller's responsibility to invalidate the string - * representation of the object. - * - *---------------------------------------------------------------------- - */ - -int -TclListObjSetElement( - Tcl_Interp *interp, /* Tcl interpreter; used for error reporting - * if not NULL. */ - Tcl_Obj *listPtr, /* List object in which element should be - * stored. */ - int index, /* Index of element to store. */ - Tcl_Obj *valuePtr) /* Tcl object to store in the designated list - * element. */ -{ - List *listRepPtr; /* Internal representation of the list being - * modified. */ - Tcl_Obj **elemPtrs; /* Pointers to elements of the list. */ - int elemCount; /* Number of elements in the list. */ - - /* - * Ensure that the listPtr parameter designates an unshared list. - */ - - if (Tcl_IsShared(listPtr)) { - Tcl_Panic("%s called with shared object", "TclListObjSetElement"); - } - if (listPtr->typePtr != &tclListType) { - int result; - - if (listPtr->bytes == tclEmptyStringRep) { - if (interp != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("list index out of range", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", - "BADINDEX", NULL); - } - return TCL_ERROR; - } - result = SetListFromAny(interp, listPtr); - if (result != TCL_OK) { - return result; - } - } - - listRepPtr = ListRepPtr(listPtr); - elemCount = listRepPtr->elemCount; - - /* - * Ensure that the index is in bounds. - */ - - if (index<0 || index>=elemCount) { - if (interp != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("list index out of range", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", "BADINDEX", - NULL); - } - return TCL_ERROR; - } - - /* - * If the internal rep is shared, replace it with an unshared copy. - */ - - if (listRepPtr->refCount > 1) { - Tcl_Obj **dst, **src = &listRepPtr->elements; - List *newPtr = AttemptNewList(NULL, listRepPtr->maxElemCount, NULL); - - if (newPtr == NULL) { - newPtr = AttemptNewList(interp, elemCount, NULL); - if (newPtr == NULL) { - return TCL_ERROR; - } - } - newPtr->refCount++; - newPtr->elemCount = elemCount; - newPtr->canonicalFlag = listRepPtr->canonicalFlag; - - dst = &newPtr->elements; - while (elemCount--) { - *dst = *src++; - Tcl_IncrRefCount(*dst++); - } - - listRepPtr->refCount--; - - listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr = newPtr; - } - elemPtrs = &listRepPtr->elements; - - /* - * Add a reference to the new list element. - */ - - Tcl_IncrRefCount(valuePtr); - - /* - * Remove a reference from the old list element. - */ - - Tcl_DecrRefCount(elemPtrs[index]); - - /* - * Stash the new object in the list. - */ - - elemPtrs[index] = valuePtr; - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * FreeListInternalRep -- - * - * Deallocate the storage associated with a list object's internal - * representation. - * - * Results: - * None. - * - * Side effects: - * Frees listPtr's List* internal representation and sets listPtr's - * internalRep.twoPtrValue.ptr1 to NULL. Decrements the ref counts of all - * element objects, which may free them. - * - *---------------------------------------------------------------------- - */ - -static void -FreeListInternalRep( - Tcl_Obj *listPtr) /* List object with internal rep to free. */ -{ - List *listRepPtr = ListRepPtr(listPtr); - - if (listRepPtr->refCount-- <= 1) { - Tcl_Obj **elemPtrs = &listRepPtr->elements; - int i, numElems = listRepPtr->elemCount; - - for (i = 0; i < numElems; i++) { - Tcl_DecrRefCount(elemPtrs[i]); - } - ckfree(listRepPtr); - } - - listPtr->typePtr = NULL; -} - -/* - *---------------------------------------------------------------------- - * - * DupListInternalRep -- - * - * Initialize the internal representation of a list Tcl_Obj to share the - * internal representation of an existing list object. - * - * Results: - * None. - * - * Side effects: - * The reference count of the List internal rep is incremented. - * - *---------------------------------------------------------------------- - */ - -static void -DupListInternalRep( - Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ - Tcl_Obj *copyPtr) /* Object with internal rep to set. */ -{ - List *listRepPtr = ListRepPtr(srcPtr); - - ListSetIntRep(copyPtr, listRepPtr); -} - -/* - *---------------------------------------------------------------------- - * - * SetListFromAny -- - * - * Attempt to generate a list internal form for the Tcl object "objPtr". - * - * Results: - * The return value is TCL_OK or TCL_ERROR. If an error occurs during - * conversion, an error message is left in the interpreter's result - * unless "interp" is NULL. - * - * Side effects: - * If no error occurs, a list is stored as "objPtr"s internal - * representation. - * - *---------------------------------------------------------------------- - */ - -static int -SetListFromAny( - Tcl_Interp *interp, /* Used for error reporting if not NULL. */ - Tcl_Obj *objPtr) /* The object to convert. */ -{ - List *listRepPtr; - Tcl_Obj **elemPtrs; - - /* - * Dictionaries are a special case; they have a string representation such - * that *all* valid dictionaries are valid lists. Hence we can convert - * more directly. Only do this when there's no existing string rep; if - * there is, it is the string rep that's authoritative (because it could - * describe duplicate keys). - */ - - if (objPtr->typePtr == &tclDictType && !objPtr->bytes) { - Tcl_Obj *keyPtr, *valuePtr; - Tcl_DictSearch search; - int done, size; - - /* - * Create the new list representation. Note that we do not need to do - * anything with the string representation as the transformation (and - * the reverse back to a dictionary) are both order-preserving. Also - * note that since we know we've got a valid dictionary (by - * representation) we also know that fetching the size of the - * dictionary or iterating over it will not fail. - */ - - Tcl_DictObjSize(NULL, objPtr, &size); - listRepPtr = AttemptNewList(interp, size > 0 ? 2*size : 1, NULL); - if (!listRepPtr) { - return TCL_ERROR; - } - listRepPtr->elemCount = 2 * size; - - /* - * Populate the list representation. - */ - - elemPtrs = &listRepPtr->elements; - Tcl_DictObjFirst(NULL, objPtr, &search, &keyPtr, &valuePtr, &done); - while (!done) { - *elemPtrs++ = keyPtr; - *elemPtrs++ = valuePtr; - Tcl_IncrRefCount(keyPtr); - Tcl_IncrRefCount(valuePtr); - Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done); - } - } else { - int estCount, length; - const char *limit, *nextElem = TclGetStringFromObj(objPtr, &length); - - /* - * Allocate enough space to hold a (Tcl_Obj *) for each - * (possible) list element. - */ - - estCount = TclMaxListLength(nextElem, length, &limit); - estCount += (estCount == 0); /* Smallest list struct holds 1 - * element. */ - listRepPtr = AttemptNewList(interp, estCount, NULL); - if (listRepPtr == NULL) { - return TCL_ERROR; - } - elemPtrs = &listRepPtr->elements; - - /* - * Each iteration, parse and store a list element. - */ - - while (nextElem < limit) { - const char *elemStart; - int elemSize, literal; - - if (TCL_OK != TclFindElement(interp, nextElem, limit - nextElem, - &elemStart, &nextElem, &elemSize, &literal)) { - while (--elemPtrs >= &listRepPtr->elements) { - Tcl_DecrRefCount(*elemPtrs); - } - ckfree((char *) listRepPtr); - return TCL_ERROR; - } - if (elemStart == limit) { - break; - } - - /* TODO: replace panic with error on alloc failure? */ - if (literal) { - TclNewStringObj(*elemPtrs, elemStart, elemSize); - } else { - TclNewObj(*elemPtrs); - (*elemPtrs)->bytes = ckalloc((unsigned) elemSize + 1); - (*elemPtrs)->length = TclCopyAndCollapse(elemSize, elemStart, - (*elemPtrs)->bytes); - } - - Tcl_IncrRefCount(*elemPtrs++);/* Since list now holds ref to it. */ - } - - listRepPtr->elemCount = elemPtrs - &listRepPtr->elements; - } - - /* - * Free the old internalRep before setting the new one. We do this as late - * as possible to allow the conversion code, in particular - * Tcl_GetStringFromObj, to use that old internalRep. - */ - - TclFreeIntRep(objPtr); - ListSetIntRep(objPtr, listRepPtr); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * UpdateStringOfList -- - * - * Update the string representation for a list object. Note: This - * function does not invalidate an existing old string rep so storage - * will be lost if this has not already been done. - * - * Results: - * None. - * - * Side effects: - * The object's string is set to a valid string that results from the - * list-to-string conversion. This string will be empty if the list has - * no elements. The list internal representation should not be NULL and - * we assume it is not NULL. - * - *---------------------------------------------------------------------- - */ - -static void -UpdateStringOfList( - Tcl_Obj *listPtr) /* List object with string rep to update. */ -{ -# define LOCAL_SIZE 20 - int localFlags[LOCAL_SIZE], *flagPtr = NULL; - List *listRepPtr = ListRepPtr(listPtr); - int numElems = listRepPtr->elemCount; - int i, length, bytesNeeded = 0; - const char *elem; - char *dst; - Tcl_Obj **elemPtrs; - - /* - * Mark the list as being canonical; although it will now have a string - * rep, it is one we derived through proper "canonical" quoting and so - * it's known to be free from nasties relating to [concat] and [eval]. - */ - - listRepPtr->canonicalFlag = 1; - - /* - * Handle empty list case first, so rest of the routine is simpler. - */ - - if (numElems == 0) { - listPtr->bytes = tclEmptyStringRep; - listPtr->length = 0; - return; - } - - /* - * Pass 1: estimate space, gather flags. - */ - - if (numElems <= LOCAL_SIZE) { - flagPtr = localFlags; - } else { - /* - * We know numElems <= LIST_MAX, so this is safe. - */ - - flagPtr = ckalloc(numElems * sizeof(int)); - } - elemPtrs = &listRepPtr->elements; - for (i = 0; i < numElems; i++) { - flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0); - elem = TclGetStringFromObj(elemPtrs[i], &length); - bytesNeeded += TclScanElement(elem, length, flagPtr+i); - if (bytesNeeded < 0) { - Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); - } - } - if (bytesNeeded > INT_MAX - numElems + 1) { - Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); - } - bytesNeeded += numElems; - - /* - * Pass 2: copy into string rep buffer. - */ - - listPtr->length = bytesNeeded - 1; - listPtr->bytes = ckalloc(bytesNeeded); - dst = listPtr->bytes; - for (i = 0; i < numElems; i++) { - flagPtr[i] |= (i ? TCL_DONT_QUOTE_HASH : 0); - elem = TclGetStringFromObj(elemPtrs[i], &length); - dst += TclConvertElement(elem, length, dst, flagPtr[i]); - *dst++ = ' '; - } - listPtr->bytes[listPtr->length] = '\0'; - - if (flagPtr != localFlags) { - ckfree(flagPtr); - } -} - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |