Tcl Source Code

Check-in [a142b240b3]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Significant rewrite of the Tcl*(Scan|Convert)*Element() system, and revisions to the callers. Needs more work on comments, and testing to check for any performance impact in either direction. Fixes reported bug.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | bug-3173086
Files: files | file ages | folders
SHA1: a142b240b3d0fb1e0757f103fd6129e80d93f0b4
User & Date: dgp 2011-03-03 21:07:17
Context
2011-03-03
21:13
Correct flaw in the rewrite handling [list \\\\\}]. check-in: c3168e2541 user: dgp tags: bug-3173086
21:07
Significant rewrite of the Tcl*(Scan|Convert)*Element() system, and revisions to the callers. Needs... check-in: a142b240b3 user: dgp tags: bug-3173086
21:02
Simplified the logic of the Scan/Convert pair of routines for generating string reps of lists to st... check-in: 2f5897ef59 user: dgp tags: bug-3173086
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclDictObj.c.

455
456
457
458
459
460
461
462
463
464
465
466
467

468
469
470
471
472
473
474







475
476
477
478
479
480
481


482
483
484
485
486
487
488
489
490
491

492
493
494
495

496


497
498
499
500

501





502
503
504
505
506

507
508
509

510
511
512
513
514
515

516
517
518
519
520
521


522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
 */

static void
UpdateStringOfDict(
    Tcl_Obj *dictPtr)
{
#define LOCAL_SIZE 20
    int localFlags[LOCAL_SIZE], *flagPtr;
    Dict *dict = dictPtr->internalRep.otherValuePtr;
    ChainEntry *cPtr;
    Tcl_Obj *keyPtr, *valuePtr;
    int numElems, i, length;
    char *elem, *dst;


    /*
     * This field is the most useful one in the whole hash structure, and it
     * is not exposed by any API function...
     */

    numElems = dict->table.numEntries * 2;








    /*
     * Pass 1: estimate space, gather flags.
     */

    if (numElems <= LOCAL_SIZE) {
	flagPtr = localFlags;


    } else {
	flagPtr = (int *) ckalloc((unsigned) numElems*sizeof(int));
    }
    dictPtr->length = 1;
    for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
	/*
	 * Assume that cPtr is never NULL since we know the number of array
	 * elements already.
	 */


	keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, &cPtr->entry);
	elem = TclGetStringFromObj(keyPtr, &length);
	dictPtr->length += Tcl_ScanCountedElement(elem, length,
		&flagPtr[i]) + 1;




	valuePtr = Tcl_GetHashValue(&cPtr->entry);
	elem = TclGetStringFromObj(valuePtr, &length);
	dictPtr->length += Tcl_ScanCountedElement(elem, length,
		&flagPtr[i+1]) + 1;

    }






    /*
     * Pass 2: copy into string rep buffer.
     */


    dictPtr->bytes = ckalloc((unsigned) dictPtr->length);
    dst = dictPtr->bytes;
    for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {

	keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, &cPtr->entry);
	elem = TclGetStringFromObj(keyPtr, &length);
	dst += Tcl_ConvertCountedElement(elem, length, dst,
		flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH));
	*(dst++) = ' ';


	valuePtr = Tcl_GetHashValue(&cPtr->entry);
	elem = TclGetStringFromObj(valuePtr, &length);
	dst += Tcl_ConvertCountedElement(elem, length, dst,
		flagPtr[i+1] | TCL_DONT_QUOTE_HASH);
	*(dst++) = ' ';
    }


    if (flagPtr != localFlags) {
	ckfree((char *) flagPtr);
    }
    if (dst == dictPtr->bytes) {
	*dst = 0;
    } else {
	*(--dst) = 0;
    }
    dictPtr->length = dst - dictPtr->bytes;
}

/*
 *----------------------------------------------------------------------
 *
 * SetDictFromAny --
 *







|



|

>






|
>
>
>
>
>
>
>







>
>



<






>


|
|
>
|
>
>


|
|
>
|
>
>
>
>
>





>
|


>


|
<
|

>


|
<
|

>
>



<
<
<
<
<
<







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494

495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533

534
535
536
537
538
539

540
541
542
543
544
545
546






547
548
549
550
551
552
553
 */

