Tcl Source Code

Check-in [120749f8c4]
Login

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

Overview
Comment:Merge 9.0
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | bug-7c2716733a
Files: files | file ages | folders
SHA3-256: 120749f8c4ff2d79aa9c58b070eca71ed213b1fe799faf7efc557af3938a9ae9
User & Date: apnadkarni 2025-06-25 03:29:27.806
Context
2025-06-25
16:23
Fix [7c2716733a] - use after free on Windows pipe handles check-in: a9351d85f3 user: apnadkarni tags: core-9-0-branch
03:29
Merge 9.0 Closed-Leaf check-in: 120749f8c4 user: apnadkarni tags: bug-7c2716733a
2025-06-24
16:23
[ecf35c7120] Correct nested handling of return option -options check-in: a9446eea4d user: dgp tags: core-9-0-branch
2025-06-15
10:15
Fat fingers in constraints check-in: c1f1232e47 user: apnadkarni tags: bug-7c2716733a
Changes
Unified Diff Ignore Whitespace Patch
Changes to README.md.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 9. [Tracking Development](#watch)
 10. [Thank You](#thanks)

## <a id="intro">1.</a> Introduction
Tcl provides a powerful platform for creating integration applications that
tie together diverse applications, protocols, devices, and frameworks.
When paired with the Tk toolkit, Tcl provides the fastest and most powerful
way to create GUI applications that run on PCs, Unix, and Mac OS X.
Tcl can also be used for a variety of web-related tasks and for creating
powerful command languages for applications.

Tcl is maintained, enhanced, and distributed freely by the Tcl community.
Source code development and tracking of bug reports and feature requests
take place at [core.tcl-lang.org](https://core.tcl-lang.org/).
Tcl/Tk release and mailing list services are [hosted by







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 9. [Tracking Development](#watch)
 10. [Thank You](#thanks)

## <a id="intro">1.</a> Introduction
Tcl provides a powerful platform for creating integration applications that
tie together diverse applications, protocols, devices, and frameworks.
When paired with the Tk toolkit, Tcl provides the fastest and most powerful
way to create GUI applications that run on PCs, Unix, and macOS.
Tcl can also be used for a variety of web-related tasks and for creating
powerful command languages for applications.

Tcl is maintained, enhanced, and distributed freely by the Tcl community.
Source code development and tracking of bug reports and feature requests
take place at [core.tcl-lang.org](https://core.tcl-lang.org/).
Tcl/Tk release and mailing list services are [hosted by
Changes to doc/OpenFileChnl.3.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'\"
'\" Copyright (c) 1996-1997 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH Tcl_OpenFileChannel 3 8.3 Tcl "Tcl Library Procedures"
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
Tcl_OpenFileChannel, Tcl_OpenCommandChannel, Tcl_MakeFileChannel, Tcl_GetChannel, Tcl_GetChannelNames, Tcl_GetChannelNamesEx, Tcl_RegisterChannel, Tcl_UnregisterChannel, Tcl_DetachChannel, Tcl_IsStandardChannel, Tcl_Close, Tcl_ReadChars, Tcl_Read, Tcl_GetsObj, Tcl_Gets, Tcl_WriteObj, Tcl_WriteChars, Tcl_Write, Tcl_Flush, Tcl_Seek, Tcl_Tell, Tcl_TruncateChannel, Tcl_GetChannelOption, Tcl_SetChannelOption, Tcl_Eof, Tcl_InputBlocked, Tcl_InputBuffered, Tcl_OutputBuffered, Tcl_Ungets, Tcl_ReadRaw, Tcl_WriteRaw \- buffered I/O facilities using channels
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
Tcl_Channel
\fBTcl_OpenFileChannel\fR(\fIinterp, fileName, mode, permissions\fR)
.sp











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'\"
'\" Copyright (c) 1996-1997 Sun Microsystems, Inc.
'\"
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
.TH Tcl_OpenFileChannel 3 8.3 Tcl "Tcl Library Procedures"
.so man.macros
.BS
'\" Note:  do not modify the .SH NAME line immediately below!
.SH NAME
Tcl_OpenFileChannel, Tcl_OpenCommandChannel, Tcl_MakeFileChannel, Tcl_GetChannel, Tcl_GetChannelNames, Tcl_GetChannelNamesEx, Tcl_RegisterChannel, Tcl_UnregisterChannel, Tcl_DetachChannel, Tcl_IsStandardChannel, Tcl_Close, Tcl_CloseEx, Tcl_ReadChars, Tcl_Read, Tcl_GetsObj, Tcl_Gets, Tcl_WriteObj, Tcl_WriteChars, Tcl_Write, Tcl_Flush, Tcl_Seek, Tcl_Tell, Tcl_TruncateChannel, Tcl_GetChannelOption, Tcl_SetChannelOption, Tcl_Eof, Tcl_InputBlocked, Tcl_InputBuffered, Tcl_OutputBuffered, Tcl_Ungets, Tcl_ReadRaw, Tcl_WriteRaw \- buffered I/O facilities using channels
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
.sp
Tcl_Channel
\fBTcl_OpenFileChannel\fR(\fIinterp, fileName, mode, permissions\fR)
.sp
42
43
44
45
46
47
48



49
50
51
52
53
54
55
.sp
int
\fBTcl_IsStandardChannel\fR(\fIchannel\fR)
.sp
int
\fBTcl_Close\fR(\fIinterp, channel\fR)
.sp



Tcl_Size
\fBTcl_ReadChars\fR(\fIchannel, readObjPtr, charsToRead, appendFlag\fR)
.sp
Tcl_Size
\fBTcl_Read\fR(\fIchannel, readBuf, bytesToRead\fR)
.sp
Tcl_Size







>
>
>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
.sp
int
\fBTcl_IsStandardChannel\fR(\fIchannel\fR)
.sp
int
\fBTcl_Close\fR(\fIinterp, channel\fR)
.sp
int
\fBTcl_CloseEx\fR(\fIinterp, channel, closeFlags\fR)
.sp
Tcl_Size
\fBTcl_ReadChars\fR(\fIchannel, readObjPtr, charsToRead, appendFlag\fR)
.sp
Tcl_Size
\fBTcl_Read\fR(\fIchannel, readBuf, bytesToRead\fR)
.sp
Tcl_Size
153
154
155
156
157
158
159




160
161
162
163
164
165
166
.AP Tcl_Obj *readObjPtr in/out
A pointer to a Tcl value in which to store the characters read from the
channel.
.AP Tcl_Size charsToRead in
The number of characters to read from the channel.  If the channel's encoding
is \fBbinary\fR, this is equivalent to the number of bytes to read from the
channel.




.AP int appendFlag in
If non-zero, data read from the channel will be appended to the value.
Otherwise, the data will replace the existing contents of the value.
.AP char *readBuf out
A buffer in which to store the bytes read from the channel.
.AP Tcl_Size bytesToRead in
The number of bytes to read from the channel.  The buffer \fIreadBuf\fR must







>
>
>
>







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
.AP Tcl_Obj *readObjPtr in/out
A pointer to a Tcl value in which to store the characters read from the
channel.
.AP Tcl_Size charsToRead in
The number of characters to read from the channel.  If the channel's encoding
is \fBbinary\fR, this is equivalent to the number of bytes to read from the
channel.
.AP int closeFlags in
If \fB0\fR, the channel is closed in both directions. If \fBTCL_CLOSE_READ\fR,
the channel is only closed for reading. If \fBTCL_CLOSE_WRITE\fR, the channel
is only closed for writing. These flags must not be combined.
.AP int appendFlag in
If non-zero, data read from the channel will be appended to the value.
Otherwise, the data will replace the existing contents of the value.
.AP char *readBuf out
A buffer in which to store the bytes read from the channel.
.AP Tcl_Size bytesToRead in
The number of bytes to read from the channel.  The buffer \fIreadBuf\fR must
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
.PP
\fBTcl_IsStandardChannel\fR tests whether a channel is one of the
three standard channels, \fBstdin\fR, \fBstdout\fR or \fBstderr\fR.
If so, it returns 1, otherwise 0.
.PP
No attempt is made to check whether the given channel or the standard
channels are initialized or otherwise valid.
.SH TCL_CLOSE
.PP
\fBTcl_Close\fR destroys the channel \fIchannel\fR, which must denote a
currently open channel. The channel should not be registered in any
interpreter when \fBTcl_Close\fR is called. Buffered output is flushed to
the channel's output device prior to destroying the channel, and any
buffered input is discarded.  If this is a blocking channel, the call does
not return until all buffered data is successfully sent to the channel's
output device.  If this is a nonblocking channel and there is buffered
output that cannot be written without blocking, the call returns
immediately; output is flushed in the background and the channel will be
closed once all of the buffered data has been output.  In this case errors
during flushing are not reported.
.PP







If the channel was closed successfully, \fBTcl_Close\fR returns \fBTCL_OK\fR.
If an error occurs, \fBTcl_Close\fR returns \fBTCL_ERROR\fR and records a
POSIX error code that can be retrieved with \fBTcl_GetErrno\fR.
If the channel is being closed synchronously and an error occurs during
closing of the channel and \fIinterp\fR is not NULL, an error message is
left in the interpreter's result.
.PP
Note that it is not safe to call \fBTcl_Close\fR on a channel that has been
registered using \fBTcl_RegisterChannel\fR; see the documentation for
\fBTcl_RegisterChannel\fR, above, for details. If the channel has ever
been given as the \fBchan\fR argument in a call to
\fBTcl_RegisterChannel\fR, you should instead use
\fBTcl_UnregisterChannel\fR, which will internally call \fBTcl_Close\fR
when all calls to \fBTcl_RegisterChannel\fR have been matched by
corresponding calls to \fBTcl_UnregisterChannel\fR.
.SH "TCL_READCHARS AND TCL_READ"
.PP







|













>
>
>
>
>
>
>
|
|
|




|
|
|







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
.PP
\fBTcl_IsStandardChannel\fR tests whether a channel is one of the
three standard channels, \fBstdin\fR, \fBstdout\fR or \fBstderr\fR.
If so, it returns 1, otherwise 0.
.PP
No attempt is made to check whether the given channel or the standard
channels are initialized or otherwise valid.
.SH "TCL_CLOSE AND TCL_CLOSEEX"
.PP
\fBTcl_Close\fR destroys the channel \fIchannel\fR, which must denote a
currently open channel. The channel should not be registered in any
interpreter when \fBTcl_Close\fR is called. Buffered output is flushed to
the channel's output device prior to destroying the channel, and any
buffered input is discarded.  If this is a blocking channel, the call does
not return until all buffered data is successfully sent to the channel's
output device.  If this is a nonblocking channel and there is buffered
output that cannot be written without blocking, the call returns
immediately; output is flushed in the background and the channel will be
closed once all of the buffered data has been output.  In this case errors
during flushing are not reported.
.PP
\fBTcl_CloseEx\fR allows for both full closing and half-closing of channels
depending on its \fBcloseFlags\fR parameter. See the description of the
parameter above. It is an error to attempt to close the channel for
a direction for which it is not open. The channel is destroyed only when
it has been closed for both reading and writing. Only socket and command
pipe channels support half-closing.
.PP
If the channel was closed successfully, \fBTcl_Close\fR and \fBTcl_CloseEx\fR
return \fBTCL_OK\fR. If an error occurs, they return \fBTCL_ERROR\fR and record
a POSIX error code that can be retrieved with \fBTcl_GetErrno\fR.
If the channel is being closed synchronously and an error occurs during
closing of the channel and \fIinterp\fR is not NULL, an error message is
left in the interpreter's result.
.PP
Note that it is not safe to call the channel closing functions on a channel
that has been registered using \fBTcl_RegisterChannel\fR; see the documentation
for \fBTcl_RegisterChannel\fR, above, for details. If the channel has ever
been given as the \fBchan\fR argument in a call to
\fBTcl_RegisterChannel\fR, you should instead use
\fBTcl_UnregisterChannel\fR, which will internally call \fBTcl_Close\fR
when all calls to \fBTcl_RegisterChannel\fR have been matched by
corresponding calls to \fBTcl_UnregisterChannel\fR.
.SH "TCL_READCHARS AND TCL_READ"
.PP
Changes to generic/tclCmdAH.c.
547
548
549
550
551
552
553

554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571

572
573
574
575
576
577
578
579
580
581
582
583
584
585
586

587
588
589
590
591
592
593
594

595
596
597

598
599
600

601
602
603
604
605
606
607

    if (bytesPtr == NULL) {
	return TCL_ERROR;
    }
    result = Tcl_ExternalToUtfDStringEx(interp, encoding, bytesPtr, length, flags,
	    &ds, failVarObj ? &errorLocation : NULL);
    /* NOTE: ds must be freed beyond this point even on error */

    switch (result) {
    case TCL_OK:
	errorLocation = TCL_INDEX_NONE;
	break;
    case TCL_ERROR:
	/* Error in parameters. Should not happen. interp will have error */
	Tcl_DStringFree(&ds);
	return TCL_ERROR;
    default:
	/*
	 * One of the TCL_CONVERT_* errors. If we were not interested in the
	 * error location, interp result would already have been filled in
	 * and we can just return the error. Otherwise, we have to return
	 * what could be decoded and the returned error location.
	 */
	if (failVarObj == NULL) {
	    Tcl_DStringFree(&ds);
	    return TCL_ERROR;

	}
	break;
    }

    /*
     * TCL_OK or a TCL_CONVERT_* error where the caller wants back as much
     * data as was converted.
     */
    if (failVarObj) {
	Tcl_Obj *failIndex;
	TclNewIndexObj(failIndex, errorLocation);
	if (Tcl_ObjSetVar2(interp, failVarObj, NULL, failIndex,
		TCL_LEAVE_ERR_MSG) == NULL) {
	    Tcl_DStringFree(&ds);
	    return TCL_ERROR;

	}
    }
    /*
     * Note that we cannot use Tcl_DStringResult here because it will
     * truncate the string at the first null byte.
     */

    Tcl_SetObjResult(interp, Tcl_DStringToObj(&ds));


    /* We're done with the encoding */


    Tcl_FreeEncoding(encoding);
    return TCL_OK;


}

/*
 *----------------------------------------------------------------------
 *
 * EncodingConverttoObjCmd --
 *







>






|
<








<
|
>













<
|
>








>

|
|
>
|
<
|
>







547
548
549
550
551
552
553
554
555
556
557
558
559
560
561

562
563
564
565
566
567
568
569

570
571
572
573
574
575
576
577
578
579
580
581
582
583
584

585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600

601
602
603
604
605
606
607
608
609

    if (bytesPtr == NULL) {
	return TCL_ERROR;
    }
    result = Tcl_ExternalToUtfDStringEx(interp, encoding, bytesPtr, length, flags,
	    &ds, failVarObj ? &errorLocation : NULL);
    /* NOTE: ds must be freed beyond this point even on error */

    switch (result) {
    case TCL_OK:
	errorLocation = TCL_INDEX_NONE;
	break;
    case TCL_ERROR:
	/* Error in parameters. Should not happen. interp will have error */
	goto done;

    default:
	/*
	 * One of the TCL_CONVERT_* errors. If we were not interested in the
	 * error location, interp result would already have been filled in
	 * and we can just return the error. Otherwise, we have to return
	 * what could be decoded and the returned error location.
	 */
	if (failVarObj == NULL) {

	    result = TCL_ERROR;
	    goto done;
	}
	break;
    }

    /*
     * TCL_OK or a TCL_CONVERT_* error where the caller wants back as much
     * data as was converted.
     */
    if (failVarObj) {
	Tcl_Obj *failIndex;
	TclNewIndexObj(failIndex, errorLocation);
	if (Tcl_ObjSetVar2(interp, failVarObj, NULL, failIndex,
		TCL_LEAVE_ERR_MSG) == NULL) {

	    result = TCL_ERROR;
	    goto done;
	}
    }
    /*
     * Note that we cannot use Tcl_DStringResult here because it will
     * truncate the string at the first null byte.
     */

    Tcl_SetObjResult(interp, Tcl_DStringToObj(&ds));
    result = TCL_OK;

done:
    Tcl_DStringFree(&ds);
    if (encoding) {
	Tcl_FreeEncoding(encoding);

    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * EncodingConverttoObjCmd --
 *
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

    switch (result) {
    case TCL_OK:
	errorLocation = TCL_INDEX_NONE;
	break;
    case TCL_ERROR:
	/* Error in parameters. Should not happen. interp will have error */
	Tcl_DStringFree(&ds);
	return TCL_ERROR;
    default:
	/*
	 * One of the TCL_CONVERT_* errors. If we were not interested in the
	 * error location, interp result would already have been filled in
	 * and we can just return the error. Otherwise, we have to return
	 * what could be decoded and the returned error location.
	 */
	if (failVarObj == NULL) {
	    Tcl_DStringFree(&ds);
	    return TCL_ERROR;

	}
	break;
    }
    /*
     * TCL_OK or a TCL_CONVERT_* error where the caller wants back as much
     * data as was converted.
     */
    if (failVarObj) {
	Tcl_Obj *failIndex;

	TclNewIndexObj(failIndex, errorLocation);
	if (Tcl_ObjSetVar2(interp, failVarObj, NULL, failIndex,
		TCL_LEAVE_ERR_MSG) == NULL) {
	    Tcl_DStringFree(&ds);
	    return TCL_ERROR;

	}
    }

    Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(
	    (unsigned char*) Tcl_DStringValue(&ds),
	    Tcl_DStringLength(&ds)));
    Tcl_DStringFree(&ds);

    /* We're done with the encoding */





    Tcl_FreeEncoding(encoding);

    return TCL_OK;

}

/*
 *----------------------------------------------------------------------
 *
 * EncodingDirsObjCmd --







|
<








<
|
>













<
|
>






<

<
>

>
>
>
|
>
|







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

    switch (result) {
    case TCL_OK:
	errorLocation = TCL_INDEX_NONE;
	break;
    case TCL_ERROR:
	/* Error in parameters. Should not happen. interp will have error */
	goto done;

    default:
	/*
	 * One of the TCL_CONVERT_* errors. If we were not interested in the
	 * error location, interp result would already have been filled in
	 * and we can just return the error. Otherwise, we have to return
	 * what could be decoded and the returned error location.
	 */
	if (failVarObj == NULL) {

	    result = TCL_ERROR;
	    goto done;
	}
	break;
    }
    /*
     * TCL_OK or a TCL_CONVERT_* error where the caller wants back as much
     * data as was converted.
     */
    if (failVarObj) {
	Tcl_Obj *failIndex;

	TclNewIndexObj(failIndex, errorLocation);
	if (Tcl_ObjSetVar2(interp, failVarObj, NULL, failIndex,
		TCL_LEAVE_ERR_MSG) == NULL) {

	    result = TCL_ERROR;
	    goto done;
	}
    }

    Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(
	    (unsigned char*) Tcl_DStringValue(&ds),
	    Tcl_DStringLength(&ds)));



    result = TCL_OK;

done:
    Tcl_DStringFree(&ds);
    if (encoding) {
	Tcl_FreeEncoding(encoding);
    }
    return result;

}

/*
 *----------------------------------------------------------------------
 *
 * EncodingDirsObjCmd --
Changes to generic/tclIOCmd.c.
944
945
946
947
948
949
950







951
952

953
954
955
956
957
958
959
	    ignoreStderr = 1;
	    break;
	case EXEC_ENCODING:
	    if (++skip >= objc) {
		Tcl_SetResult(interp, "No value given for option -encoding.",
			TCL_STATIC);
		return TCL_ERROR;







	    }
	    encodingObj = objv[skip];

	    break;
	}
    }
    if (objc <= skip) {
	Tcl_WrongNumArgs(interp, 1, objv, "?-option ...? arg ?arg ...?");
	return TCL_ERROR;
    }







>
>
>
>
>
>
>
|
|
>







944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
	    ignoreStderr = 1;
	    break;
	case EXEC_ENCODING:
	    if (++skip >= objc) {
		Tcl_SetResult(interp, "No value given for option -encoding.",
			TCL_STATIC);
		return TCL_ERROR;
	    } else {
		Tcl_Encoding encoding;
		encodingObj = objv[skip];
		/* Verify validity - bug [da5e1bc7bc] */
		if (Tcl_GetEncodingFromObj(interp, encodingObj, &encoding)
			!= TCL_OK) {
		    return TCL_ERROR;
		}
		Tcl_FreeEncoding(encoding);
	    }
	    break;
	}
    }
    if (objc <= skip) {
	Tcl_WrongNumArgs(interp, 1, objv, "?-option ...? arg ?arg ...?");
	return TCL_ERROR;
    }
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
	    return TCL_ERROR;
	}
	return TCL_OK;
    }

    /* Bug [0f1ddc0df7] - encoding errors - use replace profile */
    if (Tcl_SetChannelOption(interp, chan, "-profile", "replace") != TCL_OK) {
	return TCL_ERROR;
    }

    /* TIP 716 */
    if (encodingObj) {
	if (Tcl_SetChannelOption(
		interp, chan, "-encoding", Tcl_GetString(encodingObj)) !=
	    TCL_OK) {
	    return TCL_ERROR;
	}
    }

    TclNewObj(resultPtr);
    if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
	if (Tcl_ReadChars(chan, resultPtr, -1, 0) == TCL_IO_FAILURE) {
	    /*
	     * TIP #219.
	     * Capture error messages put by the driver into the bypass area
	     * and put them into the regular interpreter result. Fall back to
	     * the regular message if nothing was found in the bypass.
	     */

	    if (!TclChanCaughtErrorBypass(interp, chan)) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"error reading output from command: %s",
			Tcl_PosixError(interp)));
		Tcl_DecrRefCount(resultPtr);
	    }
	    return TCL_ERROR;
	}
    }

    /*
     * If the process produced anything on stderr, it will have been returned
     * in the interpreter result. It needs to be appended to the result
     * string.







|



|
|
|
|
<
<


















|







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
	    return TCL_ERROR;
	}
	return TCL_OK;
    }

    /* Bug [0f1ddc0df7] - encoding errors - use replace profile */
    if (Tcl_SetChannelOption(interp, chan, "-profile", "replace") != TCL_OK) {
	goto errorWithOpenChannel;
    }

    /* TIP 716 */
    if (encodingObj &&
	Tcl_SetChannelOption(interp, chan, "-encoding", 
	    Tcl_GetString(encodingObj)) != TCL_OK) {
	goto errorWithOpenChannel;


    }

    TclNewObj(resultPtr);
    if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
	if (Tcl_ReadChars(chan, resultPtr, -1, 0) == TCL_IO_FAILURE) {
	    /*
	     * TIP #219.
	     * Capture error messages put by the driver into the bypass area
	     * and put them into the regular interpreter result. Fall back to
	     * the regular message if nothing was found in the bypass.
	     */

	    if (!TclChanCaughtErrorBypass(interp, chan)) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"error reading output from command: %s",
			Tcl_PosixError(interp)));
		Tcl_DecrRefCount(resultPtr);
	    }
	    goto errorWithOpenChannel;
	}
    }

    /*
     * If the process produced anything on stderr, it will have been returned
     * in the interpreter result. It needs to be appended to the result
     * string.
1065
1066
1067
1068
1069
1070
1071





1072
1073
1074
1075
1076
1077
1078
	if ((length > 0) && (string[length - 1] == '\n')) {
	    Tcl_SetObjLength(resultPtr, length - 1);
	}
    }
    Tcl_SetObjResult(interp, resultPtr);

    return result;





}

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_FblockedObjCmd --
 *







>
>
>
>
>







1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
	if ((length > 0) && (string[length - 1] == '\n')) {
	    Tcl_SetObjLength(resultPtr, length - 1);
	}
    }
    Tcl_SetObjResult(interp, resultPtr);

    return result;

errorWithOpenChannel:
    /* Interpreter should already contain error. Pass NULL to not overwrite */
    (void)Tcl_CloseEx(NULL, chan, 0);
    return TCL_ERROR;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_FblockedObjCmd --
 *
