os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPipe.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclPipe.c --
     3  *
     4  *	This file contains the generic portion of the command channel
     5  *	driver as well as various utility routines used in managing
     6  *	subprocesses.
     7  *
     8  * Copyright (c) 1997 by Sun Microsystems, Inc.
     9  * Portions Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    10  *
    11  * See the file "license.terms" for information on usage and redistribution
    12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    13  *
    14  * RCS: @(#) $Id: tclPipe.c,v 1.7.2.5 2006/03/16 00:35:58 andreas_kupries Exp $
    15  */
    16 
    17 #include "tclInt.h"
    18 #include "tclPort.h"
    19 #if defined(__SYMBIAN32__)    
    20 #include "tclSymbianGlobals.h"
    21 #include <sys/select.h>
    22 
    23 /*
    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
    27  */
    28 
    29 #define MakeFile(fd) ((TclFile)((fd)+1))
    30 #define GetFd(file) (((int)file)-1)
    31 
    32 #endif
    33 
    34 /*
    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
    39  * system.
    40  */
    41 
    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
    46 					 * processes. */
    47 } Detached;
    48 
    49 static Detached *detList = NULL;	/* List of all detached proceses. */
    50 TCL_DECLARE_MUTEX(pipeMutex)		/* Guard access to detList. */
    51 
    52 /*
    53  * Declarations for local procedures defined in this file:
    54  */
    55 
    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));
    60 
    61 /*
    62  *----------------------------------------------------------------------
    63  *
    64  * FileForRedirect --
    65  *
    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.
    69  *
    70  * Results:
    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.
    75  *
    76  * Side effects:
    77  *	None.
    78  *
    79  *----------------------------------------------------------------------
    80  */
    81 
    82 static TclFile
    83 FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
    84 	releasePtr)
    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
    90 				 * it isn't. */
    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 
    95 				 * NULL. */
    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
   102 				 * otherwise. */
   103     int *releasePtr;
   104 {
   105     int writing = (flags & O_WRONLY);
   106     Tcl_Channel chan;
   107     TclFile file;
   108 
   109     *skipPtr = 1;
   110     if ((atOK != 0)  && (*spec == '@')) {
   111 	spec++;
   112 	if (*spec == '\0') {
   113 	    spec = nextArg;
   114 	    if (spec == NULL) {
   115 		goto badLastArg;
   116 	    }
   117 	    *skipPtr = 2;
   118 	}
   119         chan = Tcl_GetChannel(interp, spec, NULL);
   120         if (chan == (Tcl_Channel) NULL) {
   121             return NULL;
   122         }
   123 	file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
   124         if (file == NULL) {
   125 	    Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
   126 		    "\" wasn't opened for ",
   127 		    ((writing) ? "writing" : "reading"), (char *) NULL);
   128             return NULL;
   129         }
   130 	*releasePtr = 1;
   131 	if (writing) {
   132 
   133 	    /*
   134 	     * Be sure to flush output to the file, so that anything
   135 	     * written by the child appears after stuff we've already
   136 	     * written.
   137 	     */
   138 
   139             Tcl_Flush(chan);
   140 	}
   141     } else {
   142 	CONST char *name;
   143 	Tcl_DString nameString;
   144 
   145 	if (*spec == '\0') {
   146 	    spec = nextArg;
   147 	    if (spec == NULL) {
   148 		goto badLastArg;
   149 	    }
   150 	    *skipPtr = 2;
   151 	}
   152 	name = Tcl_TranslateFileName(interp, spec, &nameString);
   153 	if (name == NULL) {
   154 	    return NULL;
   155 	}
   156 	file = TclpOpenFile(name, flags);
   157 	Tcl_DStringFree(&nameString);
   158 	if (file == NULL) {
   159 	    Tcl_AppendResult(interp, "couldn't ",
   160 		    ((writing) ? "write" : "read"), " file \"", spec, "\": ",
   161 		    Tcl_PosixError(interp), (char *) NULL);
   162 	    return NULL;
   163 	}
   164         *closePtr = 1;
   165     }
   166     return file;
   167 
   168     badLastArg:
   169     Tcl_AppendResult(interp, "can't specify \"", arg,
   170 	    "\" as last word in command", (char *) NULL);
   171     return NULL;
   172 }
   173 
   174 /*
   175  *----------------------------------------------------------------------
   176  *
   177  * Tcl_DetachPids --
   178  *
   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.
   183  *
   184  * Results:
   185  *	None.
   186  *
   187  * Side effects:
   188  *	None.
   189  *
   190  *----------------------------------------------------------------------
   191  */
   192 
   193 EXPORT_C void
   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. */
   198 {
   199     register Detached *detPtr;
   200     int i;
   201 
   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;
   207 	detList = detPtr;
   208     }
   209     Tcl_MutexUnlock(&pipeMutex);
   210 
   211 }
   212 
   213 /*
   214  *----------------------------------------------------------------------
   215  *
   216  * Tcl_ReapDetachedProcs --
   217  *
   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.
   222  *
   223  * Results:
   224  *	None.
   225  *
   226  * Side effects:
   227  *	Processes are waited on, so that they can be reaped by the
   228  *	system.
   229  *
   230  *----------------------------------------------------------------------
   231  */
   232 
   233 EXPORT_C void
   234 Tcl_ReapDetachedProcs()
   235 {
   236     register Detached *detPtr;
   237     Detached *nextPtr, *prevPtr;
   238     int status;
   239     Tcl_Pid pid;
   240 
   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))) {
   245 	    prevPtr = detPtr;
   246 	    detPtr = detPtr->nextPtr;
   247 	    continue;
   248 	}
   249 	nextPtr = detPtr->nextPtr;
   250 	if (prevPtr == NULL) {
   251 	    detList = detPtr->nextPtr;
   252 	} else {
   253 	    prevPtr->nextPtr = detPtr->nextPtr;
   254 	}
   255 	ckfree((char *) detPtr);
   256 	detPtr = nextPtr;
   257     }
   258     Tcl_MutexUnlock(&pipeMutex);
   259 }
   260 
   261 /*
   262  *----------------------------------------------------------------------
   263  *
   264  * TclCleanupChildren --
   265  *
   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.
   269  *
   270  * Results:
   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.
   274  *
   275  * Side effects:
   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.
   279  *
   280  *----------------------------------------------------------------------
   281  */
   282 
   283 int
   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
   290 				 * stderr output. */
   291 {
   292     int result = TCL_OK;
   293     int i, abnormalExit, anyErrorInfo;
   294     Tcl_Pid pid;
   295     WAIT_STATUS_TYPE waitStatus;
   296     CONST char *msg;
   297     unsigned long resolvedPid;
   298 
   299     abnormalExit = 0;
   300     for (i = 0; i < numPids; i++) {
   301 	/*
   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
   305 	 * fail.
   306 	 */
   307 	resolvedPid = TclpGetPid(pidPtr[i]);
   308         pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
   309 	if (pid == (Tcl_Pid) -1) {
   310 	    result = TCL_ERROR;
   311             if (interp != (Tcl_Interp *) NULL) {
   312                 msg = Tcl_PosixError(interp);
   313                 if (errno == ECHILD) {
   314 		    /*
   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.
   318                      */
   319 #ifdef __SYMBIAN32__  
   320 					// change message to remove all references to signals.				
   321                     msg =
   322                         "child process lost.";
   323 #else
   324                     msg =
   325                         "child process lost (is SIGCHLD ignored or trapped?)";
   326 #endif                                             
   327                 }
   328                 Tcl_AppendResult(interp, "error waiting for process to exit: ",
   329                         msg, (char *) NULL);
   330             }
   331 	    continue;
   332 	}
   333 
   334 	/*
   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).
   339 	 */	 
   340 #ifdef __SYMBIAN32__  
   341 	// process the return status of the child.
   342 	if (waitStatus != 0) {
   343 	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
   344 
   345 	    result = TCL_ERROR;
   346 	    TclFormatInt(msg1, (long) resolvedPid);
   347 	    
   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,
   353                             (char *) NULL);
   354                 }
   355 	    	} else { 
   356                 if (interp != (Tcl_Interp *) NULL) {
   357 	    			TclFormatInt(msg2, (long) waitStatus);
   358                     Tcl_SetErrorCode(interp, "CHILDSTATUS (pid: ", msg1, ") status: ", msg2,
   359                             (char *) NULL);
   360                 }
   361 			abnormalExit = 1;
   362 	    	}
   363 #else
   364 	if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
   365 	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
   366 
   367 	    result = TCL_ERROR;
   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,
   373                             (char *) NULL);
   374                 }
   375 		abnormalExit = 1;
   376 	    } else if (WIFSIGNALED(waitStatus)) {
   377                 if (interp != (Tcl_Interp *) NULL) {
   378                     CONST char *p;
   379                     
   380                     p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
   381                     Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
   382                             Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
   383                             (char *) NULL);
   384                     Tcl_AppendResult(interp, "child killed: ", p, "\n",
   385                             (char *) NULL);
   386                 }
   387 	    } else if (WIFSTOPPED(waitStatus)) {
   388                 if (interp != (Tcl_Interp *) NULL) {
   389                     CONST char *p;
   390 
   391                     p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
   392                     Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
   393                             Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
   394                             p, (char *) NULL);
   395                     Tcl_AppendResult(interp, "child suspended: ", p, "\n",
   396                             (char *) NULL);
   397                 }
   398 	    } else {
   399                 if (interp != (Tcl_Interp *) NULL) {
   400                     Tcl_AppendResult(interp,
   401                             "child wait status didn't make sense\n",
   402                             (char *) NULL);
   403                 }
   404 	    }
   405 #endif	    
   406 	}
   407     }
   408 
   409     /*
   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
   412      * string.
   413      */
   414 
   415     anyErrorInfo = 0;
   416     if (errorChan != NULL) {
   417 
   418 	/*
   419 	 * Make sure we start at the beginning of the file.
   420 	 */
   421 
   422         if (interp != NULL) {
   423 	    int count;
   424 	    Tcl_Obj *objPtr;
   425 	    
   426 	    Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
   427 	    objPtr = Tcl_NewObj();
   428 	    count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
   429 	    if (count < 0) {
   430 		result = TCL_ERROR;
   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) {
   436 		anyErrorInfo = 1;
   437 		Tcl_SetObjResult(interp, objPtr);
   438 		result = TCL_ERROR;
   439 	    } else {
   440 		Tcl_DecrRefCount(objPtr);
   441 	    }
   442 	}
   443 	Tcl_Close(NULL, errorChan);
   444     }
   445 
   446     /*
   447      * If a child exited abnormally but didn't output any error information
   448      * at all, generate an error message here.
   449      */
   450 
   451     if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
   452 	Tcl_AppendResult(interp, "child process exited abnormally",
   453 		(char *) NULL);
   454     }
   455     return result;
   456 }
   457 
   458 /*
   459  *----------------------------------------------------------------------
   460  *
   461  * TclCreatePipeline --
   462  *
   463  *	Given an argc/argv array, instantiate a pipeline of processes
   464  *	as described by the argv.
   465  *
   466  *	This procedure is unofficially exported for use by BLT.
   467  *
   468  * Results:
   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.
   482  *
   483  * Side effects:
   484  *	Processes and pipes are created.
   485  *
   486  *----------------------------------------------------------------------
   487  */
   488 
   489 int
   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
   500 				 * in pipeline). */
   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. */
   524 {
   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 <
   537 				 * or <@). */
   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
   549 				 * stderr. */
   550     int errorClose = 0;		/* If non-zero, then errorFile should be 
   551     				 * closed when cleaning up. */
   552     int errorRelease = 0;
   553     CONST char *p;
   554     CONST char *nextArg;
   555     int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0;
   556     Tcl_DString execBuffer;
   557     TclFile pipeIn;
   558     TclFile curInFile, curOutFile, curErrFile;
   559     Tcl_Channel channel;
   560 #ifdef __SYMBIAN32__      
   561     int fifoResult;
   562     int ReadFifoFd;
   563     int WriteFifoFd;
   564     int tmpReadFifoFd;
   565 #endif    
   566 
   567     if (inPipePtr != NULL) {
   568 	*inPipePtr = NULL;
   569     }
   570     if (outPipePtr != NULL) {
   571 	*outPipePtr = NULL;
   572     }
   573     if (errFilePtr != NULL) {
   574 	*errFilePtr = NULL;
   575     }
   576 
   577     Tcl_DStringInit(&execBuffer);
   578     
   579     pipeIn = NULL;
   580     curInFile = NULL;
   581     curOutFile = NULL;
   582     numPids = 0;
   583 
   584     /*
   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.
   595      */
   596 
   597     lastBar = -1;
   598     cmdCount = 1;
   599     needCmd = 1;
   600     for (i = 0; i < argc; i++) {
   601 	errorToOutput = 0;
   602 	skip = 0;
   603 	p = argv[i];
   604 	switch (*p++) {
   605 	case '|':
   606 	    if (*p == '&') {
   607 		p++;
   608 	    }
   609 	    if (*p == '\0') {
   610 		if ((i == (lastBar + 1)) || (i == (argc - 1))) {
   611 		    Tcl_SetResult(interp,
   612 			    "illegal use of | or |& in command",
   613 			    TCL_STATIC);
   614 		    goto error;
   615 		}
   616 	    }
   617 	    lastBar = i;
   618 	    cmdCount++;
   619 	    needCmd = 1;
   620 	    break;
   621 
   622 	case '<':
   623 	    if (inputClose != 0) {
   624 		inputClose = 0;
   625 		TclpCloseFile(inputFile);
   626 	    }
   627 	    if (inputRelease != 0) {
   628 		inputRelease = 0;
   629 		TclpReleaseFile(inputFile);
   630 	    }
   631 	    if (*p == '<') {
   632 		inputFile = NULL;
   633 		inputLiteral = p + 1;
   634 		skip = 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);
   640 			goto error;
   641 		    }
   642 		    skip = 2;
   643 		}
   644 	    } else {
   645 		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
   646 		inputLiteral = NULL;
   647 		inputFile = FileForRedirect(interp, p, 1, argv[i], 
   648 			nextArg, O_RDONLY, &skip, &inputClose, &inputRelease);
   649 		if (inputFile == NULL) {
   650 		    goto error;
   651 		}
   652 	    }
   653 	    break;
   654 
   655 	case '>':
   656 	    atOK = 1;
   657 	    flags = O_WRONLY | O_CREAT | O_TRUNC;
   658 	    if (*p == '>') {
   659 		p++;
   660 		atOK = 0;
   661 
   662 		/*
   663 		 * Note that the O_APPEND flag only has an effect on POSIX
   664 		 * platforms. On Windows, we just have to carry on regardless.
   665 		 */
   666 
   667 		flags = O_WRONLY | O_CREAT | O_APPEND;
   668 	    }
   669 	    if (*p == '&') {
   670 		if (errorClose != 0) {
   671 		    errorClose = 0;
   672 		    TclpCloseFile(errorFile);
   673 		}
   674 		errorToOutput = 1;
   675 		p++;
   676 	    }
   677 
   678 	    /*
   679 	     * Close the old output file, but only if the error file is
   680 	     * not also using it.
   681 	     */
   682 
   683 	    if (outputClose != 0) {
   684 		outputClose = 0;
   685 		if (errorFile == outputFile) {
   686 		    errorClose = 1;
   687 		} else {
   688 		    TclpCloseFile(outputFile);
   689 		}
   690 	    }
   691 	    if (outputRelease != 0) {
   692 		outputRelease = 0;
   693 		if (errorFile == outputFile) {
   694 		    errorRelease = 1;
   695 		} else {
   696 		    TclpReleaseFile(outputFile);
   697 		}
   698 	    }
   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) {
   703 		goto error;
   704 	    }
   705 	    if (errorToOutput) {
   706 		if (errorClose != 0) {
   707 		    errorClose = 0;
   708 		    TclpCloseFile(errorFile);
   709 		}
   710 		if (errorRelease != 0) {
   711 		    errorRelease = 0;
   712 		    TclpReleaseFile(errorFile);
   713 		}
   714 		errorFile = outputFile;
   715 	    }
   716 	    break;
   717 
   718 	case '2':
   719 	    if (*p != '>') {
   720 		break;
   721 	    }
   722 	    p++;
   723 	    atOK = 1;
   724 	    flags = O_WRONLY | O_CREAT | O_TRUNC;
   725 	    if (*p == '>') {
   726 		p++;
   727 		atOK = 0;
   728 		flags = O_WRONLY | O_CREAT;
   729 	    }
   730 	    if (errorClose != 0) {
   731 		errorClose = 0;
   732 		TclpCloseFile(errorFile);
   733 	    }
   734 	    if (errorRelease != 0) {
   735 		errorRelease = 0;
   736 		TclpReleaseFile(errorFile);
   737 	    }
   738 	    if (atOK && p[0] == '@' && p[1] == '1' && p[2] == '\0') {
   739 		/*
   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.
   743 		 */
   744 		if (i != argc - 1) {
   745 		    Tcl_AppendResult(interp, "must specify \"", argv[i],
   746 			    "\" as last word in command", (char *) NULL);
   747 		    goto error;
   748 		}
   749 		errorFile = outputFile;
   750 		errorToOutput = 2;
   751 		skip = 1;
   752 	    } else {
   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) {
   757 		    goto error;
   758 		}
   759 	    }
   760 	    break;
   761 
   762 	default:
   763 	  /* Got a command word, not a redirection */
   764 	  needCmd = 0;
   765 	  break;
   766 	}
   767 
   768 	if (skip != 0) {
   769 	    for (j = i + skip; j < argc; j++) {
   770 		argv[j - skip] = argv[j];
   771 	    }
   772 	    argc -= skip;
   773 	    i -= 1;
   774 	}
   775     }
   776 
   777     if (needCmd) {
   778         /* We had a bar followed only by redirections. */
   779 
   780         Tcl_SetResult(interp,
   781 		      "illegal use of | or |& in command",
   782 		      TCL_STATIC);
   783 	goto error;
   784     }
   785 
   786     if (inputFile == NULL) {
   787 	if (inputLiteral != NULL) {
   788 	    /*
   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
   791 	     * file.
   792 	     */
   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);
   798 		goto error;
   799 	    }
   800 #ifdef __SYMBIAN32__  	    
   801 	    // keep the file name for IPC
   802 	    strcpy(inFileName ,tmpFileName);
   803 #endif	    
   804 	    inputClose = 1;
   805 	} else if (inPipePtr != NULL) {
   806 	    /*
   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.
   809 	     */
   810 #ifdef __SYMBIAN32__  	    
   811     	tmpnam(inFileName);
   812     	fifoResult = mkfifo(inFileName,S_IXGRP);
   813 		if(fifoResult == -1)
   814    		{
   815       		//fifo creation failure.
   816       		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   817       		goto error;
   818    		}
   819    		else
   820    		{
   821    			tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
   822  
   823          	if(tmpReadFifoFd == -1)
   824          	{
   825            	 //Failed to open the Fifo
   826            	 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
   827            	 goto error;
   828          	}
   829    			WriteFifoFd = open(inFileName,O_WRONLY);
   830  
   831          	if(WriteFifoFd == -1)
   832          	{
   833            	 //Failed to open the Fifo
   834            	 printf("\n*** failure Fifo Open ***\n");
   835            	 goto error;
   836          	}
   837         	 else
   838         	{
   839          	   *inPipePtr = MakeFile(WriteFifoFd);     
   840          	  inputFile = *inPipePtr;
   841          	}
   842    		}
   843    		
   844    		if ( close(tmpReadFifoFd) != 0)
   845    		{
   846    			printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
   847             goto error;	
   848    		}
   849 #else
   850 	    if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
   851 		Tcl_AppendResult(interp, 
   852 			"couldn't create input pipe for command: ",
   853 			Tcl_PosixError(interp), (char *) NULL);
   854 		goto error;
   855 	    }
   856 	    inputClose = 1;
   857 #endif	    
   858 	} else {
   859 	    /*
   860 	     * The input for the first process comes from stdin.
   861 	     */
   862 #ifdef __SYMBIAN32__  	    
   863 	    tmpnam(inFileName);
   864     	fifoResult = mkfifo(inFileName,S_IXGRP);
   865 		if(fifoResult == -1)
   866    		{
   867       		//fifo creation failure.
   868       		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   869       		goto error;
   870    		}
   871    		else
   872    		{
   873    			tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
   874  
   875          	if(tmpReadFifoFd == -1)
   876          	{
   877            	 //Failed to open the Fifo
   878            	 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
   879            	 goto error;
   880          	}
   881          	
   882    			WriteFifoFd = open(inFileName,O_WRONLY);
   883  
   884          	if(WriteFifoFd == -1)
   885          	{
   886            	 //Failed to open the Fifo
   887            	 printf("\n*** failure Fifo Open ***\n");
   888            	 goto error;
   889          	}
   890         	 else
   891         	{
   892          	   inputFile = MakeFile(WriteFifoFd);
   893          	}
   894    		}
   895    		
   896    		if (dup2(0/*fd for TCL_STDIN*/ , WriteFifoFd) == -1)
   897    		{
   898    			fprintf(stderr,
   899 			    "%d inputFile couldn't be redirected & dup failed ", errno);
   900 			goto error;
   901    		}
   902    		
   903    		if ( close(tmpReadFifoFd) != 0)
   904    		{
   905    			printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
   906 			goto error;
   907    		}
   908 
   909 		strcpy(inFileName,"STD");
   910 #else
   911 	    channel = Tcl_GetStdChannel(TCL_STDIN);
   912 	    if (channel != NULL) {
   913 		inputFile = TclpMakeFile(channel, TCL_READABLE);
   914 		}
   915 #endif   		
   916    		if (inputFile != NULL) {
   917 		    inputRelease = 1;
   918 		}
   919 	}
   920     }
   921 
   922     if (outputFile == NULL) {
   923 	if (outPipePtr != NULL) {
   924 	    /*
   925 	     * Output from the last process in the pipeline is to go to a
   926 	     * pipe that can be read by the caller.
   927 	     */
   928 #ifdef __SYMBIAN32__  	    	     
   929     	tmpnam(outFileName);
   930 		fifoResult = mkfifo(outFileName,S_IXGRP);
   931 		if(fifoResult == -1)
   932    		{
   933       		//fifo creation failure.
   934       		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   935       		goto error;
   936    		}
   937    		else
   938    		{
   939    			ReadFifoFd  = open(outFileName,O_RDONLY | O_NONBLOCK);
   940  
   941          	if(ReadFifoFd  == -1)
   942          	{
   943            	 //Failed to open the Fifo
   944            	 printf("\n*** failure Fifo Open ***\n");
   945            	 goto error;
   946          	}
   947         	 else
   948         	{
   949          	   *outPipePtr = MakeFile(ReadFifoFd);
   950           	  outputFile = *outPipePtr;
   951          	}
   952    		}
   953 #else   		
   954 	    if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
   955 		Tcl_AppendResult(interp, 
   956 			"couldn't create output pipe for command: ",
   957 			Tcl_PosixError(interp), (char *) NULL);
   958 		goto error;
   959 	    }
   960 	    outputClose = 1;	    
   961 #endif	    
   962 	} else {
   963 	    /*
   964 	     * The output for the last process goes to stdout.
   965 	     */
   966 #ifdef __SYMBIAN32__
   967 	    tmpnam(outFileName);
   968 		fifoResult = mkfifo(outFileName,S_IXGRP);
   969 		if(fifoResult == -1)
   970    		{
   971       		//fifo creation failure.
   972       		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   973       		goto error;
   974    		}
   975    		else
   976    		{
   977    			ReadFifoFd  = open(outFileName,O_RDONLY | O_NONBLOCK);
   978  
   979          	if(ReadFifoFd  == -1)
   980          	{
   981            	 //Failed to open the Fifo
   982            	 printf("\n*** failure Fifo Open ***\n");
   983            	 goto error;
   984          	}
   985         	 else
   986         	{
   987          	   outputFile = MakeFile(ReadFifoFd);
   988          	}
   989    		}
   990    	
   991    		if (dup2(1/*fd for TCL_STDOUT*/ , ReadFifoFd) == -1)
   992    		{
   993    			fprintf(stderr,
   994 			    "%d outputFile couldn't be redirected & dup failed ", errno);
   995 			goto error;
   996    		}
   997    		
   998    		outputRelease = 1;
   999 		strcpy(outFileName,"STD");
  1000 #else
  1001 	    channel = Tcl_GetStdChannel(TCL_STDOUT);
  1002 	    if (channel) {
  1003 		outputFile = TclpMakeFile(channel, TCL_WRITABLE);
  1004 		if (outputFile != NULL) {
  1005 		    outputRelease = 1;
  1006 		}
  1007 	    }
  1008 #endif		
  1009 	}
  1010     }
  1011 
  1012     if (errorFile == NULL) {
  1013 	if (errorToOutput == 2) {
  1014 	    /*
  1015 	     * Handle 2>@1 special case at end of cmd line
  1016 	     */
  1017 	    errorFile = outputFile;
  1018 #ifdef __SYMBIAN32__
  1019 	    strcpy(errFileName, outFileName);
  1020 #endif
  1021 	} else if (errFilePtr != NULL) {
  1022 	    /*
  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.
  1029 	     */
  1030 
  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);
  1036 		goto error;
  1037 	    }
  1038 #ifdef __SYMBIAN32__	    
  1039 	    strcpy(errFileName ,tmpFileName);
  1040 #endif	    
  1041 	    *errFilePtr = errorFile;
  1042 	} else {
  1043 	    /*
  1044 	     * Errors from the pipeline go to stderr.
  1045 	     */
  1046 
  1047 	    channel = Tcl_GetStdChannel(TCL_STDERR);
  1048 	    if (channel) {
  1049 		errorFile = TclpMakeFile(channel, TCL_WRITABLE);
  1050 		if (errorFile != NULL) {
  1051 		    errorRelease = 1;
  1052 		}
  1053 	    }
  1054 #ifdef __SYMBIAN32__	    	    
  1055 		strcpy(errFileName,"STD");
  1056 #endif		
  1057 	}
  1058     }
  1059 	
  1060     /*
  1061      * Scan through the argc array, creating a process for each
  1062      * group of arguments between the "|" characters.
  1063      */
  1064 
  1065     Tcl_ReapDetachedProcs();
  1066     pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
  1067 
  1068     curInFile = inputFile;
  1069 
  1070     for (i = 0; i < argc; i = lastArg + 1) { 
  1071 	int result, joinThisError;
  1072 	Tcl_Pid pid;
  1073 	CONST char *oldName;
  1074 
  1075 	/*
  1076 	 * Convert the program name into native form. 
  1077 	 */
  1078 
  1079 	if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
  1080 	    goto error;
  1081 	}
  1082 
  1083 	/*
  1084 	 * Find the end of the current segment of the pipeline.
  1085 	 */
  1086 
  1087 	joinThisError = 0;
  1088 	for (lastArg = i; lastArg < argc; lastArg++) {
  1089 	    if (argv[lastArg][0] == '|') { 
  1090 		if (argv[lastArg][1] == '\0') { 
  1091 		    break;
  1092 		}
  1093 		if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
  1094 		    joinThisError = 1;
  1095 		    break;
  1096 		}
  1097 	    }
  1098 	}
  1099 
  1100 	/*
  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.
  1104 	 */
  1105 
  1106 	if (lastArg == argc) {
  1107 	    curOutFile = outputFile;
  1108 	} else {
  1109 	    argv[lastArg] = NULL;
  1110 #ifdef __SYMBIAN32__	    	    
  1111 	    tmpnam(outFileName);
  1112 		fifoResult = mkfifo(outFileName,S_IXGRP);
  1113 		if(fifoResult == -1)
  1114    		{
  1115       		//fifo creation failure.
  1116       		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
  1117       		goto error;
  1118    		}
  1119    		else
  1120    		{
  1121    			ReadFifoFd  = open(outFileName,O_RDONLY);
  1122  
  1123          	if(ReadFifoFd  == -1)
  1124          	{
  1125            	 //Failed to open the Fifo
  1126            	 printf("\n*** failure Fifo Open ***\n");
  1127            	 goto error;
  1128          	}
  1129         	 else
  1130         	{
  1131          	   pipeIn = MakeFile(ReadFifoFd);
  1132          	}
  1133    		}
  1134 #else   		
  1135 	    if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
  1136 		Tcl_AppendResult(interp, "couldn't create pipe: ",
  1137 			Tcl_PosixError(interp), (char *) NULL);
  1138 		goto error;
  1139 	    }
  1140 #endif	    
  1141 	}
  1142 
  1143 	if (joinThisError != 0) {
  1144 	    curErrFile = curOutFile;
  1145 	} else {
  1146 	    curErrFile = errorFile;
  1147 	}
  1148 
  1149 	/*
  1150 	 * Restore argv[i], since a caller wouldn't expect the contents of
  1151 	 * argv to be modified.
  1152 	 */
  1153 	 
  1154 	oldName = argv[i];
  1155 	argv[i] = Tcl_DStringValue(&execBuffer);
  1156 	result = TclpCreateProcess(interp, lastArg - i, argv + i,
  1157 		curInFile, curOutFile, curErrFile, &pid);
  1158 	argv[i] = oldName;
  1159 	if (result != TCL_OK) {
  1160 	    goto error;
  1161 	}
  1162 	Tcl_DStringFree(&execBuffer);
  1163 
  1164 	pidPtr[numPids] = pid;
  1165 	numPids++;
  1166 
  1167 	/*
  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.
  1170 	 */
  1171 
  1172 	if ((curInFile != NULL) && (curInFile != inputFile)) {
  1173 	    TclpCloseFile(curInFile);
  1174 	}
  1175 	curInFile = pipeIn;
  1176 	pipeIn = NULL;
  1177 
  1178 	if ((curOutFile != NULL) && (curOutFile != outputFile)) {
  1179 	    TclpCloseFile(curOutFile);
  1180 	}
  1181 	curOutFile = NULL;
  1182     }
  1183 
  1184     *pidArrayPtr = pidPtr;
  1185 
  1186     /*
  1187      * All done.  Cleanup open files lying around and then return.
  1188      */
  1189 
  1190 cleanup:
  1191     Tcl_DStringFree(&execBuffer);
  1192 
  1193     if (inputClose) {
  1194 	TclpCloseFile(inputFile);
  1195     } else if (inputRelease) {
  1196 	TclpReleaseFile(inputFile);
  1197     }
  1198     if (outputClose) {
  1199 	TclpCloseFile(outputFile);
  1200     } else if (outputRelease) {
  1201 	TclpReleaseFile(outputFile);
  1202     }
  1203     if (errorClose) {
  1204 	TclpCloseFile(errorFile);
  1205     } else if (errorRelease) {
  1206 	TclpReleaseFile(errorFile);
  1207     }
  1208     return numPids;
  1209 
  1210     /*
  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.
  1214      */
  1215 
  1216 error:
  1217     if (pipeIn != NULL) {
  1218 	TclpCloseFile(pipeIn);
  1219     }
  1220     if ((curOutFile != NULL) && (curOutFile != outputFile)) {
  1221 	TclpCloseFile(curOutFile);
  1222     }
  1223     if ((curInFile != NULL) && (curInFile != inputFile)) {
  1224 	TclpCloseFile(curInFile);
  1225     }
  1226     if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
  1227 	TclpCloseFile(*inPipePtr);
  1228 	*inPipePtr = NULL;
  1229     }
  1230     if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
  1231 	TclpCloseFile(*outPipePtr);
  1232 	*outPipePtr = NULL;
  1233     }
  1234     if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
  1235 	TclpCloseFile(*errFilePtr);
  1236 	*errFilePtr = NULL;
  1237     }
  1238     if (pidPtr != NULL) {
  1239 	for (i = 0; i < numPids; i++) {
  1240 	    if (pidPtr[i] != (Tcl_Pid) -1) {
  1241 		Tcl_DetachPids(1, &pidPtr[i]);
  1242 	    }
  1243 	}
  1244 	ckfree((char *) pidPtr);
  1245     }
  1246     numPids = -1;
  1247     goto cleanup;
  1248 }
  1249 
  1250 /*
  1251  *----------------------------------------------------------------------
  1252  *
  1253  * Tcl_OpenCommandChannel --
  1254  *
  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.
  1273  *
  1274  * Results:
  1275  *	A new command channel, or NULL on failure with an error
  1276  *	message left in interp.
  1277  *
  1278  * Side effects:
  1279  *	Creates processes, opens pipes.
  1280  *
  1281  *----------------------------------------------------------------------
  1282  */
  1283 
  1284 EXPORT_C Tcl_Channel
  1285 Tcl_OpenCommandChannel(interp, argc, argv, flags)
  1286     Tcl_Interp *interp;		/* Interpreter for error reporting. Can
  1287                                  * NOT be NULL. */
  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. */
  1292 {
  1293     TclFile *inPipePtr, *outPipePtr, *errFilePtr;
  1294     TclFile inPipe, outPipe, errFile;
  1295     int numPids;
  1296     Tcl_Pid *pidPtr;
  1297     Tcl_Channel channel;
  1298 
  1299     inPipe = outPipe = errFile = NULL;
  1300 
  1301     inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
  1302     outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
  1303     errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
  1304     
  1305     numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
  1306             outPipePtr, errFilePtr);
  1307 
  1308     if (numPids < 0) {
  1309 	goto error;
  1310     }
  1311 
  1312     /*
  1313      * Verify that the pipes that were created satisfy the
  1314      * readable/writable constraints. 
  1315      */
  1316 
  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);
  1321 	    goto error;
  1322 	}
  1323 	if ((flags & TCL_STDIN) && (inPipe == NULL)) {
  1324 	    Tcl_AppendResult(interp, "can't write input to command:",
  1325 		    " standard input was redirected", (char *) NULL);
  1326 	    goto error;
  1327 	}
  1328     }
  1329     
  1330     channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
  1331 	    numPids, pidPtr);
  1332 
  1333     if (channel == (Tcl_Channel) NULL) {
  1334         Tcl_AppendResult(interp, "pipe for command could not be created",
  1335                 (char *) NULL);
  1336 	goto error;
  1337     }
  1338     return channel;
  1339 
  1340 error:
  1341     if (numPids > 0) {
  1342 	Tcl_DetachPids(numPids, pidPtr);
  1343 	ckfree((char *) pidPtr);
  1344     }
  1345     if (inPipe != NULL) {
  1346 	TclpCloseFile(inPipe);
  1347     }
  1348     if (outPipe != NULL) {
  1349 	TclpCloseFile(outPipe);
  1350     }
  1351     if (errFile != NULL) {
  1352 	TclpCloseFile(errFile);
  1353     }
  1354     return NULL;
  1355 }