os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPipe.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200 (2014-06-10)
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/* 
sl@0
     2
 * tclPipe.c --
sl@0
     3
 *
sl@0
     4
 *	This file contains the generic portion of the command channel
sl@0
     5
 *	driver as well as various utility routines used in managing
sl@0
     6
 *	subprocesses.
sl@0
     7
 *
sl@0
     8
 * Copyright (c) 1997 by Sun Microsystems, Inc.
sl@0
     9
 * Portions Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiaries. All rights reserved.  
sl@0
    10
 *
sl@0
    11
 * See the file "license.terms" for information on usage and redistribution
sl@0
    12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    13
 *
sl@0
    14
 * RCS: @(#) $Id: tclPipe.c,v 1.7.2.5 2006/03/16 00:35:58 andreas_kupries Exp $
sl@0
    15
 */
sl@0
    16
sl@0
    17
#include "tclInt.h"
sl@0
    18
#include "tclPort.h"
sl@0
    19
#if defined(__SYMBIAN32__)    
sl@0
    20
#include "tclSymbianGlobals.h"
sl@0
    21
#include <sys/select.h>
sl@0
    22
sl@0
    23
/*
sl@0
    24
 * The following macros convert between TclFile's and fd's.  The conversion
sl@0
    25
 * simple involves shifting fd's up by one to ensure that no valid fd is ever
sl@0
    26
 * the same as NULL.  Note that this code is duplicated from tclUnixPipe.c
sl@0
    27
 */
sl@0
    28
sl@0
    29
#define MakeFile(fd) ((TclFile)((fd)+1))
sl@0
    30
#define GetFd(file) (((int)file)-1)
sl@0
    31
sl@0
    32
#endif
sl@0
    33
sl@0
    34
/*
sl@0
    35
 * A linked list of the following structures is used to keep track
sl@0
    36
 * of child processes that have been detached but haven't exited
sl@0
    37
 * yet, so we can make sure that they're properly "reaped" (officially
sl@0
    38
 * waited for) and don't lie around as zombies cluttering the
sl@0
    39
 * system.
sl@0
    40
 */
sl@0
    41
sl@0
    42
typedef struct Detached {
sl@0
    43
    Tcl_Pid pid;			/* Id of process that's been detached
sl@0
    44
					 * but isn't known to have exited. */
sl@0
    45
    struct Detached *nextPtr;		/* Next in list of all detached
sl@0
    46
					 * processes. */
sl@0
    47
} Detached;
sl@0
    48
sl@0
    49
static Detached *detList = NULL;	/* List of all detached proceses. */
sl@0
    50
TCL_DECLARE_MUTEX(pipeMutex)		/* Guard access to detList. */
sl@0
    51
sl@0
    52
/*
sl@0
    53
 * Declarations for local procedures defined in this file:
sl@0
    54
 */
sl@0
    55
sl@0
    56
static TclFile	FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,
sl@0
    57
	            CONST char *spec, int atOk, CONST char *arg, 
sl@0
    58
		    CONST char *nextArg, int flags, int *skipPtr,
sl@0
    59
		    int *closePtr, int *releasePtr));
sl@0
    60

sl@0
    61
/*
sl@0
    62
 *----------------------------------------------------------------------
sl@0
    63
 *
sl@0
    64
 * FileForRedirect --
sl@0
    65
 *
sl@0
    66
 *	This procedure does much of the work of parsing redirection
sl@0
    67
 *	operators.  It handles "@" if specified and allowed, and a file
sl@0
    68
 *	name, and opens the file if necessary.
sl@0
    69
 *
sl@0
    70
 * Results:
sl@0
    71
 *	The return value is the descriptor number for the file.  If an
sl@0
    72
 *	error occurs then NULL is returned and an error message is left
sl@0
    73
 *	in the interp's result.  Several arguments are side-effected; see
sl@0
    74
 *	the argument list below for details.
sl@0
    75
 *
sl@0
    76
 * Side effects:
sl@0
    77
 *	None.
sl@0
    78
 *
sl@0
    79
 *----------------------------------------------------------------------
sl@0
    80
 */
sl@0
    81
sl@0
    82
static TclFile
sl@0
    83
FileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr,
sl@0
    84
	releasePtr)
sl@0
    85
    Tcl_Interp *interp;		/* Intepreter to use for error reporting. */
sl@0
    86
    CONST char *spec;		/* Points to character just after
sl@0
    87
				 * redirection character. */
sl@0
    88
    int atOK;			/* Non-zero means that '@' notation can be 
sl@0
    89
				 * used to specify a channel, zero means that
sl@0
    90
				 * it isn't. */
sl@0
    91
    CONST char *arg;		/* Pointer to entire argument containing 
sl@0
    92
				 * spec:  used for error reporting. */
sl@0
    93
    CONST char *nextArg;	/* Next argument in argc/argv array, if needed 
sl@0
    94
				 * for file name or channel name.  May be 
sl@0
    95
				 * NULL. */
sl@0
    96
    int flags;			/* Flags to use for opening file or to 
sl@0
    97
				 * specify mode for channel. */
sl@0
    98
    int *skipPtr;		/* Filled with 1 if redirection target was
sl@0
    99
				 * in spec, 2 if it was in nextArg. */
sl@0
   100
    int *closePtr;		/* Filled with one if the caller should 
sl@0
   101
				 * close the file when done with it, zero
sl@0
   102
				 * otherwise. */
sl@0
   103
    int *releasePtr;
sl@0
   104
{
sl@0
   105
    int writing = (flags & O_WRONLY);
sl@0
   106
    Tcl_Channel chan;
sl@0
   107
    TclFile file;
sl@0
   108
sl@0
   109
    *skipPtr = 1;
sl@0
   110
    if ((atOK != 0)  && (*spec == '@')) {
sl@0
   111
	spec++;
sl@0
   112
	if (*spec == '\0') {
sl@0
   113
	    spec = nextArg;
sl@0
   114
	    if (spec == NULL) {
sl@0
   115
		goto badLastArg;
sl@0
   116
	    }
sl@0
   117
	    *skipPtr = 2;
sl@0
   118
	}
sl@0
   119
        chan = Tcl_GetChannel(interp, spec, NULL);
sl@0
   120
        if (chan == (Tcl_Channel) NULL) {
sl@0
   121
            return NULL;
sl@0
   122
        }
sl@0
   123
	file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
sl@0
   124
        if (file == NULL) {
sl@0
   125
	    Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
sl@0
   126
		    "\" wasn't opened for ",
sl@0
   127
		    ((writing) ? "writing" : "reading"), (char *) NULL);
sl@0
   128
            return NULL;
sl@0
   129
        }
sl@0
   130
	*releasePtr = 1;
sl@0
   131
	if (writing) {
sl@0
   132
sl@0
   133
	    /*
sl@0
   134
	     * Be sure to flush output to the file, so that anything
sl@0
   135
	     * written by the child appears after stuff we've already
sl@0
   136
	     * written.
sl@0
   137
	     */
sl@0
   138
sl@0
   139
            Tcl_Flush(chan);
sl@0
   140
	}
sl@0
   141
    } else {
sl@0
   142
	CONST char *name;
sl@0
   143
	Tcl_DString nameString;
sl@0
   144
sl@0
   145
	if (*spec == '\0') {
sl@0
   146
	    spec = nextArg;
sl@0
   147
	    if (spec == NULL) {
sl@0
   148
		goto badLastArg;
sl@0
   149
	    }
sl@0
   150
	    *skipPtr = 2;
sl@0
   151
	}
sl@0
   152
	name = Tcl_TranslateFileName(interp, spec, &nameString);
sl@0
   153
	if (name == NULL) {
sl@0
   154
	    return NULL;
sl@0
   155
	}
sl@0
   156
	file = TclpOpenFile(name, flags);
sl@0
   157
	Tcl_DStringFree(&nameString);
sl@0
   158
	if (file == NULL) {
sl@0
   159
	    Tcl_AppendResult(interp, "couldn't ",
sl@0
   160
		    ((writing) ? "write" : "read"), " file \"", spec, "\": ",
sl@0
   161
		    Tcl_PosixError(interp), (char *) NULL);
sl@0
   162
	    return NULL;
sl@0
   163
	}
sl@0
   164
        *closePtr = 1;
sl@0
   165
    }
sl@0
   166
    return file;
sl@0
   167
sl@0
   168
    badLastArg:
sl@0
   169
    Tcl_AppendResult(interp, "can't specify \"", arg,
sl@0
   170
	    "\" as last word in command", (char *) NULL);
