Fix LTO build
[platform/upstream/expect.git] / exp_inter.c
1 /* interact (using select) - give user keyboard control
2
3 Written by: Don Libes, NIST, 2/6/90
4
5 Design and implementation of this program was paid for by U.S. tax
6 dollars.  Therefore it is public domain.  However, the author and NIST
7 would appreciate credit if this program or parts of it are used.
8
9 */
10
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
53 typedef struct ThreadSpecificData {
54     Tcl_Obj *cmdObjReturn;
55     Tcl_Obj *cmdObjInterpreter;
56 } ThreadSpecificData;
57
58 static 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
89 struct 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
97 struct 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
109 struct output {
110         struct exp_i *i_list;
111         struct action *action_eof;
112         struct output *next;
113 };
114
115 struct 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
132 struct input *
133 expStateToInput(
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
146 void
147 expCreateStateToInput(
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
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(
165     Tcl_Interp *interp,
166     struct action *action,
167     ExpState *esPtr);
168
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
174 or not.
175
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.
181
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.
185
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.
189
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.
194
195 */
196
197 static int
198 intMatch(
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 */
354 static void
355 intRegExpMatchProcess(
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  */ 
400 static void
401 intEcho(
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  */
431 static int
432 intRead(
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
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:
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
525 static sigjmp_buf env;                /* for interruptable read() */
526 #else
527 static jmp_buf env;             /* for interruptable read() */
528 #endif  /* HAVE_SIGLONGJMP */
529
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 */
535
536 static void
537 sigchld_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  */
559 static int
560 intIRead(
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
588 static void
589 clean_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
603 static int
604 update_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*/
695 static char *
696 inter_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
709 static char return_cmd[] = "return";
710 static char interpreter_cmd[] = "interpreter";
711
712 /*ARGSUSED*/
713 int
714 Exp_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) {
1596 got_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 */ 
2156 static int
2157 inter_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
2178 static void
2179 free_keymap(struct keymap *km)
2180 {
2181         if (km == 0) return;
2182         free_keymap(km->next);
2183
2184         ckfree((char *)km);
2185 }
2186
2187 static void
2188 free_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
2199 static void
2200 free_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
2213 static struct action *
2214 new_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
2225 static void
2226 free_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
2238 static struct exp_cmd_data cmd_data[]  = {
2239 {"interact",    Exp_InteractObjCmd,     0,      0,      0},
2240 {0}};
2241
2242 void
2243 exp_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 \f
2257 /*
2258  * Local Variables:
2259  * mode: c
2260  * c-basic-offset: 4
2261  * fill-column: 78
2262  * End:
2263  */