2283 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2283 lines
		
	
	
		
			63 KiB
		
	
	
	
		
			C
		
	
	
	
/* 
 | 
						||
 * tclEvent.c --
 | 
						||
 *
 | 
						||
 *	This file provides basic event-managing facilities for Tcl,
 | 
						||
 *	including an event queue, and mechanisms for attaching
 | 
						||
 *	callbacks to certain events.
 | 
						||
 *
 | 
						||
 *	It also contains the command procedures for the commands
 | 
						||
 *	"after", "vwait", and "update".
 | 
						||
 *
 | 
						||
 * Copyright (c) 1990-1994 The Regents of the University of California.
 | 
						||
 * Copyright (c) 1994-1995 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: @(#) tclEvent.c 1.127 96/03/22 12:12:33
 | 
						||
 */
 | 
						||
 | 
						||
#include "tclInt.h"
 | 
						||
#include "tclPort.h"
 | 
						||
 | 
						||
/*
 | 
						||
 * For each file registered in a call to Tcl_CreateFileHandler,
 | 
						||
 * there is one record of the following type.  All of these records
 | 
						||
 * are chained together into a single list.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct FileHandler {
 | 
						||
    Tcl_File file;		/* Generic file handle for file. */
 | 
						||
    int mask;			/* Mask of desired events: TCL_READABLE, etc. */
 | 
						||
    int readyMask;		/* Events that were ready the last time that
 | 
						||
				 * FileHandlerCheckProc checked this file. */
 | 
						||
    Tcl_FileProc *proc;		/* Procedure to call, in the style of
 | 
						||
				 * Tcl_CreateFileHandler.  This is NULL
 | 
						||
				 * if the handler was created by
 | 
						||
				 * Tcl_CreateFileHandler2. */
 | 
						||
    ClientData clientData;	/* Argument to pass to proc. */
 | 
						||
    struct FileHandler *nextPtr;/* Next in list of all files we care
 | 
						||
				 * about (NULL for end of list). */
 | 
						||
} FileHandler;
 | 
						||
 | 
						||
static FileHandler *firstFileHandlerPtr = (FileHandler *) NULL;
 | 
						||
				/* List of all file handlers. */
 | 
						||
static int fileEventSourceCreated = 0;
 | 
						||
				/* Non-zero means that the file event source
 | 
						||
				 * hasn't been registerd with the Tcl
 | 
						||
				 * notifier yet. */
 | 
						||
 | 
						||
/*
 | 
						||
 * The following structure is what is added to the Tcl event queue when
 | 
						||
 * file handlers are ready to fire.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct FileHandlerEvent {
 | 
						||
    Tcl_Event header;		/* Information that is standard for
 | 
						||
				 * all events. */
 | 
						||
    Tcl_File file;		/* File descriptor that is ready.  Used
 | 
						||
				 * to find the FileHandler structure for
 | 
						||
				 * the file (can't point directly to the
 | 
						||
				 * FileHandler structure because it could
 | 
						||
				 * go away while the event is queued). */
 | 
						||
} FileHandlerEvent;
 | 
						||
 | 
						||
/*
 | 
						||
 * For each timer callback that's pending (either regular or "modal"),
 | 
						||
 * there is one record of the following type.  The normal handlers
 | 
						||
 * (created by Tcl_CreateTimerHandler) are chained together in a
 | 
						||
 * list sorted by time (earliest event first).
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct TimerHandler {
 | 
						||
    Tcl_Time time;			/* When timer is to fire. */
 | 
						||
    Tcl_TimerProc *proc;		/* Procedure to call. */
 | 
						||
    ClientData clientData;		/* Argument to pass to proc. */
 | 
						||
    Tcl_TimerToken token;		/* Identifies event so it can be
 | 
						||
					 * deleted.  Not used in modal
 | 
						||
					 * timeouts. */
 | 
						||
    struct TimerHandler *nextPtr;	/* Next event in queue, or NULL for
 | 
						||
					 * end of queue. */
 | 
						||
} TimerHandler;
 | 
						||
 | 
						||
static TimerHandler *firstTimerHandlerPtr = NULL;
 | 
						||
					/* First event in queue. */
 | 
						||
static int timerEventSourceCreated = 0;	/* 0 means that the timer event source
 | 
						||
					 * hasn't yet been registered with the
 | 
						||
					 * Tcl notifier. */
 | 
						||
 | 
						||
/*
 | 
						||
 * The information below describes a stack of modal timeouts managed by
 | 
						||
 * Tcl_CreateModalTimer and Tcl_DeleteModalTimer.  Only the first element
 | 
						||
 * in the list is used at any given time.
 | 
						||
 */
 | 
						||
 | 
						||
static TimerHandler *firstModalHandlerPtr = NULL;
 | 
						||
 | 
						||
/*
 | 
						||
 * The following structure is what's added to the Tcl event queue when
 | 
						||
 * timer handlers are ready to fire.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct TimerEvent {
 | 
						||
    Tcl_Event header;			/* Information that is standard for
 | 
						||
					 * all events. */
 | 
						||
    Tcl_Time time;			/* All timer events that specify this
 | 
						||
					 * time or earlier are ready
 | 
						||
                                         * to fire. */
 | 
						||
} TimerEvent;
 | 
						||
 | 
						||
/*
 | 
						||
 * There is one of the following structures for each of the
 | 
						||
 * handlers declared in a call to Tcl_DoWhenIdle.  All of the
 | 
						||
 * currently-active handlers are linked together into a list.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct IdleHandler {
 | 
						||
    Tcl_IdleProc (*proc);	/* Procedure to call. */
 | 
						||
    ClientData clientData;	/* Value to pass to proc. */
 | 
						||
    int generation;		/* Used to distinguish older handlers from
 | 
						||
				 * recently-created ones. */
 | 
						||
    struct IdleHandler *nextPtr;/* Next in list of active handlers. */
 | 
						||
} IdleHandler;
 | 
						||
 | 
						||
static IdleHandler *idleList = NULL;
 | 
						||
				/* First in list of all idle handlers. */
 | 
						||
static IdleHandler *lastIdlePtr = NULL;
 | 
						||
				/* Last in list (or NULL for empty list). */
 | 
						||
static int idleGeneration = 0;	/* Used to fill in the "generation" fields
 | 
						||
				 * of IdleHandler structures.  Increments
 | 
						||
				 * each time Tcl_DoOneEvent starts calling
 | 
						||
				 * idle handlers, so that all old handlers
 | 
						||
				 * can be called without calling any of the
 | 
						||
				 * new ones created by old ones. */
 | 
						||
 | 
						||
/*
 | 
						||
 * The data structure below is used by the "after" command to remember
 | 
						||
 * the command to be executed later.  All of the pending "after" commands
 | 
						||
 * for an interpreter are linked together in a list.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct AfterInfo {
 | 
						||
    struct AfterAssocData *assocPtr;
 | 
						||
				/* Pointer to the "tclAfter" assocData for
 | 
						||
				 * the interp in which command will be
 | 
						||
				 * executed. */
 | 
						||
    char *command;		/* Command to execute.  Malloc'ed, so must
 | 
						||
				 * be freed when structure is deallocated. */
 | 
						||
    int id;			/* Integer identifier for command;  used to
 | 
						||
				 * cancel it. */
 | 
						||
    Tcl_TimerToken token;	/* Used to cancel the "after" command.  NULL
 | 
						||
				 * means that the command is run as an
 | 
						||
				 * idle handler rather than as a timer
 | 
						||
				 * handler.  NULL means this is an "after
 | 
						||
				 * idle" handler rather than a
 | 
						||
                                 * timer handler. */
 | 
						||
    struct AfterInfo *nextPtr;	/* Next in list of all "after" commands for
 | 
						||
				 * this interpreter. */
 | 
						||
} AfterInfo;
 | 
						||
 | 
						||
/*
 | 
						||
 * One of the following structures is associated with each interpreter
 | 
						||
 * for which an "after" command has ever been invoked.  A pointer to
 | 
						||
 * this structure is stored in the AssocData for the "tclAfter" key.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct AfterAssocData {
 | 
						||
    Tcl_Interp *interp;		/* The interpreter for which this data is
 | 
						||
				 * registered. */
 | 
						||
    AfterInfo *firstAfterPtr;	/* First in list of all "after" commands
 | 
						||
				 * still pending for this interpreter, or
 | 
						||
				 * NULL if none. */
 | 
						||
} AfterAssocData;
 | 
						||
 | 
						||
#ifdef STk_CODE
 | 
						||
static AfterAssocData After_list;
 | 
						||
#endif
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*
 | 
						||
 * The data structure below is used to report background errors.  One
 | 
						||
 * such structure is allocated for each error;  it holds information
 | 
						||
 * about the interpreter and the error until bgerror can be invoked
 | 
						||
 * later as an idle handler.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct BgError {
 | 
						||
    Tcl_Interp *interp;		/* Interpreter in which error occurred.  NULL
 | 
						||
				 * means this error report has been cancelled
 | 
						||
				 * (a previous report generated a break). */
 | 
						||
    char *errorMsg;		/* The error message (interp->result when
 | 
						||
				 * the error occurred).  Malloc-ed. */
 | 
						||
    char *errorInfo;		/* Value of the errorInfo variable
 | 
						||
				 * (malloc-ed). */
 | 
						||
    char *errorCode;		/* Value of the errorCode variable
 | 
						||
				 * (malloc-ed). */
 | 
						||
    struct BgError *nextPtr;	/* Next in list of all pending error
 | 
						||
				 * reports for this interpreter, or NULL
 | 
						||
				 * for end of list. */
 | 
						||
} BgError;
 | 
						||
 | 
						||
/*
 | 
						||
 * One of the structures below is associated with the "tclBgError"
 | 
						||
 * assoc data for each interpreter.  It keeps track of the head and
 | 
						||
 * tail of the list of pending background errors for the interpreter.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct ErrAssocData {
 | 
						||
    BgError *firstBgPtr;	/* First in list of all background errors
 | 
						||
				 * waiting to be processed for this
 | 
						||
				 * interpreter (NULL if none). */
 | 
						||
    BgError *lastBgPtr;		/* Last in list of all background errors
 | 
						||
				 * waiting to be processed for this
 | 
						||
				 * interpreter (NULL if none). */
 | 
						||
} ErrAssocData;
 | 
						||
 | 
						||