sl@0
   171
    return NULL;
sl@0
   172
}
sl@0
   173

sl@0
   174
/*
sl@0
   175
 *----------------------------------------------------------------------
sl@0
   176
 *
sl@0
   177
 * Tcl_DetachPids --
sl@0
   178
 *
sl@0
   179
 *	This procedure is called to indicate that one or more child
sl@0
   180
 *	processes have been placed in background and will never be
sl@0
   181
 *	waited for;  they should eventually be reaped by
sl@0
   182
 *	Tcl_ReapDetachedProcs.
sl@0
   183
 *
sl@0
   184
 * Results:
sl@0
   185
 *	None.
sl@0
   186
 *
sl@0
   187
 * Side effects:
sl@0
   188
 *	None.
sl@0
   189
 *
sl@0
   190
 *----------------------------------------------------------------------
sl@0
   191
 */
sl@0
   192
sl@0
   193
EXPORT_C void
sl@0
   194
Tcl_DetachPids(numPids, pidPtr)
sl@0
   195
    int numPids;		/* Number of pids to detach:  gives size
sl@0
   196
				 * of array pointed to by pidPtr. */
sl@0
   197
    Tcl_Pid *pidPtr;		/* Array of pids to detach. */
sl@0
   198
{
sl@0
   199
    register Detached *detPtr;
sl@0
   200
    int i;
sl@0
   201
sl@0
   202
    Tcl_MutexLock(&pipeMutex);
sl@0
   203
    for (i = 0; i < numPids; i++) {
sl@0
   204
	detPtr = (Detached *) ckalloc(sizeof(Detached));
sl@0
   205
	detPtr->pid = pidPtr[i];
sl@0
   206
	detPtr->nextPtr = detList;
sl@0
   207
	detList = detPtr;
sl@0
   208
    }
sl@0
   209
    Tcl_MutexUnlock(&pipeMutex);
sl@0
   210
sl@0
   211
}
sl@0
   212

sl@0
   213
/*
sl@0
   214
 *----------------------------------------------------------------------
sl@0
   215
 *
sl@0
   216
 * Tcl_ReapDetachedProcs --
sl@0
   217
 *
sl@0
   218
 *	This procedure checks to see if any detached processes have
sl@0
   219
 *	exited and, if so, it "reaps" them by officially waiting on
sl@0
   220
 *	them.  It should be called "occasionally" to make sure that
sl@0
   221
 *	all detached processes are eventually reaped.
sl@0
   222
 *
sl@0
   223
 * Results:
sl@0
   224
 *	None.
sl@0
   225
 *
sl@0
   226
 * Side effects:
sl@0
   227
 *	Processes are waited on, so that they can be reaped by the
sl@0
   228
 *	system.
sl@0
   229
 *
sl@0
   230
 *----------------------------------------------------------------------
sl@0
   231
 */
sl@0
   232
sl@0
   233
EXPORT_C void
sl@0
   234
Tcl_ReapDetachedProcs()
sl@0
   235
{
sl@0
   236
    register Detached *detPtr;
sl@0
   237
    Detached *nextPtr, *prevPtr;
sl@0
   238
    int status;
sl@0
   239
    Tcl_Pid pid;
sl@0
   240
sl@0
   241
    Tcl_MutexLock(&pipeMutex);
sl@0
   242
    for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
sl@0
   243
	pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
sl@0
   244
	if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {
sl@0
   245
	    prevPtr = detPtr;
sl@0
   246
	    detPtr = detPtr->nextPtr;
sl@0
   247
	    continue;
sl@0
   248
	}
sl@0
   249
	nextPtr = detPtr->nextPtr;
sl@0
   250
	if (prevPtr == NULL) {
sl@0
   251
	    detList = detPtr->nextPtr;
sl@0
   252
	} else {
sl@0
   253
	    prevPtr->nextPtr = detPtr->nextPtr;
sl@0
   254
	}
sl@0
   255
	ckfree((char *) detPtr);
sl@0
   256
	detPtr = nextPtr;
sl@0
   257
    }
sl@0
   258
    Tcl_MutexUnlock(&pipeMutex);
sl@0
   259
}
sl@0
   260

sl@0
   261
/*
sl@0
   262
 *----------------------------------------------------------------------
sl@0
   263
 *
sl@0
   264
 * TclCleanupChildren --
sl@0
   265
 *
sl@0
   266
 *	This is a utility procedure used to wait for child processes
sl@0
   267
 *	to exit, record information about abnormal exits, and then
sl@0
   268
 *	collect any stderr output generated by them.
sl@0
   269
 *
sl@0
   270
 * Results:
sl@0
   271
 *	The return value is a standard Tcl result.  If anything at
sl@0
   272
 *	weird happened with the child processes, TCL_ERROR is returned
sl@0
   273
 *	and a message is left in the interp's result.
sl@0
   274
 *
sl@0
   275
 * Side effects:
sl@0
   276
 *	If the last character of the interp's result is a newline, then it
sl@0
   277
 *	is removed unless keepNewline is non-zero.  File errorId gets
sl@0
   278
 *	closed, and pidPtr is freed back to the storage allocator.
sl@0
   279
 *
sl@0
   280
 *----------------------------------------------------------------------
sl@0
   281
 */
sl@0
   282
sl@0
   283
int
sl@0
   284
TclCleanupChildren(interp, numPids, pidPtr, errorChan)
sl@0
   285
    Tcl_Interp *interp;		/* Used for error messages. */
sl@0
   286
    int numPids;		/* Number of entries in pidPtr array. */
sl@0
   287
    Tcl_Pid *pidPtr;		/* Array of process ids of children. */
sl@0
   288
    Tcl_Channel errorChan;	/* Channel for file containing stderr output
sl@0
   289
				 * from pipeline.  NULL means there isn't any
sl@0
   290
				 * stderr output. */
