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

📄 tcluxutl.c

📁 CMX990 demonstration board (DE9901)
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 
 * tclUnixUtil.c --
 *
 *	This file contains a collection of utility procedures that
 *	are present in the Tcl's UNIX core but not in the generic
 *	core.  For example, they do file manipulation and process
 *	manipulation.
 *
 *	The Tcl_Fork and Tcl_WaitPids procedures are based on code
 *	contributed by Karl Lehenbauer, Mark Diekhans and Peter
 *	da Silva.
 *
 * Copyright 1991 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that this copyright
 * notice appears in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 *
 * $Id: tcluxutl.c,v 1.1.1.1 2001/04/29 20:35:47 karll Exp $
 */

#include "tclInt.h"
#include "tclEcos.h"

/*
 * Data structures of the following type are used by Tcl_Fork and
 * Tcl_WaitPids to keep track of child processes.
 */
#define WAIT_STATUS_TYPE int

typedef struct {
    int pid;			/* Process id of child. */
    WAIT_STATUS_TYPE status;	/* Status returned when child exited or
				 * suspended. */
    int flags;			/* Various flag bits;  see below for
				 * definitions. */
} WaitInfo;

/*
 * Flag bits in WaitInfo structures:
 *
 * WI_READY -			Non-zero means process has exited or
 *				suspended since it was forked or last
 *				returned by Tcl_WaitPids.
 * WI_DETACHED -		Non-zero means no-one cares about the
 *				process anymore.  Ignore it until it
 *				exits, then forget about it.
 */

#define WI_READY	1
#define WI_DETACHED	2

#if TCL_FORK_ENABLED
static WaitInfo *waitTable = NULL;
static int waitTableSize = 0;	/* Total number of entries available in
				 * waitTable. */
static int waitTableUsed = 0;	/* Number of entries in waitTable that
				 * are actually in use right now.  Active
				 * entries are always at the beginning
				 * of the table. */
#define WAIT_TABLE_GROW_BY 4
#endif

