Tcl Source Code

Check-in [7c5f3a8961]
Login

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

Overview
Comment:[Bug 3285375]: Rewrite Tcl_Concat*() and [string trim*].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7c5f3a896176a014cf63b2a5ed63352ab77dbbc6
User & Date: dgp 2011-04-13 20:37:26
Context
2011-04-14
16:12
More Tcl_Concat* and TclTrim* improvements. check-in: 80ebbf75f2 user: dgp tags: trunk
2011-04-13
20:37
[Bug 3285375]: Rewrite Tcl_Concat*() and [string trim*]. check-in: 7c5f3a8961 user: dgp tags: trunk
20:27
[Bug 3285375]: Rewrite Tcl_Concat*() and [string trim*]. check-in: c48b1de113 user: dgp tags: core-8-5-branch
13:26
fix merge history check-in: 7dbbe17355 user: mig tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ChangeLog.












1
2
3
4
5
6
7











2011-04-13  Miguel Sofer  <[email protected]>

	* generic/tclVar.c: fix for [Bug 2662380], crash caused by
	appending to a variable with a write trace that unsets it.

2011-04-13  Donal K. Fellows  <[email protected]>

>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2011-04-13  Don Porter  <[email protected]>

	* generic/tclUtil.c:	Rewrite of Tcl_Concat*() routines to 
	prevent segfaults on buffer overflow.  Build them out of existing
	primitives already coded to handle overflow properly.  Uses the
	new TclTrim*() routines. [Bug 3285375]

	* generic/tclCmdMZ.c:	New internal utility routines TclTrimLeft()
	* generic/tclInt.h:	and TclTrimRight().  Refactor the
	* generic/tclUtil.c:	[string trim*] implementations to use them.

2011-04-13  Miguel Sofer  <[email protected]>

	* generic/tclVar.c: fix for [Bug 2662380], crash caused by
	appending to a variable with a write trace that unsets it.

2011-04-13  Donal K. Fellows  <[email protected]>

Changes to generic/tclCmdMZ.c.

3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179

3180
3181
3182
3183
3184
3185
3186
static int
StringTrimCmd(
    ClientData dummy,		/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    Tcl_UniChar ch, trim;
    register const char *p, *end;
    const char *check, *checkEnd, *string1, *string2;
    int offset, length1, length2;

    if (objc == 3) {
	string2 = TclGetStringFromObj(objv[2], &length2);
    } else if (objc == 2) {
	string2 = DEFAULT_TRIM_SET;
	length2 = strlen(DEFAULT_TRIM_SET);
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?");
	return TCL_ERROR;
    }
    string1 = TclGetStringFromObj(objv[1], &length1);
    checkEnd = string2 + length2;

    /*
     * The outer loop iterates over the string. The inner loop iterates over
     * the trim characters. The loops terminate as soon as a non-trim
     * character is discovered and string1 is left pointing at the first
     * non-trim character.
     */

    end = string1 + length1;
    for (p = string1; p < end; p += offset) {
	offset = TclUtfToUniChar(p, &ch);

	for (check = string2; ; ) {
	    if (check >= checkEnd) {
		p = end;
		break;
	    }
	    check += TclUtfToUniChar(check, &trim);
	    if (ch == trim) {
		length1 -= offset;
		string1 += offset;
		break;
	    }
	}
    }

    /*
     * The outer loop iterates over the string. The inner loop iterates over
     * the trim characters. The loops terminate as soon as a non-trim
     * character is discovered and length1 marks the last non-trim character.
     */

    end = string1;
    for (p = string1 + length1; p > end; ) {
	p = Tcl_UtfPrev(p, string1);
	offset = TclUtfToUniChar(p, &ch);
	check = string2;
	while (1) {
	    if (check >= checkEnd) {
		p = end;
		break;
	    }
	    check += TclUtfToUniChar(check, &trim);
	    if (ch == trim) {
		length1 -= offset;
		break;
	    }
	}
    }

    Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1));

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * StringTrimLCmd --







<
<
|
|











<

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







3106
3107
3108
3109
3110
3111
3112


3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125

3126






3127



3128




3129

