Changes to generic/tclResult.c.
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
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
/*
 *----------------------------------------------------------------------
 *
 * TclMergeReturnOptions --
 *
 *	Parses, checks, and stores the options to the [return] command.
 *



 * Results:
 *	Returns TCL_ERROR if any of the option values are invalid. Otherwise,
 *	returns TCL_OK, and writes the returnOpts, code, and level values to
 *	the pointers provided.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */












































int
TclMergeReturnOptions(
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[],	/* Argument objects. */
    Tcl_Obj **optionsPtrPtr,	/* If not NULL, points to space for a (Tcl_Obj
				 * *) where the pointer to the merged return
				 * options dictionary should be written. */
    int *codePtr,		/* If not NULL, points to space where the
				 * -code value should be written. */
    int *levelPtr)		/* If not NULL, points to space where the
				 * -level value should be written. */
{
    int code = TCL_OK;
    int level = 1;
    Tcl_Obj *valuePtr;
    Tcl_Obj *returnOpts;
    Tcl_Obj **keys = GetKeys();

    TclNewObj(returnOpts);
    for (;  objc > 1;  objv += 2, objc -= 2) {
	const char *opt = TclGetString(objv[0]);
	const char *compare = TclGetString(keys[KEY_OPTIONS]);

	if ((objv[0]->length == keys[KEY_OPTIONS]->length)
		&& (memcmp(opt, compare, objv[0]->length) == 0)) {
	    Tcl_DictSearch search;
	    int done = 0;
	    Tcl_Obj *keyPtr;
	    Tcl_Obj *dict = objv[1];

	nestedOptions:
	    if (TCL_ERROR == Tcl_DictObjFirst(NULL, dict, &search,
		    &keyPtr, &valuePtr, &done)) {
		/*
		 * Value is not a legal dictionary.
		 */

		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"bad %s value: expected dictionary but got \"%s\"",
			compare, TclGetString(objv[1])));
		Tcl_SetErrorCode(interp, "TCL", "RESULT", "ILLEGAL_OPTIONS",
			(char *)NULL);
		goto error;
	    }

	    while (!done) {
		Tcl_DictObjPut(NULL, returnOpts, keyPtr, valuePtr);
		Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);
	    }

	    Tcl_DictObjGet(NULL, returnOpts, keys[KEY_OPTIONS], &valuePtr);
	    if (valuePtr != NULL) {
		dict = valuePtr;
		Tcl_DictObjRemove(NULL, returnOpts, keys[KEY_OPTIONS]);
		goto nestedOptions;
	    }

	} else {
	    Tcl_DictObjPut(NULL, returnOpts, objv[0], objv[1]);
	}
    }

    /*
     * Check for bogus -code value.
     */

    Tcl_DictObjGet(NULL, returnOpts, keys[KEY_CODE], &valuePtr);







>
>
>










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




















<
<
<
<
|
<
<
<
<
<
<

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







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
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
/*
 *----------------------------------------------------------------------
 *
 * TclMergeReturnOptions --
 *
 *	Parses, checks, and stores the options to the [return] command.
 *
 *	The number of arguments (objc) must be even, with the corresponding
 *	objv holding values to be processed as key value .... key value.
 *
 * Results:
 *	Returns TCL_ERROR if any of the option values are invalid. Otherwise,
 *	returns TCL_OK, and writes the returnOpts, code, and level values to
 *	the pointers provided.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
ExpandedOptions(
    Tcl_Interp *interp,		/* Current interpreter. */
    Tcl_Obj **keys,		/* Built-in keys (per thread) */
    Tcl_Obj *returnOpts,	/* Options dict we are building */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    for (;  objc > 1;  objv += 2, objc -= 2) {
	const char *opt = TclGetString(objv[0]);
	const char *compare = TclGetString(keys[KEY_OPTIONS]);

	if ((objv[0]->length == keys[KEY_OPTIONS]->length)
		&& (memcmp(opt, compare, objv[0]->length) == 0)) {
	    /* Process the -options switch to emulate {*} expansion.
	     *
	     * Use lists so duplicate keys are not lost.
	     */

	    Tcl_Size nestc;
	    Tcl_Obj **nestv;

	    if (TCL_ERROR == TclListObjGetElements(interp, objv[1],
		    &nestc, &nestv) || (nestc % 2)) {
		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
			"bad -options value: expected dictionary but got"
			" \"%s\"", TclGetString(objv[1])));
		Tcl_SetErrorCode(interp, "TCL", "RESULT", "ILLEGAL_OPTIONS",
			(char *)NULL);
		return TCL_ERROR;
	    }

	    if (TCL_ERROR ==
		    ExpandedOptions(interp, keys, returnOpts, nestc, nestv)) {
		return TCL_ERROR;
	    }
	} else {
	    Tcl_DictObjPut(NULL, returnOpts, objv[0], objv[1]);
	}
    }
    return TCL_OK;
}

