os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPipe.c
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 +}