⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tclpipe.c

📁 linux系统下的音频通信
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  * 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. * * SCCS: @(#) tclPipe.c 1.8 97/06/20 13:26:45 */#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. *//* * Declarations for local procedures defined in this file: */static TclFile	FileForRedirect _ANSI_ARGS_((Tcl_Interp *interp,	            char *spec, int atOk, char *arg, 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 interp->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. */    char *spec;			/* Points to character just after				 * redirection character. */    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. */    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 {	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;    for (i = 0; i < numPids; i++) {	detPtr = (Detached *) ckalloc(sizeof(Detached));	detPtr->pid = pidPtr[i];	detPtr->nextPtr = detList;	detList = detPtr;    }}/* *---------------------------------------------------------------------- * * 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;    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;    }}/* *---------------------------------------------------------------------- * * 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 interp->result. * * Side effects: *	If the last character of interp->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;    char *msg;    abnormalExit = 0;    for (i = 0; i < numPids; 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[20], msg2[20];	    result = TCL_ERROR;	    sprintf(msg1, "%ld", TclpGetPid(pid));	    if (WIFEXITED(waitStatus)) {                if (interp != (Tcl_Interp *) NULL) {                    sprintf(msg2, "%d", WEXITSTATUS(waitStatus));                    Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,                            (char *) NULL);                }		abnormalExit = 1;	    } else if (WIFSIGNALED(waitStatus)) {                if (interp != (Tcl_Interp *) NULL) {                    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) {                    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) {                    Tcl_AppendResult(interp,                            "child wait status didn't make sense\n",                            (char *) NULL);                }	    }	}    }    /*     * Read the standard error file.  If there's anything there,     * then return an error and add the file's contents to the result     * string.     */    anyErrorInfo = 0;    if (errorChan != NULL) {	/*	 * Make sure we start at the beginning of the file.	 */	Tcl_Seek(errorChan, 0L, SEEK_SET);        if (interp != (Tcl_Interp *) NULL) {            while (1) {#define BUFFER_SIZE 1000                char buffer[BUFFER_SIZE+1];                int count;                    count = Tcl_Read(errorChan, buffer, BUFFER_SIZE);                if (count == 0) {                    break;                }                result = TCL_ERROR;                if (count < 0) {                    Tcl_AppendResult(interp,                            "error reading stderr output file: ",                            Tcl_PosixError(interp), (char *) NULL);                    break;	/* out of the "while (1)" loop. */                }                buffer[count] = 0;                Tcl_AppendResult(interp, buffer, (char *) NULL);                anyErrorInfo = 1;            }        }        	Tcl_Close((Tcl_Interp *) NULL, errorChan);    }    /*     * If a child exited abnormally but didn't output any error information     * at all, generate an error message here.     */    if (abnormalExit && !anyErrorInfo && (interp != (Tcl_Interp *) NULL)) {	Tcl_AppendResult(interp, "child process exited abnormally",		(char *) NULL);    }        return result;}/* *---------------------------------------------------------------------- * * TclCreatePipeline -- * *	Given an argc/argv array, instantiate a pipeline of processes *	as described by the argv. * *	This procedure is unofficially exported for use by BLT. * * Results: *	The return value is a count of the number of new processes *	created, or -1 if an error occurred while creating the pipeline. *	*pidArrayPtr is filled in with the address of a dynamically *	allocated array giving the ids of all of the processes.  It *	is up to the caller to free this array when it isn't needed *	anymore.  If inPipePtr is non-NULL, *inPipePtr is filled in *	with the file id for the input pipe for the pipeline (if any): *	the caller must eventually close this file.  If outPipePtr *	isn't NULL, then *outPipePtr is filled in with the file id *	for the output pipe from the pipeline:  the caller must close *	this file.  If errFilePtr isn't NULL, then *errFilePtr is filled *	with a file id that may be used to read error output after the *	pipeline completes. * * Side effects: *	Processes and pipes are created. * *---------------------------------------------------------------------- */intTclCreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,	outPipePtr, errFilePtr)    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */    int argc;			/* Number of entries in argv. */    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				 * address of array of pids for processes				 * in pipeline (first pid is first process				 * in pipeline). */    TclFile *inPipePtr;		/* If non-NULL, input to the pipeline comes				 * from a pipe (unless overridden by				 * redirection in the command).  The file				 * id with which to write to this pipe is				 * stored at *inPipePtr.  NULL means command				 * specified its own input source. */    TclFile *outPipePtr;	/* If non-NULL, output to the pipeline goes				 * to a pipe, unless overriden by redirection				 * in the command.  The file id with which to				 * read 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				 * pipeline will go to a temporary file				 * created here, and a descriptor to read				 * the file will be left at *errFilePtr.				 * The file will be removed already, so				 * closing this descriptor will be the end				 * of the file.  If this is NULL, then				 * all stderr output goes to our stderr.				 * If the pipeline specifies redirection				 * then the file will still be created				 * but it will never get any data. */{    Tcl_Pid *pidPtr = NULL;	/* Points to malloc-ed array holding all				 * the pids of child processes. */    int numPids;		/* Actual number of processes that exist				 * at *pidPtr right now. */    int cmdCount;		/* Count of number of distinct commands				 * found in argc/argv. */    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 pipeline. */    TclFile inputFile = NULL;	/* If != NULL, gives file to use as input for				 * first process in pipeline (specified via <				 * or <@). */    int inputClose = 0;		/* If non-zero, then inputFile should be     				 * 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. */    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. */    int errorRelease = 0;    char *p;    int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;    Tcl_DString execBuffer;    TclFile pipeIn;    TclFile curInFile, curOutFile, curErrFile;    Tcl_Channel channel;    if (inPipePtr != NULL) {	*inPipePtr = NULL;    }    if (outPipePtr != NULL) {	*outPipePtr = NULL;    }    if (errFilePtr != NULL) {	*errFilePtr = NULL;    }    Tcl_DStringInit(&execBuffer);        pipeIn = NULL;    curInFile = NULL;    curOutFile = NULL;    numPids = 0;    /*     * First, scan through all the arguments to figure out the structure     * of the pipeline.  Process all of the input and output redirection

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -