summaryrefslogtreecommitdiff
path: root/contrib/tcl/generic/tclEvent.c
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>1997-07-25 19:27:55 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>1997-07-25 19:27:55 +0000
commit3d33409926539d866dcea9fc5cb14113b312adf0 (patch)
treed2f88b3e9ffa79ffb2cc1a0699dd3ee96c47c3e5 /contrib/tcl/generic/tclEvent.c
parent8569730d6bc2e4cb5e784997313325b13518e066 (diff)
Notes
Diffstat (limited to 'contrib/tcl/generic/tclEvent.c')
-rw-r--r--contrib/tcl/generic/tclEvent.c1731
1 files changed, 118 insertions, 1613 deletions
diff --git a/contrib/tcl/generic/tclEvent.c b/contrib/tcl/generic/tclEvent.c
index 7a081c714bc9..a503df7f841b 100644
--- a/contrib/tcl/generic/tclEvent.c
+++ b/contrib/tcl/generic/tclEvent.c
@@ -1,177 +1,23 @@
/*
* 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".
+ * This file implements some general event related interfaces including
+ * background errors, exit handlers, and the "vwait" and "update"
+ * command procedures.
*
* Copyright (c) 1990-1994 The Regents of the University of California.
- * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1994-1997 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.128 96/07/23 16:12:34
+ * SCCS: @(#) tclEvent.c 1.152 97/05/21 07:06:19
*/
#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;
-
-/*
* 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
@@ -225,25 +71,6 @@ static ExitHandler *firstExitPtr = NULL;
* 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
@@ -253,969 +80,26 @@ typedef struct FileWait {
char *tclMemDumpFileName = NULL;
/*
+ * This variable is set to 1 when Tcl_Exit is called, and at the end of
+ * its work, it is reset to 0. The variable is checked by TclInExit() to
+ * allow different behavior for exit-time processing, e.g. in closing of
+ * files and pipes.
+ */
+
+static int tclInExit = 0;
+
+/*
* 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.
- */
-
- TclpGetTime(&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.
- */
-
- TclpGetTime(&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;
- }
-
- TclpGetTime(&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)) {
- TclpGetTime(&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) {
- TclpGetTime(&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 --
@@ -1241,7 +125,7 @@ Tcl_BackgroundError(interp)
* occurred. */
{
BgError *errPtr;
- char *varValue;
+ char *errResult, *varValue;
ErrAssocData *assocPtr;
/*
@@ -1253,11 +137,13 @@ Tcl_BackgroundError(interp)
*/
Tcl_AddErrorInfo(interp, "");
+
+ errResult = TclGetStringFromObj(Tcl_GetObjResult(interp), (int *) NULL);
+
errPtr = (BgError *) ckalloc(sizeof(BgError));
errPtr->interp = interp;
- errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result)
- + 1));
- strcpy(errPtr->errorMsg, interp->result);
+ errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(errResult) + 1));
+ strcpy(errPtr->errorMsg, errResult);
varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
if (varValue == NULL) {
varValue = errPtr->errorMsg;
@@ -1327,10 +213,12 @@ HandleBgErrors(clientData)
ErrAssocData *assocPtr = (ErrAssocData *) clientData;
Tcl_Channel errChannel;
+ Tcl_Preserve((ClientData) assocPtr);
+
while (assocPtr->firstBgPtr != NULL) {
interp = assocPtr->firstBgPtr->interp;
if (interp == NULL) {
- goto doneWithReport;
+ goto doneWithInterp;
}
/*
@@ -1357,6 +245,45 @@ HandleBgErrors(clientData)
if (code == TCL_ERROR) {
/*
+ * If the interpreter is safe, we look for a hidden command
+ * named "bgerror" and call that with the error information.
+ * Otherwise, simply ignore the error. The rationale is that
+ * this could be an error caused by a malicious applet trying
+ * to cause an infinite barrage of error messages. The hidden
+ * "bgerror" command can be used by a security policy to
+ * interpose on such attacks and e.g. kill the applet after a
+ * few attempts.
+ */
+
+ if (Tcl_IsSafe(interp)) {
+ Tcl_HashTable *hTblPtr;
+ Tcl_HashEntry *hPtr;
+
+ hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
+ "tclHiddenCmds", NULL);
+ if (hTblPtr == (Tcl_HashTable *) NULL) {
+ goto doneWithInterp;
+ }
+ hPtr = Tcl_FindHashEntry(hTblPtr, "bgerror");
+ if (hPtr == (Tcl_HashEntry *) NULL) {
+ goto doneWithInterp;
+ }
+
+ /*
+ * OK, the hidden command "bgerror" exists, invoke it.
+ */
+
+ argv[0] = "bgerror";
+ argv[1] = ckalloc((unsigned)
+ strlen(assocPtr->firstBgPtr->errorMsg));
+ strcpy(argv[1], assocPtr->firstBgPtr->errorMsg);
+ (void) TclInvoke(interp, 2, argv, TCL_INVOKE_HIDDEN);
+ ckfree(argv[1]);
+
+ goto doneWithInterp;
+ }
+
+ /*
* We have to get the error output channel at the latest possible
* time, because the eval (above) might have changed the channel.
*/
@@ -1397,21 +324,28 @@ HandleBgErrors(clientData)
}
}
- 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;
+doneWithInterp:
+
+ if (assocPtr->firstBgPtr) {
+ ckfree(assocPtr->firstBgPtr->errorMsg);
+ ckfree(assocPtr->firstBgPtr->errorInfo);
+ ckfree(assocPtr->firstBgPtr->errorCode);
+ errPtr = assocPtr->firstBgPtr->nextPtr;
+ ckfree((char *) assocPtr->firstBgPtr);
+ assocPtr->firstBgPtr = errPtr;
+ }
+
+ if (interp != NULL) {
+ Tcl_Release((ClientData) interp);
+ }
}
assocPtr->lastBgPtr = NULL;
+
+ Tcl_Release((ClientData) assocPtr);
}
/*
@@ -1450,8 +384,8 @@ BgErrorDeleteProc(clientData, interp)
ckfree(errPtr->errorCode);
ckfree((char *) errPtr);
}
- ckfree((char *) assocPtr);
Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr);
+ Tcl_EventuallyFree((ClientData) assocPtr, TCL_DYNAMIC);
}
/*
@@ -1549,415 +483,82 @@ 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);
- }
+ Tcl_Finalize();
#ifdef TCL_MEM_DEBUG
if (tclMemDumpFileName != NULL) {
Tcl_DumpActiveMemory(tclMemDumpFileName);
}
#endif
-
TclPlatformExit(status);
}
/*
*----------------------------------------------------------------------
*
- * Tcl_AfterCmd --
+ * Tcl_Finalize --
*
- * 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;
- AfterAssocData *assocPtr = (AfterAssocData *) clientData;
- 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;
- }
-
- /*
- * 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);
- }
-
- /*
- * 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) {
- afterPtr->command = (char *) ckalloc((unsigned)
- (strlen(argv[2]) + 1));
- strcpy(afterPtr->command, argv[2]);
- } else {
- afterPtr->command = Tcl_Concat(argc-2, argv+2);
- }
- 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);
- } 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];
- } else {
- arg = Tcl_Concat(argc-2, argv+2);
- }
- for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
- afterPtr = afterPtr->nextPtr) {
- if (strcmp(afterPtr->command, arg) == 0) {
- break;
- }
- }
- if (afterPtr == NULL) {
- afterPtr = GetAfterEvent(assocPtr, arg);
- }
- if (arg != argv[2]) {
- ckfree(arg);
- }
- 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)) {
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " idle script script ...\"", (char *) NULL);
- return TCL_ERROR;
- }
- afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
- afterPtr->assocPtr = assocPtr;
- 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);
- }
- 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);
- } else if ((strncmp(argv[1], "info", length) == 0)
- && (length >= 2)) {
- if (argc == 2) {
- char buffer[30];
-
- for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
- afterPtr = afterPtr->nextPtr) {
- if (assocPtr->interp == interp) {
- sprintf(buffer, "after#%d", afterPtr->id);
- Tcl_AppendElement(interp, buffer);
- }
- }
- 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.
+ * Runs the exit handlers to allow Tcl to clean up its state prior
+ * to being unloaded. Called by Tcl_Exit and when Tcl was dynamically
+ * loaded and is now being unloaded.
*
* 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.
+ * Whatever the exit handlers do. Also frees up storage associated
+ * with the Tcl object type table.
*
*----------------------------------------------------------------------
*/
-static void
-AfterProc(clientData)
- ClientData clientData; /* Describes command to execute. */
+void
+Tcl_Finalize()
{
- 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.
- */
+ ExitHandler *exitPtr;
+
+ tclInExit = 1;
+ 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.
+ */
- 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;
+ firstExitPtr = exitPtr->nextPtr;
+ (*exitPtr->proc)(exitPtr->clientData);
+ ckfree((char *) exitPtr);
}
/*
- * Execute the callback.
+ * Uninitialize everything associated with the compile and execute
+ * environment. This *must* be done at the latest possible time.
*/
-
- 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);
+ TclFinalizeCompExecEnv();
+ firstExitPtr = NULL;
+ tclInExit = 0;
}
/*
*----------------------------------------------------------------------
*
- * FreeAfterPtr --
+ * TclInExit --
*
- * 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.
+ * Determines if we are in the middle of exit-time cleanup.
*
* Results:
- * None.
+ * If we are in the middle of exiting, 1, otherwise 0.
*
* 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);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * 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. */
+int
+TclInExit()
{
- 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);
+ return tclInExit;
}
/*
@@ -1992,13 +593,15 @@ Tcl_VwaitCmd(clientData, interp, argc, argv)
argv[0], " name\"", (char *) NULL);
return TCL_ERROR;
}
- Tcl_TraceVar(interp, argv[1],
+ if (Tcl_TraceVar(interp, argv[1],
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- VwaitVarProc, (ClientData) &done);
+ VwaitVarProc, (ClientData) &done) != TCL_OK) {
+ return TCL_ERROR;
+ };
done = 0;
foundEvent = 1;
while (!done && foundEvent) {
- foundEvent = Tcl_DoOneEvent(0);
+ foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
}
Tcl_UntraceVar(interp, argv[1],
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
@@ -2058,8 +661,7 @@ Tcl_UpdateCmd(clientData, interp, argc, argv)
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
- int flags = 0; /* Initialization needed only to stop
- * compiler warnings. */
+ int flags;
if (argc == 1) {
flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;
@@ -2069,7 +671,7 @@ Tcl_UpdateCmd(clientData, interp, argc, argv)
"\": must be idletasks", (char *) NULL);
return TCL_ERROR;
}
- flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
+ flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT;
} else {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " ?idletasks?\"", (char *) NULL);
@@ -2088,100 +690,3 @@ Tcl_UpdateCmd(clientData, interp, argc, argv)
Tcl_ResetResult(interp);
return TCL_OK;
}
-
-/*
- *----------------------------------------------------------------------
- *
- * 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) {
- TclpGetTime(&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;
- }
- TclpGetTime(&now);
- if ((abortTime.sec < now.sec)
- || ((abortTime.sec == now.sec)
- && (abortTime.usec <= now.usec))) {
- break;
- }
- }
- return present;
-}