4 * This file contains the generic portion of the command channel
5 * driver as well as various utility routines used in managing
8 * Copyright (c) 1997 by Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
20 * A linked list of the following structures is used to keep track
21 * of child processes that have been detached but haven't exited
22 * yet, so we can make sure that they're properly "reaped" (officially
23 * waited for) and don't lie around as zombies cluttering the
27 typedef struct Detached {
28 Tcl_Pid pid; /* Id of process that's been detached
29 * but isn't known to have exited. */
30 struct Detached *nextPtr; /* Next in list of all detached
34 static Detached *detList = NULL; /* List of all detached proceses. */
35 TCL_DECLARE_MUTEX(pipeMutex) /* Guard access to detList. */
38 * Declarations for local procedures defined in this file:
41 static TclFile FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
42 CONST char *spec, int atOk, CONST char *arg,
43 CONST char *nextArg, int flags, int *skipPtr,
44 int *closePtr, int *releasePtr));
47 *----------------------------------------------------------------------
51 * This procedure does much of the work of parsing redirection
52 * operators. It handles "@" if specified and allowed, and a file
53 * name, and opens the file if necessary.
56 * The return value is the descriptor number for the file. If an
57 * error occurs then NULL is returned and an error message is left
58 * in the interp's result. Several arguments are side-effected; see
59 * the argument list below for details.
64 *----------------------------------------------------------------------
68 FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
70 Tcl_Interp *interp; /* Intepreter to use for error reporting. */
71 CONST char *spec; /* Points to character just after
72 * redirection character. */
73 CONST char *arg; /* Pointer to entire argument containing
74 * spec: used for error reporting. */
75 int atOK; /* Non-zero means that '@' notation can be
76 * used to specify a channel, zero means that
78 CONST char *nextArg; /* Next argument in argc/argv array, if needed
79 * for file name or channel name. May be
81 int flags; /* Flags to use for opening file or to
82 * specify mode for channel. */
83 int *skipPtr; /* Filled with 1 if redirection target was
84 * in spec, 2 if it was in nextArg. */
85 int *closePtr; /* Filled with one if the caller should
86 * close the file when done with it, zero
90 int writing = (flags & O_WRONLY);
95 if ((atOK != 0) && (*spec == '@')) {
104 chan = Tcl_GetChannel(interp, spec, NULL);
105 if (chan == (Tcl_Channel) NULL) {
108 file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
110 Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
111 "\" wasn't opened for ",
112 ((writing) ? "writing" : "reading"), (char *) NULL);
119 * Be sure to flush output to the file, so that anything
120 * written by the child appears after stuff we've already
128 Tcl_DString nameString;
137 name = Tcl_TranslateFileName(interp, spec, &nameString);
139 file = TclpOpenFile(name, flags);
143 Tcl_DStringFree(&nameString);
145 Tcl_AppendResult(interp, "couldn't ",
146 ((writing) ? "write" : "read"), " file \"", spec, "\": ",
147 Tcl_PosixError(interp), (char *) NULL);
155 Tcl_AppendResult(interp, "can't specify \"", arg,
156 "\" as last word in command", (char *) NULL);
161 *----------------------------------------------------------------------
165 * This procedure is called to indicate that one or more child
166 * processes have been placed in background and will never be
167 * waited for; they should eventually be reaped by
168 * Tcl_ReapDetachedProcs.
176 *----------------------------------------------------------------------
180 Tcl_DetachPids(numPids, pidPtr)
181 int numPids; /* Number of pids to detach: gives size
182 * of array pointed to by pidPtr. */
183 Tcl_Pid *pidPtr; /* Array of pids to detach. */
185 register Detached *detPtr;
188 Tcl_MutexLock(&pipeMutex);
189 for (i = 0; i < numPids; i++) {
190 detPtr = (Detached *) ckalloc(sizeof(Detached));
191 detPtr->pid = pidPtr[i];
192 detPtr->nextPtr = detList;
195 Tcl_MutexUnlock(&pipeMutex);
200 *----------------------------------------------------------------------
202 * Tcl_ReapDetachedProcs --
204 * This procedure checks to see if any detached processes have
205 * exited and, if so, it "reaps" them by officially waiting on
206 * them. It should be called "occasionally" to make sure that
207 * all detached processes are eventually reaped.
213 * Processes are waited on, so that they can be reaped by the
216 *----------------------------------------------------------------------
220 Tcl_ReapDetachedProcs()
222 register Detached *detPtr;
223 Detached *nextPtr, *prevPtr;
227 Tcl_MutexLock(&pipeMutex);
228 for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
229 pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
230 if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {
232 detPtr = detPtr->nextPtr;
235 nextPtr = detPtr->nextPtr;
236 if (prevPtr == NULL) {
237 detList = detPtr->nextPtr;
239 prevPtr->nextPtr = detPtr->nextPtr;
241 ckfree((char *) detPtr);
244 Tcl_MutexUnlock(&pipeMutex);
248 *----------------------------------------------------------------------
250 * TclCleanupChildren --
252 * This is a utility procedure used to wait for child processes
253 * to exit, record information about abnormal exits, and then
254 * collect any stderr output generated by them.
257 * The return value is a standard Tcl result. If anything at
258 * weird happened with the child processes, TCL_ERROR is returned
259 * and a message is left in the interp's result.
262 * If the last character of the interp's result is a newline, then it
263 * is removed unless keepNewline is non-zero. File errorId gets
264 * closed, and pidPtr is freed back to the storage allocator.
266 *----------------------------------------------------------------------
270 TclCleanupChildren(interp, numPids, pidPtr, errorChan)
271 Tcl_Interp *interp; /* Used for error messages. */
272 int numPids; /* Number of entries in pidPtr array. */
273 Tcl_Pid *pidPtr; /* Array of process ids of children. */
274 Tcl_Channel errorChan; /* Channel for file containing stderr output
275 * from pipeline. NULL means there isn't any
279 int i, abnormalExit, anyErrorInfo;
281 WAIT_STATUS_TYPE waitStatus;
285 for (i = 0; i < numPids; i++) {
286 pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
287 if (pid == (Tcl_Pid) -1) {
289 if (interp != (Tcl_Interp *) NULL) {
290 msg = Tcl_PosixError(interp);
291 if (errno == ECHILD) {
293 * This changeup in message suggested by Mark Diekhans
294 * to remind people that ECHILD errors can occur on
295 * some systems if SIGCHLD isn't in its default state.
299 "child process lost (is SIGCHLD ignored or trapped?)";
301 Tcl_AppendResult(interp, "error waiting for process to exit: ",
308 * Create error messages for unusual process exits. An
309 * extra newline gets appended to each error message, but
310 * it gets removed below (in the same fashion that an
311 * extra newline in the command's output is removed).
314 if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
315 char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
318 TclFormatInt(msg1, (long) TclpGetPid(pid));
319 if (WIFEXITED(waitStatus)) {
320 if (interp != (Tcl_Interp *) NULL) {
321 TclFormatInt(msg2, WEXITSTATUS(waitStatus));
322 Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
326 } else if (WIFSIGNALED(waitStatus)) {
327 if (interp != (Tcl_Interp *) NULL) {
330 p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
331 Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
332 Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
334 Tcl_AppendResult(interp, "child killed: ", p, "\n",
337 } else if (WIFSTOPPED(waitStatus)) {
338 if (interp != (Tcl_Interp *) NULL) {
341 p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
342 Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
343 Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
345 Tcl_AppendResult(interp, "child suspended: ", p, "\n",
349 if (interp != (Tcl_Interp *) NULL) {
350 Tcl_AppendResult(interp,
351 "child wait status didn't make sense\n",
359 * Read the standard error file. If there's anything there,
360 * then return an error and add the file's contents to the result
365 if (errorChan != NULL) {
368 * Make sure we start at the beginning of the file.
371 if (interp != NULL) {
375 Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
376 objPtr = Tcl_NewObj();
377 count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
380 Tcl_DecrRefCount(objPtr);
381 Tcl_ResetResult(interp);
382 Tcl_AppendResult(interp, "error reading stderr output file: ",
383 Tcl_PosixError(interp), NULL);
384 } else if (count > 0) {
386 Tcl_SetObjResult(interp, objPtr);
389 Tcl_DecrRefCount(objPtr);
392 Tcl_Close(NULL, errorChan);
396 * If a child exited abnormally but didn't output any error information
397 * at all, generate an error message here.
400 if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
401 Tcl_AppendResult(interp, "child process exited abnormally",
408 *----------------------------------------------------------------------
410 * TclCreatePipeline --
412 * Given an argc/argv array, instantiate a pipeline of processes
413 * as described by the argv.
415 * This procedure is unofficially exported for use by BLT.
418 * The return value is a count of the number of new processes
419 * created, or -1 if an error occurred while creating the pipeline.
420 * *pidArrayPtr is filled in with the address of a dynamically
421 * allocated array giving the ids of all of the processes. It
422 * is up to the caller to free this array when it isn't needed
423 * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
424 * with the file id for the input pipe for the pipeline (if any):
425 * the caller must eventually close this file. If outPipePtr
426 * isn't NULL, then *outPipePtr is filled in with the file id
427 * for the output pipe from the pipeline: the caller must close
428 * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
429 * with a file id that may be used to read error output after the
430 * pipeline completes.
433 * Processes and pipes are created.
435 *----------------------------------------------------------------------
439 TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
440 outPipePtr, errFilePtr)
441 Tcl_Interp *interp; /* Interpreter to use for error reporting. */
442 int argc; /* Number of entries in argv. */
443 CONST char **argv; /* Array of strings describing commands in
444 * pipeline plus I/O redirection with <,
445 * <<, >, etc. Argv[argc] must be NULL. */
446 Tcl_Pid **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with
447 * address of array of pids for processes
448 * in pipeline (first pid is first process
450 TclFile *inPipePtr; /* If non-NULL, input to the pipeline comes
451 * from a pipe (unless overridden by
452 * redirection in the command). The file
453 * id with which to write to this pipe is
454 * stored at *inPipePtr. NULL means command
455 * specified its own input source. */
456 TclFile *outPipePtr; /* If non-NULL, output to the pipeline goes
457 * to a pipe, unless overriden by redirection
458 * in the command. The file id with which to
459 * read frome this pipe is stored at
460 * *outPipePtr. NULL means command specified
461 * its own output sink. */
462 TclFile *errFilePtr; /* If non-NULL, all stderr output from the
463 * pipeline will go to a temporary file
464 * created here, and a descriptor to read
465 * the file will be left at *errFilePtr.
466 * The file will be removed already, so
467 * closing this descriptor will be the end
468 * of the file. If this is NULL, then
469 * all stderr output goes to our stderr.
470 * If the pipeline specifies redirection
471 * then the file will still be created
472 * but it will never get any data. */
474 Tcl_Pid *pidPtr = NULL; /* Points to malloc-ed array holding all
475 * the pids of child processes. */
476 int numPids; /* Actual number of processes that exist
477 * at *pidPtr right now. */
478 int cmdCount; /* Count of number of distinct commands
479 * found in argc/argv. */
480 CONST char *inputLiteral = NULL; /* If non-null, then this points to a
481 * string containing input data (specified
482 * via <<) to be piped to the first process
483 * in the pipeline. */
484 TclFile inputFile = NULL; /* If != NULL, gives file to use as input for
485 * first process in pipeline (specified via <
487 int inputClose = 0; /* If non-zero, then inputFile should be
488 * closed when cleaning up. */
489 int inputRelease = 0;
490 TclFile outputFile = NULL; /* Writable file for output from last command
491 * in pipeline (could be file or pipe). NULL
492 * means use stdout. */
493 int outputClose = 0; /* If non-zero, then outputFile should be
494 * closed when cleaning up. */
495 int outputRelease = 0;
496 TclFile errorFile = NULL; /* Writable file for error output from all
497 * commands in pipeline. NULL means use
499 int errorClose = 0; /* If non-zero, then errorFile should be
500 * closed when cleaning up. */
501 int errorRelease = 0;
503 int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;
504 Tcl_DString execBuffer;
506 TclFile curInFile, curOutFile, curErrFile;
509 if (inPipePtr != NULL) {
512 if (outPipePtr != NULL) {
515 if (errFilePtr != NULL) {
519 Tcl_DStringInit(&execBuffer);
527 * First, scan through all the arguments to figure out the structure
528 * of the pipeline. Process all of the input and output redirection
529 * arguments and remove them from the argument list in the pipeline.
530 * Count the number of distinct processes (it's the number of "|"
531 * arguments plus one) but don't remove the "|" arguments because
532 * they'll be used in the second pass to seperate the individual
533 * child processes. Cannot start the child processes in this pass
534 * because the redirection symbols may appear anywhere in the
535 * command line -- e.g., the '<' that specifies the input to the
536 * entire pipe may appear at the very end of the argument list.
541 for (i = 0; i < argc; i++) {
550 if ((i == (lastBar + 1)) || (i == (argc - 1))) {
551 Tcl_SetResult(interp,
552 "illegal use of | or |& in command",
562 if (inputClose != 0) {
564 TclpCloseFile(inputFile);
566 if (inputRelease != 0) {
568 TclpReleaseFile(inputFile);
572 inputLiteral = p + 1;
574 if (*inputLiteral == '\0') {
575 inputLiteral = argv[i + 1];
576 if (inputLiteral == NULL) {
577 Tcl_AppendResult(interp, "can't specify \"", argv[i],
578 "\" as last word in command", (char *) NULL);
585 inputFile = FileForRedirect(interp, p, 1, argv[i],
586 argv[i + 1], O_RDONLY, &skip, &inputClose, &inputRelease);
587 if (inputFile == NULL) {
595 flags = O_WRONLY | O_CREAT | O_TRUNC;
600 flags = O_WRONLY | O_CREAT;
603 if (errorClose != 0) {
605 TclpCloseFile(errorFile);
612 * Close the old output file, but only if the error file is
616 if (outputClose != 0) {
618 if (errorFile == outputFile) {
621 TclpCloseFile(outputFile);
624 if (outputRelease != 0) {
626 if (errorFile == outputFile) {
629 TclpReleaseFile(outputFile);
632 outputFile = FileForRedirect(interp, p, atOK, argv[i],
633 argv[i + 1], flags, &skip, &outputClose, &outputRelease);
634 if (outputFile == NULL) {
638 if (errorClose != 0) {
640 TclpCloseFile(errorFile);
642 if (errorRelease != 0) {
644 TclpReleaseFile(errorFile);
646 errorFile = outputFile;
656 flags = O_WRONLY | O_CREAT | O_TRUNC;
660 flags = O_WRONLY | O_CREAT;
662 if (errorClose != 0) {
664 TclpCloseFile(errorFile);
666 if (errorRelease != 0) {
668 TclpReleaseFile(errorFile);
670 errorFile = FileForRedirect(interp, p, atOK, argv[i],
671 argv[i + 1], flags, &skip, &errorClose, &errorRelease);
672 if (errorFile == NULL) {
679 for (j = i + skip; j < argc; j++) {
680 argv[j - skip] = argv[j];
687 if (inputFile == NULL) {
688 if (inputLiteral != NULL) {
690 * The input for the first process is immediate data coming from
691 * Tcl. Create a temporary file for it and put the data into the
694 inputFile = TclpCreateTempFile(inputLiteral);
695 if (inputFile == NULL) {
696 Tcl_AppendResult(interp,
697 "couldn't create input file for command: ",
698 Tcl_PosixError(interp), (char *) NULL);
702 } else if (inPipePtr != NULL) {
704 * The input for the first process in the pipeline is to
705 * come from a pipe that can be written from by the caller.
708 if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
709 Tcl_AppendResult(interp,
710 "couldn't create input pipe for command: ",
711 Tcl_PosixError(interp), (char *) NULL);
717 * The input for the first process comes from stdin.
720 channel = Tcl_GetStdChannel(TCL_STDIN);
721 if (channel != NULL) {
722 inputFile = TclpMakeFile(channel, TCL_READABLE);
723 if (inputFile != NULL) {
730 if (outputFile == NULL) {
731 if (outPipePtr != NULL) {
733 * Output from the last process in the pipeline is to go to a
734 * pipe that can be read by the caller.
737 if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
738 Tcl_AppendResult(interp,
739 "couldn't create output pipe for command: ",
740 Tcl_PosixError(interp), (char *) NULL);
746 * The output for the last process goes to stdout.
749 channel = Tcl_GetStdChannel(TCL_STDOUT);
751 outputFile = TclpMakeFile(channel, TCL_WRITABLE);
752 if (outputFile != NULL) {
759 if (errorFile == NULL) {
760 if (errFilePtr != NULL) {
762 * Set up the standard error output sink for the pipeline, if
763 * requested. Use a temporary file which is opened, then deleted.
764 * Could potentially just use pipe, but if it filled up it could
765 * cause the pipeline to deadlock: we'd be waiting for processes
766 * to complete before reading stderr, and processes couldn't
767 * complete because stderr was backed up.
770 errorFile = TclpCreateTempFile(NULL);
771 if (errorFile == NULL) {
772 Tcl_AppendResult(interp,
773 "couldn't create error file for command: ",
774 Tcl_PosixError(interp), (char *) NULL);
777 *errFilePtr = errorFile;
780 * Errors from the pipeline go to stderr.
783 channel = Tcl_GetStdChannel(TCL_STDERR);
785 errorFile = TclpMakeFile(channel, TCL_WRITABLE);
786 if (errorFile != NULL) {
794 * Scan through the argc array, creating a process for each
795 * group of arguments between the "|" characters.
798 Tcl_ReapDetachedProcs();
799 pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
801 curInFile = inputFile;
803 for (i = 0; i < argc; i = lastArg + 1) {
804 int result, joinThisError;
809 * Convert the program name into native form.
812 if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
817 * Find the end of the current segment of the pipeline.
821 for (lastArg = i; lastArg < argc; lastArg++) {
822 if (argv[lastArg][0] == '|') {
823 if (argv[lastArg][1] == '\0') {
826 if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
832 argv[lastArg] = NULL;
835 * If this is the last segment, use the specified outputFile.
836 * Otherwise create an intermediate pipe. pipeIn will become the
837 * curInFile for the next segment of the pipe.
840 if (lastArg == argc) {
841 curOutFile = outputFile;
843 if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
844 Tcl_AppendResult(interp, "couldn't create pipe: ",
845 Tcl_PosixError(interp), (char *) NULL);
850 if (joinThisError != 0) {
851 curErrFile = curOutFile;
853 curErrFile = errorFile;
857 * Restore argv[i], since a caller wouldn't expect the contents of
858 * argv to be modified.
862 argv[i] = Tcl_DStringValue(&execBuffer);
863 result = TclpCreateProcess(interp, lastArg - i, argv + i,
864 curInFile, curOutFile, curErrFile, &pid);
866 if (result != TCL_OK) {
869 Tcl_DStringFree(&execBuffer);
871 pidPtr[numPids] = pid;
875 * Close off our copies of file descriptors that were set up for
876 * this child, then set up the input for the next child.
879 if ((curInFile != NULL) && (curInFile != inputFile)) {
880 TclpCloseFile(curInFile);
885 if ((curOutFile != NULL) && (curOutFile != outputFile)) {
886 TclpCloseFile(curOutFile);
891 *pidArrayPtr = pidPtr;
894 * All done. Cleanup open files lying around and then return.
898 Tcl_DStringFree(&execBuffer);
901 TclpCloseFile(inputFile);
902 } else if (inputRelease) {
903 TclpReleaseFile(inputFile);
906 TclpCloseFile(outputFile);
907 } else if (outputRelease) {
908 TclpReleaseFile(outputFile);
911 TclpCloseFile(errorFile);
912 } else if (errorRelease) {
913 TclpReleaseFile(errorFile);
918 * An error occurred. There could have been extra files open, such
919 * as pipes between children. Clean them all up. Detach any child
920 * processes that have been created.
924 if (pipeIn != NULL) {
925 TclpCloseFile(pipeIn);
927 if ((curOutFile != NULL) && (curOutFile != outputFile)) {
928 TclpCloseFile(curOutFile);
930 if ((curInFile != NULL) && (curInFile != inputFile)) {
931 TclpCloseFile(curInFile);
933 if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
934 TclpCloseFile(*inPipePtr);
937 if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
938 TclpCloseFile(*outPipePtr);
941 if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
942 TclpCloseFile(*errFilePtr);
945 if (pidPtr != NULL) {
946 for (i = 0; i < numPids; i++) {
947 if (pidPtr[i] != (Tcl_Pid) -1) {
948 Tcl_DetachPids(1, &pidPtr[i]);
951 ckfree((char *) pidPtr);
958 *----------------------------------------------------------------------
960 * Tcl_OpenCommandChannel --
962 * Opens an I/O channel to one or more subprocesses specified
963 * by argc and argv. The flags argument determines the
964 * disposition of the stdio handles. If the TCL_STDIN flag is
965 * set then the standard input for the first subprocess will
966 * be tied to the channel: writing to the channel will provide
967 * input to the subprocess. If TCL_STDIN is not set, then
968 * standard input for the first subprocess will be the same as
969 * this application's standard input. If TCL_STDOUT is set then
970 * standard output from the last subprocess can be read from the
971 * channel; otherwise it goes to this application's standard
972 * output. If TCL_STDERR is set, standard error output for all
973 * subprocesses is returned to the channel and results in an error
974 * when the channel is closed; otherwise it goes to this
975 * application's standard error. If TCL_ENFORCE_MODE is not set,
976 * then argc and argv can redirect the stdio handles to override
977 * TCL_STDIN, TCL_STDOUT, and TCL_STDERR; if it is set, then it
978 * is an error for argc and argv to override stdio channels for
979 * which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
982 * A new command channel, or NULL on failure with an error
983 * message left in interp.
986 * Creates processes, opens pipes.
988 *----------------------------------------------------------------------
992 Tcl_OpenCommandChannel(interp, argc, argv, flags)
993 Tcl_Interp *interp; /* Interpreter for error reporting. Can
995 int argc; /* How many arguments. */
996 CONST char **argv; /* Array of arguments for command pipe. */
997 int flags; /* Or'ed combination of TCL_STDIN, TCL_STDOUT,
998 * TCL_STDERR, and TCL_ENFORCE_MODE. */
1000 TclFile *inPipePtr, *outPipePtr, *errFilePtr;
1001 TclFile inPipe, outPipe, errFile;
1004 Tcl_Channel channel;
1006 inPipe = outPipe = errFile = NULL;
1008 inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
1009 outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
1010 errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
1012 numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
1013 outPipePtr, errFilePtr);
1020 * Verify that the pipes that were created satisfy the
1021 * readable/writable constraints.
1024 if (flags & TCL_ENFORCE_MODE) {
1025 if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
1026 Tcl_AppendResult(interp, "can't read output from command:",
1027 " standard output was redirected", (char *) NULL);
1030 if ((flags & TCL_STDIN) && (inPipe == NULL)) {
1031 Tcl_AppendResult(interp, "can't write input to command:",
1032 " standard input was redirected", (char *) NULL);
1037 channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
1040 if (channel == (Tcl_Channel) NULL) {
1041 Tcl_AppendResult(interp, "pipe for command could not be created",
1049 Tcl_DetachPids(numPids, pidPtr);
1050 ckfree((char *) pidPtr);
1052 if (inPipe != NULL) {
1053 TclpCloseFile(inPipe);
1055 if (outPipe != NULL) {
1056 TclpCloseFile(outPipe);
1058 if (errFile != NULL) {
1059 TclpCloseFile(errFile);