resetting manifest requested domain to floor
[platform/upstream/expect.git] / exp_event.c
1 /* exp_event.c - event interface for Expect
2
3 Written by: Don Libes, NIST, 2/6/90
4
5 I hereby place this software in the public domain.  However, the author and
6 NIST would appreciate credit if this program or parts of it are used.
7
8 */
9
10 #include "expect_cf.h"
11 #include <stdio.h>
12 #include <errno.h>
13 #include <sys/types.h>
14
15 #ifdef HAVE_SYS_WAIT_H
16 #include <sys/wait.h>
17 #endif
18
19 #ifdef HAVE_PTYTRAP
20 #  include <sys/ptyio.h>
21 #endif
22
23 #include "tcl.h"
24 #include "exp_prog.h"
25 #include "exp_command.h"        /* for ExpState defs */
26 #include "exp_event.h"
27
28 typedef struct ThreadSpecificData {
29     int rr;             /* round robin ptr */
30 } ThreadSpecificData;
31
32 static Tcl_ThreadDataKey dataKey;
33
34 void
35 exp_event_disarm_bg(esPtr)
36 ExpState *esPtr;
37 {
38     Tcl_DeleteChannelHandler(esPtr->channel,exp_background_channelhandler,(ClientData)esPtr);
39 }
40
41 static void
42 exp_arm_background_channelhandler_force(esPtr)
43 ExpState *esPtr;
44 {
45     Tcl_CreateChannelHandler(esPtr->channel,
46             TCL_READABLE|TCL_EXCEPTION,
47             exp_background_channelhandler,
48             (ClientData)esPtr);
49
50     esPtr->bg_status = armed;
51 }
52
53 void
54 exp_arm_background_channelhandler(esPtr)
55 ExpState *esPtr;
56 {
57     switch (esPtr->bg_status) {
58         case unarmed:
59             exp_arm_background_channelhandler_force(esPtr);
60             break;
61         case disarm_req_while_blocked:
62             esPtr->bg_status = blocked; /* forget request */
63             break;
64         case armed:
65         case blocked:
66             /* do nothing */
67             break;
68     }
69 }
70
71 void
72 exp_disarm_background_channelhandler(esPtr)
73 ExpState *esPtr;
74 {
75     switch (esPtr->bg_status) {
76         case blocked:
77             esPtr->bg_status = disarm_req_while_blocked;
78             break;
79         case armed:
80             esPtr->bg_status = unarmed;
81             exp_event_disarm_bg(esPtr);
82             break;
83         case disarm_req_while_blocked:
84         case unarmed:
85             /* do nothing */
86             break;
87     }
88 }
89
90 /* ignore block status and forcibly disarm handler - called from exp_close. */
91 /* After exp_close returns, we will not have an opportunity to disarm */
92 /* because the fd will be invalid, so we force it here. */
93 void
94 exp_disarm_background_channelhandler_force(esPtr)
95 ExpState *esPtr;
96 {
97     switch (esPtr->bg_status) {
98         case blocked:
99         case disarm_req_while_blocked:
100         case armed:
101             esPtr->bg_status = unarmed;
102             exp_event_disarm_bg(esPtr);
103             break;
104         case unarmed:
105             /* do nothing */
106             break;
107     }
108 }
109
110 /* this can only be called at the end of the bg handler in which */
111 /* case we know the status is some kind of "blocked" */
112 void
113 exp_unblock_background_channelhandler(esPtr)
114     ExpState *esPtr;
115 {
116     switch (esPtr->bg_status) {
117         case blocked:
118             exp_arm_background_channelhandler_force(esPtr);
119             break;
120         case disarm_req_while_blocked:
121             exp_disarm_background_channelhandler_force(esPtr);
122             break;
123     }
124 }
125
126 /* this can only be called at the beginning of the bg handler in which */
127 /* case we know the status must be "armed" */
128 void
129 exp_block_background_channelhandler(esPtr)
130 ExpState *esPtr;
131 {
132     esPtr->bg_status = blocked;
133     exp_event_disarm_bg(esPtr);
134 }
135
136
137 /*ARGSUSED*/
138 static void
139 exp_timehandler(clientData)
140 ClientData clientData;
141 {
142     *(int *)clientData = TRUE;  
143 }
144
145 static void exp_channelhandler(clientData,mask)
146 ClientData clientData;
147 int mask;
148 {
149     ExpState *esPtr = (ExpState *)clientData;
150
151     esPtr->notified = TRUE;
152     esPtr->notifiedMask = mask;
153
154     exp_event_disarm_fg(esPtr);
155 }
156
157 void
158 exp_event_disarm_fg(esPtr)
159 ExpState *esPtr;
160 {
161     /*printf("DeleteChannelHandler: %s\r\n",esPtr->name);*/
162     Tcl_DeleteChannelHandler(esPtr->channel,exp_channelhandler,(ClientData)esPtr);
163
164     /* remember that ChannelHandler has been disabled so that */
165     /* it can be turned on for fg expect's as well as bg */
166     esPtr->fg_armed = FALSE;
167 }
168
169 /* returns status, one of EOF, TIMEOUT, ERROR or DATA */
170 /* can now return RECONFIGURE, too */
171 /*ARGSUSED*/
172 int exp_get_next_event(interp,esPtrs,n,esPtrOut,timeout,key)
173 Tcl_Interp *interp;
174 ExpState *(esPtrs[]);
175 int n;                  /* # of esPtrs */
176 ExpState **esPtrOut;    /* 1st ready esPtr, not set if none */
177 int timeout;            /* seconds */
178 int key;
179 {
180     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
181
182     ExpState *esPtr;
183     int i;      /* index into in-array */
184 #ifdef HAVE_PTYTRAP
185     struct request_info ioctl_info;
186 #endif
187
188     int old_configure_count = exp_configure_count;
189
190     int timerFired = FALSE;
191     Tcl_TimerToken timerToken = 0;/* handle to Tcl timehandler descriptor */
192     /* We must delete any timer before returning.  Doing so throughout
193      * the code makes it unreadable; isolate the unreadable nonsense here.
194      */
195 #define RETURN(x) { \
196         if (timerToken) Tcl_DeleteTimerHandler(timerToken); \
197         return(x); \
198     }
199
200     for (;;) {
201         /* if anything has been touched by someone else, report that */
202         /* an event has been received */
203
204         for (i=0;i<n;i++) {
205             tsdPtr->rr++;
206             if (tsdPtr->rr >= n) tsdPtr->rr = 0;
207
208             esPtr = esPtrs[tsdPtr->rr];
209
210             if (esPtr->key != key) {
211                 esPtr->key = key;
212                 esPtr->force_read = FALSE;
213                 *esPtrOut = esPtr;
214                 RETURN(EXP_DATA_OLD);
215             } else if ((!esPtr->force_read) && (!expSizeZero(esPtr))) {
216                 *esPtrOut = esPtr;
217                 RETURN(EXP_DATA_OLD);
218             } else if (esPtr->notified) {
219                 /* this test of the mask should be redundant but SunOS */
220                 /* raises both READABLE and EXCEPTION (for no */
221                 /* apparent reason) when selecting on a plain file */
222                 if (esPtr->notifiedMask & TCL_READABLE) {
223                     *esPtrOut = esPtr;
224                     esPtr->notified = FALSE;
225                     RETURN(EXP_DATA_NEW);
226                 }
227                 /*
228                  * at this point we know that the event must be TCL_EXCEPTION
229                  * indicating either EOF or HP ptytrap.
230                  */
231 #ifndef HAVE_PTYTRAP
232                 RETURN(EXP_EOF);
233 #else
234                 if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) {
235                     expDiagLog("ioctl error on TIOCREQCHECK: %s", Tcl_PosixError(interp));
236                     RETURN(EXP_TCLERROR);
237                 }
238                 if (ioctl_info.request == TIOCCLOSE) {
239                     RETURN(EXP_EOF);
240                 }
241                 if (ioctl(esPtr->fdin, TIOCREQSET, &ioctl_info) < 0) {
242                     expDiagLog("ioctl error on TIOCREQSET after ioctl or open on slave: %s", Tcl_ErrnoMsg(errno));
243                 }
244                 /* presumably, we trapped an open here */
245                 /* so simply continue by falling thru */
246 #endif /* !HAVE_PTYTRAP */
247             }
248         }
249
250         if (!timerToken) {
251             if (timeout >= 0) {
252                 timerToken = Tcl_CreateTimerHandler(1000*timeout,
253                         exp_timehandler,
254                         (ClientData)&timerFired);
255             }
256         }
257
258         /* make sure that all fds that should be armed are */
259         for (i=0;i<n;i++) {
260             esPtr = esPtrs[i];
261                 /*printf("CreateChannelHandler: %s\r\n",esPtr->name);*/
262                 Tcl_CreateChannelHandler(
263                                          esPtr->channel,
264                                          TCL_READABLE | TCL_EXCEPTION,
265                                          exp_channelhandler,
266                                          (ClientData)esPtr);
267                 esPtr->fg_armed = TRUE;
268         }
269
270         Tcl_DoOneEvent(0);      /* do any event */
271         
272         if (timerFired) return(EXP_TIMEOUT);
273         
274         if (old_configure_count != exp_configure_count) {
275             RETURN(EXP_RECONFIGURE);
276         }
277     }
278 }
279
280 /* Having been told there was an event for a specific ExpState, get it */
281 /* This returns status, one of EOF, TIMEOUT, ERROR or DATA */
282 /*ARGSUSED*/
283 int
284 exp_get_next_event_info(interp,esPtr)
285 Tcl_Interp *interp;
286 ExpState *esPtr;
287 {
288 #ifdef HAVE_PTYTRAP
289     struct request_info ioctl_info;
290 #endif
291
292     if (esPtr->notifiedMask & TCL_READABLE) return EXP_DATA_NEW;
293
294     /* ready_mask must contain TCL_EXCEPTION */
295 #ifndef HAVE_PTYTRAP
296     return(EXP_EOF);
297 #else
298     if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) {
299         expDiagLog("ioctl error on TIOCREQCHECK: %s",
300                 Tcl_PosixError(interp));
301         return(EXP_TCLERROR);
302     }
303     if (ioctl_info.request == TIOCCLOSE) {
304         return(EXP_EOF);
305     }
306     if (ioctl(esPtr->fdin, TIOCREQSET, &ioctl_info) < 0) {
307         expDiagLog("ioctl error on TIOCREQSET after ioctl or open on slave: %s", Tcl_ErrnoMsg(errno));
308     }
309     /* presumably, we trapped an open here */
310     /* call it an error for lack of anything more descriptive */
311     /* it will be thrown away by caller anyway */
312     return EXP_TCLERROR;
313 #endif
314 }
315
316 /*ARGSUSED*/
317 int     /* returns TCL_XXX */
318 exp_dsleep(interp,sec)
319 Tcl_Interp *interp;
320 double sec;
321 {
322     int timerFired = FALSE;
323
324     Tcl_CreateTimerHandler((int)(sec*1000),exp_timehandler,(ClientData)&timerFired);
325
326     while (!timerFired) {
327         Tcl_DoOneEvent(0);
328     }
329     return TCL_OK;
330 }
331
332 static char destroy_cmd[] = "destroy .";
333
334 static void
335 exp_event_exit_real(interp)
336 Tcl_Interp *interp;
337 {
338     Tcl_Eval(interp,destroy_cmd);
339 }
340
341 /* set things up for later calls to event handler */
342 void
343 exp_init_event()
344 {
345     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
346     tsdPtr->rr = 0;
347
348     exp_event_exit = exp_event_exit_real;
349 }