int
TclMergeReturnOptions(
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[],	/* Argument objects. */
    Tcl_Obj **optionsPtrPtr,	/* If not NULL, points to space for a (Tcl_Obj
				 * *) where the pointer to the merged return
				 * options dictionary should be written. */
    int *codePtr,		/* If not NULL, points to space where the
				 * -code value should be written. */
    int *levelPtr)		/* If not NULL, points to space where the
				 * -level value should be written. */
{
    int code = TCL_OK;
    int level = 1;
    Tcl_Obj *valuePtr;
    Tcl_Obj *returnOpts;
    Tcl_Obj **keys = GetKeys();





    /* All callers are expected to pass an even value for objc. */







    TclNewObj(returnOpts);





    if (TCL_ERROR == ExpandedOptions(interp, keys, returnOpts, objc, objv)) {





	goto error;

















    }

    /*
     * Check for bogus -code value.
     */

    Tcl_DictObjGet(NULL, returnOpts, keys[KEY_CODE], &valuePtr);
Changes to macosx/README.
1
2
3
4
5
6
7
8
9
10
11
Tcl Mac OS X README
-------------------

This is the README file for the Mac OS X/Darwin version of Tcl.


1. Where to go for support
--------------------------

- The tcl-mac mailing list on sourceforge is the best place to ask questions
specific to Tcl & Tk on Mac OS X:
|


|







1
2
3
4
5
6
7
8
9
10
11
Tcl macOS README
-------------------

This is the README file for the macOS/Darwin version of Tcl.


1. Where to go for support
--------------------------

- The tcl-mac mailing list on sourceforge is the best place to ask questions
specific to Tcl & Tk on Mac OS X:
59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
framework directories:
	$HOME/Library/Frameworks /Library/Frameworks


3. Building Tcl on Mac OS X
---------------------------

- At least Mac OS X 10.3 is required to build Tcl.
Apple's Xcode Developer Tools need to be installed (only the most recent version

matching your OS release is supported), the Xcode installer is available on Mac
OS X install media or may be present in /Applications/Installers on Macs that
came with OS X preinstalled. The most recent version can always be downloaded
from the ADC website http://connect.apple.com (free ADC membership required).

