os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPipe.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPipe.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1355 @@
     1.4 +/* 
     1.5 + * tclPipe.c --
     1.6 + *
     1.7 + *	This file contains the generic portion of the command channel
     1.8 + *	driver as well as various utility routines used in managing
     1.9 + *	subprocesses.
    1.10 + *
    1.11 + * Copyright (c) 1997 by Sun Microsystems, Inc.
    1.12 + * Portions Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    1.13 + *
    1.14 + * See the file "license.terms" for information on usage and redistribution
    1.15 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.16 + *
    1.17 + * RCS: @(#) $Id: tclPipe.c,v 1.7.2.5 2006/03/16 00:35:58 andreas_kupries Exp $
    1.18 + */
    1.19 +
    1.20 +#include "tclInt.h"
    1.21 +#include "tclPort.h"
    1.22 +#if defined(__SYMBIAN32__)    
    1.23 +#include "tclSymbianGlobals.h"
    1.24 +#include <sys/select.h>
    1.25 +
    1.26 +/*
    1.27 + * The following macros convert between TclFile's and fd's.  The conversion
    1.28 + * simple involves shifting fd's up by one to ensure that no valid fd is ever
    1.29 + * the same as NULL.  Note that this code is duplicated from tclUnixPipe.c
    1.30 + */
    1.31 +
    1.32 +#define MakeFile(fd) ((TclFile)((fd)+1))
    1.33 +#define GetFd(file) (((int)file)-1)
    1.34 +
    1.35 +#endif
    1.36 +
    1.37 +/*
    1.38 + * A linked list of the following structures is used to keep track
    1.39 + * of child processes that have been detached but haven't exited
    1.40 + * yet, so we can make sure that they're properly "reaped" (officially
    1.41 + * waited for) and don't lie around as zombies cluttering the
    1.42 + * system.
    1.43 + */
    1.44 +
    1.45 +typedef struct Detached {
    1.46 +    Tcl_Pid pid;			/* Id of process that's been detached
    1.47 +					 * but isn't known to have exited. */
    1.48 +    struct Detached *nextPtr;		/* Next in list of all detached
    1.49 +					 * processes. */
    1.50 +} Detached;
    1.51 +
    1.52 +static Detached *detList = NULL;	/* List of all detached proceses. */
    1.53 +TCL_DECLARE_MUTEX(pipeMutex)		/* Guard access to detList. */
    1.54 +
    1.55 +/*
    1.56 + * Declarations for local procedures defined in this file:
    1.57 + */
    1.58 +
    1.59 +static TclFile	FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
    1.60 +	            CONST char *spec, int atOk, CONST char *arg, 
    1.61 +		    CONST char *nextArg, int flags, int *skipPtr,
    1.62 +		    int *closePtr, int *releasePtr));
    1.63 +
    1.64 +/*
    1.65 + *----------------------------------------------------------------------
    1.66 + *
    1.67 + * FileForRedirect --
    1.68 + *
    1.69 + *	This procedure does much of the work of parsing redirection
    1.70 + *	operators.  It handles "@" if specified and allowed, and a file
    1.71 + *	name, and opens the file if necessary.
    1.72 + *
    1.73 + * Results:
    1.74 + *	The return value is the descriptor number for the file.  If an
    1.75 + *	error occurs then NULL is returned and an error message is left
    1.76 + *	in the interp's result.  Several arguments are side-effected; see
    1.77 + *	the argument list below for details.
    1.78 + *
    1.79 + * Side effects:
    1.80 + *	None.
    1.81 + *
    1.82 + *----------------------------------------------------------------------
    1.83 + */
    1.84 +
    1.85 +static TclFile
    1.86 +FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
    1.87 +	releasePtr)
    1.88 +    Tcl_Interp *interp;		/* Intepreter to use for error reporting. */
    1.89 +    CONST char *spec;		/* Points to character just after
    1.90 +				 * redirection character. */
    1.91 +    int atOK;			/* Non-zero means that '@' notation can be 
    1.92 +				 * used to specify a channel, zero means that
    1.93 +				 * it isn't. */
    1.94 +    CONST char *arg;		/* Pointer to entire argument containing 
    1.95 +				 * spec:  used for error reporting. */
    1.96 +    CONST char *nextArg;	/* Next argument in argc/argv array, if needed 
    1.97 +				 * for file name or channel name.  May be 
    1.98 +				 * NULL. */
    1.99 +    int flags;			/* Flags to use for opening file or to 
   1.100 +				 * specify mode for channel. */
   1.101 +    int *skipPtr;		/* Filled with 1 if redirection target was
   1.102 +				 * in spec, 2 if it was in nextArg. */
   1.103 +    int *closePtr;		/* Filled with one if the caller should 
   1.104 +				 * close the file when done with it, zero
   1.105 +				 * otherwise. */
   1.106 +    int *releasePtr;
   1.107 +{
   1.108 +    int writing = (flags & O_WRONLY);
   1.109 +    Tcl_Channel chan;
   1.110 +    TclFile file;
   1.111 +
   1.112 +    *skipPtr = 1;
   1.113 +    if ((atOK != 0)  && (*spec == '@')) {
   1.114 +	spec++;
   1.115 +	if (*spec == '\0') {
   1.116 +	    spec = nextArg;
   1.117 +	    if (spec == NULL) {
   1.118 +		goto badLastArg;
   1.119 +	    }
   1.120 +	    *skipPtr = 2;
   1.121 +	}
   1.122 +        chan = Tcl_GetChannel(interp, spec, NULL);
   1.123 +        if (chan == (Tcl_Channel) NULL) {
   1.124 +            return NULL;
   1.125 +        }
   1.126 +	file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
   1.127 +        if (file == NULL) {
   1.128 +	    Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
   1.129 +		    "\" wasn't opened for ",
   1.130 +		    ((writing) ? "writing" : "reading"), (char *) NULL);
   1.131 +            return NULL;
   1.132 +        }
   1.133 +	*releasePtr = 1;
   1.134 +	if (writing) {
   1.135 +
   1.136 +	    /*
   1.137 +	     * Be sure to flush output to the file, so that anything
   1.138 +	     * written by the child appears after stuff we've already
   1.139 +	     * written.
   1.140 +	     */
   1.141 +
   1.142 +            Tcl_Flush(chan);
   1.143 +	}
   1.144 +    } else {
   1.145 +	CONST char *name;
   1.146 +	Tcl_DString nameString;
   1.147 +
   1.148 +	if (*spec == '\0') {
   1.149 +	    spec = nextArg;
   1.150 +	    if (spec == NULL) {
   1.151 +		goto badLastArg;
   1.152 +	    }
   1.153 +	    *skipPtr = 2;
   1.154 +	}
   1.155 +	name = Tcl_TranslateFileName(interp, spec, &nameString);
   1.156 +	if (name == NULL) {
   1.157 +	    return NULL;
   1.158 +	}
   1.159 +	file = TclpOpenFile(name, flags);
   1.160 +	Tcl_DStringFree(&nameString);
   1.161 +	if (file == NULL) {
   1.162 +	    Tcl_AppendResult(interp, "couldn't ",
   1.163 +		    ((writing) ? "write" : "read"), " file \"", spec, "\": ",
   1.164 +		    Tcl_PosixError(interp), (char *) NULL);
   1.165 +	    return NULL;
   1.166 +	}
   1.167 +        *closePtr = 1;
   1.168 +    }
   1.169 +    return file;
   1.170 +
   1.171 +    badLastArg:
   1.172 +    Tcl_AppendResult(interp, "can't specify \"", arg,
   1.173 +	    "\" as last word in command", (char *) NULL);
   1.174 +    return NULL;
   1.175 +}
   1.176 +
   1.177 +/*
   1.178 + *----------------------------------------------------------------------
   1.179 + *
   1.180 + * Tcl_DetachPids --
   1.181 + *
   1.182 + *	This procedure is called to indicate that one or more child
   1.183 + *	processes have been placed in background and will never be
   1.184 + *	waited for;  they should eventually be reaped by
   1.185 + *	Tcl_ReapDetachedProcs.
   1.186 + *
   1.187 + * Results:
   1.188 + *	None.
   1.189 + *
   1.190 + * Side effects:
   1.191 + *	None.
   1.192 + *
   1.193 + *----------------------------------------------------------------------
   1.194 + */
   1.195 +
   1.196 +EXPORT_C void
   1.197 +Tcl_DetachPids(numPids, pidPtr)
   1.198 +    int numPids;		/* Number of pids to detach:  gives size
   1.199 +				 * of array pointed to by pidPtr. */
   1.200 +    Tcl_Pid *pidPtr;		/* Array of pids to detach. */
   1.201 +{
   1.202 +    register Detached *detPtr;
   1.203 +    int i;
   1.204 +
   1.205 +    Tcl_MutexLock(&pipeMutex);
   1.206 +    for (i = 0; i < numPids; i++) {
   1.207 +	detPtr = (Detached *) ckalloc(sizeof(Detached));
   1.208 +	detPtr->pid = pidPtr[i];
   1.209 +	detPtr->nextPtr = detList;
   1.210 +	detList = detPtr;
   1.211 +    }
   1.212 +    Tcl_MutexUnlock(&pipeMutex);
   1.213 +
   1.214 +}
   1.215 +
   1.216 +/*
   1.217 + *----------------------------------------------------------------------
   1.218 + *
   1.219 + * Tcl_ReapDetachedProcs --
   1.220 + *
   1.221 + *	This procedure checks to see if any detached processes have
   1.222 + *	exited and, if so, it "reaps" them by officially waiting on
   1.223 + *	them.  It should be called "occasionally" to make sure that
   1.224 + *	all detached processes are eventually reaped.
   1.225 + *
   1.226 + * Results:
   1.227 + *	None.
   1.228 + *
   1.229 + * Side effects:
   1.230 + *	Processes are waited on, so that they can be reaped by the
   1.231 + *	system.
   1.232 + *
   1.233 + *----------------------------------------------------------------------
   1.234 + */
   1.235 +
   1.236 +EXPORT_C void
   1.237 +Tcl_ReapDetachedProcs()
   1.238 +{
   1.239 +    register Detached *detPtr;
   1.240 +    Detached *nextPtr, *prevPtr;
   1.241 +    int status;
   1.242 +    Tcl_Pid pid;
   1.243 +
   1.244 +    Tcl_MutexLock(&pipeMutex);
   1.245 +    for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
   1.246 +	pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
   1.247 +	if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {
   1.248 +	    prevPtr = detPtr;
   1.249 +	    detPtr = detPtr->nextPtr;
   1.250 +	    continue;
   1.251 +	}
   1.252 +	nextPtr = detPtr->nextPtr;
   1.253 +	if (prevPtr == NULL) {
   1.254 +	    detList = detPtr->nextPtr;
   1.255 +	} else {
   1.256 +	    prevPtr->nextPtr = detPtr->nextPtr;
   1.257 +	}
   1.258 +	ckfree((char *) detPtr);
   1.259 +	detPtr = nextPtr;
   1.260 +    }
   1.261 +    Tcl_MutexUnlock(&pipeMutex);
   1.262 +}
   1.263 +
   1.264 +/*
   1.265 + *----------------------------------------------------------------------
   1.266 + *
   1.267 + * TclCleanupChildren --
   1.268 + *
   1.269 + *	This is a utility procedure used to wait for child processes
   1.270 + *	to exit, record information about abnormal exits, and then
   1.271 + *	collect any stderr output generated by them.
   1.272 + *
   1.273 + * Results:
   1.274 + *	The return value is a standard Tcl result.  If anything at
   1.275 + *	weird happened with the child processes, TCL_ERROR is returned
   1.276 + *	and a message is left in the interp's result.
   1.277 + *
   1.278 + * Side effects:
   1.279 + *	If the last character of the interp's result is a newline, then it
   1.280 + *	is removed unless keepNewline is non-zero.  File errorId gets
   1.281 + *	closed, and pidPtr is freed back to the storage allocator.
   1.282 + *
   1.283 + *----------------------------------------------------------------------
   1.284 + */
   1.285 +
   1.286 +int
   1.287 +TclCleanupChildren(interp, numPids, pidPtr, errorChan)
   1.288 +    Tcl_Interp *interp;		/* Used for error messages. */
   1.289 +    int numPids;		/* Number of entries in pidPtr array. */
   1.290 +    Tcl_Pid *pidPtr;		/* Array of process ids of children. */
   1.291 +    Tcl_Channel errorChan;	/* Channel for file containing stderr output
   1.292 +				 * from pipeline.  NULL means there isn't any
   1.293 +				 * stderr output. */
   1.294 +{
   1.295 +    int result = TCL_OK;
   1.296 +    int i, abnormalExit, anyErrorInfo;
   1.297 +    Tcl_Pid pid;
   1.298 +    WAIT_STATUS_TYPE waitStatus;
   1.299 +    CONST char *msg;
   1.300 +    unsigned long resolvedPid;
   1.301 +
   1.302 +    abnormalExit = 0;
   1.303 +    for (i = 0; i < numPids; i++) {
   1.304 +	/*
   1.305 +	 * We need to get the resolved pid before we wait on it as
   1.306 +	 * the windows implimentation of Tcl_WaitPid deletes the
   1.307 +	 * information such that any following calls to TclpGetPid
   1.308 +	 * fail.
   1.309 +	 */
   1.310 +	resolvedPid = TclpGetPid(pidPtr[i]);
   1.311 +        pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
   1.312 +	if (pid == (Tcl_Pid) -1) {
   1.313 +	    result = TCL_ERROR;
   1.314 +            if (interp != (Tcl_Interp *) NULL) {
   1.315 +                msg = Tcl_PosixError(interp);
   1.316 +                if (errno == ECHILD) {
   1.317 +		    /*
   1.318 +                     * This changeup in message suggested by Mark Diekhans
   1.319 +                     * to remind people that ECHILD errors can occur on
   1.320 +                     * some systems if SIGCHLD isn't in its default state.
   1.321 +                     */
   1.322 +#ifdef __SYMBIAN32__  
   1.323 +					// change message to remove all references to signals.				
   1.324 +                    msg =
   1.325 +                        "child process lost.";
   1.326 +#else
   1.327 +                    msg =
   1.328 +                        "child process lost (is SIGCHLD ignored or trapped?)";
   1.329 +#endif                                             
   1.330 +                }
   1.331 +                Tcl_AppendResult(interp, "error waiting for process to exit: ",
   1.332 +                        msg, (char *) NULL);
   1.333 +            }
   1.334 +	    continue;
   1.335 +	}
   1.336 +
   1.337 +	/*
   1.338 +	 * Create error messages for unusual process exits.  An
   1.339 +	 * extra newline gets appended to each error message, but
   1.340 +	 * it gets removed below (in the same fashion that an
   1.341 +	 * extra newline in the command's output is removed).
   1.342 +	 */	 
   1.343 +#ifdef __SYMBIAN32__  
   1.344 +	// process the return status of the child.
   1.345 +	if (waitStatus != 0) {
   1.346 +	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
   1.347 +
   1.348 +	    result = TCL_ERROR;
   1.349 +	    TclFormatInt(msg1, (long) resolvedPid);
   1.350 +	    
   1.351 +	    	if (waitStatus < 0) {
   1.352 +                if (interp != (Tcl_Interp *) NULL) {
   1.353 +	    			TclFormatInt(msg2, (long) waitStatus);
   1.354 +                    Tcl_AppendResult(interp,
   1.355 +                            "child wait status is error (pid ", msg1, ") status: ", msg2,
   1.356 +                            (char *) NULL);
   1.357 +                }
   1.358 +	    	} else { 
   1.359 +                if (interp != (Tcl_Interp *) NULL) {
   1.360 +	    			TclFormatInt(msg2, (long) waitStatus);
   1.361 +                    Tcl_SetErrorCode(interp, "CHILDSTATUS (pid: ", msg1, ") status: ", msg2,
   1.362 +                            (char *) NULL);
   1.363 +                }
   1.364 +			abnormalExit = 1;
   1.365 +	    	}
   1.366 +#else
   1.367 +	if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
   1.368 +	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
   1.369 +
   1.370 +	    result = TCL_ERROR;
   1.371 +	    TclFormatInt(msg1, (long) resolvedPid);
   1.372 +	    if (WIFEXITED(waitStatus)) {
   1.373 +                if (interp != (Tcl_Interp *) NULL) {
   1.374 +		    TclFormatInt(msg2, WEXITSTATUS(waitStatus));
   1.375 +                    Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
   1.376 +                            (char *) NULL);
   1.377 +                }
   1.378 +		abnormalExit = 1;
   1.379 +	    } else if (WIFSIGNALED(waitStatus)) {
   1.380 +                if (interp != (Tcl_Interp *) NULL) {
   1.381 +                    CONST char *p;
   1.382 +                    
   1.383 +                    p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
   1.384 +                    Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
   1.385 +                            Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
   1.386 +                            (char *) NULL);
   1.387 +                    Tcl_AppendResult(interp, "child killed: ", p, "\n",
   1.388 +                            (char *) NULL);
   1.389 +                }
   1.390 +	    } else if (WIFSTOPPED(waitStatus)) {
   1.391 +                if (interp != (Tcl_Interp *) NULL) {
   1.392 +                    CONST char *p;
   1.393 +
   1.394 +                    p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
   1.395 +                    Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
   1.396 +                            Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
   1.397 +                            p, (char *) NULL);
   1.398 +                    Tcl_AppendResult(interp, "child suspended: ", p, "\n",
   1.399 +                            (char *) NULL);
   1.400 +                }
   1.401 +	    } else {
   1.402 +                if (interp != (Tcl_Interp *) NULL) {
   1.403 +                    Tcl_AppendResult(interp,
   1.404 +                            "child wait status didn't make sense\n",
   1.405 +                            (char *) NULL);
   1.406 +                }
   1.407 +	    }
   1.408 +#endif	    
   1.409 +	}
   1.410 +    }
   1.411 +
   1.412 +    /*
   1.413 +     * Read the standard error file.  If there's anything there,
   1.414 +     * then return an error and add the file's contents to the result
   1.415 +     * string.
   1.416 +     */
   1.417 +
   1.418 +    anyErrorInfo = 0;
   1.419 +    if (errorChan != NULL) {
   1.420 +
   1.421 +	/*
   1.422 +	 * Make sure we start at the beginning of the file.
   1.423 +	 */
   1.424 +
   1.425 +        if (interp != NULL) {
   1.426 +	    int count;
   1.427 +	    Tcl_Obj *objPtr;
   1.428 +	    
   1.429 +	    Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
   1.430 +	    objPtr = Tcl_NewObj();
   1.431 +	    count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
   1.432 +	    if (count < 0) {
   1.433 +		result = TCL_ERROR;
   1.434 +		Tcl_DecrRefCount(objPtr);
   1.435 +		Tcl_ResetResult(interp);
   1.436 +		Tcl_AppendResult(interp, "error reading stderr output file: ",
   1.437 +			Tcl_PosixError(interp), NULL);
   1.438 +	    } else if (count > 0) {
   1.439 +		anyErrorInfo = 1;
   1.440 +		Tcl_SetObjResult(interp, objPtr);
   1.441 +		result = TCL_ERROR;
   1.442 +	    } else {
   1.443 +		Tcl_DecrRefCount(objPtr);
   1.444 +	    }
   1.445 +	}
   1.446 +	Tcl_Close(NULL, errorChan);
   1.447 +    }
   1.448 +
   1.449 +    /*
   1.450 +     * If a child exited abnormally but didn't output any error information
   1.451 +     * at all, generate an error message here.
   1.452 +     */
   1.453 +
   1.454 +    if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
   1.455 +	Tcl_AppendResult(interp, "child process exited abnormally",
   1.456 +		(char *) NULL);
   1.457 +    }
   1.458 +    return result;
   1.459 +}
   1.460 +
   1.461 +/*
   1.462 + *----------------------------------------------------------------------
   1.463 + *
   1.464 + * TclCreatePipeline --
   1.465 + *
   1.466 + *	Given an argc/argv array, instantiate a pipeline of processes
   1.467 + *	as described by the argv.
   1.468 + *
   1.469 + *	This procedure is unofficially exported for use by BLT.
   1.470 + *
   1.471 + * Results:
   1.472 + *	The return value is a count of the number of new processes
   1.473 + *	created, or -1 if an error occurred while creating the pipeline.
   1.474 + *	*pidArrayPtr is filled in with the address of a dynamically
   1.475 + *	allocated array giving the ids of all of the processes.  It
   1.476 + *	is up to the caller to free this array when it isn't needed
   1.477 + *	anymore.  If inPipePtr is non-NULL, *inPipePtr is filled in
   1.478 + *	with the file id for the input pipe for the pipeline (if any):
   1.479 + *	the caller must eventually close this file.  If outPipePtr
   1.480 + *	isn't NULL, then *outPipePtr is filled in with the file id
   1.481 + *	for the output pipe from the pipeline:  the caller must close
   1.482 + *	this file.  If errFilePtr isn't NULL, then *errFilePtr is filled
   1.483 + *	with a file id that may be used to read error output after the
   1.484 + *	pipeline completes.
   1.485 + *
   1.486 + * Side effects:
   1.487 + *	Processes and pipes are created.
   1.488 + *
   1.489 + *----------------------------------------------------------------------
   1.490 + */
   1.491 +
   1.492 +int
   1.493 +TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
   1.494 +	outPipePtr, errFilePtr)
   1.495 +    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
   1.496 +    int argc;			/* Number of entries in argv. */
   1.497 +    CONST char **argv;		/* Array of strings describing commands in
   1.498 +				 * pipeline plus I/O redirection with <,
   1.499 +				 * <<,  >, etc.  Argv[argc] must be NULL. */
   1.500 +    Tcl_Pid **pidArrayPtr;	/* Word at *pidArrayPtr gets filled in with
   1.501 +				 * address of array of pids for processes
   1.502 +				 * in pipeline (first pid is first process
   1.503 +				 * in pipeline). */
   1.504 +    TclFile *inPipePtr;		/* If non-NULL, input to the pipeline comes
   1.505 +				 * from a pipe (unless overridden by
   1.506 +				 * redirection in the command).  The file
   1.507 +				 * id with which to write to this pipe is
   1.508 +				 * stored at *inPipePtr.  NULL means command
   1.509 +				 * specified its own input source. */
   1.510 +    TclFile *outPipePtr;	/* If non-NULL, output to the pipeline goes
   1.511 +				 * to a pipe, unless overriden by redirection
   1.512 +				 * in the command.  The file id with which to
   1.513 +				 * read frome this pipe is stored at
   1.514 +				 * *outPipePtr.  NULL means command specified
   1.515 +				 * its own output sink. */
   1.516 +    TclFile *errFilePtr;	/* If non-NULL, all stderr output from the
   1.517 +				 * pipeline will go to a temporary file
   1.518 +				 * created here, and a descriptor to read
   1.519 +				 * the file will be left at *errFilePtr.
   1.520 +				 * The file will be removed already, so
   1.521 +				 * closing this descriptor will be the end
   1.522 +				 * of the file.  If this is NULL, then
   1.523 +				 * all stderr output goes to our stderr.
   1.524 +				 * If the pipeline specifies redirection
   1.525 +				 * then the file will still be created
   1.526 +				 * but it will never get any data. */
   1.527 +{
   1.528 +    Tcl_Pid *pidPtr = NULL;	/* Points to malloc-ed array holding all
   1.529 +				 * the pids of child processes. */
   1.530 +    int numPids;		/* Actual number of processes that exist
   1.531 +				 * at *pidPtr right now. */
   1.532 +    int cmdCount;		/* Count of number of distinct commands
   1.533 +				 * found in argc/argv. */
   1.534 +    CONST char *inputLiteral = NULL;	/* If non-null, then this points to a
   1.535 +				 * string containing input data (specified
   1.536 +				 * via <<) to be piped to the first process
   1.537 +				 * in the pipeline. */
   1.538 +    TclFile inputFile = NULL;	/* If != NULL, gives file to use as input for
   1.539 +				 * first process in pipeline (specified via <
   1.540 +				 * or <@). */
   1.541 +    int inputClose = 0;		/* If non-zero, then inputFile should be 
   1.542 +    				 * closed when cleaning up. */
   1.543 +    int inputRelease = 0;
   1.544 +    TclFile outputFile = NULL;	/* Writable file for output from last command
   1.545 +				 * in pipeline (could be file or pipe).  NULL
   1.546 +				 * means use stdout. */
   1.547 +    int outputClose = 0;	/* If non-zero, then outputFile should be 
   1.548 +    				 * closed when cleaning up. */
   1.549 +    int outputRelease = 0;
   1.550 +    TclFile errorFile = NULL;	/* Writable file for error output from all
   1.551 +				 * commands in pipeline.  NULL means use
   1.552 +				 * stderr. */
   1.553 +    int errorClose = 0;		/* If non-zero, then errorFile should be 
   1.554 +    				 * closed when cleaning up. */
   1.555 +    int errorRelease = 0;
   1.556 +    CONST char *p;
   1.557 +    CONST char *nextArg;
   1.558 +    int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0;
   1.559 +    Tcl_DString execBuffer;
   1.560 +    TclFile pipeIn;
   1.561 +    TclFile curInFile, curOutFile, curErrFile;
   1.562 +    Tcl_Channel channel;
   1.563 +#ifdef __SYMBIAN32__      
   1.564 +    int fifoResult;
   1.565 +    int ReadFifoFd;
   1.566 +    int WriteFifoFd;
   1.567 +    int tmpReadFifoFd;
   1.568 +#endif    
   1.569 +
   1.570 +    if (inPipePtr != NULL) {
   1.571 +	*inPipePtr = NULL;
   1.572 +    }
   1.573 +    if (outPipePtr != NULL) {
   1.574 +	*outPipePtr = NULL;
   1.575 +    }
   1.576 +    if (errFilePtr != NULL) {
   1.577 +	*errFilePtr = NULL;
   1.578 +    }
   1.579 +
   1.580 +    Tcl_DStringInit(&execBuffer);
   1.581 +    
   1.582 +    pipeIn = NULL;
   1.583 +    curInFile = NULL;
   1.584 +    curOutFile = NULL;
   1.585 +    numPids = 0;
   1.586 +
   1.587 +    /*
   1.588 +     * First, scan through all the arguments to figure out the structure
   1.589 +     * of the pipeline.  Process all of the input and output redirection
   1.590 +     * arguments and remove them from the argument list in the pipeline.
   1.591 +     * Count the number of distinct processes (it's the number of "|"
   1.592 +     * arguments plus one) but don't remove the "|" arguments because 
   1.593 +     * they'll be used in the second pass to seperate the individual 
   1.594 +     * child processes.  Cannot start the child processes in this pass 
   1.595 +     * because the redirection symbols may appear anywhere in the 
   1.596 +     * command line -- e.g., the '<' that specifies the input to the 
   1.597 +     * entire pipe may appear at the very end of the argument list.
   1.598 +     */
   1.599 +
   1.600 +    lastBar = -1;
   1.601 +    cmdCount = 1;
   1.602 +    needCmd = 1;
   1.603 +    for (i = 0; i < argc; i++) {
   1.604 +	errorToOutput = 0;
   1.605 +	skip = 0;
   1.606 +	p = argv[i];
   1.607 +	switch (*p++) {
   1.608 +	case '|':
   1.609 +	    if (*p == '&') {
   1.610 +		p++;
   1.611 +	    }
   1.612 +	    if (*p == '\0') {
   1.613 +		if ((i == (lastBar + 1)) || (i == (argc - 1))) {
   1.614 +		    Tcl_SetResult(interp,
   1.615 +			    "illegal use of | or |& in command",
   1.616 +			    TCL_STATIC);
   1.617 +		    goto error;
   1.618 +		}
   1.619 +	    }
   1.620 +	    lastBar = i;
   1.621 +	    cmdCount++;
   1.622 +	    needCmd = 1;
   1.623 +	    break;
   1.624 +
   1.625 +	case '<':
   1.626 +	    if (inputClose != 0) {
   1.627 +		inputClose = 0;
   1.628 +		TclpCloseFile(inputFile);
   1.629 +	    }
   1.630 +	    if (inputRelease != 0) {
   1.631 +		inputRelease = 0;
   1.632 +		TclpReleaseFile(inputFile);
   1.633 +	    }
   1.634 +	    if (*p == '<') {
   1.635 +		inputFile = NULL;
   1.636 +		inputLiteral = p + 1;
   1.637 +		skip = 1;
   1.638 +		if (*inputLiteral == '\0') {
   1.639 +		    inputLiteral = ((i + 1) == argc) ? NULL : argv[i + 1];
   1.640 +		    if (inputLiteral == NULL) {
   1.641 +			Tcl_AppendResult(interp, "can't specify \"", argv[i],
   1.642 +				"\" as last word in command", (char *) NULL);
   1.643 +			goto error;
   1.644 +		    }
   1.645 +		    skip = 2;
   1.646 +		}
   1.647 +	    } else {
   1.648 +		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
   1.649 +		inputLiteral = NULL;
   1.650 +		inputFile = FileForRedirect(interp, p, 1, argv[i], 
   1.651 +			nextArg, O_RDONLY, &skip, &inputClose, &inputRelease);
   1.652 +		if (inputFile == NULL) {
   1.653 +		    goto error;
   1.654 +		}
   1.655 +	    }
   1.656 +	    break;
   1.657 +
   1.658 +	case '>':
   1.659 +	    atOK = 1;
   1.660 +	    flags = O_WRONLY | O_CREAT | O_TRUNC;
   1.661 +	    if (*p == '>') {
   1.662 +		p++;
   1.663 +		atOK = 0;
   1.664 +
   1.665 +		/*
   1.666 +		 * Note that the O_APPEND flag only has an effect on POSIX
   1.667 +		 * platforms. On Windows, we just have to carry on regardless.
   1.668 +		 */
   1.669 +
   1.670 +		flags = O_WRONLY | O_CREAT | O_APPEND;
   1.671 +	    }
   1.672 +	    if (*p == '&') {
   1.673 +		if (errorClose != 0) {
   1.674 +		    errorClose = 0;
   1.675 +		    TclpCloseFile(errorFile);
   1.676 +		}
   1.677 +		errorToOutput = 1;
   1.678 +		p++;
   1.679 +	    }
   1.680 +
   1.681 +	    /*
   1.682 +	     * Close the old output file, but only if the error file is
   1.683 +	     * not also using it.
   1.684 +	     */
   1.685 +
   1.686 +	    if (outputClose != 0) {
   1.687 +		outputClose = 0;
   1.688 +		if (errorFile == outputFile) {
   1.689 +		    errorClose = 1;
   1.690 +		} else {
   1.691 +		    TclpCloseFile(outputFile);
   1.692 +		}
   1.693 +	    }
   1.694 +	    if (outputRelease != 0) {
   1.695 +		outputRelease = 0;
   1.696 +		if (errorFile == outputFile) {
   1.697 +		    errorRelease = 1;
   1.698 +		} else {
   1.699 +		    TclpReleaseFile(outputFile);
   1.700 +		}
   1.701 +	    }
   1.702 +	    nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
   1.703 +	    outputFile = FileForRedirect(interp, p, atOK, argv[i], 
   1.704 +		    nextArg, flags, &skip, &outputClose, &outputRelease);
   1.705 +	    if (outputFile == NULL) {
   1.706 +		goto error;
   1.707 +	    }
   1.708 +	    if (errorToOutput) {
   1.709 +		if (errorClose != 0) {
   1.710 +		    errorClose = 0;
   1.711 +		    TclpCloseFile(errorFile);
   1.712 +		}
   1.713 +		if (errorRelease != 0) {
   1.714 +		    errorRelease = 0;
   1.715 +		    TclpReleaseFile(errorFile);
   1.716 +		}
   1.717 +		errorFile = outputFile;
   1.718 +	    }
   1.719 +	    break;
   1.720 +
   1.721 +	case '2':
   1.722 +	    if (*p != '>') {
   1.723 +		break;
   1.724 +	    }
   1.725 +	    p++;
   1.726 +	    atOK = 1;
   1.727 +	    flags = O_WRONLY | O_CREAT | O_TRUNC;
   1.728 +	    if (*p == '>') {
   1.729 +		p++;
   1.730 +		atOK = 0;
   1.731 +		flags = O_WRONLY | O_CREAT;
   1.732 +	    }
   1.733 +	    if (errorClose != 0) {
   1.734 +		errorClose = 0;
   1.735 +		TclpCloseFile(errorFile);
   1.736 +	    }
   1.737 +	    if (errorRelease != 0) {
   1.738 +		errorRelease = 0;
   1.739 +		TclpReleaseFile(errorFile);
   1.740 +	    }
   1.741 +	    if (atOK && p[0] == '@' && p[1] == '1' && p[2] == '\0') {
   1.742 +		/*
   1.743 +		 * Special case handling of 2>@1 to redirect stderr to the
   1.744 +		 * exec/open output pipe as well.  This is meant for the end
   1.745 +		 * of the command string, otherwise use |& between commands.
   1.746 +		 */
   1.747 +		if (i != argc - 1) {
   1.748 +		    Tcl_AppendResult(interp, "must specify \"", argv[i],
   1.749 +			    "\" as last word in command", (char *) NULL);
   1.750 +		    goto error;
   1.751 +		}
   1.752 +		errorFile = outputFile;
   1.753 +		errorToOutput = 2;
   1.754 +		skip = 1;
   1.755 +	    } else {
   1.756 +		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
   1.757 +		errorFile = FileForRedirect(interp, p, atOK, argv[i], 
   1.758 +			nextArg, flags, &skip, &errorClose, &errorRelease);
   1.759 +		if (errorFile == NULL) {
   1.760 +		    goto error;
   1.761 +		}
   1.762 +	    }
   1.763 +	    break;
   1.764 +
   1.765 +	default:
   1.766 +	  /* Got a command word, not a redirection */
   1.767 +	  needCmd = 0;
   1.768 +	  break;
   1.769 +	}
   1.770 +
   1.771 +	if (skip != 0) {
   1.772 +	    for (j = i + skip; j < argc; j++) {
   1.773 +		argv[j - skip] = argv[j];
   1.774 +	    }
   1.775 +	    argc -= skip;
   1.776 +	    i -= 1;
   1.777 +	}
   1.778 +    }
   1.779 +
   1.780 +    if (needCmd) {
   1.781 +        /* We had a bar followed only by redirections. */
   1.782 +
   1.783 +        Tcl_SetResult(interp,
   1.784 +		      "illegal use of | or |& in command",
   1.785 +		      TCL_STATIC);
   1.786 +	goto error;
   1.787 +    }
   1.788 +
   1.789 +    if (inputFile == NULL) {
   1.790 +	if (inputLiteral != NULL) {
   1.791 +	    /*
   1.792 +	     * The input for the first process is immediate data coming from
   1.793 +	     * Tcl.  Create a temporary file for it and put the data into the
   1.794 +	     * file.
   1.795 +	     */
   1.796 +	    inputFile = TclpCreateTempFile(inputLiteral);
   1.797 +	    if (inputFile == NULL) {
   1.798 +		Tcl_AppendResult(interp,
   1.799 +			"couldn't create input file for command: ",
   1.800 +			Tcl_PosixError(interp), (char *) NULL);
   1.801 +		goto error;
   1.802 +	    }
   1.803 +#ifdef __SYMBIAN32__  	    
   1.804 +	    // keep the file name for IPC
   1.805 +	    strcpy(inFileName ,tmpFileName);
   1.806 +#endif	    
   1.807 +	    inputClose = 1;
   1.808 +	} else if (inPipePtr != NULL) {
   1.809 +	    /*
   1.810 +	     * The input for the first process in the pipeline is to
   1.811 +	     * come from a pipe that can be written from by the caller.
   1.812 +	     */
   1.813 +#ifdef __SYMBIAN32__  	    
   1.814 +    	tmpnam(inFileName);
   1.815 +    	fifoResult = mkfifo(inFileName,S_IXGRP);
   1.816 +		if(fifoResult == -1)
   1.817 +   		{
   1.818 +      		//fifo creation failure.
   1.819 +      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   1.820 +      		goto error;
   1.821 +   		}
   1.822 +   		else
   1.823 +   		{
   1.824 +   			tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
   1.825 + 
   1.826 +         	if(tmpReadFifoFd == -1)
   1.827 +         	{
   1.828 +           	 //Failed to open the Fifo
   1.829 +           	 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
   1.830 +           	 goto error;
   1.831 +         	}
   1.832 +   			WriteFifoFd = open(inFileName,O_WRONLY);
   1.833 + 
   1.834 +         	if(WriteFifoFd == -1)
   1.835 +         	{
   1.836 +           	 //Failed to open the Fifo
   1.837 +           	 printf("\n*** failure Fifo Open ***\n");
   1.838 +           	 goto error;
   1.839 +         	}
   1.840 +        	 else
   1.841 +        	{
   1.842 +         	   *inPipePtr = MakeFile(WriteFifoFd);     
   1.843 +         	  inputFile = *inPipePtr;
   1.844 +         	}
   1.845 +   		}
   1.846 +   		
   1.847 +   		if ( close(tmpReadFifoFd) != 0)
   1.848 +   		{
   1.849 +   			printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
   1.850 +            goto error;	
   1.851 +   		}
   1.852 +#else
   1.853 +	    if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
   1.854 +		Tcl_AppendResult(interp, 
   1.855 +			"couldn't create input pipe for command: ",
   1.856 +			Tcl_PosixError(interp), (char *) NULL);
   1.857 +		goto error;
   1.858 +	    }
   1.859 +	    inputClose = 1;
   1.860 +#endif	    
   1.861 +	} else {
   1.862 +	    /*
   1.863 +	     * The input for the first process comes from stdin.
   1.864 +	     */
   1.865 +#ifdef __SYMBIAN32__  	    
   1.866 +	    tmpnam(inFileName);
   1.867 +    	fifoResult = mkfifo(inFileName,S_IXGRP);
   1.868 +		if(fifoResult == -1)
   1.869 +   		{
   1.870 +      		//fifo creation failure.
   1.871 +      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   1.872 +      		goto error;
   1.873 +   		}
   1.874 +   		else
   1.875 +   		{
   1.876 +   			tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
   1.877 + 
   1.878 +         	if(tmpReadFifoFd == -1)
   1.879 +         	{
   1.880 +           	 //Failed to open the Fifo
   1.881 +           	 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
   1.882 +           	 goto error;
   1.883 +         	}
   1.884 +         	
   1.885 +   			WriteFifoFd = open(inFileName,O_WRONLY);
   1.886 + 
   1.887 +         	if(WriteFifoFd == -1)
   1.888 +         	{
   1.889 +           	 //Failed to open the Fifo
   1.890 +           	 printf("\n*** failure Fifo Open ***\n");
   1.891 +           	 goto error;
   1.892 +         	}
   1.893 +        	 else
   1.894 +        	{
   1.895 +         	   inputFile = MakeFile(WriteFifoFd);
   1.896 +         	}
   1.897 +   		}
   1.898 +   		
   1.899 +   		if (dup2(0/*fd for TCL_STDIN*/ , WriteFifoFd) == -1)
   1.900 +   		{
   1.901 +   			fprintf(stderr,
   1.902 +			    "%d inputFile couldn't be redirected & dup failed ", errno);
   1.903 +			goto error;
   1.904 +   		}
   1.905 +   		
   1.906 +   		if ( close(tmpReadFifoFd) != 0)
   1.907 +   		{
   1.908 +   			printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
   1.909 +			goto error;
   1.910 +   		}
   1.911 +
   1.912 +		strcpy(inFileName,"STD");
   1.913 +#else
   1.914 +	    channel = Tcl_GetStdChannel(TCL_STDIN);
   1.915 +	    if (channel != NULL) {
   1.916 +		inputFile = TclpMakeFile(channel, TCL_READABLE);
   1.917 +		}
   1.918 +#endif   		
   1.919 +   		if (inputFile != NULL) {
   1.920 +		    inputRelease = 1;
   1.921 +		}
   1.922 +	}
   1.923 +    }
   1.924 +
   1.925 +    if (outputFile == NULL) {
   1.926 +	if (outPipePtr != NULL) {
   1.927 +	    /*
   1.928 +	     * Output from the last process in the pipeline is to go to a
   1.929 +	     * pipe that can be read by the caller.
   1.930 +	     */
   1.931 +#ifdef __SYMBIAN32__  	    	     
   1.932 +    	tmpnam(outFileName);
   1.933 +		fifoResult = mkfifo(outFileName,S_IXGRP);
   1.934 +		if(fifoResult == -1)
   1.935 +   		{
   1.936 +      		//fifo creation failure.
   1.937 +      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   1.938 +      		goto error;
   1.939 +   		}
   1.940 +   		else
   1.941 +   		{
   1.942 +   			ReadFifoFd  = open(outFileName,O_RDONLY | O_NONBLOCK);
   1.943 + 
   1.944 +         	if(ReadFifoFd  == -1)
   1.945 +         	{
   1.946 +           	 //Failed to open the Fifo
   1.947 +           	 printf("\n*** failure Fifo Open ***\n");
   1.948 +           	 goto error;
   1.949 +         	}
   1.950 +        	 else
   1.951 +        	{
   1.952 +         	   *outPipePtr = MakeFile(ReadFifoFd);
   1.953 +          	  outputFile = *outPipePtr;
   1.954 +         	}
   1.955 +   		}
   1.956 +#else   		
   1.957 +	    if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
   1.958 +		Tcl_AppendResult(interp, 
   1.959 +			"couldn't create output pipe for command: ",
   1.960 +			Tcl_PosixError(interp), (char *) NULL);
   1.961 +		goto error;
   1.962 +	    }
   1.963 +	    outputClose = 1;	    
   1.964 +#endif	    
   1.965 +	} else {
   1.966 +	    /*
   1.967 +	     * The output for the last process goes to stdout.
   1.968 +	     */
   1.969 +#ifdef __SYMBIAN32__
   1.970 +	    tmpnam(outFileName);
   1.971 +		fifoResult = mkfifo(outFileName,S_IXGRP);
   1.972 +		if(fifoResult == -1)
   1.973 +   		{
   1.974 +      		//fifo creation failure.
   1.975 +      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   1.976 +      		goto error;
   1.977 +   		}
   1.978 +   		else
   1.979 +   		{
   1.980 +   			ReadFifoFd  = open(outFileName,O_RDONLY | O_NONBLOCK);
   1.981 + 
   1.982 +         	if(ReadFifoFd  == -1)
   1.983 +         	{
   1.984 +           	 //Failed to open the Fifo
   1.985 +           	 printf("\n*** failure Fifo Open ***\n");
   1.986 +           	 goto error;
   1.987 +         	}
   1.988 +        	 else
   1.989 +        	{
   1.990 +         	   outputFile = MakeFile(ReadFifoFd);
   1.991 +         	}
   1.992 +   		}
   1.993 +   	
   1.994 +   		if (dup2(1/*fd for TCL_STDOUT*/ , ReadFifoFd) == -1)
   1.995 +   		{
   1.996 +   			fprintf(stderr,
   1.997 +			    "%d outputFile couldn't be redirected & dup failed ", errno);
   1.998 +			goto error;
   1.999 +   		}
  1.1000 +   		
  1.1001 +   		outputRelease = 1;
  1.1002 +		strcpy(outFileName,"STD");
  1.1003 +#else
  1.1004 +	    channel = Tcl_GetStdChannel(TCL_STDOUT);
  1.1005 +	    if (channel) {
  1.1006 +		outputFile = TclpMakeFile(channel, TCL_WRITABLE);
  1.1007 +		if (outputFile != NULL) {
  1.1008 +		    outputRelease = 1;
  1.1009 +		}
  1.1010 +	    }
  1.1011 +#endif		
  1.1012 +	}
  1.1013 +    }
  1.1014 +
  1.1015 +    if (errorFile == NULL) {
  1.1016 +	if (errorToOutput == 2) {
  1.1017 +	    /*
  1.1018 +	     * Handle 2>@1 special case at end of cmd line
  1.1019 +	     */
  1.1020 +	    errorFile = outputFile;
  1.1021 +#ifdef __SYMBIAN32__
  1.1022 +	    strcpy(errFileName, outFileName);
  1.1023 +#endif
  1.1024 +	} else if (errFilePtr != NULL) {
  1.1025 +	    /*
  1.1026 +	     * Set up the standard error output sink for the pipeline, if
  1.1027 +	     * requested.  Use a temporary file which is opened, then deleted.
  1.1028 +	     * Could potentially just use pipe, but if it filled up it could
  1.1029 +	     * cause the pipeline to deadlock:  we'd be waiting for processes
  1.1030 +	     * to complete before reading stderr, and processes couldn't 
  1.1031 +	     * complete because stderr was backed up.
  1.1032 +	     */
  1.1033 +
  1.1034 +	    errorFile = TclpCreateTempFile(NULL);
  1.1035 +	    if (errorFile == NULL) {
  1.1036 +		Tcl_AppendResult(interp,
  1.1037 +			"couldn't create error file for command: ",
  1.1038 +			Tcl_PosixError(interp), (char *) NULL);
  1.1039 +		goto error;
  1.1040 +	    }
  1.1041 +#ifdef __SYMBIAN32__	    
  1.1042 +	    strcpy(errFileName ,tmpFileName);
  1.1043 +#endif	    
  1.1044 +	    *errFilePtr = errorFile;
  1.1045 +	} else {
  1.1046 +	    /*
  1.1047 +	     * Errors from the pipeline go to stderr.
  1.1048 +	     */
  1.1049 +
  1.1050 +	    channel = Tcl_GetStdChannel(TCL_STDERR);
  1.1051 +	    if (channel) {
  1.1052 +		errorFile = TclpMakeFile(channel, TCL_WRITABLE);
  1.1053 +		if (errorFile != NULL) {
  1.1054 +		    errorRelease = 1;
  1.1055 +		}
  1.1056 +	    }
  1.1057 +#ifdef __SYMBIAN32__	    	    
  1.1058 +		strcpy(errFileName,"STD");
  1.1059 +#endif		
  1.1060 +	}
  1.1061 +    }
  1.1062 +	
  1.1063 +    /*
  1.1064 +     * Scan through the argc array, creating a process for each
  1.1065 +     * group of arguments between the "|" characters.
  1.1066 +     */
  1.1067 +
  1.1068 +    Tcl_ReapDetachedProcs();
  1.1069 +    pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
  1.1070 +
  1.1071 +    curInFile = inputFile;
  1.1072 +
  1.1073 +    for (i = 0; i < argc; i = lastArg + 1) { 
  1.1074 +	int result, joinThisError;
  1.1075 +	Tcl_Pid pid;
  1.1076 +	CONST char *oldName;
  1.1077 +
  1.1078 +	/*
  1.1079 +	 * Convert the program name into native form. 
  1.1080 +	 */
  1.1081 +
  1.1082 +	if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
  1.1083 +	    goto error;
  1.1084 +	}
  1.1085 +
  1.1086 +	/*
  1.1087 +	 * Find the end of the current segment of the pipeline.
  1.1088 +	 */
  1.1089 +
  1.1090 +	joinThisError = 0;
  1.1091 +	for (lastArg = i; lastArg < argc; lastArg++) {
  1.1092 +	    if (argv[lastArg][0] == '|') { 
  1.1093 +		if (argv[lastArg][1] == '\0') { 
  1.1094 +		    break;
  1.1095 +		}
  1.1096 +		if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
  1.1097 +		    joinThisError = 1;
  1.1098 +		    break;
  1.1099 +		}
  1.1100 +	    }
  1.1101 +	}
  1.1102 +
  1.1103 +	/*
  1.1104 +	 * If this is the last segment, use the specified outputFile.
  1.1105 +	 * Otherwise create an intermediate pipe.  pipeIn will become the
  1.1106 +	 * curInFile for the next segment of the pipe.
  1.1107 +	 */
  1.1108 +
  1.1109 +	if (lastArg == argc) {
  1.1110 +	    curOutFile = outputFile;
  1.1111 +	} else {
  1.1112 +	    argv[lastArg] = NULL;
  1.1113 +#ifdef __SYMBIAN32__	    	    
  1.1114 +	    tmpnam(outFileName);
  1.1115 +		fifoResult = mkfifo(outFileName,S_IXGRP);
  1.1116 +		if(fifoResult == -1)
  1.1117 +   		{
  1.1118 +      		//fifo creation failure.
  1.1119 +      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
  1.1120 +      		goto error;
  1.1121 +   		}
  1.1122 +   		else
  1.1123 +   		{
  1.1124 +   			ReadFifoFd  = open(outFileName,O_RDONLY);
  1.1125 + 
  1.1126 +         	if(ReadFifoFd  == -1)
  1.1127 +         	{
  1.1128 +           	 //Failed to open the Fifo
  1.1129 +           	 printf("\n*** failure Fifo Open ***\n");
  1.1130 +           	 goto error;
  1.1131 +         	}
  1.1132 +        	 else
  1.1133 +        	{
  1.1134 +         	   pipeIn = MakeFile(ReadFifoFd);
  1.1135 +         	}
  1.1136 +   		}
  1.1137 +#else   		
  1.1138 +	    if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
  1.1139 +		Tcl_AppendResult(interp, "couldn't create pipe: ",
  1.1140 +			Tcl_PosixError(interp), (char *) NULL);
  1.1141 +		goto error;
  1.1142 +	    }
  1.1143 +#endif	    
  1.1144 +	}
  1.1145 +
  1.1146 +	if (joinThisError != 0) {
  1.1147 +	    curErrFile = curOutFile;
  1.1148 +	} else {
  1.1149 +	    curErrFile = errorFile;
  1.1150 +	}
  1.1151 +
  1.1152 +	/*
  1.1153 +	 * Restore argv[i], since a caller wouldn't expect the contents of
  1.1154 +	 * argv to be modified.
  1.1155 +	 */
  1.1156 +	 
  1.1157 +	oldName = argv[i];
  1.1158 +	argv[i] = Tcl_DStringValue(&execBuffer);
  1.1159 +	result = TclpCreateProcess(interp, lastArg - i, argv + i,
  1.1160 +		curInFile, curOutFile, curErrFile, &pid);
  1.1161 +	argv[i] = oldName;
  1.1162 +	if (result != TCL_OK) {
  1.1163 +	    goto error;
  1.1164 +	}
  1.1165 +	Tcl_DStringFree(&execBuffer);
  1.1166 +
  1.1167 +	pidPtr[numPids] = pid;
  1.1168 +	numPids++;
  1.1169 +
  1.1170 +	/*
  1.1171 +	 * Close off our copies of file descriptors that were set up for
  1.1172 +	 * this child, then set up the input for the next child.
  1.1173 +	 */
  1.1174 +
  1.1175 +	if ((curInFile != NULL) && (curInFile != inputFile)) {
  1.1176 +	    TclpCloseFile(curInFile);
  1.1177 +	}
  1.1178 +	curInFile = pipeIn;
  1.1179 +	pipeIn = NULL;
  1.1180 +
  1.1181 +	if ((curOutFile != NULL) && (curOutFile != outputFile)) {
  1.1182 +	    TclpCloseFile(curOutFile);
  1.1183 +	}
  1.1184 +	curOutFile = NULL;
  1.1185 +    }
  1.1186 +
  1.1187 +    *pidArrayPtr = pidPtr;
  1.1188 +
  1.1189 +    /*
  1.1190 +     * All done.  Cleanup open files lying around and then return.
  1.1191 +     */
  1.1192 +
  1.1193 +cleanup:
  1.1194 +    Tcl_DStringFree(&execBuffer);
  1.1195 +
  1.1196 +    if (inputClose) {
  1.1197 +	TclpCloseFile(inputFile);
  1.1198 +    } else if (inputRelease) {
  1.1199 +	TclpReleaseFile(inputFile);
  1.1200 +    }
  1.1201 +    if (outputClose) {
  1.1202 +	TclpCloseFile(outputFile);
  1.1203 +    } else if (outputRelease) {
  1.1204 +	TclpReleaseFile(outputFile);
  1.1205 +    }
  1.1206 +    if (errorClose) {
  1.1207 +	TclpCloseFile(errorFile);
  1.1208 +    } else if (errorRelease) {
  1.1209 +	TclpReleaseFile(errorFile);
  1.1210 +    }
  1.1211 +    return numPids;
  1.1212 +
  1.1213 +    /*
  1.1214 +     * An error occurred.  There could have been extra files open, such
  1.1215 +     * as pipes between children.  Clean them all up.  Detach any child
  1.1216 +     * processes that have been created.
  1.1217 +     */
  1.1218 +
  1.1219 +error:
  1.1220 +    if (pipeIn != NULL) {
  1.1221 +	TclpCloseFile(pipeIn);
  1.1222 +    }
  1.1223 +    if ((curOutFile != NULL) && (curOutFile != outputFile)) {
  1.1224 +	TclpCloseFile(curOutFile);
  1.1225 +    }
  1.1226 +    if ((curInFile != NULL) && (curInFile != inputFile)) {
  1.1227 +	TclpCloseFile(curInFile);
  1.1228 +    }
  1.1229 +    if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
  1.1230 +	TclpCloseFile(*inPipePtr);
  1.1231 +	*inPipePtr = NULL;
  1.1232 +    }
  1.1233 +    if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
  1.1234 +	TclpCloseFile(*outPipePtr);
  1.1235 +	*outPipePtr = NULL;
  1.1236 +    }
  1.1237 +    if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
  1.1238 +	TclpCloseFile(*errFilePtr);
  1.1239 +	*errFilePtr = NULL;
  1.1240 +    }
  1.1241 +    if (pidPtr != NULL) {
  1.1242 +	for (i = 0; i < numPids; i++) {
  1.1243 +	    if (pidPtr[i] != (Tcl_Pid) -1) {
  1.1244 +		Tcl_DetachPids(1, &pidPtr[i]);
  1.1245 +	    }
  1.1246 +	}
  1.1247 +	ckfree((char *) pidPtr);
  1.1248 +    }
  1.1249 +    numPids = -1;
  1.1250 +    goto cleanup;
  1.1251 +}
  1.1252 +
  1.1253 +/*
  1.1254 + *----------------------------------------------------------------------
  1.1255 + *
  1.1256 + * Tcl_OpenCommandChannel --
  1.1257 + *
  1.1258 + *	Opens an I/O channel to one or more subprocesses specified
  1.1259 + *	by argc and argv.  The flags argument determines the
  1.1260 + *	disposition of the stdio handles.  If the TCL_STDIN flag is
  1.1261 + *	set then the standard input for the first subprocess will
  1.1262 + *	be tied to the channel:  writing to the channel will provide
  1.1263 + *	input to the subprocess.  If TCL_STDIN is not set, then
  1.1264 + *	standard input for the first subprocess will be the same as
  1.1265 + *	this application's standard input.  If TCL_STDOUT is set then
  1.1266 + *	standard output from the last subprocess can be read from the
  1.1267 + *	channel;  otherwise it goes to this application's standard
  1.1268 + *	output.  If TCL_STDERR is set, standard error output for all
  1.1269 + *	subprocesses is returned to the channel and results in an error
  1.1270 + *	when the channel is closed;  otherwise it goes to this
  1.1271 + *	application's standard error.  If TCL_ENFORCE_MODE is not set,
  1.1272 + *	then argc and argv can redirect the stdio handles to override
  1.1273 + *	TCL_STDIN, TCL_STDOUT, and TCL_STDERR;  if it is set, then it 
  1.1274 + *	is an error for argc and argv to override stdio channels for
  1.1275 + *	which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
  1.1276 + *
  1.1277 + * Results:
  1.1278 + *	A new command channel, or NULL on failure with an error
  1.1279 + *	message left in interp.
  1.1280 + *
  1.1281 + * Side effects:
  1.1282 + *	Creates processes, opens pipes.
  1.1283 + *
  1.1284 + *----------------------------------------------------------------------
  1.1285 + */
  1.1286 +
  1.1287 +EXPORT_C Tcl_Channel
  1.1288 +Tcl_OpenCommandChannel(interp, argc, argv, flags)
  1.1289 +    Tcl_Interp *interp;		/* Interpreter for error reporting. Can
  1.1290 +                                 * NOT be NULL. */
  1.1291 +    int argc;			/* How many arguments. */
  1.1292 +    CONST char **argv;		/* Array of arguments for command pipe. */
  1.1293 +    int flags;			/* Or'ed combination of TCL_STDIN, TCL_STDOUT,
  1.1294 +				 * TCL_STDERR, and TCL_ENFORCE_MODE. */
  1.1295 +{
  1.1296 +    TclFile *inPipePtr, *outPipePtr, *errFilePtr;
  1.1297 +    TclFile inPipe, outPipe, errFile;
  1.1298 +    int numPids;
  1.1299 +    Tcl_Pid *pidPtr;
  1.1300 +    Tcl_Channel channel;
  1.1301 +
  1.1302 +    inPipe = outPipe = errFile = NULL;
  1.1303 +
  1.1304 +    inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
  1.1305 +    outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
  1.1306 +    errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
  1.1307 +    
  1.1308 +    numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
  1.1309 +            outPipePtr, errFilePtr);
  1.1310 +
  1.1311 +    if (numPids < 0) {
  1.1312 +	goto error;
  1.1313 +    }
  1.1314 +
  1.1315 +    /*
  1.1316 +     * Verify that the pipes that were created satisfy the
  1.1317 +     * readable/writable constraints. 
  1.1318 +     */
  1.1319 +
  1.1320 +    if (flags & TCL_ENFORCE_MODE) {
  1.1321 +	if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
  1.1322 +	    Tcl_AppendResult(interp, "can't read output from command:",
  1.1323 +		    " standard output was redirected", (char *) NULL);
  1.1324 +	    goto error;
  1.1325 +	}
  1.1326 +	if ((flags & TCL_STDIN) && (inPipe == NULL)) {
  1.1327 +	    Tcl_AppendResult(interp, "can't write input to command:",
  1.1328 +		    " standard input was redirected", (char *) NULL);
  1.1329 +	    goto error;
  1.1330 +	}
  1.1331 +    }
  1.1332 +    
  1.1333 +    channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
  1.1334 +	    numPids, pidPtr);
  1.1335 +
  1.1336 +    if (channel == (Tcl_Channel) NULL) {
  1.1337 +        Tcl_AppendResult(interp, "pipe for command could not be created",
  1.1338 +                (char *) NULL);
  1.1339 +	goto error;
  1.1340 +    }
  1.1341 +    return channel;
  1.1342 +
  1.1343 +error:
  1.1344 +    if (numPids > 0) {
  1.1345 +	Tcl_DetachPids(numPids, pidPtr);
  1.1346 +	ckfree((char *) pidPtr);
  1.1347 +    }
  1.1348 +    if (inPipe != NULL) {
  1.1349 +	TclpCloseFile(inPipe);
  1.1350 +    }
  1.1351 +    if (outPipe != NULL) {
  1.1352 +	TclpCloseFile(outPipe);
  1.1353 +    }
  1.1354 +    if (errFile != NULL) {
  1.1355 +	TclpCloseFile(errFile);
  1.1356 +    }
  1.1357 +    return NULL;
  1.1358 +}