1 /* command.h - definitions for expect commands
2 
3 Written by: Don Libes, NIST, 2/6/90
4 
5 Design and implementation of this program was paid for by U.S. tax
6 dollars.  Therefore it is public domain.  However, the author and NIST
7 would appreciate credit if this program or parts of it are used.
8 */
9 
10 #ifdef HAVE_SYS_WAIT_H
11   /* ISC doesn't def WNOHANG unless _POSIX_SOURCE is def'ed */
12 # ifdef WNOHANG_REQUIRES_POSIX_SOURCE
13 #  define _POSIX_SOURCE
14 # endif
15 # include <sys/wait.h>
16 # ifdef WNOHANG_REQUIRES_POSIX_SOURCE
17 #  undef _POSIX_SOURCE
18 # endif
19 #endif
20 
21 #ifdef __APPLE__
22 /* From: "Daniel A. Steffen" <steffen@ics.mq.edu.au> */
23 # undef panic
24 #endif
25 
26 #include <tclPort.h>
27 
28 #define EXP_CHANNELNAMELEN (16 + TCL_INTEGER_SPACE)
29 
30 EXTERN char *		exp_get_var _ANSI_ARGS_((Tcl_Interp *,char *));
31 
32 EXTERN int exp_default_match_max;
33 EXTERN int exp_default_parity;
34 EXTERN int exp_default_rm_nulls;
35 EXTERN int exp_default_close_on_eof;
36 
37 EXTERN int		exp_one_arg_braced _ANSI_ARGS_((Tcl_Obj *));
38 
39 EXTERN Tcl_Obj*		exp_eval_with_one_arg _ANSI_ARGS_((ClientData,
40 				Tcl_Interp *, struct Tcl_Obj * CONST objv[]));
41 
42 EXTERN void		exp_lowmemcpy _ANSI_ARGS_((char *,char *,int));
43 
44 EXTERN int exp_flageq_code _ANSI_ARGS_((char *,char *,int));
45 
46 #define exp_flageq(flag,string,minlen) \
47 (((string)[0] == (flag)[0]) && (exp_flageq_code(((flag)+1),((string)+1),((minlen)-1))))
48 
49 /* exp_flageq for single char flags */
50 #define exp_flageq1(flag,string) \
51 	((string[0] == flag) && (string[1] == '\0'))
52 
53 #define EXP_SPAWN_ID_USER		0
54 #define EXP_SPAWN_ID_ANY_LIT		"-1"
55 
56 #define EXP_CHANNEL_PREFIX "exp"
57 #define EXP_CHANNEL_PREFIX_LENGTH 3
58 #define isExpChannelName(name) \
59     (0 == strncmp(name,EXP_CHANNEL_PREFIX,EXP_CHANNEL_PREFIX_LENGTH))
60 
61 #define exp_is_stdinfd(x)	((x) == 0)
62 #define exp_is_devttyfd(x)	((x) == exp_dev_tty)
63 
64 #define EXP_NOPID	0	/* Used when there is no associated pid to */
65 				/* wait for.  For example: */
66 				/* 1) When fd opened by someone else, e.g., */
67 				/* Tcl's open */
68 				/* 2) When entry not in use */
69 				/* 3) To tell user pid of "spawn -open" */
70 				/* 4) stdin, out, error */
71 
72 #define EXP_NOFD	-1
73 
74 /* these are occasionally useful to distinguish between various expect */
75 /* commands and are also used as array indices into the per-fd eg[] arrays */
76 #define EXP_CMD_BEFORE	0
77 #define EXP_CMD_AFTER	1
78 #define EXP_CMD_BG	2
79 #define EXP_CMD_FG	3
80 
81 /*
82  * This structure describes per-instance state of an Exp channel.
83  */
84 
85 typedef struct ExpOrigin {
86   int         refCount;       /* Number of times this channel is used. */
87   Tcl_Channel channel_orig;   /* If opened by someone else, i.e. tcl::open */
88 } ExpOrigin;
89 
90 
91 typedef struct ExpUniBuf {
92     Tcl_UniChar* buffer;    /* char buffer, holdings unicode chars (fixed width) */
93     int          max;       /* number of CHARS the buffer has space for (== old msize) */
94     int          use;       /* number of CHARS the buffer is currently holding */
95     Tcl_Obj*     newchars;  /* Object to hold newly read characters */
96 } ExpUniBuf;
97 
98 typedef struct ExpState {
99     Tcl_Channel channel;	/* Channel associated with this file. */
100     char name[EXP_CHANNELNAMELEN+1]; /* expect and interact set variables
101 				   to channel name, so for efficiency
102 				   cache it here */
103     int fdin;		/* input fd */
104     int fdout;		/* output fd - usually the same as fdin, although
105 			   may be different if channel opened by tcl::open */
106     ExpOrigin* chan_orig;   /* If opened by someone else, i.e. tcl::open */
107     int fd_slave;	/* slave fd if "spawn -pty" used */
108 
109     /* this may go away if we find it is not needed */
110     /* it might be needed by inherited channels */
111     int validMask;		/* OR'ed combination of TCL_READABLE,
112 				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
113 				 * which operations are valid on the file. */
114 
115     int pid;		/* pid or EXP_NOPID if no pid */
116 
117     ExpUniBuf input;    /* input buffer */
118 
119     int umsize;	        /* # of bytes (min) that is guaranteed to match */
120 			/* this comes from match_max command */
121     int printed;	/* # of characters! written to stdout (if logging on) */
122                         /* but not actually returned via a match yet */
123     int echoed;	        /* additional # of characters (beyond "printed" above) */
124                         /* echoed back but not actually returned via a match */
125                         /* yet.  This supports interact -echo */
126 
127     int rm_nulls;	/* if nulls should be stripped before pat matching */
128     int open;		/* if fdin/fdout open */
129     int user_waited;    /* if user has issued "wait" command */
130     int sys_waited;	/* if wait() (or variant) has been called */
131     int registered;	/* if channel registered */
132     WAIT_STATUS_TYPE wait;	/* raw status from wait() */
133     int parity;	        /* if parity should be preserved */
134     int close_on_eof;   /* if channel should be closed automatically on eof */
135     int key;	        /* unique id that identifies what command instance */
136                         /* last touched this buffer */
137     int force_read;	/* force read to occur (even if buffer already has */
138                         /* data).  This supports interact CAN_MATCH */
139     int notified;	/* If Tcl_NotifyChannel has been called and we */
140 		        /* have not yet read from the channel. */
141     int notifiedMask;	/* Mask reported when notified. */
142 
143     int fg_armed;	/* If we have requested Tk_CreateFileHandler to be */
144 			/* responding to foreground events.  Note that */
145 		        /* other handlers can have stolen it away so this */
146 			/* doesn't necessarily mean the handler is set.  */
147 			/* However, if fg_armed is 0, then the handlers */
148 			/* definitely needs to be set.  The significance of */
149 			/* this flag is so we can remember to turn it off. */
150 #ifdef HAVE_PTYTRAP
151     char *slave_name;   /* Full name of slave, i.e., /dev/ttyp0 */
152 #endif /* HAVE_PTYTRAP */
153     /* may go away */
154     int leaveopen;	/* If we should not call Tcl's close when we close - */
155                         /* only relevant if Tcl does the original open */
156 
157     Tcl_Interp *bg_interp;	/* interp to process the bg cases */
158     int bg_ecount;		/* number of background ExpStates */
159     enum {
160 	blocked,	/* blocked because we are processing the */
161 			/* file handler */
162 	armed,		/* normal state when bg handler in use */
163 	unarmed,	/* no bg handler in use */
164 	disarm_req_while_blocked	/* while blocked, a request */
165 				/* was received to disarm it.  Rather than */
166 				/* processing the request immediately, defer */
167 				/* it so that when we later try to unblock */
168 				/* we will see at that time that it should */
169 				/* instead be disarmed */
170     } bg_status;
171 
172     /*
173      * If the channel is freed while in the middle of a bg event handler,
174      * remember that and defer freeing of the ExpState structure until
175      * it is safe.
176      */
177     int freeWhenBgHandlerUnblocked;
178 
179     /* If channel is closed but not yet waited on, we tie up the fd by
180      * attaching it to /dev/null.  We play this little game so that we
181      * can embed the fd in the channel name.  If we didn't tie up the
182      * fd, we'd get channel name collisions.  I'd consider naming the
183      * channels independently of the fd, but this makes debugging easier.
184      */
185     int fdBusy;
186 
187     /*
188      * stdinout and stderr never go away so that our internal refs to them
189      * don't have to be invalidated.  Having to worry about invalidating them
190      * would be a major pain.  */
191     int keepForever;
192 
193     /*  Remember that "reserved" esPtrs are no longer in use. */
194     int valid;
195 
196     struct ExpState *nextPtr;	/* Pointer to next file in list of all
197 				 * file channels. */
198 } ExpState;
199 
200 #define EXP_SPAWN_ID_BAD	((ExpState *)0)
201 
202 #define EXP_TIME_INFINITY	-1
203 
204 extern Tcl_ChannelType expChannelType;
205 
206 #define EXP_TEMPORARY	1	/* expect */
207 #define EXP_PERMANENT	2	/* expect_after, expect_before, expect_bg */
208 
209 #define EXP_DIRECT	1
210 #define EXP_INDIRECT	2
211 
212 EXTERN void		expAdjust _ANSI_ARGS_((ExpState *));
213 EXTERN int		expWriteChars _ANSI_ARGS_((ExpState *,char *,int));
214 EXTERN int		expWriteCharsUni _ANSI_ARGS_((ExpState *,Tcl_UniChar *,int));
215 EXTERN void		exp_buffer_shuffle _ANSI_ARGS_((Tcl_Interp *,ExpState *,int,char *,char *));
216 EXTERN int		exp_close _ANSI_ARGS_((Tcl_Interp *,ExpState *));
217 EXTERN void		exp_close_all _ANSI_ARGS_((Tcl_Interp *));
218 EXTERN void		exp_ecmd_remove_fd_direct_and_indirect
219 				_ANSI_ARGS_((Tcl_Interp *,int));
220 EXTERN void		exp_trap_on _ANSI_ARGS_((int));
221 EXTERN int		exp_trap_off _ANSI_ARGS_((char *));
222 
223 EXTERN void		exp_strftime(char *format, const struct tm *timeptr,Tcl_DString *dstring);
224 
225 #define exp_deleteProc (void (*)())0
226 #define exp_deleteObjProc (void (*)())0
227 
228 EXTERN int expect_key;
229 EXTERN int exp_configure_count;	/* # of times descriptors have been closed */
230 				/* or indirect lists have been changed */
231 EXTERN int exp_nostack_dump;	/* TRUE if user has requested unrolling of */
232 				/* stack with no trace */
233 
234 EXTERN void		exp_init_pty _ANSI_ARGS_((void));
235 EXTERN void		exp_pty_exit _ANSI_ARGS_((void));
236 EXTERN void		exp_init_tty _ANSI_ARGS_((void));
237 EXTERN void		exp_init_stdio _ANSI_ARGS_((void));
238 /*EXTERN void		exp_init_expect _ANSI_ARGS_((Tcl_Interp *));*/
239 EXTERN void		exp_init_spawn_ids _ANSI_ARGS_((Tcl_Interp *));
240 EXTERN void		exp_init_spawn_id_vars _ANSI_ARGS_((Tcl_Interp *));
241 EXTERN void		exp_init_trap _ANSI_ARGS_((void));
242 EXTERN void		exp_init_send _ANSI_ARGS_((void));
243 EXTERN void		exp_init_unit_random _ANSI_ARGS_((void));
244 EXTERN void		exp_init_sig _ANSI_ARGS_((void));
245 EXTERN void		expChannelInit _ANSI_ARGS_((void));
246 EXTERN int		expChannelCountGet _ANSI_ARGS_((void));
247 EXTERN int              expChannelStillAlive _ANSI_ARGS_((ExpState *, char *));
248 
249 EXTERN int		exp_tcl2_returnvalue _ANSI_ARGS_((int));
250 EXTERN int		exp_2tcl_returnvalue _ANSI_ARGS_((int));
251 
252 EXTERN void		exp_rearm_sigchld _ANSI_ARGS_((Tcl_Interp *));
253 EXTERN int		exp_string_to_signal _ANSI_ARGS_((Tcl_Interp *,char *));
254 
255 EXTERN char *exp_onexit_action;
256 
257 #define exp_new(x)	(x *)malloc(sizeof(x))
258 
259 struct exp_state_list {
260 	ExpState *esPtr;
261 	struct exp_state_list *next;
262 };
263 
264 /* describes a -i flag */
265 struct exp_i {
266 	int cmdtype;	/* EXP_CMD_XXX.  When an indirect update is */
267 			/* triggered by Tcl, this helps tell us in what */
268 			/* exp_i list to look in. */
269 	int direct;	/* if EXP_DIRECT, then the spawn ids have been given */
270 			/* literally, else indirectly through a variable */
271 	int duration;	/* if EXP_PERMANENT, char ptrs here had to be */
272 			/* malloc'd because Tcl command line went away - */
273 			/* i.e., in expect_before/after */
274 	char *variable;
275 	char *value;	/* if type == direct, this is the string that the */
276 			/* user originally supplied to the -i flag.  It may */
277 			/* lose relevance as the fd_list is manipulated */
278 			/* over time.  If type == direct, this is  the */
279 			/* cached value of variable use this to tell if it */
280 			/* has changed or not, and ergo whether it's */
281 			/* necessary to reparse. */
282 
283 	int ecount;	/* # of ecases this is used by */
284 
285 	struct exp_state_list *state_list;
286 	struct exp_i *next;
287 };
288 
289 EXTERN struct exp_i *	exp_new_i_complex _ANSI_ARGS_((Tcl_Interp *,
290 					char *, int, Tcl_VarTraceProc *));
291 EXTERN struct exp_i *	exp_new_i_simple _ANSI_ARGS_((ExpState *,int));
292 EXTERN struct exp_state_list *exp_new_state _ANSI_ARGS_((ExpState *));
293 EXTERN void		exp_free_i _ANSI_ARGS_((Tcl_Interp *,struct exp_i *,
294 					Tcl_VarTraceProc *));
295 EXTERN void		exp_free_state _ANSI_ARGS_((struct exp_state_list *));
296 EXTERN void		exp_free_state_single _ANSI_ARGS_((struct exp_state_list *));
297 EXTERN int		exp_i_update _ANSI_ARGS_((Tcl_Interp *,
298 					struct exp_i *));
299 
300 /*
301  * definitions for creating commands
302  */
303 
304 #define EXP_NOPREFIX	1	/* don't define with "exp_" prefix */
305 #define EXP_REDEFINE	2	/* stomp on old commands with same name */
306 
307 #define exp_proc(cmdproc) 0, cmdproc
308 
309 struct exp_cmd_data {
310 	char		*name;
311 	Tcl_ObjCmdProc	*objproc;
312 	Tcl_CmdProc	*proc;
313 	ClientData	data;
314 	int 		flags;
315 };
316 
317 EXTERN void		exp_create_commands _ANSI_ARGS_((Tcl_Interp *,
318 						struct exp_cmd_data *));
319 EXTERN void		exp_init_main_cmds _ANSI_ARGS_((Tcl_Interp *));
320 EXTERN void		exp_init_expect_cmds _ANSI_ARGS_((Tcl_Interp *));
321 EXTERN void		exp_init_most_cmds _ANSI_ARGS_((Tcl_Interp *));
322 EXTERN void		exp_init_trap_cmds _ANSI_ARGS_((Tcl_Interp *));
323 EXTERN void		exp_init_interact_cmds _ANSI_ARGS_((Tcl_Interp *));
324 EXTERN void		exp_init_tty_cmds();
325 
326 EXTERN ExpState *	expStateCheck _ANSI_ARGS_((Tcl_Interp *,ExpState *,int,int,char *));
327 EXTERN ExpState *       expStateCurrent _ANSI_ARGS_((Tcl_Interp *,int,int,int));
328 EXTERN ExpState *       expStateFromChannelName _ANSI_ARGS_((Tcl_Interp *,char *,int,int,int,char *));
329 EXTERN void		expStateFree _ANSI_ARGS_((ExpState *));
330 
331 EXTERN ExpState *	expCreateChannel _ANSI_ARGS_((Tcl_Interp *,int,int,int));
332 EXTERN ExpState *	expWaitOnAny _ANSI_ARGS_((void));
333 EXTERN ExpState *	expWaitOnOne _ANSI_ARGS_((void));
334 EXTERN void		expExpectVarsInit _ANSI_ARGS_((void));
335 EXTERN int		expStateAnyIs _ANSI_ARGS_((ExpState *));
336 EXTERN int		expDevttyIs _ANSI_ARGS_((ExpState *));
337 EXTERN int		expStdinoutIs _ANSI_ARGS_((ExpState *));
338 EXTERN ExpState *	expStdinoutGet _ANSI_ARGS_((void));
339 EXTERN ExpState *	expDevttyGet _ANSI_ARGS_((void));
340 
341 /* generic functions that really should be provided by Tcl */
342 #if 0 /* Redefined as macros. */
343 EXTERN int		expSizeGet _ANSI_ARGS_((ExpState *));
344 EXTERN int		expSizeZero _ANSI_ARGS_((ExpState *));
345 #else
346 #define expSizeGet(esPtr)  ((esPtr)->input.use)
347 #define expSizeZero(esPtr) (((esPtr)->input.use) == 0)
348 #endif
349 
350 #define EXP_CMDINFO_CLOSE  "expect/cmdinfo/close"
351 #define EXP_CMDINFO_RETURN "expect/cmdinfo/return"
352 
353 /*
354  * Local Variables:
355  * mode: c
356  * c-basic-offset: 4
357  * fill-column: 78
358  * End:
359  */
360