- Tcl is most easily built as a Mac OS X framework via GNUmakefile in tcl/macosx
(see below for details), but can also be built with the standard unix configure
and make buildsystem in tcl/unix as on any other unix platform (indeed, the
GNUmakefile is just a wrapper around the unix buildsystem).
The Mac OS X specific configure flags are --enable-framework and
--disable-corefoundation (which disables CF and notably reverts to the standard
select based notifier).

- To build universal binaries outside of the Xcode IDE, set CFLAGS as follows:
	export CFLAGS="-arch x86_64 -arch arm64"
This requires Mac OS X 10.6 and Xcode 10.2 and will work on any architecture.
Note that configure requires CFLAGS to contain a least one architecture that can
be run on the build machine (i.e. x86_64 on Core2/Xeon).
Universal builds of Tcl TEA extensions are also possible with CFLAGS set as
above, they will be [load]able by universal as well as thin binaries of Tcl.

Detailed Instructions for building with macosx/GNUmakefile
----------------------------------------------------------

- Unpack the Tcl source release archive.

- The following instructions assume the Tcl source tree is named "tcl${ver}",
(where ${ver} is a shell variable containing the Tcl version number e.g. '9.0').
Setup this shell variable as follows:
	ver="9.0"

- Setup environment variables as desired, e.g. for a universal build on 10.9:
	CFLAGS="-arch x86_64 -arch arm64 -mmacosx-version-min=10.9"
	export CFLAGS

