/*1* tclEvent.c --2*3* This file provides basic event-managing facilities for Tcl,4* including an event queue, and mechanisms for attaching5* callbacks to certain events.6*7* It also contains the command procedures for the commands8* "after", "vwait", and "update".9*10* Copyright (c) 1990-1994 The Regents of the University of California.11* Copyright (c) 1994-1995 Sun Microsystems, Inc.12*13* See the file "license.terms" for information on usage and redistribution14* of this file, and for a DISCLAIMER OF ALL WARRANTIES.15*16* SCCS: @(#) tclEvent.c 1.132 96/11/12 11:52:2717*/1819#include "tclInt.h"20#include "tclPort.h"2122/*23* For each file registered in a call to Tcl_CreateFileHandler,24* there is one record of the following type. All of these records25* are chained together into a single list.26*/2728typedef struct FileHandler {29Tcl_File file; /* Generic file handle for file. */30int mask; /* Mask of desired events: TCL_READABLE, etc. */31int readyMask; /* Events that were ready the last time that32* FileHandlerCheckProc checked this file. */33Tcl_FileProc *proc; /* Procedure to call, in the style of34* Tcl_CreateFileHandler. This is NULL35* if the handler was created by36* Tcl_CreateFileHandler2. */37ClientData clientData; /* Argument to pass to proc. */38struct FileHandler *nextPtr;/* Next in list of all files we care39* about (NULL for end of list). */40} FileHandler;4142static FileHandler *firstFileHandlerPtr = (FileHandler *) NULL;43/* List of all file handlers. */44static int fileEventSourceCreated = 0;45/* Zero means that the file event source46* hasn't been registerd with the Tcl47* notifier yet. */4849/*50* The following structure is what is added to the Tcl event queue when51* file handlers are ready to fire.52*/5354typedef struct FileHandlerEvent {55Tcl_Event header; /* Information that is standard for56* all events. */57Tcl_File file; /* File descriptor that is ready. Used58* to find the FileHandler structure for59* the file (can't point directly to the60* FileHandler structure because it could61* go away while the event is queued). */62} FileHandlerEvent;6364/*65* For each timer callback that's pending (either regular or "modal"),66* there is one record of the following type. The normal handlers67* (created by Tcl_CreateTimerHandler) are chained together in a68* list sorted by time (earliest event first).69*/7071typedef struct TimerHandler {72Tcl_Time time; /* When timer is to fire. */73Tcl_TimerProc *proc; /* Procedure to call. */74ClientData clientData; /* Argument to pass to proc. */75Tcl_TimerToken token; /* Identifies event so it can be76* deleted. Not used in modal77* timeouts. */78struct TimerHandler *nextPtr; /* Next event in queue, or NULL for79* end of queue. */80} TimerHandler;8182static TimerHandler *firstTimerHandlerPtr = NULL;83/* First event in queue. */84static int timerEventSourceCreated = 0; /* 0 means that the timer event source85* hasn't yet been registered with the86* Tcl notifier. */8788/*89* The information below describes a stack of modal timeouts managed by90* Tcl_CreateModalTimer and Tcl_DeleteModalTimer. Only the first element91* in the list is used at any given time.92*/9394static TimerHandler *firstModalHandlerPtr = NULL;9596/*97* The following structure is what's added to the Tcl event queue when98* timer handlers are ready to fire.99*/100101typedef struct TimerEvent {102Tcl_Event header; /* Information that is standard for103* all events. */104Tcl_Time time; /* All timer events that specify this105* time or earlier are ready106* to fire. */107} TimerEvent;108109/*110* There is one of the following structures for each of the111* handlers declared in a call to Tcl_DoWhenIdle. All of the112* currently-active handlers are linked together into a list.113*/114115typedef struct IdleHandler {116Tcl_IdleProc (*proc); /* Procedure to call. */117ClientData clientData; /* Value to pass to proc. */118int generation; /* Used to distinguish older handlers from119* recently-created ones. */120struct IdleHandler *nextPtr;/* Next in list of active handlers. */121} IdleHandler;122123static IdleHandler *idleList = NULL;124/* First in list of all idle handlers. */125static IdleHandler *lastIdlePtr = NULL;126/* Last in list (or NULL for empty list). */127static int idleGeneration = 0; /* Used to fill in the "generation" fields128* of IdleHandler structures. Increments129* each time Tcl_DoOneEvent starts calling130* idle handlers, so that all old handlers131* can be called without calling any of the132* new ones created by old ones. */133134/*135* The data structure below is used by the "after" command to remember136* the command to be executed later. All of the pending "after" commands137* for an interpreter are linked together in a list.138*/139140typedef struct AfterInfo {141struct AfterAssocData *assocPtr;142/* Pointer to the "tclAfter" assocData for143* the interp in which command will be144* executed. */145char *command; /* Command to execute. Malloc'ed, so must146* be freed when structure is deallocated. */147int id; /* Integer identifier for command; used to148* cancel it. */149Tcl_TimerToken token; /* Used to cancel the "after" command. NULL150* means that the command is run as an151* idle handler rather than as a timer152* handler. NULL means this is an "after153* idle" handler rather than a154* timer handler. */155struct AfterInfo *nextPtr; /* Next in list of all "after" commands for156* this interpreter. */157int interpType;158} AfterInfo;159160/*161* One of the following structures is associated with each interpreter162* for which an "after" command has ever been invoked. A pointer to163* this structure is stored in the AssocData for the "tclAfter" key.164*/165166typedef struct AfterAssocData {167Tcl_Interp *interp; /* The interpreter for which this data is168* registered. */169AfterInfo *firstAfterPtr; /* First in list of all "after" commands170* still pending for this interpreter, or171* NULL if none. */172} AfterAssocData;173174/*175* The data structure below is used to report background errors. One176* such structure is allocated for each error; it holds information177* about the interpreter and the error until bgerror can be invoked178* later as an idle handler.179*/180181typedef struct BgError {182Tcl_Interp *interp; /* Interpreter in which error occurred. NULL183* means this error report has been cancelled184* (a previous report generated a break). */185char *errorMsg; /* The error message (interp->result when186* the error occurred). Malloc-ed. */187char *errorInfo; /* Value of the errorInfo variable188* (malloc-ed). */189char *errorCode; /* Value of the errorCode variable190* (malloc-ed). */191struct BgError *nextPtr; /* Next in list of all pending error192* reports for this interpreter, or NULL193* for end of list. */194} BgError;195196/*197* One of the structures below is associated with the "tclBgError"198* assoc data for each interpreter. It keeps track of the head and199* tail of the list of pending background errors for the interpreter.200*/201202typedef struct ErrAssocData {203BgError *firstBgPtr; /* First in list of all background errors204* waiting to be processed for this205* interpreter (NULL if none). */206BgError *lastBgPtr; /* Last in list of all background errors207* waiting to be processed for this208* interpreter (NULL if none). */209} ErrAssocData;210211/*212* For each exit handler created with a call to Tcl_CreateExitHandler213* there is a structure of the following type:214*/215216typedef struct ExitHandler {217Tcl_ExitProc *proc; /* Procedure to call when process exits. */218ClientData clientData; /* One word of information to pass to proc. */219struct ExitHandler *nextPtr;/* Next in list of all exit handlers for220* this application, or NULL for end of list. */221} ExitHandler;222223static ExitHandler *firstExitPtr = NULL;224/* First in list of all exit handlers for225* application. */226227/*228* Structures of the following type are used during the execution229* of Tcl_WaitForFile, to keep track of the file and timeout.230*/231232typedef struct FileWait {233Tcl_File file; /* File to wait on. */234int mask; /* Conditions to wait for (TCL_READABLE,235* etc.) */236int timeout; /* Original "timeout" argument to237* Tcl_WaitForFile. */238Tcl_Time abortTime; /* Time at which to abort the wait. */239int present; /* Conditions present on the file during240* the last time through the event loop. */241int done; /* Non-zero means we're done: either one of242* the desired conditions is present or the243* timeout period has elapsed. */244} FileWait;245246/*247* The following variable is a "secret" indication to Tcl_Exit that248* it should dump out the state of memory before exiting. If the249* value is non-NULL, it gives the name of the file in which to250* dump memory usage information.251*/252253char *tclMemDumpFileName = NULL;254255/*256* This variable is set to 1 when Tcl_Exit is called, and at the end of257* its work, it is reset to 0. The variable is checked by TclInExit() to258* allow different behavior for exit-time processing, e.g. in closing of259* files and pipes.260*/261262static int tclInExit = 0;263264/*265* Prototypes for procedures referenced only in this file:266*/267268static void AfterCleanupProc _ANSI_ARGS_((ClientData clientData,269Tcl_Interp *interp));270static void AfterProc _ANSI_ARGS_((ClientData clientData));271static void BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData,272Tcl_Interp *interp));273static void FileHandlerCheckProc _ANSI_ARGS_((274ClientData clientData, int flags));275static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,276int flags));277static void FileHandlerExitProc _ANSI_ARGS_((ClientData data));278static void FileHandlerSetupProc _ANSI_ARGS_((279ClientData clientData, int flags));280static void FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));281static AfterInfo * GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,282char *string));283static void HandleBgErrors _ANSI_ARGS_((ClientData clientData));284static void TimerHandlerCheckProc _ANSI_ARGS_((285ClientData clientData, int flags));286static int TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,287int flags));288static void TimerHandlerExitProc _ANSI_ARGS_((ClientData data));289static void TimerHandlerSetupProc _ANSI_ARGS_((290ClientData clientData, int flags));291static char * VwaitVarProc _ANSI_ARGS_((ClientData clientData,292Tcl_Interp *interp, char *name1, char *name2,293int flags));294295/*296*--------------------------------------------------------------297*298* Tcl_CreateFileHandler --299*300* Arrange for a given procedure to be invoked whenever301* a given file becomes readable or writable.302*303* Results:304* None.305*306* Side effects:307* From now on, whenever the I/O channel given by file becomes308* ready in the way indicated by mask, proc will be invoked.309* See the manual entry for details on the calling sequence310* to proc. If file is already registered then the old mask311* and proc and clientData values will be replaced with312* new ones.313*314*--------------------------------------------------------------315*/316317void318Tcl_CreateFileHandler(file, mask, proc, clientData)319Tcl_File file; /* Handle of stream to watch. */320int mask; /* OR'ed combination of TCL_READABLE,321* TCL_WRITABLE, and TCL_EXCEPTION:322* indicates conditions under which323* proc should be called. */324Tcl_FileProc *proc; /* Procedure to call for each325* selected event. */326ClientData clientData; /* Arbitrary data to pass to proc. */327{328register FileHandler *filePtr;329330if (!fileEventSourceCreated) {331fileEventSourceCreated = 1;332Tcl_CreateEventSource(FileHandlerSetupProc, FileHandlerCheckProc,333(ClientData) NULL);334Tcl_CreateExitHandler(FileHandlerExitProc, (ClientData) NULL);335}336337/*338* Make sure the file isn't already registered. Create a339* new record in the normal case where there's no existing340* record.341*/342343for (filePtr = firstFileHandlerPtr; filePtr != NULL;344filePtr = filePtr->nextPtr) {345if (filePtr->file == file) {346break;347}348}349if (filePtr == NULL) {350filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));351filePtr->file = file;352filePtr->nextPtr = firstFileHandlerPtr;353firstFileHandlerPtr = filePtr;354}355356/*357* The remainder of the initialization below is done regardless358* of whether or not this is a new record or a modification of359* an old one.360*/361362filePtr->mask = mask;363filePtr->readyMask = 0;364filePtr->proc = proc;365filePtr->clientData = clientData;366}367368/*369*--------------------------------------------------------------370*371* Tcl_DeleteFileHandler --372*373* Cancel a previously-arranged callback arrangement for374* a file.375*376* Results:377* None.378*379* Side effects:380* If a callback was previously registered on file, remove it.381*382*--------------------------------------------------------------383*/384385void386Tcl_DeleteFileHandler(file)387Tcl_File file; /* Stream id for which to remove388* callback procedure. */389{390FileHandler *filePtr, *prevPtr;391392/*393* Find the entry for the given file (and return if there394* isn't one).395*/396397for (prevPtr = NULL, filePtr = firstFileHandlerPtr; ;398prevPtr = filePtr, filePtr = filePtr->nextPtr) {399if (filePtr == NULL) {400return;401}402if (filePtr->file == file) {403break;404}405}406407/*408* Clean up information in the callback record.409*/410411if (prevPtr == NULL) {412firstFileHandlerPtr = filePtr->nextPtr;413} else {414prevPtr->nextPtr = filePtr->nextPtr;415}416ckfree((char *) filePtr);417}418419/*420*----------------------------------------------------------------------421*422* FileHandlerExitProc --423*424* Cleanup procedure to delete the file event source during exit425* cleanup.426*427* Results:428* None.429*430* Side effects:431* Destroys the file event source.432*433*----------------------------------------------------------------------434*/435436/* ARGSUSED */437static void438FileHandlerExitProc(clientData)439ClientData clientData; /* Not used. */440{441Tcl_DeleteEventSource(FileHandlerSetupProc, FileHandlerCheckProc,442(ClientData) NULL);443}444445/*446*----------------------------------------------------------------------447*448* FileHandlerSetupProc --449*450* This procedure is part of the "event source" for file handlers.451* It is invoked by Tcl_DoOneEvent before it calls select (or452* whatever it uses to wait).453*454* Results:455* None.456*457* Side effects:458* Tells the notifier which files should be waited for.459*460*----------------------------------------------------------------------461*/462463static void464FileHandlerSetupProc(clientData, flags)465ClientData clientData; /* Not used. */466int flags; /* Flags passed to Tk_DoOneEvent:467* if it doesn't include468* TCL_FILE_EVENTS then we do469* nothing. */470{471FileHandler *filePtr;472473if (!(flags & TCL_FILE_EVENTS)) {474return;475}476for (filePtr = firstFileHandlerPtr; filePtr != NULL;477filePtr = filePtr->nextPtr) {478if (filePtr->mask != 0) {479Tcl_WatchFile(filePtr->file, filePtr->mask);480}481}482}483484/*485*----------------------------------------------------------------------486*487* FileHandlerCheckProc --488*489* This procedure is the second part of the "event source" for490* file handlers. It is invoked by Tcl_DoOneEvent after it calls491* select (or whatever it uses to wait for events).492*493* Results:494* None.495*496* Side effects:497* Makes entries on the Tcl event queue for each file that is498* now ready.499*500*----------------------------------------------------------------------501*/502503static void504FileHandlerCheckProc(clientData, flags)505ClientData clientData; /* Not used. */506int flags; /* Flags passed to Tk_DoOneEvent:507* if it doesn't include508* TCL_FILE_EVENTS then we do509* nothing. */510{511FileHandler *filePtr;512FileHandlerEvent *fileEvPtr;513514if (!(flags & TCL_FILE_EVENTS)) {515return;516}517for (filePtr = firstFileHandlerPtr; filePtr != NULL;518filePtr = filePtr->nextPtr) {519if (filePtr->mask != 0) {520filePtr->readyMask = Tcl_FileReady(filePtr->file, filePtr->mask);521if (filePtr->readyMask != 0) {522fileEvPtr = (FileHandlerEvent *) ckalloc(523sizeof(FileHandlerEvent));524fileEvPtr->header.proc = FileHandlerEventProc;525fileEvPtr->file = filePtr->file;526Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);527}528}529}530}531532/*533*----------------------------------------------------------------------534*535* FileHandlerEventProc --536*537* This procedure is called by Tcl_DoOneEvent when a file event538* reaches the front of the event queue. This procedure is responsible539* for actually handling the event by invoking the callback for the540* file handler.541*542* Results:543* Returns 1 if the event was handled, meaning it should be removed544* from the queue. Returns 0 if the event was not handled, meaning545* it should stay on the queue. The only time the event isn't546* handled is if the TCL_FILE_EVENTS flag bit isn't set.547*548* Side effects:549* Whatever the file handler's callback procedure does550*551*----------------------------------------------------------------------552*/553554static int555FileHandlerEventProc(evPtr, flags)556Tcl_Event *evPtr; /* Event to service. */557int flags; /* Flags that indicate what events to558* handle, such as TCL_FILE_EVENTS. */559{560FileHandler *filePtr;561FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;562int mask;563564if (!(flags & TCL_FILE_EVENTS)) {565return 0;566}567568/*569* Search through the file handlers to find the one whose handle matches570* the event. We do this rather than keeping a pointer to the file571* handler directly in the event, so that the handler can be deleted572* while the event is queued without leaving a dangling pointer.573*/574575for (filePtr = firstFileHandlerPtr; filePtr != NULL;576filePtr = filePtr->nextPtr) {577if (filePtr->file != fileEvPtr->file) {578continue;579}580581/*582* The code is tricky for two reasons:583* 1. The file handler's desired events could have changed584* since the time when the event was queued, so AND the585* ready mask with the desired mask.586* 2. The file could have been closed and re-opened since587* the time when the event was queued. This is why the588* ready mask is stored in the file handler rather than589* the queued event: it will be zeroed when a new590* file handler is created for the newly opened file.591*/592593mask = filePtr->readyMask & filePtr->mask;594filePtr->readyMask = 0;595if (mask != 0) {596(*filePtr->proc)(filePtr->clientData, mask);597}598break;599}600return 1;601}602603/*604*--------------------------------------------------------------605*606* Tcl_CreateTimerHandler --607*608* Arrange for a given procedure to be invoked at a particular609* time in the future.610*611* Results:612* The return value is a token for the timer event, which613* may be used to delete the event before it fires.614*615* Side effects:616* When milliseconds have elapsed, proc will be invoked617* exactly once.618*619*--------------------------------------------------------------620*/621622Tcl_TimerToken623Tcl_CreateTimerHandler(milliseconds, proc, clientData)624int milliseconds; /* How many milliseconds to wait625* before invoking proc. */626Tcl_TimerProc *proc; /* Procedure to invoke. */627ClientData clientData; /* Arbitrary data to pass to proc. */628{629register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;630static int id = 0;631632if (!timerEventSourceCreated) {633timerEventSourceCreated = 1;634Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,635(ClientData) NULL);636Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL);637}638639timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));640641/*642* Compute when the event should fire.643*/644645TclpGetTime(&timerHandlerPtr->time);646timerHandlerPtr->time.sec += milliseconds/1000;647timerHandlerPtr->time.usec += (milliseconds%1000)*1000;648if (timerHandlerPtr->time.usec >= 1000000) {649timerHandlerPtr->time.usec -= 1000000;650timerHandlerPtr->time.sec += 1;651}652653/*654* Fill in other fields for the event.655*/656657timerHandlerPtr->proc = proc;658timerHandlerPtr->clientData = clientData;659id++;660timerHandlerPtr->token = (Tcl_TimerToken) id;661662/*663* Add the event to the queue in the correct position664* (ordered by event firing time).665*/666667for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;668prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {669if ((tPtr2->time.sec > timerHandlerPtr->time.sec)670|| ((tPtr2->time.sec == timerHandlerPtr->time.sec)671&& (tPtr2->time.usec > timerHandlerPtr->time.usec))) {672break;673}674}675timerHandlerPtr->nextPtr = tPtr2;676if (prevPtr == NULL) {677firstTimerHandlerPtr = timerHandlerPtr;678} else {679prevPtr->nextPtr = timerHandlerPtr;680}681return timerHandlerPtr->token;682}683684/*685*--------------------------------------------------------------686*687* Tcl_DeleteTimerHandler --688*689* Delete a previously-registered timer handler.690*691* Results:692* None.693*694* Side effects:695* Destroy the timer callback identified by TimerToken,696* so that its associated procedure will not be called.697* If the callback has already fired, or if the given698* token doesn't exist, then nothing happens.699*700*--------------------------------------------------------------701*/702703void704Tcl_DeleteTimerHandler(token)705Tcl_TimerToken token; /* Result previously returned by706* Tcl_DeleteTimerHandler. */707{708register TimerHandler *timerHandlerPtr, *prevPtr;709710for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;711timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,712timerHandlerPtr = timerHandlerPtr->nextPtr) {713if (timerHandlerPtr->token != token) {714continue;715}716if (prevPtr == NULL) {717firstTimerHandlerPtr = timerHandlerPtr->nextPtr;718} else {719prevPtr->nextPtr = timerHandlerPtr->nextPtr;720}721ckfree((char *) timerHandlerPtr);722return;723}724}725726/*727*--------------------------------------------------------------728*729* Tcl_CreateModalTimeout --730*731* Arrange for a given procedure to be invoked at a particular732* time in the future, independently of all other timer events.733*734* Results:735* None.736*737* Side effects:738* When milliseconds have elapsed, proc will be invoked739* exactly once.740*741*--------------------------------------------------------------742*/743744void745Tcl_CreateModalTimeout(milliseconds, proc, clientData)746int milliseconds; /* How many milliseconds to wait747* before invoking proc. */748Tcl_TimerProc *proc; /* Procedure to invoke. */749ClientData clientData; /* Arbitrary data to pass to proc. */750{751TimerHandler *timerHandlerPtr;752753if (!timerEventSourceCreated) {754timerEventSourceCreated = 1;755Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,756(ClientData) NULL);757Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL);758}759760timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));761762/*763* Compute when the timeout should fire and fill in the other fields764* of the handler.765*/766767TclpGetTime(&timerHandlerPtr->time);768timerHandlerPtr->time.sec += milliseconds/1000;769timerHandlerPtr->time.usec += (milliseconds%1000)*1000;770if (timerHandlerPtr->time.usec >= 1000000) {771timerHandlerPtr->time.usec -= 1000000;772timerHandlerPtr->time.sec += 1;773}774timerHandlerPtr->proc = proc;775timerHandlerPtr->clientData = clientData;776777/*778* Push the handler on the top of the modal stack.779*/780781timerHandlerPtr->nextPtr = firstModalHandlerPtr;782firstModalHandlerPtr = timerHandlerPtr;783}784785/*786*--------------------------------------------------------------787*788* Tcl_DeleteModalTimeout --789*790* Remove the topmost modal timer handler from the stack of791* modal handlers.792*793* Results:794* None.795*796* Side effects:797* Destroys the topmost modal timeout handler, which must798* match proc and clientData.799*800*--------------------------------------------------------------801*/802803void804Tcl_DeleteModalTimeout(proc, clientData)805Tcl_TimerProc *proc; /* Callback procedure for the timeout. */806ClientData clientData; /* Arbitrary data to pass to proc. */807{808TimerHandler *timerHandlerPtr;809810timerHandlerPtr = firstModalHandlerPtr;811firstModalHandlerPtr = timerHandlerPtr->nextPtr;812if ((timerHandlerPtr->proc != proc)813|| (timerHandlerPtr->clientData != clientData)) {814panic("Tcl_DeleteModalTimeout found timeout stack corrupted");815}816ckfree((char *) timerHandlerPtr);817}818819/*820*----------------------------------------------------------------------821*822* TimerHandlerSetupProc --823*824* This procedure is part of the "event source" for timers.825* It is invoked by Tcl_DoOneEvent before it calls select (or826* whatever it uses to wait).827*828* Results:829* None.830*831* Side effects:832* Tells the notifier how long to sleep if it decides to block.833*834*----------------------------------------------------------------------835*/836837static void838TimerHandlerSetupProc(clientData, flags)839ClientData clientData; /* Not used. */840int flags; /* Flags passed to Tk_DoOneEvent:841* if it doesn't include842* TCL_TIMER_EVENTS then we only843* consider modal timers. */844{845TimerHandler *timerHandlerPtr, *tPtr2;846Tcl_Time blockTime;847848/*849* Find the timer handler (regular or modal) that fires first.850*/851852timerHandlerPtr = firstTimerHandlerPtr;853if (!(flags & TCL_TIMER_EVENTS)) {854timerHandlerPtr = NULL;855}856if (timerHandlerPtr != NULL) {857tPtr2 = firstModalHandlerPtr;858if (tPtr2 != NULL) {859if ((timerHandlerPtr->time.sec > tPtr2->time.sec)860|| ((timerHandlerPtr->time.sec == tPtr2->time.sec)861&& (timerHandlerPtr->time.usec > tPtr2->time.usec))) {862timerHandlerPtr = tPtr2;863}864}865} else {866timerHandlerPtr = firstModalHandlerPtr;867}868if (timerHandlerPtr == NULL) {869return;870}871872TclpGetTime(&blockTime);873blockTime.sec = timerHandlerPtr->time.sec - blockTime.sec;874blockTime.usec = timerHandlerPtr->time.usec - blockTime.usec;875if (blockTime.usec < 0) {876blockTime.sec -= 1;877blockTime.usec += 1000000;878}879if (blockTime.sec < 0) {880blockTime.sec = 0;881blockTime.usec = 0;882}883Tcl_SetMaxBlockTime(&blockTime);884}885886/*887*----------------------------------------------------------------------888*889* TimerHandlerCheckProc --890*891* This procedure is the second part of the "event source" for892* file handlers. It is invoked by Tcl_DoOneEvent after it calls893* select (or whatever it uses to wait for events).894*895* Results:896* None.897*898* Side effects:899* Makes entries on the Tcl event queue for each file that is900* now ready.901*902*----------------------------------------------------------------------903*/904905static void906TimerHandlerCheckProc(clientData, flags)907ClientData clientData; /* Not used. */908int flags; /* Flags passed to Tk_DoOneEvent:909* if it doesn't include910* TCL_TIMER_EVENTS then we only911* consider modal timeouts. */912{913TimerHandler *timerHandlerPtr;914TimerEvent *timerEvPtr;915int triggered, gotTime;916Tcl_Time curTime;917918triggered = 0;919gotTime = 0;920timerHandlerPtr = firstTimerHandlerPtr;921if ((flags & TCL_TIMER_EVENTS) && (timerHandlerPtr != NULL)) {922TclpGetTime(&curTime);923gotTime = 1;924if ((timerHandlerPtr->time.sec < curTime.sec)925|| ((timerHandlerPtr->time.sec == curTime.sec)926&& (timerHandlerPtr->time.usec <= curTime.usec))) {927triggered = 1;928}929}930timerHandlerPtr = firstModalHandlerPtr;931if (timerHandlerPtr != NULL) {932if (!gotTime) {933TclpGetTime(&curTime);934}935if ((timerHandlerPtr->time.sec < curTime.sec)936|| ((timerHandlerPtr->time.sec == curTime.sec)937&& (timerHandlerPtr->time.usec <= curTime.usec))) {938triggered = 1;939}940}941if (triggered) {942timerEvPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent));943timerEvPtr->header.proc = TimerHandlerEventProc;944timerEvPtr->time.sec = curTime.sec;945timerEvPtr->time.usec = curTime.usec;946Tcl_QueueEvent((Tcl_Event *) timerEvPtr, TCL_QUEUE_TAIL);947}948}949950/*951*----------------------------------------------------------------------952*953* TimerHandlerExitProc --954*955* Callback invoked during exit cleanup to destroy the timer event956* source.957*958* Results:959* None.960*961* Side effects:962* Destroys the timer event source.963*964*----------------------------------------------------------------------965*/966967/* ARGSUSED */968static void969TimerHandlerExitProc(clientData)970ClientData clientData; /* Not used. */971{972Tcl_DeleteEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,973(ClientData) NULL);974}975976/*977*----------------------------------------------------------------------978*979* TimerHandlerEventProc --980*981* This procedure is called by Tcl_DoOneEvent when a timer event982* reaches the front of the event queue. This procedure handles983* the event by invoking the callbacks for all timers that are984* ready.985*986* Results:987* Returns 1 if the event was handled, meaning it should be removed988* from the queue. Returns 0 if the event was not handled, meaning989* it should stay on the queue. The only time the event isn't990* handled is if the TCL_TIMER_EVENTS flag bit isn't set.991*992* Side effects:993* Whatever the timer handler callback procedures do.994*995*----------------------------------------------------------------------996*/997998static int999TimerHandlerEventProc(evPtr, flags)1000Tcl_Event *evPtr; /* Event to service. */1001int flags; /* Flags that indicate what events to1002* handle, such as TCL_FILE_EVENTS. */1003{1004TimerHandler *timerHandlerPtr;1005TimerEvent *timerEvPtr = (TimerEvent *) evPtr;10061007/*1008* Invoke the current modal timeout first, if there is one and1009* it has triggered.1010*/10111012timerHandlerPtr = firstModalHandlerPtr;1013if (firstModalHandlerPtr != NULL) {1014if ((timerHandlerPtr->time.sec < timerEvPtr->time.sec)1015|| ((timerHandlerPtr->time.sec == timerEvPtr->time.sec)1016&& (timerHandlerPtr->time.usec <= timerEvPtr->time.usec))) {1017(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);1018}1019}10201021/*1022* Invoke any normal timers that have fired.1023*/10241025if (!(flags & TCL_TIMER_EVENTS)) {1026return 1;1027}10281029while (1) {1030timerHandlerPtr = firstTimerHandlerPtr;1031if (timerHandlerPtr == NULL) {1032break;1033}1034if ((timerHandlerPtr->time.sec > timerEvPtr->time.sec)1035|| ((timerHandlerPtr->time.sec == timerEvPtr->time.sec)1036&& (timerHandlerPtr->time.usec >= timerEvPtr->time.usec))) {1037break;1038}10391040/*1041* Remove the handler from the queue before invoking it,1042* to avoid potential reentrancy problems.1043*/10441045firstTimerHandlerPtr = timerHandlerPtr->nextPtr;1046(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);1047ckfree((char *) timerHandlerPtr);1048}1049return 1;1050}10511052/*1053*--------------------------------------------------------------1054*1055* Tcl_DoWhenIdle --1056*1057* Arrange for proc to be invoked the next time the system is1058* idle (i.e., just before the next time that Tcl_DoOneEvent1059* would have to wait for something to happen).1060*1061* Results:1062* None.1063*1064* Side effects:1065* Proc will eventually be called, with clientData as argument.1066* See the manual entry for details.1067*1068*--------------------------------------------------------------1069*/10701071void1072Tcl_DoWhenIdle(proc, clientData)1073Tcl_IdleProc *proc; /* Procedure to invoke. */1074ClientData clientData; /* Arbitrary value to pass to proc. */1075{1076register IdleHandler *idlePtr;10771078idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));1079idlePtr->proc = proc;1080idlePtr->clientData = clientData;1081idlePtr->generation = idleGeneration;1082idlePtr->nextPtr = NULL;1083if (lastIdlePtr == NULL) {1084idleList = idlePtr;1085} else {1086lastIdlePtr->nextPtr = idlePtr;1087}1088lastIdlePtr = idlePtr;1089}10901091/*1092*----------------------------------------------------------------------1093*1094* Tcl_CancelIdleCall --1095*1096* If there are any when-idle calls requested to a given procedure1097* with given clientData, cancel all of them.1098*1099* Results:1100* None.1101*1102* Side effects:1103* If the proc/clientData combination were on the when-idle list,1104* they are removed so that they will never be called.1105*1106*----------------------------------------------------------------------1107*/11081109void1110Tcl_CancelIdleCall(proc, clientData)1111Tcl_IdleProc *proc; /* Procedure that was previously registered. */1112ClientData clientData; /* Arbitrary value to pass to proc. */1113{1114register IdleHandler *idlePtr, *prevPtr;1115IdleHandler *nextPtr;11161117for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;1118prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {1119while ((idlePtr->proc == proc)1120&& (idlePtr->clientData == clientData)) {1121nextPtr = idlePtr->nextPtr;1122ckfree((char *) idlePtr);1123idlePtr = nextPtr;1124if (prevPtr == NULL) {1125idleList = idlePtr;1126} else {1127prevPtr->nextPtr = idlePtr;1128}1129if (idlePtr == NULL) {1130lastIdlePtr = prevPtr;1131return;1132}1133}1134}1135}11361137/*1138*----------------------------------------------------------------------1139*1140* TclIdlePending --1141*1142* This function is called by the notifier subsystem to determine1143* whether there are any idle handlers currently scheduled.1144*1145* Results:1146* Returns 0 if the idle list is empty, otherwise it returns 1.1147*1148* Side effects:1149* None.1150*1151*----------------------------------------------------------------------1152*/11531154int1155TclIdlePending()1156{1157return (idleList == NULL) ? 0 : 1;1158}11591160/*1161*----------------------------------------------------------------------1162*1163* TclServiceIdle --1164*1165* This procedure is invoked by the notifier when it becomes idle.1166*1167* Results:1168* The return value is 1 if the procedure actually found an idle1169* handler to invoke. If no handler was found then 0 is returned.1170*1171* Side effects:1172* Invokes all pending idle handlers.1173*1174*----------------------------------------------------------------------1175*/11761177int1178TclServiceIdle()1179{1180IdleHandler *idlePtr;1181int oldGeneration;1182int foundIdle;11831184if (idleList == NULL) {1185return 0;1186}11871188foundIdle = 0;1189oldGeneration = idleGeneration;1190idleGeneration++;11911192/*1193* The code below is trickier than it may look, for the following1194* reasons:1195*1196* 1. New handlers can get added to the list while the current1197* one is being processed. If new ones get added, we don't1198* want to process them during this pass through the list (want1199* to check for other work to do first). This is implemented1200* using the generation number in the handler: new handlers1201* will have a different generation than any of the ones currently1202* on the list.1203* 2. The handler can call Tcl_DoOneEvent, so we have to remove1204* the handler from the list before calling it. Otherwise an1205* infinite loop could result.1206* 3. Tcl_CancelIdleCall can be called to remove an element from1207* the list while a handler is executing, so the list could1208* change structure during the call.1209*/12101211for (idlePtr = idleList;1212((idlePtr != NULL)1213&& ((oldGeneration - idlePtr->generation) >= 0));1214idlePtr = idleList) {1215idleList = idlePtr->nextPtr;1216if (idleList == NULL) {1217lastIdlePtr = NULL;1218}1219foundIdle = 1;1220(*idlePtr->proc)(idlePtr->clientData);1221ckfree((char *) idlePtr);1222}12231224return foundIdle;1225}12261227/*1228*----------------------------------------------------------------------1229*1230* Tcl_BackgroundError --1231*1232* This procedure is invoked to handle errors that occur in Tcl1233* commands that are invoked in "background" (e.g. from event or1234* timer bindings).1235*1236* Results:1237* None.1238*1239* Side effects:1240* The command "bgerror" is invoked later as an idle handler to1241* process the error, passing it the error message. If that fails,1242* then an error message is output on stderr.1243*1244*----------------------------------------------------------------------1245*/12461247void1248Tcl_BackgroundError(interp)1249Tcl_Interp *interp; /* Interpreter in which an error has1250* occurred. */1251{1252BgError *errPtr;1253char *varValue;1254ErrAssocData *assocPtr;12551256/*1257* The Tcl_AddErrorInfo call below (with an empty string) ensures that1258* errorInfo gets properly set. It's needed in cases where the error1259* came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;1260* in these cases errorInfo still won't have been set when this1261* procedure is called.1262*/12631264Tcl_AddErrorInfo(interp, "");1265errPtr = (BgError *) ckalloc(sizeof(BgError));1266errPtr->interp = interp;1267errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result)1268+ 1));1269strcpy(errPtr->errorMsg, interp->result);1270varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);1271if (varValue == NULL) {1272varValue = errPtr->errorMsg;1273}1274errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));1275strcpy(errPtr->errorInfo, varValue);1276varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);1277if (varValue == NULL) {1278varValue = "";1279}1280errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));1281strcpy(errPtr->errorCode, varValue);1282errPtr->nextPtr = NULL;12831284assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError",1285(Tcl_InterpDeleteProc **) NULL);1286if (assocPtr == NULL) {12871288/*1289* This is the first time a background error has occurred in1290* this interpreter. Create associated data to keep track of1291* pending error reports.1292*/12931294assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData));1295assocPtr->firstBgPtr = NULL;1296assocPtr->lastBgPtr = NULL;1297Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc,1298(ClientData) assocPtr);1299}1300if (assocPtr->firstBgPtr == NULL) {1301assocPtr->firstBgPtr = errPtr;1302Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr);1303} else {1304assocPtr->lastBgPtr->nextPtr = errPtr;1305}1306assocPtr->lastBgPtr = errPtr;1307Tcl_ResetResult(interp);1308}13091310/*1311*----------------------------------------------------------------------1312*1313* HandleBgErrors --1314*1315* This procedure is invoked as an idle handler to process all of1316* the accumulated background errors.1317*1318* Results:1319* None.1320*1321* Side effects:1322* Depends on what actions "bgerror" takes for the errors.1323*1324*----------------------------------------------------------------------1325*/13261327static void1328HandleBgErrors(clientData)1329ClientData clientData; /* Pointer to ErrAssocData structure. */1330{1331Tcl_Interp *interp;1332char *command;1333char *argv[2];1334int code;1335BgError *errPtr;1336ErrAssocData *assocPtr = (ErrAssocData *) clientData;1337Tcl_Channel errChannel;13381339while (assocPtr->firstBgPtr != NULL) {1340interp = assocPtr->firstBgPtr->interp;1341if (interp == NULL) {1342goto doneWithReport;1343}13441345/*1346* Restore important state variables to what they were at1347* the time the error occurred.1348*/13491350Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo,1351TCL_GLOBAL_ONLY);1352Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode,1353TCL_GLOBAL_ONLY);13541355/*1356* Create and invoke the bgerror command.1357*/13581359argv[0] = "bgerror";1360argv[1] = assocPtr->firstBgPtr->errorMsg;1361command = Tcl_Merge(2, argv);1362Tcl_AllowExceptions(interp);1363Tcl_Preserve((ClientData) interp);1364code = Tcl_GlobalEval(interp, command);1365ckfree(command);1366if (code == TCL_ERROR) {13671368/*1369* We have to get the error output channel at the latest possible1370* time, because the eval (above) might have changed the channel.1371*/13721373errChannel = Tcl_GetStdChannel(TCL_STDERR);1374if (errChannel != (Tcl_Channel) NULL) {1375if (strcmp(interp->result,1376"\"bgerror\" is an invalid command name or ambiguous abbreviation")1377== 0) {1378Tcl_Write(errChannel, assocPtr->firstBgPtr->errorInfo, -1);1379Tcl_Write(errChannel, "\n", -1);1380} else {1381Tcl_Write(errChannel,1382"bgerror failed to handle background error.\n",1383-1);1384Tcl_Write(errChannel, " Original error: ", -1);1385Tcl_Write(errChannel, assocPtr->firstBgPtr->errorMsg,1386-1);1387Tcl_Write(errChannel, "\n", -1);1388Tcl_Write(errChannel, " Error in bgerror: ", -1);1389Tcl_Write(errChannel, interp->result, -1);1390Tcl_Write(errChannel, "\n", -1);1391}1392Tcl_Flush(errChannel);1393}1394} else if (code == TCL_BREAK) {13951396/*1397* Break means cancel any remaining error reports for this1398* interpreter.1399*/14001401for (errPtr = assocPtr->firstBgPtr; errPtr != NULL;1402errPtr = errPtr->nextPtr) {1403if (errPtr->interp == interp) {1404errPtr->interp = NULL;1405}1406}1407}14081409Tcl_Release((ClientData) interp);14101411/*1412* Discard the command and the information about the error report.1413*/14141415doneWithReport:1416ckfree(assocPtr->firstBgPtr->errorMsg);1417ckfree(assocPtr->firstBgPtr->errorInfo);1418ckfree(assocPtr->firstBgPtr->errorCode);1419errPtr = assocPtr->firstBgPtr->nextPtr;1420ckfree((char *) assocPtr->firstBgPtr);1421assocPtr->firstBgPtr = errPtr;1422}1423assocPtr->lastBgPtr = NULL;1424}14251426/*1427*----------------------------------------------------------------------1428*1429* BgErrorDeleteProc --1430*1431* This procedure is associated with the "tclBgError" assoc data1432* for an interpreter; it is invoked when the interpreter is1433* deleted in order to free the information assoicated with any1434* pending error reports.1435*1436* Results:1437* None.1438*1439* Side effects:1440* Background error information is freed: if there were any1441* pending error reports, they are cancelled.1442*1443*----------------------------------------------------------------------1444*/14451446static void1447BgErrorDeleteProc(clientData, interp)1448ClientData clientData; /* Pointer to ErrAssocData structure. */1449Tcl_Interp *interp; /* Interpreter being deleted. */1450{1451ErrAssocData *assocPtr = (ErrAssocData *) clientData;1452BgError *errPtr;14531454while (assocPtr->firstBgPtr != NULL) {1455errPtr = assocPtr->firstBgPtr;1456assocPtr->firstBgPtr = errPtr->nextPtr;1457ckfree(errPtr->errorMsg);1458ckfree(errPtr->errorInfo);1459ckfree(errPtr->errorCode);1460ckfree((char *) errPtr);1461}1462ckfree((char *) assocPtr);1463Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr);1464}14651466/*1467*----------------------------------------------------------------------1468*1469* Tcl_CreateExitHandler --1470*1471* Arrange for a given procedure to be invoked just before the1472* application exits.1473*1474* Results:1475* None.1476*1477* Side effects:1478* Proc will be invoked with clientData as argument when the1479* application exits.1480*1481*----------------------------------------------------------------------1482*/14831484void1485Tcl_CreateExitHandler(proc, clientData)1486Tcl_ExitProc *proc; /* Procedure to invoke. */1487ClientData clientData; /* Arbitrary value to pass to proc. */1488{1489ExitHandler *exitPtr;14901491exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler));1492exitPtr->proc = proc;1493exitPtr->clientData = clientData;1494exitPtr->nextPtr = firstExitPtr;1495firstExitPtr = exitPtr;1496}14971498/*1499*----------------------------------------------------------------------1500*1501* Tcl_DeleteExitHandler --1502*1503* This procedure cancels an existing exit handler matching proc1504* and clientData, if such a handler exits.1505*1506* Results:1507* None.1508*1509* Side effects:1510* If there is an exit handler corresponding to proc and clientData1511* then it is cancelled; if no such handler exists then nothing1512* happens.1513*1514*----------------------------------------------------------------------1515*/15161517void1518Tcl_DeleteExitHandler(proc, clientData)1519Tcl_ExitProc *proc; /* Procedure that was previously registered. */1520ClientData clientData; /* Arbitrary value to pass to proc. */1521{1522ExitHandler *exitPtr, *prevPtr;15231524for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL;1525prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {1526if ((exitPtr->proc == proc)1527&& (exitPtr->clientData == clientData)) {1528if (prevPtr == NULL) {1529firstExitPtr = exitPtr->nextPtr;1530} else {1531prevPtr->nextPtr = exitPtr->nextPtr;1532}1533ckfree((char *) exitPtr);1534return;1535}1536}1537}15381539/*1540*----------------------------------------------------------------------1541*1542* Tcl_Exit --1543*1544* This procedure is called to terminate the application.1545*1546* Results:1547* None.1548*1549* Side effects:1550* All existing exit handlers are invoked, then the application1551* ends.1552*1553*----------------------------------------------------------------------1554*/15551556void1557Tcl_Exit(status)1558int status; /* Exit status for application; typically1559* 0 for normal return, 1 for error return. */1560{1561ExitHandler *exitPtr;15621563tclInExit = 1;1564for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) {1565/*1566* Be careful to remove the handler from the list before invoking1567* its callback. This protects us against double-freeing if the1568* callback should call Tcl_DeleteExitHandler on itself.1569*/15701571firstExitPtr = exitPtr->nextPtr;1572(*exitPtr->proc)(exitPtr->clientData);1573ckfree((char *) exitPtr);1574}1575#ifdef TCL_MEM_DEBUG1576if (tclMemDumpFileName != NULL) {1577Tcl_DumpActiveMemory(tclMemDumpFileName);1578}1579#endif1580tclInExit = 0;1581TclPlatformExit(status);1582}15831584/*1585*----------------------------------------------------------------------1586*1587* TclInExit --1588*1589* Determines if we are in the middle of exit-time cleanup.1590*1591* Results:1592* If we are in the middle of exiting, 1, otherwise 0.1593*1594* Side effects:1595* None.1596*1597*----------------------------------------------------------------------1598*/15991600int1601TclInExit()1602{1603return tclInExit;1604}16051606/*1607*----------------------------------------------------------------------1608*1609* Tcl_AfterCmd --1610*1611* This procedure is invoked to process the "after" Tcl command.1612* See the user documentation for details on what it does.1613*1614* Results:1615* A standard Tcl result.1616*1617* Side effects:1618* See the user documentation.1619*1620*----------------------------------------------------------------------1621*/16221623/* ARGSUSED */1624int1625Tcl_AfterCmd(clientData, interp, argc, argv)1626ClientData clientData; /* Points to the "tclAfter" assocData for1627* this interpreter, or NULL if the assocData1628* hasn't been created yet.*/1629Tcl_Interp *interp; /* Current interpreter. */1630int argc; /* Number of arguments. */1631char **argv; /* Argument strings. */1632{1633/*1634* The variable below is used to generate unique identifiers for1635* after commands. This id can wrap around, which can potentially1636* cause problems. However, there are not likely to be problems1637* in practice, because after commands can only be requested to1638* about a month in the future, and wrap-around is unlikely to1639* occur in less than about 1-10 years. Thus it's unlikely that1640* any old ids will still be around when wrap-around occurs.1641*/16421643static int nextId = 1;1644int ms;1645AfterInfo *afterPtr;1646AfterAssocData *assocPtr = (AfterAssocData *) clientData;1647Tcl_CmdInfo cmdInfo;1648size_t length;16491650if (argc < 2) {1651Tcl_AppendResult(interp, "wrong # args: should be \"",1652argv[0], " option ?arg arg ...?\"", (char *) NULL);1653return TCL_ERROR;1654}16551656/*1657* Create the "after" information associated for this interpreter,1658* if it doesn't already exist. Associate it with the command too,1659* so that it will be passed in as the ClientData argument in the1660* future.1661*/16621663if (assocPtr == NULL) {1664assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));1665assocPtr->interp = interp;1666assocPtr->firstAfterPtr = NULL;1667Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,1668(ClientData) assocPtr);1669cmdInfo.proc = Tcl_AfterCmd;1670cmdInfo.clientData = (ClientData) assocPtr;1671cmdInfo.deleteProc = NULL;1672cmdInfo.deleteData = (ClientData) assocPtr;1673Tcl_SetCommandInfo(interp, argv[0], &cmdInfo);1674}16751676/*1677* Parse the command.1678*/16791680length = strlen(argv[1]);1681if (isdigit(UCHAR(argv[1][0]))) {1682if (Tcl_GetInt(interp, argv[1], &ms) != TCL_OK) {1683return TCL_ERROR;1684}1685if (ms < 0) {1686ms = 0;1687}1688if (argc == 2) {1689Tcl_Sleep(ms);1690return TCL_OK;1691}1692afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));1693afterPtr->assocPtr = assocPtr;1694if (argc == 3) {1695afterPtr->command = (char *) ckalloc((unsigned)1696(strlen(argv[2]) + 1));1697strcpy(afterPtr->command, argv[2]);1698} else {1699afterPtr->command = Tcl_Concat(argc-2, argv+2);1700}1701afterPtr->interpType = ((Interp *)interp)->interpType;1702afterPtr->id = nextId;1703nextId += 1;1704afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,1705(ClientData) afterPtr);1706afterPtr->nextPtr = assocPtr->firstAfterPtr;1707assocPtr->firstAfterPtr = afterPtr;1708sprintf(interp->result, "after#%d", afterPtr->id);1709} else if (strncmp(argv[1], "cancel", length) == 0) {1710char *arg;17111712if (argc < 3) {1713Tcl_AppendResult(interp, "wrong # args: should be \"",1714argv[0], " cancel id|command\"", (char *) NULL);1715return TCL_ERROR;1716}1717if (argc == 3) {1718arg = argv[2];1719} else {1720arg = Tcl_Concat(argc-2, argv+2);1721}1722for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;1723afterPtr = afterPtr->nextPtr) {1724if (strcmp(afterPtr->command, arg) == 0) {1725break;1726}1727}1728if (afterPtr == NULL) {1729afterPtr = GetAfterEvent(assocPtr, arg);1730}1731if (arg != argv[2]) {1732ckfree(arg);1733}1734if (afterPtr != NULL) {1735if (afterPtr->token != NULL) {1736Tcl_DeleteTimerHandler(afterPtr->token);1737} else {1738Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);1739}1740FreeAfterPtr(afterPtr);1741}1742} else if ((strncmp(argv[1], "idle", length) == 0)1743&& (length >= 2)) {1744if (argc < 3) {1745Tcl_AppendResult(interp, "wrong # args: should be \"",1746argv[0], " idle script script ...\"", (char *) NULL);1747return TCL_ERROR;1748}1749afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));1750afterPtr->assocPtr = assocPtr;1751if (argc == 3) {1752afterPtr->command = (char *) ckalloc((unsigned)1753(strlen(argv[2]) + 1));1754strcpy(afterPtr->command, argv[2]);1755} else {1756afterPtr->command = Tcl_Concat(argc-2, argv+2);1757}1758afterPtr->interpType = ((Interp *)interp)->interpType;1759afterPtr->id = nextId;1760nextId += 1;1761afterPtr->token = NULL;1762afterPtr->nextPtr = assocPtr->firstAfterPtr;1763assocPtr->firstAfterPtr = afterPtr;1764Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);1765sprintf(interp->result, "after#%d", afterPtr->id);1766} else if ((strncmp(argv[1], "info", length) == 0)1767&& (length >= 2)) {1768if (argc == 2) {1769char buffer[30];17701771for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;1772afterPtr = afterPtr->nextPtr) {1773if (assocPtr->interp == interp) {1774sprintf(buffer, "after#%d", afterPtr->id);1775Tcl_AppendElement(interp, buffer);1776}1777}1778return TCL_OK;1779}1780if (argc != 3) {1781Tcl_AppendResult(interp, "wrong # args: should be \"",1782argv[0], " info ?id?\"", (char *) NULL);1783return TCL_ERROR;1784}1785afterPtr = GetAfterEvent(assocPtr, argv[2]);1786if (afterPtr == NULL) {1787Tcl_AppendResult(interp, "event \"", argv[2],1788"\" doesn't exist", (char *) NULL);1789return TCL_ERROR;1790}1791Tcl_AppendElement(interp, afterPtr->command);1792Tcl_AppendElement(interp,1793(afterPtr->token == NULL) ? "idle" : "timer");1794} else {1795Tcl_AppendResult(interp, "bad argument \"", argv[1],1796"\": must be cancel, idle, info, or a number",1797(char *) NULL);1798return TCL_ERROR;1799}1800return TCL_OK;1801}18021803/*1804*----------------------------------------------------------------------1805*1806* GetAfterEvent --1807*1808* This procedure parses an "after" id such as "after#4" and1809* returns a pointer to the AfterInfo structure.1810*1811* Results:1812* The return value is either a pointer to an AfterInfo structure,1813* if one is found that corresponds to "string" and is for interp,1814* or NULL if no corresponding after event can be found.1815*1816* Side effects:1817* None.1818*1819*----------------------------------------------------------------------1820*/18211822static AfterInfo *1823GetAfterEvent(assocPtr, string)1824AfterAssocData *assocPtr; /* Points to "after"-related information for1825* this interpreter. */1826char *string; /* Textual identifier for after event, such1827* as "after#6". */1828{1829AfterInfo *afterPtr;1830int id;1831char *end;18321833if (strncmp(string, "after#", 6) != 0) {1834return NULL;1835}1836string += 6;1837id = strtoul(string, &end, 10);1838if ((end == string) || (*end != 0)) {1839return NULL;1840}1841for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;1842afterPtr = afterPtr->nextPtr) {1843if (afterPtr->id == id) {1844return afterPtr;1845}1846}1847return NULL;1848}18491850/*1851*----------------------------------------------------------------------1852*1853* AfterProc --1854*1855* Timer callback to execute commands registered with the1856* "after" command.1857*1858* Results:1859* None.1860*1861* Side effects:1862* Executes whatever command was specified. If the command1863* returns an error, then the command "bgerror" is invoked1864* to process the error; if bgerror fails then information1865* about the error is output on stderr.1866*1867*----------------------------------------------------------------------1868*/18691870static void1871AfterProc(clientData)1872ClientData clientData; /* Describes command to execute. */1873{1874AfterInfo *afterPtr = (AfterInfo *) clientData;1875AfterAssocData *assocPtr = afterPtr->assocPtr;1876AfterInfo *prevPtr;1877int result;1878Tcl_Interp *interp;18791880/*1881* First remove the callback from our list of callbacks; otherwise1882* someone could delete the callback while it's being executed, which1883* could cause a core dump.1884*/18851886if (assocPtr->firstAfterPtr == afterPtr) {1887assocPtr->firstAfterPtr = afterPtr->nextPtr;1888} else {1889for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;1890prevPtr = prevPtr->nextPtr) {1891/* Empty loop body. */1892}1893prevPtr->nextPtr = afterPtr->nextPtr;1894}18951896/*1897* Execute the callback.1898*/18991900interp = assocPtr->interp;1901Tcl_Preserve((ClientData) interp);1902result = Tksh_GlobalEval(interp, afterPtr->command, afterPtr->interpType);1903if (result != TCL_OK) {1904Tcl_AddErrorInfo(interp, "\n (\"after\" script)");1905Tcl_BackgroundError(interp);1906}1907Tcl_Release((ClientData) interp);19081909/*1910* Free the memory for the callback.1911*/19121913ckfree(afterPtr->command);1914ckfree((char *) afterPtr);1915}19161917/*1918*----------------------------------------------------------------------1919*1920* FreeAfterPtr --1921*1922* This procedure removes an "after" command from the list of1923* those that are pending and frees its resources. This procedure1924* does *not* cancel the timer handler; if that's needed, the1925* caller must do it.1926*1927* Results:1928* None.1929*1930* Side effects:1931* The memory associated with afterPtr is released.1932*1933*----------------------------------------------------------------------1934*/19351936static void1937FreeAfterPtr(afterPtr)1938AfterInfo *afterPtr; /* Command to be deleted. */1939{1940AfterInfo *prevPtr;1941AfterAssocData *assocPtr = afterPtr->assocPtr;19421943if (assocPtr->firstAfterPtr == afterPtr) {1944assocPtr->firstAfterPtr = afterPtr->nextPtr;1945} else {1946for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;1947prevPtr = prevPtr->nextPtr) {1948/* Empty loop body. */1949}1950prevPtr->nextPtr = afterPtr->nextPtr;1951}1952ckfree(afterPtr->command);1953ckfree((char *) afterPtr);1954}19551956/*1957*----------------------------------------------------------------------1958*1959* AfterCleanupProc --1960*1961* This procedure is invoked whenever an interpreter is deleted1962* to cleanup the AssocData for "tclAfter".1963*1964* Results:1965* None.1966*1967* Side effects:1968* After commands are removed.1969*1970*----------------------------------------------------------------------1971*/19721973/* ARGSUSED */1974static void1975AfterCleanupProc(clientData, interp)1976ClientData clientData; /* Points to AfterAssocData for the1977* interpreter. */1978Tcl_Interp *interp; /* Interpreter that is being deleted. */1979{1980AfterAssocData *assocPtr = (AfterAssocData *) clientData;1981AfterInfo *afterPtr;19821983while (assocPtr->firstAfterPtr != NULL) {1984afterPtr = assocPtr->firstAfterPtr;1985assocPtr->firstAfterPtr = afterPtr->nextPtr;1986if (afterPtr->token != NULL) {1987Tcl_DeleteTimerHandler(afterPtr->token);1988} else {1989Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);1990}1991ckfree(afterPtr->command);1992ckfree((char *) afterPtr);1993}1994ckfree((char *) assocPtr);1995}19961997/*1998*----------------------------------------------------------------------1999*2000* Tcl_VwaitCmd --2001*2002* This procedure is invoked to process the "vwait" Tcl command.2003* See the user documentation for details on what it does.2004*2005* Results:2006* A standard Tcl result.2007*2008* Side effects:2009* See the user documentation.2010*2011*----------------------------------------------------------------------2012*/20132014/* ARGSUSED */2015int2016Tcl_VwaitCmd(clientData, interp, argc, argv)2017ClientData clientData; /* Not used. */2018Tcl_Interp *interp; /* Current interpreter. */2019int argc; /* Number of arguments. */2020char **argv; /* Argument strings. */2021{2022int done, foundEvent;20232024if (argc != 2) {2025Tcl_AppendResult(interp, "wrong # args: should be \"",2026argv[0], " name\"", (char *) NULL);2027return TCL_ERROR;2028}2029if (Tcl_TraceVar(interp, argv[1],2030TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,2031VwaitVarProc, (ClientData) &done) != TCL_OK) {2032return TCL_ERROR;2033};2034done = 0;2035foundEvent = 1;2036while (!done && foundEvent) {2037foundEvent = Tcl_DoOneEvent(0);2038}2039Tcl_UntraceVar(interp, argv[1],2040TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,2041VwaitVarProc, (ClientData) &done);20422043/*2044* Clear out the interpreter's result, since it may have been set2045* by event handlers.2046*/20472048Tcl_ResetResult(interp);2049if (!foundEvent) {2050Tcl_AppendResult(interp, "can't wait for variable \"", argv[1],2051"\": would wait forever", (char *) NULL);2052return TCL_ERROR;2053}2054return TCL_OK;2055}20562057/* ARGSUSED */2058static char *2059VwaitVarProc(clientData, interp, name1, name2, flags)2060ClientData clientData; /* Pointer to integer to set to 1. */2061Tcl_Interp *interp; /* Interpreter containing variable. */2062char *name1; /* Name of variable. */2063char *name2; /* Second part of variable name. */2064int flags; /* Information about what happened. */2065{2066int *donePtr = (int *) clientData;20672068*donePtr = 1;2069return (char *) NULL;2070}20712072/*2073*----------------------------------------------------------------------2074*2075* Tcl_UpdateCmd --2076*2077* This procedure is invoked to process the "update" Tcl command.2078* See the user documentation for details on what it does.2079*2080* Results:2081* A standard Tcl result.2082*2083* Side effects:2084* See the user documentation.2085*2086*----------------------------------------------------------------------2087*/20882089/* ARGSUSED */2090int2091Tcl_UpdateCmd(clientData, interp, argc, argv)2092ClientData clientData; /* Not used. */2093Tcl_Interp *interp; /* Current interpreter. */2094int argc; /* Number of arguments. */2095char **argv; /* Argument strings. */2096{2097int flags = 0; /* Initialization needed only to stop2098* compiler warnings. */20992100if (argc == 1) {2101flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;2102} else if (argc == 2) {2103if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {2104Tcl_AppendResult(interp, "bad option \"", argv[1],2105"\": must be idletasks", (char *) NULL);2106return TCL_ERROR;2107}2108flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;2109} else {2110Tcl_AppendResult(interp, "wrong # args: should be \"",2111argv[0], " ?idletasks?\"", (char *) NULL);2112return TCL_ERROR;2113}21142115while (Tcl_DoOneEvent(flags) != 0) {2116/* Empty loop body */2117}21182119/*2120* Must clear the interpreter's result because event handlers could2121* have executed commands.2122*/21232124Tcl_ResetResult(interp);2125return TCL_OK;2126}21272128/*2129*----------------------------------------------------------------------2130*2131* TclWaitForFile --2132*2133* This procedure waits synchronously for a file to become readable2134* or writable, with an optional timeout.2135*2136* Results:2137* The return value is an OR'ed combination of TCL_READABLE,2138* TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions2139* that are present on file at the time of the return. This2140* procedure will not return until either "timeout" milliseconds2141* have elapsed or at least one of the conditions given by mask2142* has occurred for file (a return value of 0 means that a timeout2143* occurred). No normal events will be serviced during the2144* execution of this procedure.2145*2146* Side effects:2147* Time passes.2148*2149*----------------------------------------------------------------------2150*/21512152int2153TclWaitForFile(file, mask, timeout)2154Tcl_File file; /* Handle for file on which to wait. */2155int mask; /* What to wait for: OR'ed combination of2156* TCL_READABLE, TCL_WRITABLE, and2157* TCL_EXCEPTION. */2158int timeout; /* Maximum amount of time to wait for one2159* of the conditions in mask to occur, in2160* milliseconds. A value of 0 means don't2161* wait at all, and a value of -1 means2162* wait forever. */2163{2164Tcl_Time abortTime, now, blockTime;2165int present;21662167/*2168* If there is a non-zero finite timeout, compute the time when2169* we give up.2170*/21712172if (timeout > 0) {2173TclpGetTime(&now);2174abortTime.sec = now.sec + timeout/1000;2175abortTime.usec = now.usec + (timeout%1000)*1000;2176if (abortTime.usec >= 1000000) {2177abortTime.usec -= 1000000;2178abortTime.sec += 1;2179}2180}21812182/*2183* Loop in a mini-event loop of our own, waiting for either the2184* file to become ready or a timeout to occur.2185*/21862187while (1) {2188Tcl_WatchFile(file, mask);2189if (timeout > 0) {2190blockTime.sec = abortTime.sec - now.sec;2191blockTime.usec = abortTime.usec - now.usec;2192if (blockTime.usec < 0) {2193blockTime.sec -= 1;2194blockTime.usec += 1000000;2195}2196if (blockTime.sec < 0) {2197blockTime.sec = 0;2198blockTime.usec = 0;2199}2200Tcl_WaitForEvent(&blockTime);2201} else if (timeout == 0) {2202blockTime.sec = 0;2203blockTime.usec = 0;2204Tcl_WaitForEvent(&blockTime);2205} else {2206Tcl_WaitForEvent((Tcl_Time *) NULL);2207}2208present = Tcl_FileReady(file, mask);2209if (present != 0) {2210break;2211}2212if (timeout == 0) {2213break;2214}2215TclpGetTime(&now);2216if ((abortTime.sec < now.sec)2217|| ((abortTime.sec == now.sec)2218&& (abortTime.usec <= now.usec))) {2219break;2220}2221}2222return present;2223}222422252226