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