- Change to the directory containing the Tcl source tree and build:
	make -C tcl${ver}/macosx

- Install Tcl onto the root volume (admin password required):
	sudo make -C tcl${ver}/macosx install







|
|
>
|
|
|
|

|







|
|
|
|
<













|
|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
framework directories:
	$HOME/Library/Frameworks /Library/Frameworks


3. Building Tcl on Mac OS X
---------------------------

- Tcl supports macOS 10.13 and newer.
While Tcl may build on earlier versions of the OS, it is not tested on versions
older than 10.13.  You will need to install an  Apple clang toolchain either by
downloading the Xcode app from Apple's App Store, or by installing the Command
Line Tools. The Command Line Tools can be installed by running the command:
    xcode-select --install
in the Terminal.

- Tcl is most easily built as a macOS framework via the GNUmakefile in tcl/macosx
(see below for details), but can also be built with the standard unix configure
and make buildsystem in tcl/unix as on any other unix platform (indeed, the
GNUmakefile is just a wrapper around the unix buildsystem).
The Mac OS X specific configure flags are --enable-framework and
--disable-corefoundation (which disables CF and notably reverts to the standard
select based notifier).

- To build universal binaries for macOS 10.13 and newer set CFLAGS as follows:
	export CFLAGS="-arch x86_64 -arch arm64 -mmacosx-version-min=10.13"
(This will cause clang to set macOS 11 as the target OS for the arm64 architecture
since Apple Silicon was not supported until macOS 11.)

Universal builds of Tcl TEA extensions are also possible with CFLAGS set as
above, they will be [load]able by universal as well as thin binaries of Tcl.

Detailed Instructions for building with macosx/GNUmakefile
----------------------------------------------------------

- Unpack the Tcl source release archive.

- The following instructions assume the Tcl source tree is named "tcl${ver}",
(where ${ver} is a shell variable containing the Tcl version number e.g. '9.0').
Setup this shell variable as follows:
	ver="9.0"

