1/* interact (using select) - give user keyboard control
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#include "expect_cf.h"
12#include <stdio.h>
13#ifdef HAVE_INTTYPES_H
14#  include <inttypes.h>
15#endif
16#include <sys/types.h>
17#ifdef HAVE_UNISTD_H
18# include <unistd.h>
19#endif
20
21#ifdef TIME_WITH_SYS_TIME
22# include <sys/time.h>
23# include <time.h>
24#else
25# if HAVE_SYS_TIME_H
26#  include <sys/time.h>
27# else
28#  include <time.h>
29# endif
30#endif
31
32#ifdef HAVE_SYS_WAIT_H
33#include <sys/wait.h>
34#endif
35
36#include <ctype.h>
37
38#include "tclInt.h"
39#include "string.h"
40
41#include "exp_tty_in.h"
42#include "exp_rename.h"
43#include "exp_prog.h"
44#include "exp_command.h"
45#include "exp_log.h"
46#include "exp_event.h" /* exp_get_next_event decl */
47
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
51#endif
52
53typedef struct ThreadSpecificData {
54    Tcl_Obj *cmdObjReturn;
55    Tcl_Obj *cmdObjInterpreter;
56} ThreadSpecificData;
57
58static Tcl_ThreadDataKey dataKey;
59
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);
66
67/*
68 * tests if we are running this using a real tty
69 *
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
77 * were.
78 */
79#if 0
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)))
82#endif
83
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))))
86
87#define new(x)	(x *)ckalloc(sizeof(x))
88
89struct action {
90	Tcl_Obj *statement;
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 */
95};
96
97struct keymap {
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 */
101	int case_sensitive;
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;
106	struct keymap *next;
107};
108
109struct output {
110	struct exp_i *i_list;
111	struct action *action_eof;
112	struct output *next;
113};
114
115struct input {
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 */
123	struct input *next;
124};
125
126/*
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.
130 */
131
132struct input *
133expStateToInput(
134    Tcl_HashTable *hash,
135    ExpState *esPtr)
136{
137    Tcl_HashEntry *entry = Tcl_FindHashEntry(hash,(char *)esPtr);
138
139    if (!entry) {
140	/* should never happen */
141	return 0;
142    }
143    return ((struct input *)Tcl_GetHashValue(entry));
144}
145
146void
147expCreateStateToInput(
148    Tcl_HashTable *hash,
149    ExpState *esPtr,
150    struct input *inp)
151{
152    Tcl_HashEntry *entry;
153    int newPtr;
154
155    entry = Tcl_CreateHashEntry(hash,(char *)esPtr,&newPtr);
156    Tcl_SetHashValue(entry,(ClientData)inp);
157}
158
159static void free_input(Tcl_Interp *interp, struct input *i);
160static void free_keymap(struct keymap *km);
161static void free_output(Tcl_Interp *interp, struct output *o);
162static void free_action(struct action *a);
163static struct action *new_action(struct action **base);
164static int inter_eval(
165    Tcl_Interp *interp,
166    struct action *action,
167    ExpState *esPtr);
168
169/* intMatch() accepts user keystrokes and returns one of MATCH,
170CANMATCH, or CANTMATCH.  These describe whether the keystrokes match a
171key sequence, and could or can't if more characters arrive.  The
172function assigns a matching keymap if there is a match or can-match.
173A matching keymap is assigned on can-match so we know whether to echo
174or not.
175
176intMatch is optimized (if you can call it that) towards a small
177number of key mappings, but still works well for large maps, since no
178function calls are made, and we stop as soon as there is a single-char
179mismatch, and go on to the next one.  A hash table or compiled DFA
180probably would not buy very much here for most maps.
181
182The basic idea of how this works is it does a smart sequential search.
183At each position of the input string, we attempt to match each of the
184keymaps.  If at least one matches, the first match is returned.
185
186If there is a CANMATCH and there are more keymaps to try, we continue
187trying.  If there are no more keymaps to try, we stop trying and
188return with an indication of the first keymap that can match.
189
190Note that I've hacked up the regexp pattern matcher in two ways.  One
191is to force the pattern to always be anchored at the front.  That way,
192it doesn't waste time attempting to match later in the string (before
193we're ready).  The other is to return can-match.
194
195*/
196
197static int
198intMatch(
199    ExpState *esPtr,
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)
205{
206    Tcl_UniChar *string;
207    struct keymap *km;
208    char *ks;		/* string from a keymap */
209
210    Tcl_UniChar *start_search;	/* where in string to start searching */
211    int offset;		/* # of chars from string to start searching */
212
213    Tcl_UniChar *string_end;
214    int numchars;
215    int rm_nulls;		/* skip nulls if true */
216    Tcl_UniChar ch;
217
218    string   = esPtr->input.buffer;
219    numchars = esPtr->input.use; /* Actually #chars */
220
221    /* assert (*km == 0) */
222
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. */
226    if (!keymap) {
227	*skip = numchars;
228	return(EXP_CANTMATCH);
229    }
230
231    rm_nulls = esPtr->rm_nulls;
232
233    string_end = string + numchars;
234
235    /*
236     * Maintain both a character index and a string pointer so we
237     * can easily index into either the UTF or the Unicode representations.
238     */
239
240    for (start_search = string, offset = 0;
241	 start_search < string_end;
242	 start_search ++, offset++) {
243
244	ch = *start_search;
245
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 */
249
250	for (km=keymap;km;km=km->next) {
251	    Tcl_UniChar *s;	/* current character being examined */
252
253	    if (km->null) {
254		if (ch == 0) {
255		    *skip = start_search-string;
256		    *matchLen = 1;	/* s - start_search == 1 */
257		    *km_match = km;
258		    return(EXP_MATCH);
259	        }
260	    } else if (!km->re) {
261		int kslen;
262		Tcl_UniChar sch, ksch;
263
264		/* fixed string */
265
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! */
269		    if (*ks == 0) {
270			*skip = start_search-string;
271			*matchLen = s-start_search;
272			*km_match = km;
273			return(EXP_MATCH);
274		    }
275
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 */
279
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;
284			break;
285		    }
286
287		    sch = *s;
288		    kslen = Tcl_UtfToUniChar(ks, &ksch);
289
290		    if (sch == ksch) continue;
291		    if ((sch == '\0') && rm_nulls) {
292			kslen = 0;
293			continue;
294		    }
295		    break;
296		}
297	    } else {
298		/* regexp */
299		Tcl_RegExp re;
300		int flags;
301		int result;
302		Tcl_Obj* buf;
303
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;
307
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);
314		if (result > 0) {
315		    *km_match = km;
316		    *skip = start_search-string;
317		    Tcl_RegExpGetInfo(re, info);
318		    *matchLen = info->matches[0].end;
319		    return EXP_MATCH;
320		} else if (result == 0) {
321		    Tcl_RegExpGetInfo(re, info);
322
323		    /*
324		     * Check to see if there was a partial match starting
325		     * at the current character.
326		     */
327		    if (info->extendStart == 0) {
328			if (!*km_match) *km_match = km;
329		    }
330		}
331	    }
332	}
333    }
334
335    if (*km_match) {
336	/* report CANMATCH for -re and -ex */
337
338	/*
339	 * since canmatch is only detected after we've advanced too far,
340	 * adjust start_search back to make other computations simpler
341	 */
342	start_search--;
343
344	*skip = start_search - string;
345	*matchLen = string_end - start_search;
346	return(EXP_CANMATCH);
347    }
348
349    *skip = start_search-string;
350    return(EXP_CANTMATCH);
351}
352
353/* put regexp result in variables */
354static void
355intRegExpMatchProcess(
356    Tcl_Interp *interp,
357    ExpState *esPtr,
358    struct keymap *km,	/* ptr for above while parsing */
359    Tcl_RegExpInfo *info,
360    int offset)
361{
362    char name[20], value[20];
363    int i;
364    Tcl_Obj* buf = Tcl_NewUnicodeObj (esPtr->input.buffer,esPtr->input.use);
365
366    for (i=0;i<=info->nsubs;i++) {
367	int start, end;
368	Tcl_Obj *val;
369
370	start = info->matches[i].start + offset;
371	if (start == -1) continue;
372	end = (info->matches[i].end-1) + offset;
373
374	if (km->indices) {
375	    /* start index */
376	    sprintf(name,"%d,start",i);
377	    sprintf(value,"%d",start);
378	    out(name,value);
379
380	    /* end index */
381	    sprintf(name,"%d,end",i);
382	    sprintf(value,"%d",end);
383	    out(name,value);
384	}
385
386	/* string itself */
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);
393    }
394    Tcl_DecrRefCount (buf);
395}
396
397/*
398 * echo chars
399 */
400static void
401intEcho(
402    ExpState *esPtr,
403    int skipBytes,
404    int matchBytes)
405{
406    int seenBytes;	/* either printed or echoed */
407    int echoBytes;
408    int offsetBytes;
409
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;
418    }
419
420    (void) expWriteCharsUni(esPtr,
421			    esPtr->input.buffer + offsetBytes,
422		   echoBytes);
423
424    esPtr->echoed = matchBytes + skipBytes - esPtr->printed;
425}
426
427/*
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.
430 */
431static int
432intRead(
433    Tcl_Interp *interp,
434    ExpState *esPtr,
435    int warnOnBufferFull,
436    int interruptible,
437    int key)
438{
439    Tcl_UniChar *eobOld;  /* old end of buffer */
440    int cc;
441    int numchars;
442    Tcl_UniChar *str;
443
444    str      = esPtr->input.buffer;
445    numchars = esPtr->input.use;
446    eobOld   = str + numchars;
447
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) {
451	/*
452	 * In theory, interact could be invoked when this situation
453	 * already exists, hence the "probably" in the warning below
454	 */
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");
461	}
462	exp_buffer_shuffle(interp,esPtr,0,INTER_OUT,"interact");
463    }
464    if (!interruptible) {
465        cc = Tcl_ReadChars(esPtr->channel, esPtr->input.newchars,
466			   esPtr->input.max - esPtr->input.use,
467			   0 /* no append */);
468    } else {
469#ifdef SIMPLE_EVENT
470        cc = intIRead(esPtr->channel, esPtr->input.newchars,
471		      esPtr->input.max - esPtr->input.use,
472		      0 /* no append */);
473#endif
474    }
475
476    if (cc > 0) {
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;
481
482	expDiagLog("spawn id %s sent <",esPtr->name);
483	expDiagLogU(expPrintifyUni(eobOld,cc));
484	expDiagLogU(">\r\n");
485
486	esPtr->key = key;
487    }
488    return cc;
489}
490
491
492
493#ifdef SIMPLE_EVENT
494
495/*
496
497The way that the "simple" interact works is that the original Expect
498process reads from the tty and writes to the spawned process.  A child
499process is forked to read from the spawned process and write to the
500tty.  It looks like this:
501
502                        user
503                    --> tty >--
504                   /           \
505                  ^             v
506                child        original
507               process        Expect
508                  ^          process
509                  |             v
510                   \           /
511                    < spawned <
512                      process
513
514*/
515
516
517
518#ifndef WEXITSTATUS
519#define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
520#endif
521
522#include <setjmp.h>
523
524#ifdef HAVE_SIGLONGJMP
525static sigjmp_buf env;                /* for interruptable read() */
526#else
527static jmp_buf env;		/* for interruptable read() */
528#endif  /* HAVE_SIGLONGJMP */
529
530static int reading;		/* while we are reading */
531				/* really, while "env" is valid */
532static 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 */
535
536static void
537sigchld_handler()
538{
539  if (reading) {
540#ifdef HAVE_SIGLONGJMP
541     siglongjmp(env,1);
542#else
543    longjmp(env,1);
544#endif  /* HAVE_SIGLONGJMP */
545  }
546  deferred_interrupt = TRUE;
547}
548
549#define EXP_CHILD_EOF -100
550
551/*
552 * Name: expIRead, do an interruptable read
553 *
554 * intIRead() reads from chars from the user.
555 *
556 * It returns early if it detects the death of a proc (either the spawned
557 * process or the child (surrogate).
558 */
559static int
560intIRead(
561    Tcl_Channel channel,
562    Tcl_Obj *obj,
563    int size,
564    int flags)
565{
566    int cc = EXP_CHILD_EOF;
567
568    if (deferred_interrupt) return(cc);
569
570    if (
571#ifdef HAVE_SIGLONGJMP
572	0 == sigsetjmp(env,1)
573#else
574	0 == setjmp(env)
575#endif  /* HAVE_SIGLONGJMP */
576	) {
577	reading = TRUE;
578	cc = Tcl_ReadChars(channel,obj,size,flags);
579    }
580    reading = FALSE;
581    return(cc);
582}
583
584/* exit status for the child process created by cmdInteract */
585#define CHILD_DIED		-2
586#define SPAWNED_PROCESS_DIED	-3
587
588static void
589clean_up_after_child(
590    Tcl_Interp *interp,
591    ExpState *esPtr)
592{
593    expWaitOnOne(); /* wait for slave */
594    expWaitOnOne(); /* wait for child */
595
596    deferred_interrupt = FALSE;
597    if (esPtr->close_on_eof) {
598    exp_close(interp,esPtr);
599}
600}
601#endif /*SIMPLE_EVENT*/
602
603static int
604update_interact_fds(
605    Tcl_Interp *interp,
606    int *esPtrCount,
607    Tcl_HashTable **esPtrToInput,	/* map from ExpStates to "struct inputs" */
608    ExpState ***esPtrs,
609    struct input *input_base,
610    int do_indirect,		/* if true do indirects */
611    int *config_count,
612    int *real_tty_caller)
613{
614	struct input *inp;
615	struct output *outp;
616	struct exp_state_list *fdp;
617	int count;
618
619	int real_tty = FALSE;
620
621	*config_count = exp_configure_count;
622
623	count = 0;
624	for (inp = input_base;inp;inp=inp->next) {
625
626		if (do_indirect) {
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);
631			}
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);
635				}
636			}
637		}
638
639		/* revalidate all input descriptors */
640		for (fdp = inp->i_list->state_list;fdp;fdp=fdp->next) {
641		    count++;
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")) {
645			return(TCL_ERROR);
646		    }
647		}
648
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"))
655				    return(TCL_ERROR);
656			    }
657			}
658		}
659	}
660	if (!do_indirect) return TCL_OK;
661
662	if (*esPtrToInput == 0) {
663	    *esPtrToInput = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
664	    *esPtrs = (ExpState **)ckalloc(count * sizeof(ExpState *));
665	} else {
666	    /* if hash table already exists, delete it and start over */
667	    Tcl_DeleteHashTable(*esPtrToInput);
668	    *esPtrs = (ExpState **)ckrealloc((char *)*esPtrs,count * sizeof(ExpState *));
669	}
670	Tcl_InitHashTable(*esPtrToInput,TCL_ONE_WORD_KEYS);
671
672	count = 0;
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);
677
678		/* build input to ready() */
679		(*esPtrs)[count] = fdp->esPtr;
680
681		if (real_tty_input(fdp->esPtr)) real_tty = TRUE;
682
683		count++;
684	    }
685	}
686	*esPtrCount = count;
687
688	*real_tty_caller = real_tty; /* tell caller if we have found that */
689					/* we are using real tty */
690
691	return TCL_OK;
692}
693
694/*ARGSUSED*/
695static char *
696inter_updateproc(
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. */
702{
703	exp_configure_count++;
704	return 0;
705}
706
707#define finish(x)	{ status = x; goto done; }
708
709static char return_cmd[] = "return";
710static char interpreter_cmd[] = "interpreter";
711
712/*ARGSUSED*/
713int
714Exp_InteractObjCmd(
715    ClientData clientData,
716    Tcl_Interp *interp,
717    int objc,
718    Tcl_Obj *CONST initial_objv[])		/* Argument objects. */
719{
720    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
721
722    Tcl_Obj *CONST *objv_copy;	/* original, for error messages */
723    Tcl_Obj **objv = (Tcl_Obj **) initial_objv;
724    char *string;
725    Tcl_UniChar *ustring;
726
727#ifdef SIMPLE_EVENT
728    int pid;
729#endif /*SIMPLE_EVENT*/
730
731    /*declarations*/
732    int input_count;	/* count of struct input descriptors */
733
734    Tcl_HashTable *esPtrToInput = 0;	/* map from ExpState to "struct inputs" */
735    ExpState **esPtrs;
736    struct keymap *km;	/* ptr for above while parsing */
737    Tcl_RegExpInfo reInfo;
738    ExpState *u = 0;
739    ExpState *esPtr = 0;
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 */
743
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 */
755
756    int timeout_simple = TRUE;	/* if no or global timeout */
757
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) */
761    int was_raw;
762    int was_echo;
763    exp_tty tty_old;
764
765    Tcl_Obj *replace_user_by_process = 0; /* for -u flag */
766
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 */
772
773    int dash_input_count = 0; /* # of "-input"s seen */
774    int dash_o_count = 0; /* # of "-o"s seen */
775    int arbitrary_timeout;
776    int default_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 */
781		/* -eof */
782    struct action *action_base = 0;
783    struct keymap **end_km;
784
785    int key;
786    int configure_count;	/* monitor reconfigure events */
787    Tcl_Obj* new_cmd = NULL;
788
789    if ((objc == 2) && exp_one_arg_braced(objv[1])) {
790	/* expect {...} */
791
792	new_cmd = exp_eval_with_one_arg(clientData,interp,objv);
793	if (!new_cmd) return TCL_ERROR;
794
795	/* Replace old arguments with result of reparse */
796	Tcl_ListObjGetElements (interp, new_cmd, &objc, &objv);
797
798    } else if ((objc == 3) && streq(Tcl_GetString(objv[1]),"-brace")) {
799	/* expect -brace {...} ... fake command line for reparsing */
800
801	Tcl_Obj *new_objv[2];
802	new_objv[0] = objv[0];
803	new_objv[1] = objv[2];
804
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);
809    }
810
811    objv_copy = objv;
812
813    objv++;
814    objc--;
815
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. */
820
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;
828
829    end_km = &input_user->keymap;
830    inp = input_user;
831    action_eof_ptr = &input_user->action_eof;
832
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;
842
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;
848
849    /*
850     * Parse the command arguments.
851     */
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
861	    };
862	    enum switches {
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,
872		EXP_SWITCH_NOBRACE
873	    };
874	    int index;
875
876	    /*
877	     * Allow abbreviations of switches and report an error if we
878	     * get an invalid switch.
879	     */
880
881	    if (Tcl_GetIndexFromObj(interp, *objv, switches, "switch", 0,
882		    &index) != TCL_OK) {
883		goto error;
884	    }
885	    switch ((enum switches) index) {
886		case EXP_SWITCH_DASH:
887		case EXP_SWITCH_EXACT:
888		    objc--;
889		    objv++;
890		    goto pattern;
891		case EXP_SWITCH_REGEXP:
892		    if (objc < 1) {
893			Tcl_WrongNumArgs(interp,1,objv_copy,"-re pattern");
894		    goto error;
895		    }
896		    next_re = TRUE;
897		    objc--;
898		    objv++;
899
900		    /*
901		     * Try compiling the expression so we can report
902		     * any errors now rather then when we first try to
903		     * use it.
904		     */
905
906		    if (!(Tcl_GetRegExpFromObj(interp, *objv,
907			    TCL_REG_ADVANCED|TCL_REG_BOSONLY))) {
908		    goto error;
909		    }
910		    goto pattern;
911		case EXP_SWITCH_INPUT:
912		    dash_input_count++;
913		    if (dash_input_count == 2) {
914			inp = input_default;
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;
920		    }
921		    inp->output = 0;
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;
926		    inp->keymap = 0;
927		    end_km = &inp->keymap;
928		    inp->next = 0;
929		    objc--;objv++;
930		    if (objc < 1) {
931			Tcl_WrongNumArgs(interp,1,objv_copy,"-input spawn_id");
932		    goto error;
933		    }
934		    inp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv),
935			    EXP_TEMPORARY,inter_updateproc);
936		if (!inp->i_list) {
937		    goto error;
938		}
939		    break;
940		case EXP_SWITCH_OUTPUT: {
941		    struct output *tmp;
942
943		    /* imply a "-input" */
944		    if (dash_input_count == 0) dash_input_count = 1;
945
946		    outp = new(struct output);
947
948				/* link new output in front of others */
949		    tmp = inp->output;
950		    inp->output = outp;
951		    outp->next = tmp;
952
953		    objc--;objv++;
954		    if (objc < 1) {
955			Tcl_WrongNumArgs(interp,1,objv_copy,"-output spawn_id");
956		    goto error;
957		    }
958		    outp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv),
959			    EXP_TEMPORARY,inter_updateproc);
960		if (!outp->i_list) {
961		    goto error;
962		}
963		    outp->action_eof = &action_eof;
964		    action_eof_ptr = &outp->action_eof;
965		    break;
966		}
967		case EXP_SWITCH_USER:
968		    objc--;objv++;
969		    if (objc < 1) {
970			Tcl_WrongNumArgs(interp,1,objv_copy,"-u spawn_id");
971		    goto error;
972		    }
973		    replace_user_by_process = *objv;
974
975		    /* imply a "-input" */
976		    if (dash_input_count == 0) dash_input_count = 1;
977		    break;
978		case EXP_SWITCH_OPPOSITE:
979		    /* apply following patterns to opposite side */
980		    /* of interaction */
981
982		    end_km = &input_default->keymap;
983
984		    if (dash_o_count > 0) {
985			exp_error(interp,"cannot use -o more than once");
986			goto error;
987		    }
988		    dash_o_count++;
989
990		    /* imply two "-input" */
991		    if (dash_input_count < 2) {
992		      dash_input_count = 2;
993		      inp = input_default;
994		      action_eof_ptr = &inp->action_eof;
995		    }
996		    break;
997		case EXP_SWITCH_SPAWN_ID:
998		    /* substitute master */
999
1000		    objc--;objv++;
1001		    chanName = *objv;
1002		    /* will be used later on */
1003
1004		    end_km = &input_default->keymap;
1005
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;
1011		    }
1012		    break;
1013		case EXP_SWITCH_ECHO:
1014		    next_echo = TRUE;
1015		    break;
1016		case EXP_SWITCH_NOBUFFER:
1017		    next_writethru = TRUE;
1018		    break;
1019		case EXP_SWITCH_INDICES:
1020		    next_indices = TRUE;
1021		    break;
1022		case EXP_SWITCH_RESET:
1023		    next_tty_reset = TRUE;
1024		    break;
1025		case EXP_SWITCH_IREAD:
1026		    next_iread = TRUE;
1027		    break;
1028		case EXP_SWITCH_IWRITE:
1029			next_iwrite= TRUE;
1030		    break;
1031		case EXP_SWITCH_EOF: {
1032		    struct action *action;
1033
1034		    objc--;objv++;
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;
1043		    next_iread = FALSE;
1044		    break;
1045		}
1046		case EXP_SWITCH_TIMEOUT: {
1047		    int t;
1048		    struct action *action;
1049		    expDiagLogU("-timeout is deprecated, use timeout\r\n");
1050
1051		    objc--;objv++;
1052		    if (objc < 1) {
1053			Tcl_WrongNumArgs(interp,1,objv_copy,"-timeout time");
1054			goto error;
1055		    }
1056
1057		    if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) {
1058		    goto error;
1059		    }
1060		    objc--;objv++;
1061		    if (t != -1)
1062			arbitrary_timeout = t;
1063		    /* we need an arbitrary timeout to start */
1064		    /* search for lowest one later */
1065
1066		    timeout_simple = FALSE;
1067		    action = inp->action_timeout = new_action(&action_base);
1068		    inp->timeout_nominal = t;
1069
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;
1076		    next_iread = FALSE;
1077		    break;
1078		}
1079		case EXP_SWITCH_FAST:
1080		case EXP_SWITCH_CAPFAST:
1081		    /* noop compatibility switches for fast mode */
1082		    break;
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. */
1089		    break;
1090	    }
1091	    continue;
1092    	} else {
1093	    static char *options[] = {
1094		"eof", "timeout", "null", (char *)0
1095	    };
1096	    enum options {
1097		EXP_OPTION_EOF, EXP_OPTION_TIMEOUT, EXP_OPTION_NULL
1098	    };
1099	    int index;
1100
1101	    /*
1102	     * Match keywords exactly, otherwise they are patterns.
1103	     */
1104
1105	    if (Tcl_GetIndexFromObj(interp, *objv, options, "option",
1106		    1 /* exact */, &index) != TCL_OK) {
1107		Tcl_ResetResult(interp);
1108		goto pattern;
1109	    }
1110	    switch ((enum options) index) {
1111		case EXP_OPTION_EOF: {
1112		    struct action *action;
1113
1114		    objc--;objv++;
1115		    *action_eof_ptr = action = new_action(&action_base);
1116
1117		    action->statement = *objv;
1118
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;
1124		    next_iread = FALSE;
1125		    break;
1126		}
1127		case EXP_OPTION_TIMEOUT: {
1128		    int t;
1129		    struct action *action;
1130
1131		    objc--;objv++;
1132		    if (objc < 1) {
1133			Tcl_WrongNumArgs(interp,1,objv_copy,"timeout time [action]");
1134		    goto error;
1135		    }
1136		    if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) {
1137		    goto error;
1138		    }
1139		    objc--;objv++;
1140
1141		    /* we need an arbitrary timeout to start */
1142		    /* search for lowest one later */
1143		    if (t != -1) arbitrary_timeout = t;
1144
1145		    timeout_simple = FALSE;
1146		    action = inp->action_timeout = new_action(&action_base);
1147		    inp->timeout_nominal = t;
1148
1149		    if (objc >= 1) {
1150		      action->statement = *objv;
1151		    } else {
1152		      action->statement = 0;
1153		    }
1154
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;
1160		    next_iread = FALSE;
1161		    break;
1162		}
1163		case EXP_OPTION_NULL:
1164		    next_null = TRUE;
1165		    goto pattern;
1166	    }
1167	    continue;
1168	}
1169
1170	/*
1171	 * pick up the pattern
1172	 */
1173
1174	pattern:
1175	km = new(struct keymap);
1176
1177	/* so that we can match in order user specified */
1178	/* link to end of keymap list */
1179	*end_km = km;
1180	km->next = 0;
1181	end_km = &km->next;
1182
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;
1189
1190	next_indices = next_echo = next_writethru = FALSE;
1191	next_tty_reset = FALSE;
1192	next_iwrite = next_iread = FALSE;
1193
1194	km->keys = *objv;
1195
1196	km->null = FALSE;
1197	km->re = 0;
1198	if (next_re) {
1199	    km->re = TRUE;
1200	    next_re = FALSE;
1201	}
1202	if (next_null) {
1203	    km->null = TRUE;
1204	    next_null = FALSE;
1205	}
1206
1207	objc--;objv++;
1208	if (objc >= 1) {
1209	    km->action.statement = *objv;
1210	} else {
1211	    km->action.statement = 0;
1212	}
1213
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");
1219
1220	/* imply a "-input" */
1221	if (dash_input_count == 0) dash_input_count = 1;
1222    }
1223
1224    /* if the user has not supplied either "-output" for the */
1225    /* default two "-input"s, fix them up here */
1226
1227    if (!input_user->output) {
1228	struct output *o = new(struct output);
1229	if (!chanName) {
1230	    if (!(esPtr = expStateCurrent(interp,1,1,0))) {
1231		goto error;
1232	    }
1233	    o->i_list = exp_new_i_simple(esPtr,EXP_TEMPORARY);
1234	} else {
1235	    o->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName),
1236		    EXP_TEMPORARY,inter_updateproc);
1237	    if (!o->i_list) {
1238		goto error;
1239	    }
1240	}
1241	o->next = 0;	/* no one else */
1242	o->action_eof = &action_eof;
1243	input_user->output = o;
1244    }
1245
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;
1252    }
1253
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);
1260
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)
1266	    goto error;
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)
1271	    goto error;
1272    }
1273
1274    /*
1275     * now fix up for default spawn id
1276     */
1277
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);
1281    }
1282
1283    if (input_default->i_list->state_list
1284	    && (input_default->i_list->state_list->esPtr == EXP_SPAWN_ID_BAD)) {
1285	if (!chanName) {
1286	    if (!(esPtr = expStateCurrent(interp,1,1,0))) {
1287		goto error;
1288	    }
1289	    input_default->i_list->state_list->esPtr = esPtr;
1290	} else {
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)
1296		goto error;
1297	}
1298    }
1299
1300    /*
1301     * check for user attempting to interact with self
1302     * they're almost certainly just fooling around
1303     */
1304
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);
1308    }
1309
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");
1313	goto error;
1314    }
1315
1316    esPtrs = 0;
1317
1318    /*
1319     * all data structures are sufficiently set up that we can now
1320     * "finish()" to terminate this procedure
1321     */
1322
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);
1325
1326    if (real_tty) {
1327	tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1328    }
1329
1330    for (inp = input_base,i=0;inp;inp=inp->next,i++) {
1331	/* start timers */
1332	inp->timeout_remaining = inp->timeout_nominal;
1333    }
1334
1335    key = expect_key++;
1336
1337    /* declare ourselves "in sync" with external view of close/indirect */
1338    configure_count = exp_configure_count;
1339
1340#ifndef SIMPLE_EVENT
1341    /* loop waiting (in event handler) for input */
1342    for (;;) {
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 */
1357	Tcl_Time temp_time;
1358
1359	/* calculate how long to wait */
1360	/* by finding shortest remaining timeout */
1361	if (timeout_simple) {
1362	    timeout = default_timeout;
1363	} else {
1364	    timeout = arbitrary_timeout;
1365
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;
1371		}
1372	    }
1373
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 */
1379
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) */
1383	}
1384
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);
1391	}
1392
1393	rc = exp_get_next_event(interp,esPtrs,input_count,&u,timeout,key);
1394	if (rc == EXP_TCLERROR)
1395	    goto error;
1396	if (rc == EXP_RECONFIGURE) continue;
1397	if (rc == EXP_TIMEOUT) {
1398	    if (timeout_simple) {
1399		action = &action_timeout;
1400		goto got_action;
1401	    } else {
1402		action = soonest_input->action_timeout;
1403		/* arbitrarily pick first fd out of list */
1404		u = soonest_input->i_list->state_list->esPtr;
1405	    }
1406	}
1407	if (!timeout_simple) {
1408	    int time_diff;
1409
1410	    Tcl_GetTime (&temp_time);
1411	    current_time = temp_time.sec;
1412	    time_diff = current_time - previous_time;
1413
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;
1420		}
1421	    }
1422	}
1423
1424	/* at this point, we have some kind of event which can be */
1425	/* immediately processed - i.e. something that doesn't block */
1426
1427	/* figure out who we are */
1428	inp = expStateToInput(esPtrToInput,u);
1429
1430	/* reset timer */
1431	inp->timeout_remaining = inp->timeout_nominal;
1432
1433	switch (rc) {
1434	    case EXP_DATA_NEW:
1435		cc = intRead(interp,u,1,0,key);
1436		if (cc > 0) break;
1437
1438		rc = EXP_EOF;
1439		/*
1440		 * FALLTHRU
1441		 *
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.
1447		 */
1448	    case EXP_EOF:
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;
1456		break;
1457	    case EXP_DATA_OLD:
1458		cc = 0;
1459		break;
1460	    case EXP_TIMEOUT:
1461		action = inp->action_timeout;
1462		attempt_match = FALSE;
1463		skip = expSizeGet(u);
1464		break;
1465	}
1466
1467	km = 0;
1468
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);
1473	    }
1474	} else {
1475	    attempt_match = TRUE;
1476	}
1477
1478	/*
1479	 * dispose of chars that should be skipped
1480	 * i.e., chars that cannot possibly be part of a match.
1481	 */
1482	if (km && km->writethru) {
1483	    print = skip + matchLen;
1484	} else print = skip;
1485
1486	if (km && km->echo) {
1487	    intEcho(u,skip,matchLen);
1488	}
1489	oldprinted = u->printed;
1490
1491	/*
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
1498	 * buffers directly.
1499	 */
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) */
1505		    /*
1506		     * Following should eventually be rewritten to ...WriteCharsAnd...
1507		     */
1508		    int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
1509						       u->input.buffer + u->printed,
1510			    print - u->printed);
1511		    if (wc < 0) {
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);
1515
1516			if (change && tty_changed)
1517			    exp_tty_set(interp,&tty_old,was_raw,was_echo);
1518			te = inter_eval(interp,action,u);
1519
1520			if (change && real_tty) tty_changed =
1521						    exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1522			switch (te) {
1523			    case TCL_BREAK:
1524			    case TCL_CONTINUE:
1525				finish(te);
1526			    case EXP_TCL_RETURN:
1527				finish(TCL_RETURN);
1528			    case TCL_RETURN:
1529				finish(TCL_OK);
1530			    case TCL_OK:
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 */
1535				continue;
1536			    default:
1537				finish(te);
1538			}
1539		    }
1540		}
1541	    }
1542	    u->printed = print;
1543	}
1544
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 */
1548	/* updated */
1549
1550	/* first update size based on skip information */
1551	/* then set skip to the total amount skipped */
1552
1553	size = expSizeGet(u);
1554	if (rc == EXP_MATCH) {
1555	    action = &km->action;
1556
1557	    skip += matchLen;
1558	    size -= skip;
1559	    if (size) {
1560		ustring = u->input.buffer;
1561		memmove(ustring, ustring + skip, size * sizeof(Tcl_UniChar));
1562	    }
1563	} else {
1564	    ustring = u->input.buffer;
1565	    if (skip) {
1566		size -= skip;
1567		memcpy(ustring, ustring + skip, size * sizeof(Tcl_UniChar));
1568	    }
1569	}
1570	u->input.use = size;
1571
1572	/* now update printed based on total amount skipped */
1573
1574	u->printed -= skip;
1575	/* if more skipped than printed (i.e., keymap encountered) */
1576	/* for printed positive */
1577	if (u->printed < 0) u->printed = 0;
1578
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);
1582
1583	/* finally reset echoed if necessary */
1584	if (rc != EXP_CANMATCH) {
1585	    if (skip >= oldprinted + u->echoed) u->echoed = 0;
1586	}
1587
1588	if (rc == EXP_EOF) {
1589	  if (u->close_on_eof) {
1590	    exp_close(interp,u);
1591	  }
1592	    need_to_close_master = FALSE;
1593	}
1594
1595	if (action) {
1596got_action:
1597	    change = (action && action->tty_reset);
1598	    if (change && tty_changed)
1599		exp_tty_set(interp,&tty_old,was_raw,was_echo);
1600
1601	    te = inter_eval(interp,action,u);
1602
1603	    if (change && real_tty) tty_changed =
1604					exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1605	    switch (te) {
1606		case TCL_BREAK:
1607		case TCL_CONTINUE:
1608		    finish(te);
1609		case EXP_TCL_RETURN:
1610		    finish(TCL_RETURN);
1611		case TCL_RETURN:
1612		    finish(TCL_OK);
1613		case TCL_OK:
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 */
1618		    continue;
1619		default:
1620		    finish(te);
1621	    }
1622	}
1623    }
1624
1625#else /* SIMPLE_EVENT */
1626/*	deferred_interrupt = FALSE;*/
1627{
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;
1636		int matchLen, skip;
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 */
1642
1643		int timeout;	/* current as opposed to default_timeout */
1644
1645	if (-1 == (pid = fork())) {
1646		exp_error(interp,"fork: %s",Tcl_PosixError(interp));
1647		finish(TCL_ERROR);
1648	}
1649	if (pid == 0) {
1650	    /*
1651	     * This is a new child process.
1652	     * It exists only for this interact command and will go away when
1653	     * the interact returns.
1654	     *
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.)
1658	     */
1659
1660	    exp_close(interp,expStdinoutGet());
1661
1662	    u = esPtrs[1];  /* get 2nd ExpState */
1663	    input_count = 1;
1664
1665	    while (1) {
1666
1667		/* calculate how long to wait */
1668		/* by finding shortest remaining timeout */
1669		if (timeout_simple) {
1670			timeout = default_timeout;
1671		} else {
1672			timeout = arbitrary_timeout;
1673
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;
1679			}
1680
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 */
1686
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) */
1690		}
1691
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) {
1695			int time_diff;
1696
1697			Tcl_GetTime (&temp_time);
1698			current_time = temp_time.sec;
1699			time_diff = current_time - previous_time;
1700
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;
1707				}
1708			}
1709		}
1710
1711		/* at this point, we have some kind of event which can be */
1712		/* immediately processed - i.e. something that doesn't block */
1713
1714		/* figure out who we are */
1715		inp = expStateToInput(esPtrToInput,u);
1716
1717		switch (rc) {
1718		case EXP_DATA_NEW:
1719		    cc = intRead(interp,u,0,0,key);
1720		    if (cc > 0) break;
1721		    /*
1722		     * FALLTHRU
1723		     *
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.
1729		     */
1730		case EXP_EOF:
1731			action = inp->action_eof;
1732			attempt_match = FALSE;
1733			skip = expSizeGet(u);
1734			rc = EXP_EOF;
1735			expDiagLog("interact: child received eof from spawn_id %s\r\n",u->name);
1736			exp_close(interp,u);
1737			break;
1738		case EXP_DATA_OLD:
1739			cc = 0;
1740			break;
1741		}
1742
1743		km = 0;
1744
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);
1749		    }
1750		} else {
1751		    attempt_match = TRUE;
1752		}
1753
1754		/* dispose of chars that should be skipped */
1755
1756		/* skip is chars not involved in match */
1757		/* print is with chars involved in match */
1758
1759		if (km && km->writethru) {
1760			print = skip + matchLen;
1761		} else print = skip;
1762
1763		if (km && km->echo) {
1764		    intEcho(u,skip,matchLen);
1765		}
1766		oldprinted = u->printed;
1767
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);
1783			    if (wc < 0) {
1784				expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp));
1785				action = outp->action_eof;
1786
1787				te = inter_eval(interp,action,u);
1788
1789				switch (te) {
1790				    case TCL_BREAK:
1791				    case TCL_CONTINUE:
1792					finish(te);
1793				    case EXP_TCL_RETURN:
1794					finish(TCL_RETURN);
1795				    case TCL_RETURN:
1796					finish(TCL_OK);
1797				    case TCL_OK:
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 */
1802					continue;
1803				    default:
1804					finish(te);
1805				}
1806			    }
1807			}
1808		    }
1809		    u->printed = print;
1810		}
1811
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 */
1815		/* updated */
1816
1817		/* first update size based on skip information */
1818		/* then set skip to the total amount skipped */
1819
1820		size = expSizeGet(u);
1821		if (rc == EXP_MATCH) {
1822		    action = &km->action;
1823
1824		    skip += matchLen;
1825		    size -= skip;
1826		    if (size) {
1827			memcpy(u->buffer, u->buffer + skip, size);
1828		    }
1829		} else {
1830		    if (skip) {
1831			size -= skip;
1832			memcpy(u->buffer, u->buffer + skip, size);
1833		    }
1834		}
1835		Tcl_SetObjLength(size);
1836
1837		/* now update printed based on total amount skipped */
1838
1839		u->printed -= skip;
1840		/* if more skipped than printed (i.e., keymap encountered) */
1841		/* for printed positive */
1842		if (u->printed < 0) u->printed = 0;
1843
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);
1847
1848		/* finally reset echoed if necessary */
1849		if (rc != EXP_CANMATCH) {
1850			if (skip >= oldprinted + u->echoed) u->echoed = 0;
1851		}
1852
1853		if (action) {
1854			te = inter_eval(interp,action,u);
1855			switch (te) {
1856			case TCL_BREAK:
1857			case TCL_CONTINUE:
1858				finish(te);
1859			case EXP_TCL_RETURN:
1860				finish(TCL_RETURN);
1861			case TCL_RETURN:
1862				finish(TCL_OK);
1863			case TCL_OK:
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 */
1868				continue;
1869			default:
1870				finish(te);
1871			}
1872		}
1873	    }
1874	} else {
1875	    /*
1876	     * This is the original Expect process.
1877	     *
1878	     * It now loops, reading keystrokes from the user tty
1879	     * and sending them to the spawned process.
1880	     * (See diagram above.)
1881	     */
1882
1883#include <signal.h>
1884
1885#if defined(SIGCLD) && !defined(SIGCHLD)
1886#define SIGCHLD SIGCLD
1887#endif
1888		expDiagLog("fork = %d\r\n",pid);
1889		signal(SIGCHLD,sigchld_handler);
1890/*	restart:*/
1891/*		tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);*/
1892
1893	    u = esPtrs[0];  /* get 1st ExpState */
1894	    input_count = 1;
1895
1896	    while (1) {
1897		/* calculate how long to wait */
1898		/* by finding shortest remaining timeout */
1899		if (timeout_simple) {
1900			timeout = default_timeout;
1901		} else {
1902			timeout = arbitrary_timeout;
1903
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;
1909			}
1910
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 */
1916
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) */
1920		}
1921
1922		rc = exp_get_next_event(interp,esPtrs,input_count,&u,timeout,key);
1923		if (!timeout_simple) {
1924			int time_diff;
1925
1926			Tcl_GetTime (&temp_time);
1927			current_time = temp_time.sec;
1928			time_diff = current_time - previous_time;
1929
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;
1936				}
1937			}
1938		}
1939
1940		/* at this point, we have some kind of event which can be */
1941		/* immediately processed - i.e. something that doesn't block */
1942
1943		/* figure out who we are */
1944		inp = expStateToInput(esPtrToInput,u);
1945
1946		switch (rc) {
1947		case EXP_DATA_NEW:
1948		        cc = intRead(interp,u,0,1,key);
1949		        if (cc > 0) {
1950				break;
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);
1958				rc = EXP_EOF;
1959				expDiagLogU("interact: process died/eof\r\n");
1960				clean_up_after_child(interp,esPtrs[1]);
1961				break;
1962			}
1963			/*
1964			 * FALLTHRU
1965			 *
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.
1971			 */
1972		case EXP_EOF:
1973			action = inp->action_eof;
1974			attempt_match = FALSE;
1975			skip = expSizeGet(u);
1976			rc = EXP_EOF;
1977			expDiagLogU("user sent EOF or disappeared\n\n");
1978			break;
1979		case EXP_DATA_OLD:
1980			cc = 0;
1981			break;
1982		}
1983
1984		km = 0;
1985
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);
1990		    }
1991		} else {
1992		    attempt_match = TRUE;
1993		}
1994
1995		/* dispose of chars that should be skipped */
1996
1997		/* skip is chars not involved in match */
1998		/* print is with chars involved in match */
1999
2000		if (km && km->writethru) {
2001			print = skip + matchLen;
2002		} else print = skip;
2003
2004		if (km && km->echo) {
2005		    intEcho(u,skip,matchLen);
2006		}
2007		oldprinted = u->printed;
2008
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);
2024			    if (wc < 0) {
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);
2032
2033				if (change && real_tty) tty_changed =
2034							    exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
2035				switch (te) {
2036				    case TCL_BREAK:
2037				    case TCL_CONTINUE:
2038					finish(te);
2039				    case EXP_TCL_RETURN:
2040					finish(TCL_RETURN);
2041				    case TCL_RETURN:
2042					finish(TCL_OK);
2043				    case TCL_OK:
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 */
2048					continue;
2049				    default:
2050					finish(te);
2051				}
2052			    }
2053			}
2054		    }
2055		    u->printed = print;
2056		}
2057
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 */
2061		/* updated */
2062
2063		/* first update size based on skip information */
2064		/* then set skip to the total amount skipped */
2065
2066		size = expSizeGet(u);
2067		if (rc == EXP_MATCH) {
2068		    action = &km->action;
2069
2070		    skip += matchLen;
2071		    size -= skip;
2072		    if (size) {
2073			memcpy(u->buffer, u->buffer + skip, size);
2074		    }
2075		} else {
2076		    if (skip) {
2077			size -= skip;
2078			memcpy(u->buffer, u->buffer + skip, size);
2079		    }
2080		}
2081		Tcl_SetObjLength(size);
2082
2083		/* now update printed based on total amount skipped */
2084
2085		u->printed -= skip;
2086		/* if more skipped than printed (i.e., keymap encountered) */
2087		/* for printed positive */
2088		if (u->printed < 0) u->printed = 0;
2089
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);
2093
2094		/* finally reset echoed if necessary */
2095		if (rc != EXP_CANMATCH) {
2096			if (skip >= oldprinted + u->echoed) u->echoed = 0;
2097		}
2098
2099		if (action) {
2100			change = (action && action->tty_reset);
2101			if (change && tty_changed)
2102				exp_tty_set(interp,&tty_old,was_raw,was_echo);
2103
2104			te = inter_eval(interp,action,u);
2105
2106			if (change && real_tty) tty_changed =
2107			   exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
2108			switch (te) {
2109			case TCL_BREAK:
2110			case TCL_CONTINUE:
2111				finish(te);
2112			case EXP_TCL_RETURN:
2113				finish(TCL_RETURN);
2114			case TCL_RETURN:
2115				finish(TCL_OK);
2116			case TCL_OK:
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 */
2121				continue;
2122			default:
2123				finish(te);
2124			}
2125		}
2126	    }
2127	}
2128}
2129#endif /* SIMPLE_EVENT */
2130
2131 done:
2132#ifdef SIMPLE_EVENT
2133    /* force child to exit upon eof from master */
2134    if (pid == 0) {
2135	exit(SPAWNED_PROCESS_DIED);
2136    }
2137#endif /* SIMPLE_EVENT */
2138
2139    if (need_to_close_master && u->close_on_eof) exp_close(interp,u);
2140
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);
2146
2147    if (new_cmd) { Tcl_DecrRefCount (new_cmd); }
2148    return(status);
2149
2150 error:
2151    if (new_cmd) { Tcl_DecrRefCount (new_cmd); }
2152    return TCL_ERROR;
2153}
2154
2155/* version of Tcl_Eval for interact */
2156static int
2157inter_eval(
2158    Tcl_Interp *interp,
2159    struct action *action,
2160    ExpState *esPtr)
2161{
2162    int status;
2163
2164    if (action->iwrite) {
2165	out("spawn_id",esPtr->name);
2166    }
2167
2168    if (action->statement) {
2169	status = Tcl_EvalObjEx(interp,action->statement,0);
2170    } else {
2171	expStdoutLogU("\r\n",1);
2172	status = exp_interpreter(interp,(Tcl_Obj *)0);
2173    }
2174
2175    return status;
2176}
2177
2178static void
2179free_keymap(struct keymap *km)
2180{
2181	if (km == 0) return;
2182	free_keymap(km->next);
2183
2184	ckfree((char *)km);
2185}
2186
2187static void
2188free_action(struct action *a)
2189{
2190	struct action *next;
2191
2192	while (a) {
2193		next = a->next;
2194		ckfree((char *)a);
2195		a = next;
2196	}
2197}
2198
2199static void
2200free_input(
2201    Tcl_Interp *interp,
2202    struct input *i)
2203{
2204	if (i == 0) return;
2205	free_input(interp,i->next);
2206
2207	exp_free_i(interp,i->i_list,inter_updateproc);
2208	free_output(interp,i->output);
2209	free_keymap(i->keymap);
2210	ckfree((char *)i);
2211}
2212
2213static struct action *
2214new_action(struct action **base)
2215{
2216	struct action *o = new(struct action);
2217
2218	/* stick new action into beginning of list of all actions */
2219	o->next = *base;
2220	*base = o;
2221
2222	return o;
2223}
2224
2225static void
2226free_output(
2227    Tcl_Interp *interp,
2228    struct output *o)
2229{
2230	if (o == 0) return;
2231	free_output(interp,o->next);
2232	exp_free_i(interp,o->i_list,inter_updateproc);
2233
2234	ckfree((char *)o);
2235}
2236
2237
2238static struct exp_cmd_data cmd_data[]  = {
2239{"interact",	Exp_InteractObjCmd,	0,	0,	0},
2240{0}};
2241
2242void
2243exp_init_interact_cmds(Tcl_Interp *interp)
2244{
2245    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2246
2247    exp_create_commands(interp,cmd_data);
2248
2249    tsdPtr->cmdObjReturn = Tcl_NewStringObj("return",6);
2250    Tcl_IncrRefCount(tsdPtr->cmdObjReturn);
2251#if 0
2252    tsdPtr->cmdObjInterpreter = Tcl_NewStringObj("interpreter",11);
2253    Tcl_IncrRefCount(tsdPtr->cmdObjInterpreter);
2254#endif
2255}
2256
2257/*
2258 * Local Variables:
2259 * mode: c
2260 * c-basic-offset: 4
2261 * fill-column: 78
2262 * End:
2263 */
2264