1 /* interact (using select) - give user keyboard control
3 Written by: Don Libes, NIST, 2/6/90
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.
11 #include "expect_cf.h"
13 #ifdef HAVE_INTTYPES_H
14 # include <inttypes.h>
16 #include <sys/types.h>
21 #ifdef TIME_WITH_SYS_TIME
22 # include <sys/time.h>
26 # include <sys/time.h>
32 #ifdef HAVE_SYS_WAIT_H
41 #include "exp_tty_in.h"
42 #include "exp_rename.h"
44 #include "exp_command.h"
46 #include "exp_event.h" /* exp_get_next_event decl */
48 /* Tcl 8.5+ moved this internal - needed for when I compile expect against 8.5. */
49 #ifndef TCL_REG_BOSONLY
50 #define TCL_REG_BOSONLY 002000
53 typedef struct ThreadSpecificData {
54 Tcl_Obj *cmdObjReturn;
55 Tcl_Obj *cmdObjInterpreter;
58 static Tcl_ThreadDataKey dataKey;
60 #define INTER_OUT "interact_out"
61 #define out(var,val) \
62 expDiagLog("interact: set %s(%s) ",INTER_OUT,var); \
63 expDiagLogU(expPrintify(val)); \
64 expDiagLogU("\"\r\n"); \
65 Tcl_SetVar2(interp,INTER_OUT,var,val,0);
68 * tests if we are running this using a real tty
70 * these tests are currently only used to control what gets written to the
71 * logfile. Note that removal of the test of "..._is_tty" means that stdin
72 * or stdout could be redirected and yet stdout would still be logged.
73 * However, it's not clear why anyone would use log_file when these are
74 * redirected in the first place. On the other hand, it is reasonable to
75 * run expect as a daemon in which case, stdin/out do not appear to be
76 * ttys, yet it makes sense for them to be logged with log_file as if they
80 #define real_tty_output(x) (exp_stdout_is_tty && (((x)==1) || ((x)==exp_dev_tty)))
81 #define real_tty_input(x) (exp_stdin_is_tty && (((x)==0) || ((x)==exp_dev_tty)))
84 #define real_tty_output(x) ((x->fdout == 1) || (expDevttyIs(x)))
85 #define real_tty_input(x) (exp_stdin_is_tty && ((x->fdin==0) || (expDevttyIs(x))))
87 #define new(x) (x *)ckalloc(sizeof(x))
91 int tty_reset; /* if true, reset tty mode upon action */
92 int iread; /* if true, reread indirects */
93 int iwrite; /* if true, write spawn_id element */
94 struct action *next; /* chain only for later for freeing */
98 Tcl_Obj *keys; /* original pattern provided by user */
99 int re; /* true if looking to match a regexp. */
100 int null; /* true if looking to match 0 byte */
102 int echo; /* if keystrokes should be echoed */
103 int writethru; /* if keystrokes should go through to process */
104 int indices; /* true if should write indices */
105 struct action action;
110 struct exp_i *i_list;
111 struct action *action_eof;
116 struct exp_i *i_list;
117 struct output *output;
118 struct action *action_eof;
119 struct action *action_timeout;
120 struct keymap *keymap;
121 int timeout_nominal; /* timeout nominal */
122 int timeout_remaining; /* timeout remaining */
127 * Once we are handed an ExpState from the event handler, we can figure out
128 * which "struct input *" it references by using expStateToInput. This has is
129 * populated by expCreateStateToInput.
137 Tcl_HashEntry *entry = Tcl_FindHashEntry(hash,(char *)esPtr);
140 /* should never happen */
143 return ((struct input *)Tcl_GetHashValue(entry));
147 expCreateStateToInput(
152 Tcl_HashEntry *entry;
155 entry = Tcl_CreateHashEntry(hash,(char *)esPtr,&newPtr);
156 Tcl_SetHashValue(entry,(ClientData)inp);
159 static void free_input(Tcl_Interp *interp, struct input *i);
160 static void free_keymap(struct keymap *km);
161 static void free_output(Tcl_Interp *interp, struct output *o);
162 static void free_action(struct action *a);
163 static struct action *new_action(struct action **base);
164 static int inter_eval(
166 struct action *action,
169 /* intMatch() accepts user keystrokes and returns one of MATCH,
170 CANMATCH, or CANTMATCH. These describe whether the keystrokes match a
171 key sequence, and could or can't if more characters arrive. The
172 function assigns a matching keymap if there is a match or can-match.
173 A matching keymap is assigned on can-match so we know whether to echo
176 intMatch is optimized (if you can call it that) towards a small
177 number of key mappings, but still works well for large maps, since no
178 function calls are made, and we stop as soon as there is a single-char
179 mismatch, and go on to the next one. A hash table or compiled DFA
180 probably would not buy very much here for most maps.
182 The basic idea of how this works is it does a smart sequential search.
183 At each position of the input string, we attempt to match each of the
184 keymaps. If at least one matches, the first match is returned.
186 If there is a CANMATCH and there are more keymaps to try, we continue
187 trying. If there are no more keymaps to try, we stop trying and
188 return with an indication of the first keymap that can match.
190 Note that I've hacked up the regexp pattern matcher in two ways. One
191 is to force the pattern to always be anchored at the front. That way,
192 it doesn't waste time attempting to match later in the string (before
193 we're ready). The other is to return can-match.
200 struct keymap *keymap, /* linked list of keymaps */
201 struct keymap **km_match, /* keymap that matches or can match */
202 int *matchLen, /* # of bytes that matched */
203 int *skip, /* # of chars to skip */
204 Tcl_RegExpInfo *info)
208 char *ks; /* string from a keymap */
210 Tcl_UniChar *start_search; /* where in string to start searching */
211 int offset; /* # of chars from string to start searching */
213 Tcl_UniChar *string_end;
215 int rm_nulls; /* skip nulls if true */
218 string = esPtr->input.buffer;
219 numchars = esPtr->input.use; /* Actually #chars */
221 /* assert (*km == 0) */
223 /* a shortcut that should help master output which typically */
224 /* is lengthy and has no key maps. Otherwise it would mindlessly */
225 /* iterate on each character anyway. */
228 return(EXP_CANTMATCH);
231 rm_nulls = esPtr->rm_nulls;
233 string_end = string + numchars;
236 * Maintain both a character index and a string pointer so we
237 * can easily index into either the UTF or the Unicode representations.
240 for (start_search = string, offset = 0;
241 start_search < string_end;
242 start_search ++, offset++) {
246 if (*km_match) break; /* if we've already found a CANMATCH */
247 /* don't bother starting search from positions */
248 /* further along the string */
250 for (km=keymap;km;km=km->next) {
251 Tcl_UniChar *s; /* current character being examined */
255 *skip = start_search-string;
256 *matchLen = 1; /* s - start_search == 1 */
260 } else if (!km->re) {
262 Tcl_UniChar sch, ksch;
266 ks = Tcl_GetString(km->keys);
267 for (s = start_search;; s++, ks += kslen) {
268 /* if we hit the end of this map, must've matched! */
270 *skip = start_search-string;
271 *matchLen = s-start_search;
276 /* if we ran out of user-supplied characters, and */
277 /* still haven't matched, it might match if the user */
278 /* supplies more characters next time */
280 if (s == string_end) {
281 /* skip to next key entry, but remember */
282 /* possibility that this entry might match */
283 if (!*km_match) *km_match = km;
288 kslen = Tcl_UtfToUniChar(ks, &ksch);
290 if (sch == ksch) continue;
291 if ((sch == '\0') && rm_nulls) {
304 re = Tcl_GetRegExpFromObj(NULL, km->keys,
305 TCL_REG_ADVANCED|TCL_REG_BOSONLY|TCL_REG_CANMATCH);
306 flags = (offset > 0) ? TCL_REG_NOTBOL : 0;
308 /* ZZZ: Future optimization: Avoid copying */
309 buf = Tcl_NewUnicodeObj (esPtr->input.buffer, esPtr->input.use);
310 Tcl_IncrRefCount (buf);
311 result = Tcl_RegExpExecObj(NULL, re, buf, offset,
312 -1 /* nmatches */, flags);
313 Tcl_DecrRefCount (buf);
316 *skip = start_search-string;
317 Tcl_RegExpGetInfo(re, info);
318 *matchLen = info->matches[0].end;
320 } else if (result == 0) {
321 Tcl_RegExpGetInfo(re, info);
324 * Check to see if there was a partial match starting
325 * at the current character.
327 if (info->extendStart == 0) {
328 if (!*km_match) *km_match = km;
336 /* report CANMATCH for -re and -ex */
339 * since canmatch is only detected after we've advanced too far,
340 * adjust start_search back to make other computations simpler
344 *skip = start_search - string;
345 *matchLen = string_end - start_search;
346 return(EXP_CANMATCH);
349 *skip = start_search-string;
350 return(EXP_CANTMATCH);
353 /* put regexp result in variables */
355 intRegExpMatchProcess(
358 struct keymap *km, /* ptr for above while parsing */
359 Tcl_RegExpInfo *info,
362 char name[20], value[20];
364 Tcl_Obj* buf = Tcl_NewUnicodeObj (esPtr->input.buffer,esPtr->input.use);
366 for (i=0;i<=info->nsubs;i++) {
370 start = info->matches[i].start + offset;
371 if (start == -1) continue;
372 end = (info->matches[i].end-1) + offset;
376 sprintf(name,"%d,start",i);
377 sprintf(value,"%d",start);
381 sprintf(name,"%d,end",i);
382 sprintf(value,"%d",end);
387 sprintf(name,"%d,string",i);
388 val = Tcl_GetRange(buf, start, end);
389 expDiagLog("interact: set %s(%s) \"",INTER_OUT,name);
390 expDiagLogU(expPrintifyObj(val));
391 expDiagLogU("\"\r\n");
392 Tcl_SetVar2Ex(interp,INTER_OUT,name,val,0);
394 Tcl_DecrRefCount (buf);
406 int seenBytes; /* either printed or echoed */
410 /* write is unlikely to fail, since we just read from same descriptor */
411 seenBytes = esPtr->printed + esPtr->echoed;
412 if (skipBytes >= seenBytes) {
413 echoBytes = matchBytes;
414 offsetBytes = skipBytes;
415 } else if ((matchBytes + skipBytes - seenBytes) > 0) {
416 echoBytes = matchBytes + skipBytes - seenBytes;
417 offsetBytes = seenBytes;
420 (void) expWriteCharsUni(esPtr,
421 esPtr->input.buffer + offsetBytes,
424 esPtr->echoed = matchBytes + skipBytes - esPtr->printed;
428 * intRead() does the logical equivalent of a read() for the interact command.
429 * Returns # of bytes read or negative number (EXP_XXX) indicating unusual event.
435 int warnOnBufferFull,
439 Tcl_UniChar *eobOld; /* old end of buffer */
444 str = esPtr->input.buffer;
445 numchars = esPtr->input.use;
446 eobOld = str + numchars;
448 /* We drop one third when are at least 2/3 full */
449 /* condition is (size >= max*2/3) <=> (size*3 >= max*2) */
450 if (numchars*3 >= esPtr->input.max*2) {
452 * In theory, interact could be invoked when this situation
453 * already exists, hence the "probably" in the warning below
455 if (warnOnBufferFull) {
456 expDiagLogU("WARNING: interact buffer is full, probably because your\r\n");
457 expDiagLogU("patterns have matched all of it but require more chars\r\n");
458 expDiagLogU("in order to complete the match.\r\n");
459 expDiagLogU("Dumping first half of buffer in order to continue\r\n");
460 expDiagLogU("Recommend you enlarge the buffer or fix your patterns.\r\n");
462 exp_buffer_shuffle(interp,esPtr,0,INTER_OUT,"interact");
464 if (!interruptible) {
465 cc = Tcl_ReadChars(esPtr->channel, esPtr->input.newchars,
466 esPtr->input.max - esPtr->input.use,
470 cc = intIRead(esPtr->channel, esPtr->input.newchars,
471 esPtr->input.max - esPtr->input.use,
477 memcpy (esPtr->input.buffer + esPtr->input.use,
478 Tcl_GetUnicodeFromObj (esPtr->input.newchars, NULL),
479 cc * sizeof (Tcl_UniChar));
480 esPtr->input.use += cc;
482 expDiagLog("spawn id %s sent <",esPtr->name);
483 expDiagLogU(expPrintifyUni(eobOld,cc));
484 expDiagLogU(">\r\n");
497 The way that the "simple" interact works is that the original Expect
498 process reads from the tty and writes to the spawned process. A child
499 process is forked to read from the spawned process and write to the
500 tty. It looks like this:
519 #define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
524 #ifdef HAVE_SIGLONGJMP
525 static sigjmp_buf env; /* for interruptable read() */
527 static jmp_buf env; /* for interruptable read() */
528 #endif /* HAVE_SIGLONGJMP */
530 static int reading; /* while we are reading */
531 /* really, while "env" is valid */
532 static int deferred_interrupt = FALSE; /* if signal is received, but not */
533 /* in expIRead record this here, so it will */
534 /* be handled next time through expIRead */
540 #ifdef HAVE_SIGLONGJMP
544 #endif /* HAVE_SIGLONGJMP */
546 deferred_interrupt = TRUE;
549 #define EXP_CHILD_EOF -100
552 * Name: expIRead, do an interruptable read
554 * intIRead() reads from chars from the user.
556 * It returns early if it detects the death of a proc (either the spawned
557 * process or the child (surrogate).
566 int cc = EXP_CHILD_EOF;
568 if (deferred_interrupt) return(cc);
571 #ifdef HAVE_SIGLONGJMP
572 0 == sigsetjmp(env,1)
575 #endif /* HAVE_SIGLONGJMP */
578 cc = Tcl_ReadChars(channel,obj,size,flags);
584 /* exit status for the child process created by cmdInteract */
585 #define CHILD_DIED -2
586 #define SPAWNED_PROCESS_DIED -3
589 clean_up_after_child(
593 expWaitOnOne(); /* wait for slave */
594 expWaitOnOne(); /* wait for child */
596 deferred_interrupt = FALSE;
597 if (esPtr->close_on_eof) {
598 exp_close(interp,esPtr);
601 #endif /*SIMPLE_EVENT*/
607 Tcl_HashTable **esPtrToInput, /* map from ExpStates to "struct inputs" */
609 struct input *input_base,
610 int do_indirect, /* if true do indirects */
612 int *real_tty_caller)
616 struct exp_state_list *fdp;
619 int real_tty = FALSE;
621 *config_count = exp_configure_count;
624 for (inp = input_base;inp;inp=inp->next) {
627 /* do not update "direct" entries (again) */
628 /* they were updated upon creation */
629 if (inp->i_list->direct == EXP_INDIRECT) {
630 exp_i_update(interp,inp->i_list);
632 for (outp = inp->output;outp;outp=outp->next) {
633 if (outp->i_list->direct == EXP_INDIRECT) {
634 exp_i_update(interp,outp->i_list);
639 /* revalidate all input descriptors */
640 for (fdp = inp->i_list->state_list;fdp;fdp=fdp->next) {
642 /* have to "adjust" just in case spawn id hasn't had */
643 /* a buffer sized yet */
644 if (!expStateCheck(interp,fdp->esPtr,1,1,"interact")) {
649 /* revalidate all output descriptors */
650 for (outp = inp->output;outp;outp=outp->next) {
651 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
652 /* make user_spawn_id point to stdout */
653 if (!expStdinoutIs(fdp->esPtr)) {
654 if (!expStateCheck(interp,fdp->esPtr,1,0,"interact"))
660 if (!do_indirect) return TCL_OK;
662 if (*esPtrToInput == 0) {
663 *esPtrToInput = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
664 *esPtrs = (ExpState **)ckalloc(count * sizeof(ExpState *));
666 /* if hash table already exists, delete it and start over */
667 Tcl_DeleteHashTable(*esPtrToInput);
668 *esPtrs = (ExpState **)ckrealloc((char *)*esPtrs,count * sizeof(ExpState *));
670 Tcl_InitHashTable(*esPtrToInput,TCL_ONE_WORD_KEYS);
673 for (inp = input_base;inp;inp=inp->next) {
674 for (fdp = inp->i_list->state_list;fdp;fdp=fdp->next) {
675 /* build map to translate from spawn_id to struct input */
676 expCreateStateToInput(*esPtrToInput,fdp->esPtr,inp);
678 /* build input to ready() */
679 (*esPtrs)[count] = fdp->esPtr;
681 if (real_tty_input(fdp->esPtr)) real_tty = TRUE;
688 *real_tty_caller = real_tty; /* tell caller if we have found that */
689 /* we are using real tty */
697 ClientData clientData,
698 Tcl_Interp *interp, /* Interpreter containing variable. */
699 char *name1, /* Name of variable. */
700 char *name2, /* Second part of variable name. */
701 int flags) /* Information about what happened. */
703 exp_configure_count++;
707 #define finish(x) { status = x; goto done; }
709 static char return_cmd[] = "return";
710 static char interpreter_cmd[] = "interpreter";
715 ClientData clientData,
718 Tcl_Obj *CONST initial_objv[]) /* Argument objects. */
720 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
722 Tcl_Obj *CONST *objv_copy; /* original, for error messages */
723 Tcl_Obj **objv = (Tcl_Obj **) initial_objv;
725 Tcl_UniChar *ustring;
729 #endif /*SIMPLE_EVENT*/
732 int input_count; /* count of struct input descriptors */
734 Tcl_HashTable *esPtrToInput = 0; /* map from ExpState to "struct inputs" */
736 struct keymap *km; /* ptr for above while parsing */
737 Tcl_RegExpInfo reInfo;
740 Tcl_Obj *chanName = 0;
741 int need_to_close_master = FALSE; /* if an eof is received */
742 /* we use this to defer close until later */
744 int next_tty_reset = FALSE; /* if we've seen a single -reset */
745 int next_iread = FALSE;/* if we've seen a single -iread */
746 int next_iwrite = FALSE;/* if we've seen a single -iread */
747 int next_re = FALSE; /* if we've seen a single -re */
748 int next_null = FALSE; /* if we've seen the null keyword */
749 int next_writethru = FALSE;/*if macros should also go to proc output */
750 int next_indices = FALSE;/* if we should write indices */
751 int next_echo = FALSE; /* if macros should be echoed */
752 int status = TCL_OK; /* final return value */
753 int i; /* misc temp */
754 int size; /* size temp */
756 int timeout_simple = TRUE; /* if no or global timeout */
758 int real_tty; /* TRUE if we are interacting with real tty */
759 int tty_changed = FALSE;/* true if we had to change tty modes for */
760 /* interact to work (i.e., to raw, noecho) */
765 Tcl_Obj *replace_user_by_process = 0; /* for -u flag */
767 struct input *input_base;
768 #define input_user input_base
769 struct input *input_default;
770 struct input *inp; /* overused ptr to struct input */
771 struct output *outp; /* overused ptr to struct output */
773 int dash_input_count = 0; /* # of "-input"s seen */
774 int dash_o_count = 0; /* # of "-o"s seen */
775 int arbitrary_timeout;
777 struct action action_timeout; /* common to all */
778 struct action action_eof; /* common to all */
779 struct action **action_eof_ptr; /* allow -input/ouput to */
780 /* leave their eof-action assignable by a later */
782 struct action *action_base = 0;
783 struct keymap **end_km;
786 int configure_count; /* monitor reconfigure events */
787 Tcl_Obj* new_cmd = NULL;
789 if ((objc == 2) && exp_one_arg_braced(objv[1])) {
792 new_cmd = exp_eval_with_one_arg(clientData,interp,objv);
793 if (!new_cmd) return TCL_ERROR;
795 /* Replace old arguments with result of reparse */
796 Tcl_ListObjGetElements (interp, new_cmd, &objc, &objv);
798 } else if ((objc == 3) && streq(Tcl_GetString(objv[1]),"-brace")) {
799 /* expect -brace {...} ... fake command line for reparsing */
801 Tcl_Obj *new_objv[2];
802 new_objv[0] = objv[0];
803 new_objv[1] = objv[2];
805 new_cmd = exp_eval_with_one_arg(clientData,interp,new_objv);
806 if (!new_cmd) return TCL_ERROR;
807 /* Replace old arguments with result of reparse */
808 Tcl_ListObjGetElements (interp, new_cmd, &objc, &objv);
816 default_timeout = EXP_TIME_INFINITY;
817 arbitrary_timeout = EXP_TIME_INFINITY; /* if user specifies */
818 /* a bunch of timeouts with EXP_TIME_INFINITY, this will be */
819 /* left around for us to find. */
821 input_user = new(struct input);
822 input_user->i_list = exp_new_i_simple(expStdinoutGet(),EXP_TEMPORARY); /* stdin by default */
823 input_user->output = 0;
824 input_user->action_eof = &action_eof;
825 input_user->timeout_nominal = EXP_TIME_INFINITY;
826 input_user->action_timeout = 0;
827 input_user->keymap = 0;
829 end_km = &input_user->keymap;
831 action_eof_ptr = &input_user->action_eof;
833 input_default = new(struct input);
834 input_default->i_list = exp_new_i_simple((ExpState *)0,EXP_TEMPORARY); /* fix up later */
835 input_default->output = 0;
836 input_default->action_eof = &action_eof;
837 input_default->timeout_nominal = EXP_TIME_INFINITY;
838 input_default->action_timeout = 0;
839 input_default->keymap = 0;
840 input_default->next = 0; /* no one else */
841 input_user->next = input_default;
843 /* default and common -eof action */
844 action_eof.statement = tsdPtr->cmdObjReturn;
845 action_eof.tty_reset = FALSE;
846 action_eof.iread = FALSE;
847 action_eof.iwrite = FALSE;
850 * Parse the command arguments.
852 for (;objc>0;objc--,objv++) {
853 string = Tcl_GetString(*objv);
854 if (string[0] == '-') {
855 static char *switches[] = {
856 "--", "-exact", "-re", "-input",
857 "-output", "-u", "-o", "-i",
858 "-echo", "-nobuffer", "-indices", "-f",
859 "-reset", "-F", "-iread", "-iwrite",
860 "-eof", "-timeout", "-nobrace", (char *)0
863 EXP_SWITCH_DASH, EXP_SWITCH_EXACT,
864 EXP_SWITCH_REGEXP, EXP_SWITCH_INPUT,
865 EXP_SWITCH_OUTPUT, EXP_SWITCH_USER,
866 EXP_SWITCH_OPPOSITE, EXP_SWITCH_SPAWN_ID,
867 EXP_SWITCH_ECHO, EXP_SWITCH_NOBUFFER,
868 EXP_SWITCH_INDICES, EXP_SWITCH_FAST,
869 EXP_SWITCH_RESET, EXP_SWITCH_CAPFAST,
870 EXP_SWITCH_IREAD, EXP_SWITCH_IWRITE,
871 EXP_SWITCH_EOF, EXP_SWITCH_TIMEOUT,
877 * Allow abbreviations of switches and report an error if we
878 * get an invalid switch.
881 if (Tcl_GetIndexFromObj(interp, *objv, switches, "switch", 0,
885 switch ((enum switches) index) {
886 case EXP_SWITCH_DASH:
887 case EXP_SWITCH_EXACT:
891 case EXP_SWITCH_REGEXP:
893 Tcl_WrongNumArgs(interp,1,objv_copy,"-re pattern");
901 * Try compiling the expression so we can report
902 * any errors now rather then when we first try to
906 if (!(Tcl_GetRegExpFromObj(interp, *objv,
907 TCL_REG_ADVANCED|TCL_REG_BOSONLY))) {
911 case EXP_SWITCH_INPUT:
913 if (dash_input_count == 2) {
915 input_user->next = input_default;
916 } else if (dash_input_count > 2) {
917 struct input *previous_input = inp;
918 inp = new(struct input);
919 previous_input->next = inp;
922 inp->action_eof = &action_eof;
923 action_eof_ptr = &inp->action_eof;
924 inp->timeout_nominal = default_timeout;
925 inp->action_timeout = &action_timeout;
927 end_km = &inp->keymap;
931 Tcl_WrongNumArgs(interp,1,objv_copy,"-input spawn_id");
934 inp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv),
935 EXP_TEMPORARY,inter_updateproc);
940 case EXP_SWITCH_OUTPUT: {
943 /* imply a "-input" */
944 if (dash_input_count == 0) dash_input_count = 1;
946 outp = new(struct output);
948 /* link new output in front of others */
955 Tcl_WrongNumArgs(interp,1,objv_copy,"-output spawn_id");
958 outp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv),
959 EXP_TEMPORARY,inter_updateproc);
963 outp->action_eof = &action_eof;
964 action_eof_ptr = &outp->action_eof;
967 case EXP_SWITCH_USER:
970 Tcl_WrongNumArgs(interp,1,objv_copy,"-u spawn_id");
973 replace_user_by_process = *objv;
975 /* imply a "-input" */
976 if (dash_input_count == 0) dash_input_count = 1;
978 case EXP_SWITCH_OPPOSITE:
979 /* apply following patterns to opposite side */
982 end_km = &input_default->keymap;
984 if (dash_o_count > 0) {
985 exp_error(interp,"cannot use -o more than once");
990 /* imply two "-input" */
991 if (dash_input_count < 2) {
992 dash_input_count = 2;
994 action_eof_ptr = &inp->action_eof;
997 case EXP_SWITCH_SPAWN_ID:
998 /* substitute master */
1002 /* will be used later on */
1004 end_km = &input_default->keymap;
1006 /* imply two "-input" */
1007 if (dash_input_count < 2) {
1008 dash_input_count = 2;
1009 inp = input_default;
1010 action_eof_ptr = &inp->action_eof;
1013 case EXP_SWITCH_ECHO:
1016 case EXP_SWITCH_NOBUFFER:
1017 next_writethru = TRUE;
1019 case EXP_SWITCH_INDICES:
1020 next_indices = TRUE;
1022 case EXP_SWITCH_RESET:
1023 next_tty_reset = TRUE;
1025 case EXP_SWITCH_IREAD:
1028 case EXP_SWITCH_IWRITE:
1031 case EXP_SWITCH_EOF: {
1032 struct action *action;
1035 expDiagLogU("-eof is deprecated, use eof\r\n");
1036 *action_eof_ptr = action = new_action(&action_base);
1037 action->statement = *objv;
1038 action->tty_reset = next_tty_reset;
1039 next_tty_reset = FALSE;
1040 action->iwrite = next_iwrite;
1041 next_iwrite = FALSE;
1042 action->iread = next_iread;
1046 case EXP_SWITCH_TIMEOUT: {
1048 struct action *action;
1049 expDiagLogU("-timeout is deprecated, use timeout\r\n");
1053 Tcl_WrongNumArgs(interp,1,objv_copy,"-timeout time");
1057 if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) {
1062 arbitrary_timeout = t;
1063 /* we need an arbitrary timeout to start */
1064 /* search for lowest one later */
1066 timeout_simple = FALSE;
1067 action = inp->action_timeout = new_action(&action_base);
1068 inp->timeout_nominal = t;
1070 action->statement = *objv;
1071 action->tty_reset = next_tty_reset;
1072 next_tty_reset = FALSE;
1073 action->iwrite = next_iwrite;
1074 next_iwrite = FALSE;
1075 action->iread = next_iread;
1079 case EXP_SWITCH_FAST:
1080 case EXP_SWITCH_CAPFAST:
1081 /* noop compatibility switches for fast mode */
1083 case EXP_SWITCH_NOBRACE:
1084 /* nobrace does nothing but take up space */
1085 /* on the command line which prevents */
1086 /* us from re-expanding any command lines */
1087 /* of one argument that looks like it should */
1088 /* be expanded to multiple arguments. */
1093 static char *options[] = {
1094 "eof", "timeout", "null", (char *)0
1097 EXP_OPTION_EOF, EXP_OPTION_TIMEOUT, EXP_OPTION_NULL
1102 * Match keywords exactly, otherwise they are patterns.
1105 if (Tcl_GetIndexFromObj(interp, *objv, options, "option",
1106 1 /* exact */, &index) != TCL_OK) {
1107 Tcl_ResetResult(interp);
1110 switch ((enum options) index) {
1111 case EXP_OPTION_EOF: {
1112 struct action *action;
1115 *action_eof_ptr = action = new_action(&action_base);
1117 action->statement = *objv;
1119 action->tty_reset = next_tty_reset;
1120 next_tty_reset = FALSE;
1121 action->iwrite = next_iwrite;
1122 next_iwrite = FALSE;
1123 action->iread = next_iread;
1127 case EXP_OPTION_TIMEOUT: {
1129 struct action *action;
1133 Tcl_WrongNumArgs(interp,1,objv_copy,"timeout time [action]");
1136 if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) {
1141 /* we need an arbitrary timeout to start */
1142 /* search for lowest one later */
1143 if (t != -1) arbitrary_timeout = t;
1145 timeout_simple = FALSE;
1146 action = inp->action_timeout = new_action(&action_base);
1147 inp->timeout_nominal = t;
1150 action->statement = *objv;
1152 action->statement = 0;
1155 action->tty_reset = next_tty_reset;
1156 next_tty_reset = FALSE;
1157 action->iwrite = next_iwrite;
1158 next_iwrite = FALSE;
1159 action->iread = next_iread;
1163 case EXP_OPTION_NULL:
1171 * pick up the pattern
1175 km = new(struct keymap);
1177 /* so that we can match in order user specified */
1178 /* link to end of keymap list */
1183 km->echo = next_echo;
1184 km->writethru = next_writethru;
1185 km->indices = next_indices;
1186 km->action.tty_reset = next_tty_reset;
1187 km->action.iwrite = next_iwrite;
1188 km->action.iread = next_iread;
1190 next_indices = next_echo = next_writethru = FALSE;
1191 next_tty_reset = FALSE;
1192 next_iwrite = next_iread = FALSE;
1209 km->action.statement = *objv;
1211 km->action.statement = 0;
1214 expDiagLogU("defining key ");
1215 expDiagLogU(Tcl_GetString(km->keys));
1216 expDiagLogU(", action ");
1217 expDiagLogU(km->action.statement?expPrintify(Tcl_GetString(km->action.statement)):"interpreter");
1218 expDiagLogU("\r\n");
1220 /* imply a "-input" */
1221 if (dash_input_count == 0) dash_input_count = 1;
1224 /* if the user has not supplied either "-output" for the */
1225 /* default two "-input"s, fix them up here */
1227 if (!input_user->output) {
1228 struct output *o = new(struct output);
1230 if (!(esPtr = expStateCurrent(interp,1,1,0))) {
1233 o->i_list = exp_new_i_simple(esPtr,EXP_TEMPORARY);
1235 o->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName),
1236 EXP_TEMPORARY,inter_updateproc);
1241 o->next = 0; /* no one else */
1242 o->action_eof = &action_eof;
1243 input_user->output = o;
1246 if (!input_default->output) {
1247 struct output *o = new(struct output);
1248 o->i_list = exp_new_i_simple(expStdinoutGet(),EXP_TEMPORARY);/* stdout by default */
1249 o->next = 0; /* no one else */
1250 o->action_eof = &action_eof;
1251 input_default->output = o;
1254 /* if user has given "-u" flag, substitute process for user */
1255 /* in first two -inputs */
1256 if (replace_user_by_process) {
1257 /* through away old ones */
1258 exp_free_i(interp,input_user->i_list, inter_updateproc);
1259 exp_free_i(interp,input_default->output->i_list,inter_updateproc);
1261 /* replace with arg to -u */
1262 input_user->i_list = exp_new_i_complex(interp,
1263 Tcl_GetString(replace_user_by_process),
1264 EXP_TEMPORARY,inter_updateproc);
1265 if (!input_user->i_list)
1267 input_default->output->i_list = exp_new_i_complex(interp,
1268 Tcl_GetString(replace_user_by_process),
1269 EXP_TEMPORARY,inter_updateproc);
1270 if (!input_default->output->i_list)
1275 * now fix up for default spawn id
1278 /* user could have replaced it with an indirect, so force update */
1279 if (input_default->i_list->direct == EXP_INDIRECT) {
1280 exp_i_update(interp,input_default->i_list);
1283 if (input_default->i_list->state_list
1284 && (input_default->i_list->state_list->esPtr == EXP_SPAWN_ID_BAD)) {
1286 if (!(esPtr = expStateCurrent(interp,1,1,0))) {
1289 input_default->i_list->state_list->esPtr = esPtr;
1291 /* discard old one and install new one */
1292 exp_free_i(interp,input_default->i_list,inter_updateproc);
1293 input_default->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName),
1294 EXP_TEMPORARY,inter_updateproc);
1295 if (!input_default->i_list)
1301 * check for user attempting to interact with self
1302 * they're almost certainly just fooling around
1305 /* user could have replaced it with an indirect, so force update */
1306 if (input_user->i_list->direct == EXP_INDIRECT) {
1307 exp_i_update(interp,input_user->i_list);
1310 if (input_user->i_list->state_list && input_default->i_list->state_list
1311 && (input_user->i_list->state_list->esPtr == input_default->i_list->state_list->esPtr)) {
1312 exp_error(interp,"cannot interact with self - set spawn_id to a spawned process");
1319 * all data structures are sufficiently set up that we can now
1320 * "finish()" to terminate this procedure
1323 status = update_interact_fds(interp,&input_count,&esPtrToInput,&esPtrs,input_base,1,&configure_count,&real_tty);
1324 if (status == TCL_ERROR) finish(TCL_ERROR);
1327 tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1330 for (inp = input_base,i=0;inp;inp=inp->next,i++) {
1332 inp->timeout_remaining = inp->timeout_nominal;
1337 /* declare ourselves "in sync" with external view of close/indirect */
1338 configure_count = exp_configure_count;
1340 #ifndef SIMPLE_EVENT
1341 /* loop waiting (in event handler) for input */
1343 int te; /* result of Tcl_Eval */
1344 int rc; /* return code from ready. This is further refined by matcher. */
1345 int cc; /* # of chars from read() */
1346 struct action *action = 0;
1347 time_t previous_time;
1348 time_t current_time;
1349 int matchLen; /* # of chars matched */
1350 int skip; /* # of chars not involved in match */
1351 int print; /* # of chars to print */
1352 int oldprinted; /* old version of u->printed */
1353 int change; /* if action requires cooked mode */
1354 int attempt_match = TRUE;
1355 struct input *soonest_input;
1356 int timeout; /* current as opposed to default_timeout */
1359 /* calculate how long to wait */
1360 /* by finding shortest remaining timeout */
1361 if (timeout_simple) {
1362 timeout = default_timeout;
1364 timeout = arbitrary_timeout;
1366 for (inp=input_base;inp;inp=inp->next) {
1367 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
1368 (inp->timeout_remaining <= timeout)) {
1369 soonest_input = inp;
1370 timeout = inp->timeout_remaining;
1374 Tcl_GetTime (&temp_time);
1375 previous_time = temp_time.sec;
1376 /* timestamp here rather than simply saving old */
1377 /* current time (after ready()) to account for */
1378 /* possibility of slow actions */
1380 /* timeout can actually be EXP_TIME_INFINITY here if user */
1381 /* explicitly supplied it in a few cases (or */
1382 /* the count-down code is broken) */
1385 /* update the world, if necessary */
1386 if (configure_count != exp_configure_count) {
1387 status = update_interact_fds(interp,&input_count,
1388 &esPtrToInput,&esPtrs,input_base,1,
1389 &configure_count,&real_tty);
1390 if (status) finish(status);
1393 rc = exp_get_next_event(interp,esPtrs,input_count,&u,timeout,key);
1394 if (rc == EXP_TCLERROR)
1396 if (rc == EXP_RECONFIGURE) continue;
1397 if (rc == EXP_TIMEOUT) {
1398 if (timeout_simple) {
1399 action = &action_timeout;
1402 action = soonest_input->action_timeout;
1403 /* arbitrarily pick first fd out of list */
1404 u = soonest_input->i_list->state_list->esPtr;
1407 if (!timeout_simple) {
1410 Tcl_GetTime (&temp_time);
1411 current_time = temp_time.sec;
1412 time_diff = current_time - previous_time;
1414 /* update all timers */
1415 for (inp=input_base;inp;inp=inp->next) {
1416 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
1417 inp->timeout_remaining -= time_diff;
1418 if (inp->timeout_remaining < 0)
1419 inp->timeout_remaining = 0;
1424 /* at this point, we have some kind of event which can be */
1425 /* immediately processed - i.e. something that doesn't block */
1427 /* figure out who we are */
1428 inp = expStateToInput(esPtrToInput,u);
1431 inp->timeout_remaining = inp->timeout_nominal;
1435 cc = intRead(interp,u,1,0,key);
1442 * Most systems have read() return 0, allowing
1443 * control to fall thru and into this code. On some
1444 * systems (currently HP and new SGI), read() does
1445 * see eof, and it must be detected earlier. Then
1446 * control jumps directly to this EXP_EOF label.
1449 action = inp->action_eof;
1450 attempt_match = FALSE;
1451 skip = expSizeGet(u);
1452 expDiagLog("interact: received eof from spawn_id %s\r\n",u->name);
1453 /* actual close is done later so that we have a */
1454 /* chance to flush out any remaining characters */
1455 need_to_close_master = TRUE;
1461 action = inp->action_timeout;
1462 attempt_match = FALSE;
1463 skip = expSizeGet(u);
1469 if (attempt_match) {
1470 rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo);
1471 if ((rc == EXP_MATCH) && km && km->re) {
1472 intRegExpMatchProcess(interp,u,km,&reInfo,skip);
1475 attempt_match = TRUE;
1479 * dispose of chars that should be skipped
1480 * i.e., chars that cannot possibly be part of a match.
1482 if (km && km->writethru) {
1483 print = skip + matchLen;
1484 } else print = skip;
1486 if (km && km->echo) {
1487 intEcho(u,skip,matchLen);
1489 oldprinted = u->printed;
1492 * If expect has left characters in buffer, it has
1493 * already echoed them to the screen, thus we must
1494 * prevent them being rewritten. Unfortunately this
1495 * gives the possibility of matching chars that have
1496 * already been output, but we do so since the user
1497 * could have avoided it by flushing the output
1500 if (print > u->printed) { /* usual case */
1501 for (outp = inp->output;outp;outp=outp->next) {
1502 struct exp_state_list *fdp;
1503 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
1504 /* send to channel (and log if chan is stdout or devtty) */
1506 * Following should eventually be rewritten to ...WriteCharsAnd...
1508 int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
1509 u->input.buffer + u->printed,
1510 print - u->printed);
1512 expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp));
1513 action = outp->action_eof;
1514 change = (action && action->tty_reset);
1516 if (change && tty_changed)
1517 exp_tty_set(interp,&tty_old,was_raw,was_echo);
1518 te = inter_eval(interp,action,u);
1520 if (change && real_tty) tty_changed =
1521 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1526 case EXP_TCL_RETURN:
1531 /* god knows what the user might */
1532 /* have done to us in the way of */
1533 /* closed fds, so .... */
1534 action = 0; /* reset action */
1545 /* u->printed is now accurate with respect to the buffer */
1546 /* However, we're about to shift the old data out of the */
1547 /* buffer. Thus size, printed, and echoed must be */
1550 /* first update size based on skip information */
1551 /* then set skip to the total amount skipped */
1553 size = expSizeGet(u);
1554 if (rc == EXP_MATCH) {
1555 action = &km->action;
1560 ustring = u->input.buffer;
1561 memmove(ustring, ustring + skip, size * sizeof(Tcl_UniChar));
1564 ustring = u->input.buffer;
1567 memcpy(ustring, ustring + skip, size * sizeof(Tcl_UniChar));
1570 u->input.use = size;
1572 /* now update printed based on total amount skipped */
1575 /* if more skipped than printed (i.e., keymap encountered) */
1576 /* for printed positive */
1577 if (u->printed < 0) u->printed = 0;
1579 /* if we are in the middle of a match, force the next event */
1580 /* to wait for more data to arrive */
1581 u->force_read = (rc == EXP_CANMATCH);
1583 /* finally reset echoed if necessary */
1584 if (rc != EXP_CANMATCH) {
1585 if (skip >= oldprinted + u->echoed) u->echoed = 0;
1588 if (rc == EXP_EOF) {
1589 if (u->close_on_eof) {
1590 exp_close(interp,u);
1592 need_to_close_master = FALSE;
1597 change = (action && action->tty_reset);
1598 if (change && tty_changed)
1599 exp_tty_set(interp,&tty_old,was_raw,was_echo);
1601 te = inter_eval(interp,action,u);
1603 if (change && real_tty) tty_changed =
1604 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1609 case EXP_TCL_RETURN:
1614 /* god knows what the user might */
1615 /* have done to us in the way of */
1616 /* closed fds, so .... */
1617 action = 0; /* reset action */
1625 #else /* SIMPLE_EVENT */
1626 /* deferred_interrupt = FALSE;*/
1628 int te; /* result of Tcl_Eval */
1629 ExpState *u; /*master*/
1630 int rc; /* return code from ready. This is further */
1631 /* refined by matcher. */
1632 int cc; /* chars count from read() */
1633 struct action *action = 0;
1634 time_t previous_time;
1635 time_t current_time;
1637 int change; /* if action requires cooked mode */
1638 int attempt_match = TRUE;
1639 struct input *soonest_input;
1640 int print; /* # of chars to print */
1641 int oldprinted; /* old version of u->printed */
1643 int timeout; /* current as opposed to default_timeout */
1645 if (-1 == (pid = fork())) {
1646 exp_error(interp,"fork: %s",Tcl_PosixError(interp));
1651 * This is a new child process.
1652 * It exists only for this interact command and will go away when
1653 * the interact returns.
1655 * The purpose of this child process is to read output from the
1656 * spawned process and send it to the user tty.
1657 * (See diagram above.)
1660 exp_close(interp,expStdinoutGet());
1662 u = esPtrs[1]; /* get 2nd ExpState */
1667 /* calculate how long to wait */
1668 /* by finding shortest remaining timeout */
1669 if (timeout_simple) {
1670 timeout = default_timeout;
1672 timeout = arbitrary_timeout;
1674 for (inp=input_base;inp;inp=inp->next) {
1675 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
1676 (inp->timeout_remaining < timeout))
1677 soonest_input = inp;
1678 timeout = inp->timeout_remaining;
1681 Tcl_GetTime (&temp_time);
1682 previous_time = temp_time.sec;
1683 /* timestamp here rather than simply saving old */
1684 /* current time (after ready()) to account for */
1685 /* possibility of slow actions */
1687 /* timeout can actually be EXP_TIME_INFINITY here if user */
1688 /* explicitly supplied it in a few cases (or */
1689 /* the count-down code is broken) */
1692 /* +1 so we can look at the "other" file descriptor */
1693 rc = exp_get_next_event(interp,esPtrs+1,input_count,&u,timeout,key);
1694 if (!timeout_simple) {
1697 Tcl_GetTime (&temp_time);
1698 current_time = temp_time.sec;
1699 time_diff = current_time - previous_time;
1701 /* update all timers */
1702 for (inp=input_base;inp;inp=inp->next) {
1703 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
1704 inp->timeout_remaining -= time_diff;
1705 if (inp->timeout_remaining < 0)
1706 inp->timeout_remaining = 0;
1711 /* at this point, we have some kind of event which can be */
1712 /* immediately processed - i.e. something that doesn't block */
1714 /* figure out who we are */
1715 inp = expStateToInput(esPtrToInput,u);
1719 cc = intRead(interp,u,0,0,key);
1724 * Most systems have read() return 0, allowing
1725 * control to fall thru and into this code. On some
1726 * systems (currently HP and new SGI), read() does
1727 * see eof, and it must be detected earlier. Then
1728 * control jumps directly to this EXP_EOF label.
1731 action = inp->action_eof;
1732 attempt_match = FALSE;
1733 skip = expSizeGet(u);
1735 expDiagLog("interact: child received eof from spawn_id %s\r\n",u->name);
1736 exp_close(interp,u);
1745 if (attempt_match) {
1746 rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo);
1747 if ((rc == EXP_MATCH) && km && km->re) {
1748 intRegExpMatchProcess(interp,u,km,&reInfo,skip);
1751 attempt_match = TRUE;
1754 /* dispose of chars that should be skipped */
1756 /* skip is chars not involved in match */
1757 /* print is with chars involved in match */
1759 if (km && km->writethru) {
1760 print = skip + matchLen;
1761 } else print = skip;
1763 if (km && km->echo) {
1764 intEcho(u,skip,matchLen);
1766 oldprinted = u->printed;
1768 /* If expect has left characters in buffer, it has */
1769 /* already echoed them to the screen, thus we must */
1770 /* prevent them being rewritten. Unfortunately this */
1771 /* gives the possibility of matching chars that have */
1772 /* already been output, but we do so since the user */
1773 /* could have avoided it by flushing the output */
1774 /* buffers directly. */
1775 if (print > u->printed) { /* usual case */
1776 for (outp = inp->output;outp;outp=outp->next) {
1777 struct exp_state_list *fdp;
1778 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
1779 /* send to channel (and log if chan is stdout or devtty) */
1780 int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
1781 u->input.buffer + u->printed,
1782 print - u->printed);
1784 expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp));
1785 action = outp->action_eof;
1787 te = inter_eval(interp,action,u);
1793 case EXP_TCL_RETURN:
1798 /* god knows what the user might */
1799 /* have done to us in the way of */
1800 /* closed fds, so .... */
1801 action = 0; /* reset action */
1812 /* u->printed is now accurate with respect to the buffer */
1813 /* However, we're about to shift the old data out of the */
1814 /* buffer. Thus size, printed, and echoed must be */
1817 /* first update size based on skip information */
1818 /* then set skip to the total amount skipped */
1820 size = expSizeGet(u);
1821 if (rc == EXP_MATCH) {
1822 action = &km->action;
1827 memcpy(u->buffer, u->buffer + skip, size);
1832 memcpy(u->buffer, u->buffer + skip, size);
1835 Tcl_SetObjLength(size);
1837 /* now update printed based on total amount skipped */
1840 /* if more skipped than printed (i.e., keymap encountered) */
1841 /* for printed positive */
1842 if (u->printed < 0) u->printed = 0;
1844 /* if we are in the middle of a match, force the next event */
1845 /* to wait for more data to arrive */
1846 u->force_read = (rc == EXP_CANMATCH);
1848 /* finally reset echoed if necessary */
1849 if (rc != EXP_CANMATCH) {
1850 if (skip >= oldprinted + u->echoed) u->echoed = 0;
1854 te = inter_eval(interp,action,u);
1859 case EXP_TCL_RETURN:
1864 /* god knows what the user might */
1865 /* have done to us in the way of */
1866 /* closed fds, so .... */
1867 action = 0; /* reset action */
1876 * This is the original Expect process.
1878 * It now loops, reading keystrokes from the user tty
1879 * and sending them to the spawned process.
1880 * (See diagram above.)
1885 #if defined(SIGCLD) && !defined(SIGCHLD)
1886 #define SIGCHLD SIGCLD
1888 expDiagLog("fork = %d\r\n",pid);
1889 signal(SIGCHLD,sigchld_handler);
1891 /* tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);*/
1893 u = esPtrs[0]; /* get 1st ExpState */
1897 /* calculate how long to wait */
1898 /* by finding shortest remaining timeout */
1899 if (timeout_simple) {
1900 timeout = default_timeout;
1902 timeout = arbitrary_timeout;
1904 for (inp=input_base;inp;inp=inp->next) {
1905 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
1906 (inp->timeout_remaining < timeout))
1907 soonest_input = inp;
1908 timeout = inp->timeout_remaining;
1911 Tcl_GetTime (&temp_time);
1912 previous_time = temp_time.sec;
1913 /* timestamp here rather than simply saving old */
1914 /* current time (after ready()) to account for */
1915 /* possibility of slow actions */
1917 /* timeout can actually be EXP_TIME_INFINITY here if user */
1918 /* explicitly supplied it in a few cases (or */
1919 /* the count-down code is broken) */
1922 rc = exp_get_next_event(interp,esPtrs,input_count,&u,timeout,key);
1923 if (!timeout_simple) {
1926 Tcl_GetTime (&temp_time);
1927 current_time = temp_time.sec;
1928 time_diff = current_time - previous_time;
1930 /* update all timers */
1931 for (inp=input_base;inp;inp=inp->next) {
1932 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
1933 inp->timeout_remaining -= time_diff;
1934 if (inp->timeout_remaining < 0)
1935 inp->timeout_remaining = 0;
1940 /* at this point, we have some kind of event which can be */
1941 /* immediately processed - i.e. something that doesn't block */
1943 /* figure out who we are */
1944 inp = expStateToInput(esPtrToInput,u);
1948 cc = intRead(interp,u,0,1,key);
1951 } else if (cc == EXP_CHILD_EOF) {
1952 /* user could potentially have two outputs in which */
1953 /* case we might be looking at the wrong one, but */
1954 /* the likelihood of this is nil */
1955 action = inp->output->action_eof;
1956 attempt_match = FALSE;
1957 skip = expSizeGet(u);
1959 expDiagLogU("interact: process died/eof\r\n");
1960 clean_up_after_child(interp,esPtrs[1]);
1966 * Most systems have read() return 0, allowing
1967 * control to fall thru and into this code. On some
1968 * systems (currently HP and new SGI), read() does
1969 * see eof, and it must be detected earlier. Then
1970 * control jumps directly to this EXP_EOF label.
1973 action = inp->action_eof;
1974 attempt_match = FALSE;
1975 skip = expSizeGet(u);
1977 expDiagLogU("user sent EOF or disappeared\n\n");
1986 if (attempt_match) {
1987 rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo);
1988 if ((rc == EXP_MATCH) && km && km->re) {
1989 intRegExpMatchProcess(interp,u,km,&reInfo,skip);
1992 attempt_match = TRUE;
1995 /* dispose of chars that should be skipped */
1997 /* skip is chars not involved in match */
1998 /* print is with chars involved in match */
2000 if (km && km->writethru) {
2001 print = skip + matchLen;
2002 } else print = skip;
2004 if (km && km->echo) {
2005 intEcho(u,skip,matchLen);
2007 oldprinted = u->printed;
2009 /* If expect has left characters in buffer, it has */
2010 /* already echoed them to the screen, thus we must */
2011 /* prevent them being rewritten. Unfortunately this */
2012 /* gives the possibility of matching chars that have */
2013 /* already been output, but we do so since the user */
2014 /* could have avoided it by flushing the output */
2015 /* buffers directly. */
2016 if (print > u->printed) { /* usual case */
2017 for (outp = inp->output;outp;outp=outp->next) {
2018 struct exp_state_list *fdp;
2019 for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
2020 /* send to channel (and log if chan is stdout or devtty) */
2021 int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
2022 u->input.buffer + u->printed,
2023 print - u->printed);
2025 expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp));
2026 clean_up_after_child(interp,fdp->esPtr);
2027 action = outp->action_eof;
2028 change = (action && action->tty_reset);
2029 if (change && tty_changed)
2030 exp_tty_set(interp,&tty_old,was_raw,was_echo);
2031 te = inter_eval(interp,action,u);
2033 if (change && real_tty) tty_changed =
2034 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
2039 case EXP_TCL_RETURN:
2044 /* god knows what the user might */
2045 /* have done to us in the way of */
2046 /* closed fds, so .... */
2047 action = 0; /* reset action */
2058 /* u->printed is now accurate with respect to the buffer */
2059 /* However, we're about to shift the old data out of the */
2060 /* buffer. Thus size, printed, and echoed must be */
2063 /* first update size based on skip information */
2064 /* then set skip to the total amount skipped */
2066 size = expSizeGet(u);
2067 if (rc == EXP_MATCH) {
2068 action = &km->action;
2073 memcpy(u->buffer, u->buffer + skip, size);
2078 memcpy(u->buffer, u->buffer + skip, size);
2081 Tcl_SetObjLength(size);
2083 /* now update printed based on total amount skipped */
2086 /* if more skipped than printed (i.e., keymap encountered) */
2087 /* for printed positive */
2088 if (u->printed < 0) u->printed = 0;
2090 /* if we are in the middle of a match, force the next event */
2091 /* to wait for more data to arrive */
2092 u->force_read = (rc == EXP_CANMATCH);
2094 /* finally reset echoed if necessary */
2095 if (rc != EXP_CANMATCH) {
2096 if (skip >= oldprinted + u->echoed) u->echoed = 0;
2100 change = (action && action->tty_reset);
2101 if (change && tty_changed)
2102 exp_tty_set(interp,&tty_old,was_raw,was_echo);
2104 te = inter_eval(interp,action,u);
2106 if (change && real_tty) tty_changed =
2107 exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
2112 case EXP_TCL_RETURN:
2117 /* god knows what the user might */
2118 /* have done to us in the way of */
2119 /* closed fds, so .... */
2120 action = 0; /* reset action */
2129 #endif /* SIMPLE_EVENT */
2133 /* force child to exit upon eof from master */
2135 exit(SPAWNED_PROCESS_DIED);
2137 #endif /* SIMPLE_EVENT */
2139 if (need_to_close_master && u->close_on_eof) exp_close(interp,u);
2141 if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo);
2142 if (esPtrs) ckfree((char *)esPtrs);
2143 if (esPtrToInput) Tcl_DeleteHashTable(esPtrToInput);
2144 free_input(interp,input_base);
2145 free_action(action_base);
2147 if (new_cmd) { Tcl_DecrRefCount (new_cmd); }
2151 if (new_cmd) { Tcl_DecrRefCount (new_cmd); }
2155 /* version of Tcl_Eval for interact */
2159 struct action *action,
2164 if (action->iwrite) {
2165 out("spawn_id",esPtr->name);
2168 if (action->statement) {
2169 status = Tcl_EvalObjEx(interp,action->statement,0);
2171 expStdoutLogU("\r\n",1);
2172 status = exp_interpreter(interp,(Tcl_Obj *)0);
2179 free_keymap(struct keymap *km)
2181 if (km == 0) return;
2182 free_keymap(km->next);
2188 free_action(struct action *a)
2190 struct action *next;
2205 free_input(interp,i->next);
2207 exp_free_i(interp,i->i_list,inter_updateproc);
2208 free_output(interp,i->output);
2209 free_keymap(i->keymap);
2213 static struct action *
2214 new_action(struct action **base)
2216 struct action *o = new(struct action);
2218 /* stick new action into beginning of list of all actions */
2231 free_output(interp,o->next);
2232 exp_free_i(interp,o->i_list,inter_updateproc);
2238 static struct exp_cmd_data cmd_data[] = {
2239 {"interact", Exp_InteractObjCmd, 0, 0, 0},
2243 exp_init_interact_cmds(Tcl_Interp *interp)
2245 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2247 exp_create_commands(interp,cmd_data);
2249 tsdPtr->cmdObjReturn = Tcl_NewStringObj("return",6);
2250 Tcl_IncrRefCount(tsdPtr->cmdObjReturn);
2252 tsdPtr->cmdObjInterpreter = Tcl_NewStringObj("interpreter",11);
2253 Tcl_IncrRefCount(tsdPtr->cmdObjInterpreter);