sl@0
   291
{
sl@0
   292
    int result = TCL_OK;
sl@0
   293
    int i, abnormalExit, anyErrorInfo;
sl@0
   294
    Tcl_Pid pid;
sl@0
   295
    WAIT_STATUS_TYPE waitStatus;
sl@0
   296
    CONST char *msg;
sl@0
   297
    unsigned long resolvedPid;
sl@0
   298
sl@0
   299
    abnormalExit = 0;
sl@0
   300
    for (i = 0; i < numPids; i++) {
sl@0
   301
	/*
sl@0
   302
	 * We need to get the resolved pid before we wait on it as
sl@0
   303
	 * the windows implimentation of Tcl_WaitPid deletes the
sl@0
   304
	 * information such that any following calls to TclpGetPid
sl@0
   305
	 * fail.
sl@0
   306
	 */
sl@0
   307
	resolvedPid = TclpGetPid(pidPtr[i]);
sl@0
   308
        pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
sl@0
   309
	if (pid == (Tcl_Pid) -1) {
sl@0
   310
	    result = TCL_ERROR;
sl@0
   311
            if (interp != (Tcl_Interp *) NULL) {
sl@0
   312
                msg = Tcl_PosixError(interp);
sl@0
   313
                if (errno == ECHILD) {
sl@0
   314
		    /*
sl@0
   315
                     * This changeup in message suggested by Mark Diekhans
sl@0
   316
                     * to remind people that ECHILD errors can occur on
sl@0
   317
                     * some systems if SIGCHLD isn't in its default state.
sl@0
   318
                     */
sl@0
   319
#ifdef __SYMBIAN32__  
sl@0
   320
					// change message to remove all references to signals.				
sl@0
   321
                    msg =
sl@0
   322
                        "child process lost.";
sl@0
   323
#else
sl@0
   324
                    msg =
sl@0
   325
                        "child process lost (is SIGCHLD ignored or trapped?)";
sl@0
   326
#endif                                             
sl@0
   327
                }
sl@0
   328
                Tcl_AppendResult(interp, "error waiting for process to exit: ",
sl@0
   329
                        msg, (char *) NULL);
sl@0
   330
            }
sl@0
   331
	    continue;
sl@0
   332
	}
sl@0
   333
sl@0
   334
	/*
sl@0
   335
	 * Create error messages for unusual process exits.  An
sl@0
   336
	 * extra newline gets appended to each error message, but
sl@0
   337
	 * it gets removed below (in the same fashion that an
sl@0
   338
	 * extra newline in the command's output is removed).
sl@0
   339
	 */	 
sl@0
   340
#ifdef __SYMBIAN32__  
sl@0
   341
	// process the return status of the child.
sl@0
   342
	if (waitStatus != 0) {
sl@0
   343
	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
sl@0
   344
sl@0
   345
	    result = TCL_ERROR;
sl@0
   346
	    TclFormatInt(msg1, (long) resolvedPid);
sl@0
   347
	    
sl@0
   348
	    	if (waitStatus < 0) {
sl@0
   349
                if (interp != (Tcl_Interp *) NULL) {
sl@0
   350
	    			TclFormatInt(msg2, (long) waitStatus);
sl@0
   351
                    Tcl_AppendResult(interp,
sl@0
   352
                            "child wait status is error (pid ", msg1, ") status: ", msg2,
sl@0
   353
                            (char *) NULL);
sl@0
   354
                }
sl@0
   355
	    	} else { 
sl@0
   356
                if (interp != (Tcl_Interp *) NULL) {
sl@0
   357
	    			TclFormatInt(msg2, (long) waitStatus);
sl@0
   358
                    Tcl_SetErrorCode(interp, "CHILDSTATUS (pid: ", msg1, ") status: ", msg2,
sl@0
   359
                            (char *) NULL);
sl@0
   360
                }
sl@0
   361
			abnormalExit = 1;
sl@0
   362
	    	}
sl@0
   363
#else
sl@0
   364
	if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
sl@0
   365
	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
sl@0
   366
sl@0
   367
	    result = TCL_ERROR;
sl@0
   368
	    TclFormatInt(msg1, (long) resolvedPid);
sl@0
   369
	    if (WIFEXITED(waitStatus)) {
sl@0
   370
                if (interp != (Tcl_Interp *) NULL) {
sl@0
   371
		    TclFormatInt(msg2, WEXITSTATUS(waitStatus));
sl@0
   372
                    Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
sl@0
   373
                            (char *) NULL);
sl@0
   374
                }
sl@0
   375
		abnormalExit = 1;
sl@0
   376
	    } else if (WIFSIGNALED(waitStatus)) {
sl@0
   377
                if (interp != (Tcl_Interp *) NULL) {
sl@0
   378
                    CONST char *p;
sl@0
   379
                    
sl@0
   380
                    p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
sl@0
   381
                    Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
sl@0
   382
                            Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
sl@0
   383
                            (char *) NULL);
sl@0
   384
                    Tcl_AppendResult(interp, "child killed: ", p, "\n",
sl@0
   385
                            (char *) NULL);
sl@0
   386
                }
sl@0
   387
	    } else if (WIFSTOPPED(waitStatus)) {
sl@0
   388
                if (interp != (Tcl_Interp *) NULL) {
sl@0
   389
                    CONST char *p;
sl@0
   390
sl@0
   391
                    p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
sl@0
   392
                    Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
sl@0
   393
                            Tcl_SignalId((int) (WSTOPSIG(waitStatus))),
sl@0
   394
                            p, (char *) NULL);
sl@0
   395
                    Tcl_AppendResult(interp, "child suspended: ", p, "\n",
sl@0
   396
                            (char *) NULL);
sl@0
   397
                }
sl@0
   398
	    } else {
sl@0
   399
                if (interp != (Tcl_Interp *) NULL) {
sl@0
   400
                    Tcl_AppendResult(interp,
sl@0
   401
                            "child wait status didn't make sense\n",
sl@0
   402
                            (char *) NULL);
sl@0
   403
                }
sl@0
   404
	    }
sl@0
   405
#endif	    
sl@0
   406
	}
sl@0
   407
    }
sl@0
   408
sl@0
   409
    /*
sl@0
   410
     * Read the standard error file.  If there's anything there,
sl@0
   411
     * then return an error and add the file's contents to the result
sl@0
   412
     * string.
sl@0
   413
     */
sl@0
   414
sl@0
   415
    anyErrorInfo = 0;
sl@0
   416
    if (errorChan != NULL) {
sl@0
   417
sl@0
   418
	/*
sl@0
   419
	 * Make sure we start at the beginning of the file.
sl@0
   420
	 */
sl@0
   421
sl@0
   422
        if (interp != NULL) {
sl@0
   423
	    int count;
sl@0
   424
	    Tcl_Obj *objPtr;
sl@0
   425
	    
sl@0
   426
	    Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
sl@0
   427
	    objPtr = Tcl_NewObj();
sl@0
   428
	    count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
sl@0
   429
	    if (count < 0) {
sl@0
   430
		result = TCL_ERROR;
sl@0
   431
		Tcl_DecrRefCount(objPtr);
sl@0
   432
		Tcl_ResetResult(interp);
sl@0
   433
		Tcl_AppendResult(interp, "error reading stderr output file: ",
sl@0
   434
			Tcl_PosixError(interp), NULL);
sl@0
   435
	    } else if (count > 0) {
sl@0
   436
		anyErrorInfo = 1;
sl@0
   437
		Tcl_SetObjResult(interp, objPtr);
sl@0
   438
		result = TCL_ERROR;
sl@0
   439
	    } else {
sl@0
   440
		Tcl_DecrRefCount(objPtr);
sl@0
   441
	    }
sl@0
   442
	}
sl@0
   443
	Tcl_Close(NULL, errorChan);
sl@0
   444
    }
sl@0
   445
sl@0
   446
    /*
sl@0
   447
     * If a child exited abnormally but didn't output any error information
sl@0
   448
     * at all, generate an error message here.
sl@0
   449
     */
sl@0
   450
sl@0
   451
    if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
sl@0
   452
	Tcl_AppendResult(interp, "child process exited abnormally",
sl@0
   453
		(char *) NULL);
sl@0
   454
    }
sl@0
   455
    return result;
sl@0
   456
}
sl@0
   457

sl@0
   458
/*
sl@0
   459
 *----------------------------------------------------------------------
sl@0
   460
 *
sl@0
   461
 * TclCreatePipeline --
sl@0
   462
 *
sl@0
   463
 *	Given an argc/argv array, instantiate a pipeline of processes
sl@0
   464
 *	as described by the argv.
sl@0
   465
 *
sl@0
   466
 *	This procedure is unofficially exported for use by BLT.
sl@0
   467
 *
sl@0
   468
 * Results:
sl@0
   469
 *	The return value is a count of the number of new processes
sl@0
   470
 *	created, or -1 if an error occurred while creating the pipeline.
sl@0
   471
 *	*pidArrayPtr is filled in with the address of a dynamically
sl@0
   472
 *	allocated array giving the ids of all of the processes.  It
sl@0
   473
 *	is up to the caller to free this array when it isn't needed
sl@0
   474
 *	anymore.  If inPipePtr is non-NULL, *inPipePtr is filled in
sl@0
   475
 *	with the file id for the input pipe for the pipeline (if any):
sl@0
   476
 *	the caller must eventually close this file.  If outPipePtr
sl@0
   477
 *	isn't NULL, then *outPipePtr is filled in with the file id
sl@0
   478
 *	for the output pipe from the pipeline:  the caller must close
sl@0
   479
 *	this file.  If errFilePtr isn't NULL, then *errFilePtr is filled
sl@0
   480
 *	with a file id that may be used to read error output after the
sl@0
   481
 *	pipeline completes.
sl@0
   482
 *
sl@0
   483
 * Side effects:
sl@0
   484
 *	Processes and pipes are created.
sl@0
   485
 *
sl@0
   486
 *----------------------------------------------------------------------
sl@0
   487
 */
sl@0
   488
sl@0
   489
int
sl@0
   490
TclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
sl@0
   491
	outPipePtr, errFilePtr)
sl@0
   492
    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
sl@0
   493
    int argc;			/* Number of entries in argv. */
sl@0
   494
    CONST char **argv;		/* Array of strings describing commands in
sl@0
   495
				 * pipeline plus I/O redirection with <,
sl@0
   496
				 * <<,  >, etc.  Argv[argc] must be NULL. */
sl@0
   497
    Tcl_Pid **pidArrayPtr;	/* Word at *pidArrayPtr gets filled in with
sl@0
   498
				 * address of array of pids for processes
sl@0
   499
				 * in pipeline (first pid is first process
sl@0
   500
				 * in pipeline). */
sl@0
   501
    TclFile *inPipePtr;		/* If non-NULL, input to the pipeline comes
sl@0
   502
				 * from a pipe (unless overridden by
sl@0
   503
				 * redirection in the command).  The file
sl@0
   504
				 * id with which to write to this pipe is
sl@0
   505
				 * stored at *inPipePtr.  NULL means command
sl@0
   506
				 * specified its own input source. */