/*
 *----------------------------------------------------------------------
 *
 * Tcl_EvalFile --
 *
 *	Read in a file and process the entire file as one gigantic
 *	Tcl command.
 *
 * Results:
 *	A standard Tcl result, which is either the result of executing
 *	the file or an error indicating why the file couldn't be read.
 *
 * Side effects:
 *	Depends on the commands in the file.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_EvalFile(interp, fileName)
    Tcl_Interp *interp;		/* Interpreter in which to process file. */
    char *fileName;		/* Name of file to process.  Tilde-substitution
				 * will be performed on this name. */
{
    int fileId, result;
    struct stat statBuf;
    char *cmdBuffer, *end, *oldScriptFile;
    Interp *iPtr = (Interp *) interp;

    oldScriptFile = iPtr->scriptFile;
    iPtr->scriptFile = fileName;
    fileName = Tcl_TildeSubst(interp, fileName);
    if (fileName == NULL) {
	goto error;
    }
    fileId = open(fileName, O_RDONLY, 0);
    if (fileId < 0) {
	Tcl_AppendResult(interp, "couldn't read file \"", fileName,
		"\": ", Tcl_UnixError(interp), (char *) NULL);
	goto error;
    }
    if (fstat(fileId, &statBuf) == -1) {
	Tcl_AppendResult(interp, "couldn't stat file \"", fileName,
		"\": ", Tcl_UnixError(interp), (char *) NULL);
	close(fileId);
	goto error;
    }
    cmdBuffer = (char *) ckalloc((unsigned) statBuf.st_size+1);
    if (read(fileId, cmdBuffer, (int) statBuf.st_size) != statBuf.st_size) {
	Tcl_AppendResult(interp, "error in reading file \"", fileName,
		"\": ", Tcl_UnixError(interp), (char *) NULL);
	close(fileId);
	ckfree(cmdBuffer);
	goto error;
    }
    if (close(fileId) != 0) {
	Tcl_AppendResult(interp, "error closing file \"", fileName,
		"\": ", Tcl_UnixError(interp), (char *) NULL);
	ckfree(cmdBuffer);
	goto error;
    }
    cmdBuffer[statBuf.st_size] = 0;
    result = Tcl_Eval(interp, cmdBuffer, 0, &end);
    if (result == TCL_RETURN) {
	result = TCL_OK;
    }
    if (result == TCL_ERROR) {
	char msg[200];

	/*
	 * Record information telling where the error occurred.
	 */

	sprintf(msg, "\n    (file \"%.150s\" line %d)", fileName,
		interp->errorLine);
	Tcl_AddErrorInfo(interp, msg);
    }
    ckfree(cmdBuffer);
    iPtr->scriptFile = oldScriptFile;
    return result;

    error:
    iPtr->scriptFile = oldScriptFile;
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_Fork --
 *
 *	Create a new process using the vfork system call, and keep
 *	track of it for "safe" waiting with Tcl_WaitPids.
 *
 * Results:
 *	The return value is the value returned by the vfork system
 *	call (0 means child, > 0 means parent (value is child id),
 *	< 0 means error).
 *
 * Side effects:
 *	A new process is created, and an entry is added to an internal
 *	table of child processes if the process is created successfully.
 *
 *----------------------------------------------------------------------
 */
#if TCL_FORK_ENABLED
int
Tcl_Fork()
{
    WaitInfo *waitPtr;
    pid_t pid;

    /*
     * Disable SIGPIPE signals:  if they were allowed, this process
     * might go away unexpectedly if children misbehave.  This code
     * can potentially interfere with other application code that
     * expects to handle SIGPIPEs;  what's really needed is an
     * arbiter for signals to allow them to be "shared".
     */

    if (waitTable == NULL) {
	(void) signal(SIGPIPE, SIG_IGN);
    }

    /*
     * Enlarge the wait table if there isn't enough space for a new
     * entry.
     */

    if (waitTableUsed == waitTableSize) {
	int newSize;
	WaitInfo *newWaitTable;

	newSize = waitTableSize + WAIT_TABLE_GROW_BY;
	newWaitTable = (WaitInfo *) ckalloc((unsigned)
		(newSize * sizeof(WaitInfo)));
	memcpy((VOID *) newWaitTable, (VOID *) waitTable,
		(waitTableSize * sizeof(WaitInfo)));
	if (waitTable != NULL) {
	    ckfree((char *) waitTable);
	}
	waitTable = newWaitTable;
	waitTableSize = newSize;
    }

    /*
     * Make a new process and enter it into the table if the fork
     * is successful.
     */

    waitPtr = &waitTable[waitTableUsed];
    pid = vfork();
    if (pid > 0) {
	waitPtr->pid = pid;
	waitPtr->flags = 0;
	waitTableUsed++;
    }
    return pid;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_WaitPids --
 *
 *	This procedure is used to wait for one or more processes created
 *	by Tcl_Fork to exit or suspend.  It records information about
 *	all processes that exit or suspend, even those not waited for,
 *	so that later waits for them will be able to get the status
 *	information.
 *
 * Results:
 *	-1 is returned if there is an error in the wait kernel call.
 *	Otherwise the pid of an exited/suspended process from *pidPtr
 *	is returned and *statusPtr is set to the status value returned
 *	by the wait kernel call.
 *
 * Side effects:
 *	Doesn't return until one of the pids at *pidPtr exits or suspends.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_WaitPids(numPids, pidPtr, statusPtr)
    int numPids;		/* Number of pids to wait on:  gives size
				 * of array pointed to by pidPtr. */
    int *pidPtr;		/* Pids to wait on:  return when one of
				 * these processes exits or suspends. */
    int *statusPtr;		/* Wait status is returned here. */
{
    int i, count, pid;
    register WaitInfo *waitPtr;
    int anyProcesses;
    WAIT_STATUS_TYPE status;

    while (1) {
	/*
	 * Scan the table of child processes to see if one of the
	 * specified children has already exited or suspended.  If so,
	 * remove it from the table and return its status.
	 */

	anyProcesses = 0;
	for (waitPtr = waitTable, count = waitTableUsed;
		count > 0; waitPtr++, count--) {
	    for (i = 0; i < numPids; i++) {
		if (pidPtr[i] != waitPtr->pid) {
		    continue;
		}
		anyProcesses = 1;
		if (waitPtr->flags & WI_READY) {
		    *statusPtr = *((int *) &waitPtr->status);
		    pid = waitPtr->pid;
		    if (WIFEXITED(waitPtr->status)
			    || WIFSIGNALED(waitPtr->status)) {
			*waitPtr = waitTable[waitTableUsed-1];
			waitTableUsed--;
		    } else {
			waitPtr->flags &= ~WI_READY;
		    }
		    return pid;
		}
	    }
	}

	/*
	 * Make sure that the caller at least specified one valid
	 * process to wait for.
	 */

	if (!anyProcesses) {
	    errno = ECHILD;
	    return -1;
	}

	/*
	 * Wait for a process to exit or suspend, then update its
	 * entry in the table and go back to the beginning of the
	 * loop to see if it's one of the desired processes.
	 */

	pid = wait(&status);
	if (pid < 0) {
	    return pid;
	}
	for (waitPtr = waitTable, count = waitTableUsed; ;
		waitPtr++, count--) {
	    if (count == 0) {
		break;			/* Ignore unknown processes. */
	    }
	    if (pid != waitPtr->pid) {
		continue;
	    }

	    /*
	     * If the process has been detached, then ignore anything
	     * other than an exit, and drop the entry on exit.
	     */

	    if (waitPtr->flags & WI_DETACHED) {
		if (WIFEXITED(status) || WIFSIGNALED(status)) {
		    *waitPtr = waitTable[waitTableUsed-1];
		    waitTableUsed--;
		}
	    } else {
		waitPtr->status = status;
		waitPtr->flags |= WI_READY;
	    }
	    break;
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_DetachPids --
 *
 *	This procedure is called to indicate that one or more child
 *	processes have been placed in background and are no longer
 *	cared about.  They should be ignored in future calls to
 *	Tcl_WaitPids.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_DetachPids(numPids, pidPtr)
    int numPids;		/* Number of pids to detach:  gives size
				 * of array pointed to by pidPtr. */
    int *pidPtr;		/* Array of pids to detach:  must have
				 * been created by Tcl_Fork. */
{
    register WaitInfo *waitPtr;
    int i, count, pid;

    for (i = 0; i < numPids; i++) {
	pid = pidPtr[i];
	for (waitPtr = waitTable, count = waitTableUsed;
		count > 0; waitPtr++, count--) {
	    if (pid != waitPtr->pid) {
		continue;
	    }

	    /*
	     * If the process has already exited then destroy its
	     * table entry now.
	     */

	    if ((waitPtr->flags & WI_READY) && (WIFEXITED(waitPtr->status)
		    || WIFSIGNALED(waitPtr->status))) {
		*waitPtr = waitTable[waitTableUsed-1];
		waitTableUsed--;
	    } else {
		waitPtr->flags |= WI_DETACHED;
	    }
	    goto nextPid;
	}
	panic("Tcl_Detach couldn't find process");

	nextPid:
	continue;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_CreatePipeline --
 *
 *	Given an argc/argv array, instantiate a pipeline of processes
 *	as described by the argv.
 *
 * 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.
 *
 *----------------------------------------------------------------------
 */

int
Tcl_CreatePipeline(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 <,
				 * <<, and >.  Argv[argc] must be NULL. */
    int **pidArrayPtr;		/* Word at *pidArrayPtr gets filled in with
				 * address of array of pids for processes
				 * in pipeline (first pid is first process
				 * in pipeline). */
    int *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.  -1 means command
				 * specified its own input source. */
    int *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.  -1 means command specified
				 * its own output sink. */
    int *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. */
{
    int *pidPtr = NULL;		/* Points to malloc-ed array holding all
				 * the pids of child processes. */
    int numPids = 0;		/* Actual number of processes that exist
				 * at *pidPtr right now. */
    int cmdCount;		/* Count of number of distinct commands
				 * found in argc/argv. */
    char *input = NULL;		/* Describes input for pipeline, depending
				 * on "inputFile".  NULL means take input
				 * from stdin/pipe. */
    int inputFile = 0;		/* Non-zero means input is name of input
				 * file.  Zero means input holds actual
				 * text to be input to command. */
    char *output = NULL;	/* Holds name of output file to pipe to,
				 * or NULL if output goes to stdout/pipe. */
    int inputId = -1;		/* Readable file id input to current command in
				 * pipeline (could be file or pipe).  -1
				 * means use stdin. */
    int outputId = -1;		/* Writable file id for output from current
				 * command in pipeline (could be file or pipe).
				 * -1 means use stdout. */
    int errorId = -1;		/* Writable file id for all standard error
				 * output from all commands in pipeline.  -1
				 * means use stderr. */
    int lastOutputId = -1;	/* Write file id for output from last command
				 * in pipeline (could be file or pipe).
				 * -1 means use stdout. */
    int pipeIds[2];		/* File ids for pipe that's being created. */
    int firstArg, lastArg;	/* Indexes of first and last arguments in
				 * current command. */
    int lastBar;
    char *execName;
    int i, j, pid;

    if (inPipePtr != NULL) {
	*inPipePtr = -1;
    }
    if (outPipePtr != NULL) {
	*outPipePtr = -1;
    }
    if (errFilePtr != NULL) {
	*errFilePtr = -1;
    }
    pipeIds[0] = pipeIds[1] = -1;

    /*
     * First, scan through all the arguments to figure out the structure
     * of the pipeline.  Count the number of distinct processes (it's the

⌨️ 快捷键说明

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