diff options
Diffstat (limited to 'generic/tclPipe.c')
| -rw-r--r-- | generic/tclPipe.c | 299 |
1 files changed, 150 insertions, 149 deletions
diff --git a/generic/tclPipe.c b/generic/tclPipe.c index 8b6eb11..698f85d 100644 --- a/generic/tclPipe.c +++ b/generic/tclPipe.c @@ -4,7 +4,7 @@ * This file contains the generic portion of the command channel driver * as well as various utility routines used in managing subprocesses. * - * Copyright © 1997 Sun Microsystems, Inc. + * Copyright (c) 1997 by Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -32,8 +32,8 @@ TCL_DECLARE_MUTEX(pipeMutex) /* Guard access to detList. */ * Declarations for local functions defined in this file: */ -static TclFile FileForRedirect(Tcl_Interp *interp, const char *spec, - int atOk, const char *arg, const char *nextArg, +static TclFile FileForRedirect(Tcl_Interp *interp, CONST char *spec, + int atOk, CONST char *arg, CONST char *nextArg, int flags, int *skipPtr, int *closePtr, int *releasePtr); @@ -60,15 +60,15 @@ static TclFile FileForRedirect(Tcl_Interp *interp, const char *spec, static TclFile FileForRedirect( - Tcl_Interp *interp, /* Interpreter to use for error reporting. */ - const char *spec, /* Points to character just after redirection + Tcl_Interp *interp, /* Intepreter to use for error reporting. */ + CONST char *spec, /* Points to character just after redirection * character. */ int atOK, /* Non-zero means that '@' notation can be * used to specify a channel, zero means that * it isn't. */ - const char *arg, /* Pointer to entire argument containing spec: + CONST char *arg, /* Pointer to entire argument containing spec: * used for error reporting. */ - const char *nextArg, /* Next argument in argc/argv array, if needed + CONST char *nextArg, /* Next argument in argc/argv array, if needed * for file name or channel name. May be * NULL. */ int flags, /* Flags to use for opening file or to specify @@ -94,27 +94,23 @@ FileForRedirect( } *skipPtr = 2; } - chan = Tcl_GetChannel(interp, spec, NULL); - if (chan == (Tcl_Channel) NULL) { - return NULL; - } + chan = Tcl_GetChannel(interp, spec, NULL); + if (chan == (Tcl_Channel) NULL) { + return NULL; + } file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE); - if (file == NULL) { - Tcl_Obj *msg; - + if (file == NULL) { + Tcl_Obj* msg; Tcl_GetChannelError(chan, &msg); if (msg) { - Tcl_SetObjResult(interp, msg); + Tcl_SetObjResult (interp, msg); } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "channel \"%s\" wasn't opened for %s", - Tcl_GetChannelName(chan), - ((writing) ? "writing" : "reading"))); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", - "BADCHAN", (void *)NULL); + Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan), + "\" wasn't opened for ", + ((writing) ? "writing" : "reading"), NULL); } - return NULL; - } + return NULL; + } *releasePtr = 1; if (writing) { /* @@ -122,10 +118,10 @@ FileForRedirect( * by the child appears after stuff we've already written. */ - Tcl_Flush(chan); + Tcl_Flush(chan); } } else { - const char *name; + CONST char *name; Tcl_DString nameString; if (*spec == '\0') { @@ -142,20 +138,18 @@ FileForRedirect( file = TclpOpenFile(name, flags); Tcl_DStringFree(&nameString); if (file == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't %s file \"%s\": %s", - (writing ? "write" : "read"), spec, - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, "couldn't ", + ((writing) ? "write" : "read"), " file \"", spec, "\": ", + Tcl_PosixError(interp), NULL); return NULL; } - *closePtr = 1; + *closePtr = 1; } return file; badLastArg: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't specify \"%s\" as last word in command", arg)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", "SYNTAX", (void *)NULL); + Tcl_AppendResult(interp, "can't specify \"", arg, + "\" as last word in command", NULL); return NULL; } @@ -183,12 +177,12 @@ Tcl_DetachPids( * array pointed to by pidPtr. */ Tcl_Pid *pidPtr) /* Array of pids to detach. */ { - Detached *detPtr; + register Detached *detPtr; int i; Tcl_MutexLock(&pipeMutex); for (i = 0; i < numPids; i++) { - detPtr = (Detached *)ckalloc(sizeof(Detached)); + detPtr = (Detached *) ckalloc(sizeof(Detached)); detPtr->pid = pidPtr[i]; detPtr->nextPtr = detList; detList = detPtr; @@ -219,15 +213,15 @@ Tcl_DetachPids( void Tcl_ReapDetachedProcs(void) { - Detached *detPtr; + register Detached *detPtr; Detached *nextPtr, *prevPtr; - int status, code; + int status; + Tcl_Pid pid; Tcl_MutexLock(&pipeMutex); for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) { - status = TclProcessWait(detPtr->pid, WNOHANG, &code, NULL, NULL); - if (status == TCL_PROCESS_UNCHANGED || (status == TCL_PROCESS_ERROR - && code != ECHILD)) { + pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG); + if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) { prevPtr = detPtr; detPtr = detPtr->nextPtr; continue; @@ -238,7 +232,7 @@ Tcl_ReapDetachedProcs(void) } else { prevPtr->nextPtr = detPtr->nextPtr; } - ckfree(detPtr); + ckfree((char *) detPtr); detPtr = nextPtr; } Tcl_MutexUnlock(&pipeMutex); @@ -277,21 +271,38 @@ TclCleanupChildren( { int result = TCL_OK; int i, abnormalExit, anyErrorInfo; - TclProcessWaitStatus waitStatus; - int code; - Tcl_Obj *msg, *error; + Tcl_Pid pid; + WAIT_STATUS_TYPE waitStatus; + CONST char *msg; + unsigned long resolvedPid; abnormalExit = 0; for (i = 0; i < numPids; i++) { - waitStatus = TclProcessWait(pidPtr[i], 0, &code, &msg, &error); - if (waitStatus == TCL_PROCESS_ERROR) { + /* + * We need to get the resolved pid before we wait on it as the windows + * implementation of Tcl_WaitPid deletes the information such that any + * following calls to TclpGetPid fail. + */ + + resolvedPid = TclpGetPid(pidPtr[i]); + pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0); + if (pid == (Tcl_Pid) -1) { result = TCL_ERROR; - if (interp != NULL) { - Tcl_SetObjErrorCode(interp, error); - Tcl_SetObjResult(interp, msg); - } - Tcl_DecrRefCount(error); - Tcl_DecrRefCount(msg); + if (interp != NULL) { + msg = Tcl_PosixError(interp); + if (errno == ECHILD) { + /* + * This changeup in message suggested by Mark Diekhans to + * remind people that ECHILD errors can occur on some + * systems if SIGCHLD isn't in its default state. + */ + + msg = + "child process lost (is SIGCHLD ignored or trapped?)"; + } + Tcl_AppendResult(interp, "error waiting for process to exit: ", + msg, NULL); + } continue; } @@ -302,19 +313,39 @@ TclCleanupChildren( * removed). */ - if (waitStatus != TCL_PROCESS_EXITED || code != 0) { + if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) { + char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE]; + result = TCL_ERROR; - if (waitStatus == TCL_PROCESS_EXITED) { - if (interp != NULL) { - Tcl_SetObjErrorCode(interp, error); - } + sprintf(msg1, "%lu", resolvedPid); + if (WIFEXITED(waitStatus)) { + if (interp != (Tcl_Interp *) NULL) { + sprintf(msg2, "%lu", + (unsigned long) WEXITSTATUS(waitStatus)); + Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2, NULL); + } abnormalExit = 1; } else if (interp != NULL) { - Tcl_SetObjErrorCode(interp, error); - Tcl_SetObjResult(interp, msg); + CONST char *p; + + if (WIFSIGNALED(waitStatus)) { + p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus))); + Tcl_SetErrorCode(interp, "CHILDKILLED", msg1, + Tcl_SignalId((int) (WTERMSIG(waitStatus))), p, + NULL); + Tcl_AppendResult(interp, "child killed: ", p, "\n", NULL); + } else if (WIFSTOPPED(waitStatus)) { + p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus))); + Tcl_SetErrorCode(interp, "CHILDSUSP", msg1, + Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p, + NULL); + Tcl_AppendResult(interp, "child suspended: ", p, "\n", + NULL); + } else { + Tcl_AppendResult(interp, + "child wait status didn't make sense\n", NULL); + } } - Tcl_DecrRefCount(error); - Tcl_DecrRefCount(msg); } } @@ -329,20 +360,19 @@ TclCleanupChildren( * Make sure we start at the beginning of the file. */ - if (interp != NULL) { + if (interp != NULL) { int count; Tcl_Obj *objPtr; - Tcl_Seek(errorChan, 0, SEEK_SET); - TclNewObj(objPtr); + Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET); + objPtr = Tcl_NewObj(); count = Tcl_ReadChars(errorChan, objPtr, -1, 0); if (count < 0) { result = TCL_ERROR; Tcl_DecrRefCount(objPtr); Tcl_ResetResult(interp); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "error reading stderr output file: %s", - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, "error reading stderr output file: ", + Tcl_PosixError(interp), NULL); } else if (count > 0) { anyErrorInfo = 1; Tcl_SetObjResult(interp, objPtr); @@ -360,8 +390,7 @@ TclCleanupChildren( */ if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "child process exited abnormally", -1)); + Tcl_AppendResult(interp, "child process exited abnormally", NULL); } return result; } @@ -399,7 +428,7 @@ int TclCreatePipeline( Tcl_Interp *interp, /* Interpreter to use for error reporting. */ int argc, /* Number of entries in argv. */ - const char **argv, /* Array of strings describing commands in + CONST char **argv, /* Array of strings describing commands in * pipeline plus I/O redirection with <, <<, * >, etc. Argv[argc] must be NULL. */ Tcl_Pid **pidArrayPtr, /* Word at *pidArrayPtr gets filled in with @@ -413,9 +442,9 @@ TclCreatePipeline( * at *inPipePtr. NULL means command specified * its own input source. */ TclFile *outPipePtr, /* If non-NULL, output to the pipeline goes to - * a pipe, unless overridden by redirection in + * a pipe, unless overriden by redirection in * the command. The file id with which to read - * from this pipe is stored at *outPipePtr. + * frome this pipe is stored at *outPipePtr. * NULL means command specified its own output * sink. */ TclFile *errFilePtr) /* If non-NULL, all stderr output from the @@ -435,7 +464,7 @@ TclCreatePipeline( * *pidPtr right now. */ int cmdCount; /* Count of number of distinct commands found * in argc/argv. */ - const char *inputLiteral = NULL; + CONST char *inputLiteral = NULL; /* If non-null, then this points to a string * containing input data (specified via <<) to * be piped to the first process in the @@ -444,22 +473,22 @@ TclCreatePipeline( * first process in pipeline (specified via < * or <@). */ int inputClose = 0; /* If non-zero, then inputFile should be - * closed when cleaning up. */ + * closed when cleaning up. */ int inputRelease = 0; TclFile outputFile = NULL; /* Writable file for output from last command * in pipeline (could be file or pipe). NULL * means use stdout. */ int outputClose = 0; /* If non-zero, then outputFile should be - * closed when cleaning up. */ + * closed when cleaning up. */ int outputRelease = 0; TclFile errorFile = NULL; /* Writable file for error output from all * commands in pipeline. NULL means use * stderr. */ int errorClose = 0; /* If non-zero, then errorFile should be - * closed when cleaning up. */ + * closed when cleaning up. */ int errorRelease = 0; - const char *p; - const char *nextArg; + CONST char *p; + CONST char *nextArg; int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0; Tcl_DString execBuffer; TclFile pipeIn; @@ -489,7 +518,7 @@ TclCreatePipeline( * and remove them from the argument list in the pipeline. Count the * number of distinct processes (it's the number of "|" arguments plus * one) but don't remove the "|" arguments because they'll be used in the - * second pass to separate the individual child processes. Cannot start + * second pass to seperate the individual child processes. Cannot start * the child processes in this pass because the redirection symbols may * appear anywhere in the command line - e.g., the '<' that specifies the * input to the entire pipe may appear at the very end of the argument @@ -510,10 +539,8 @@ TclCreatePipeline( } if (*p == '\0') { if ((i == (lastBar + 1)) || (i == (argc - 1))) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "illegal use of | or |& in command", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", - "PIPESYNTAX", (void *)NULL); + Tcl_SetResult(interp, "illegal use of | or |& in command", + TCL_STATIC); goto error; } } @@ -538,11 +565,8 @@ TclCreatePipeline( if (*inputLiteral == '\0') { inputLiteral = ((i + 1) == argc) ? NULL : argv[i + 1]; if (inputLiteral == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't specify \"%s\" as last word in command", - argv[i])); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", - "PIPESYNTAX", (void *)NULL); + Tcl_AppendResult(interp, "can't specify \"", argv[i], + "\" as last word in command", NULL); goto error; } skip = 2; @@ -631,13 +655,7 @@ TclCreatePipeline( if (*p == '>') { p++; atOK = 0; - - /* - * Note that the O_APPEND flag only has an effect on POSIX - * platforms. On Windows, we just have to carry on regardless. - */ - - flags = O_WRONLY | O_CREAT | O_APPEND; + flags = O_WRONLY | O_CREAT; } if (errorClose != 0) { errorClose = 0; @@ -655,11 +673,8 @@ TclCreatePipeline( */ if (i != argc-1) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "must specify \"%s\" as last word in command", - argv[i])); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", - "PIPESYNTAX", (void *)NULL); + Tcl_AppendResult(interp, "must specify \"", argv[i], + "\" as last word in command", NULL); goto error; } errorFile = outputFile; @@ -676,12 +691,9 @@ TclCreatePipeline( break; default: - /* - * Got a command word, not a redirection. - */ - - needCmd = 0; - break; + /* Got a command word, not a redirection */ + needCmd = 0; + break; } if (skip != 0) { @@ -694,14 +706,11 @@ TclCreatePipeline( } if (needCmd) { - /* - * We had a bar followed only by redirections. - */ + /* We had a bar followed only by redirections. */ - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "illegal use of | or |& in command", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", "PIPESYNTAX", - (void *)NULL); + Tcl_SetResult(interp, + "illegal use of | or |& in command", + TCL_STATIC); goto error; } @@ -715,9 +724,9 @@ TclCreatePipeline( inputFile = TclpCreateTempFile(inputLiteral); if (inputFile == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't create input file for command: %s", - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, + "couldn't create input file for command: ", + Tcl_PosixError(interp), NULL); goto error; } inputClose = 1; @@ -728,9 +737,9 @@ TclCreatePipeline( */ if (TclpCreatePipe(&inputFile, inPipePtr) == 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't create input pipe for command: %s", - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, + "couldn't create input pipe for command: ", + Tcl_PosixError(interp), NULL); goto error; } inputClose = 1; @@ -757,9 +766,9 @@ TclCreatePipeline( */ if (TclpCreatePipe(outPipePtr, &outputFile) == 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't create output pipe for command: %s", - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, + "couldn't create output pipe for command: ", + Tcl_PosixError(interp), NULL); goto error; } outputClose = 1; @@ -797,9 +806,9 @@ TclCreatePipeline( errorFile = TclpCreateTempFile(NULL); if (errorFile == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't create error file for command: %s", - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, + "couldn't create error file for command: ", + Tcl_PosixError(interp), NULL); goto error; } *errFilePtr = errorFile; @@ -824,14 +833,14 @@ TclCreatePipeline( */ Tcl_ReapDetachedProcs(); - pidPtr = (Tcl_Pid *)ckalloc(cmdCount * sizeof(Tcl_Pid)); + pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid))); curInFile = inputFile; for (i = 0; i < argc; i = lastArg + 1) { int result, joinThisError; Tcl_Pid pid; - const char *oldName; + CONST char *oldName; /* * Convert the program name into native form. @@ -870,8 +879,8 @@ TclCreatePipeline( } else { argv[lastArg] = NULL; if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't create pipe: %s", Tcl_PosixError(interp))); + Tcl_AppendResult(interp, "couldn't create pipe: ", + Tcl_PosixError(interp), NULL); goto error; } } @@ -899,7 +908,6 @@ TclCreatePipeline( pidPtr[numPids] = pid; numPids++; - TclProcessCreated(pid); /* * Close off our copies of file descriptors that were set up for this @@ -978,7 +986,7 @@ TclCreatePipeline( Tcl_DetachPids(1, &pidPtr[i]); } } - ckfree(pidPtr); + ckfree((char *) pidPtr); } numPids = -1; goto cleanup; @@ -1019,9 +1027,9 @@ TclCreatePipeline( Tcl_Channel Tcl_OpenCommandChannel( Tcl_Interp *interp, /* Interpreter for error reporting. Can NOT be - * NULL. */ + * NULL. */ int argc, /* How many arguments. */ - const char **argv, /* Array of arguments for command pipe. */ + CONST char **argv, /* Array of arguments for command pipe. */ int flags) /* Or'ed combination of TCL_STDIN, TCL_STDOUT, * TCL_STDERR, and TCL_ENFORCE_MODE. */ { @@ -1038,7 +1046,7 @@ Tcl_OpenCommandChannel( errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL; numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr, - outPipePtr, errFilePtr); + outPipePtr, errFilePtr); if (numPids < 0) { goto error; @@ -1051,19 +1059,13 @@ Tcl_OpenCommandChannel( if (flags & TCL_ENFORCE_MODE) { if ((flags & TCL_STDOUT) && (outPipe == NULL)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can't read output from command:" - " standard output was redirected", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", - "BADREDIRECT", (void *)NULL); + Tcl_AppendResult(interp, "can't read output from command:" + " standard output was redirected", NULL); goto error; } if ((flags & TCL_STDIN) && (inPipe == NULL)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can't write input to command:" - " standard input was redirected", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", - "BADREDIRECT", (void *)NULL); + Tcl_AppendResult(interp, "can't write input to command:" + " standard input was redirected", NULL); goto error; } } @@ -1071,10 +1073,9 @@ Tcl_OpenCommandChannel( channel = TclpCreateCommandChannel(outPipe, inPipe, errFile, numPids, pidPtr); - if (channel == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "pipe for command could not be created", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", "NOPIPE", (void *)NULL); + if (channel == (Tcl_Channel) NULL) { + Tcl_AppendResult(interp, "pipe for command could not be created", + NULL); goto error; } return channel; @@ -1082,7 +1083,7 @@ Tcl_OpenCommandChannel( error: if (numPids > 0) { Tcl_DetachPids(numPids, pidPtr); - ckfree(pidPtr); + ckfree((char *) pidPtr); } if (inPipe != NULL) { TclpCloseFile(inPipe); |