- Setup environment variables as desired, for example:
	CFLAGS="-arch x86_64 -arch arm64 -mmacosx-version-min=10.13"
	export CFLAGS

- Change to the directory containing the Tcl source tree and build:
	make -C tcl${ver}/macosx

- Install Tcl onto the root volume (admin password required):
	sudo make -C tcl${ver}/macosx install
Changes to tests/cmdMZ.test.
193
194
195
196
197
198
199













200
201
202
203
204
205
206
    } result] $result $::errorCode
} {1 c {a b}}
test cmdMZ-return-2.18 {return option handling} {
    list [catch {
	return -code error -errorstack [list CALL a CALL b] yo
    } -> foo] [dictSort $foo] [info errorstack]
} {2 {-code 1 -errorcode NONE -errorstack {CALL a CALL b} -level 1} {CALL a CALL b}}














# Check that the result of a [return -options $opts $result] is
# indistinguishable from that of the originally caught script, no matter what
# the script is/does. (TIP 90)
foreach {testid script} {
    cmdMZ-return-3.0 {}
    cmdMZ-return-3.1 {format x}







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







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    } result] $result $::errorCode
} {1 c {a b}}
test cmdMZ-return-2.18 {return option handling} {
    list [catch {
	return -code error -errorstack [list CALL a CALL b] yo
    } -> foo] [dictSort $foo] [info errorstack]
} {2 {-code 1 -errorcode NONE -errorstack {CALL a CALL b} -level 1} {CALL a CALL b}}
test cmdMZ-return-2.19 {return option handling} -body {
    return -level 0 -options {-options {-code break} -code continue}
} -returnCodes continue -result {}
test cmdMZ-return-2.20 {return option handling} {
    list [catch {
	return -level 0 -options {-foo 1} -options {-bar 2} 
    } -> foo] $foo
} {0 {-foo 1 -bar 2 -code 0 -level 0}}
test cmdMZ-return-2.21 {return option handling} {
    list [catch {
	return -level 0 -options {-options {-foo 1} -options {-bar 2}}
    } -> foo] $foo
} {0 {-foo 1 -bar 2 -code 0 -level 0}}

