tclpipe.c
来自「tcl是工具命令语言」· C语言 代码 · 共 1,071 行 · 第 1/3 页
C
1,071 行
/* * tclPipe.c -- * * This file contains the generic portion of the command channel * driver as well as various utility routines used in managing * subprocesses. * * 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. * * RCS: @(#) $Id: tclPipe.c,v 1.7 2002/12/17 02:47:39 davygrvy Exp $ */#include "tclInt.h"#include "tclPort.h"/* * A linked list of the following structures is used to keep track * of child processes that have been detached but haven't exited * yet, so we can make sure that they're properly "reaped" (officially * waited for) and don't lie around as zombies cluttering the * system. */typedef struct Detached { Tcl_Pid pid; /* Id of process that's been detached * but isn't known to have exited. */ struct Detached *nextPtr; /* Next in list of all detached * processes. */} Detached;static Detached *detList = NULL; /* List of all detached proceses. */TCL_DECLARE_MUTEX(pipeMutex) /* Guard access to detList. *//* * Declarations for local procedures defined in this file: */static TclFile FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp, CONST char *spec, int atOk, CONST char *arg, CONST char *nextArg, int flags, int *skipPtr, int *closePtr, int *releasePtr));/* *---------------------------------------------------------------------- * * FileForRedirect -- * * This procedure does much of the work of parsing redirection * operators. It handles "@" if specified and allowed, and a file * name, and opens the file if necessary. * * Results: * The return value is the descriptor number for the file. If an * error occurs then NULL is returned and an error message is left * in the interp's result. Several arguments are side-effected; see * the argument list below for details. * * Side effects: * None. * *---------------------------------------------------------------------- */static TclFileFileForRedirect(interp, spec, atOK, arg, nextArg, flags, skipPtr, closePtr, releasePtr) Tcl_Interp *interp; /* Intepreter to use for error reporting. */ CONST char *spec; /* Points to character just after * redirection character. */ CONST char *arg; /* Pointer to entire argument containing * spec: used for error reporting. */ int atOK; /* Non-zero means that '@' notation can be * used to specify a channel, zero means that * it isn't. */ 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 mode for channel. */ int *skipPtr; /* Filled with 1 if redirection target was * in spec, 2 if it was in nextArg. */ int *closePtr; /* Filled with one if the caller should * close the file when done with it, zero * otherwise. */ int *releasePtr;{ int writing = (flags & O_WRONLY); Tcl_Channel chan; TclFile file; *skipPtr = 1; if ((atOK != 0) && (*spec == '@')) { spec++; if (*spec == '\0') { spec = nextArg; if (spec == NULL) { goto badLastArg; } *skipPtr = 2; } 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_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan), "\" wasn't opened for ", ((writing) ? "writing" : "reading"), (char *) NULL); return NULL; } *releasePtr = 1; if (writing) { /* * Be sure to flush output to the file, so that anything * written by the child appears after stuff we've already * written. */ Tcl_Flush(chan); } } else { CONST char *name; Tcl_DString nameString; if (*spec == '\0') { spec = nextArg; if (spec == NULL) { goto badLastArg; } *skipPtr = 2; } name = Tcl_TranslateFileName(interp, spec, &nameString); if (name != NULL) { file = TclpOpenFile(name, flags); } else { file = NULL; } Tcl_DStringFree(&nameString); if (file == NULL) { Tcl_AppendResult(interp, "couldn't ", ((writing) ? "write" : "read"), " file \"", spec, "\": ", Tcl_PosixError(interp), (char *) NULL); return NULL; } *closePtr = 1; } return file; badLastArg: Tcl_AppendResult(interp, "can't specify \"", arg, "\" as last word in command", (char *) NULL); return NULL;}/* *---------------------------------------------------------------------- * * Tcl_DetachPids -- * * This procedure is called to indicate that one or more child * processes have been placed in background and will never be * waited for; they should eventually be reaped by * Tcl_ReapDetachedProcs. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */voidTcl_DetachPids(numPids, pidPtr) int numPids; /* Number of pids to detach: gives size * of array pointed to by pidPtr. */ Tcl_Pid *pidPtr; /* Array of pids to detach. */{ register Detached *detPtr; int i; Tcl_MutexLock(&pipeMutex); for (i = 0; i < numPids; i++) { detPtr = (Detached *) ckalloc(sizeof(Detached)); detPtr->pid = pidPtr[i]; detPtr->nextPtr = detList; detList = detPtr; } Tcl_MutexUnlock(&pipeMutex);}/* *---------------------------------------------------------------------- * * Tcl_ReapDetachedProcs -- * * This procedure checks to see if any detached processes have * exited and, if so, it "reaps" them by officially waiting on * them. It should be called "occasionally" to make sure that * all detached processes are eventually reaped. * * Results: * None. * * Side effects: * Processes are waited on, so that they can be reaped by the * system. * *---------------------------------------------------------------------- */voidTcl_ReapDetachedProcs(){ register Detached *detPtr; Detached *nextPtr, *prevPtr; int status; Tcl_Pid pid; Tcl_MutexLock(&pipeMutex); for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) { pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG); if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) { prevPtr = detPtr; detPtr = detPtr->nextPtr; continue; } nextPtr = detPtr->nextPtr; if (prevPtr == NULL) { detList = detPtr->nextPtr; } else { prevPtr->nextPtr = detPtr->nextPtr; } ckfree((char *) detPtr); detPtr = nextPtr; } Tcl_MutexUnlock(&pipeMutex);}/* *---------------------------------------------------------------------- * * TclCleanupChildren -- * * This is a utility procedure used to wait for child processes * to exit, record information about abnormal exits, and then * collect any stderr output generated by them. * * Results: * The return value is a standard Tcl result. If anything at * weird happened with the child processes, TCL_ERROR is returned * and a message is left in the interp's result. * * Side effects: * If the last character of the interp's result is a newline, then it * is removed unless keepNewline is non-zero. File errorId gets * closed, and pidPtr is freed back to the storage allocator. * *---------------------------------------------------------------------- */intTclCleanupChildren(interp, numPids, pidPtr, errorChan) Tcl_Interp *interp; /* Used for error messages. */ int numPids; /* Number of entries in pidPtr array. */ Tcl_Pid *pidPtr; /* Array of process ids of children. */ Tcl_Channel errorChan; /* Channel for file containing stderr output * from pipeline. NULL means there isn't any * stderr output. */{ int result = TCL_OK; int i, abnormalExit, anyErrorInfo; Tcl_Pid pid; WAIT_STATUS_TYPE waitStatus; CONST char *msg; unsigned long resolvedPid; abnormalExit = 0; for (i = 0; i < numPids; i++) { /* * We need to get the resolved pid before we wait on it as * the windows implimentation 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 != (Tcl_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, (char *) NULL); } continue; } /* * Create error messages for unusual process exits. An * extra newline gets appended to each error message, but * it gets removed below (in the same fashion that an * extra newline in the command's output is removed). */ if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) { char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE]; result = TCL_ERROR; TclFormatInt(msg1, (long) resolvedPid); if (WIFEXITED(waitStatus)) { if (interp != (Tcl_Interp *) NULL) { TclFormatInt(msg2, WEXITSTATUS(waitStatus)); Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2, (char *) NULL); } abnormalExit = 1; } else if (WIFSIGNALED(waitStatus)) { if (interp != (Tcl_Interp *) NULL) { CONST char *p; p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus))); Tcl_SetErrorCode(interp, "CHILDKILLED", msg1, Tcl_SignalId((int) (WTERMSIG(waitStatus))), p, (char *) NULL); Tcl_AppendResult(interp, "child killed: ", p, "\n", (char *) NULL); } } else if (WIFSTOPPED(waitStatus)) { if (interp != (Tcl_Interp *) NULL) { CONST char *p; p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus))); Tcl_SetErrorCode(interp, "CHILDSUSP", msg1, Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p, (char *) NULL); Tcl_AppendResult(interp, "child suspended: ", p, "\n", (char *) NULL); } } else { if (interp != (Tcl_Interp *) NULL) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?