os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPipe.c
First public contribution.
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.
9 * Portions Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiaries. All rights reserved.
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 * RCS: @(#) $Id: tclPipe.c,v 1.7.2.5 2006/03/16 00:35:58 andreas_kupries Exp $
19 #if defined(__SYMBIAN32__)
20 #include "tclSymbianGlobals.h"
21 #include <sys/select.h>
24 * The following macros convert between TclFile's and fd's. The conversion
25 * simple involves shifting fd's up by one to ensure that no valid fd is ever
26 * the same as NULL. Note that this code is duplicated from tclUnixPipe.c
29 #define MakeFile(fd) ((TclFile)((fd)+1))
30 #define GetFd(file) (((int)file)-1)
35 * A linked list of the following structures is used to keep track
36 * of child processes that have been detached but haven't exited
37 * yet, so we can make sure that they're properly "reaped" (officially
38 * waited for) and don't lie around as zombies cluttering the
42 typedef struct Detached {
43 Tcl_Pid pid; /* Id of process that's been detached
44 * but isn't known to have exited. */
45 struct Detached *nextPtr; /* Next in list of all detached
49 static Detached *detList = NULL; /* List of all detached proceses. */
50 TCL_DECLARE_MUTEX(pipeMutex) /* Guard access to detList. */
53 * Declarations for local procedures defined in this file:
56 static TclFile FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
57 CONST char *spec, int atOk, CONST char *arg,
58 CONST char *nextArg, int flags, int *skipPtr,
59 int *closePtr, int *releasePtr));
62 *----------------------------------------------------------------------
66 * This procedure does much of the work of parsing redirection
67 * operators. It handles "@" if specified and allowed, and a file
68 * name, and opens the file if necessary.
71 * The return value is the descriptor number for the file. If an
72 * error occurs then NULL is returned and an error message is left
73 * in the interp's result. Several arguments are side-effected; see
74 * the argument list below for details.
79 *----------------------------------------------------------------------
83 FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
85 Tcl_Interp *interp; /* Intepreter to use for error reporting. */
86 CONST char *spec; /* Points to character just after
87 * redirection character. */
88 int atOK; /* Non-zero means that '@' notation can be
89 * used to specify a channel, zero means that
91 CONST char *arg; /* Pointer to entire argument containing
92 * spec: used for error reporting. */
93 CONST char *nextArg; /* Next argument in argc/argv array, if needed
94 * for file name or channel name. May be
96 int flags; /* Flags to use for opening file or to
97 * specify mode for channel. */
98 int *skipPtr; /* Filled with 1 if redirection target was
99 * in spec, 2 if it was in nextArg. */
100 int *closePtr; /* Filled with one if the caller should
101 * close the file when done with it, zero
105 int writing = (flags & O_WRONLY);
110 if ((atOK != 0) && (*spec == '@')) {
119 chan = Tcl_GetChannel(interp, spec, NULL);
120 if (chan == (Tcl_Channel) NULL) {
123 file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
125 Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
126 "\" wasn't opened for ",
127 ((writing) ? "writing" : "reading"), (char *) NULL);
134 * Be sure to flush output to the file, so that anything
135 * written by the child appears after stuff we've already
143 Tcl_DString nameString;
152 name = Tcl_TranslateFileName(interp, spec, &nameString);
156 file = TclpOpenFile(name, flags);
157 Tcl_DStringFree(&nameString);
159 Tcl_AppendResult(interp, "couldn't ",
160 ((writing) ? "write" : "read"), " file \"", spec, "\": ",
161 Tcl_PosixError(interp), (char *) NULL);
169 Tcl_AppendResult(interp, "can't specify \"", arg,
170 "\" as last word in command", (char *) NULL);
175 *----------------------------------------------------------------------
179 * This procedure is called to indicate that one or more child
180 * processes have been placed in background and will never be
181 * waited for; they should eventually be reaped by
182 * Tcl_ReapDetachedProcs.
190 *----------------------------------------------------------------------
194 Tcl_DetachPids(numPids, pidPtr)
195 int numPids; /* Number of pids to detach: gives size
196 * of array pointed to by pidPtr. */
197 Tcl_Pid *pidPtr; /* Array of pids to detach. */
199 register Detached *detPtr;
202 Tcl_MutexLock(&pipeMutex);
203 for (i = 0; i < numPids; i++) {
204 detPtr = (Detached *) ckalloc(sizeof(Detached));
205 detPtr->pid = pidPtr[i];
206 detPtr->nextPtr = detList;
209 Tcl_MutexUnlock(&pipeMutex);
214 *----------------------------------------------------------------------
216 * Tcl_ReapDetachedProcs --
218 * This procedure checks to see if any detached processes have
219 * exited and, if so, it "reaps" them by officially waiting on
220 * them. It should be called "occasionally" to make sure that
221 * all detached processes are eventually reaped.
227 * Processes are waited on, so that they can be reaped by the
230 *----------------------------------------------------------------------
234 Tcl_ReapDetachedProcs()
236 register Detached *detPtr;
237 Detached *nextPtr, *prevPtr;
241 Tcl_MutexLock(&pipeMutex);
242 for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
243 pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
244 if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {
246 detPtr = detPtr->nextPtr;
249 nextPtr = detPtr->nextPtr;
250 if (prevPtr == NULL) {
251 detList = detPtr->nextPtr;
253 prevPtr->nextPtr = detPtr->nextPtr;
255 ckfree((char *) detPtr);
258 Tcl_MutexUnlock(&pipeMutex);
262 *----------------------------------------------------------------------
264 * TclCleanupChildren --
266 * This is a utility procedure used to wait for child processes
267 * to exit, record information about abnormal exits, and then
268 * collect any stderr output generated by them.
271 * The return value is a standard Tcl result. If anything at
272 * weird happened with the child processes, TCL_ERROR is returned
273 * and a message is left in the interp's result.
276 * If the last character of the interp's result is a newline, then it
277 * is removed unless keepNewline is non-zero. File errorId gets
278 * closed, and pidPtr is freed back to the storage allocator.
280 *----------------------------------------------------------------------
284 TclCleanupChildren(interp, numPids, pidPtr, errorChan)
285 Tcl_Interp *interp; /* Used for error messages. */
286 int numPids; /* Number of entries in pidPtr array. */
287 Tcl_Pid *pidPtr; /* Array of process ids of children. */
288 Tcl_Channel errorChan; /* Channel for file containing stderr output
289 * from pipeline. NULL means there isn't any
293 int i, abnormalExit, anyErrorInfo;
295 WAIT_STATUS_TYPE waitStatus;
297 unsigned long resolvedPid;
300 for (i = 0; i < numPids; i++) {
302 * We need to get the resolved pid before we wait on it as
303 * the windows implimentation of Tcl_WaitPid deletes the
304 * information such that any following calls to TclpGetPid
307 resolvedPid = TclpGetPid(pidPtr[i]);
308 pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
309 if (pid == (Tcl_Pid) -1) {
311 if (interp != (Tcl_Interp *) NULL) {
312 msg = Tcl_PosixError(interp);
313 if (errno == ECHILD) {
315 * This changeup in message suggested by Mark Diekhans
316 * to remind people that ECHILD errors can occur on
317 * some systems if SIGCHLD isn't in its default state.
320 // change message to remove all references to signals.
322 "child process lost.";
325 "child process lost (is SIGCHLD ignored or trapped?)";
328 Tcl_AppendResult(interp, "error waiting for process to exit: ",
335 * Create error messages for unusual process exits. An
336 * extra newline gets appended to each error message, but
337 * it gets removed below (in the same fashion that an
338 * extra newline in the command's output is removed).
341 // process the return status of the child.
342 if (waitStatus != 0) {
343 char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
346 TclFormatInt(msg1, (long) resolvedPid);
348 if (waitStatus < 0) {
349 if (interp != (Tcl_Interp *) NULL) {
350 TclFormatInt(msg2, (long) waitStatus);
351 Tcl_AppendResult(interp,
352 "child wait status is error (pid ", msg1, ") status: ", msg2,
356 if (interp != (Tcl_Interp *) NULL) {
357 TclFormatInt(msg2, (long) waitStatus);
358 Tcl_SetErrorCode(interp, "CHILDSTATUS (pid: ", msg1, ") status: ", msg2,
364 if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
365 char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
368 TclFormatInt(msg1, (long) resolvedPid);
369 if (WIFEXITED(waitStatus)) {
370 if (interp != (Tcl_Interp *) NULL) {
371 TclFormatInt(msg2, WEXITSTATUS(waitStatus));
372 Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
376 } else if (WIFSIGNALED(waitStatus)) {
377 if (interp != (Tcl_Interp *) NULL) {
380 p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
381 Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
382 Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
384 Tcl_AppendResult(interp, "child killed: ", p, "\n",
387 } else if (WIFSTOPPED(waitStatus)) {
388 if (interp != (Tcl_Interp *) NULL) {
391 p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
392 Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
393 Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
395 Tcl_AppendResult(interp, "child suspended: ", p, "\n",
399 if (interp != (Tcl_Interp *) NULL) {
400 Tcl_AppendResult(interp,
401 "child wait status didn't make sense\n",
410 * Read the standard error file. If there's anything there,
411 * then return an error and add the file's contents to the result
416 if (errorChan != NULL) {
419 * Make sure we start at the beginning of the file.
422 if (interp != NULL) {
426 Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
427 objPtr = Tcl_NewObj();
428 count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
431 Tcl_DecrRefCount(objPtr);
432 Tcl_ResetResult(interp);
433 Tcl_AppendResult(interp, "error reading stderr output file: ",
434 Tcl_PosixError(interp), NULL);
435 } else if (count > 0) {
437 Tcl_SetObjResult(interp, objPtr);
440 Tcl_DecrRefCount(objPtr);
443 Tcl_Close(NULL, errorChan);
447 * If a child exited abnormally but didn't output any error information
448 * at all, generate an error message here.
451 if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
452 Tcl_AppendResult(interp, "child process exited abnormally",
459 *----------------------------------------------------------------------
461 * TclCreatePipeline --
463 * Given an argc/argv array, instantiate a pipeline of processes
464 * as described by the argv.
466 * This procedure is unofficially exported for use by BLT.
469 * The return value is a count of the number of new processes
470 * created, or -1 if an error occurred while creating the pipeline.
471 * *pidArrayPtr is filled in with the address of a dynamically
472 * allocated array giving the ids of all of the processes. It
473 * is up to the caller to free this array when it isn't needed
474 * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in
475 * with the file id for the input pipe for the pipeline (if any):
476 * the caller must eventually close this file. If outPipePtr
477 * isn't NULL, then *outPipePtr is filled in with the file id
478 * for the output pipe from the pipeline: the caller must close
479 * this file. If errFilePtr isn't NULL, then *errFilePtr is filled
480 * with a file id that may be used to read error output after the
481 * pipeline completes.
484 * Processes and pipes are created.
486 *----------------------------------------------------------------------
490 TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
491 outPipePtr, errFilePtr)
492 Tcl_Interp *interp; /* Interpreter to use for error reporting. */
493 int argc; /* Number of entries in argv. */
494 CONST char **argv; /* Array of strings describing commands in
495 * pipeline plus I/O redirection with <,
496 * <<, >, etc. Argv[argc] must be NULL. */
497 Tcl_Pid **pidArrayPtr; /* Word at *pidArrayPtr gets filled in with
498 * address of array of pids for processes
499 * in pipeline (first pid is first process
501 TclFile *inPipePtr; /* If non-NULL, input to the pipeline comes
502 * from a pipe (unless overridden by
503 * redirection in the command). The file
504 * id with which to write to this pipe is
505 * stored at *inPipePtr. NULL means command
506 * specified its own input source. */
507 TclFile *outPipePtr; /* If non-NULL, output to the pipeline goes
508 * to a pipe, unless overriden by redirection
509 * in the command. The file id with which to
510 * read frome this pipe is stored at
511 * *outPipePtr. NULL means command specified
512 * its own output sink. */
513 TclFile *errFilePtr; /* If non-NULL, all stderr output from the
514 * pipeline will go to a temporary file
515 * created here, and a descriptor to read
516 * the file will be left at *errFilePtr.
517 * The file will be removed already, so
518 * closing this descriptor will be the end
519 * of the file. If this is NULL, then
520 * all stderr output goes to our stderr.
521 * If the pipeline specifies redirection
522 * then the file will still be created
523 * but it will never get any data. */
525 Tcl_Pid *pidPtr = NULL; /* Points to malloc-ed array holding all
526 * the pids of child processes. */
527 int numPids; /* Actual number of processes that exist
528 * at *pidPtr right now. */
529 int cmdCount; /* Count of number of distinct commands
530 * found in argc/argv. */
531 CONST char *inputLiteral = NULL; /* If non-null, then this points to a
532 * string containing input data (specified
533 * via <<) to be piped to the first process
534 * in the pipeline. */
535 TclFile inputFile = NULL; /* If != NULL, gives file to use as input for
536 * first process in pipeline (specified via <
538 int inputClose = 0; /* If non-zero, then inputFile should be
539 * closed when cleaning up. */
540 int inputRelease = 0;
541 TclFile outputFile = NULL; /* Writable file for output from last command
542 * in pipeline (could be file or pipe). NULL
543 * means use stdout. */
544 int outputClose = 0; /* If non-zero, then outputFile should be
545 * closed when cleaning up. */
546 int outputRelease = 0;
547 TclFile errorFile = NULL; /* Writable file for error output from all
548 * commands in pipeline. NULL means use
550 int errorClose = 0; /* If non-zero, then errorFile should be
551 * closed when cleaning up. */
552 int errorRelease = 0;
555 int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0;
556 Tcl_DString execBuffer;
558 TclFile curInFile, curOutFile, curErrFile;
567 if (inPipePtr != NULL) {
570 if (outPipePtr != NULL) {
573 if (errFilePtr != NULL) {
577 Tcl_DStringInit(&execBuffer);
585 * First, scan through all the arguments to figure out the structure
586 * of the pipeline. Process all of the input and output redirection
587 * arguments and remove them from the argument list in the pipeline.
588 * Count the number of distinct processes (it's the number of "|"
589 * arguments plus one) but don't remove the "|" arguments because
590 * they'll be used in the second pass to seperate the individual
591 * child processes. Cannot start the child processes in this pass
592 * because the redirection symbols may appear anywhere in the
593 * command line -- e.g., the '<' that specifies the input to the
594 * entire pipe may appear at the very end of the argument list.
600 for (i = 0; i < argc; i++) {
610 if ((i == (lastBar + 1)) || (i == (argc - 1))) {
611 Tcl_SetResult(interp,
612 "illegal use of | or |& in command",
623 if (inputClose != 0) {
625 TclpCloseFile(inputFile);
627 if (inputRelease != 0) {
629 TclpReleaseFile(inputFile);
633 inputLiteral = p + 1;
635 if (*inputLiteral == '\0') {
636 inputLiteral = ((i + 1) == argc) ? NULL : argv[i + 1];
637 if (inputLiteral == NULL) {
638 Tcl_AppendResult(interp, "can't specify \"", argv[i],
639 "\" as last word in command", (char *) NULL);
645 nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
647 inputFile = FileForRedirect(interp, p, 1, argv[i],
648 nextArg, O_RDONLY, &skip, &inputClose, &inputRelease);
649 if (inputFile == NULL) {
657 flags = O_WRONLY | O_CREAT | O_TRUNC;
663 * Note that the O_APPEND flag only has an effect on POSIX
664 * platforms. On Windows, we just have to carry on regardless.
667 flags = O_WRONLY | O_CREAT | O_APPEND;
670 if (errorClose != 0) {
672 TclpCloseFile(errorFile);
679 * Close the old output file, but only if the error file is
683 if (outputClose != 0) {
685 if (errorFile == outputFile) {
688 TclpCloseFile(outputFile);
691 if (outputRelease != 0) {
693 if (errorFile == outputFile) {
696 TclpReleaseFile(outputFile);
699 nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
700 outputFile = FileForRedirect(interp, p, atOK, argv[i],
701 nextArg, flags, &skip, &outputClose, &outputRelease);
702 if (outputFile == NULL) {
706 if (errorClose != 0) {
708 TclpCloseFile(errorFile);
710 if (errorRelease != 0) {
712 TclpReleaseFile(errorFile);
714 errorFile = outputFile;
724 flags = O_WRONLY | O_CREAT | O_TRUNC;
728 flags = O_WRONLY | O_CREAT;
730 if (errorClose != 0) {
732 TclpCloseFile(errorFile);
734 if (errorRelease != 0) {
736 TclpReleaseFile(errorFile);
738 if (atOK && p[0] == '@' && p[1] == '1' && p[2] == '\0') {
740 * Special case handling of 2>@1 to redirect stderr to the
741 * exec/open output pipe as well. This is meant for the end
742 * of the command string, otherwise use |& between commands.
745 Tcl_AppendResult(interp, "must specify \"", argv[i],
746 "\" as last word in command", (char *) NULL);
749 errorFile = outputFile;
753 nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
754 errorFile = FileForRedirect(interp, p, atOK, argv[i],
755 nextArg, flags, &skip, &errorClose, &errorRelease);
756 if (errorFile == NULL) {
763 /* Got a command word, not a redirection */
769 for (j = i + skip; j < argc; j++) {
770 argv[j - skip] = argv[j];
778 /* We had a bar followed only by redirections. */
780 Tcl_SetResult(interp,
781 "illegal use of | or |& in command",
786 if (inputFile == NULL) {
787 if (inputLiteral != NULL) {
789 * The input for the first process is immediate data coming from
790 * Tcl. Create a temporary file for it and put the data into the
793 inputFile = TclpCreateTempFile(inputLiteral);
794 if (inputFile == NULL) {
795 Tcl_AppendResult(interp,
796 "couldn't create input file for command: ",
797 Tcl_PosixError(interp), (char *) NULL);
801 // keep the file name for IPC
802 strcpy(inFileName ,tmpFileName);
805 } else if (inPipePtr != NULL) {
807 * The input for the first process in the pipeline is to
808 * come from a pipe that can be written from by the caller.
812 fifoResult = mkfifo(inFileName,S_IXGRP);
815 //fifo creation failure.
816 fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
821 tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
823 if(tmpReadFifoFd == -1)
825 //Failed to open the Fifo
826 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
829 WriteFifoFd = open(inFileName,O_WRONLY);
831 if(WriteFifoFd == -1)
833 //Failed to open the Fifo
834 printf("\n*** failure Fifo Open ***\n");
839 *inPipePtr = MakeFile(WriteFifoFd);
840 inputFile = *inPipePtr;
844 if ( close(tmpReadFifoFd) != 0)
846 printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
850 if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
851 Tcl_AppendResult(interp,
852 "couldn't create input pipe for command: ",
853 Tcl_PosixError(interp), (char *) NULL);
860 * The input for the first process comes from stdin.
864 fifoResult = mkfifo(inFileName,S_IXGRP);
867 //fifo creation failure.
868 fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
873 tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
875 if(tmpReadFifoFd == -1)
877 //Failed to open the Fifo
878 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
882 WriteFifoFd = open(inFileName,O_WRONLY);
884 if(WriteFifoFd == -1)
886 //Failed to open the Fifo
887 printf("\n*** failure Fifo Open ***\n");
892 inputFile = MakeFile(WriteFifoFd);
896 if (dup2(0/*fd for TCL_STDIN*/ , WriteFifoFd) == -1)
899 "%d inputFile couldn't be redirected & dup failed ", errno);
903 if ( close(tmpReadFifoFd) != 0)
905 printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
909 strcpy(inFileName,"STD");
911 channel = Tcl_GetStdChannel(TCL_STDIN);
912 if (channel != NULL) {
913 inputFile = TclpMakeFile(channel, TCL_READABLE);
916 if (inputFile != NULL) {
922 if (outputFile == NULL) {
923 if (outPipePtr != NULL) {
925 * Output from the last process in the pipeline is to go to a
926 * pipe that can be read by the caller.
930 fifoResult = mkfifo(outFileName,S_IXGRP);
933 //fifo creation failure.
934 fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
939 ReadFifoFd = open(outFileName,O_RDONLY | O_NONBLOCK);
943 //Failed to open the Fifo
944 printf("\n*** failure Fifo Open ***\n");
949 *outPipePtr = MakeFile(ReadFifoFd);
950 outputFile = *outPipePtr;
954 if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
955 Tcl_AppendResult(interp,
956 "couldn't create output pipe for command: ",
957 Tcl_PosixError(interp), (char *) NULL);
964 * The output for the last process goes to stdout.
968 fifoResult = mkfifo(outFileName,S_IXGRP);
971 //fifo creation failure.
972 fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
977 ReadFifoFd = open(outFileName,O_RDONLY | O_NONBLOCK);
981 //Failed to open the Fifo
982 printf("\n*** failure Fifo Open ***\n");
987 outputFile = MakeFile(ReadFifoFd);
991 if (dup2(1/*fd for TCL_STDOUT*/ , ReadFifoFd) == -1)
994 "%d outputFile couldn't be redirected & dup failed ", errno);
999 strcpy(outFileName,"STD");
1001 channel = Tcl_GetStdChannel(TCL_STDOUT);
1003 outputFile = TclpMakeFile(channel, TCL_WRITABLE);
1004 if (outputFile != NULL) {
1012 if (errorFile == NULL) {
1013 if (errorToOutput == 2) {
1015 * Handle 2>@1 special case at end of cmd line
1017 errorFile = outputFile;
1018 #ifdef __SYMBIAN32__
1019 strcpy(errFileName, outFileName);
1021 } else if (errFilePtr != NULL) {
1023 * Set up the standard error output sink for the pipeline, if
1024 * requested. Use a temporary file which is opened, then deleted.
1025 * Could potentially just use pipe, but if it filled up it could
1026 * cause the pipeline to deadlock: we'd be waiting for processes
1027 * to complete before reading stderr, and processes couldn't
1028 * complete because stderr was backed up.
1031 errorFile = TclpCreateTempFile(NULL);
1032 if (errorFile == NULL) {
1033 Tcl_AppendResult(interp,
1034 "couldn't create error file for command: ",
1035 Tcl_PosixError(interp), (char *) NULL);
1038 #ifdef __SYMBIAN32__
1039 strcpy(errFileName ,tmpFileName);
1041 *errFilePtr = errorFile;
1044 * Errors from the pipeline go to stderr.
1047 channel = Tcl_GetStdChannel(TCL_STDERR);
1049 errorFile = TclpMakeFile(channel, TCL_WRITABLE);
1050 if (errorFile != NULL) {
1054 #ifdef __SYMBIAN32__
1055 strcpy(errFileName,"STD");
1061 * Scan through the argc array, creating a process for each
1062 * group of arguments between the "|" characters.
1065 Tcl_ReapDetachedProcs();
1066 pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
1068 curInFile = inputFile;
1070 for (i = 0; i < argc; i = lastArg + 1) {
1071 int result, joinThisError;
1073 CONST char *oldName;
1076 * Convert the program name into native form.
1079 if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
1084 * Find the end of the current segment of the pipeline.
1088 for (lastArg = i; lastArg < argc; lastArg++) {
1089 if (argv[lastArg][0] == '|') {
1090 if (argv[lastArg][1] == '\0') {
1093 if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
1101 * If this is the last segment, use the specified outputFile.
1102 * Otherwise create an intermediate pipe. pipeIn will become the
1103 * curInFile for the next segment of the pipe.
1106 if (lastArg == argc) {
1107 curOutFile = outputFile;
1109 argv[lastArg] = NULL;
1110 #ifdef __SYMBIAN32__
1111 tmpnam(outFileName);
1112 fifoResult = mkfifo(outFileName,S_IXGRP);
1113 if(fifoResult == -1)
1115 //fifo creation failure.
1116 fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
1121 ReadFifoFd = open(outFileName,O_RDONLY);
1123 if(ReadFifoFd == -1)
1125 //Failed to open the Fifo
1126 printf("\n*** failure Fifo Open ***\n");
1131 pipeIn = MakeFile(ReadFifoFd);
1135 if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
1136 Tcl_AppendResult(interp, "couldn't create pipe: ",
1137 Tcl_PosixError(interp), (char *) NULL);
1143 if (joinThisError != 0) {
1144 curErrFile = curOutFile;
1146 curErrFile = errorFile;
1150 * Restore argv[i], since a caller wouldn't expect the contents of
1151 * argv to be modified.
1155 argv[i] = Tcl_DStringValue(&execBuffer);
1156 result = TclpCreateProcess(interp, lastArg - i, argv + i,
1157 curInFile, curOutFile, curErrFile, &pid);
1159 if (result != TCL_OK) {
1162 Tcl_DStringFree(&execBuffer);
1164 pidPtr[numPids] = pid;
1168 * Close off our copies of file descriptors that were set up for
1169 * this child, then set up the input for the next child.
1172 if ((curInFile != NULL) && (curInFile != inputFile)) {
1173 TclpCloseFile(curInFile);
1178 if ((curOutFile != NULL) && (curOutFile != outputFile)) {
1179 TclpCloseFile(curOutFile);
1184 *pidArrayPtr = pidPtr;
1187 * All done. Cleanup open files lying around and then return.
1191 Tcl_DStringFree(&execBuffer);
1194 TclpCloseFile(inputFile);
1195 } else if (inputRelease) {
1196 TclpReleaseFile(inputFile);
1199 TclpCloseFile(outputFile);
1200 } else if (outputRelease) {
1201 TclpReleaseFile(outputFile);
1204 TclpCloseFile(errorFile);
1205 } else if (errorRelease) {
1206 TclpReleaseFile(errorFile);
1211 * An error occurred. There could have been extra files open, such
1212 * as pipes between children. Clean them all up. Detach any child
1213 * processes that have been created.
1217 if (pipeIn != NULL) {
1218 TclpCloseFile(pipeIn);
1220 if ((curOutFile != NULL) && (curOutFile != outputFile)) {
1221 TclpCloseFile(curOutFile);
1223 if ((curInFile != NULL) && (curInFile != inputFile)) {
1224 TclpCloseFile(curInFile);
1226 if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
1227 TclpCloseFile(*inPipePtr);
1230 if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
1231 TclpCloseFile(*outPipePtr);
1234 if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
1235 TclpCloseFile(*errFilePtr);
1238 if (pidPtr != NULL) {
1239 for (i = 0; i < numPids; i++) {
1240 if (pidPtr[i] != (Tcl_Pid) -1) {
1241 Tcl_DetachPids(1, &pidPtr[i]);
1244 ckfree((char *) pidPtr);
1251 *----------------------------------------------------------------------
1253 * Tcl_OpenCommandChannel --
1255 * Opens an I/O channel to one or more subprocesses specified
1256 * by argc and argv. The flags argument determines the
1257 * disposition of the stdio handles. If the TCL_STDIN flag is
1258 * set then the standard input for the first subprocess will
1259 * be tied to the channel: writing to the channel will provide
1260 * input to the subprocess. If TCL_STDIN is not set, then
1261 * standard input for the first subprocess will be the same as
1262 * this application's standard input. If TCL_STDOUT is set then
1263 * standard output from the last subprocess can be read from the
1264 * channel; otherwise it goes to this application's standard
1265 * output. If TCL_STDERR is set, standard error output for all
1266 * subprocesses is returned to the channel and results in an error
1267 * when the channel is closed; otherwise it goes to this
1268 * application's standard error. If TCL_ENFORCE_MODE is not set,
1269 * then argc and argv can redirect the stdio handles to override
1270 * TCL_STDIN, TCL_STDOUT, and TCL_STDERR; if it is set, then it
1271 * is an error for argc and argv to override stdio channels for
1272 * which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
1275 * A new command channel, or NULL on failure with an error
1276 * message left in interp.
1279 * Creates processes, opens pipes.
1281 *----------------------------------------------------------------------
1284 EXPORT_C Tcl_Channel
1285 Tcl_OpenCommandChannel(interp, argc, argv, flags)
1286 Tcl_Interp *interp; /* Interpreter for error reporting. Can
1288 int argc; /* How many arguments. */
1289 CONST char **argv; /* Array of arguments for command pipe. */
1290 int flags; /* Or'ed combination of TCL_STDIN, TCL_STDOUT,
1291 * TCL_STDERR, and TCL_ENFORCE_MODE. */
1293 TclFile *inPipePtr, *outPipePtr, *errFilePtr;
1294 TclFile inPipe, outPipe, errFile;
1297 Tcl_Channel channel;
1299 inPipe = outPipe = errFile = NULL;
1301 inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
1302 outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
1303 errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
1305 numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
1306 outPipePtr, errFilePtr);
1313 * Verify that the pipes that were created satisfy the
1314 * readable/writable constraints.
1317 if (flags & TCL_ENFORCE_MODE) {
1318 if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
1319 Tcl_AppendResult(interp, "can't read output from command:",
1320 " standard output was redirected", (char *) NULL);
1323 if ((flags & TCL_STDIN) && (inPipe == NULL)) {
1324 Tcl_AppendResult(interp, "can't write input to command:",
1325 " standard input was redirected", (char *) NULL);
1330 channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
1333 if (channel == (Tcl_Channel) NULL) {
1334 Tcl_AppendResult(interp, "pipe for command could not be created",
1342 Tcl_DetachPids(numPids, pidPtr);
1343 ckfree((char *) pidPtr);
1345 if (inPipe != NULL) {
1346 TclpCloseFile(inPipe);
1348 if (outPipe != NULL) {
1349 TclpCloseFile(outPipe);
1351 if (errFile != NULL) {
1352 TclpCloseFile(errFile);