static void
UpdateStringOfDict(
    Tcl_Obj *dictPtr)
{
#define LOCAL_SIZE 20
    int localFlags[LOCAL_SIZE], *flagPtr = NULL;
    Dict *dict = dictPtr->internalRep.otherValuePtr;
    ChainEntry *cPtr;
    Tcl_Obj *keyPtr, *valuePtr;
    int i, length, bytesNeeded = 0;
    char *elem, *dst;
    const int maxFlags = UINT_MAX / sizeof(int);

    /*
     * This field is the most useful one in the whole hash structure, and it
     * is not exposed by any API function...
     */

    int numElems = dict->table.numEntries * 2;

    /* Handle empty list case first, simplifies what follows */
    if (numElems == 0) {
	dictPtr->bytes = tclEmptyStringRep;
	dictPtr->length = 0;
	return;
    }

    /*
     * Pass 1: estimate space, gather flags.
     */

    if (numElems <= LOCAL_SIZE) {
	flagPtr = localFlags;
    } else if (numElems > maxFlags) {
	Tcl_Panic("UpdateStringOfDict: size requirement exceeds limits");
    } else {
	flagPtr = (int *) ckalloc((unsigned) numElems*sizeof(int));
    }

    for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
	/*
	 * Assume that cPtr is never NULL since we know the number of array
	 * elements already.
	 */

	flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 );
	keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, &cPtr->entry);
	elem = TclGetStringFromObj(keyPtr, &length);
	bytesNeeded += TclScanElement(elem, length, flagPtr+i);
	if (bytesNeeded < 0) {
	    Tcl_Panic("UpdateStringOfDict: size requirement exceeds limits");
	}

	flagPtr[i+1] = TCL_DONT_QUOTE_HASH;
	valuePtr = Tcl_GetHashValue(&cPtr->entry);
	elem = TclGetStringFromObj(valuePtr, &length);
	bytesNeeded += TclScanElement(elem, length, flagPtr+i+1);
	if (bytesNeeded < 0) {
	    Tcl_Panic("UpdateStringOfDict: size requirement exceeds limits");
	}
    }
    if (bytesNeeded > INT_MAX - numElems + 1) {
	Tcl_Panic("UpdateStringOfDict: size requirement exceeds limits");
    }
    bytesNeeded += numElems;

    /*
     * Pass 2: copy into string rep buffer.
     */

    dictPtr->length = bytesNeeded - 1;
    dictPtr->bytes = ckalloc((unsigned) bytesNeeded);
    dst = dictPtr->bytes;
    for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
	flagPtr[i] |= ( i ? TCL_DONT_QUOTE_HASH : 0 );
	keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, &cPtr->entry);
	elem = TclGetStringFromObj(keyPtr, &length);
	dst += TclConvertElement(elem, length, dst, flagPtr[i]);

	*dst++ = ' ';

	flagPtr[i+1] |= TCL_DONT_QUOTE_HASH;
	valuePtr = Tcl_GetHashValue(&cPtr->entry);
	elem = TclGetStringFromObj(valuePtr, &length);
	dst += TclConvertElement(elem, length, dst, flagPtr[i+1]);

	*dst++ = ' ';
    }
    dictPtr->bytes[dictPtr->length] = '\0';

    if (flagPtr != localFlags) {
	ckfree((char *) flagPtr);
    }






}

/*
 *----------------------------------------------------------------------
 *
 * SetDictFromAny --
 *

Changes to generic/tclIndexObj.c.

530
531
532
533
534
535
536

537
538
539
540
541
542
543
544
545
546
547
548
549
			origObjv[i]->internalRep.otherValuePtr;

		elementStr = ecrPtr->fullSubcmdName;
		elemLen = strlen(elementStr);
	    } else {
		elementStr = TclGetStringFromObj(origObjv[i], &elemLen);
	    }

	    len = Tcl_ScanCountedElement(elementStr, elemLen, &flags);

	    if (MAY_QUOTE_WORD && len != elemLen) {
		char *quotedElementStr = TclStackAlloc(interp, (unsigned)len);

		len = Tcl_ConvertCountedElement(elementStr, elemLen,
			quotedElementStr, flags);
		Tcl_AppendToObj(objPtr, quotedElementStr, len);
		TclStackFree(interp, quotedElementStr);
	    } else {
		Tcl_AppendToObj(objPtr, elementStr, elemLen);
	    }








>
|




|







530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
			origObjv[i]->internalRep.otherValuePtr;

		elementStr = ecrPtr->fullSubcmdName;
		elemLen = strlen(elementStr);
	    } else {
		elementStr = TclGetStringFromObj(origObjv[i], &elemLen);
	    }
	    flags = 0;
	    len = TclScanElement(elementStr, elemLen, &flags);

	    if (MAY_QUOTE_WORD && len != elemLen) {
		char *quotedElementStr = TclStackAlloc(interp, (unsigned)len);

		len = TclConvertElement(elementStr, elemLen,
			quotedElementStr, flags);
		Tcl_AppendToObj(objPtr, quotedElementStr, len);
		TclStackFree(interp, quotedElementStr);
	    } else {
		Tcl_AppendToObj(objPtr, elementStr, elemLen);
	    }

584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599
600
601
602
603
	    Tcl_AppendStringsToObj(objPtr, ecrPtr->fullSubcmdName, NULL);
	} else {
	    /*
	     * Quote the argument if it contains spaces (Bug 942757).
	     */

	    elementStr = TclGetStringFromObj(objv[i], &elemLen);

	    len = Tcl_ScanCountedElement(elementStr, elemLen, &flags);

	    if (MAY_QUOTE_WORD && len != elemLen) {
		char *quotedElementStr = TclStackAlloc(interp,(unsigned) len);

		len = Tcl_ConvertCountedElement(elementStr, elemLen,
			quotedElementStr, flags);
		Tcl_AppendToObj(objPtr, quotedElementStr, len);
		TclStackFree(interp, quotedElementStr);
	    } else {
		Tcl_AppendToObj(objPtr, elementStr, elemLen);
	    }
	}







>
|




|







585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
	    Tcl_AppendStringsToObj(objPtr, ecrPtr->fullSubcmdName, NULL);
	} else {
	    /*
	     * Quote the argument if it contains spaces (Bug 942757).
	     */

	    elementStr = TclGetStringFromObj(objv[i], &elemLen);
	    flags = 0;
	    len = TclScanElement(elementStr, elemLen, &flags);

	    if (MAY_QUOTE_WORD && len != elemLen) {
		char *quotedElementStr = TclStackAlloc(interp,(unsigned) len);

		len = TclConvertElement(elementStr, elemLen,
			quotedElementStr, flags);
		Tcl_AppendToObj(objPtr, quotedElementStr, len);
		TclStackFree(interp, quotedElementStr);
	    } else {
		Tcl_AppendToObj(objPtr, elementStr, elemLen);
	    }
	}

Changes to generic/tclInt.h.

2554
2555
2556
2557
2558
2559
2560


2561
2562
2563
2564
2565
2566
2567
MODULE_SCOPE ContLineLoc* TclContinuationsEnter(Tcl_Obj *objPtr, int num,
			    int *loc);