sl@0
   507
    TclFile *outPipePtr;	/* If non-NULL, output to the pipeline goes
sl@0
   508
				 * to a pipe, unless overriden by redirection
sl@0
   509
				 * in the command.  The file id with which to
sl@0
   510
				 * read frome this pipe is stored at
sl@0
   511
				 * *outPipePtr.  NULL means command specified
sl@0
   512
				 * its own output sink. */
sl@0
   513
    TclFile *errFilePtr;	/* If non-NULL, all stderr output from the
sl@0
   514
				 * pipeline will go to a temporary file
sl@0
   515
				 * created here, and a descriptor to read
sl@0
   516
				 * the file will be left at *errFilePtr.
sl@0
   517
				 * The file will be removed already, so
sl@0
   518
				 * closing this descriptor will be the end
sl@0
   519
				 * of the file.  If this is NULL, then
sl@0
   520
				 * all stderr output goes to our stderr.
sl@0
   521
				 * If the pipeline specifies redirection
sl@0
   522
				 * then the file will still be created
sl@0
   523
				 * but it will never get any data. */
sl@0
   524
{
sl@0
   525
    Tcl_Pid *pidPtr = NULL;	/* Points to malloc-ed array holding all
sl@0
   526
				 * the pids of child processes. */
sl@0
   527
    int numPids;		/* Actual number of processes that exist
sl@0
   528
				 * at *pidPtr right now. */
sl@0
   529
    int cmdCount;		/* Count of number of distinct commands
sl@0
   530
				 * found in argc/argv. */
sl@0
   531
    CONST char *inputLiteral = NULL;	/* If non-null, then this points to a
sl@0
   532
				 * string containing input data (specified
sl@0
   533
				 * via <<) to be piped to the first process
sl@0
   534
				 * in the pipeline. */
sl@0
   535
    TclFile inputFile = NULL;	/* If != NULL, gives file to use as input for
sl@0
   536
				 * first process in pipeline (specified via <
sl@0
   537
				 * or <@). */
sl@0
   538
    int inputClose = 0;		/* If non-zero, then inputFile should be 
sl@0
   539
    				 * closed when cleaning up. */
sl@0
   540
    int inputRelease = 0;
sl@0
   541
    TclFile outputFile = NULL;	/* Writable file for output from last command
sl@0
   542
				 * in pipeline (could be file or pipe).  NULL
sl@0
   543
				 * means use stdout. */
sl@0
   544
    int outputClose = 0;	/* If non-zero, then outputFile should be 
sl@0
   545
    				 * closed when cleaning up. */
sl@0
   546
    int outputRelease = 0;
sl@0
   547
    TclFile errorFile = NULL;	/* Writable file for error output from all
sl@0
   548
				 * commands in pipeline.  NULL means use
sl@0
   549
				 * stderr. */
sl@0
   550
    int errorClose = 0;		/* If non-zero, then errorFile should be 
sl@0
   551
    				 * closed when cleaning up. */
sl@0
   552
    int errorRelease = 0;
sl@0
   553
    CONST char *p;
sl@0
   554
    CONST char *nextArg;
sl@0
   555
    int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0;
sl@0
   556
    Tcl_DString execBuffer;
sl@0
   557
    TclFile pipeIn;
sl@0
   558
    TclFile curInFile, curOutFile, curErrFile;
sl@0
   559
    Tcl_Channel channel;
sl@0
   560
#ifdef __SYMBIAN32__      
sl@0
   561
    int fifoResult;
sl@0
   562
    int ReadFifoFd;
sl@0
   563
    int WriteFifoFd;
sl@0
   564
    int tmpReadFifoFd;
sl@0
   565
#endif    
sl@0
   566
sl@0
   567
    if (inPipePtr != NULL) {
sl@0
   568
	*inPipePtr = NULL;
sl@0
   569
    }
sl@0
   570
    if (outPipePtr != NULL) {
sl@0
   571
	*outPipePtr = NULL;
sl@0
   572
    }
sl@0
   573
    if (errFilePtr != NULL) {
sl@0
   574
	*errFilePtr = NULL;
sl@0
   575
    }
sl@0
   576
sl@0
   577
    Tcl_DStringInit(&execBuffer);
sl@0
   578
    
sl@0
   579
    pipeIn = NULL;
sl@0
   580
    curInFile = NULL;
sl@0
   581
    curOutFile = NULL;
sl@0
   582
    numPids = 0;
sl@0
   583
sl@0
   584
    /*
sl@0
   585
     * First, scan through all the arguments to figure out the structure
sl@0
   586
     * of the pipeline.  Process all of the input and output redirection
sl@0
   587
     * arguments and remove them from the argument list in the pipeline.
sl@0
   588
     * Count the number of distinct processes (it's the number of "|"
sl@0
   589
     * arguments plus one) but don't remove the "|" arguments because 
sl@0
   590
     * they'll be used in the second pass to seperate the individual 
sl@0
   591
     * child processes.  Cannot start the child processes in this pass 
sl@0
   592
     * because the redirection symbols may appear anywhere in the 
sl@0
   593
     * command line -- e.g., the '<' that specifies the input to the 
sl@0
   594
     * entire pipe may appear at the very end of the argument list.
sl@0
   595
     */
sl@0
   596
sl@0
   597
    lastBar = -1;
sl@0
   598
    cmdCount = 1;
sl@0
   599
    needCmd = 1;
sl@0
   600
    for (i = 0; i < argc; i++) {
sl@0
   601
	errorToOutput = 0;
sl@0
   602
	skip = 0;
sl@0
   603
	p = argv[i];
sl@0
   604
	switch (*p++) {
sl@0
   605
	case '|':
sl@0
   606
	    if (*p == '&') {
sl@0
   607
		p++;
sl@0
   608
	    }
sl@0
   609
	    if (*p == '\0') {
sl@0
   610
		if ((i == (lastBar + 1)) || (i == (argc - 1))) {
sl@0
   611
		    Tcl_SetResult(interp,
sl@0
   612
			    "illegal use of | or |& in command",
sl@0
   613
			    TCL_STATIC);
sl@0
   614
		    goto error;
sl@0
   615
		}
sl@0
   616
	    }
sl@0
   617
	    lastBar = i;
sl@0
   618
	    cmdCount++;
sl@0
   619
	    needCmd = 1;
sl@0
   620
	    break;
sl@0
   621
sl@0
   622
	case '<':
sl@0
   623
	    if (inputClose != 0) {
sl@0
   624
		inputClose = 0;
sl@0
   625
		TclpCloseFile(inputFile);
sl@0
   626
	    }
sl@0
   627
	    if (inputRelease != 0) {
sl@0
   628
		inputRelease = 0;
sl@0
   629
		TclpReleaseFile(inputFile);
sl@0
   630
	    }
sl@0
   631
	    if (*p == '<') {
sl@0
   632
		inputFile = NULL;
sl@0
   633
		inputLiteral = p + 1;
sl@0
   634
		skip = 1;
sl@0
   635
		if (*inputLiteral == '\0') {
sl@0
   636
		    inputLiteral = ((i + 1) == argc) ? NULL : argv[i + 1];
sl@0
   637
		    if (inputLiteral == NULL) {
sl@0
   638
			Tcl_AppendResult(interp, "can't specify \"", argv[i],
sl@0
   639
				"\" as last word in command", (char *) NULL);
sl@0
   640
			goto error;
sl@0
   641
		    }
sl@0
   642
		    skip = 2;
sl@0
   643
		}
sl@0
   644
	    } else {
sl@0
   645
		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
sl@0
   646
		inputLiteral = NULL;
sl@0
   647
		inputFile = FileForRedirect(interp, p, 1, argv[i], 
sl@0
   648
			nextArg, O_RDONLY, &skip, &inputClose, &inputRelease);
sl@0
   649
		if (inputFile == NULL) {
sl@0
   650
		    goto error;
sl@0
   651
		}
sl@0
   652
	    }
sl@0
   653
	    break;
sl@0
   654
sl@0
   655
	case '>':
sl@0
   656
	    atOK = 1;
sl@0
   657
	    flags = O_WRONLY | O_CREAT | O_TRUNC;
sl@0
   658
	    if (*p == '>') {
sl@0
   659
		p++;
sl@0
   660
		atOK = 0;
sl@0
   661
sl@0
   662
		/*
sl@0
   663
		 * Note that the O_APPEND flag only has an effect on POSIX
sl@0
   664
		 * platforms. On Windows, we just have to carry on regardless.
sl@0
   665
		 */
sl@0
   666
sl@0
   667
		flags = O_WRONLY | O_CREAT | O_APPEND;
sl@0
   668
	    }
sl@0
   669
	    if (*p == '&') {
sl@0
   670
		if (errorClose != 0) {
sl@0
   671
		    errorClose = 0;
sl@0
   672
		    TclpCloseFile(errorFile);
sl@0
   673
		}
sl@0
   674
		errorToOutput = 1;
sl@0
   675
		p++;
sl@0
   676
	    }
sl@0
   677
sl@0
   678
	    /*
sl@0
   679
	     * Close the old output file, but only if the error file is
sl@0
   680
	     * not also using it.
sl@0
   681
	     */
sl@0
   682
sl@0
   683
	    if (outputClose != 0) {
sl@0
   684
		outputClose = 0;
sl@0
   685
		if (errorFile == outputFile) {
sl@0
   686
		    errorClose = 1;
sl@0
   687
		} else {
sl@0
   688
		    TclpCloseFile(outputFile);
sl@0
   689
		}
sl@0
   690
	    }
sl@0
   691
	    if (outputRelease != 0) {
sl@0
   692
		outputRelease = 0;
sl@0
   693
		if (errorFile == outputFile) {
sl@0
   694
		    errorRelease = 1;
sl@0
   695
		} else {
sl@0
   696
		    TclpReleaseFile(outputFile);
sl@0
   697
		}
sl@0
   698
	    }
sl@0
   699
	    nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
sl@0
   700
	    outputFile = FileForRedirect(interp, p, atOK, argv[i], 
sl@0
   701
		    nextArg, flags, &skip, &outputClose, &outputRelease);
sl@0
   702
	    if (outputFile == NULL) {
sl@0
   703
		goto error;
sl@0
   704
	    }
sl@0
   705
	    if (errorToOutput) {
sl@0
   706
		if (errorClose != 0) {
sl@0
   707
		    errorClose = 0;
sl@0
   708
		    TclpCloseFile(errorFile);
sl@0
   709
		}
sl@0
   710
		if (errorRelease != 0) {
sl@0
   711
		    errorRelease = 0;
sl@0
   712
		    TclpReleaseFile(errorFile);
sl@0
   713
		}
sl@0
   714
		errorFile = outputFile;
sl@0
   715
	    }
sl@0
   716
	    break;
sl@0
   717
sl@0
   718
	case '2':
sl@0
   719
	    if (*p != '>') {
sl@0
   720
		break;
sl@0
   721
	    }
sl@0
   722
	    p++;
sl@0
   723
	    atOK = 1;
sl@0
   724
	    flags = O_WRONLY | O_CREAT | O_TRUNC;
sl@0
   725
	    if (*p == '>') {
sl@0
   726
		p++;
sl@0
   727
		atOK = 0;
sl@0
   728
		flags = O_WRONLY | O_CREAT;
sl@0
   729
	    }
sl@0
   730
	    if (errorClose != 0) {
sl@0
   731
		errorClose = 0;
sl@0
   732
		TclpCloseFile(errorFile);
sl@0
   733
	    }
sl@0
   734
	    if (errorRelease != 0) {
sl@0
   735
		errorRelease = 0;
sl@0
   736
		TclpReleaseFile(errorFile);
sl@0
   737
	    }
sl@0
   738
	    if (atOK && p[0] == '@' && p[1] == '1' && p[2] == '\0') {
sl@0
   739
		/*
sl@0
   740
		 * Special case handling of 2>@1 to redirect stderr to the
sl@0
   741
		 * exec/open output pipe as well.  This is meant for the end
sl@0
   742
		 * of the command string, otherwise use |& between commands.
sl@0
   743
		 */
sl@0
   744
		if (i != argc - 1) {
sl@0
   745
		    Tcl_AppendResult(interp, "must specify \"", argv[i],
sl@0
   746
			    "\" as last word in command", (char *) NULL);
sl@0
   747
		    goto error;
sl@0
   748
		}
sl@0
   749
		errorFile = outputFile;
sl@0
   750
		errorToOutput = 2;
sl@0
   751
		skip = 1;
sl@0
   752
	    } else {
sl@0
   753
		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
sl@0
   754
		errorFile = FileForRedirect(interp, p, atOK, argv[i], 
sl@0
   755
			nextArg, flags, &skip, &errorClose, &errorRelease);
sl@0
   756
		if (errorFile == NULL) {
sl@0
   757
		    goto error;
sl@0
   758
		}
sl@0
   759
	    }
sl@0
   760
	    break;
sl@0
   761
sl@0
   762
	default:
sl@0
   763
	  /* Got a command word, not a redirection */
sl@0
   764
	  needCmd = 0;
sl@0
   765
	  break;
sl@0
   766
	}
sl@0
   767
sl@0
   768
	if (skip != 0) {
sl@0
   769
	    for (j = i + skip; j < argc; j++) {
sl@0
   770
		argv[j - skip] = argv[j];
sl@0
   771
	    }
sl@0
   772
	    argc -= skip;
sl@0
   773
	    i -= 1;
sl@0
   774
	}
sl@0
   775
    }
sl@0
   776
sl@0
   777
    if (needCmd) {
sl@0
   778
        /* We had a bar followed only by redirections. */
sl@0
   779
sl@0
   780
        Tcl_SetResult(interp,
sl@0
   781
		      "illegal use of | or |& in command",
sl@0
   782
		      TCL_STATIC);
sl@0
   783
	goto error;
sl@0
   784
    }
sl@0
   785
sl@0
   786
    if (inputFile == NULL) {
sl@0
   787
	if (inputLiteral != NULL) {
sl@0
   788
	    /*
sl@0
   789
	     * The input for the first process is immediate data coming from
sl@0
   790
	     * Tcl.  Create a temporary file for it and put the data into the
sl@0
   791
	     * file.
sl@0
   792
	     */
sl@0
   793
	    inputFile = TclpCreateTempFile(inputLiteral);
sl@0
   794
	    if (inputFile == NULL) {
sl@0
   795
		Tcl_AppendResult(interp,
sl@0
   796
			"couldn't create input file for command: ",
sl@0
   797
			Tcl_PosixError(interp), (char *) NULL);
sl@0
   798
		goto error;
sl@0
   799
	    }
sl@0
   800
#ifdef __SYMBIAN32__  	    
sl@0
   801
	    // keep the file name for IPC
sl@0
   802
	    strcpy(inFileName ,tmpFileName);
sl@0
   803
#endif	    
sl@0
   804
	    inputClose = 1;
sl@0
   805
	} else if (inPipePtr != NULL) {
sl@0
   806
	    /*
sl@0
   807
	     * The input for the first process in the pipeline is to
sl@0
   808
	     * come from a pipe that can be written from by the caller.
sl@0
   809
	     */
sl@0
   810
#ifdef __SYMBIAN32__  	    
sl@0
   811
    	tmpnam(inFileName);
sl@0
   812
    	fifoResult = mkfifo(inFileName,S_IXGRP);
sl@0
   813
		if(fifoResult == -1)
sl@0
   814
   		{
sl@0
   815
      		//fifo creation failure.
sl@0
   816
      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
sl@0
   817
      		goto error;
sl@0
   818
   		}
sl@0
   819
   		else
sl@0
   820
   		{
sl@0
   821
   			tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
sl@0
   822
 
sl@0
   823
         	if(tmpReadFifoFd == -1)
sl@0
   824
         	{
sl@0
   825
           	 //Failed to open the Fifo
sl@0
   826
           	 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
sl@0
   827
           	 goto error;
sl@0
   828
         	}
sl@0
   829
   			WriteFifoFd = open(inFileName,O_WRONLY);
sl@0
   830
 
sl@0
   831
         	if(WriteFifoFd == -1)
sl@0
   832
         	{
sl@0
   833
           	 //Failed to open the Fifo
sl@0
   834
           	 printf("\n*** failure Fifo Open ***\n");
sl@0
   835
           	 goto error;
sl@0
   836
         	}
sl@0
   837
        	 else
sl@0
   838
        	{
sl@0
   839
         	   *inPipePtr = MakeFile(WriteFifoFd);     
sl@0
   840
         	  inputFile = *inPipePtr;
sl@0
   841
         	}
sl@0
   842
   		}
sl@0
   843
   		
sl@0
   844
   		if ( close(tmpReadFifoFd) != 0)
sl@0
   845
   		{
sl@0
   846
   			printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
sl@0
   847
            goto error;	
sl@0
   848
   		}
sl@0
   849
#else
sl@0
   850
	    if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
sl@0
   851
		Tcl_AppendResult(interp, 
sl@0
   852
			"couldn't create input pipe for command: ",
sl@0
   853
			Tcl_PosixError(interp), (char *) NULL);
sl@0
   854
		goto error;
sl@0
   855
	    }
sl@0
   856
	    inputClose = 1;
sl@0
   857
#endif	    
sl@0
   858
	} else {
sl@0
   859
	    /*
sl@0
   860
	     * The input for the first process comes from stdin.
sl@0
   861
	     */
sl@0
   862
#ifdef __SYMBIAN32__  	    
sl@0
   863
	    tmpnam(inFileName);
sl@0
   864
    	fifoResult = mkfifo(inFileName,S_IXGRP);
sl@0
   865
		if(fifoResult == -1)
sl@0
   866
   		{
sl@0
   867
      		//fifo creation failure.
sl@0
   868
      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
sl@0
   869
      		goto error;
sl@0
   870
   		}
sl@0
   871
   		else
sl@0
   872
   		{
sl@0
   873
   			tmpReadFifoFd = open(inFileName,O_RDONLY | O_NONBLOCK);
sl@0
   874
 
sl@0
   875
         	if(tmpReadFifoFd == -1)
sl@0
   876
         	{
sl@0
   877
           	 //Failed to open the Fifo
sl@0
   878
           	 printf("\n*** failure tmpReadFifoFd Fifo Open ***\n");
sl@0
   879
           	 goto error;
sl@0
   880
         	}
sl@0
   881
         	
sl@0
   882
   			WriteFifoFd = open(inFileName,O_WRONLY);
sl@0
   883
 
sl@0
   884
         	if(WriteFifoFd == -1)
sl@0
   885
         	{
sl@0
   886
           	 //Failed to open the Fifo
sl@0
   887
           	 printf("\n*** failure Fifo Open ***\n");
sl@0
   888
           	 goto error;
sl@0
   889
         	}
sl@0
   890
        	 else
sl@0
   891
        	{
sl@0
   892
         	   inputFile = MakeFile(WriteFifoFd);
sl@0
   893
         	}
sl@0
   894
   		}
sl@0
   895
   		
sl@0
   896
   		if (dup2(0/*fd for TCL_STDIN*/ , WriteFifoFd) == -1)
sl@0
   897
   		{
sl@0
   898
   			fprintf(stderr,
sl@0
   899
			    "%d inputFile couldn't be redirected & dup failed ", errno);
sl@0
   900
			goto error;
sl@0
   901
   		}
sl@0
   902
   		
sl@0
   903
   		if ( close(tmpReadFifoFd) != 0)
sl@0
   904
   		{
sl@0
   905
   			printf("\n*** failure tmpReadFifoFd Fifo close ***\n");
sl@0
   906
			goto error;
sl@0
   907
   		}
sl@0
   908
sl@0
   909
		strcpy(inFileName,"STD");
sl@0
   910
#else
sl@0
   911
	    channel = Tcl_GetStdChannel(TCL_STDIN);
sl@0
   912
	    if (channel != NULL) {
sl@0
   913
		inputFile = TclpMakeFile(channel, TCL_READABLE);
sl@0
   914
		}
sl@0
   915
#endif   		
sl@0
   916
   		if (inputFile != NULL) {
sl@0
   917
		    inputRelease = 1;
sl@0
   918
		}
sl@0
   919
	}
sl@0
   920
    }