3130
3131
3132
3133
3134
3135
3136
3137
3138
static int
StringTrimCmd(
    ClientData dummy,		/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{


    const char *string1, *string2;
    int triml, trimr, length1, length2;

    if (objc == 3) {
	string2 = TclGetStringFromObj(objv[2], &length2);
    } else if (objc == 2) {
	string2 = DEFAULT_TRIM_SET;
	length2 = strlen(DEFAULT_TRIM_SET);
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?");
	return TCL_ERROR;
    }
    string1 = TclGetStringFromObj(objv[1], &length1);








    triml = TclTrimLeft(string1, length1, string2, length2);



    trimr = TclTrimRight(string1 + triml, length1 - triml, string2, length2);






































    Tcl_SetObjResult(interp,
	    Tcl_NewStringObj(string1 + triml, length1 - triml - trimr));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * StringTrimLCmd --
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
static int
StringTrimLCmd(
    ClientData dummy,		/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    Tcl_UniChar ch, trim;
    register const char *p, *end;
    const char *check, *checkEnd, *string1, *string2;
    int offset, length1, length2;

    if (objc == 3) {
	string2 = TclGetStringFromObj(objv[2], &length2);
    } else if (objc == 2) {
	string2 = DEFAULT_TRIM_SET;
	length2 = strlen(DEFAULT_TRIM_SET);
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?");
	return TCL_ERROR;
    }
    string1 = TclGetStringFromObj(objv[1], &length1);
    checkEnd = string2 + length2;

    /*
     * The outer loop iterates over the string. The inner loop iterates over
     * the trim characters. The loops terminate as soon as a non-trim
     * character is discovered and string1 is left pointing at the first
     * non-trim character.
     */

    end = string1 + length1;
    for (p = string1; p < end; p += offset) {
	offset = TclUtfToUniChar(p, &ch);

	for (check = string2; ; ) {
	    if (check >= checkEnd) {
		p = end;
		break;
	    }
	    check += TclUtfToUniChar(check, &trim);
	    if (ch == trim) {
		length1 -= offset;
		string1 += offset;
		break;
	    }
	}
    }

    Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * StringTrimRCmd --







<
<
|
|











<

<
<
<
<
<
<
|
<
<
<

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







3154
3155
3156
3157
3158
3159
3160


3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173

3174






3175



3176














3177
3178
3179
3180
3181
3182
3183
3184
static int
StringTrimLCmd(
    ClientData dummy,		/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{


    const char *string1, *string2;
    int trim, length1, length2;

    if (objc == 3) {
	string2 = TclGetStringFromObj(objv[2], &length2);
    } else if (objc == 2) {
	string2 = DEFAULT_TRIM_SET;
	length2 = strlen(DEFAULT_TRIM_SET);
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?");
	return TCL_ERROR;
    }
    string1 = TclGetStringFromObj(objv[1], &length1);








    trim = TclTrimLeft(string1, length1, string2, length2);


















    Tcl_SetObjResult(interp, Tcl_NewStringObj(string1+trim, length1-trim));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * StringTrimRCmd --
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
static int
StringTrimRCmd(
    ClientData dummy,		/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    Tcl_UniChar ch, trim;
    register const char *p, *end;
    const char *check, *checkEnd, *string1, *string2;
    int offset, length1, length2;

    if (objc == 3) {
	string2 = TclGetStringFromObj(objv[2], &length2);
    } else if (objc == 2) {
	string2 = DEFAULT_TRIM_SET;
	length2 = strlen(DEFAULT_TRIM_SET);
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?");
	return TCL_ERROR;
    }
    string1 = TclGetStringFromObj(objv[1], &length1);
    checkEnd = string2 + length2;

    /*
     * The outer loop iterates over the string. The inner loop iterates over
     * the trim characters. The loops terminate as soon as a non-trim
     * character is discovered and length1 marks the last non-trim character.
     */

    end = string1;
    for (p = string1 + length1; p > end; ) {
	p = Tcl_UtfPrev(p, string1);
	offset = TclUtfToUniChar(p, &ch);
	check = string2;
	while (1) {
	    if (check >= checkEnd) {
		p = end;
		break;
	    }
	    check += TclUtfToUniChar(check, &trim);
	    if (ch == trim) {
		length1 -= offset;
		break;
	    }
	}
    }

    Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclInitStringCmd --







<
<
|
|











<

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







3200
3201
3202
3203
3204
3205
3206


3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219

3220





3221









3222








3223
3224
3225
3226
3227
3228
3229
3230
static int
StringTrimRCmd(
    ClientData dummy,		/* Not used. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{


    const char *string1, *string2;
    int trim, length1, length2;

    if (objc == 3) {
	string2 = TclGetStringFromObj(objv[2], &length2);
    } else if (objc == 2) {
	string2 = DEFAULT_TRIM_SET;
	length2 = strlen(DEFAULT_TRIM_SET);
    } else {
	Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?");
	return TCL_ERROR;
    }
    string1 = TclGetStringFromObj(objv[1], &length1);







    trim = TclTrimRight(string1, length1, string2, length2);


















    Tcl_SetObjResult(interp, Tcl_NewStringObj(string1, length1-trim));
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TclInitStringCmd --

Changes to generic/tclInt.h.

3111
3112
3113
3114
3115
3116
3117




3118
3119
3120
3121
3122
3123
3124
			    Tcl_Obj *const opts[], int *flagPtr);
MODULE_SCOPE void	TclSubstParse(Tcl_Interp *interp, const char *bytes,
			    int numBytes, int flags, Tcl_Parse *parsePtr,
			    Tcl_InterpState *statePtr);
MODULE_SCOPE int	TclSubstTokens(Tcl_Interp *interp, Tcl_Token *tokenPtr,
			    int count, int *tokensLeftPtr, int line,
			    int *clNextOuter, const char *outerScript);




MODULE_SCOPE Tcl_Obj *	TclpNativeToNormalized(ClientData clientData);
MODULE_SCOPE Tcl_Obj *	TclpFilesystemPathType(Tcl_Obj *pathPtr);
MODULE_SCOPE int	TclpDlopen(Tcl_Interp *interp, Tcl_Obj *pathPtr,
			    Tcl_LoadHandle *loadHandle,
			    Tcl_FSUnloadFileProc **unloadProcPtr);
MODULE_SCOPE int	TclpUtime(Tcl_Obj *pathPtr, struct utimbuf *tval);
#ifdef TCL_LOAD_FROM_MEMORY







>
>
>
>







3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
			    Tcl_Obj *const opts[], int *flagPtr);
MODULE_SCOPE void	TclSubstParse(Tcl_Interp *interp, const char *bytes,
			    int numBytes, int flags, Tcl_Parse *parsePtr,
			    Tcl_InterpState *statePtr);
MODULE_SCOPE int	TclSubstTokens(Tcl_Interp *interp, Tcl_Token *tokenPtr,
			    int count, int *tokensLeftPtr, int line,
			    int *clNextOuter, const char *outerScript);
MODULE_SCOPE int	TclTrimLeft(const char *bytes, int numBytes,
			    const char *trim, int numTrim);
MODULE_SCOPE int	TclTrimRight(const char *bytes, int numBytes,
			    const char *trim, int numTrim);
MODULE_SCOPE Tcl_Obj *	TclpNativeToNormalized(ClientData clientData);
MODULE_SCOPE Tcl_Obj *	TclpFilesystemPathType(Tcl_Obj *pathPtr);
MODULE_SCOPE int	TclpDlopen(Tcl_Interp *interp, Tcl_Obj *pathPtr,
			    Tcl_LoadHandle *loadHandle,
			    Tcl_FSUnloadFileProc **unloadProcPtr);
MODULE_SCOPE int	TclpUtime(Tcl_Obj *pathPtr, struct utimbuf *tval);
#ifdef TCL_LOAD_FROM_MEMORY

Changes to generic/tclUtil.c.

941
942
943
944
945
946
947








































































































































948
949
950
951
952
953
954
    TclUtfToUniChar(buf, &ch);
    return (char) ch;
}

/*
 *----------------------------------------------------------------------
 *








































































































































 * Tcl_Concat --
 *
 *	Concatenate a set of strings into a single large string.
 *
 * Results:
 *	The return value is dynamically-allocated string containing a
 *	concatenation of all the strings in argv, with spaces between the







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







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
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
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
    TclUtfToUniChar(buf, &ch);
    return (char) ch;
}

/*
 *----------------------------------------------------------------------
 *
 * TclTrimRight --
 *	Takes two counted strings in the Tcl encoding which must both be
 *	null terminated.  Conceptually trims from the right side of the
 *	first string all characters found in the second string.
 *
 * Results:
 *	The number of bytes to be removed from the end of the string.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TclTrimRight(
    const char *bytes,	/* String to be trimmed... */
    int numBytes,	/* ...and its length in bytes */
    const char *trim,	/* String of trim characters... */
    int numTrim)	/* ...and its length in bytes */
{
    const char *p = bytes + numBytes;
    int pInc;

    if ((bytes[numBytes] != '\0') || (trim[numTrim] != '\0')) {
	Tcl_Panic("TclTrimRight works only on null-terminated strings");
    }

    /* Empty strings -> nothing to do */
    if ((numBytes == 0) || (numTrim == 0)) {
	return 0;
    }

    /* Outer loop: iterate over string to be trimmed */
    do {
	Tcl_UniChar ch1;
	const char *q = trim;
	int bytesLeft = numTrim;

	p = Tcl_UtfPrev(p, bytes);
 	pInc = TclUtfToUniChar(p, &ch1);

	/* Inner loop: scan trim string for match to current character */
	do {
	    Tcl_UniChar ch2;
	    int qInc = TclUtfToUniChar(q, &ch2);

	    if (ch1 == ch2) {
		break;
	    }

	    q += qInc;
	    bytesLeft -= qInc;
	} while (bytesLeft);

	if (bytesLeft == 0) {
	    /* No match; trim task done; *p is last non-trimmed char */
	    break;
	}
	pInc = 0;
    } while (p > bytes);

    return numBytes - (p - bytes) - pInc;
}

/*
 *----------------------------------------------------------------------
 *
 * TclTrimLeft --
 *	Takes two counted strings in the Tcl encoding which must both be
 *	null terminated.  Conceptually trims from the left side of the
 *	first string all characters found in the second string.
 *
 * Results:
 *	An integer index into the first string, pointing to the first
 *	character not to be trimmed.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TclTrimLeft(
    const char *bytes,	/* String to be trimmed... */
    int numBytes,	/* ...and its length in bytes */
    const char *trim,	/* String of trim characters... */
    int numTrim)	/* ...and its length in bytes */
{
    const char *p = bytes;

    if ((bytes[numBytes] != '\0') || (trim[numTrim] != '\0')) {
	Tcl_Panic("TclTrimLeft works only on null-terminated strings");
    }

    /* Empty strings -> nothing to do */
    if ((numBytes == 0) || (numTrim == 0)) {
	return 0;
    }

    /* Outer loop: iterate over string to be trimmed */
    do {
	Tcl_UniChar ch1;
	int pInc = TclUtfToUniChar(p, &ch1);
	const char *q = trim;
	int bytesLeft = numTrim;

	/* Inner loop: scan trim string for match to current character */
	do {
	    Tcl_UniChar ch2;
	    int qInc = TclUtfToUniChar(q, &ch2);

	    if (ch1 == ch2) {
		break;
	    }

	    q += qInc;
	    bytesLeft -= qInc;
	} while (bytesLeft);

	if (bytesLeft == 0) {
	    /* No match; trim task done; *p is first non-trimmed char */
	    break;
	}

	p += pInc;
	numBytes -= pInc;
    } while (numBytes);

    return p - bytes;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Concat --
 *
 *	Concatenate a set of strings into a single large string.
 *
 * Results:
 *	The return value is dynamically-allocated string containing a
 *	concatenation of all the strings in argv, with spaces between the
968
969
970
971
972
973
974



975
976
977
978
979
980
981
{
    int totalSize, i;
    char *p;
    char *result;

    for (totalSize = 1, i = 0; i < argc; i++) {
	totalSize += strlen(argv[i]) + 1;



    }
    result = ckalloc(totalSize);
    if (argc == 0) {
	*result = '\0';
	return result;
    }
    for (p = result, i = 0; i < argc; i++) {







>
>
>







1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
{
    int totalSize, i;
    char *p;
    char *result;

    for (totalSize = 1, i = 0; i < argc; i++) {
	totalSize += strlen(argv[i]) + 1;
	if (totalSize <= 0) {
	    Tcl_Panic("Tcl_Concat: max size of Tcl value exceeded");
	}
    }
    result = ckalloc(totalSize);
    if (argc == 0) {
	*result = '\0';
	return result;
    }
    for (p = result, i = 0; i < argc; i++) {
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
 */

Tcl_Obj *
Tcl_ConcatObj(
    int objc,			/* Number of objects to concatenate. */
    Tcl_Obj *const objv[])	/* Array of objects to concatenate. */
{
    int allocSize, finalSize, length, elemLength, i;
    char *p;
    const char *element;
    char *concatStr;
    Tcl_Obj *objPtr, *resPtr;

    /*
     * Check first to see if all the items are of list type or empty. If so,
     * we will concat them together as lists, and return a list object. This
     * is only valid when the lists have no current string representation,
     * since we don't know what the original type was. An original string rep
     * may have lost some whitespace info when converted which could be
     * important.
     */

    for (i = 0;  i < objc;  i++) {
	List *listRepPtr;

	objPtr = objv[i];
	if (objPtr->typePtr != &tclListType) {







|
<
<
<





|
<
<
<







1172
1173
1174
1175
1176
1177
1178
1179



1180
1181
1182
1183
1184
1185



1186
1187
1188
1189
1190
1191
1192
 */

Tcl_Obj *
Tcl_ConcatObj(
    int objc,			/* Number of objects to concatenate. */
    Tcl_Obj *const objv[])	/* Array of objects to concatenate. */
{
    int i, needSpace = 0;



    Tcl_Obj *objPtr, *resPtr;

    /*
     * Check first to see if all the items are of list type or empty. If so,
     * we will concat them together as lists, and return a list object. This
     * is only valid when the lists are in canonical form.



     */

    for (i = 0;  i < objc;  i++) {
	List *listRepPtr;

	objPtr = objv[i];
	if (objPtr->typePtr != &tclListType) {
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113

1114
1115
1116
1117
1118
1119
1120
1121
1122
1123


1124
1125

1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167

1168
1169
1170
1171
1172
1173
1174
1175
1176

1177
1178
1179
1180
1181

1182


1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
    }

    /*
     * Something cannot be determined to be safe, so build the concatenation
     * the slow way, using the string representations.
     */

    allocSize = 0;
    for (i = 0;  i < objc;  i++) {
	int oldAllocSize = allocSize;


	objPtr = objv[i];
	element = TclGetStringFromObj(objPtr, &length);
	if ((element != NULL) && (length > 0)) {
	    allocSize += (length + 1);
	    if (allocSize < oldAllocSize) {
		Tcl_Panic("too much memory required");
	    }
	}
    }


    if (allocSize == 0) {
	allocSize = 1;		/* enough for the NULL byte at end */

    }

    /*
     * Allocate storage for the concatenated result. Note that allocSize is
     * one more than the total number of characters, and so includes room for
     * the terminating NULL byte.
     */

    concatStr = ckalloc(allocSize);

    /*
     * Now concatenate the elements. Clip white space off the front and back
     * to generate a neater result, and ignore any empty elements. Also put a
     * null byte at the end.
     */

    finalSize = 0;
    if (objc == 0) {
	*concatStr = '\0';
    } else {
	p = concatStr;
	for (i = 0;  i < objc;  i++) {
	    objPtr = objv[i];
	    element = TclGetStringFromObj(objPtr, &elemLength);
	    while ((elemLength > 0) && (UCHAR(*element) < 127)
		    && isspace(UCHAR(*element))) { /* INTL: ISO C space. */
		element++;
		elemLength--;
	    }

	    /*
	     * Trim trailing white space. But, be careful not to trim a space
	     * character if it is preceded by a backslash: in this case it
	     * could be significant.
	     */

	    while ((elemLength > 0) && (UCHAR(element[elemLength-1]) < 127)
		    && isspace(UCHAR(element[elemLength-1]))
						/* INTL: ISO C space. */
		    && ((elemLength < 2) || (element[elemLength-2] != '\\'))) {
		elemLength--;
	    }

	    if (elemLength == 0) {
		continue;	/* nothing left of this element */
	    }
	    memcpy(p, element, (size_t) elemLength);
	    p += elemLength;
	    *p = ' ';
	    p++;
	    finalSize += (elemLength + 1);
	}

	if (p != concatStr) {
	    p[-1] = 0;
	    finalSize -= 1;	/* we overwrote the final ' ' */
	} else {
	    *p = 0;

	}


    }

    TclNewObj(objPtr);
    objPtr->bytes = concatStr;
    objPtr->length = finalSize;
    return objPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_StringMatch --
 *







|

|
>
|

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

>
>

<
<
<
<
|







1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250




1251


1252
1253
1254

1255
1256

1257




1258

1259




1260













1261
1262







1263


1264
1265
1266
1267
1268
1269





1270
1271
1272




1273
1274
1275
1276
1277




1278
1279
1280
1281
1282
1283
1284
1285
    }

    /*
     * Something cannot be determined to be safe, so build the concatenation
     * the slow way, using the string representations.
     */

    TclNewObj(resPtr);
    for (i = 0;  i < objc;  i++) {
	int trim, elemLength;
	const char *element;
	
	objPtr = objv[i];
	element = TclGetStringFromObj(objPtr, &elemLength);







	/* Trim away the leading whitespace */
	trim = TclTrimLeft(element, elemLength, " \f\v\r\t\n", 6);
	element += trim;

	elemLength -= trim;


	/*




	 * Trim away the trailing whitespace.  Do not permit trimming

	 * to expose a final backslash character.




	 */














	trim = TclTrimRight(element, elemLength, " \f\v\r\t\n", 6);







	trim -= trim && (element[elemLength - trim - 1] == '\\');


	elemLength -= trim;

	/* If we're left with empty element after trimming, do nothing */
	if (elemLength == 0) {
	    continue;
	}






	/* Append to the result with space if needed */
	if (needSpace) {




	    Tcl_AppendToObj(resPtr, " ", 1);
	}
	Tcl_AppendToObj(resPtr, element, elemLength);
	needSpace = 1;
    }




    return resPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_StringMatch --
 *