# Check that the result of a [return -options $opts $result] is
# indistinguishable from that of the originally caught script, no matter what
# the script is/does. (TIP 90)
foreach {testid script} {
    cmdMZ-return-3.0 {}
    cmdMZ-return-3.1 {format x}
Changes to tests/exec.test.
28
29
30
31
32
33
34


35
36
37
38
39
40
41
# no idea how to check it in GHA programmatically, so simply disable it (todo: rewrite with better check later)
if {[testConstraint win] && ![info exists ::env(CI)] &&
    [info exists ::env(LOCALAPPDATA)] &&
    [file exists [file join $::env(LOCALAPPDATA) "Microsoft" "WindowsApps" "winget.exe"]]} {
	testConstraint haveWinget 1
}



unset -nocomplain path

# Utilities that are like Bourne shell stalwarts, but cross-platform.
set path(echo) [makeFile {
    puts -nonewline [lindex $argv 0]
    foreach str [lrange $argv 1 end] {
	puts -nonewline " $str"







>
>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# no idea how to check it in GHA programmatically, so simply disable it (todo: rewrite with better check later)
if {[testConstraint win] && ![info exists ::env(CI)] &&
    [info exists ::env(LOCALAPPDATA)] &&
    [file exists [file join $::env(LOCALAPPDATA) "Microsoft" "WindowsApps" "winget.exe"]]} {
	testConstraint haveWinget 1
}

testConstraint testhandlecount [expr {[llength [info commands testhandlecount]] != 0}]

unset -nocomplain path

# Utilities that are like Bourne shell stalwarts, but cross-platform.
set path(echo) [makeFile {
    puts -nonewline [lindex $argv 0]
    foreach str [lrange $argv 1 end] {
	puts -nonewline " $str"
150
151
152
153
154
155
156






157
158
159
160
161
162
163
} {3 1 4}
set arg {12345678901234567890123456789012345678901234567890}
set arg "$arg$arg$arg$arg$arg$arg"
test exec-1.4 {long command lines} {exec} {
    exec [interpreter] $path(echo) $arg
} $arg
set arg {}







# I/O redirection: input from Tcl command.

test exec-2.1 {redirecting input from immediate source} {exec stdio} {
    exec [interpreter] $path(cat) << "Sample text"
} {Sample text}
test exec-2.2 {redirecting input from immediate source} {exec stdio} {







>
>
>
>
>
>







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
} {3 1 4}
set arg {12345678901234567890123456789012345678901234567890}
set arg "$arg$arg$arg$arg$arg$arg"
test exec-1.4 {long command lines} {exec} {
    exec [interpreter] $path(echo) $arg
} $arg
set arg {}
test exec-1.5 {pipelining - handle leaks} -constraints {exec stdio testhandlecount} -body {
    set numHandles [testhandlecount]
    set a [exec [interpreter] $path(echo) a b c d | [interpreter] $path(cat) | [interpreter] $path(wc)]
    list [scan $a "%d %d %d" b c d] $b $c [expr {[testhandlecount] - $numHandles}]
} -result {3 1 4 0}


# I/O redirection: input from Tcl command.

test exec-2.1 {redirecting input from immediate source} {exec stdio} {
    exec [interpreter] $path(cat) << "Sample text"
} {Sample text}
test exec-2.2 {redirecting input from immediate source} {exec stdio} {
186
187
188
189
190
191
192







193
194
195
196
197
198
199
    # If it does, this means that the UTF -> external conversion did not occur
    # before writing out the temp file.
    quotenonascii [exec [interpreter] $path(cat) << "\xE9\xE0\xFC\xF1"]
} -cleanup {
    encoding system $sysenc
    rename quotenonascii {}
} -result {\xE9\xE0\xFC\xF1}








# I/O redirection: output to file.

set path(gorp.file) [makeFile {} gorp.file]
file delete $path(gorp.file)

test exec-3.1 {redirecting output to file} {exec} {







>
>
>
>
>
>
>







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    # If it does, this means that the UTF -> external conversion did not occur
    # before writing out the temp file.
    quotenonascii [exec [interpreter] $path(cat) << "\xE9\xE0\xFC\xF1"]
} -cleanup {
    encoding system $sysenc
    rename quotenonascii {}
} -result {\xE9\xE0\xFC\xF1}
test exec-2.7 {handle count redirecting input from immediate source} -constraints {
    exec stdio testhandlecount
} -body {
    set numHandles [testhandlecount]
    list [exec [interpreter] $path(cat) | [interpreter] $path(cat) << "Sample text"] \
        [expr {[testhandlecount] - $numHandles}]
} -result [list {Sample text} 0]

# I/O redirection: output to file.

set path(gorp.file) [makeFile {} gorp.file]
file delete $path(gorp.file)

test exec-3.1 {redirecting output to file} {exec} {
228
229
230
231
232
233
234








235
236
237
238
239
240
241
    flush $f
    exec [interpreter] $path(echo) "More text" >@ $f
    exec [interpreter] $path(echo) >@$f "Even more"
    puts $f "Line 3"
    close $f
    exec [interpreter] $path(cat) $path(gorp.file)
} "Line 1\nMore text\nEven more\nLine 3"









# I/O redirection: output and stderr to file.

file delete $path(gorp.file)

test exec-4.1 {redirecting output and stderr to file} {exec} {
    exec [interpreter] $path(echo) "test output" >& $path(gorp.file)







>
>
>
>
>
>
>
>







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
    flush $f
    exec [interpreter] $path(echo) "More text" >@ $f
    exec [interpreter] $path(echo) >@$f "Even more"
    puts $f "Line 3"
    close $f
    exec [interpreter] $path(cat) $path(gorp.file)
} "Line 1\nMore text\nEven more\nLine 3"
test exec-3.8 {handle count redirecting output to file} -constraints {
    exec stdio testhandlecount
} -body {
    set numHandles [testhandlecount]
    exec > $path(gorp.file) [interpreter] $path(echo) "Different simple words" | [interpreter] $path(cat) | [interpreter] $path(cat)
    list [exec [interpreter] $path(cat) $path(gorp.file)] \
        [expr {[testhandlecount] - $numHandles}]
} -result [list "Different simple words" 0]

# I/O redirection: output and stderr to file.

file delete $path(gorp.file)

test exec-4.1 {redirecting output and stderr to file} {exec} {
    exec [interpreter] $path(echo) "test output" >& $path(gorp.file)
299
300
301
302
303
304
305







306
307
308
309
310
311
312
} -result {Just a few thoughts}
test exec-5.7 {redirecting input from file} -constraints {exec} -body {
    set f [open $path(gorp.file) r]
    exec <@$f [interpreter] $path(cat)
} -cleanup {
    close $f
} -result {Just a few thoughts}








# I/O redirection: standard error through a pipeline.

test exec-6.1 {redirecting stderr through a pipeline} {exec stdio} {
    exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar" |& [interpreter] $path(cat)
} "foo bar"
test exec-6.2 {redirecting stderr through a pipeline} {exec stdio} {







>
>
>
>
>
>
>







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
} -result {Just a few thoughts}
test exec-5.7 {redirecting input from file} -constraints {exec} -body {
    set f [open $path(gorp.file) r]
    exec <@$f [interpreter] $path(cat)
} -cleanup {
    close $f
} -result {Just a few thoughts}
test exec-5.8 {handle count redirecting input from file} -constraints {
    exec stdio testhandlecount
} -body {
    set numHandles [testhandlecount]
    list [exec [interpreter] $path(cat) | [interpreter] $path(cat) < $path(gorp.file)] \
        [expr {[testhandlecount] - $numHandles}]
} -result [list {Just a few thoughts} 0]

# I/O redirection: standard error through a pipeline.

test exec-6.1 {redirecting stderr through a pipeline} {exec stdio} {
    exec [interpreter] $path(sh) -c "\"$path(echo)\" foo bar" |& [interpreter] $path(cat)
} "foo bar"
test exec-6.2 {redirecting stderr through a pipeline} {exec stdio} {
480
481
482
483
484
485
486









487
488
489
490
491
492
493
} 3
test exec-11.5 {commands in background} {exec} {
    set f [open $path(gorp.file) w]
    puts $f [list catch [list exec [info nameofexecutable] $path(echo) foo &]]
    close $f
    exec [interpreter] $path(gorp.file)
} foo










# Make sure that background commands are properly reaped when they
# eventually die.

if {[testConstraint exec] && [testConstraint nonPortable]} {
    after 1300
    exec [interpreter] $path(sleep) 1







>
>
>
>
>
>
>
>
>







510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
} 3
test exec-11.5 {commands in background} {exec} {
    set f [open $path(gorp.file) w]
    puts $f [list catch [list exec [info nameofexecutable] $path(echo) foo &]]
    close $f
    exec [interpreter] $path(gorp.file)
} foo
test exec-11.6 {commands in background} -constraints {
    exec stdio testhandlecount
} -body {
    set numHandles [testhandlecount]
    set n [llength [exec [interpreter] $path(sleep) 1 | [interpreter] $path(sleep) 1 | [interpreter] $path(sleep) 1 &]]
    after 1100
    tcl::process::purge
    list $n [expr {([testhandlecount] - $numHandles) <= 0}]; # Could be < 0 if prior processes were reaped
} -result {3 1}

# Make sure that background commands are properly reaped when they
# eventually die.

if {[testConstraint exec] && [testConstraint nonPortable]} {
    after 1300
    exec [interpreter] $path(sleep) 1