sl@0
   921
sl@0
   922
    if (outputFile == NULL) {
sl@0
   923
	if (outPipePtr != NULL) {
sl@0
   924
	    /*
sl@0
   925
	     * Output from the last process in the pipeline is to go to a
sl@0
   926
	     * pipe that can be read by the caller.
sl@0
   927
	     */
sl@0
   928
#ifdef __SYMBIAN32__  	    	     
sl@0
   929
    	tmpnam(outFileName);
sl@0
   930
		fifoResult = mkfifo(outFileName,S_IXGRP);
sl@0
   931
		if(fifoResult == -1)
sl@0
   932
   		{
sl@0
   933
      		//fifo creation failure.
sl@0
   934
      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
sl@0
   935
      		goto error;
sl@0
   936
   		}
sl@0
   937
   		else
sl@0
   938
   		{
sl@0
   939
   			ReadFifoFd  = open(outFileName,O_RDONLY | O_NONBLOCK);
sl@0
   940
 
sl@0
   941
         	if(ReadFifoFd  == -1)
sl@0
   942
         	{
sl@0
   943
           	 //Failed to open the Fifo
sl@0
   944
           	 printf("\n*** failure Fifo Open ***\n");
sl@0
   945
           	 goto error;
sl@0
   946
         	}
sl@0
   947
        	 else
sl@0
   948
        	{
sl@0
   949
         	   *outPipePtr = MakeFile(ReadFifoFd);
sl@0
   950
          	  outputFile = *outPipePtr;
sl@0
   951
         	}
sl@0
   952
   		}
sl@0
   953
#else   		
sl@0
   954
	    if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
sl@0
   955
		Tcl_AppendResult(interp, 
sl@0
   956
			"couldn't create output pipe for command: ",
sl@0
   957
			Tcl_PosixError(interp), (char *) NULL);
sl@0
   958
		goto error;
sl@0
   959
	    }
sl@0
   960
	    outputClose = 1;	    
sl@0
   961
#endif	    
sl@0
   962
	} else {
sl@0
   963
	    /*
sl@0
   964
	     * The output for the last process goes to stdout.
sl@0
   965
	     */
sl@0
   966
#ifdef __SYMBIAN32__
sl@0
   967
	    tmpnam(outFileName);
sl@0
   968
		fifoResult = mkfifo(outFileName,S_IXGRP);
sl@0
   969
		if(fifoResult == -1)
sl@0
   970
   		{
sl@0
   971
      		//fifo creation failure.
sl@0
   972
      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
sl@0
   973
      		goto error;
sl@0
   974
   		}
sl@0
   975
   		else
sl@0
   976
   		{
sl@0
   977
   			ReadFifoFd  = open(outFileName,O_RDONLY | O_NONBLOCK);
sl@0
   978
 
sl@0
   979
         	if(ReadFifoFd  == -1)
sl@0
   980
         	{
sl@0
   981
           	 //Failed to open the Fifo
sl@0
   982
           	 printf("\n*** failure Fifo Open ***\n");
sl@0
   983
           	 goto error;
sl@0
   984
         	}
sl@0
   985
        	 else
sl@0
   986
        	{
sl@0
   987
         	   outputFile = MakeFile(ReadFifoFd);
sl@0
   988
         	}
sl@0
   989
   		}
sl@0
   990
   	
sl@0
   991
   		if (dup2(1/*fd for TCL_STDOUT*/ , ReadFifoFd) == -1)
sl@0
   992
   		{
sl@0
   993
   			fprintf(stderr,
sl@0
   994
			    "%d outputFile couldn't be redirected & dup failed ", errno);
sl@0
   995
			goto error;
sl@0
   996
   		}
sl@0
   997
   		
sl@0
   998
   		outputRelease = 1;
sl@0
   999
		strcpy(outFileName,"STD");
sl@0
  1000
#else
sl@0
  1001
	    channel = Tcl_GetStdChannel(TCL_STDOUT);
sl@0
  1002
	    if (channel) {
sl@0
  1003
		outputFile = TclpMakeFile(channel, TCL_WRITABLE);
sl@0
  1004
		if (outputFile != NULL) {
sl@0
  1005
		    outputRelease = 1;
sl@0
  1006
		}
sl@0
  1007
	    }
sl@0
  1008
#endif		
sl@0
  1009
	}