MODULE_SCOPE void	TclContinuationsEnterDerived(Tcl_Obj *objPtr,
			    int start, int *clNext);
MODULE_SCOPE ContLineLoc* TclContinuationsGet(Tcl_Obj *objPtr);
MODULE_SCOPE void	TclContinuationsCopy(Tcl_Obj *objPtr,
			    Tcl_Obj *originObjPtr);


MODULE_SCOPE void	TclDeleteNamespaceVars(Namespace *nsPtr);
/* TIP #280 - Modified token based evulation, with line information. */
MODULE_SCOPE int	TclEvalEx(Tcl_Interp *interp, const char *script,
			    int numBytes, int flags, int line,
			    int *clNextOuter, CONST char *outerScript);
MODULE_SCOPE int	TclFileAttrsCmd(Tcl_Interp *interp,
			    int objc, Tcl_Obj *const objv[]);







>
>







2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
MODULE_SCOPE ContLineLoc* TclContinuationsEnter(Tcl_Obj *objPtr, int num,
			    int *loc);
MODULE_SCOPE void	TclContinuationsEnterDerived(Tcl_Obj *objPtr,
			    int start, int *clNext);
MODULE_SCOPE ContLineLoc* TclContinuationsGet(Tcl_Obj *objPtr);
MODULE_SCOPE void	TclContinuationsCopy(Tcl_Obj *objPtr,
			    Tcl_Obj *originObjPtr);
MODULE_SCOPE int	TclConvertElement(CONST char *src, int length,
			    char *dst, int flags);
MODULE_SCOPE void	TclDeleteNamespaceVars(Namespace *nsPtr);
/* TIP #280 - Modified token based evulation, with line information. */
MODULE_SCOPE int	TclEvalEx(Tcl_Interp *interp, const char *script,
			    int numBytes, int flags, int line,
			    int *clNextOuter, CONST char *outerScript);
MODULE_SCOPE int	TclFileAttrsCmd(Tcl_Interp *interp,
			    int objc, Tcl_Obj *const objv[]);
2762
2763
2764
2765
2766
2767
2768


2769
2770
2771
2772
2773
2774
2775
MODULE_SCOPE size_t	TclpThreadGetStackSize(void);
MODULE_SCOPE void	TclRememberCondition(Tcl_Condition *mutex);
MODULE_SCOPE void	TclRememberJoinableThread(Tcl_ThreadId id);
MODULE_SCOPE void	TclRememberMutex(Tcl_Mutex *mutex);
MODULE_SCOPE void	TclRemoveScriptLimitCallbacks(Tcl_Interp *interp);
MODULE_SCOPE int	TclReToGlob(Tcl_Interp *interp, const char *reStr,
			    int reStrLen, Tcl_DString *dsPtr, int *flagsPtr);


MODULE_SCOPE void	TclSetBgErrorHandler(Tcl_Interp *interp,
			    Tcl_Obj *cmdPrefix);
MODULE_SCOPE void	TclSetBignumIntRep(Tcl_Obj *objPtr,
			    mp_int *bignumValue);
MODULE_SCOPE void	TclSetCmdNameObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
			    Command *cmdPtr);
MODULE_SCOPE void	TclSetProcessGlobalValue(ProcessGlobalValue *pgvPtr,







>
>







2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
MODULE_SCOPE size_t	TclpThreadGetStackSize(void);
MODULE_SCOPE void	TclRememberCondition(Tcl_Condition *mutex);
MODULE_SCOPE void	TclRememberJoinableThread(Tcl_ThreadId id);
MODULE_SCOPE void	TclRememberMutex(Tcl_Mutex *mutex);
MODULE_SCOPE void	TclRemoveScriptLimitCallbacks(Tcl_Interp *interp);
MODULE_SCOPE int	TclReToGlob(Tcl_Interp *interp, const char *reStr,
			    int reStrLen, Tcl_DString *dsPtr, int *flagsPtr);
MODULE_SCOPE int	TclScanElement(CONST char *string, int length,
			    int *flagPtr);
MODULE_SCOPE void	TclSetBgErrorHandler(Tcl_Interp *interp,
			    Tcl_Obj *cmdPrefix);
MODULE_SCOPE void	TclSetBignumIntRep(Tcl_Obj *objPtr,
			    mp_int *bignumValue);
MODULE_SCOPE void	TclSetCmdNameObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
			    Command *cmdPtr);
