Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclEvent.c
1810 views
1
/*
2
* tclEvent.c --
3
*
4
* This file provides basic event-managing facilities for Tcl,
5
* including an event queue, and mechanisms for attaching
6
* callbacks to certain events.
7
*
8
* It also contains the command procedures for the commands
9
* "after", "vwait", and "update".
10
*
11
* Copyright (c) 1990-1994 The Regents of the University of California.
12
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
13
*
14
* See the file "license.terms" for information on usage and redistribution
15
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
*
17
* SCCS: @(#) tclEvent.c 1.132 96/11/12 11:52:27
18
*/
19
20
#include "tclInt.h"
21
#include "tclPort.h"
22
23
/*
24
* For each file registered in a call to Tcl_CreateFileHandler,
25
* there is one record of the following type. All of these records
26
* are chained together into a single list.
27
*/
28
29
typedef struct FileHandler {
30
Tcl_File file; /* Generic file handle for file. */
31
int mask; /* Mask of desired events: TCL_READABLE, etc. */
32
int readyMask; /* Events that were ready the last time that
33
* FileHandlerCheckProc checked this file. */
34
Tcl_FileProc *proc; /* Procedure to call, in the style of
35
* Tcl_CreateFileHandler. This is NULL
36
* if the handler was created by
37
* Tcl_CreateFileHandler2. */
38
ClientData clientData; /* Argument to pass to proc. */
39
struct FileHandler *nextPtr;/* Next in list of all files we care
40
* about (NULL for end of list). */
41
} FileHandler;
42
43
static FileHandler *firstFileHandlerPtr = (FileHandler *) NULL;
44
/* List of all file handlers. */
45
static int fileEventSourceCreated = 0;
46
/* Zero means that the file event source
47
* hasn't been registerd with the Tcl
48
* notifier yet. */
49
50
/*
51
* The following structure is what is added to the Tcl event queue when
52
* file handlers are ready to fire.
53
*/
54
55
typedef struct FileHandlerEvent {
56
Tcl_Event header; /* Information that is standard for
57
* all events. */
58
Tcl_File file; /* File descriptor that is ready. Used
59
* to find the FileHandler structure for
60
* the file (can't point directly to the
61
* FileHandler structure because it could
62
* go away while the event is queued). */
63
} FileHandlerEvent;
64
65
/*
66
* For each timer callback that's pending (either regular or "modal"),
67
* there is one record of the following type. The normal handlers
68
* (created by Tcl_CreateTimerHandler) are chained together in a
69
* list sorted by time (earliest event first).
70
*/
71
72
typedef struct TimerHandler {
73
Tcl_Time time; /* When timer is to fire. */
74
Tcl_TimerProc *proc; /* Procedure to call. */
75
ClientData clientData; /* Argument to pass to proc. */
76
Tcl_TimerToken token; /* Identifies event so it can be
77
* deleted. Not used in modal
78
* timeouts. */
79
struct TimerHandler *nextPtr; /* Next event in queue, or NULL for
80
* end of queue. */
81
} TimerHandler;
82
83
static TimerHandler *firstTimerHandlerPtr = NULL;
84
/* First event in queue. */
85
static int timerEventSourceCreated = 0; /* 0 means that the timer event source
86
* hasn't yet been registered with the
87
* Tcl notifier. */
88
89
/*
90
* The information below describes a stack of modal timeouts managed by
91
* Tcl_CreateModalTimer and Tcl_DeleteModalTimer. Only the first element
92
* in the list is used at any given time.
93
*/
94
95
static TimerHandler *firstModalHandlerPtr = NULL;
96
97
/*
98
* The following structure is what's added to the Tcl event queue when
99
* timer handlers are ready to fire.
100
*/
101
102
typedef struct TimerEvent {
103
Tcl_Event header; /* Information that is standard for
104
* all events. */
105
Tcl_Time time; /* All timer events that specify this
106
* time or earlier are ready
107
* to fire. */
108
} TimerEvent;
109
110
/*
111
* There is one of the following structures for each of the
112
* handlers declared in a call to Tcl_DoWhenIdle. All of the
113
* currently-active handlers are linked together into a list.
114
*/
115
116
typedef struct IdleHandler {
117
Tcl_IdleProc (*proc); /* Procedure to call. */
118
ClientData clientData; /* Value to pass to proc. */
119
int generation; /* Used to distinguish older handlers from
120
* recently-created ones. */
121
struct IdleHandler *nextPtr;/* Next in list of active handlers. */
122
} IdleHandler;
123
124
static IdleHandler *idleList = NULL;
125
/* First in list of all idle handlers. */
126
static IdleHandler *lastIdlePtr = NULL;
127
/* Last in list (or NULL for empty list). */
128
static int idleGeneration = 0; /* Used to fill in the "generation" fields
129
* of IdleHandler structures. Increments
130
* each time Tcl_DoOneEvent starts calling
131
* idle handlers, so that all old handlers
132
* can be called without calling any of the
133
* new ones created by old ones. */
134
135
/*
136
* The data structure below is used by the "after" command to remember
137
* the command to be executed later. All of the pending "after" commands
138
* for an interpreter are linked together in a list.
139
*/
140
141
typedef struct AfterInfo {
142
struct AfterAssocData *assocPtr;
143
/* Pointer to the "tclAfter" assocData for
144
* the interp in which command will be
145
* executed. */
146
char *command; /* Command to execute. Malloc'ed, so must
147
* be freed when structure is deallocated. */
148
int id; /* Integer identifier for command; used to
149
* cancel it. */
150
Tcl_TimerToken token; /* Used to cancel the "after" command. NULL
151
* means that the command is run as an
152
* idle handler rather than as a timer
153
* handler. NULL means this is an "after
154
* idle" handler rather than a
155
* timer handler. */
156
struct AfterInfo *nextPtr; /* Next in list of all "after" commands for
157
* this interpreter. */
158
int interpType;
159
} AfterInfo;
160
161
/*
162
* One of the following structures is associated with each interpreter
163
* for which an "after" command has ever been invoked. A pointer to
164
* this structure is stored in the AssocData for the "tclAfter" key.
165
*/
166
167
typedef struct AfterAssocData {
168
Tcl_Interp *interp; /* The interpreter for which this data is
169
* registered. */
170
AfterInfo *firstAfterPtr; /* First in list of all "after" commands
171
* still pending for this interpreter, or
172
* NULL if none. */
173
} AfterAssocData;
174
175
/*
176
* The data structure below is used to report background errors. One
177
* such structure is allocated for each error; it holds information
178
* about the interpreter and the error until bgerror can be invoked
179
* later as an idle handler.
180
*/
181
182
typedef struct BgError {
183
Tcl_Interp *interp; /* Interpreter in which error occurred. NULL
184
* means this error report has been cancelled
185
* (a previous report generated a break). */
186
char *errorMsg; /* The error message (interp->result when
187
* the error occurred). Malloc-ed. */
188
char *errorInfo; /* Value of the errorInfo variable
189
* (malloc-ed). */
190
char *errorCode; /* Value of the errorCode variable
191
* (malloc-ed). */
192
struct BgError *nextPtr; /* Next in list of all pending error
193
* reports for this interpreter, or NULL
194
* for end of list. */
195
} BgError;
196
197
/*
198
* One of the structures below is associated with the "tclBgError"
199
* assoc data for each interpreter. It keeps track of the head and
200
* tail of the list of pending background errors for the interpreter.
201
*/
202
203
typedef struct ErrAssocData {
204
BgError *firstBgPtr; /* First in list of all background errors
205
* waiting to be processed for this
206
* interpreter (NULL if none). */
207
BgError *lastBgPtr; /* Last in list of all background errors
208
* waiting to be processed for this
209
* interpreter (NULL if none). */
210
} ErrAssocData;
211
212
/*
213
* For each exit handler created with a call to Tcl_CreateExitHandler
214
* there is a structure of the following type:
215
*/
216
217
typedef struct ExitHandler {
218
Tcl_ExitProc *proc; /* Procedure to call when process exits. */
219
ClientData clientData; /* One word of information to pass to proc. */
220
struct ExitHandler *nextPtr;/* Next in list of all exit handlers for
221
* this application, or NULL for end of list. */
222
} ExitHandler;
223
224
static ExitHandler *firstExitPtr = NULL;
225
/* First in list of all exit handlers for
226
* application. */
227
228
/*
229
* Structures of the following type are used during the execution
230
* of Tcl_WaitForFile, to keep track of the file and timeout.
231
*/
232
233
typedef struct FileWait {
234
Tcl_File file; /* File to wait on. */
235
int mask; /* Conditions to wait for (TCL_READABLE,
236
* etc.) */
237
int timeout; /* Original "timeout" argument to
238
* Tcl_WaitForFile. */
239
Tcl_Time abortTime; /* Time at which to abort the wait. */
240
int present; /* Conditions present on the file during
241
* the last time through the event loop. */
242
int done; /* Non-zero means we're done: either one of
243
* the desired conditions is present or the
244
* timeout period has elapsed. */
245
} FileWait;
246
247
/*
248
* The following variable is a "secret" indication to Tcl_Exit that
249
* it should dump out the state of memory before exiting. If the
250
* value is non-NULL, it gives the name of the file in which to
251
* dump memory usage information.
252
*/
253
254
char *tclMemDumpFileName = NULL;
255
256
/*
257
* This variable is set to 1 when Tcl_Exit is called, and at the end of
258
* its work, it is reset to 0. The variable is checked by TclInExit() to
259
* allow different behavior for exit-time processing, e.g. in closing of
260
* files and pipes.
261
*/
262
263
static int tclInExit = 0;
264
265
/*
266
* Prototypes for procedures referenced only in this file:
267
*/
268
269
static void AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
270
Tcl_Interp *interp));
271
static void AfterProc _ANSI_ARGS_((ClientData clientData));
272
static void BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData,
273
Tcl_Interp *interp));
274
static void FileHandlerCheckProc _ANSI_ARGS_((
275
ClientData clientData, int flags));
276
static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
277
int flags));
278
static void FileHandlerExitProc _ANSI_ARGS_((ClientData data));
279
static void FileHandlerSetupProc _ANSI_ARGS_((
280
ClientData clientData, int flags));
281
static void FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
282
static AfterInfo * GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
283
char *string));
284
static void HandleBgErrors _ANSI_ARGS_((ClientData clientData));
285
static void TimerHandlerCheckProc _ANSI_ARGS_((
286
ClientData clientData, int flags));
287
static int TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
288
int flags));
289
static void TimerHandlerExitProc _ANSI_ARGS_((ClientData data));
290
static void TimerHandlerSetupProc _ANSI_ARGS_((
291
ClientData clientData, int flags));
292
static char * VwaitVarProc _ANSI_ARGS_((ClientData clientData,
293
Tcl_Interp *interp, char *name1, char *name2,
294
int flags));
295
296
/*
297
*--------------------------------------------------------------
298
*
299
* Tcl_CreateFileHandler --
300
*
301
* Arrange for a given procedure to be invoked whenever
302
* a given file becomes readable or writable.
303
*
304
* Results:
305
* None.
306
*
307
* Side effects:
308
* From now on, whenever the I/O channel given by file becomes
309
* ready in the way indicated by mask, proc will be invoked.
310
* See the manual entry for details on the calling sequence
311
* to proc. If file is already registered then the old mask
312
* and proc and clientData values will be replaced with
313
* new ones.
314
*
315
*--------------------------------------------------------------
316
*/
317
318
void
319
Tcl_CreateFileHandler(file, mask, proc, clientData)
320
Tcl_File file; /* Handle of stream to watch. */
321
int mask; /* OR'ed combination of TCL_READABLE,
322
* TCL_WRITABLE, and TCL_EXCEPTION:
323
* indicates conditions under which
324
* proc should be called. */
325
Tcl_FileProc *proc; /* Procedure to call for each
326
* selected event. */
327
ClientData clientData; /* Arbitrary data to pass to proc. */
328
{
329
register FileHandler *filePtr;
330
331
if (!fileEventSourceCreated) {
332
fileEventSourceCreated = 1;
333
Tcl_CreateEventSource(FileHandlerSetupProc, FileHandlerCheckProc,
334
(ClientData) NULL);
335
Tcl_CreateExitHandler(FileHandlerExitProc, (ClientData) NULL);
336
}
337
338
/*
339
* Make sure the file isn't already registered. Create a
340
* new record in the normal case where there's no existing
341
* record.
342
*/
343
344
for (filePtr = firstFileHandlerPtr; filePtr != NULL;
345
filePtr = filePtr->nextPtr) {
346
if (filePtr->file == file) {
347
break;
348
}
349
}
350
if (filePtr == NULL) {
351
filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
352
filePtr->file = file;
353
filePtr->nextPtr = firstFileHandlerPtr;
354
firstFileHandlerPtr = filePtr;
355
}
356
357
/*
358
* The remainder of the initialization below is done regardless
359
* of whether or not this is a new record or a modification of
360
* an old one.
361
*/
362
363
filePtr->mask = mask;
364
filePtr->readyMask = 0;
365
filePtr->proc = proc;
366
filePtr->clientData = clientData;
367
}
368
369
/*
370
*--------------------------------------------------------------
371
*
372
* Tcl_DeleteFileHandler --
373
*
374
* Cancel a previously-arranged callback arrangement for
375
* a file.
376
*
377
* Results:
378
* None.
379
*
380
* Side effects:
381
* If a callback was previously registered on file, remove it.
382
*
383
*--------------------------------------------------------------
384
*/
385
386
void
387
Tcl_DeleteFileHandler(file)
388
Tcl_File file; /* Stream id for which to remove
389
* callback procedure. */
390
{
391
FileHandler *filePtr, *prevPtr;
392
393
/*
394
* Find the entry for the given file (and return if there
395
* isn't one).
396
*/
397
398
for (prevPtr = NULL, filePtr = firstFileHandlerPtr; ;
399
prevPtr = filePtr, filePtr = filePtr->nextPtr) {
400
if (filePtr == NULL) {
401
return;
402
}
403
if (filePtr->file == file) {
404
break;
405
}
406
}
407
408
/*
409
* Clean up information in the callback record.
410
*/
411
412
if (prevPtr == NULL) {
413
firstFileHandlerPtr = filePtr->nextPtr;
414
} else {
415
prevPtr->nextPtr = filePtr->nextPtr;
416
}
417
ckfree((char *) filePtr);
418
}
419
420
/*
421
*----------------------------------------------------------------------
422
*
423
* FileHandlerExitProc --
424
*
425
* Cleanup procedure to delete the file event source during exit
426
* cleanup.
427
*
428
* Results:
429
* None.
430
*
431
* Side effects:
432
* Destroys the file event source.
433
*
434
*----------------------------------------------------------------------
435
*/
436
437
/* ARGSUSED */
438
static void
439
FileHandlerExitProc(clientData)
440
ClientData clientData; /* Not used. */
441
{
442
Tcl_DeleteEventSource(FileHandlerSetupProc, FileHandlerCheckProc,
443
(ClientData) NULL);
444
}
445
446
/*
447
*----------------------------------------------------------------------
448
*
449
* FileHandlerSetupProc --
450
*
451
* This procedure is part of the "event source" for file handlers.
452
* It is invoked by Tcl_DoOneEvent before it calls select (or
453
* whatever it uses to wait).
454
*
455
* Results:
456
* None.
457
*
458
* Side effects:
459
* Tells the notifier which files should be waited for.
460
*
461
*----------------------------------------------------------------------
462
*/
463
464
static void
465
FileHandlerSetupProc(clientData, flags)
466
ClientData clientData; /* Not used. */
467
int flags; /* Flags passed to Tk_DoOneEvent:
468
* if it doesn't include
469
* TCL_FILE_EVENTS then we do
470
* nothing. */
471
{
472
FileHandler *filePtr;
473
474
if (!(flags & TCL_FILE_EVENTS)) {
475
return;
476
}
477
for (filePtr = firstFileHandlerPtr; filePtr != NULL;
478
filePtr = filePtr->nextPtr) {
479
if (filePtr->mask != 0) {
480
Tcl_WatchFile(filePtr->file, filePtr->mask);
481
}
482
}
483
}
484
485
/*
486
*----------------------------------------------------------------------
487
*
488
* FileHandlerCheckProc --
489
*
490
* This procedure is the second part of the "event source" for
491
* file handlers. It is invoked by Tcl_DoOneEvent after it calls
492
* select (or whatever it uses to wait for events).
493
*
494
* Results:
495
* None.
496
*
497
* Side effects:
498
* Makes entries on the Tcl event queue for each file that is
499
* now ready.
500
*
501
*----------------------------------------------------------------------
502
*/
503
504
static void
505
FileHandlerCheckProc(clientData, flags)
506
ClientData clientData; /* Not used. */
507
int flags; /* Flags passed to Tk_DoOneEvent:
508
* if it doesn't include
509
* TCL_FILE_EVENTS then we do
510
* nothing. */
511
{
512
FileHandler *filePtr;
513
FileHandlerEvent *fileEvPtr;
514
515
if (!(flags & TCL_FILE_EVENTS)) {
516
return;
517
}
518
for (filePtr = firstFileHandlerPtr; filePtr != NULL;
519
filePtr = filePtr->nextPtr) {
520
if (filePtr->mask != 0) {
521
filePtr->readyMask = Tcl_FileReady(filePtr->file, filePtr->mask);
522
if (filePtr->readyMask != 0) {
523
fileEvPtr = (FileHandlerEvent *) ckalloc(
524
sizeof(FileHandlerEvent));
525
fileEvPtr->header.proc = FileHandlerEventProc;
526
fileEvPtr->file = filePtr->file;
527
Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
528
}
529
}
530
}
531
}
532
533
/*
534
*----------------------------------------------------------------------
535
*
536
* FileHandlerEventProc --
537
*
538
* This procedure is called by Tcl_DoOneEvent when a file event
539
* reaches the front of the event queue. This procedure is responsible
540
* for actually handling the event by invoking the callback for the
541
* file handler.
542
*
543
* Results:
544
* Returns 1 if the event was handled, meaning it should be removed
545
* from the queue. Returns 0 if the event was not handled, meaning
546
* it should stay on the queue. The only time the event isn't
547
* handled is if the TCL_FILE_EVENTS flag bit isn't set.
548
*
549
* Side effects:
550
* Whatever the file handler's callback procedure does
551
*
552
*----------------------------------------------------------------------
553
*/
554
555
static int
556
FileHandlerEventProc(evPtr, flags)
557
Tcl_Event *evPtr; /* Event to service. */
558
int flags; /* Flags that indicate what events to
559
* handle, such as TCL_FILE_EVENTS. */
560
{
561
FileHandler *filePtr;
562
FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
563
int mask;
564
565
if (!(flags & TCL_FILE_EVENTS)) {
566
return 0;
567
}
568
569
/*
570
* Search through the file handlers to find the one whose handle matches
571
* the event. We do this rather than keeping a pointer to the file
572
* handler directly in the event, so that the handler can be deleted
573
* while the event is queued without leaving a dangling pointer.
574
*/
575
576
for (filePtr = firstFileHandlerPtr; filePtr != NULL;
577
filePtr = filePtr->nextPtr) {
578
if (filePtr->file != fileEvPtr->file) {
579
continue;
580
}
581
582
/*
583
* The code is tricky for two reasons:
584
* 1. The file handler's desired events could have changed
585
* since the time when the event was queued, so AND the
586
* ready mask with the desired mask.
587
* 2. The file could have been closed and re-opened since
588
* the time when the event was queued. This is why the
589
* ready mask is stored in the file handler rather than
590
* the queued event: it will be zeroed when a new
591
* file handler is created for the newly opened file.
592
*/
593
594
mask = filePtr->readyMask & filePtr->mask;
595
filePtr->readyMask = 0;
596
if (mask != 0) {
597
(*filePtr->proc)(filePtr->clientData, mask);
598
}
599
break;
600
}
601
return 1;
602
}
603
604
/*
605
*--------------------------------------------------------------
606
*
607
* Tcl_CreateTimerHandler --
608
*
609
* Arrange for a given procedure to be invoked at a particular
610
* time in the future.
611
*
612
* Results:
613
* The return value is a token for the timer event, which
614
* may be used to delete the event before it fires.
615
*
616
* Side effects:
617
* When milliseconds have elapsed, proc will be invoked
618
* exactly once.
619
*
620
*--------------------------------------------------------------
621
*/
622
623
Tcl_TimerToken
624
Tcl_CreateTimerHandler(milliseconds, proc, clientData)
625
int milliseconds; /* How many milliseconds to wait
626
* before invoking proc. */
627
Tcl_TimerProc *proc; /* Procedure to invoke. */
628
ClientData clientData; /* Arbitrary data to pass to proc. */
629
{
630
register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
631
static int id = 0;
632
633
if (!timerEventSourceCreated) {
634
timerEventSourceCreated = 1;
635
Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,
636
(ClientData) NULL);
637
Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL);
638
}
639
640
timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
641
642
/*
643
* Compute when the event should fire.
644
*/
645
646
TclpGetTime(&timerHandlerPtr->time);
647
timerHandlerPtr->time.sec += milliseconds/1000;
648
timerHandlerPtr->time.usec += (milliseconds%1000)*1000;
649
if (timerHandlerPtr->time.usec >= 1000000) {
650
timerHandlerPtr->time.usec -= 1000000;
651
timerHandlerPtr->time.sec += 1;
652
}
653
654
/*
655
* Fill in other fields for the event.
656
*/
657
658
timerHandlerPtr->proc = proc;
659
timerHandlerPtr->clientData = clientData;
660
id++;
661
timerHandlerPtr->token = (Tcl_TimerToken) id;
662
663
/*
664
* Add the event to the queue in the correct position
665
* (ordered by event firing time).
666
*/
667
668
for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
669
prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
670
if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
671
|| ((tPtr2->time.sec == timerHandlerPtr->time.sec)
672
&& (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
673
break;
674
}
675
}
676
timerHandlerPtr->nextPtr = tPtr2;
677
if (prevPtr == NULL) {
678
firstTimerHandlerPtr = timerHandlerPtr;
679
} else {
680
prevPtr->nextPtr = timerHandlerPtr;
681
}
682
return timerHandlerPtr->token;
683
}
684
685
/*
686
*--------------------------------------------------------------
687
*
688
* Tcl_DeleteTimerHandler --
689
*
690
* Delete a previously-registered timer handler.
691
*
692
* Results:
693
* None.
694
*
695
* Side effects:
696
* Destroy the timer callback identified by TimerToken,
697
* so that its associated procedure will not be called.
698
* If the callback has already fired, or if the given
699
* token doesn't exist, then nothing happens.
700
*
701
*--------------------------------------------------------------
702
*/
703
704
void
705
Tcl_DeleteTimerHandler(token)
706
Tcl_TimerToken token; /* Result previously returned by
707
* Tcl_DeleteTimerHandler. */
708
{
709
register TimerHandler *timerHandlerPtr, *prevPtr;
710
711
for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;
712
timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
713
timerHandlerPtr = timerHandlerPtr->nextPtr) {
714
if (timerHandlerPtr->token != token) {
715
continue;
716
}
717
if (prevPtr == NULL) {
718
firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
719
} else {
720
prevPtr->nextPtr = timerHandlerPtr->nextPtr;
721
}
722
ckfree((char *) timerHandlerPtr);
723
return;
724
}
725
}
726
727
/*
728
*--------------------------------------------------------------
729
*
730
* Tcl_CreateModalTimeout --
731
*
732
* Arrange for a given procedure to be invoked at a particular
733
* time in the future, independently of all other timer events.
734
*
735
* Results:
736
* None.
737
*
738
* Side effects:
739
* When milliseconds have elapsed, proc will be invoked
740
* exactly once.
741
*
742
*--------------------------------------------------------------
743
*/
744
745
void
746
Tcl_CreateModalTimeout(milliseconds, proc, clientData)
747
int milliseconds; /* How many milliseconds to wait
748
* before invoking proc. */
749
Tcl_TimerProc *proc; /* Procedure to invoke. */
750
ClientData clientData; /* Arbitrary data to pass to proc. */
751
{
752
TimerHandler *timerHandlerPtr;
753
754
if (!timerEventSourceCreated) {
755
timerEventSourceCreated = 1;
756
Tcl_CreateEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,
757
(ClientData) NULL);
758
Tcl_CreateExitHandler(TimerHandlerExitProc, (ClientData) NULL);
759
}
760
761
timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
762
763
/*
764
* Compute when the timeout should fire and fill in the other fields
765
* of the handler.
766
*/
767
768
TclpGetTime(&timerHandlerPtr->time);
769
timerHandlerPtr->time.sec += milliseconds/1000;
770
timerHandlerPtr->time.usec += (milliseconds%1000)*1000;
771
if (timerHandlerPtr->time.usec >= 1000000) {
772
timerHandlerPtr->time.usec -= 1000000;
773
timerHandlerPtr->time.sec += 1;
774
}
775
timerHandlerPtr->proc = proc;
776
timerHandlerPtr->clientData = clientData;
777
778
/*
779
* Push the handler on the top of the modal stack.
780
*/
781
782
timerHandlerPtr->nextPtr = firstModalHandlerPtr;
783
firstModalHandlerPtr = timerHandlerPtr;
784
}
785
786
/*
787
*--------------------------------------------------------------
788
*
789
* Tcl_DeleteModalTimeout --
790
*
791
* Remove the topmost modal timer handler from the stack of
792
* modal handlers.
793
*
794
* Results:
795
* None.
796
*
797
* Side effects:
798
* Destroys the topmost modal timeout handler, which must
799
* match proc and clientData.
800
*
801
*--------------------------------------------------------------
802
*/
803
804
void
805
Tcl_DeleteModalTimeout(proc, clientData)
806
Tcl_TimerProc *proc; /* Callback procedure for the timeout. */
807
ClientData clientData; /* Arbitrary data to pass to proc. */
808
{
809
TimerHandler *timerHandlerPtr;
810
811
timerHandlerPtr = firstModalHandlerPtr;
812
firstModalHandlerPtr = timerHandlerPtr->nextPtr;
813
if ((timerHandlerPtr->proc != proc)
814
|| (timerHandlerPtr->clientData != clientData)) {
815
panic("Tcl_DeleteModalTimeout found timeout stack corrupted");
816
}
817
ckfree((char *) timerHandlerPtr);
818
}
819
820
/*
821
*----------------------------------------------------------------------
822
*
823
* TimerHandlerSetupProc --
824
*
825
* This procedure is part of the "event source" for timers.
826
* It is invoked by Tcl_DoOneEvent before it calls select (or
827
* whatever it uses to wait).
828
*
829
* Results:
830
* None.
831
*
832
* Side effects:
833
* Tells the notifier how long to sleep if it decides to block.
834
*
835
*----------------------------------------------------------------------
836
*/
837
838
static void
839
TimerHandlerSetupProc(clientData, flags)
840
ClientData clientData; /* Not used. */
841
int flags; /* Flags passed to Tk_DoOneEvent:
842
* if it doesn't include
843
* TCL_TIMER_EVENTS then we only
844
* consider modal timers. */
845
{
846
TimerHandler *timerHandlerPtr, *tPtr2;
847
Tcl_Time blockTime;
848
849
/*
850
* Find the timer handler (regular or modal) that fires first.
851
*/
852
853
timerHandlerPtr = firstTimerHandlerPtr;
854
if (!(flags & TCL_TIMER_EVENTS)) {
855
timerHandlerPtr = NULL;
856
}
857
if (timerHandlerPtr != NULL) {
858
tPtr2 = firstModalHandlerPtr;
859
if (tPtr2 != NULL) {
860
if ((timerHandlerPtr->time.sec > tPtr2->time.sec)
861
|| ((timerHandlerPtr->time.sec == tPtr2->time.sec)
862
&& (timerHandlerPtr->time.usec > tPtr2->time.usec))) {
863
timerHandlerPtr = tPtr2;
864
}
865
}
866
} else {
867
timerHandlerPtr = firstModalHandlerPtr;
868
}
869
if (timerHandlerPtr == NULL) {
870
return;
871
}
872
873
TclpGetTime(&blockTime);
874
blockTime.sec = timerHandlerPtr->time.sec - blockTime.sec;
875
blockTime.usec = timerHandlerPtr->time.usec - blockTime.usec;
876
if (blockTime.usec < 0) {
877
blockTime.sec -= 1;
878
blockTime.usec += 1000000;
879
}
880
if (blockTime.sec < 0) {
881
blockTime.sec = 0;
882
blockTime.usec = 0;
883
}
884
Tcl_SetMaxBlockTime(&blockTime);
885
}
886
887
/*
888
*----------------------------------------------------------------------
889
*
890
* TimerHandlerCheckProc --
891
*
892
* This procedure is the second part of the "event source" for
893
* file handlers. It is invoked by Tcl_DoOneEvent after it calls
894
* select (or whatever it uses to wait for events).
895
*
896
* Results:
897
* None.
898
*
899
* Side effects:
900
* Makes entries on the Tcl event queue for each file that is
901
* now ready.
902
*
903
*----------------------------------------------------------------------
904
*/
905
906
static void
907
TimerHandlerCheckProc(clientData, flags)
908
ClientData clientData; /* Not used. */
909
int flags; /* Flags passed to Tk_DoOneEvent:
910
* if it doesn't include
911
* TCL_TIMER_EVENTS then we only
912
* consider modal timeouts. */
913
{
914
TimerHandler *timerHandlerPtr;
915
TimerEvent *timerEvPtr;
916
int triggered, gotTime;
917
Tcl_Time curTime;
918
919
triggered = 0;
920
gotTime = 0;
921
timerHandlerPtr = firstTimerHandlerPtr;
922
if ((flags & TCL_TIMER_EVENTS) && (timerHandlerPtr != NULL)) {
923
TclpGetTime(&curTime);
924
gotTime = 1;
925
if ((timerHandlerPtr->time.sec < curTime.sec)
926
|| ((timerHandlerPtr->time.sec == curTime.sec)
927
&& (timerHandlerPtr->time.usec <= curTime.usec))) {
928
triggered = 1;
929
}
930
}
931
timerHandlerPtr = firstModalHandlerPtr;
932
if (timerHandlerPtr != NULL) {
933
if (!gotTime) {
934
TclpGetTime(&curTime);
935
}
936
if ((timerHandlerPtr->time.sec < curTime.sec)
937
|| ((timerHandlerPtr->time.sec == curTime.sec)
938
&& (timerHandlerPtr->time.usec <= curTime.usec))) {
939
triggered = 1;
940
}
941
}
942
if (triggered) {
943
timerEvPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent));
944
timerEvPtr->header.proc = TimerHandlerEventProc;
945
timerEvPtr->time.sec = curTime.sec;
946
timerEvPtr->time.usec = curTime.usec;
947
Tcl_QueueEvent((Tcl_Event *) timerEvPtr, TCL_QUEUE_TAIL);
948
}
949
}
950
951
/*
952
*----------------------------------------------------------------------
953
*
954
* TimerHandlerExitProc --
955
*
956
* Callback invoked during exit cleanup to destroy the timer event
957
* source.
958
*
959
* Results:
960
* None.
961
*
962
* Side effects:
963
* Destroys the timer event source.
964
*
965
*----------------------------------------------------------------------
966
*/
967
968
/* ARGSUSED */
969
static void
970
TimerHandlerExitProc(clientData)
971
ClientData clientData; /* Not used. */
972
{
973
Tcl_DeleteEventSource(TimerHandlerSetupProc, TimerHandlerCheckProc,
974
(ClientData) NULL);
975
}
976
977
/*
978
*----------------------------------------------------------------------
979
*
980
* TimerHandlerEventProc --
981
*
982
* This procedure is called by Tcl_DoOneEvent when a timer event
983
* reaches the front of the event queue. This procedure handles
984
* the event by invoking the callbacks for all timers that are
985
* ready.
986
*
987
* Results:
988
* Returns 1 if the event was handled, meaning it should be removed
989
* from the queue. Returns 0 if the event was not handled, meaning
990
* it should stay on the queue. The only time the event isn't
991
* handled is if the TCL_TIMER_EVENTS flag bit isn't set.
992
*
993
* Side effects:
994
* Whatever the timer handler callback procedures do.
995
*
996
*----------------------------------------------------------------------
997
*/
998
999
static int
1000
TimerHandlerEventProc(evPtr, flags)
1001
Tcl_Event *evPtr; /* Event to service. */
1002
int flags; /* Flags that indicate what events to
1003
* handle, such as TCL_FILE_EVENTS. */
1004
{
1005
TimerHandler *timerHandlerPtr;
1006
TimerEvent *timerEvPtr = (TimerEvent *) evPtr;
1007
1008
/*
1009
* Invoke the current modal timeout first, if there is one and
1010
* it has triggered.
1011
*/
1012
1013
timerHandlerPtr = firstModalHandlerPtr;
1014
if (firstModalHandlerPtr != NULL) {
1015
if ((timerHandlerPtr->time.sec < timerEvPtr->time.sec)
1016
|| ((timerHandlerPtr->time.sec == timerEvPtr->time.sec)
1017
&& (timerHandlerPtr->time.usec <= timerEvPtr->time.usec))) {
1018
(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
1019
}
1020
}
1021
1022
/*
1023
* Invoke any normal timers that have fired.
1024
*/
1025
1026
if (!(flags & TCL_TIMER_EVENTS)) {
1027
return 1;
1028
}
1029
1030
while (1) {
1031
timerHandlerPtr = firstTimerHandlerPtr;
1032
if (timerHandlerPtr == NULL) {
1033
break;
1034
}
1035
if ((timerHandlerPtr->time.sec > timerEvPtr->time.sec)
1036
|| ((timerHandlerPtr->time.sec == timerEvPtr->time.sec)
1037
&& (timerHandlerPtr->time.usec >= timerEvPtr->time.usec))) {
1038
break;
1039
}
1040
1041
/*
1042
* Remove the handler from the queue before invoking it,
1043
* to avoid potential reentrancy problems.
1044
*/
1045
1046
firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
1047
(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
1048
ckfree((char *) timerHandlerPtr);
1049
}
1050
return 1;
1051
}
1052
1053
/*
1054
*--------------------------------------------------------------
1055
*
1056
* Tcl_DoWhenIdle --
1057
*
1058
* Arrange for proc to be invoked the next time the system is
1059
* idle (i.e., just before the next time that Tcl_DoOneEvent
1060
* would have to wait for something to happen).
1061
*
1062
* Results:
1063
* None.
1064
*
1065
* Side effects:
1066
* Proc will eventually be called, with clientData as argument.
1067
* See the manual entry for details.
1068
*
1069
*--------------------------------------------------------------
1070
*/
1071
1072
void
1073
Tcl_DoWhenIdle(proc, clientData)
1074
Tcl_IdleProc *proc; /* Procedure to invoke. */
1075
ClientData clientData; /* Arbitrary value to pass to proc. */
1076
{
1077
register IdleHandler *idlePtr;
1078
1079
idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
1080
idlePtr->proc = proc;
1081
idlePtr->clientData = clientData;
1082
idlePtr->generation = idleGeneration;
1083
idlePtr->nextPtr = NULL;
1084
if (lastIdlePtr == NULL) {
1085
idleList = idlePtr;
1086
} else {
1087
lastIdlePtr->nextPtr = idlePtr;
1088
}
1089
lastIdlePtr = idlePtr;
1090
}
1091
1092
/*
1093
*----------------------------------------------------------------------
1094
*
1095
* Tcl_CancelIdleCall --
1096
*
1097
* If there are any when-idle calls requested to a given procedure
1098
* with given clientData, cancel all of them.
1099
*
1100
* Results:
1101
* None.
1102
*
1103
* Side effects:
1104
* If the proc/clientData combination were on the when-idle list,
1105
* they are removed so that they will never be called.
1106
*
1107
*----------------------------------------------------------------------
1108
*/
1109
1110
void
1111
Tcl_CancelIdleCall(proc, clientData)
1112
Tcl_IdleProc *proc; /* Procedure that was previously registered. */
1113
ClientData clientData; /* Arbitrary value to pass to proc. */
1114
{
1115
register IdleHandler *idlePtr, *prevPtr;
1116
IdleHandler *nextPtr;
1117
1118
for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
1119
prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
1120
while ((idlePtr->proc == proc)
1121
&& (idlePtr->clientData == clientData)) {
1122
nextPtr = idlePtr->nextPtr;
1123
ckfree((char *) idlePtr);
1124
idlePtr = nextPtr;
1125
if (prevPtr == NULL) {
1126
idleList = idlePtr;
1127
} else {
1128
prevPtr->nextPtr = idlePtr;
1129
}
1130
if (idlePtr == NULL) {
1131
lastIdlePtr = prevPtr;
1132
return;
1133
}
1134
}
1135
}
1136
}
1137
1138
/*
1139
*----------------------------------------------------------------------
1140
*
1141
* TclIdlePending --
1142
*
1143
* This function is called by the notifier subsystem to determine
1144
* whether there are any idle handlers currently scheduled.
1145
*
1146
* Results:
1147
* Returns 0 if the idle list is empty, otherwise it returns 1.
1148
*
1149
* Side effects:
1150
* None.
1151
*
1152
*----------------------------------------------------------------------
1153
*/
1154
1155
int
1156
TclIdlePending()
1157
{
1158
return (idleList == NULL) ? 0 : 1;
1159
}
1160
1161
/*
1162
*----------------------------------------------------------------------
1163
*
1164
* TclServiceIdle --
1165
*
1166
* This procedure is invoked by the notifier when it becomes idle.
1167
*
1168
* Results:
1169
* The return value is 1 if the procedure actually found an idle
1170
* handler to invoke. If no handler was found then 0 is returned.
1171
*
1172
* Side effects:
1173
* Invokes all pending idle handlers.
1174
*
1175
*----------------------------------------------------------------------
1176
*/
1177
1178
int
1179
TclServiceIdle()
1180
{
1181
IdleHandler *idlePtr;
1182
int oldGeneration;
1183
int foundIdle;
1184
1185
if (idleList == NULL) {
1186
return 0;
1187
}
1188
1189
foundIdle = 0;
1190
oldGeneration = idleGeneration;
1191
idleGeneration++;
1192
1193
/*
1194
* The code below is trickier than it may look, for the following
1195
* reasons:
1196
*
1197
* 1. New handlers can get added to the list while the current
1198
* one is being processed. If new ones get added, we don't
1199
* want to process them during this pass through the list (want
1200
* to check for other work to do first). This is implemented
1201
* using the generation number in the handler: new handlers
1202
* will have a different generation than any of the ones currently
1203
* on the list.
1204
* 2. The handler can call Tcl_DoOneEvent, so we have to remove
1205
* the handler from the list before calling it. Otherwise an
1206
* infinite loop could result.
1207
* 3. Tcl_CancelIdleCall can be called to remove an element from
1208
* the list while a handler is executing, so the list could
1209
* change structure during the call.
1210
*/
1211
1212
for (idlePtr = idleList;
1213
((idlePtr != NULL)
1214
&& ((oldGeneration - idlePtr->generation) >= 0));
1215
idlePtr = idleList) {
1216
idleList = idlePtr->nextPtr;
1217
if (idleList == NULL) {
1218
lastIdlePtr = NULL;
1219
}
1220
foundIdle = 1;
1221
(*idlePtr->proc)(idlePtr->clientData);
1222
ckfree((char *) idlePtr);
1223
}
1224
1225
return foundIdle;
1226
}
1227
1228
/*
1229
*----------------------------------------------------------------------
1230
*
1231
* Tcl_BackgroundError --
1232
*
1233
* This procedure is invoked to handle errors that occur in Tcl
1234
* commands that are invoked in "background" (e.g. from event or
1235
* timer bindings).
1236
*
1237
* Results:
1238
* None.
1239
*
1240
* Side effects:
1241
* The command "bgerror" is invoked later as an idle handler to
1242
* process the error, passing it the error message. If that fails,
1243
* then an error message is output on stderr.
1244
*
1245
*----------------------------------------------------------------------
1246
*/
1247
1248
void
1249
Tcl_BackgroundError(interp)
1250
Tcl_Interp *interp; /* Interpreter in which an error has
1251
* occurred. */
1252
{
1253
BgError *errPtr;
1254
char *varValue;
1255
ErrAssocData *assocPtr;
1256
1257
/*
1258
* The Tcl_AddErrorInfo call below (with an empty string) ensures that
1259
* errorInfo gets properly set. It's needed in cases where the error
1260
* came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;
1261
* in these cases errorInfo still won't have been set when this
1262
* procedure is called.
1263
*/
1264
1265
Tcl_AddErrorInfo(interp, "");
1266
errPtr = (BgError *) ckalloc(sizeof(BgError));
1267
errPtr->interp = interp;
1268
errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result)
1269
+ 1));
1270
strcpy(errPtr->errorMsg, interp->result);
1271
varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
1272
if (varValue == NULL) {
1273
varValue = errPtr->errorMsg;
1274
}
1275
errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
1276
strcpy(errPtr->errorInfo, varValue);
1277
varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
1278
if (varValue == NULL) {
1279
varValue = "";
1280
}
1281
errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
1282
strcpy(errPtr->errorCode, varValue);
1283
errPtr->nextPtr = NULL;
1284
1285
assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError",
1286
(Tcl_InterpDeleteProc **) NULL);
1287
if (assocPtr == NULL) {
1288
1289
/*
1290
* This is the first time a background error has occurred in
1291
* this interpreter. Create associated data to keep track of
1292
* pending error reports.
1293
*/
1294
1295
assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData));
1296
assocPtr->firstBgPtr = NULL;
1297
assocPtr->lastBgPtr = NULL;
1298
Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc,
1299
(ClientData) assocPtr);
1300
}
1301
if (assocPtr->firstBgPtr == NULL) {
1302
assocPtr->firstBgPtr = errPtr;
1303
Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr);
1304
} else {
1305
assocPtr->lastBgPtr->nextPtr = errPtr;
1306
}
1307
assocPtr->lastBgPtr = errPtr;
1308
Tcl_ResetResult(interp);
1309
}
1310
1311
/*
1312
*----------------------------------------------------------------------
1313
*
1314
* HandleBgErrors --
1315
*
1316
* This procedure is invoked as an idle handler to process all of
1317
* the accumulated background errors.
1318
*
1319
* Results:
1320
* None.
1321
*
1322
* Side effects:
1323
* Depends on what actions "bgerror" takes for the errors.
1324
*
1325
*----------------------------------------------------------------------
1326
*/
1327
1328
static void
1329
HandleBgErrors(clientData)
1330
ClientData clientData; /* Pointer to ErrAssocData structure. */
1331
{
1332
Tcl_Interp *interp;
1333
char *command;
1334
char *argv[2];
1335
int code;
1336
BgError *errPtr;
1337
ErrAssocData *assocPtr = (ErrAssocData *) clientData;
1338
Tcl_Channel errChannel;
1339
1340
while (assocPtr->firstBgPtr != NULL) {
1341
interp = assocPtr->firstBgPtr->interp;
1342
if (interp == NULL) {
1343
goto doneWithReport;
1344
}
1345
1346
/*
1347
* Restore important state variables to what they were at
1348
* the time the error occurred.
1349
*/
1350
1351
Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo,
1352
TCL_GLOBAL_ONLY);
1353
Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode,
1354
TCL_GLOBAL_ONLY);
1355
1356
/*
1357
* Create and invoke the bgerror command.
1358
*/
1359
1360
argv[0] = "bgerror";
1361
argv[1] = assocPtr->firstBgPtr->errorMsg;
1362
command = Tcl_Merge(2, argv);
1363
Tcl_AllowExceptions(interp);
1364
Tcl_Preserve((ClientData) interp);
1365
code = Tcl_GlobalEval(interp, command);
1366
ckfree(command);
1367
if (code == TCL_ERROR) {
1368
1369
/*
1370
* We have to get the error output channel at the latest possible
1371
* time, because the eval (above) might have changed the channel.
1372
*/
1373
1374
errChannel = Tcl_GetStdChannel(TCL_STDERR);
1375
if (errChannel != (Tcl_Channel) NULL) {
1376
if (strcmp(interp->result,
1377
"\"bgerror\" is an invalid command name or ambiguous abbreviation")
1378
== 0) {
1379
Tcl_Write(errChannel, assocPtr->firstBgPtr->errorInfo, -1);
1380
Tcl_Write(errChannel, "\n", -1);
1381
} else {
1382
Tcl_Write(errChannel,
1383
"bgerror failed to handle background error.\n",
1384
-1);
1385
Tcl_Write(errChannel, " Original error: ", -1);
1386
Tcl_Write(errChannel, assocPtr->firstBgPtr->errorMsg,
1387
-1);
1388
Tcl_Write(errChannel, "\n", -1);
1389
Tcl_Write(errChannel, " Error in bgerror: ", -1);
1390
Tcl_Write(errChannel, interp->result, -1);
1391
Tcl_Write(errChannel, "\n", -1);
1392
}
1393
Tcl_Flush(errChannel);
1394
}
1395
} else if (code == TCL_BREAK) {
1396
1397
/*
1398
* Break means cancel any remaining error reports for this
1399
* interpreter.
1400
*/
1401
1402
for (errPtr = assocPtr->firstBgPtr; errPtr != NULL;
1403
errPtr = errPtr->nextPtr) {
1404
if (errPtr->interp == interp) {
1405
errPtr->interp = NULL;
1406
}
1407
}
1408
}
1409
1410
Tcl_Release((ClientData) interp);
1411
1412
/*
1413
* Discard the command and the information about the error report.
1414
*/
1415
1416
doneWithReport:
1417
ckfree(assocPtr->firstBgPtr->errorMsg);
1418
ckfree(assocPtr->firstBgPtr->errorInfo);
1419
ckfree(assocPtr->firstBgPtr->errorCode);
1420
errPtr = assocPtr->firstBgPtr->nextPtr;
1421
ckfree((char *) assocPtr->firstBgPtr);
1422
assocPtr->firstBgPtr = errPtr;
1423
}
1424
assocPtr->lastBgPtr = NULL;
1425
}
1426
1427
/*
1428
*----------------------------------------------------------------------
1429
*
1430
* BgErrorDeleteProc --
1431
*
1432
* This procedure is associated with the "tclBgError" assoc data
1433
* for an interpreter; it is invoked when the interpreter is
1434
* deleted in order to free the information assoicated with any
1435
* pending error reports.
1436
*
1437
* Results:
1438
* None.
1439
*
1440
* Side effects:
1441
* Background error information is freed: if there were any
1442
* pending error reports, they are cancelled.
1443
*
1444
*----------------------------------------------------------------------
1445
*/
1446
1447
static void
1448
BgErrorDeleteProc(clientData, interp)
1449
ClientData clientData; /* Pointer to ErrAssocData structure. */
1450
Tcl_Interp *interp; /* Interpreter being deleted. */
1451
{
1452
ErrAssocData *assocPtr = (ErrAssocData *) clientData;
1453
BgError *errPtr;
1454
1455
while (assocPtr->firstBgPtr != NULL) {
1456
errPtr = assocPtr->firstBgPtr;
1457
assocPtr->firstBgPtr = errPtr->nextPtr;
1458
ckfree(errPtr->errorMsg);
1459
ckfree(errPtr->errorInfo);
1460
ckfree(errPtr->errorCode);
1461
ckfree((char *) errPtr);
1462
}
1463
ckfree((char *) assocPtr);
1464
Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr);
1465
}
1466
1467
/*
1468
*----------------------------------------------------------------------
1469
*
1470
* Tcl_CreateExitHandler --
1471
*
1472
* Arrange for a given procedure to be invoked just before the
1473
* application exits.
1474
*
1475
* Results:
1476
* None.
1477
*
1478
* Side effects:
1479
* Proc will be invoked with clientData as argument when the
1480
* application exits.
1481
*
1482
*----------------------------------------------------------------------
1483
*/
1484
1485
void
1486
Tcl_CreateExitHandler(proc, clientData)
1487
Tcl_ExitProc *proc; /* Procedure to invoke. */
1488
ClientData clientData; /* Arbitrary value to pass to proc. */
1489
{
1490
ExitHandler *exitPtr;
1491
1492
exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler));
1493
exitPtr->proc = proc;
1494
exitPtr->clientData = clientData;
1495
exitPtr->nextPtr = firstExitPtr;
1496
firstExitPtr = exitPtr;
1497
}
1498
1499
/*
1500
*----------------------------------------------------------------------
1501
*
1502
* Tcl_DeleteExitHandler --
1503
*
1504
* This procedure cancels an existing exit handler matching proc
1505
* and clientData, if such a handler exits.
1506
*
1507
* Results:
1508
* None.
1509
*
1510
* Side effects:
1511
* If there is an exit handler corresponding to proc and clientData
1512
* then it is cancelled; if no such handler exists then nothing
1513
* happens.
1514
*
1515
*----------------------------------------------------------------------
1516
*/
1517
1518
void
1519
Tcl_DeleteExitHandler(proc, clientData)
1520
Tcl_ExitProc *proc; /* Procedure that was previously registered. */
1521
ClientData clientData; /* Arbitrary value to pass to proc. */
1522
{
1523
ExitHandler *exitPtr, *prevPtr;
1524
1525
for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL;
1526
prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
1527
if ((exitPtr->proc == proc)
1528
&& (exitPtr->clientData == clientData)) {
1529
if (prevPtr == NULL) {
1530
firstExitPtr = exitPtr->nextPtr;
1531
} else {
1532
prevPtr->nextPtr = exitPtr->nextPtr;
1533
}
1534
ckfree((char *) exitPtr);
1535
return;
1536
}
1537
}
1538
}
1539
1540
/*
1541
*----------------------------------------------------------------------
1542
*
1543
* Tcl_Exit --
1544
*
1545
* This procedure is called to terminate the application.
1546
*
1547
* Results:
1548
* None.
1549
*
1550
* Side effects:
1551
* All existing exit handlers are invoked, then the application
1552
* ends.
1553
*
1554
*----------------------------------------------------------------------
1555
*/
1556
1557
void
1558
Tcl_Exit(status)
1559
int status; /* Exit status for application; typically
1560
* 0 for normal return, 1 for error return. */
1561
{
1562
ExitHandler *exitPtr;
1563
1564
tclInExit = 1;
1565
for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) {
1566
/*
1567
* Be careful to remove the handler from the list before invoking
1568
* its callback. This protects us against double-freeing if the
1569
* callback should call Tcl_DeleteExitHandler on itself.
1570
*/
1571
1572
firstExitPtr = exitPtr->nextPtr;
1573
(*exitPtr->proc)(exitPtr->clientData);
1574
ckfree((char *) exitPtr);
1575
}
1576
#ifdef TCL_MEM_DEBUG
1577
if (tclMemDumpFileName != NULL) {
1578
Tcl_DumpActiveMemory(tclMemDumpFileName);
1579
}
1580
#endif
1581
tclInExit = 0;
1582
TclPlatformExit(status);
1583
}
1584
1585
/*
1586
*----------------------------------------------------------------------
1587
*
1588
* TclInExit --
1589
*
1590
* Determines if we are in the middle of exit-time cleanup.
1591
*
1592
* Results:
1593
* If we are in the middle of exiting, 1, otherwise 0.
1594
*
1595
* Side effects:
1596
* None.
1597
*
1598
*----------------------------------------------------------------------
1599
*/
1600
1601
int
1602
TclInExit()
1603
{
1604
return tclInExit;
1605
}
1606
1607
/*
1608
*----------------------------------------------------------------------
1609
*
1610
* Tcl_AfterCmd --
1611
*
1612
* This procedure is invoked to process the "after" Tcl command.
1613
* See the user documentation for details on what it does.
1614
*
1615
* Results:
1616
* A standard Tcl result.
1617
*
1618
* Side effects:
1619
* See the user documentation.
1620
*
1621
*----------------------------------------------------------------------
1622
*/
1623
1624
/* ARGSUSED */
1625
int
1626
Tcl_AfterCmd(clientData, interp, argc, argv)
1627
ClientData clientData; /* Points to the "tclAfter" assocData for
1628
* this interpreter, or NULL if the assocData
1629
* hasn't been created yet.*/
1630
Tcl_Interp *interp; /* Current interpreter. */
1631
int argc; /* Number of arguments. */
1632
char **argv; /* Argument strings. */
1633
{
1634
/*
1635
* The variable below is used to generate unique identifiers for
1636
* after commands. This id can wrap around, which can potentially
1637
* cause problems. However, there are not likely to be problems
1638
* in practice, because after commands can only be requested to
1639
* about a month in the future, and wrap-around is unlikely to
1640
* occur in less than about 1-10 years. Thus it's unlikely that
1641
* any old ids will still be around when wrap-around occurs.
1642
*/
1643
1644
static int nextId = 1;
1645
int ms;
1646
AfterInfo *afterPtr;
1647
AfterAssocData *assocPtr = (AfterAssocData *) clientData;
1648
Tcl_CmdInfo cmdInfo;
1649
size_t length;
1650
1651
if (argc < 2) {
1652
Tcl_AppendResult(interp, "wrong # args: should be \"",
1653
argv[0], " option ?arg arg ...?\"", (char *) NULL);
1654
return TCL_ERROR;
1655
}
1656
1657
/*
1658
* Create the "after" information associated for this interpreter,
1659
* if it doesn't already exist. Associate it with the command too,
1660
* so that it will be passed in as the ClientData argument in the
1661
* future.
1662
*/
1663
1664
if (assocPtr == NULL) {
1665
assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
1666
assocPtr->interp = interp;
1667
assocPtr->firstAfterPtr = NULL;
1668
Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
1669
(ClientData) assocPtr);
1670
cmdInfo.proc = Tcl_AfterCmd;
1671
cmdInfo.clientData = (ClientData) assocPtr;
1672
cmdInfo.deleteProc = NULL;
1673
cmdInfo.deleteData = (ClientData) assocPtr;
1674
Tcl_SetCommandInfo(interp, argv[0], &cmdInfo);
1675
}
1676
1677
/*
1678
* Parse the command.
1679
*/
1680
1681
length = strlen(argv[1]);
1682
if (isdigit(UCHAR(argv[1][0]))) {
1683
if (Tcl_GetInt(interp, argv[1], &ms) != TCL_OK) {
1684
return TCL_ERROR;
1685
}
1686
if (ms < 0) {
1687
ms = 0;
1688
}
1689
if (argc == 2) {
1690
Tcl_Sleep(ms);
1691
return TCL_OK;
1692
}
1693
afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
1694
afterPtr->assocPtr = assocPtr;
1695
if (argc == 3) {
1696
afterPtr->command = (char *) ckalloc((unsigned)
1697
(strlen(argv[2]) + 1));
1698
strcpy(afterPtr->command, argv[2]);
1699
} else {
1700
afterPtr->command = Tcl_Concat(argc-2, argv+2);
1701
}
1702
afterPtr->interpType = ((Interp *)interp)->interpType;
1703
afterPtr->id = nextId;
1704
nextId += 1;
1705
afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
1706
(ClientData) afterPtr);
1707
afterPtr->nextPtr = assocPtr->firstAfterPtr;
1708
assocPtr->firstAfterPtr = afterPtr;
1709
sprintf(interp->result, "after#%d", afterPtr->id);
1710
} else if (strncmp(argv[1], "cancel", length) == 0) {
1711
char *arg;
1712
1713
if (argc < 3) {
1714
Tcl_AppendResult(interp, "wrong # args: should be \"",
1715
argv[0], " cancel id|command\"", (char *) NULL);
1716
return TCL_ERROR;
1717
}
1718
if (argc == 3) {
1719
arg = argv[2];
1720
} else {
1721
arg = Tcl_Concat(argc-2, argv+2);
1722
}
1723
for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
1724
afterPtr = afterPtr->nextPtr) {
1725
if (strcmp(afterPtr->command, arg) == 0) {
1726
break;
1727
}
1728
}
1729
if (afterPtr == NULL) {
1730
afterPtr = GetAfterEvent(assocPtr, arg);
1731
}
1732
if (arg != argv[2]) {
1733
ckfree(arg);
1734
}
1735
if (afterPtr != NULL) {
1736
if (afterPtr->token != NULL) {
1737
Tcl_DeleteTimerHandler(afterPtr->token);
1738
} else {
1739
Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
1740
}
1741
FreeAfterPtr(afterPtr);
1742
}
1743
} else if ((strncmp(argv[1], "idle", length) == 0)
1744
&& (length >= 2)) {
1745
if (argc < 3) {
1746
Tcl_AppendResult(interp, "wrong # args: should be \"",
1747
argv[0], " idle script script ...\"", (char *) NULL);
1748
return TCL_ERROR;
1749
}
1750
afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
1751
afterPtr->assocPtr = assocPtr;
1752
if (argc == 3) {
1753
afterPtr->command = (char *) ckalloc((unsigned)
1754
(strlen(argv[2]) + 1));
1755
strcpy(afterPtr->command, argv[2]);
1756
} else {
1757
afterPtr->command = Tcl_Concat(argc-2, argv+2);
1758
}
1759
afterPtr->interpType = ((Interp *)interp)->interpType;
1760
afterPtr->id = nextId;
1761
nextId += 1;
1762
afterPtr->token = NULL;
1763
afterPtr->nextPtr = assocPtr->firstAfterPtr;
1764
assocPtr->firstAfterPtr = afterPtr;
1765
Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
1766
sprintf(interp->result, "after#%d", afterPtr->id);
1767
} else if ((strncmp(argv[1], "info", length) == 0)
1768
&& (length >= 2)) {
1769
if (argc == 2) {
1770
char buffer[30];
1771
1772
for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
1773
afterPtr = afterPtr->nextPtr) {
1774
if (assocPtr->interp == interp) {
1775
sprintf(buffer, "after#%d", afterPtr->id);
1776
Tcl_AppendElement(interp, buffer);
1777
}
1778
}
1779
return TCL_OK;
1780
}
1781
if (argc != 3) {
1782
Tcl_AppendResult(interp, "wrong # args: should be \"",
1783
argv[0], " info ?id?\"", (char *) NULL);
1784
return TCL_ERROR;
1785
}
1786
afterPtr = GetAfterEvent(assocPtr, argv[2]);
1787
if (afterPtr == NULL) {
1788
Tcl_AppendResult(interp, "event \"", argv[2],
1789
"\" doesn't exist", (char *) NULL);
1790
return TCL_ERROR;
1791
}
1792
Tcl_AppendElement(interp, afterPtr->command);
1793
Tcl_AppendElement(interp,
1794
(afterPtr->token == NULL) ? "idle" : "timer");
1795
} else {
1796
Tcl_AppendResult(interp, "bad argument \"", argv[1],
1797
"\": must be cancel, idle, info, or a number",
1798
(char *) NULL);
1799
return TCL_ERROR;
1800
}
1801
return TCL_OK;
1802
}
1803
1804
/*
1805
*----------------------------------------------------------------------
1806
*
1807
* GetAfterEvent --
1808
*
1809
* This procedure parses an "after" id such as "after#4" and
1810
* returns a pointer to the AfterInfo structure.
1811
*
1812
* Results:
1813
* The return value is either a pointer to an AfterInfo structure,
1814
* if one is found that corresponds to "string" and is for interp,
1815
* or NULL if no corresponding after event can be found.
1816
*
1817
* Side effects:
1818
* None.
1819
*
1820
*----------------------------------------------------------------------
1821
*/
1822
1823
static AfterInfo *
1824
GetAfterEvent(assocPtr, string)
1825
AfterAssocData *assocPtr; /* Points to "after"-related information for
1826
* this interpreter. */
1827
char *string; /* Textual identifier for after event, such
1828
* as "after#6". */
1829
{
1830
AfterInfo *afterPtr;
1831
int id;
1832
char *end;
1833
1834
if (strncmp(string, "after#", 6) != 0) {
1835
return NULL;
1836
}
1837
string += 6;
1838
id = strtoul(string, &end, 10);
1839
if ((end == string) || (*end != 0)) {
1840
return NULL;
1841
}
1842
for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
1843
afterPtr = afterPtr->nextPtr) {
1844
if (afterPtr->id == id) {
1845
return afterPtr;
1846
}
1847
}
1848
return NULL;
1849
}
1850
1851
/*
1852
*----------------------------------------------------------------------
1853
*
1854
* AfterProc --
1855
*
1856
* Timer callback to execute commands registered with the
1857
* "after" command.
1858
*
1859
* Results:
1860
* None.
1861
*
1862
* Side effects:
1863
* Executes whatever command was specified. If the command
1864
* returns an error, then the command "bgerror" is invoked
1865
* to process the error; if bgerror fails then information
1866
* about the error is output on stderr.
1867
*
1868
*----------------------------------------------------------------------
1869
*/
1870
1871
static void
1872
AfterProc(clientData)
1873
ClientData clientData; /* Describes command to execute. */
1874
{
1875
AfterInfo *afterPtr = (AfterInfo *) clientData;
1876
AfterAssocData *assocPtr = afterPtr->assocPtr;
1877
AfterInfo *prevPtr;
1878
int result;
1879
Tcl_Interp *interp;
1880
1881
/*
1882
* First remove the callback from our list of callbacks; otherwise
1883
* someone could delete the callback while it's being executed, which
1884
* could cause a core dump.
1885
*/
1886
1887
if (assocPtr->firstAfterPtr == afterPtr) {
1888
assocPtr->firstAfterPtr = afterPtr->nextPtr;
1889
} else {
1890
for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
1891
prevPtr = prevPtr->nextPtr) {
1892
/* Empty loop body. */
1893
}
1894
prevPtr->nextPtr = afterPtr->nextPtr;
1895
}
1896
1897
/*
1898
* Execute the callback.
1899
*/
1900
1901
interp = assocPtr->interp;
1902
Tcl_Preserve((ClientData) interp);
1903
result = Tksh_GlobalEval(interp, afterPtr->command, afterPtr->interpType);
1904
if (result != TCL_OK) {
1905
Tcl_AddErrorInfo(interp, "\n (\"after\" script)");
1906
Tcl_BackgroundError(interp);
1907
}
1908
Tcl_Release((ClientData) interp);
1909
1910
/*
1911
* Free the memory for the callback.
1912
*/
1913
1914
ckfree(afterPtr->command);
1915
ckfree((char *) afterPtr);
1916
}
1917
1918
/*
1919
*----------------------------------------------------------------------
1920
*
1921
* FreeAfterPtr --
1922
*
1923
* This procedure removes an "after" command from the list of
1924
* those that are pending and frees its resources. This procedure
1925
* does *not* cancel the timer handler; if that's needed, the
1926
* caller must do it.
1927
*
1928
* Results:
1929
* None.
1930
*
1931
* Side effects:
1932
* The memory associated with afterPtr is released.
1933
*
1934
*----------------------------------------------------------------------
1935
*/
1936
1937
static void
1938
FreeAfterPtr(afterPtr)
1939
AfterInfo *afterPtr; /* Command to be deleted. */
1940
{
1941
AfterInfo *prevPtr;
1942
AfterAssocData *assocPtr = afterPtr->assocPtr;
1943
1944
if (assocPtr->firstAfterPtr == afterPtr) {
1945
assocPtr->firstAfterPtr = afterPtr->nextPtr;
1946
} else {
1947
for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
1948
prevPtr = prevPtr->nextPtr) {
1949
/* Empty loop body. */
1950
}
1951
prevPtr->nextPtr = afterPtr->nextPtr;
1952
}
1953
ckfree(afterPtr->command);
1954
ckfree((char *) afterPtr);
1955
}
1956
1957
/*
1958
*----------------------------------------------------------------------
1959
*
1960
* AfterCleanupProc --
1961
*
1962
* This procedure is invoked whenever an interpreter is deleted
1963
* to cleanup the AssocData for "tclAfter".
1964
*
1965
* Results:
1966
* None.
1967
*
1968
* Side effects:
1969
* After commands are removed.
1970
*
1971
*----------------------------------------------------------------------
1972
*/
1973
1974
/* ARGSUSED */
1975
static void
1976
AfterCleanupProc(clientData, interp)
1977
ClientData clientData; /* Points to AfterAssocData for the
1978
* interpreter. */
1979
Tcl_Interp *interp; /* Interpreter that is being deleted. */
1980
{
1981
AfterAssocData *assocPtr = (AfterAssocData *) clientData;
1982
AfterInfo *afterPtr;
1983
1984
while (assocPtr->firstAfterPtr != NULL) {
1985
afterPtr = assocPtr->firstAfterPtr;
1986
assocPtr->firstAfterPtr = afterPtr->nextPtr;
1987
if (afterPtr->token != NULL) {
1988
Tcl_DeleteTimerHandler(afterPtr->token);
1989
} else {
1990
Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
1991
}
1992
ckfree(afterPtr->command);
1993
ckfree((char *) afterPtr);
1994
}
1995
ckfree((char *) assocPtr);
1996
}
1997
1998
/*
1999
*----------------------------------------------------------------------
2000
*
2001
* Tcl_VwaitCmd --
2002
*
2003
* This procedure is invoked to process the "vwait" Tcl command.
2004
* See the user documentation for details on what it does.
2005
*
2006
* Results:
2007
* A standard Tcl result.
2008
*
2009
* Side effects:
2010
* See the user documentation.
2011
*
2012
*----------------------------------------------------------------------
2013
*/
2014
2015
/* ARGSUSED */
2016
int
2017
Tcl_VwaitCmd(clientData, interp, argc, argv)
2018
ClientData clientData; /* Not used. */
2019
Tcl_Interp *interp; /* Current interpreter. */
2020
int argc; /* Number of arguments. */
2021
char **argv; /* Argument strings. */
2022
{
2023
int done, foundEvent;
2024
2025
if (argc != 2) {
2026
Tcl_AppendResult(interp, "wrong # args: should be \"",
2027
argv[0], " name\"", (char *) NULL);
2028
return TCL_ERROR;
2029
}
2030
if (Tcl_TraceVar(interp, argv[1],
2031
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2032
VwaitVarProc, (ClientData) &done) != TCL_OK) {
2033
return TCL_ERROR;
2034
};
2035
done = 0;
2036
foundEvent = 1;
2037
while (!done && foundEvent) {
2038
foundEvent = Tcl_DoOneEvent(0);
2039
}
2040
Tcl_UntraceVar(interp, argv[1],
2041
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2042
VwaitVarProc, (ClientData) &done);
2043
2044
/*
2045
* Clear out the interpreter's result, since it may have been set
2046
* by event handlers.
2047
*/
2048
2049
Tcl_ResetResult(interp);
2050
if (!foundEvent) {
2051
Tcl_AppendResult(interp, "can't wait for variable \"", argv[1],
2052
"\": would wait forever", (char *) NULL);
2053
return TCL_ERROR;
2054
}
2055
return TCL_OK;
2056
}
2057
2058
/* ARGSUSED */
2059
static char *
2060
VwaitVarProc(clientData, interp, name1, name2, flags)
2061
ClientData clientData; /* Pointer to integer to set to 1. */
2062
Tcl_Interp *interp; /* Interpreter containing variable. */
2063
char *name1; /* Name of variable. */
2064
char *name2; /* Second part of variable name. */
2065
int flags; /* Information about what happened. */
2066
{
2067
int *donePtr = (int *) clientData;
2068
2069
*donePtr = 1;
2070
return (char *) NULL;
2071
}
2072
2073
/*
2074
*----------------------------------------------------------------------
2075
*
2076
* Tcl_UpdateCmd --
2077
*
2078
* This procedure is invoked to process the "update" Tcl command.
2079
* See the user documentation for details on what it does.
2080
*
2081
* Results:
2082
* A standard Tcl result.
2083
*
2084
* Side effects:
2085
* See the user documentation.
2086
*
2087
*----------------------------------------------------------------------
2088
*/
2089
2090
/* ARGSUSED */
2091
int
2092
Tcl_UpdateCmd(clientData, interp, argc, argv)
2093
ClientData clientData; /* Not used. */
2094
Tcl_Interp *interp; /* Current interpreter. */
2095
int argc; /* Number of arguments. */
2096
char **argv; /* Argument strings. */
2097
{
2098
int flags = 0; /* Initialization needed only to stop
2099
* compiler warnings. */
2100
2101
if (argc == 1) {
2102
flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;
2103
} else if (argc == 2) {
2104
if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {
2105
Tcl_AppendResult(interp, "bad option \"", argv[1],
2106
"\": must be idletasks", (char *) NULL);
2107
return TCL_ERROR;
2108
}
2109
flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
2110
} else {
2111
Tcl_AppendResult(interp, "wrong # args: should be \"",
2112
argv[0], " ?idletasks?\"", (char *) NULL);
2113
return TCL_ERROR;
2114
}
2115
2116
while (Tcl_DoOneEvent(flags) != 0) {
2117
/* Empty loop body */
2118
}
2119
2120
/*
2121
* Must clear the interpreter's result because event handlers could
2122
* have executed commands.
2123
*/
2124
2125
Tcl_ResetResult(interp);
2126
return TCL_OK;
2127
}
2128
2129
/*
2130
*----------------------------------------------------------------------
2131
*
2132
* TclWaitForFile --
2133
*
2134
* This procedure waits synchronously for a file to become readable
2135
* or writable, with an optional timeout.
2136
*
2137
* Results:
2138
* The return value is an OR'ed combination of TCL_READABLE,
2139
* TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
2140
* that are present on file at the time of the return. This
2141
* procedure will not return until either "timeout" milliseconds
2142
* have elapsed or at least one of the conditions given by mask
2143
* has occurred for file (a return value of 0 means that a timeout
2144
* occurred). No normal events will be serviced during the
2145
* execution of this procedure.
2146
*
2147
* Side effects:
2148
* Time passes.
2149
*
2150
*----------------------------------------------------------------------
2151
*/
2152
2153
int
2154
TclWaitForFile(file, mask, timeout)
2155
Tcl_File file; /* Handle for file on which to wait. */
2156
int mask; /* What to wait for: OR'ed combination of
2157
* TCL_READABLE, TCL_WRITABLE, and
2158
* TCL_EXCEPTION. */
2159
int timeout; /* Maximum amount of time to wait for one
2160
* of the conditions in mask to occur, in
2161
* milliseconds. A value of 0 means don't
2162
* wait at all, and a value of -1 means
2163
* wait forever. */
2164
{
2165
Tcl_Time abortTime, now, blockTime;
2166
int present;
2167
2168
/*
2169
* If there is a non-zero finite timeout, compute the time when
2170
* we give up.
2171
*/
2172
2173
if (timeout > 0) {
2174
TclpGetTime(&now);
2175
abortTime.sec = now.sec + timeout/1000;
2176
abortTime.usec = now.usec + (timeout%1000)*1000;
2177
if (abortTime.usec >= 1000000) {
2178
abortTime.usec -= 1000000;
2179
abortTime.sec += 1;
2180
}
2181
}
2182
2183
/*
2184
* Loop in a mini-event loop of our own, waiting for either the
2185
* file to become ready or a timeout to occur.
2186
*/
2187
2188
while (1) {
2189
Tcl_WatchFile(file, mask);
2190
if (timeout > 0) {
2191
blockTime.sec = abortTime.sec - now.sec;
2192
blockTime.usec = abortTime.usec - now.usec;
2193
if (blockTime.usec < 0) {
2194
blockTime.sec -= 1;
2195
blockTime.usec += 1000000;
2196
}
2197
if (blockTime.sec < 0) {
2198
blockTime.sec = 0;
2199
blockTime.usec = 0;
2200
}
2201
Tcl_WaitForEvent(&blockTime);
2202
} else if (timeout == 0) {
2203
blockTime.sec = 0;
2204
blockTime.usec = 0;
2205
Tcl_WaitForEvent(&blockTime);
2206
} else {
2207
Tcl_WaitForEvent((Tcl_Time *) NULL);
2208
}
2209
present = Tcl_FileReady(file, mask);
2210
if (present != 0) {
2211
break;
2212
}
2213
if (timeout == 0) {
2214
break;
2215
}
2216
TclpGetTime(&now);
2217
if ((abortTime.sec < now.sec)
2218
|| ((abortTime.sec == now.sec)
2219
&& (abortTime.usec <= now.usec))) {
2220
break;
2221
}
2222
}
2223
return present;
2224
}
2225
2226