sl@0
  1010
    }
sl@0
  1011
sl@0
  1012
    if (errorFile == NULL) {
sl@0
  1013
	if (errorToOutput == 2) {
sl@0
  1014
	    /*
sl@0
  1015
	     * Handle 2>@1 special case at end of cmd line
sl@0
  1016
	     */
sl@0
  1017
	    errorFile = outputFile;
sl@0
  1018
#ifdef __SYMBIAN32__
sl@0
  1019
	    strcpy(errFileName, outFileName);
sl@0
  1020
#endif
sl@0
  1021
	} else if (errFilePtr != NULL) {
sl@0
  1022
	    /*
sl@0
  1023
	     * Set up the standard error output sink for the pipeline, if
sl@0
  1024
	     * requested.  Use a temporary file which is opened, then deleted.
sl@0
  1025
	     * Could potentially just use pipe, but if it filled up it could
sl@0
  1026
	     * cause the pipeline to deadlock:  we'd be waiting for processes
sl@0
  1027
	     * to complete before reading stderr, and processes couldn't 
sl@0
  1028
	     * complete because stderr was backed up.
sl@0
  1029
	     */
sl@0
  1030
sl@0
  1031
	    errorFile = TclpCreateTempFile(NULL);
sl@0
  1032
	    if (errorFile == NULL) {
sl@0
  1033
		Tcl_AppendResult(interp,
sl@0
  1034
			"couldn't create error file for command: ",
sl@0
  1035
			Tcl_PosixError(interp), (char *) NULL);
sl@0
  1036
		goto error;
sl@0
  1037
	    }
sl@0
  1038
#ifdef __SYMBIAN32__	    
sl@0
  1039
	    strcpy(errFileName ,tmpFileName);
sl@0
  1040
#endif	    
sl@0
  1041
	    *errFilePtr = errorFile;
sl@0
  1042
	} else {
sl@0
  1043
	    /*
sl@0
  1044
	     * Errors from the pipeline go to stderr.
sl@0
  1045
	     */
sl@0
  1046
sl@0
  1047
	    channel = Tcl_GetStdChannel(TCL_STDERR);
sl@0
  1048
	    if (channel) {
sl@0
  1049
		errorFile = TclpMakeFile(channel, TCL_WRITABLE);
sl@0
  1050
		if (errorFile != NULL) {
sl@0
  1051
		    errorRelease = 1;
sl@0
  1052
		}
sl@0
  1053
	    }
sl@0
  1054
#ifdef __SYMBIAN32__	    	    
sl@0
  1055
		strcpy(errFileName,"STD");
sl@0
  1056
#endif		
sl@0
  1057
	}
sl@0
  1058
    }
