1 /* exp_event.c - event interface for Expect
3 Written by: Don Libes, NIST, 2/6/90
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.
10 #include "expect_cf.h"
13 #include <sys/types.h>
15 #ifdef HAVE_SYS_WAIT_H
20 # include <sys/ptyio.h>
25 #include "exp_command.h" /* for ExpState defs */
26 #include "exp_event.h"
28 typedef struct ThreadSpecificData {
29 int rr; /* round robin ptr */
32 static Tcl_ThreadDataKey dataKey;
35 exp_event_disarm_bg(esPtr)
38 Tcl_DeleteChannelHandler(esPtr->channel,exp_background_channelhandler,(ClientData)esPtr);
42 exp_arm_background_channelhandler_force(esPtr)
45 Tcl_CreateChannelHandler(esPtr->channel,
46 TCL_READABLE|TCL_EXCEPTION,
47 exp_background_channelhandler,
50 esPtr->bg_status = armed;
54 exp_arm_background_channelhandler(esPtr)
57 switch (esPtr->bg_status) {
59 exp_arm_background_channelhandler_force(esPtr);
61 case disarm_req_while_blocked:
62 esPtr->bg_status = blocked; /* forget request */
72 exp_disarm_background_channelhandler(esPtr)
75 switch (esPtr->bg_status) {
77 esPtr->bg_status = disarm_req_while_blocked;
80 esPtr->bg_status = unarmed;
81 exp_event_disarm_bg(esPtr);
83 case disarm_req_while_blocked:
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. */
94 exp_disarm_background_channelhandler_force(esPtr)
97 switch (esPtr->bg_status) {
99 case disarm_req_while_blocked:
101 esPtr->bg_status = unarmed;
102 exp_event_disarm_bg(esPtr);
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" */
113 exp_unblock_background_channelhandler(esPtr)
116 switch (esPtr->bg_status) {
118 exp_arm_background_channelhandler_force(esPtr);
120 case disarm_req_while_blocked:
121 exp_disarm_background_channelhandler_force(esPtr);
126 /* this can only be called at the beginning of the bg handler in which */
127 /* case we know the status must be "armed" */
129 exp_block_background_channelhandler(esPtr)
132 esPtr->bg_status = blocked;
133 exp_event_disarm_bg(esPtr);
139 exp_timehandler(clientData)
140 ClientData clientData;
142 *(int *)clientData = TRUE;
145 static void exp_channelhandler(clientData,mask)
146 ClientData clientData;
149 ExpState *esPtr = (ExpState *)clientData;
151 esPtr->notified = TRUE;
152 esPtr->notifiedMask = mask;
154 exp_event_disarm_fg(esPtr);
158 exp_event_disarm_fg(esPtr)
161 /*printf("DeleteChannelHandler: %s\r\n",esPtr->name);*/
162 Tcl_DeleteChannelHandler(esPtr->channel,exp_channelhandler,(ClientData)esPtr);
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;
169 /* returns status, one of EOF, TIMEOUT, ERROR or DATA */
170 /* can now return RECONFIGURE, too */
172 int exp_get_next_event(interp,esPtrs,n,esPtrOut,timeout,key)
174 ExpState *(esPtrs[]);
175 int n; /* # of esPtrs */
176 ExpState **esPtrOut; /* 1st ready esPtr, not set if none */
177 int timeout; /* seconds */
180 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
183 int i; /* index into in-array */
185 struct request_info ioctl_info;
188 int old_configure_count = exp_configure_count;
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.
195 #define RETURN(x) { \
196 if (timerToken) Tcl_DeleteTimerHandler(timerToken); \
201 /* if anything has been touched by someone else, report that */
202 /* an event has been received */
206 if (tsdPtr->rr >= n) tsdPtr->rr = 0;
208 esPtr = esPtrs[tsdPtr->rr];
210 if (esPtr->key != key) {
212 esPtr->force_read = FALSE;
214 RETURN(EXP_DATA_OLD);
215 } else if ((!esPtr->force_read) && (!expSizeZero(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) {
224 esPtr->notified = FALSE;
225 RETURN(EXP_DATA_NEW);
228 * at this point we know that the event must be TCL_EXCEPTION
229 * indicating either EOF or HP ptytrap.
234 if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) {
235 expDiagLog("ioctl error on TIOCREQCHECK: %s", Tcl_PosixError(interp));
236 RETURN(EXP_TCLERROR);
238 if (ioctl_info.request == TIOCCLOSE) {
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));
244 /* presumably, we trapped an open here */
245 /* so simply continue by falling thru */
246 #endif /* !HAVE_PTYTRAP */
252 timerToken = Tcl_CreateTimerHandler(1000*timeout,
254 (ClientData)&timerFired);
258 /* make sure that all fds that should be armed are */
261 /*printf("CreateChannelHandler: %s\r\n",esPtr->name);*/
262 Tcl_CreateChannelHandler(
264 TCL_READABLE | TCL_EXCEPTION,
267 esPtr->fg_armed = TRUE;
270 Tcl_DoOneEvent(0); /* do any event */
272 if (timerFired) return(EXP_TIMEOUT);
274 if (old_configure_count != exp_configure_count) {
275 RETURN(EXP_RECONFIGURE);
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 */
284 exp_get_next_event_info(interp,esPtr)
289 struct request_info ioctl_info;
292 if (esPtr->notifiedMask & TCL_READABLE) return EXP_DATA_NEW;
294 /* ready_mask must contain TCL_EXCEPTION */
298 if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) {
299 expDiagLog("ioctl error on TIOCREQCHECK: %s",
300 Tcl_PosixError(interp));
301 return(EXP_TCLERROR);
303 if (ioctl_info.request == TIOCCLOSE) {
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));
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 */
317 int /* returns TCL_XXX */
318 exp_dsleep(interp,sec)
322 int timerFired = FALSE;
324 Tcl_CreateTimerHandler((int)(sec*1000),exp_timehandler,(ClientData)&timerFired);
326 while (!timerFired) {
332 static char destroy_cmd[] = "destroy .";
335 exp_event_exit_real(interp)
338 Tcl_Eval(interp,destroy_cmd);
341 /* set things up for later calls to event handler */
345 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
348 exp_event_exit = exp_event_exit_real;