MODULE_SCOPE void	TclSetProcessGlobalValue(ProcessGlobalValue *pgvPtr,

Changes to generic/tclListObj.c.

1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851

1852
1853
1854
1855

1856










1857
1858
1859
1860
1861
1862
1863


1864
1865
1866
1867
1868
1869

1870
1871
1872
1873
1874
1875

1876
1877
1878
1879


1880

1881
1882
1883
1884
1885

1886
1887
1888

1889
1890
1891
1892
1893
1894


1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
 */

static void
UpdateStringOfList(
    Tcl_Obj *listPtr)		/* List object with string rep to update. */
{
#   define LOCAL_SIZE 20
    int localFlags[LOCAL_SIZE], *flagPtr;
    List *listRepPtr = (List *) listPtr->internalRep.twoPtrValue.ptr1;
    int numElems = listRepPtr->elemCount;
    register int i;
    char *elem, *dst;
    int length;
    Tcl_Obj **elemPtrs;


    /*
     * Convert each element of the list to string form and then convert it to
     * proper list element form, adding it to the result buffer.

     */











    /*
     * Pass 1: estimate space, gather flags.
     */

    if (numElems <= LOCAL_SIZE) {
	flagPtr = localFlags;


    } else {
	flagPtr = (int *) ckalloc((unsigned) numElems * sizeof(int));
    }
    listPtr->length = 1;
    elemPtrs = &listRepPtr->elements;
    for (i = 0; i < numElems; i++) {

	elem = TclGetStringFromObj(elemPtrs[i], &length);
	listPtr->length += Tcl_ScanCountedElement(elem, length, flagPtr+i)+1;

	/*
	 * Check for continued sanity. [Bug 1267380]
	 */


	if (listPtr->length < 1) {
	    Tcl_Panic("string representation size exceeds sane bounds");
	}


    }


    /*
     * Pass 2: copy into string rep buffer.
     */


    listPtr->bytes = ckalloc((unsigned) listPtr->length);
    dst = listPtr->bytes;
    for (i = 0; i < numElems; i++) {

	elem = TclGetStringFromObj(elemPtrs[i], &length);
	dst += Tcl_ConvertCountedElement(elem, length, dst,
		flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH));
	*dst = ' ';
	dst++;
    }


    if (flagPtr != localFlags) {
	ckfree((char *) flagPtr);
    }
    if (dst == listPtr->bytes) {
	*dst = 0;
    } else {
	dst--;
	*dst = 0;
    }
    listPtr->length = dst - listPtr->bytes;

    /*
     * Mark the list as being canonical; although it has 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;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78







|


|

<

>


|
|
>

>
>
>
>
>
>
>
>
>
>







>
>



<


>

|
|
<
<
<
>
|
<
<
|
>
>

>





>
|


>

|
<
|
<

>
>



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849

1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879

1880
1881
1882
1883
1884
1885



1886
1887


1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904

1905

1906
1907
1908
1909
1910
1911















1912
1913
1914
1915
1916
1917
1918
 */

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 = (List *) listPtr->internalRep.twoPtrValue.ptr1;
    int numElems = listRepPtr->elemCount;
    int i, length, bytesNeeded = 0;
    char *elem, *dst;

    Tcl_Obj **elemPtrs;
    const int maxFlags = UINT_MAX / sizeof(int);

    /*
     * 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 if (numElems > maxFlags) {
	Tcl_Panic("UpdateStringOfList: size requirement exceeds limits");
    } else {
	flagPtr = (int *) ckalloc((unsigned) 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("UpdateStringOfList: size requirement exceeds limits");
	}


    }
    if (bytesNeeded > INT_MAX - numElems + 1) {
	Tcl_Panic("UpdateStringOfList: size requirement exceeds limits");
    }
    bytesNeeded += numElems;

    /*
     * Pass 2: copy into string rep buffer.
     */

    listPtr->length = bytesNeeded - 1;
    listPtr->bytes = ckalloc((unsigned) 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((char *) flagPtr);
    }















}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78

Changes to generic/tclUtil.c.

640
641
642
643
644
645
646









647
648
649
650
651
652
653
654
655

656


657
658
659
660
661
662
663
664
665
666
667
668

669
670

671
672
673

674
675
676
677
678
679
680
681
682
683

684
685
686
687
688

689
690
691
692
693
694
695
696
697

698
699
700
701
702
703
704
705



706
707
708
709
710
711
712
713
714




715
716
717




718
719
720

721
722










723
724
725
726
727
728
729
730
731
732




733
734





735

736


737


738


739
740
741

742





743

744
745


746


747

















748
749










750

751












752
753

754


755



756
757
758
759
760
761
762
763
764
 *	needed by Tcl_ConvertCountedElement when doing the actual conversion.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */










int
Tcl_ScanCountedElement(
    CONST char *string,		/* String to convert to Tcl list element. */
    int length,			/* Number of bytes in string, or -1. */
    int *flagPtr)		/* Where to store information to guide
				 * Tcl_ConvertElement. */
{
    int flags, nestingLevel;

    register CONST char *p, *lastChar;



    /*
     * This function and Tcl_ConvertElement together do two things:
     *
     * 1. They produce a proper list, one that will yield back the argument
     *	  strings when evaluated or when disassembled with Tcl_SplitList. This
     *	  is the most important thing.
     *
     * 2. They try to produce legible output, which means minimizing the use
     *	  of backslashes (using braces instead). However, there are some
     *	  situations where backslashes must be used (e.g. an element like
     *	  "{abc": the leading brace will have to be backslashed. For each

     *	  element, one of three things must be done:
     *

     * 	  (a) Use the element as-is (it doesn't contain any special
     *	      characters). This is the most desirable option.
     *

     *	  (b) Enclose the element in braces, but leave the contents alone.
     *	      This happens if the element contains embedded space, or if it
     *	      contains characters with special interpretation ($, [, ;, or \),
     *	      or if it starts with a brace or double-quote, or if there are no
     *	      characters in the element.
     *
     *	  (c) Don't enclose the element in braces, but add backslashes to
     *	      prevent special interpretation of special characters. This is a
     *	      last resort used when the argument would normally fall under
     *	      case (b) but contains unmatched braces. It also occurs if the

     *	      last character of the argument is a backslash or if the element
     *	      contains a backslash followed by newline.
     *
     * The function figures out how many bytes will be needed to store the
     * result (actually, it overestimates). It also collects information about

     * the element in the form of a flags word.
     *
     * Note: list elements produced by this function and
     * Tcl_ConvertCountedElement must have the property that they can be
     * enclosing in curly braces to make sub-lists. This means, for example,
     * that we must not leave unmatched curly braces in the resulting list
     * element. This property is necessary in order for functions like
     * Tcl_DStringStartSublist to work.
     */


    nestingLevel = 0;
    flags = 0;
    if (string == NULL) {
	string = "";
    }
    if (length == -1) {
	length = strlen(string);



    }
    lastChar = string + length;
    p = string;
    if ((p == lastChar) || (*p == '{') || (*p == '"')) {
	flags |= USE_BRACES;
    }
    for (; p < lastChar; p++) {
	switch (*p) {
	case '{':




	    nestingLevel++;
	    break;
	case '}':




	    nestingLevel--;
	    if (nestingLevel < 0) {
		flags |= TCL_DONT_USE_BRACES;

	    }
	    break;










	case '[':
	case '$':
	case ';':
	case ' ':
	case '\f':
	case '\n':
	case '\r':
	case '\t':
	case '\v':
	    flags |= USE_BRACES;




	    break;
	case '\\':





	    if ((p+1 == lastChar) || (p[1] == '\n')) {

		flags = TCL_DONT_USE_BRACES;


	    } else {


		int size;



		Tcl_UtfBackslash(p, &size, NULL);
		p += size-1;

		flags |= USE_BRACES;





	    }

	    break;
	}


    }


    if (nestingLevel != 0) {

















	flags = TCL_DONT_USE_BRACES;
    }










    *flagPtr = flags;














    /*
     * Allow enough space to backslash every character plus leave two spaces

     * for braces.


     */




    return 2*(p-string) + 2;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ConvertElement --
 *







>
>
>
>
>
>
>
>
>








|
>
|
>
>
|
<
<
|
<
|
|
|
|
|
|
<
>
|
<
>
|
|
<
>
|
|
|
<
|
<
|
<
<
<
>
<
<
|
<
<
>
|
<
<
<
<
<
<
<
<
>
|
<
<
<
<
|
|
|
>
>
>

<
<
<
<
|
|


>
>
>
>



>
>
>
>


<
>


>
>
>
>
>
>
>
>
>
>









|
>
>
>
>


>
>
>
>
>
|
>
|
>
>
|
>
>
|
>
>
|
<
|
>
|
>
>
>
>
>

>


>
>

>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
|
>
>
|
>
>
>
|
|







640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669


670

671
672
673
674
675
676

677
678

679
680
681

682
683
684
685

686

687



688


689


690
691








692
693




694
695
696
697
698
699
700




701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717

718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763

764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
 *	needed by Tcl_ConvertCountedElement when doing the actual conversion.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#define COMPAT 1

#define CONVERT_NONE	0
#define CONVERT_BRACE	2
#define CONVERT_ESCAPE	4
#define CONVERT_SAFE	16
#define CONVERT_MASK	(CONVERT_BRACE | CONVERT_ESCAPE)


int
Tcl_ScanCountedElement(
    CONST char *string,		/* String to convert to Tcl list element. */
    int length,			/* Number of bytes in string, or -1. */
    int *flagPtr)		/* Where to store information to guide
				 * Tcl_ConvertElement. */
{
    int flags = CONVERT_SAFE;
    int numBytes = TclScanElement(string, length, &flags);

    *flagPtr = flags;
    return numBytes;
}




int
TclScanElement(
    CONST char *string,		/* String to convert to Tcl list element. */
    int length,			/* Number of bytes in string, or -1. */
    int *flagPtr)		/* Where to store information to guide
				 * Tcl_ConvertElement. */

{
    CONST char *p = string;

    int nestingLevel = 0;
    int forbidNone = 0;
    int requireEscape = 0;

    int extra = 0;
    int bytesNeeded;
#if COMPAT
    int preferEscape = 0;

    int preferBrace = 0;

    int braceCount = 0;



#endif


    


    if ((p == NULL) || (length == 0)) {
	*flagPtr = CONVERT_BRACE;








	return 2;
    }





    if ((*p == '{') || (*p == '"') || ((*p == '\0') && (length == -1))) {
	forbidNone = 1;
#if COMPAT
	preferBrace = 1;
#endif
    }





    while (length) {
	switch (*p) {
	case '{':
#if COMPAT
	    braceCount++;
#endif
	    extra++;
	    nestingLevel++;
	    break;
	case '}':
#if COMPAT
	    braceCount++;
#endif
	    extra++;
	    nestingLevel--;
	    if (nestingLevel < 0) {

		requireEscape = 1;
	    }
	    break;
	case ']':
	case '"':
#if COMPAT
	    forbidNone = 1;
	    extra++;
	    preferEscape = 1;
	    break;
#else
	    /* FLOW THROUGH */
#endif
	case '[':
	case '$':
	case ';':
	case ' ':
	case '\f':
	case '\n':
	case '\r':
	case '\t':
	case '\v':
	    forbidNone = 1;
	    extra++;
#if COMPAT
	    preferBrace = 1;
#endif
	    break;
	case '\\':
	    extra++;
	    if ((length == 1) || ((length == -1) && (p[1] == '\0'))) {
		requireEscape = 1;
		break;
	    }
	    if (p[1] == '\n') {
		extra++;
		requireEscape = 1;
		length -= (length > 0);
		p++;
		break;
	    }
	    if ((p[1] == '{') || (p[1] == '}')) {
		extra++;
		length -= (length > 0);
		p++;
	    }

	    forbidNone = 1;
#if COMPAT
	    preferBrace = 1;
#endif
	    break;
	case '\0':
	    if (length == -1) {
		goto endOfString;
	    }
	    /* TODO: Panic on improper encoding? */
	    break;
	}
	length -= (length > 0);
	p++;
    }

    endOfString:
    if (nestingLevel != 0) {
	requireEscape = 1;
    }

    bytesNeeded = p - string;

    if (requireEscape) {
	bytesNeeded += extra;
	if ((*string == '#') && !(*flagPtr & TCL_DONT_QUOTE_HASH)) {
	    bytesNeeded++;
	}
	*flagPtr = CONVERT_ESCAPE;
	goto overflowCheck;
    }
    if (*flagPtr & CONVERT_SAFE) {
	if (extra < 2) {
	    extra = 2;
	}
	*flagPtr |= TCL_DONT_USE_BRACES;
    }
    if (forbidNone) {
#if COMPAT
	if (preferEscape && !preferBrace) {
	    bytesNeeded += (extra - braceCount);
	    if ((*string == '#') && !(*flagPtr & TCL_DONT_QUOTE_HASH)) {
		bytesNeeded++;
	    }
	    if (*flagPtr & TCL_DONT_USE_BRACES) {
		bytesNeeded += braceCount;
	    }
	    *flagPtr = CONVERT_MASK;
	    goto overflowCheck;
	}
#endif
	if (*flagPtr & TCL_DONT_USE_BRACES) {
	    bytesNeeded += extra;
	    if ((*string == '#') && !(*flagPtr & TCL_DONT_QUOTE_HASH)) {
		bytesNeeded++;
	    }
	} else {
	    bytesNeeded += 2;
	}
	*flagPtr = CONVERT_BRACE;
	goto overflowCheck;
    }


    if ((*string == '#') && !(*flagPtr & TCL_DONT_QUOTE_HASH)) {
	bytesNeeded += 2;
    }
    *flagPtr = CONVERT_NONE;

    overflowCheck:
    if (bytesNeeded < 0) {
	Tcl_Panic("TclScanElement: string length overflow");
    }
    return bytesNeeded;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ConvertElement --
 *
811
812
813
814
815
816
817




818

819



820

821
822
823
824
825
826

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857



858






























859

860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884

885

886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916



917









918
919
920
921
922
923
924
925
926
927
928
929
930
int
Tcl_ConvertCountedElement(
    register CONST char *src,	/* Source information for list element. */
    int length,			/* Number of bytes in src, or -1. */
    char *dst,			/* Place to put list-ified element. */
    int flags)			/* Flags produced by Tcl_ScanElement. */
{




    register char *p = dst;

    register CONST char *lastChar;





    /*
     * See the comment block at the beginning of the Tcl_ScanElement code for
     * details of how this works.
     */

    if (src && length == -1) {

	length = strlen(src);
    }
    if ((src == NULL) || (length == 0)) {
	p[0] = '{';
	p[1] = '}';
	p[2] = 0;
	return 2;
    }
    lastChar = src + length;
    if ((*src == '#') && !(flags & TCL_DONT_QUOTE_HASH)) {
	flags |= USE_BRACES;
    }
    if ((flags & USE_BRACES) && !(flags & TCL_DONT_USE_BRACES)) {
	*p = '{';
	p++;
	for (; src != lastChar; src++, p++) {
	    *p = *src;
	}
	*p = '}';
	p++;
    } else {
	if ((*src == '#') && !(flags & TCL_DONT_QUOTE_HASH)) {
	    /*
	     * Leading '#' could be seen by [eval] as the start of a comment,
	     * if on the first element of a list, so quote it.
	     */

	    p[0] = '\\';
	    p[1] = '#';
	    p += 2;
	    src++;



	}






























	for (; src != lastChar; src++) {

	    switch (*src) {
	    case ']':
	    case '[':
	    case '$':
	    case ';':
	    case ' ':
	    case '\\':
	    case '"':
		*p = '\\';
		p++;
		break;
	    case '{':
	    case '}':
		/*
		 * It may not seem necessary to backslash braces, but it is.
		 * The reason for this is that the resulting list element may
		 * actually be an element of a sub-list enclosed in braces
		 * (e.g. if Tcl_DStringStartSublist has been invoked), so
		 * there may be a brace mismatch if the braces aren't
		 * backslashed.
		 */

		if (flags & TCL_DONT_USE_BRACES) {
		    *p = '\\';
		    p++;

		}

		break;
	    case '\f':
		*p = '\\';
		p++;
		*p = 'f';
		p++;
		continue;
	    case '\n':
		*p = '\\';
		p++;
		*p = 'n';
		p++;
		continue;
	    case '\r':
		*p = '\\';
		p++;
		*p = 'r';
		p++;
		continue;
	    case '\t':
		*p = '\\';
		p++;
		*p = 't';
		p++;
		continue;
	    case '\v':
		*p = '\\';
		p++;
		*p = 'v';
		p++;
		continue;



	    }









	    *p = *src;
	    p++;
	}
    }
    *p = '\0';
    return p-dst;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Merge --
 *







>
>
>
>
|
>
|
>
>
>
|
>
|
<
<
<

<
>
|


|
<
|
<
<
<
<
|

<
<
<
<
<
<
<
<
<
|
<
<
<
<
|




>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
|
<
<
|
|
|
|
>
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>

>
>
>
>
>
>
>
>
>
|
|
|
<
<
|







890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909



910

911
912
913
914
915

916




917
918









919




920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973





974


975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028


1029
1030
1031
1032
1033
1034
1035
1036
int
Tcl_ConvertCountedElement(
    register CONST char *src,	/* Source information for list element. */
    int length,			/* Number of bytes in src, or -1. */
    char *dst,			/* Place to put list-ified element. */
    int flags)			/* Flags produced by Tcl_ScanElement. */
{
    int numBytes = TclConvertElement(src, length, dst, flags);
    dst[numBytes] = '\0';
    return numBytes;
}

int TclConvertElement(
    register CONST char *src,	/* Source information for list element. */
    int length,			/* Number of bytes in src, or -1. */
    char *dst,			/* Place to put list-ified element. */
    int flags)			/* Flags produced by Tcl_ScanElement. */
{
    int conversion = flags & CONVERT_MASK;
    char *p = dst;





    if ((flags & TCL_DONT_USE_BRACES) && (conversion & CONVERT_BRACE)) {
	conversion = CONVERT_ESCAPE;
    }
    if ((src == NULL) || (length == 0)) {
	src = tclEmptyStringRep;

	length = 0;




	conversion = CONVERT_BRACE;
    }









    if ((*src == '#') && !(flags & TCL_DONT_QUOTE_HASH)) {




	if (conversion == CONVERT_ESCAPE) {
	    p[0] = '\\';
	    p[1] = '#';
	    p += 2;
	    src++;
	    length--;
	} else {
	    conversion = CONVERT_BRACE;
	}
    }
    if (conversion == CONVERT_NONE) {
	if (length == -1) {
	    /* TODO: INT_MAX overflow? */
	    while (*src) {
		*p++ = *src++;
	    }
	    return p - dst;
	} else {
	    memcpy(dst, src, length);
	    return length;
	}
    }
    if (conversion == CONVERT_BRACE) {
	*p = '{';
	p++;
	if (length == -1) {
	    /* TODO: INT_MAX overflow? */
	    while (*src) {
		*p++ = *src++;
	    }
	} else {
	    memcpy(p, src, length);
	    p += length;
	}
	*p = '}';
	p++;
	return p - dst;
    }
    /* conversion == CONVERT_ESCAPE or CONVERT_MASK */

    for ( ; length; src++, length -= (length > 0)) {
	switch (*src) {
	case ']':
	case '[':
	case '$':
	case ';':
	case ' ':
	case '\\':
	case '"':
	    *p = '\\';
	    p++;
	    break;
	case '{':
	case '}':





#if COMPAT


	    if (conversion == CONVERT_ESCAPE) {
#endif
		*p = '\\';
		p++;
#if COMPAT
	    }
#endif
	    break;
	case '\f':
	    *p = '\\';
	    p++;
	    *p = 'f';
	    p++;
	    continue;
	case '\n':
	    *p = '\\';
	    p++;
	    *p = 'n';
	    p++;
	    continue;
	case '\r':
	    *p = '\\';
	    p++;
	    *p = 'r';
	    p++;
	    continue;
	case '\t':
	    *p = '\\';
	    p++;
	    *p = 't';
	    p++;
	    continue;
	case '\v':
	    *p = '\\';
	    p++;
	    *p = 'v';
	    p++;
	    continue;
	case '\0':
	    if (length == -1) {
		return p - dst;
	    }
	    /* 
	     * If we reach this point, there's an embedded NULL in the
	     * string range being processed, which should not happen when
	     * the encoding rules for Tcl strings are properly followed.
	     * If the day ever comes when we stop tolerating such things,
	     * this is where to put the Tcl_Panic().
	     */
	    break;
	}
	*p = *src;
	p++;
    }


    return p - dst;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Merge --
 *
945
946
947
948
949
950
951
952
953
954

955





956



957
958
959
960
961
962
963














964
965
966
967
968

969


970





971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996

char *
Tcl_Merge(
    int argc,			/* How many strings to merge. */
    CONST char * CONST *argv)	/* Array of string values. */
{
#   define LOCAL_SIZE 20
    int localFlags[LOCAL_SIZE], *flagPtr;
    int numChars;
    char *result;

    char *dst;





    int i;




    /*
     * Pass 1: estimate space, gather flags.
     */

    if (argc <= LOCAL_SIZE) {
	flagPtr = localFlags;














    } else {
	flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int));
    }
    numChars = 1;
    for (i = 0; i < argc; i++) {

	numChars += Tcl_ScanElement(argv[i], &flagPtr[i]) + 1;


    }






    /*
     * Pass two: copy into the result area.
     */

    result = (char *) ckalloc((unsigned) numChars);
    dst = result;
    for (i = 0; i < argc; i++) {
	numChars = Tcl_ConvertElement(argv[i], dst,
		flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH));
	dst += numChars;
	*dst = ' ';
	dst++;
    }
    if (dst == result) {
	*dst = 0;
    } else {
	dst[-1] = 0;
    }

    if (flagPtr != localFlags) {
	ckfree((char *) flagPtr);
    }
    return result;
}








|
|
|
>
|
>
>
>
>
>
|
>
>
>







>
>
>
>
>
>
>
>
>
>
>
>
>
>



<

>
|
>
>
|
>
>
>
>
>





|


<
|
|



<
<
<
|
<







1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095

1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114

1115
1116
1117
1118
1119



1120

1121
1122
1123
1124
1125
1126
1127

char *
Tcl_Merge(
    int argc,			/* How many strings to merge. */
    CONST char * CONST *argv)	/* Array of string values. */
{
#   define LOCAL_SIZE 20
    int localFlags[LOCAL_SIZE], *flagPtr = NULL;
    int i, bytesNeeded = 0;
    char *result, *dst;
    const int maxFlags = UINT_MAX / sizeof(int);

    if (argc == 0) {
	/*
	 * Handle empty list case first, so logic of the general case
	 * can be simpler.
	 */
	result = ckalloc(1);
	result[0] = '\0';
	return result;
    }

    /*
     * Pass 1: estimate space, gather flags.
     */

    if (argc <= LOCAL_SIZE) {
	flagPtr = localFlags;
    } else if (argc > maxFlags) {
	/*
	 * We cannot allocate a large enough flag array to format this
	 * list in one pass.  We could imagine converting this routine
	 * to a multi-pass implementation, but for sizeof(int) == 4, 
	 * the limit is a max of 2^30 list elements and since each element
	 * is at least one byte formatted, and requires one byte space
	 * between it and the next one, that a minimum space requirement
	 * of 2^31 bytes, which is already INT_MAX. If we tried to format
	 * a list of > maxFlags elements, we're just going to overflow
	 * the size limits on the formatted string anyway, so just issue
	 * that same panic early.
	 */
	Tcl_Panic("Tcl_Merge: size requirement exceeds limits");
    } else {
	flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int));
    }

    for (i = 0; i < argc; i++) {
	flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 );
	bytesNeeded += TclScanElement(argv[i], -1, &flagPtr[i]);
	if (bytesNeeded < 0) {
	    Tcl_Panic("Tcl_Merge: size requirement exceeds limits");
	}
    }
    if (bytesNeeded > INT_MAX - argc + 1) {
	Tcl_Panic("Tcl_Merge: size requirement exceeds limits");
    }
    bytesNeeded += argc;

    /*
     * Pass two: copy into the result area.
     */

    result = ckalloc((unsigned) bytesNeeded);
    dst = result;
    for (i = 0; i < argc; i++) {

	flagPtr[i] |= ( i ? TCL_DONT_QUOTE_HASH : 0 );
	dst += TclConvertElement(argv[i], -1, dst, flagPtr[i]);
	*dst = ' ';
	dst++;
    }



    dst[-1] = 0;


    if (flagPtr != localFlags) {
	ckfree((char *) flagPtr);
    }
    return result;
}