sl@0
  1059
	
sl@0
  1060
    /*
sl@0
  1061
     * Scan through the argc array, creating a process for each
sl@0
  1062
     * group of arguments between the "|" characters.
sl@0
  1063
     */
sl@0
  1064
sl@0
  1065
    Tcl_ReapDetachedProcs();
sl@0
  1066
    pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
sl@0
  1067
sl@0
  1068
    curInFile = inputFile;
sl@0
  1069
sl@0
  1070
    for (i = 0; i < argc; i = lastArg + 1) { 
sl@0
  1071
	int result, joinThisError;
sl@0
  1072
	Tcl_Pid pid;
sl@0
  1073
	CONST char *oldName;
sl@0
  1074
sl@0
  1075
	/*
sl@0
  1076
	 * Convert the program name into native form. 
sl@0
  1077
	 */
sl@0
  1078
sl@0
  1079
	if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
sl@0
  1080
	    goto error;
sl@0
  1081
	}
sl@0
  1082
sl@0
  1083
	/*
sl@0
  1084
	 * Find the end of the current segment of the pipeline.
sl@0
  1085
	 */
sl@0
  1086
sl@0
  1087
	joinThisError = 0;
sl@0
  1088
	for (lastArg = i; lastArg < argc; lastArg++) {
sl@0
  1089
	    if (argv[lastArg][0] == '|') { 
sl@0
  1090
		if (argv[lastArg][1] == '\0') { 
sl@0
  1091
		    break;
sl@0
  1092
		}
sl@0
  1093
		if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
sl@0
  1094
		    joinThisError = 1;
sl@0
  1095
		    break;
sl@0
  1096
		}
sl@0
  1097
	    }
sl@0
  1098
	}
sl@0
  1099
sl@0
  1100
	/*
sl@0
  1101
	 * If this is the last segment, use the specified outputFile.
sl@0
  1102
	 * Otherwise create an intermediate pipe.  pipeIn will become the
sl@0
  1103
	 * curInFile for the next segment of the pipe.
sl@0
  1104
	 */
sl@0
  1105
sl@0
  1106
	if (lastArg == argc) {
sl@0
  1107
	    curOutFile = outputFile;
sl@0
  1108
	} else {
sl@0
  1109
	    argv[lastArg] = NULL;
sl@0
  1110
#ifdef __SYMBIAN32__	    	    
sl@0
  1111
	    tmpnam(outFileName);
sl@0
  1112
		fifoResult = mkfifo(outFileName,S_IXGRP);
sl@0
  1113
		if(fifoResult == -1)
sl@0
  1114
   		{
sl@0
  1115
      		//fifo creation failure.
sl@0
  1116
      		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
sl@0
  1117
      		goto error;
sl@0
  1118
   		}
sl@0
  1119
   		else
sl@0
  1120
   		{
sl@0
  1121
   			ReadFifoFd  = open(outFileName,O_RDONLY);
sl@0
  1122
 
sl@0
  1123
         	if(ReadFifoFd  == -1)
sl@0
  1124
         	{
sl@0
  1125
           	 //Failed to open the Fifo
sl@0
  1126
           	 printf("\n*** failure Fifo Open ***\n");
sl@0
  1127
           	 goto error;
sl@0
  1128
         	}
sl@0
  1129
        	 else
sl@0
  1130
        	{
sl@0
  1131
         	   pipeIn = MakeFile(ReadFifoFd);
sl@0
  1132
         	}
sl@0
  1133
   		}
sl@0
  1134
#else   		
sl@0
  1135
	    if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
sl@0
  1136
		Tcl_AppendResult(interp, "couldn't create pipe: ",
sl@0
  1137
			Tcl_PosixError(interp), (char *) NULL);
sl@0
  1138
		goto error;
sl@0
  1139
	    }
sl@0
  1140
#endif	    
sl@0
  1141
	}
sl@0
  1142
sl@0
  1143
	if (joinThisError != 0) {
sl@0
  1144
	    curErrFile = curOutFile;
sl@0
  1145
	} else {
sl@0
  1146
	    curErrFile = errorFile;
sl@0
  1147
	}
sl@0
  1148
sl@0
  1149
	/*
sl@0
  1150
	 * Restore argv[i], since a caller wouldn't expect the contents of
sl@0
  1151
	 * argv to be modified.
sl@0
  1152
	 */
sl@0
  1153
	 
sl@0
  1154
	oldName = argv[i];
sl@0
  1155
	argv[i] = Tcl_DStringValue(&execBuffer);
sl@0
  1156
	result = TclpCreateProcess(interp, lastArg - i, argv + i,
sl@0
  1157
		curInFile, curOutFile, curErrFile, &pid);
sl@0
  1158
	argv[i] = oldName;
sl@0
  1159
	if (result != TCL_OK) {
sl@0
  1160
	    goto error;
sl@0
  1161
	}
sl@0
  1162
	Tcl_DStringFree(&execBuffer);
sl@0
  1163
sl@0
  1164
	pidPtr[numPids] = pid;
sl@0
  1165
	numPids++;
sl@0
  1166
sl@0
  1167
	/*
sl@0
  1168
	 * Close off our copies of file descriptors that were set up for
sl@0
  1169
	 * this child, then set up the input for the next child.
sl@0
  1170
	 */
sl@0
  1171
sl@0
  1172
	if ((curInFile != NULL) && (curInFile != inputFile)) {
sl@0
  1173
	    TclpCloseFile(curInFile);
sl@0
  1174
	}
sl@0
  1175
	curInFile = pipeIn;
sl@0
  1176
	pipeIn = NULL;
sl@0
  1177
sl@0
  1178
	if ((curOutFile != NULL) && (curOutFile != outputFile)) {
sl@0
  1179
	    TclpCloseFile(curOutFile);
sl@0
  1180
	}
sl@0
  1181
	curOutFile = NULL;
sl@0
  1182
    }
sl@0
  1183
sl@0
  1184
    *pidArrayPtr = pidPtr;
sl@0
  1185
sl@0
  1186
    /*
sl@0
  1187
     * All done.  Cleanup open files lying around and then return.
sl@0
  1188
     */
sl@0
  1189
sl@0
  1190
cleanup:
sl@0
  1191
    Tcl_DStringFree(&execBuffer);
sl@0
  1192
sl@0
  1193
    if (inputClose) {
sl@0
  1194
	TclpCloseFile(inputFile);
sl@0
  1195
    } else if (inputRelease) {
sl@0
  1196
	TclpReleaseFile(inputFile);
sl@0
  1197
    }