/*
 | 
						||
 * For each exit handler created with a call to Tcl_CreateExitHandler
 | 
						||
 * there is a structure of the following type:
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct ExitHandler {
 | 
						||
    Tcl_ExitProc *proc;		/* Procedure to call when process exits. */
 | 
						||
    ClientData clientData;	/* One word of information to pass to proc. */
 | 
						||
    struct ExitHandler *nextPtr;/* Next in list of all exit handlers for
 | 
						||
				 * this application, or NULL for end of list. */
 | 
						||
} ExitHandler;
 | 
						||
 | 
						||
static ExitHandler *firstExitPtr = NULL;
 | 
						||
				/* First in list of all exit handlers for
 | 
						||
				 * application. */
 | 
						||
 | 
						||
/*
 | 
						||
 * Structures of the following type are used during the execution
 | 
						||
 * of Tcl_WaitForFile, to keep track of the file and timeout.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct FileWait {
 | 
						||
    Tcl_File file;		/* File to wait on. */
 | 
						||
    int mask;			/* Conditions to wait for (TCL_READABLE,
 | 
						||
				 * etc.) */
 | 
						||
    int timeout;		/* Original "timeout" argument to
 | 
						||
				 * Tcl_WaitForFile. */
 | 
						||
    Tcl_Time abortTime;		/* Time at which to abort the wait. */
 | 
						||
    int present;		/* Conditions present on the file during
 | 
						||
				 * the last time through the event loop. */
 | 
						||
    int done;			/* Non-zero means we're done:  either one of
 | 
						||
				 * the desired conditions is present or the
 | 
						||
				 * timeout period has elapsed. */
 | 
						||
} FileWait;
 | 
						||
 | 
						||
/*
 | 
						||
 * The following variable is a "secret" indication to Tcl_Exit that
 | 
						||
 * it should dump out the state of memory before exiting.  If the
 | 
						||
 * value is non-NULL, it gives the name of the file in which to
 | 
						||
 * dump memory usage information.
 | 
						||
 */
 | 
						||
 | 
						||
char *tclMemDumpFileName = NULL;
 | 
						||
 | 
						||
/*
 | 
						||
 * Prototypes for procedures referenced only in this file:
 | 
						||
 */
 | 
						||
 | 
						||
static void		AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
 | 
						||
			    Tcl_Interp *interp));
 | 
						||
static void		AfterProc _ANSI_ARGS_((ClientData clientData));
 | 
						||
static void		BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData,
 | 
						||
			    Tcl_Interp *interp));
 | 
						||
static void		FileHandlerCheckProc _ANSI_ARGS_((
 | 
						||
			    ClientData clientData, int flags));
 | 
						||
static int		FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
 | 
						||
			    int flags));
 | 
						||
static void		FileHandlerExitProc _ANSI_ARGS_((ClientData data));
 | 
						||
static void		FileHandlerSetupProc _ANSI_ARGS_((
 | 
						||
			    ClientData clientData, int flags));
 | 
						||
static void		FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
 | 
						||
static AfterInfo *	GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
 | 
						||
			    char *string));
 | 
						||
static void		HandleBgErrors _ANSI_ARGS_((ClientData clientData));
 | 
						||
static void		TimerHandlerCheckProc _ANSI_ARGS_((
 | 
						||
			    ClientData clientData, int flags));
 | 
						||
static int		TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
 | 
						||
			    int flags));
 | 
						||
static void		TimerHandlerExitProc _ANSI_ARGS_((ClientData data));
 | 
						||
static void		TimerHandlerSetupProc _ANSI_ARGS_((
 | 
						||
			    ClientData clientData, int flags));
 | 
						||