1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884

1885

1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905

1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927

1928
1929
1930
1931
1932
1933
1934

char *
Tcl_DStringAppendElement(
    Tcl_DString *dsPtr,		/* Structure describing dynamic string. */
    CONST char *element)	/* String to append. Must be
				 * null-terminated. */
{
    int newSize, flags, strSize;
    char *dst;

    strSize = ((element== NULL) ? 0 : strlen(element));
    newSize = Tcl_ScanCountedElement(element, strSize, &flags)

	+ dsPtr->length + 1;


    /*
     * Allocate a larger buffer for the string if the current one isn't large
     * enough. Allocate extra space in the new buffer so that there will be
     * room to grow before we have to allocate again. SPECIAL NOTE: must use
     * memcpy, not strcpy, to copy the string to a larger buffer, since there
     * may be embedded NULLs in the string in some cases.
     */

    if (newSize >= dsPtr->spaceAvl) {
	dsPtr->spaceAvl = newSize * 2;
	if (dsPtr->string == dsPtr->staticSpace) {
	    char *newString = ckalloc((unsigned) dsPtr->spaceAvl);

	    memcpy(newString, dsPtr->string, (size_t) dsPtr->length);
	    dsPtr->string = newString;
	} else {
	    dsPtr->string = (char *) ckrealloc((void *) dsPtr->string,
		    (size_t) dsPtr->spaceAvl);
	}

    }

    /*
     * Convert the new string to a list element and copy it into the buffer at
     * the end, with a space, if needed.
     */

    dst = dsPtr->string + dsPtr->length;
    if (TclNeedSpace(dsPtr->string, dst)) {
	*dst = ' ';
	dst++;
	dsPtr->length++;

	/*
	 * If we need a space to separate this element from preceding stuff,
	 * then this element will not lead a list, and need not have it's
	 * leading '#' quoted.
	 */

	flags |= TCL_DONT_QUOTE_HASH;
    }
    dsPtr->length += Tcl_ConvertCountedElement(element, strSize, dst, flags);

    return dsPtr->string;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_DStringSetLength --







<
|
|
<
<
>
|
>




















>







<
|












|
>







2004
2005
2006
2007
2008
2009
2010

2011
2012


2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043

2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065

char *
Tcl_DStringAppendElement(
    Tcl_DString *dsPtr,		/* Structure describing dynamic string. */
    CONST char *element)	/* String to append. Must be
				 * null-terminated. */
{

    char *dst = dsPtr->string + dsPtr->length;
    int needSpace = TclNeedSpace(dsPtr->string, dst);


    int flags = needSpace ? TCL_DONT_QUOTE_HASH : 0;
    int newSize = dsPtr->length + needSpace
	    + TclScanElement(element, -1, &flags);

    /*
     * Allocate a larger buffer for the string if the current one isn't large
     * enough. Allocate extra space in the new buffer so that there will be
     * room to grow before we have to allocate again. SPECIAL NOTE: must use
     * memcpy, not strcpy, to copy the string to a larger buffer, since there
     * may be embedded NULLs in the string in some cases.
     */

    if (newSize >= dsPtr->spaceAvl) {
	dsPtr->spaceAvl = newSize * 2;
	if (dsPtr->string == dsPtr->staticSpace) {
	    char *newString = ckalloc((unsigned) dsPtr->spaceAvl);

	    memcpy(newString, dsPtr->string, (size_t) dsPtr->length);
	    dsPtr->string = newString;
	} else {
	    dsPtr->string = (char *) ckrealloc((void *) dsPtr->string,
		    (size_t) dsPtr->spaceAvl);
	}
	dst = dsPtr->string + dsPtr->length;
    }

    /*
     * Convert the new string to a list element and copy it into the buffer at
     * the end, with a space, if needed.
     */


    if (needSpace) {
	*dst = ' ';
	dst++;
	dsPtr->length++;

	/*
	 * If we need a space to separate this element from preceding stuff,
	 * then this element will not lead a list, and need not have it's
	 * leading '#' quoted.
	 */

	flags |= TCL_DONT_QUOTE_HASH;
    }
    dsPtr->length += TclConvertElement(element, -1, dst, flags);
    dsPtr->string[dsPtr->length] = '\0';
    return dsPtr->string;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_DStringSetLength --