sl@0
  1198
    if (outputClose) {
sl@0
  1199
	TclpCloseFile(outputFile);
sl@0
  1200
    } else if (outputRelease) {
sl@0
  1201
	TclpReleaseFile(outputFile);
sl@0
  1202
    }
sl@0
  1203
    if (errorClose) {
sl@0
  1204
	TclpCloseFile(errorFile);
sl@0
  1205
    } else if (errorRelease) {
sl@0
  1206
	TclpReleaseFile(errorFile);
sl@0
  1207
    }
sl@0
  1208
    return numPids;
sl@0
  1209
sl@0
  1210
    /*
sl@0
  1211
     * An error occurred.  There could have been extra files open, such
sl@0
  1212
     * as pipes between children.  Clean them all up.  Detach any child
sl@0
  1213
     * processes that have been created.
sl@0
  1214
     */
sl@0
  1215
sl@0
  1216
error:
sl@0
  1217
    if (pipeIn != NULL) {
sl@0
  1218
	TclpCloseFile(pipeIn);
sl@0
  1219
    }
sl@0
  1220
    if ((curOutFile != NULL) && (curOutFile != outputFile)) {
sl@0
  1221
	TclpCloseFile(curOutFile);
sl@0
  1222
    }
sl@0
  1223
    if ((curInFile != NULL) && (curInFile != inputFile)) {
sl@0
  1224
	TclpCloseFile(curInFile);
sl@0
  1225
    }
sl@0
  1226
    if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
sl@0
  1227
	TclpCloseFile(*inPipePtr);
sl@0
  1228
	*inPipePtr = NULL;
sl@0
  1229
    }
sl@0
  1230
    if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
sl@0
  1231
	TclpCloseFile(*outPipePtr);
sl@0
  1232
	*outPipePtr = NULL;
sl@0
  1233
    }
sl@0
  1234
    if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
sl@0
  1235
	TclpCloseFile(*errFilePtr);
sl@0
  1236
	*errFilePtr = NULL;
sl@0
  1237
    }
sl@0
  1238
    if (pidPtr != NULL) {
sl@0
  1239
	for (i = 0; i < numPids; i++) {
sl@0
  1240
	    if (pidPtr[i] != (Tcl_Pid) -1) {
sl@0
  1241
		Tcl_DetachPids(1, &pidPtr[i]);
sl@0
  1242
	    }
sl@0
  1243
	}
sl@0
  1244
	ckfree((char *) pidPtr);
sl@0
  1245
    }
sl@0
  1246
    numPids = -1;
sl@0
  1247
    goto cleanup;
sl@0
  1248
}
sl@0
  1249

sl@0
  1250
/*
sl@0
  1251
 *----------------------------------------------------------------------
sl@0
  1252
 *
sl@0
  1253
 * Tcl_OpenCommandChannel --
sl@0
  1254
 *
sl@0
  1255
 *	Opens an I/O channel to one or more subprocesses specified
sl@0
  1256
 *	by argc and argv.  The flags argument determines the
sl@0
  1257
 *	disposition of the stdio handles.  If the TCL_STDIN flag is
sl@0
  1258
 *	set then the standard input for the first subprocess will
sl@0
  1259
 *	be tied to the channel:  writing to the channel will provide
sl@0
  1260
 *	input to the subprocess.  If TCL_STDIN is not set, then
sl@0
  1261
 *	standard input for the first subprocess will be the same as
sl@0
  1262
 *	this application's standard input.  If TCL_STDOUT is set then
sl@0
  1263
 *	standard output from the last subprocess can be read from the
sl@0
  1264
 *	channel;  otherwise it goes to this application's standard
sl@0
  1265
 *	output.  If TCL_STDERR is set, standard error output for all
sl@0
  1266
 *	subprocesses is returned to the channel and results in an error
sl@0
  1267
 *	when the channel is closed;  otherwise it goes to this
sl@0
  1268
 *	application's standard error.  If TCL_ENFORCE_MODE is not set,
sl@0
  1269
 *	then argc and argv can redirect the stdio handles to override
sl@0
  1270
 *	TCL_STDIN, TCL_STDOUT, and TCL_STDERR;  if it is set, then it 
sl@0
  1271
 *	is an error for argc and argv to override stdio channels for
sl@0
  1272
 *	which TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
sl@0
  1273
 *
sl@0
  1274
 * Results:
sl@0
  1275
 *	A new command channel, or NULL on failure with an error
sl@0
  1276
 *	message left in interp.
sl@0
  1277
 *
sl@0
  1278
 * Side effects:
sl@0
  1279
 *	Creates processes, opens pipes.
sl@0
  1280
 *
sl@0
  1281
 *----------------------------------------------------------------------
sl@0
  1282
 */
sl@0
  1283
sl@0
  1284
EXPORT_C Tcl_Channel
sl@0
  1285
Tcl_OpenCommandChannel(interp, argc, argv, flags)
sl@0
  1286
    Tcl_Interp *interp;		/* Interpreter for error reporting. Can
sl@0
  1287
                                 * NOT be NULL. */
sl@0
  1288
    int argc;			/* How many arguments. */
sl@0
  1289
    CONST char **argv;		/* Array of arguments for command pipe. */
sl@0
  1290
    int flags;			/* Or'ed combination of TCL_STDIN, TCL_STDOUT,
sl@0
  1291
				 * TCL_STDERR, and TCL_ENFORCE_MODE. */
sl@0
  1292
{
sl@0
  1293
    TclFile *inPipePtr, *outPipePtr, *errFilePtr;
sl@0
  1294
    TclFile inPipe, outPipe, errFile;
sl@0
  1295
    int numPids;
sl@0
  1296
    Tcl_Pid *pidPtr;
sl@0
  1297
    Tcl_Channel channel;
sl@0
  1298
sl@0
  1299
    inPipe = outPipe = errFile = NULL;
sl@0
  1300
sl@0
  1301
    inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
sl@0
  1302
    outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
sl@0
  1303
    errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
sl@0
  1304
    
sl@0
  1305
    numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
sl@0
  1306
            outPipePtr, errFilePtr);
sl@0
  1307
sl@0
  1308
    if (numPids < 0) {
sl@0
  1309
	goto error;
sl@0
  1310
    }
sl@0
  1311
sl@0
  1312
    /*
sl@0
  1313
     * Verify that the pipes that were created satisfy the
sl@0
  1314
     * readable/writable constraints. 
sl@0
  1315
     */
sl@0
  1316
sl@0
  1317
    if (flags & TCL_ENFORCE_MODE) {
sl@0
  1318
	if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
sl@0
  1319
	    Tcl_AppendResult(interp, "can't read output from command:",
sl@0
  1320
		    " standard output was redirected", (char *) NULL);
sl@0
  1321
	    goto error;
sl@0
  1322
	}
sl@0
  1323
	if ((flags & TCL_STDIN) && (inPipe == NULL)) {
sl@0
  1324
	    Tcl_AppendResult(interp, "can't write input to command:",
sl@0
  1325
		    " standard input was redirected", (char *) NULL);
sl@0
  1326
	    goto error;
sl@0
  1327
	}
sl@0
  1328
    }
sl@0
  1329
    
sl@0
  1330
    channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
sl@0
  1331
	    numPids, pidPtr);
sl@0
  1332
sl@0
  1333
    if (channel == (Tcl_Channel) NULL) {
sl@0
  1334
        Tcl_AppendResult(interp, "pipe for command could not be created",
sl@0
  1335
                (char *) NULL);
sl@0
  1336
	goto error;
sl@0
  1337
    }
sl@0
  1338
    return channel;
sl@0
  1339
sl@0
  1340
error:
sl@0
  1341
    if (numPids > 0) {
sl@0
  1342
	Tcl_DetachPids(numPids, pidPtr);
sl@0
  1343
	ckfree((char *) pidPtr);
sl@0
  1344
    }
sl@0
  1345
    if (inPipe != NULL) {
sl@0
  1346
	TclpCloseFile(inPipe);
sl@0
  1347
    }
sl@0
  1348
    if (outPipe != NULL) {
sl@0
  1349
	TclpCloseFile(outPipe);
sl@0
  1350
    }
sl@0
  1351
    if (errFile != NULL) {
sl@0
  1352
	TclpCloseFile(errFile);
sl@0
  1353
    }
sl@0
  1354
    return NULL;
sl@0
  1355
}