static char *		VwaitVarProc _ANSI_ARGS_((ClientData clientData,
 | 
						||
			    Tcl_Interp *interp, char *name1, char *name2,
 | 
						||
			    int flags));
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_CreateFileHandler --
 | 
						||
 *
 | 
						||
 *	Arrange for a given procedure to be invoked whenever
 | 
						||
 *	a given file becomes readable or writable.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	From now on, whenever the I/O channel given by file becomes
 | 
						||
 *	ready in the way indicated by mask, proc will be invoked.
 | 
						||
 *	See the manual entry for details on the calling sequence
 | 
						||
 *	to proc.  If file is already registered then the old mask
 | 
						||
 *	and proc and clientData values will be replaced with
 | 
						||
 *	new ones.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_CreateFileHandler(file, mask, proc, clientData)
 | 
						||
    Tcl_File file;		/* Handle of stream to watch. */
 | 
						||
    int mask;			/* OR'ed combination of TCL_READABLE,
 | 
						||
				 * TCL_WRITABLE, and TCL_EXCEPTION:
 | 
						||
				 * indicates conditions under which
 | 
						||
				 * proc should be called. */
 | 
						||
    Tcl_FileProc *proc;		/* Procedure to call for each
 | 
						||
				 * selected event. */
 | 
						||
    ClientData clientData;	/* Arbitrary data to pass to proc. */
 | 
						||
{
 | 
						||
    register FileHandler *filePtr;
 | 
						||
 | 
						||
    if (!fileEventSourceCreated) {
 | 
						||
	fileEventSourceCreated = 1;
 | 
						||
	Tcl_CreateEventSource(FileHandlerSetupProc, FileHandlerCheckProc,
 | 
						||
		(ClientData) NULL);
 | 
						||
        Tcl_CreateExitHandler(FileHandlerExitProc, (ClientData) NULL);
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Make sure the file isn't already registered.  Create a
 | 
						||
     * new record in the normal case where there's no existing
 | 
						||
     * record.
 | 
						||
     */
 | 
						||
 | 
						||
    for (filePtr = firstFileHandlerPtr; filePtr != NULL;
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr->file == file) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    if (filePtr == NULL) {
 | 
						||
	filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
 | 
						||
	filePtr->file = file;
 | 
						||
	filePtr->nextPtr = firstFileHandlerPtr;
 | 
						||
	firstFileHandlerPtr = filePtr;
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * The remainder of the initialization below is done regardless
 | 
						||
     * of whether or not this is a new record or a modification of
 | 
						||
     * an old one.
 | 
						||
     */
 | 
						||
 | 
						||
    filePtr->mask = mask;
 | 
						||
    filePtr->readyMask = 0;
 | 
						||
    filePtr->proc = proc;
 | 
						||
    filePtr->clientData = clientData;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_DeleteFileHandler --
 | 
						||
 *
 | 
						||
 *	Cancel a previously-arranged callback arrangement for
 | 
						||
 *	a file.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	If a callback was previously registered on file, remove it.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_DeleteFileHandler(file)
 | 
						||
    Tcl_File file;		/* Stream id for which to remove
 | 
						||
				 * callback procedure. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr, *prevPtr;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Find the entry for the given file (and return if there
 | 
						||
     * isn't one).
 | 
						||
     */
 | 
						||
 | 
						||
    for (prevPtr = NULL, filePtr = firstFileHandlerPtr; ;
 | 
						||
	    prevPtr = filePtr, filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr == NULL) {
 | 
						||
	    return;
 | 
						||
	}
 | 
						||
	if (filePtr->file == file) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Clean up information in the callback record.
 | 
						||
     */
 | 
						||
 | 
						||
    if (prevPtr == NULL) {
 | 
						||
	firstFileHandlerPtr = filePtr->nextPtr;
 | 
						||
    } else {
 | 
						||
	prevPtr->nextPtr = filePtr->nextPtr;
 | 
						||
    }
 | 
						||
    ckfree((char *) filePtr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * FileHandlerExitProc --
 | 
						||
 *
 | 
						||
 *	Cleanup procedure to delete the file event source during exit
 | 
						||
 *	cleanup.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Destroys the file event source.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
static void
 | 
						||
FileHandlerExitProc(clientData)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
{
 | 
						||
    Tcl_DeleteEventSource(FileHandlerSetupProc, FileHandlerCheckProc,
 | 
						||
            (ClientData) NULL);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * FileHandlerSetupProc --
 | 
						||
 *
 | 
						||
 *	This procedure is part of the "event source" for file handlers.
 | 
						||
 *	It is invoked by Tcl_DoOneEvent before it calls select (or
 | 
						||
 *	whatever it uses to wait).
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Tells the notifier which files should be waited for.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
FileHandlerSetupProc(clientData, flags)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
    int flags;				/* Flags passed to Tk_DoOneEvent:
 | 
						||
					 * if it doesn't include
 | 
						||
					 * TCL_FILE_EVENTS then we do
 | 
						||
					 * nothing. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr;
 | 
						||
 | 
						||
    if (!(flags & TCL_FILE_EVENTS)) {
 | 
						||
	return;
 | 
						||
    }
 | 
						||
    for (filePtr = firstFileHandlerPtr; filePtr != NULL;
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr->mask != 0) {
 | 
						||
	    Tcl_WatchFile(filePtr->file, filePtr->mask);
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * FileHandlerCheckProc --
 | 
						||
 *
 | 
						||
 *	This procedure is the second part of the "event source" for
 | 
						||
 *	file handlers.  It is invoked by Tcl_DoOneEvent after it calls
 | 
						||
 *	select (or whatever it uses to wait for events).
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Makes entries on the Tcl event queue for each file that is
 | 
						||
 *	now ready.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
FileHandlerCheckProc(clientData, flags)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
    int flags;				/* Flags passed to Tk_DoOneEvent:
 | 
						||
					 * if it doesn't include 
 | 
						||
					 * TCL_FILE_EVENTS then we do
 | 
						||
					 * nothing. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr;
 | 
						||
    FileHandlerEvent *fileEvPtr;
 | 
						||
 | 
						||
    if (!(flags & TCL_FILE_EVENTS)) {
 | 
						||
	return;
 | 
						||
    }
 | 
						||
    for (filePtr = firstFileHandlerPtr; filePtr != NULL;
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr->mask != 0) {
 | 
						||
	    filePtr->readyMask = Tcl_FileReady(filePtr->file, filePtr->mask);
 | 
						||
	    if (filePtr->readyMask != 0) {
 | 
						||
		fileEvPtr = (FileHandlerEvent *) ckalloc(
 | 
						||
			sizeof(FileHandlerEvent));
 | 
						||
		fileEvPtr->header.proc = FileHandlerEventProc;
 | 
						||
		fileEvPtr->file = filePtr->file;
 | 
						||
		Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * FileHandlerEventProc --
 | 
						||
 *
 | 
						||
 *	This procedure is called by Tcl_DoOneEvent when a file event
 | 
						||
 *	reaches the front of the event queue.  This procedure is responsible
 | 
						||
 *	for actually handling the event by invoking the callback for the
 | 
						||
 *	file handler.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	Returns 1 if the event was handled, meaning it should be removed
 | 
						||
 *	from the queue.  Returns 0 if the event was not handled, meaning
 | 
						||
 *	it should stay on the queue.  The only time the event isn't
 | 
						||
 *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Whatever the file handler's callback procedure does
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static int
 | 
						||
FileHandlerEventProc(evPtr, flags)
 | 
						||
    Tcl_Event *evPtr;		/* Event to service. */
 | 
						||
    int flags;			/* Flags that indicate what events to
 | 
						||
				 * handle, such as TCL_FILE_EVENTS. */
 | 
						||
{
 | 
						||
    FileHandler *filePtr;
 | 
						||
    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
 | 
						||
    int mask;
 | 
						||
 | 
						||
    if (!(flags & TCL_FILE_EVENTS)) {
 | 
						||
	return 0;
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Search through the file handlers to find the one whose handle matches
 | 
						||
     * the event.  We do this rather than keeping a pointer to the file
 | 
						||
     * handler directly in the event, so that the handler can be deleted
 | 
						||
     * while the event is queued without leaving a dangling pointer.
 | 
						||
     */
 | 
						||
 | 
						||
    for (filePtr = firstFileHandlerPtr; filePtr != NULL;
 | 
						||
	    filePtr = filePtr->nextPtr) {
 | 
						||
	if (filePtr->file != fileEvPtr->file) {
 | 
						||
	    continue;
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * The code is tricky for two reasons:
 | 
						||
	 * 1. The file handler's desired events could have changed
 | 
						||
	 *    since the time when the event was queued, so AND the
 | 
						||
	 *    ready mask with the desired mask.
 | 
						||
	 * 2. The file could have been closed and re-opened since
 | 
						||
	 *    the time when the event was queued.  This is why the
 | 
						||
	 *    ready mask is stored in the file handler rather than
 | 
						||
	 *    the queued event:  it will be zeroed when a new
 | 
						||
	 *    file handler is created for the newly opened file.
 | 
						||
	 */
 | 
						||
 | 
						||
	mask = filePtr->readyMask & filePtr->mask;
 | 
						||
	filePtr->readyMask = 0;
 | 
						||
	if (mask != 0) {
 | 
						||
	    (*filePtr->proc)(filePtr->clientData, mask);
 | 
						||
	}
 | 
						||
	break;
 | 
						||
    }
 | 
						||
    return 1;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_CreateTimerHandler --
 | 
						||
 *
 | 
						||
 *	Arrange for a given procedure to be invoked at a particular
 | 
						||
 *	time in the future.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	The return value is a token for the timer event, which
 | 
						||
 *	may be used to delete the event before it fires.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	When milliseconds have elapsed, proc will be invoked
 | 
						||
 *	exactly once.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
Tcl_TimerToken
 | 
						||
Tcl_CreateTimerHandler(milliseconds, proc, clientData)
 | 
						||
    int milliseconds;		/* How many milliseconds to wait
 | 
						||
				 * before invoking proc. */
 | 
						||
    Tcl_TimerProc *proc;	/* Procedure to invoke. */
 | 
						||
    ClientData clientData;	/* Arbitrary data to pass to proc. */
 | 
						||
{
 | 
						||
    register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
 | 
						||
    static int id = 0;
 | 
						||
 | 
						||
    if (!timerEventSourceCreated) {
 | 
						||
	timerEventSourceCreated = 1;
 | 
						||
	Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,
 | 
						||
		(ClientData) NULL);
 | 
						||
        Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL);
 | 
						||
    }
 | 
						||
 | 
						||
    timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
 | 
						||
 | 
						||
    /*
 | 
						||
     * Compute when the event should fire.
 | 
						||
     */
 | 
						||
 | 
						||
    TclGetTime(&timerHandlerPtr->time);
 | 
						||
    timerHandlerPtr->time.sec += milliseconds/1000;
 | 
						||
    timerHandlerPtr->time.usec += (milliseconds%1000)*1000;
 | 
						||
    if (timerHandlerPtr->time.usec >= 1000000) {
 | 
						||
	timerHandlerPtr->time.usec -= 1000000;
 | 
						||
	timerHandlerPtr->time.sec += 1;
 | 
						||
    }
 | 
						||
    
 | 
						||
    /*
 | 
						||
     * Fill in other fields for the event.
 | 
						||
     */
 | 
						||
 | 
						||
    timerHandlerPtr->proc = proc;
 | 
						||
    timerHandlerPtr->clientData = clientData;
 | 
						||
    id++;
 | 
						||
    timerHandlerPtr->token = (Tcl_TimerToken) id;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Add the event to the queue in the correct position
 | 
						||
     * (ordered by event firing time).
 | 
						||
     */
 | 
						||
 | 
						||
    for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
 | 
						||
	    prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
 | 
						||
	if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
 | 
						||
		|| ((tPtr2->time.sec == timerHandlerPtr->time.sec)
 | 
						||
		&& (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    timerHandlerPtr->nextPtr = tPtr2;
 | 
						||
    if (prevPtr == NULL) {
 | 
						||
	firstTimerHandlerPtr = timerHandlerPtr;
 | 
						||
    } else {
 | 
						||
	prevPtr->nextPtr = timerHandlerPtr;
 | 
						||
    }
 | 
						||
    return timerHandlerPtr->token;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_DeleteTimerHandler --
 | 
						||
 *
 | 
						||
 *	Delete a previously-registered timer handler.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Destroy the timer callback identified by TimerToken,
 | 
						||
 *	so that its associated procedure will not be called.
 | 
						||
 *	If the callback has already fired, or if the given
 | 
						||
 *	token doesn't exist, then nothing happens.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_DeleteTimerHandler(token)
 | 
						||
    Tcl_TimerToken token;	/* Result previously returned by
 | 
						||
				 * Tcl_DeleteTimerHandler. */
 | 
						||
{
 | 
						||
    register TimerHandler *timerHandlerPtr, *prevPtr;
 | 
						||
 | 
						||
    for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;
 | 
						||
	    timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
 | 
						||
	    timerHandlerPtr = timerHandlerPtr->nextPtr) {
 | 
						||
	if (timerHandlerPtr->token != token) {
 | 
						||
	    continue;
 | 
						||
	}
 | 
						||
	if (prevPtr == NULL) {
 | 
						||
	    firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
 | 
						||
	} else {
 | 
						||
	    prevPtr->nextPtr = timerHandlerPtr->nextPtr;
 | 
						||
	}
 | 
						||
	ckfree((char *) timerHandlerPtr);
 | 
						||
	return;
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_CreateModalTimeout --
 | 
						||
 *
 | 
						||
 *	Arrange for a given procedure to be invoked at a particular
 | 
						||
 *	time in the future, independently of all other timer events.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	When milliseconds have elapsed, proc will be invoked
 | 
						||
 *	exactly once.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_CreateModalTimeout(milliseconds, proc, clientData)
 | 
						||
    int milliseconds;		/* How many milliseconds to wait
 | 
						||
				 * before invoking proc. */
 | 
						||
    Tcl_TimerProc *proc;	/* Procedure to invoke. */
 | 
						||
    ClientData clientData;	/* Arbitrary data to pass to proc. */
 | 
						||
{
 | 
						||
    TimerHandler *timerHandlerPtr;
 | 
						||
 | 
						||
    if (!timerEventSourceCreated) {
 | 
						||
	timerEventSourceCreated = 1;
 | 
						||
	Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,
 | 
						||
		(ClientData) NULL);
 | 
						||
        Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL);
 | 
						||
    }
 | 
						||
 | 
						||
    timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
 | 
						||
 | 
						||
    /*
 | 
						||
     * Compute when the timeout should fire and fill in the other fields
 | 
						||
     * of the handler.
 | 
						||
     */
 | 
						||
 | 
						||
    TclGetTime(&timerHandlerPtr->time);
 | 
						||
    timerHandlerPtr->time.sec += milliseconds/1000;
 | 
						||
    timerHandlerPtr->time.usec += (milliseconds%1000)*1000;
 | 
						||
    if (timerHandlerPtr->time.usec >= 1000000) {
 | 
						||
	timerHandlerPtr->time.usec -= 1000000;
 | 
						||
	timerHandlerPtr->time.sec += 1;
 | 
						||
    }
 | 
						||
    timerHandlerPtr->proc = proc;
 | 
						||
    timerHandlerPtr->clientData = clientData;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Push the handler on the top of the modal stack.
 | 
						||
     */
 | 
						||
 | 
						||
    timerHandlerPtr->nextPtr = firstModalHandlerPtr;
 | 
						||
    firstModalHandlerPtr = timerHandlerPtr;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_DeleteModalTimeout --
 | 
						||
 *
 | 
						||
 *	Remove the topmost modal timer handler from the stack of
 | 
						||
 *	modal  handlers.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Destroys the topmost modal timeout handler, which must
 | 
						||
 *	match proc and clientData.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_DeleteModalTimeout(proc, clientData)
 | 
						||
    Tcl_TimerProc *proc;	/* Callback procedure for the timeout. */
 | 
						||
    ClientData clientData;	/* Arbitrary data to pass to proc. */
 | 
						||
{
 | 
						||
    TimerHandler *timerHandlerPtr;
 | 
						||
 | 
						||
    timerHandlerPtr = firstModalHandlerPtr;
 | 
						||
    firstModalHandlerPtr = timerHandlerPtr->nextPtr;
 | 
						||
    if ((timerHandlerPtr->proc != proc)
 | 
						||
	    || (timerHandlerPtr->clientData != clientData)) {
 | 
						||
	panic("Tcl_DeleteModalTimeout found timeout stack corrupted");
 | 
						||
    }
 | 
						||
    ckfree((char *) timerHandlerPtr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TimerHandlerSetupProc --
 | 
						||
 *
 | 
						||
 *	This procedure is part of the "event source" for timers.
 | 
						||
 *	It is invoked by Tcl_DoOneEvent before it calls select (or
 | 
						||
 *	whatever it uses to wait).
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Tells the notifier how long to sleep if it decides to block.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
TimerHandlerSetupProc(clientData, flags)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
    int flags;				/* Flags passed to Tk_DoOneEvent:
 | 
						||
					 * if it doesn't include
 | 
						||
					 * TCL_TIMER_EVENTS then we only
 | 
						||
					 * consider modal timers. */
 | 
						||
{
 | 
						||
    TimerHandler *timerHandlerPtr, *tPtr2;
 | 
						||
    Tcl_Time blockTime;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Find the timer handler (regular or modal) that fires first.
 | 
						||
     */
 | 
						||
 | 
						||
    timerHandlerPtr = firstTimerHandlerPtr;
 | 
						||
    if (!(flags & TCL_TIMER_EVENTS)) {
 | 
						||
	timerHandlerPtr = NULL;
 | 
						||
    }
 | 
						||
    if (timerHandlerPtr != NULL) {
 | 
						||
	tPtr2 = firstModalHandlerPtr;
 | 
						||
	if (tPtr2 != NULL) {
 | 
						||
	    if ((timerHandlerPtr->time.sec > tPtr2->time.sec)
 | 
						||
		    || ((timerHandlerPtr->time.sec == tPtr2->time.sec)
 | 
						||
		    && (timerHandlerPtr->time.usec > tPtr2->time.usec))) {
 | 
						||
		timerHandlerPtr = tPtr2;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    } else {
 | 
						||
	timerHandlerPtr = firstModalHandlerPtr;
 | 
						||
    }
 | 
						||
    if (timerHandlerPtr == NULL) {
 | 
						||
	return;
 | 
						||
    }
 | 
						||
 | 
						||
    TclGetTime(&blockTime);
 | 
						||
    blockTime.sec = timerHandlerPtr->time.sec - blockTime.sec;
 | 
						||
    blockTime.usec = timerHandlerPtr->time.usec - blockTime.usec;
 | 
						||
    if (blockTime.usec < 0) {
 | 
						||
	blockTime.sec -= 1;
 | 
						||
	blockTime.usec += 1000000;
 | 
						||
    }
 | 
						||
    if (blockTime.sec < 0) {
 | 
						||
	blockTime.sec = 0;
 | 
						||
	blockTime.usec = 0;
 | 
						||
    }
 | 
						||
    Tcl_SetMaxBlockTime(&blockTime);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TimerHandlerCheckProc --
 | 
						||
 *
 | 
						||
 *	This procedure is the second part of the "event source" for
 | 
						||
 *	file handlers.  It is invoked by Tcl_DoOneEvent after it calls
 | 
						||
 *	select (or whatever it uses to wait for events).
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Makes entries on the Tcl event queue for each file that is
 | 
						||
 *	now ready.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
TimerHandlerCheckProc(clientData, flags)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
    int flags;				/* Flags passed to Tk_DoOneEvent:
 | 
						||
					 * if it doesn't include 
 | 
						||
					 * TCL_TIMER_EVENTS then we only
 | 
						||
					 * consider modal timeouts. */
 | 
						||
{
 | 
						||
    TimerHandler *timerHandlerPtr;
 | 
						||
    TimerEvent *timerEvPtr;
 | 
						||
    int triggered, gotTime;
 | 
						||
    Tcl_Time curTime;
 | 
						||
 | 
						||
    triggered = 0;
 | 
						||
    gotTime = 0;
 | 
						||
    timerHandlerPtr = firstTimerHandlerPtr;
 | 
						||
    if ((flags & TCL_TIMER_EVENTS) && (timerHandlerPtr != NULL)) {
 | 
						||
	TclGetTime(&curTime);
 | 
						||
	gotTime = 1;
 | 
						||
	if ((timerHandlerPtr->time.sec < curTime.sec)
 | 
						||
		|| ((timerHandlerPtr->time.sec == curTime.sec)
 | 
						||
		&& (timerHandlerPtr->time.usec <= curTime.usec))) {
 | 
						||
	    triggered = 1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    timerHandlerPtr = firstModalHandlerPtr;
 | 
						||
    if (timerHandlerPtr != NULL) {
 | 
						||
	if (!gotTime) {
 | 
						||
	    TclGetTime(&curTime);
 | 
						||
	}
 | 
						||
	if ((timerHandlerPtr->time.sec < curTime.sec)
 | 
						||
		|| ((timerHandlerPtr->time.sec == curTime.sec)
 | 
						||
		&& (timerHandlerPtr->time.usec <= curTime.usec))) {
 | 
						||
	    triggered = 1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    if (triggered) {
 | 
						||
	timerEvPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent));
 | 
						||
	timerEvPtr->header.proc = TimerHandlerEventProc;
 | 
						||
	timerEvPtr->time.sec = curTime.sec;
 | 
						||
	timerEvPtr->time.usec = curTime.usec;
 | 
						||
	Tcl_QueueEvent((Tcl_Event *) timerEvPtr, TCL_QUEUE_TAIL);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TimerHandlerExitProc --
 | 
						||
 *
 | 
						||
 *	Callback invoked during exit cleanup to destroy the timer event
 | 
						||
 *	source.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Destroys the timer event source.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
static void
 | 
						||
TimerHandlerExitProc(clientData)
 | 
						||
    ClientData clientData;		/* Not used. */
 | 
						||
{
 | 
						||
    Tcl_DeleteEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,
 | 
						||
            (ClientData) NULL);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TimerHandlerEventProc --
 | 
						||
 *
 | 
						||
 *	This procedure is called by Tcl_DoOneEvent when a timer event
 | 
						||
 *	reaches the front of the event queue.  This procedure handles
 | 
						||
 *	the event by invoking the callbacks for all timers that are
 | 
						||
 *	ready.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	Returns 1 if the event was handled, meaning it should be removed
 | 
						||
 *	from the queue.  Returns 0 if the event was not handled, meaning
 | 
						||
 *	it should stay on the queue.  The only time the event isn't
 | 
						||
 *	handled is if the TCL_TIMER_EVENTS flag bit isn't set.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Whatever the timer handler callback procedures do.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static int
 | 
						||
TimerHandlerEventProc(evPtr, flags)
 | 
						||
    Tcl_Event *evPtr;		/* Event to service. */
 | 
						||
    int flags;			/* Flags that indicate what events to
 | 
						||
				 * handle, such as TCL_FILE_EVENTS. */
 | 
						||
{
 | 
						||
    TimerHandler *timerHandlerPtr;
 | 
						||
    TimerEvent *timerEvPtr = (TimerEvent *) evPtr;
 | 
						||
 | 
						||
    /*
 | 
						||
     * Invoke the current modal timeout first, if there is one and
 | 
						||
     * it has triggered.
 | 
						||
     */
 | 
						||
 | 
						||
    timerHandlerPtr = firstModalHandlerPtr;
 | 
						||
    if (firstModalHandlerPtr != NULL) {
 | 
						||
	if ((timerHandlerPtr->time.sec < timerEvPtr->time.sec)
 | 
						||
		|| ((timerHandlerPtr->time.sec == timerEvPtr->time.sec)
 | 
						||
		&& (timerHandlerPtr->time.usec <= timerEvPtr->time.usec))) {
 | 
						||
	    (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Invoke any normal timers that have fired.
 | 
						||
     */
 | 
						||
 | 
						||
    if (!(flags & TCL_TIMER_EVENTS)) {
 | 
						||
	return 1;
 | 
						||
    }
 | 
						||
 | 
						||
    while (1) {
 | 
						||
	timerHandlerPtr = firstTimerHandlerPtr;
 | 
						||
	if (timerHandlerPtr == NULL) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
	if ((timerHandlerPtr->time.sec > timerEvPtr->time.sec)
 | 
						||
		|| ((timerHandlerPtr->time.sec == timerEvPtr->time.sec)
 | 
						||
		&& (timerHandlerPtr->time.usec >= timerEvPtr->time.usec))) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Remove the handler from the queue before invoking it,
 | 
						||
	 * to avoid potential reentrancy problems.
 | 
						||
	 */
 | 
						||
 | 
						||
	firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
 | 
						||
	(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
 | 
						||
	ckfree((char *) timerHandlerPtr);
 | 
						||
    }
 | 
						||
    return 1;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_DoWhenIdle --
 | 
						||
 *
 | 
						||
 *	Arrange for proc to be invoked the next time the system is
 | 
						||
 *	idle (i.e., just before the next time that Tcl_DoOneEvent
 | 
						||
 *	would have to wait for something to happen).
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Proc will eventually be called, with clientData as argument.
 | 
						||
 *	See the manual entry for details.
 | 
						||
 *
 | 
						||
 *--------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_DoWhenIdle(proc, clientData)
 | 
						||
    Tcl_IdleProc *proc;		/* Procedure to invoke. */
 | 
						||
    ClientData clientData;	/* Arbitrary value to pass to proc. */
 | 
						||
{
 | 
						||
    register IdleHandler *idlePtr;
 | 
						||
 | 
						||
    idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
 | 
						||
    idlePtr->proc = proc;
 | 
						||
    idlePtr->clientData = clientData;
 | 
						||
    idlePtr->generation = idleGeneration;
 | 
						||
    idlePtr->nextPtr = NULL;
 | 
						||
    if (lastIdlePtr == NULL) {
 | 
						||
	idleList = idlePtr;
 | 
						||
    } else {
 | 
						||
	lastIdlePtr->nextPtr = idlePtr;
 | 
						||
    }
 | 
						||
    lastIdlePtr = idlePtr;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_CancelIdleCall --
 | 
						||
 *
 | 
						||
 *	If there are any when-idle calls requested to a given procedure
 | 
						||
 *	with given clientData, cancel all of them.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	If the proc/clientData combination were on the when-idle list,
 | 
						||
 *	they are removed so that they will never be called.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_CancelIdleCall(proc, clientData)
 | 
						||
    Tcl_IdleProc *proc;		/* Procedure that was previously registered. */
 | 
						||
    ClientData clientData;	/* Arbitrary value to pass to proc. */
 | 
						||
{
 | 
						||
    register IdleHandler *idlePtr, *prevPtr;
 | 
						||
    IdleHandler *nextPtr;
 | 
						||
 | 
						||
    for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
 | 
						||
	    prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
 | 
						||
	while ((idlePtr->proc == proc)
 | 
						||
		&& (idlePtr->clientData == clientData)) {
 | 
						||
	    nextPtr = idlePtr->nextPtr;
 | 
						||
	    ckfree((char *) idlePtr);
 | 
						||
	    idlePtr = nextPtr;
 | 
						||
	    if (prevPtr == NULL) {
 | 
						||
		idleList = idlePtr;
 | 
						||
	    } else {
 | 
						||
		prevPtr->nextPtr = idlePtr;
 | 
						||
	    }
 | 
						||
	    if (idlePtr == NULL) {
 | 
						||
		lastIdlePtr = prevPtr;
 | 
						||
		return;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TclIdlePending --
 | 
						||
 *
 | 
						||
 *	This function is called by the notifier subsystem to determine
 | 
						||
 *	whether there are any idle handlers currently scheduled.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	Returns 0 if the idle list is empty, otherwise it returns 1.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
int
 | 
						||
TclIdlePending()
 | 
						||
{
 | 
						||
    return (idleList == NULL) ? 0 : 1;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TclServiceIdle --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked by the notifier when it becomes idle.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	The return value is 1 if the procedure actually found an idle
 | 
						||
 *	handler to invoke.  If no handler was found then 0 is returned.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Invokes all pending idle handlers.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
int
 | 
						||
TclServiceIdle()
 | 
						||
{
 | 
						||
    IdleHandler *idlePtr;
 | 
						||
    int oldGeneration;
 | 
						||
    int foundIdle;
 | 
						||
 | 
						||
    if (idleList == NULL) {
 | 
						||
	return 0;
 | 
						||
    }
 | 
						||
    
 | 
						||
    foundIdle = 0;
 | 
						||
    oldGeneration = idleGeneration;
 | 
						||
    idleGeneration++;
 | 
						||
 | 
						||
    /*
 | 
						||
     * The code below is trickier than it may look, for the following
 | 
						||
     * reasons:
 | 
						||
     *
 | 
						||
     * 1. New handlers can get added to the list while the current
 | 
						||
     *    one is being processed.  If new ones get added, we don't
 | 
						||
     *    want to process them during this pass through the list (want
 | 
						||
     *    to check for other work to do first).  This is implemented
 | 
						||
     *    using the generation number in the handler:  new handlers
 | 
						||
     *    will have a different generation than any of the ones currently
 | 
						||
     *    on the list.
 | 
						||
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
 | 
						||
     *    the handler from the list before calling it. Otherwise an
 | 
						||
     *    infinite loop could result.
 | 
						||
     * 3. Tcl_CancelIdleCall can be called to remove an element from
 | 
						||
     *    the list while a handler is executing, so the list could
 | 
						||
     *    change structure during the call.
 | 
						||
     */
 | 
						||
 | 
						||
    for (idlePtr = idleList;
 | 
						||
	    ((idlePtr != NULL)
 | 
						||
		    && ((oldGeneration - idlePtr->generation) >= 0));
 | 
						||
	    idlePtr = idleList) {
 | 
						||
	idleList = idlePtr->nextPtr;
 | 
						||
	if (idleList == NULL) {
 | 
						||
	    lastIdlePtr = NULL;
 | 
						||
	}
 | 
						||
	foundIdle = 1;
 | 
						||
	(*idlePtr->proc)(idlePtr->clientData);
 | 
						||
	ckfree((char *) idlePtr);
 | 
						||
    }
 | 
						||
 | 
						||
    return foundIdle;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_BackgroundError --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked to handle errors that occur in Tcl
 | 
						||
 *	commands that are invoked in "background" (e.g. from event or
 | 
						||
 *	timer bindings).
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	The command "bgerror" is invoked later as an idle handler to
 | 
						||
 *	process the error, passing it the error message.  If that fails,
 | 
						||
 *	then an error message is output on stderr.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_BackgroundError(interp)
 | 
						||
    Tcl_Interp *interp;		/* Interpreter in which an error has
 | 
						||
				 * occurred. */
 | 
						||
{
 | 
						||
    BgError *errPtr;
 | 
						||
    char *varValue;
 | 
						||
    ErrAssocData *assocPtr;
 | 
						||
 | 
						||
    /*
 | 
						||
     * The Tcl_AddErrorInfo call below (with an empty string) ensures that
 | 
						||
     * errorInfo gets properly set.  It's needed in cases where the error
 | 
						||
     * came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;
 | 
						||
     * in these cases errorInfo still won't have been set when this
 | 
						||
     * procedure is called.
 | 
						||
     */
 | 
						||
 | 
						||
    Tcl_AddErrorInfo(interp, "");
 | 
						||
    errPtr = (BgError *) ckalloc(sizeof(BgError));
 | 
						||
    errPtr->interp = interp;
 | 
						||
    errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result)
 | 
						||
	    + 1));
 | 
						||
    strcpy(errPtr->errorMsg, interp->result);
 | 
						||
#ifdef STk_CODE
 | 
						||
    varValue = Tcl_GetVar(interp, "*error-info*", TCL_GLOBAL_ONLY);
 | 
						||
#else
 | 
						||
    varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
 | 
						||
#endif
 | 
						||
    if (varValue == NULL) {
 | 
						||
	varValue = errPtr->errorMsg;
 | 
						||
    }
 | 
						||
    errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
 | 
						||
    strcpy(errPtr->errorInfo, varValue);
 | 
						||
#ifdef STk_CODE
 | 
						||
    varValue = Tcl_GetVar(interp, "*error-code*", TCL_GLOBAL_ONLY);
 | 
						||
#else
 | 
						||
    varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
 | 
						||
#endif
 | 
						||
    if (varValue == NULL) {
 | 
						||
	varValue = "";
 | 
						||
    }
 | 
						||
    errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
 | 
						||
    strcpy(errPtr->errorCode, varValue);
 | 
						||
    errPtr->nextPtr = NULL;
 | 
						||
 | 
						||
    assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError",
 | 
						||
	    (Tcl_InterpDeleteProc **) NULL);
 | 
						||
    if (assocPtr == NULL) {
 | 
						||
 | 
						||
	/*
 | 
						||
	 * This is the first time a background error has occurred in
 | 
						||
	 * this interpreter.  Create associated data to keep track of
 | 
						||
	 * pending error reports.
 | 
						||
	 */
 | 
						||
 | 
						||
	assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData));
 | 
						||
	assocPtr->firstBgPtr = NULL;
 | 
						||
	assocPtr->lastBgPtr = NULL;
 | 
						||
	Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc,
 | 
						||
		(ClientData) assocPtr);
 | 
						||
    }
 | 
						||
    if (assocPtr->firstBgPtr == NULL) {
 | 
						||
	assocPtr->firstBgPtr = errPtr;
 | 
						||
	Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr);
 | 
						||
    } else {
 | 
						||
	assocPtr->lastBgPtr->nextPtr = errPtr;
 | 
						||
    }
 | 
						||
    assocPtr->lastBgPtr = errPtr;
 | 
						||
    Tcl_ResetResult(interp);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * HandleBgErrors --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked as an idle handler to process all of
 | 
						||
 *	the accumulated background errors.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Depends on what actions "bgerror" takes for the errors.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
HandleBgErrors(clientData)
 | 
						||
    ClientData clientData;	/* Pointer to ErrAssocData structure. */
 | 
						||
{
 | 
						||
    Tcl_Interp *interp;
 | 
						||
    char *command;
 | 
						||
    char *argv[2];
 | 
						||
    int code;
 | 
						||
    BgError *errPtr;
 | 
						||
    ErrAssocData *assocPtr = (ErrAssocData *) clientData;
 | 
						||
    Tcl_Channel errChannel;
 | 
						||
 | 
						||
    while (assocPtr->firstBgPtr != NULL) {
 | 
						||
	interp = assocPtr->firstBgPtr->interp;
 | 
						||
	if (interp == NULL) {
 | 
						||
	    goto doneWithReport;
 | 
						||
	}
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Restore important state variables to what they were at
 | 
						||
	 * the time the error occurred.
 | 
						||
	 */
 | 
						||
 | 
						||
#ifdef STk_CODE
 | 
						||
	Tcl_SetVar(interp, "*error-info*", assocPtr->firstBgPtr->errorInfo,
 | 
						||
		STk_STRINGIFY | TCL_GLOBAL_ONLY);
 | 
						||
	Tcl_SetVar(interp, "*error-code*", assocPtr->firstBgPtr->errorCode,
 | 
						||
		STk_STRINGIFY | TCL_GLOBAL_ONLY);
 | 
						||
#else
 | 
						||
	Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo,
 | 
						||
		TCL_GLOBAL_ONLY);
 | 
						||
	Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode,
 | 
						||
		TCL_GLOBAL_ONLY);
 | 
						||
#endif
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Create and invoke the bgerror command.
 | 
						||
	 */
 | 
						||
 | 
						||
	argv[0] = "bgerror";
 | 
						||
#ifdef STk_CODE
 | 
						||
	assocPtr->firstBgPtr->errorMsg= 
 | 
						||
	  		(char *)STk_stringify(assocPtr->firstBgPtr->errorMsg, 1);
 | 
						||
#endif
 | 
						||
	argv[1] = assocPtr->firstBgPtr->errorMsg;
 | 
						||
	command = Tcl_Merge(2, argv);
 | 
						||
	Tcl_AllowExceptions(interp);
 | 
						||
        Tcl_Preserve((ClientData) interp);
 | 
						||
	code = Tcl_GlobalEval(interp, command);
 | 
						||
	ckfree(command);
 | 
						||
	if (code == TCL_ERROR) {
 | 
						||
 | 
						||
            /*
 | 
						||
             * We have to get the error output channel at the latest possible
 | 
						||
             * time, because the eval (above) might have changed the channel.
 | 
						||
             */
 | 
						||
            
 | 
						||
            errChannel = Tcl_GetStdChannel(TCL_STDERR);
 | 
						||
            if (errChannel != (Tcl_Channel) NULL) {
 | 
						||
                if (strcmp(interp->result,
 | 
						||
           "\"bgerror\" is an invalid command name or ambiguous abbreviation")
 | 
						||
                        == 0) {
 | 
						||
                    Tcl_Write(errChannel, assocPtr->firstBgPtr->errorInfo, -1);
 | 
						||
                    Tcl_Write(errChannel, "\n", -1);
 | 
						||
                } else {
 | 
						||
                    Tcl_Write(errChannel,
 | 
						||
                            "bgerror failed to handle background error.\n",
 | 
						||
                            -1);
 | 
						||
                    Tcl_Write(errChannel, "    Original error: ", -1);
 | 
						||
                    Tcl_Write(errChannel, assocPtr->firstBgPtr->errorMsg,
 | 
						||
                            -1);
 | 
						||
                    Tcl_Write(errChannel, "\n", -1);
 | 
						||
                    Tcl_Write(errChannel, "    Error in bgerror: ", -1);
 | 
						||
                    Tcl_Write(errChannel, interp->result, -1);
 | 
						||
                    Tcl_Write(errChannel, "\n", -1);
 | 
						||
                }
 | 
						||
                Tcl_Flush(errChannel);
 | 
						||
            }
 | 
						||
	} else if (code == TCL_BREAK) {
 | 
						||
 | 
						||
	    /*
 | 
						||
	     * Break means cancel any remaining error reports for this
 | 
						||
	     * interpreter.
 | 
						||
	     */
 | 
						||
 | 
						||
	    for (errPtr = assocPtr->firstBgPtr; errPtr != NULL;
 | 
						||
		    errPtr = errPtr->nextPtr) {
 | 
						||
		if (errPtr->interp == interp) {
 | 
						||
		    errPtr->interp = NULL;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	}
 | 
						||
 | 
						||
        Tcl_Release((ClientData) interp);
 | 
						||
 | 
						||
	/*
 | 
						||
	 * Discard the command and the information about the error report.
 | 
						||
	 */
 | 
						||
 | 
						||
	doneWithReport:
 | 
						||
	ckfree(assocPtr->firstBgPtr->errorMsg);
 | 
						||
	ckfree(assocPtr->firstBgPtr->errorInfo);
 | 
						||
	ckfree(assocPtr->firstBgPtr->errorCode);
 | 
						||
	errPtr = assocPtr->firstBgPtr->nextPtr;
 | 
						||
	ckfree((char *) assocPtr->firstBgPtr);
 | 
						||
	assocPtr->firstBgPtr = errPtr;
 | 
						||
    }
 | 
						||
    assocPtr->lastBgPtr = NULL;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * BgErrorDeleteProc --
 | 
						||
 *
 | 
						||
 *	This procedure is associated with the "tclBgError" assoc data
 | 
						||
 *	for an interpreter;  it is invoked when the interpreter is
 | 
						||
 *	deleted in order to free the information assoicated with any
 | 
						||
 *	pending error reports.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Background error information is freed: if there were any
 | 
						||
 *	pending error reports, they are cancelled.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
BgErrorDeleteProc(clientData, interp)
 | 
						||
    ClientData clientData;	/* Pointer to ErrAssocData structure. */
 | 
						||
    Tcl_Interp *interp;		/* Interpreter being deleted. */
 | 
						||
{
 | 
						||
    ErrAssocData *assocPtr = (ErrAssocData *) clientData;
 | 
						||
    BgError *errPtr;
 | 
						||
 | 
						||
    while (assocPtr->firstBgPtr != NULL) {
 | 
						||
	errPtr = assocPtr->firstBgPtr;
 | 
						||
	assocPtr->firstBgPtr = errPtr->nextPtr;
 | 
						||
	ckfree(errPtr->errorMsg);
 | 
						||
	ckfree(errPtr->errorInfo);
 | 
						||
	ckfree(errPtr->errorCode);
 | 
						||
	ckfree((char *) errPtr);
 | 
						||
    }
 | 
						||
    ckfree((char *) assocPtr);
 | 
						||
    Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_CreateExitHandler --
 | 
						||
 *
 | 
						||
 *	Arrange for a given procedure to be invoked just before the
 | 
						||
 *	application exits.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Proc will be invoked with clientData as argument when the
 | 
						||
 *	application exits.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_CreateExitHandler(proc, clientData)
 | 
						||
    Tcl_ExitProc *proc;		/* Procedure to invoke. */
 | 
						||
    ClientData clientData;	/* Arbitrary value to pass to proc. */
 | 
						||
{
 | 
						||
    ExitHandler *exitPtr;
 | 
						||
 | 
						||
    exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler));
 | 
						||
    exitPtr->proc = proc;
 | 
						||
    exitPtr->clientData = clientData;
 | 
						||
    exitPtr->nextPtr = firstExitPtr;
 | 
						||
    firstExitPtr = exitPtr;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_DeleteExitHandler --
 | 
						||
 *
 | 
						||
 *	This procedure cancels an existing exit handler matching proc
 | 
						||
 *	and clientData, if such a handler exits.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	If there is an exit handler corresponding to proc and clientData
 | 
						||
 *	then it is cancelled;  if no such handler exists then nothing
 | 
						||
 *	happens.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_DeleteExitHandler(proc, clientData)
 | 
						||
    Tcl_ExitProc *proc;		/* Procedure that was previously registered. */
 | 
						||
    ClientData clientData;	/* Arbitrary value to pass to proc. */
 | 
						||
{
 | 
						||
    ExitHandler *exitPtr, *prevPtr;
 | 
						||
 | 
						||
    for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL;
 | 
						||
	    prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
 | 
						||
	if ((exitPtr->proc == proc)
 | 
						||
		&& (exitPtr->clientData == clientData)) {
 | 
						||
	    if (prevPtr == NULL) {
 | 
						||
		firstExitPtr = exitPtr->nextPtr;
 | 
						||
	    } else {
 | 
						||
		prevPtr->nextPtr = exitPtr->nextPtr;
 | 
						||
	    }
 | 
						||
	    ckfree((char *) exitPtr);
 | 
						||
	    return;
 | 
						||
	}
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_Exit --
 | 
						||
 *
 | 
						||
 *	This procedure is called to terminate the application.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	All existing exit handlers are invoked, then the application
 | 
						||
 *	ends.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
void
 | 
						||
Tcl_Exit(status)
 | 
						||
    int status;			/* Exit status for application;  typically
 | 
						||
				 * 0 for normal return, 1 for error return. */
 | 
						||
{
 | 
						||
    ExitHandler *exitPtr;
 | 
						||
 | 
						||
    for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) {
 | 
						||
	/*
 | 
						||
	 * Be careful to remove the handler from the list before invoking
 | 
						||
	 * its callback.  This protects us against double-freeing if the
 | 
						||
	 * callback should call Tcl_DeleteExitHandler on itself.
 | 
						||
	 */
 | 
						||
 | 
						||
	firstExitPtr = exitPtr->nextPtr;
 | 
						||
	(*exitPtr->proc)(exitPtr->clientData);
 | 
						||
	ckfree((char *) exitPtr);
 | 
						||
    }
 | 
						||
#ifdef TCL_MEM_DEBUG
 | 
						||
    if (tclMemDumpFileName != NULL) {
 | 
						||
	Tcl_DumpActiveMemory(tclMemDumpFileName);
 | 
						||
    }
 | 
						||
#endif
 | 
						||
    
 | 
						||
    TclPlatformExit(status);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_AfterCmd --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked to process the "after" Tcl command.
 | 
						||
 *	See the user documentation for details on what it does.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	A standard Tcl result.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	See the user documentation.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
int
 | 
						||
Tcl_AfterCmd(clientData, interp, argc, argv)
 | 
						||
    ClientData clientData;	/* Points to the "tclAfter" assocData for
 | 
						||
				 * this interpreter, or NULL if the assocData
 | 
						||
				 * hasn't been created yet.*/
 | 
						||
    Tcl_Interp *interp;		/* Current interpreter. */
 | 
						||
    int argc;			/* Number of arguments. */
 | 
						||
    char **argv;		/* Argument strings. */
 | 
						||
{
 | 
						||
    /*
 | 
						||
     * The variable below is used to generate unique identifiers for
 | 
						||
     * after commands.  This id can wrap around, which can potentially
 | 
						||
     * cause problems.  However, there are not likely to be problems
 | 
						||
     * in practice, because after commands can only be requested to
 | 
						||
     * about a month in the future, and wrap-around is unlikely to
 | 
						||
     * occur in less than about 1-10 years.  Thus it's unlikely that
 | 
						||
     * any old ids will still be around when wrap-around occurs.
 | 
						||
     */
 | 
						||
 | 
						||
    static int nextId = 1;
 | 
						||
    int ms;
 | 
						||
    AfterInfo *afterPtr;
 | 
						||
#ifdef STk_CODE
 | 
						||
    static int initialized = 0;
 | 
						||
    void *closure;
 | 
						||
    AfterAssocData *assocPtr = &After_list;
 | 
						||
#else
 | 
						||
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
 | 
						||
#endif
 | 
						||
    Tcl_CmdInfo cmdInfo;
 | 
						||
    size_t length;
 | 
						||
 | 
						||
 | 
						||
    if (argc < 2) {
 | 
						||
	Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		argv[0], " option ?arg arg ...?\"", (char *) NULL);
 | 
						||
	return TCL_ERROR;
 | 
						||
    }
 | 
						||
 | 
						||
#ifdef STk_CODE
 | 
						||
    if (!initialized) {
 | 
						||
      After_list.interp = interp; 	/* really useless !!!! */ 
 | 
						||
      After_list.firstAfterPtr = NULL; 
 | 
						||
      initialized = 1;
 | 
						||
    }
 | 
						||
#else
 | 
						||
    /*
 | 
						||
     * Create the "after" information associated for this interpreter,
 | 
						||
     * if it doesn't already exist.  Associate it with the command too,
 | 
						||
     * so that it will be passed in as the ClientData argument in the
 | 
						||
     * future.
 | 
						||
     */
 | 
						||
 | 
						||
    if (assocPtr == NULL) {
 | 
						||
	assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
 | 
						||
	assocPtr->interp = interp;
 | 
						||
	assocPtr->firstAfterPtr = NULL;
 | 
						||
	Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
 | 
						||
		(ClientData) assocPtr);
 | 
						||
	cmdInfo.proc = Tcl_AfterCmd;
 | 
						||
	cmdInfo.clientData = (ClientData) assocPtr;
 | 
						||
	cmdInfo.deleteProc = NULL;
 | 
						||
	cmdInfo.deleteData = (ClientData) assocPtr;
 | 
						||
	Tcl_SetCommandInfo(interp, argv[0], &cmdInfo);
 | 
						||
    }
 | 
						||
#endif
 | 
						||
 | 
						||
    /*
 | 
						||
     * Parse the command.
 | 
						||
     */
 | 
						||
 | 
						||
    length = strlen(argv[1]);
 | 
						||
    if (isdigit(UCHAR(argv[1][0]))) {
 | 
						||
	if (Tcl_GetInt(interp, argv[1], &ms) != TCL_OK) {
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
	if (ms < 0) {
 | 
						||
	    ms = 0;
 | 
						||
	}
 | 
						||
	if (argc == 2) {
 | 
						||
	    Tcl_Sleep(ms);
 | 
						||
	    return TCL_OK;
 | 
						||
	}
 | 
						||
	afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
 | 
						||
	afterPtr->assocPtr = assocPtr;
 | 
						||
	if (argc == 3) {
 | 
						||
#ifdef STk_CODE
 | 
						||
	    if (!STk_valid_callback(argv[2], &closure)) {
 | 
						||
	        Tcl_AppendResult(interp, "bad closure specification \"",
 | 
						||
				         argv[2], "\"", (char *) NULL);
 | 
						||
		return TCL_ERROR;
 | 
						||
	    }
 | 
						||
#endif
 | 
						||
	    afterPtr->command = (char *) ckalloc((unsigned)
 | 
						||
		    (strlen(argv[2]) + 1));
 | 
						||
	    strcpy(afterPtr->command, argv[2]);
 | 
						||
	} else {
 | 
						||
#ifdef STk_CODE
 | 
						||
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		    argv[0], " ms [script]\"", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
#else
 | 
						||
	    afterPtr->command = Tcl_Concat(argc-2, argv+2);
 | 
						||
#endif
 | 
						||
	}
 | 
						||
	afterPtr->id = nextId;
 | 
						||
	nextId += 1;
 | 
						||
	afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
 | 
						||
		(ClientData) afterPtr);
 | 
						||
	afterPtr->nextPtr = assocPtr->firstAfterPtr;
 | 
						||
	assocPtr->firstAfterPtr = afterPtr;
 | 
						||
	sprintf(interp->result, "after#%d", afterPtr->id);
 | 
						||
#ifdef STk_CODE
 | 
						||
	if (closure != NULL)
 | 
						||
	  /* Register the callback to prinevent it to be GC'ed */
 | 
						||
	  STk_add_callback(interp->result, "", "", closure);
 | 
						||
#endif
 | 
						||
    } else if (strncmp(argv[1], "cancel", length) == 0) {
 | 
						||
	char *arg;
 | 
						||
 | 
						||
	if (argc < 3) {
 | 
						||
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		    argv[0], " cancel id|command\"", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
	if (argc == 3) {
 | 
						||
	    arg = argv[2];
 | 
						||
#ifndef STk_CODE
 | 
						||
	} else {
 | 
						||
	    arg = Tcl_Concat(argc-2, argv+2);
 | 
						||
#endif
 | 
						||
	}
 | 
						||
	for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
 | 
						||
		afterPtr = afterPtr->nextPtr) {
 | 
						||
	    if (strcmp(afterPtr->command, arg) == 0) {
 | 
						||
		break;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
	if (afterPtr == NULL) {
 | 
						||
	    afterPtr = GetAfterEvent(assocPtr, arg);
 | 
						||
	}
 | 
						||
#ifndef STk_CODE
 | 
						||
	if (arg != argv[2]) {
 | 
						||
	    ckfree(arg);
 | 
						||
	}
 | 
						||
#endif
 | 
						||
	if (afterPtr != NULL) {
 | 
						||
	    if (afterPtr->token != NULL) {
 | 
						||
		Tcl_DeleteTimerHandler(afterPtr->token);
 | 
						||
	    } else {
 | 
						||
		Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
 | 
						||
	    }
 | 
						||
	    FreeAfterPtr(afterPtr);
 | 
						||
	}
 | 
						||
    } else if ((strncmp(argv[1], "idle", length) == 0)
 | 
						||
	     && (length >= 2)) {
 | 
						||
#ifdef STk_CODE
 | 
						||
	if (argc != 3) {
 | 
						||
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		    argv[0], " idle script\"", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
	if (!STk_valid_callback(argv[2], &closure)) {
 | 
						||
	  Tcl_AppendResult(interp, "bad closure specification \"",
 | 
						||
			   argv[2], "\"", (char *) NULL);
 | 
						||
	  return TCL_ERROR;
 | 
						||
	}
 | 
						||
#else
 | 
						||
	if (argc < 3) {
 | 
						||
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		    argv[0], " idle script script ...\"", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
#endif
 | 
						||
	afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
 | 
						||
	afterPtr->assocPtr = assocPtr;
 | 
						||
#ifdef STk_CODE
 | 
						||
	afterPtr->command = (char *) ckalloc((unsigned) (strlen(argv[2]) + 1));
 | 
						||
	strcpy(afterPtr->command, argv[2]);
 | 
						||
#else
 | 
						||
	if (argc == 3) {
 | 
						||
	    afterPtr->command = (char *) ckalloc((unsigned)
 | 
						||
		    (strlen(argv[2]) + 1));
 | 
						||
	    strcpy(afterPtr->command, argv[2]);
 | 
						||
	} else {
 | 
						||
	    afterPtr->command = Tcl_Concat(argc-2, argv+2);
 | 
						||
	}
 | 
						||
#endif
 | 
						||
	afterPtr->id = nextId;
 | 
						||
	nextId += 1;
 | 
						||
	afterPtr->token = NULL;
 | 
						||
	afterPtr->nextPtr = assocPtr->firstAfterPtr;
 | 
						||
	assocPtr->firstAfterPtr = afterPtr;
 | 
						||
	Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
 | 
						||
	sprintf(interp->result, "after#%d", afterPtr->id);
 | 
						||
#ifdef STk_CODE
 | 
						||
	if (closure != NULL)
 | 
						||
	  /* Register the callback to prevent it to be GC'ed */
 | 
						||
	  STk_add_callback(interp->result, "", "", closure);
 | 
						||
#endif
 | 
						||
    } else if ((strncmp(argv[1], "info", length) == 0)
 | 
						||
	     && (length >= 2)) {
 | 
						||
	if (argc == 2) {
 | 
						||
	    char buffer[30];
 | 
						||
 | 
						||
#ifdef STk_CODE
 | 
						||
	    Tcl_AppendResult(interp, "(", NULL);
 | 
						||
#endif
 | 
						||
	    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
 | 
						||
		    afterPtr = afterPtr->nextPtr) {
 | 
						||
		if (assocPtr->interp == interp) {
 | 
						||
		    sprintf(buffer, "after#%d", afterPtr->id);
 | 
						||
		    Tcl_AppendElement(interp, buffer);
 | 
						||
		}
 | 
						||
	    }
 | 
						||
#ifdef STk_CODE
 | 
						||
	    Tcl_AppendResult(interp, ")", NULL);
 | 
						||
#endif
 | 
						||
	    return TCL_OK;
 | 
						||
	}
 | 
						||
	if (argc != 3) {
 | 
						||
	    Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		    argv[0], " info ?id?\"", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
	afterPtr = GetAfterEvent(assocPtr, argv[2]);
 | 
						||
	if (afterPtr == NULL) {
 | 
						||
	    Tcl_AppendResult(interp, "event \"", argv[2],
 | 
						||
		    "\" doesn't exist", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
	Tcl_AppendElement(interp, afterPtr->command);
 | 
						||
	Tcl_AppendElement(interp,
 | 
						||
		(afterPtr->token == NULL) ? "idle" : "timer");
 | 
						||
    } else {
 | 
						||
	Tcl_AppendResult(interp, "bad argument \"", argv[1],
 | 
						||
		"\": must be cancel, idle, info, or a number",
 | 
						||
		(char *) NULL);
 | 
						||
	return TCL_ERROR;
 | 
						||
    }
 | 
						||
    return TCL_OK;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * GetAfterEvent --
 | 
						||
 *
 | 
						||
 *	This procedure parses an "after" id such as "after#4" and
 | 
						||
 *	returns a pointer to the AfterInfo structure.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	The return value is either a pointer to an AfterInfo structure,
 | 
						||
 *	if one is found that corresponds to "string" and is for interp,
 | 
						||
 *	or NULL if no corresponding after event can be found.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static AfterInfo *
 | 
						||
GetAfterEvent(assocPtr, string)
 | 
						||
    AfterAssocData *assocPtr;	/* Points to "after"-related information for
 | 
						||
				 * this interpreter. */
 | 
						||
    char *string;		/* Textual identifier for after event, such
 | 
						||
				 * as "after#6". */
 | 
						||
{
 | 
						||
    AfterInfo *afterPtr;
 | 
						||
    int id;
 | 
						||
    char *end;
 | 
						||
 | 
						||
    if (strncmp(string, "after#", 6) != 0) {
 | 
						||
	return NULL;
 | 
						||
    }
 | 
						||
    string += 6;
 | 
						||
    id = strtoul(string, &end, 10);
 | 
						||
    if ((end == string) || (*end != 0)) {
 | 
						||
	return NULL;
 | 
						||
    }
 | 
						||
    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
 | 
						||
	    afterPtr = afterPtr->nextPtr) {
 | 
						||
	if (afterPtr->id == id) {
 | 
						||
	    return afterPtr;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    return NULL;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * AfterProc --
 | 
						||
 *
 | 
						||
 *	Timer callback to execute commands registered with the
 | 
						||
 *	"after" command.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Executes whatever command was specified.  If the command
 | 
						||
 *	returns an error, then the command "bgerror" is invoked
 | 
						||
 *	to process the error;  if bgerror fails then information
 | 
						||
 *	about the error is output on stderr.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
AfterProc(clientData)
 | 
						||
    ClientData clientData;	/* Describes command to execute. */
 | 
						||
{
 | 
						||
    AfterInfo *afterPtr = (AfterInfo *) clientData;
 | 
						||
    AfterAssocData *assocPtr = afterPtr->assocPtr;
 | 
						||
    AfterInfo *prevPtr;
 | 
						||
    int result;
 | 
						||
    Tcl_Interp *interp;
 | 
						||
 | 
						||
    /*
 | 
						||
     * First remove the callback from our list of callbacks;  otherwise
 | 
						||
     * someone could delete the callback while it's being executed, which
 | 
						||
     * could cause a core dump.
 | 
						||
     */
 | 
						||
 | 
						||
    if (assocPtr->firstAfterPtr == afterPtr) {
 | 
						||
	assocPtr->firstAfterPtr = afterPtr->nextPtr;
 | 
						||
    } else {
 | 
						||
	for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
 | 
						||
		prevPtr = prevPtr->nextPtr) {
 | 
						||
	    /* Empty loop body. */
 | 
						||
	}
 | 
						||
	prevPtr->nextPtr = afterPtr->nextPtr;
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Execute the callback.
 | 
						||
     */
 | 
						||
 | 
						||
    interp = assocPtr->interp;
 | 
						||
    Tcl_Preserve((ClientData) interp);
 | 
						||
    result = Tcl_GlobalEval(interp, afterPtr->command);
 | 
						||
    if (result != TCL_OK) {
 | 
						||
	Tcl_AddErrorInfo(interp, "\n    (\"after\" script)");
 | 
						||
	Tcl_BackgroundError(interp);
 | 
						||
    }
 | 
						||
    Tcl_Release((ClientData) interp);
 | 
						||
    
 | 
						||
    /*
 | 
						||
     * Free the memory for the callback.
 | 
						||
     */
 | 
						||
 | 
						||
    ckfree(afterPtr->command);
 | 
						||
    ckfree((char *) afterPtr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * FreeAfterPtr --
 | 
						||
 *
 | 
						||
 *	This procedure removes an "after" command from the list of
 | 
						||
 *	those that are pending and frees its resources.  This procedure
 | 
						||
 *	does *not* cancel the timer handler;  if that's needed, the
 | 
						||
 *	caller must do it.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	The memory associated with afterPtr is released.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
FreeAfterPtr(afterPtr)
 | 
						||
    AfterInfo *afterPtr;		/* Command to be deleted. */
 | 
						||
{
 | 
						||
    AfterInfo *prevPtr;
 | 
						||
    AfterAssocData *assocPtr = afterPtr->assocPtr;
 | 
						||
 | 
						||
    if (assocPtr->firstAfterPtr == afterPtr) {
 | 
						||
	assocPtr->firstAfterPtr = afterPtr->nextPtr;
 | 
						||
    } else {
 | 
						||
	for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
 | 
						||
		prevPtr = prevPtr->nextPtr) {
 | 
						||
	    /* Empty loop body. */
 | 
						||
	}
 | 
						||
	prevPtr->nextPtr = afterPtr->nextPtr;
 | 
						||
    }
 | 
						||
    ckfree(afterPtr->command);
 | 
						||
    ckfree((char *) afterPtr);
 | 
						||
}
 | 
						||
 | 
						||
#ifndef STk_CODE
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * AfterCleanupProc --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked whenever an interpreter is deleted
 | 
						||
 *	to cleanup the AssocData for "tclAfter".
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	None.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	After commands are removed.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
static void
 | 
						||
AfterCleanupProc(clientData, interp)
 | 
						||
    ClientData clientData;	/* Points to AfterAssocData for the
 | 
						||
				 * interpreter. */
 | 
						||
    Tcl_Interp *interp;		/* Interpreter that is being deleted. */
 | 
						||
{
 | 
						||
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
 | 
						||
    AfterInfo *afterPtr;
 | 
						||
 | 
						||
    while (assocPtr->firstAfterPtr != NULL) {
 | 
						||
	afterPtr = assocPtr->firstAfterPtr;
 | 
						||
	assocPtr->firstAfterPtr = afterPtr->nextPtr;
 | 
						||
	if (afterPtr->token != NULL) {
 | 
						||
	    Tcl_DeleteTimerHandler(afterPtr->token);
 | 
						||
	} else {
 | 
						||
	    Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
 | 
						||
	}
 | 
						||
	ckfree(afterPtr->command);
 | 
						||
	ckfree((char *) afterPtr);
 | 
						||
    }
 | 
						||
    ckfree((char *) assocPtr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_VwaitCmd --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked to process the "vwait" Tcl command.
 | 
						||
 *	See the user documentation for details on what it does.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	A standard Tcl result.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	See the user documentation.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
int
 | 
						||
Tcl_VwaitCmd(clientData, interp, argc, argv)
 | 
						||
    ClientData clientData;	/* Not used. */
 | 
						||
    Tcl_Interp *interp;		/* Current interpreter. */
 | 
						||
    int argc;			/* Number of arguments. */
 | 
						||
    char **argv;		/* Argument strings. */
 | 
						||
{
 | 
						||
    int done, foundEvent;
 | 
						||
 | 
						||
    if (argc != 2) {
 | 
						||
	Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		argv[0], " name\"", (char *) NULL);
 | 
						||
	return TCL_ERROR;
 | 
						||
    }
 | 
						||
    Tcl_TraceVar(interp, argv[1],
 | 
						||
	    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 | 
						||
	    VwaitVarProc, (ClientData) &done);
 | 
						||
    done = 0;
 | 
						||
    foundEvent = 1;
 | 
						||
    while (!done && foundEvent) {
 | 
						||
	foundEvent = Tcl_DoOneEvent(0);
 | 
						||
    }
 | 
						||
    Tcl_UntraceVar(interp, argv[1],
 | 
						||
	    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 | 
						||
	    VwaitVarProc, (ClientData) &done);
 | 
						||
 | 
						||
    /*
 | 
						||
     * Clear out the interpreter's result, since it may have been set
 | 
						||
     * by event handlers.
 | 
						||
     */
 | 
						||
 | 
						||
    Tcl_ResetResult(interp);
 | 
						||
    if (!foundEvent) {
 | 
						||
	Tcl_AppendResult(interp, "can't wait for variable \"", argv[1],
 | 
						||
		"\":  would wait forever", (char *) NULL);
 | 
						||
	return TCL_ERROR;
 | 
						||
    }
 | 
						||
    return TCL_OK;
 | 
						||
}
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
static char *
 | 
						||
VwaitVarProc(clientData, interp, name1, name2, flags)
 | 
						||
    ClientData clientData;	/* Pointer to integer to set to 1. */
 | 
						||
    Tcl_Interp *interp;		/* Interpreter containing variable. */
 | 
						||
    char *name1;		/* Name of variable. */
 | 
						||
    char *name2;		/* Second part of variable name. */
 | 
						||
    int flags;			/* Information about what happened. */
 | 
						||
{
 | 
						||
    int *donePtr = (int *) clientData;
 | 
						||
 | 
						||
    *donePtr = 1;
 | 
						||
    return (char *) NULL;
 | 
						||
}
 | 
						||
#endif
 | 
						||
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * Tcl_UpdateCmd --
 | 
						||
 *
 | 
						||
 *	This procedure is invoked to process the "update" Tcl command.
 | 
						||
 *	See the user documentation for details on what it does.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	A standard Tcl result.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	See the user documentation.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
	/* ARGSUSED */
 | 
						||
int
 | 
						||
Tcl_UpdateCmd(clientData, interp, argc, argv)
 | 
						||
    ClientData clientData;	/* Not used. */
 | 
						||
    Tcl_Interp *interp;		/* Current interpreter. */
 | 
						||
    int argc;			/* Number of arguments. */
 | 
						||
    char **argv;		/* Argument strings. */
 | 
						||
{
 | 
						||
    int flags = 0;		/* Initialization needed only to stop
 | 
						||
				 * compiler warnings. */
 | 
						||
 | 
						||
    if (argc == 1) {
 | 
						||
	flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;
 | 
						||
    } else if (argc == 2) {
 | 
						||
	if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {
 | 
						||
	    Tcl_AppendResult(interp, "bad option \"", argv[1],
 | 
						||
		    "\": must be idletasks", (char *) NULL);
 | 
						||
	    return TCL_ERROR;
 | 
						||
	}
 | 
						||
	flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
 | 
						||
    } else {
 | 
						||
	Tcl_AppendResult(interp, "wrong # args: should be \"",
 | 
						||
		argv[0], " ?idletasks?\"", (char *) NULL);
 | 
						||
	return TCL_ERROR;
 | 
						||
    }
 | 
						||
 | 
						||
    while (Tcl_DoOneEvent(flags) != 0) {
 | 
						||
	/* Empty loop body */
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Must clear the interpreter's result because event handlers could
 | 
						||
     * have executed commands.
 | 
						||
     */
 | 
						||
 | 
						||
    Tcl_ResetResult(interp);
 | 
						||
    return TCL_OK;
 | 
						||
}
 | 
						||
 | 
						||
#ifndef STk_CODE
 | 
						||
/*
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 *
 | 
						||
 * TclWaitForFile --
 | 
						||
 *
 | 
						||
 *	This procedure waits synchronously for a file to become readable
 | 
						||
 *	or writable, with an optional timeout.
 | 
						||
 *
 | 
						||
 * Results:
 | 
						||
 *	The return value is an OR'ed combination of TCL_READABLE,
 | 
						||
 *	TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
 | 
						||
 *	that are present on file at the time of the return.  This
 | 
						||
 *	procedure will not return until either "timeout" milliseconds
 | 
						||
 *	have elapsed or at least one of the conditions given by mask
 | 
						||
 *	has occurred for file (a return value of 0 means that a timeout
 | 
						||
 *	occurred).  No normal events will be serviced during the
 | 
						||
 *	execution of this procedure.
 | 
						||
 *
 | 
						||
 * Side effects:
 | 
						||
 *	Time passes.
 | 
						||
 *
 | 
						||
 *----------------------------------------------------------------------
 | 
						||
 */
 | 
						||
 | 
						||
int
 | 
						||
TclWaitForFile(file, mask, timeout)
 | 
						||
    Tcl_File file;		/* Handle for file on which to wait. */
 | 
						||
    int mask;			/* What to wait for: OR'ed combination of
 | 
						||
				 * TCL_READABLE, TCL_WRITABLE, and
 | 
						||
				 * TCL_EXCEPTION. */
 | 
						||
    int timeout;		/* Maximum amount of time to wait for one
 | 
						||
				 * of the conditions in mask to occur, in
 | 
						||
				 * milliseconds.  A value of 0 means don't
 | 
						||
				 * wait at all, and a value of -1 means
 | 
						||
				 * wait forever. */
 | 
						||
{
 | 
						||
    Tcl_Time abortTime, now, blockTime;
 | 
						||
    int present;
 | 
						||
 | 
						||
    /*
 | 
						||
     * If there is a non-zero finite timeout, compute the time when
 | 
						||
     * we give up.
 | 
						||
     */
 | 
						||
 | 
						||
    if (timeout > 0) {
 | 
						||
	TclGetTime(&now);
 | 
						||
	abortTime.sec = now.sec + timeout/1000;
 | 
						||
	abortTime.usec = now.usec + (timeout%1000)*1000;
 | 
						||
	if (abortTime.usec >= 1000000) {
 | 
						||
	    abortTime.usec -= 1000000;
 | 
						||
	    abortTime.sec += 1;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
    /*
 | 
						||
     * Loop in a mini-event loop of our own, waiting for either the
 | 
						||
     * file to become ready or a timeout to occur.
 | 
						||
     */
 | 
						||
 | 
						||
    while (1) {
 | 
						||
	Tcl_WatchFile(file, mask);
 | 
						||
	if (timeout > 0) {
 | 
						||
	    blockTime.sec = abortTime.sec - now.sec;
 | 
						||
	    blockTime.usec = abortTime.usec - now.usec;
 | 
						||
	    if (blockTime.usec < 0) {
 | 
						||
		blockTime.sec -= 1;
 | 
						||
		blockTime.usec += 1000000;
 | 
						||
	    }
 | 
						||
	    if (blockTime.sec < 0) {
 | 
						||
		blockTime.sec = 0;
 | 
						||
		blockTime.usec = 0;
 | 
						||
	    }
 | 
						||
	    Tcl_WaitForEvent(&blockTime);
 | 
						||
	} else if (timeout == 0) {
 | 
						||
	    blockTime.sec = 0;
 | 
						||
	    blockTime.usec = 0;
 | 
						||
	    Tcl_WaitForEvent(&blockTime);
 | 
						||
	} else {
 | 
						||
	    Tcl_WaitForEvent((Tcl_Time *) NULL);
 | 
						||
	}
 | 
						||
	present = Tcl_FileReady(file, mask);
 | 
						||
	if (present != 0) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
	if (timeout == 0) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
	TclGetTime(&now);
 | 
						||
	if ((abortTime.sec < now.sec)
 | 
						||
		|| ((abortTime.sec == now.sec)
 | 
						||
		&& (abortTime.usec <= now.usec))) {
 | 
						||
	    break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
    return present;
 | 
						||
}
 | 
						||
#endif
 |