- priority: 5 --> 1
- status: open --> closed-duplicate
OriginalBugID: 582 Bug
Version: 8.0p2
SubmitDate: '1998-05-21'
LastModified: '2000-04-26'
Severity: MED
Status: Closed
Submitter: hershey
ChangedBy: hobbs
RelatedBugIDs: 682
OS: All Windows
FixedDate: '2000-10-25'
FixedInVersion: NA
ClosedDate: '2000-04-26'
Consider the following Tcl script:
set version [exec gcc -v 2>@ stdout]
if {$version != ""} then {
puts "Got version"
} else {
puts "***FAILED***"
}
The command `gcc -v' normally outputs version information on stderr.
The 2>@ stdout is intended to redirect the stderr output to stdout, so
that it can be easily read using the exec command.
This commands works as expected on Unix. However, when using Tcl
8.0p2, it fails on Windows.
What happens is that when TclCreatePipeline sees the 2>@ stdout, it
calls FileForRedirect, which finds the stdout channel and then calls
TclpMakeFile. On Unix, this returns a code indicating descriptor 1.
On Windows, this returns a file handle for stdout.
Since the exec command does not redirect stdout, TclCreatePipeline
winds up calling TclCreatePipe to create a pipe for stdout.
TclCreatePipeline then calls TclpCreateProcess.
On Unix, this forks, and the child first calls SetupStdFile for
stdout, and then calls it for stderr. Calling SetupStdFile for stdout
dups the stdout pipe onto file descriptor 1. Calling SetupStdFile for
stderr file descriptor 1, as returned by TclpMakeFile when called by
FileForRedirect, onto file descriptor 2. So everything works as
expected: both stdout and stderr are redirected to the same file.
On Windows, however, stdout and stderr are handled differently. In
this case, stdout will be redirected onto the stdout pipe as expected.
However, when stderr is redirected, it will be redirected from the old
stdout handle, rather than the new one. The effect is that stdout
will go to the pipe, and stderr will go to the console.
In other words, using 2>@ stderr happens to work on Unix because of
the way in which the redirection descriptor is represented, and the
order in which the descriptors are redirected. On Windows, which does
not always use the same handle number for stdout, it fails.
I have appended a patch which fixes this problem on Windows, by
changing the way in which TclCreatePipeline handles a redirection of
stderr on stdout. I changed TclCreatePipeline to check for that case
specially. When it happens, we arrange to call TclpCreateProcess with
the same handle for both stdout and stderr. This code works correctly
on both Unix and Windows. On Unix it is, perhaps, less fragile.
We are using this as a local patch at Cygnus. I would like some
version of this patch to be included in future Tcl releases.
Actually, to be more precise, I would like the above Tcl script to
work on Windows in future releases, and this patch is one way to make
that happen. Please let me know if I can help further.
Ian Taylor
ian@cygnus.com
*** /5g/ian/tcl8.0p2/generic/tclPipe.c Wed Jun 25 13:39:57 1997
--- tclPipe.c Fri Apr 10 14:37:15 1998
*************** TclCreatePipeline(interp, argc, argv, pi
*** 497,502 ****
--- 497,504 ----
int errorClose = 0; /* If non-zero, then errorFile should be
* closed when cleaning up. */
int errorRelease = 0;
+ int joinError = 0; /* If non-zero, stderr is being redirected
+ * onto stdout. */
char *p;
int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;
Tcl_DString execBuffer;
*************** TclCreatePipeline(interp, argc, argv, pi
*** 665,674 ****
errorRelease = 0;
TclpReleaseFile(errorFile);
}
! errorFile = FileForRedirect(interp, p, atOK, argv[i],
! argv[i + 1], flags, &skip, &errorClose, &errorRelease);
! if (errorFile == NULL) {
! goto error;
}
break;
}
--- 667,691 ----
errorRelease = 0;
TclpReleaseFile(errorFile);
}
!
! /* Check specially for the case of redirecting stderr onto
! stdout. */
! joinError = 0;
! if (*p == '@'
! && strcmp ((p[1] == '\0' ? argv[i + 1] : p + 1),
! "stdout") == 0) {
! joinError = 1;
! if (p[1] == '\0') {
! skip = 2;
! } else {
! skip = 1;
! }
! } else {
! errorFile = FileForRedirect(interp, p, atOK, argv[i],
! argv[i + 1], flags, &skip, &errorClose, &errorRelease);
! if (errorFile == NULL) {
! goto error;
! }
}
break;
}
*************** TclCreatePipeline(interp, argc, argv, pi
*** 755,761 ****
}
if (errorFile == NULL) {
! if (errFilePtr != NULL) {
/*
* Set up the standard error output sink for the pipeline, if
* requested. Use a temporary file which is opened, then deleted.
--- 772,780 ----
}
if (errorFile == NULL) {
! if (joinError) {
! errorFile = outputFile;
! } else if (errFilePtr != NULL) {
/*
* Set up the standard error output sink for the pipeline, if
* requested. Use a temporary file which is opened, then deleted.