1/* exp_select.c - select() interface for Expect
2
3Written by: Don Libes, NIST, 2/6/90
4
5Design and implementation of this program was paid for by U.S. tax
6dollars.  Therefore it is public domain.  However, the author and NIST
7would appreciate credit if this program or parts of it are used.
8
9*/
10
11/* suppress file-empty warnings produced by some compilers */
12void exp_unused() {}
13
14#if 0 /* WHOLE FILE!!!! */
15#include "expect_cf.h"
16#include <stdio.h>
17#include <errno.h>
18#include <sys/types.h>
19
20#ifdef HAVE_SYS_WAIT_H
21#include <sys/wait.h>
22#endif
23
24#ifdef HAVE_SYS_TIME_H
25#include <sys/time.h>
26#endif
27
28#ifdef HAVE_SYSSELECT_H
29#  include <sys/select.h>	/* Intel needs this for timeval */
30#endif
31
32#ifdef HAVE_PTYTRAP
33#  include <sys/ptyio.h>
34#endif
35
36#ifdef HAVE_UNISTD_H
37#  include <unistd.h>
38#endif
39
40#ifdef _AIX
41/* AIX has some unusual definition of FD_SET */
42#include <sys/select.h>
43#endif
44
45#if !defined( FD_SET )  &&  defined( HAVE_SYS_BSDTYPES_H )
46    /* like AIX, ISC has it's own definition of FD_SET */
47#   include <sys/bsdtypes.h>
48#endif /*  ! FD_SET  &&  HAVE_SYS_BSDTYPES_H */
49
50#include "tcl.h"
51#include "exp_prog.h"
52#include "exp_command.h"	/* for struct exp_f defs */
53#include "exp_event.h"
54
55#ifdef HAVE_SYSCONF_H
56#include <sys/sysconfig.h>
57#endif
58
59#ifndef FD_SET
60#define FD_SET(fd,fdset)	(fdset)->fds_bits[0] |= (1<<(fd))
61#define FD_CLR(fd,fdset)	(fdset)->fds_bits[0] &= ~(1<<(fd))
62#define FD_ZERO(fdset)		(fdset)->fds_bits[0] = 0
63#define FD_ISSET(fd,fdset)	(((fdset)->fds_bits[0]) & (1<<(fd)))
64#ifndef AUX2
65typedef struct fd_set {
66	long fds_bits[1];
67	/* any implementation so pathetic as to not define FD_SET will just */
68	/* have to suffer with only 32 bits worth of fds */
69} fd_set;
70#endif /* AUX2 */
71#endif
72
73static struct timeval zerotime = {0, 0};
74static struct timeval anytime = {0, 0};	/* can be changed by user */
75
76/* returns status, one of EOF, TIMEOUT, ERROR or DATA */
77int
78exp_get_next_event(interp,masters, n,master_out,timeout,key)
79Tcl_Interp *interp;
80int *masters;
81int n;			/* # of masters */
82int *master_out;	/* 1st event master, not set if none */
83int timeout;		/* seconds */
84int key;
85{
86	static rr = 0;	/* round robin ptr */
87
88	int i;	/* index into in-array */
89	struct timeval *t;
90
91	fd_set rdrs;
92	fd_set excep;
93/* FIXME: This is really gross, but the folks at Lynx said their select is
94 *        way hosed and to ignore all exceptions.
95 */
96#ifdef __Lynx__
97#define EXCEP 0
98#else
99#define EXCEP &excep
100#endif
101
102	for (i=0;i<n;i++) {
103		struct exp_f *f;
104		int m;
105
106		rr++;
107		if (rr >= n) rr = 0;
108
109		m = masters[rr];
110		f = exp_fs + m;
111
112		if (f->key != key) {
113			f->key = key;
114			f->force_read = FALSE;
115			*master_out = m;
116			return(EXP_DATA_OLD);
117		} else if ((!f->force_read) && (f->size != 0)) {
118			*master_out = m;
119			return(EXP_DATA_OLD);
120		}
121	}
122
123	if (timeout >= 0) {
124		t = &anytime;
125		t->tv_sec = timeout;
126	} else {
127		t = NULL;
128	}
129
130 restart:
131	if (Tcl_AsyncReady()) {
132		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
133		if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
134
135		/* anything in the environment could have changed */
136		return EXP_RECONFIGURE;
137	}
138
139	FD_ZERO(&rdrs);
140	FD_ZERO(&excep);
141	for (i = 0;i < n;i++) {
142		FD_SET(masters[i],&rdrs);
143		FD_SET(masters[i],&excep);
144	}
145
146	/* The reason all fd masks are (seemingly) redundantly cast to */
147	/* SELECT_MASK_TYPE is that the HP defines its mask in terms of */
148	/* of int * and yet defines FD_SET in terms of fd_set. */
149
150	if (-1 == select(exp_fd_max+1,
151			(SELECT_MASK_TYPE *)&rdrs,
152			(SELECT_MASK_TYPE *)0,
153			(SELECT_MASK_TYPE *)EXCEP,
154			t)) {
155		/* window refreshes trigger EINTR, ignore */
156		if (errno == EINTR) goto restart;
157		else if (errno == EBADF) {
158		    /* someone is rotten */
159		    for (i=0;i<n;i++) {
160			fd_set suspect;
161			FD_ZERO(&suspect);
162			FD_SET(masters[i],&suspect);
163			if (-1 == select(exp_fd_max+1,
164			    		(SELECT_MASK_TYPE *)&suspect,
165			    		(SELECT_MASK_TYPE *)0,
166					(SELECT_MASK_TYPE *)0,
167					&zerotime)) {
168				exp_error(interp,"invalid spawn_id (%d)\r",masters[i]);
169				return(EXP_TCLERROR);
170			}
171		   }
172	        } else {
173			/* not prepared to handle anything else */
174			exp_error(interp,"select: %s\r",Tcl_PosixError(interp));
175			return(EXP_TCLERROR);
176		}
177	}
178
179	for (i=0;i<n;i++) {
180		rr++;
181		if (rr >= n) rr = 0;	/* ">" catches previous readys that */
182				/* used more fds then we're using now */
183
184		if (FD_ISSET(masters[rr],&rdrs)) {
185			*master_out = masters[rr];
186			return(EXP_DATA_NEW);
187/*#ifdef HAVE_PTYTRAP*/
188		} else if (FD_ISSET(masters[rr], &excep)) {
189#ifndef HAVE_PTYTRAP
190			*master_out = masters[rr];
191			return(EXP_EOF);
192#else
193			struct request_info ioctl_info;
194			if (ioctl(masters[rr],TIOCREQCHECK,&ioctl_info) < 0) {
195				exp_DiagLog("ioctl error on TIOCREQCHECK: %s",Tcl_ErrnoMsg(errno));
196				break;
197			}
198			if (ioctl_info.request == TIOCCLOSE) {
199				/* eof */
200				*master_out = masters[rr];
201				return(EXP_EOF);
202			}
203			if (ioctl(masters[rr], TIOCREQSET, &ioctl_info) < 0)
204				expDiagLog("ioctl error on TIOCREQSET after ioctl or open on slave: %s", Tcl_ErrnoMsg(errno));
205			/* presumably, we trapped an open here */
206			goto restart;
207#endif /* HAVE_PTYTRAP */
208		}
209	}
210	return(EXP_TIMEOUT);
211}
212
213/*ARGSUSED*/
214int
215exp_get_next_event_info(interp,fd,ready_mask)
216Tcl_Interp *interp;
217int fd;
218int ready_mask;
219{
220	/* this function is only used when running with Tk */
221	/* hence, it is merely a stub in this file but to */
222	/* pacify lint, return something */
223	return 0;
224}
225
226int	/* returns TCL_XXX */
227exp_dsleep(interp,sec)
228Tcl_Interp *interp;
229double sec;
230{
231	struct timeval t;
232
233	t.tv_sec = sec;
234	t.tv_usec = (sec - t.tv_sec) * 1000000L;
235 restart:
236	if (Tcl_AsyncReady()) {
237		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
238		if (rc != TCL_OK) return rc;
239	}
240	if (-1 == select(1,
241			(SELECT_MASK_TYPE *)0,
242			(SELECT_MASK_TYPE *)0,
243			(SELECT_MASK_TYPE *)0,
244			&t)
245				&& errno == EINTR)
246		goto restart;
247	return TCL_OK;
248}
249
250#if 0
251int	/* returns TCL_XXX */
252exp_usleep(interp,usec)
253Tcl_Interp *interp;
254long usec;		/* microseconds */
255{
256	struct timeval t;
257
258	t.tv_sec = usec/1000000L;
259	t.tv_usec = usec%1000000L;
260 restart:
261	if (Tcl_AsyncReady()) {
262		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
263		if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
264	}
265	if (-1 == select(1,
266			(SELECT_MASK_TYPE *)0,
267			(SELECT_MASK_TYPE *)0,
268			(SELECT_MASK_TYPE *)0,
269			&t)
270				&& errno == EINTR)
271		goto restart;
272	return TCL_OK;
273}
274#endif /*0*/
275
276/* set things up for later calls to event handler */
277void
278exp_init_event()
279{
280#if 0
281#ifdef _SC_OPEN_MAX
282	maxfds = sysconf(_SC_OPEN_MAX);
283#else
284	maxfds = getdtablesize();
285#endif
286#endif
287
288	exp_event_exit = 0;
289}
290#endif /* WHOLE FILE !!!! */
291