OSDN Git Service

* thread-db.c (get_thread_signals): Doc fix.
[pf3gnuchains/pf3gnuchains3x.git] / expect / 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 "tcl.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_tstamp.h" /* remove when timestamp stuff is gone */
47
48 #include "tcl_regexp.h"
49 #include "exp_regexp.h"
50
51 extern char *TclGetRegError();
52 extern void Expect_TclRegError();
53
54 #define INTER_OUT "interact_out"
55
56 /*
57  * tests if we are running this using a real tty
58  *
59  * these tests are currently only used to control what gets written to the
60  * logfile.  Note that removal of the test of "..._is_tty" means that stdin
61  * or stdout could be redirected and yet stdout would still be logged.
62  * However, it's not clear why anyone would use log_file when these are
63  * redirected in the first place.  On the other hand, it is reasonable to
64  * run expect as a daemon in which case, stdin/out do not appear to be
65  * ttys, yet it makes sense for them to be logged with log_file as if they
66  * were.
67  */
68 #if 0
69 #define real_tty_output(x) (exp_stdout_is_tty && (((x)==1) || ((x)==exp_dev_tty)))
70 #define real_tty_input(x) (exp_stdin_is_tty && (((x)==0) || ((x)==exp_dev_tty)))
71 #endif
72
73 #define real_tty_output(x) (((x)==1) || ((x)==exp_dev_tty))
74 #define real_tty_input(x) (exp_stdin_is_tty && (((x)==0) || ((x)==exp_dev_tty)))
75
76 #define new(x)  (x *)ckalloc(sizeof(x))
77
78 struct action {
79         char *statement;
80         int tty_reset;          /* if true, reset tty mode upon action */
81         int iread;              /* if true, reread indirects */
82         int iwrite;             /* if true, write spawn_id element */
83         int timestamp;          /* if true, generate timestamp */
84         struct action *next;    /* chain only for later for freeing */
85 };
86
87 struct keymap {
88         char *keys;     /* original pattern provided by user */
89         Expect_regexp *re;
90         int null;       /* true if looking to match 0 byte */
91         int case_sensitive;
92         int echo;       /* if keystrokes should be echoed */
93         int writethru;  /* if keystrokes should go through to process */
94         int indices;    /* true if should write indices */
95         struct action action;
96         struct keymap *next;
97 };
98
99 struct output {
100         struct exp_i *i_list;
101         struct action *action_eof;
102         struct output *next;
103 };
104
105 struct input {
106         struct exp_i *i_list;
107         struct output *output;
108         struct action *action_eof;
109         struct action *action_timeout;
110         struct keymap *keymap;
111         int timeout_nominal;            /* timeout nominal */
112         int timeout_remaining;          /* timeout remaining */
113         struct input *next;
114 };
115
116 static void free_input();
117 static void free_keymap();
118 static void free_output();
119 static void free_action();
120 static struct action *new_action();
121 static int inter_eval();
122
123 /* in_keymap() accepts user keystrokes and returns one of MATCH,
124 CANMATCH, or CANTMATCH.  These describe whether the keystrokes match a
125 key sequence, and could or can't if more characters arrive.  The
126 function assigns a matching keymap if there is a match or can-match.
127 A matching keymap is assigned on can-match so we know whether to echo
128 or not.
129
130 in_keymap is optimized (if you can call it that) towards a small
131 number of key mappings, but still works well for large maps, since no
132 function calls are made, and we stop as soon as there is a single-char
133 mismatch, and go on to the next one.  A hash table or compiled DFA
134 probably would not buy very much here for most maps.
135
136 The basic idea of how this works is it does a smart sequential search.
137 At each position of the input string, we attempt to match each of the
138 keymaps.  If at least one matches, the first match is returned.
139
140 If there is a CANMATCH and there are more keymaps to try, we continue
141 trying.  If there are no more keymaps to try, we stop trying and
142 return with an indication of the first keymap that can match.
143
144 Note that I've hacked up the regexp pattern matcher in two ways.  One
145 is to force the pattern to always be anchored at the front.  That way,
146 it doesn't waste time attempting to match later in the string (before
147 we're ready).  The other is to return can-match.
148
149 */
150
151 static int
152 in_keymap(string,stringlen,keymap,km_match,match_length,skip,rm_nulls)
153 char *string;
154 int stringlen;
155 struct keymap *keymap;          /* linked list of keymaps */
156 struct keymap **km_match;       /* keymap that matches or can match */
157 int *match_length;              /* # of chars that matched */
158 int *skip;                      /* # of chars to skip */
159 int rm_nulls;                   /* skip nulls if true */
160 {
161         struct keymap *km;
162         char *ks;               /* string from a keymap */
163         char *start_search;     /* where in the string to start searching */
164         char *string_end;
165
166         /* assert (*km == 0) */
167
168         /* a shortcut that should help master output which typically */
169         /* is lengthy and has no key maps.  Otherwise it would mindlessly */
170         /* iterate on each character anyway. */
171         if (!keymap) {
172                 *skip = stringlen;
173                 return(EXP_CANTMATCH);
174         }
175
176         string_end = string + stringlen;
177
178         /* Mark beginning of line for ^ . */
179         regbol = string;
180
181 /* skip over nulls - Pascal Meheut, pascal@cnam.cnam.fr 18-May-1993 */
182 /*    for (start_search = string;*start_search;start_search++) {*/
183     for (start_search = string;start_search<string_end;start_search++) {
184         if (*km_match) break; /* if we've already found a CANMATCH */
185                         /* don't bother starting search from positions */
186                         /* further along the string */
187
188         for (km=keymap;km;km=km->next) {
189             char *s;    /* current character being examined */
190
191             if (km->null) {
192                 if (*start_search == 0) {
193                     *skip = start_search-string;
194                     *match_length = 1;  /* s - start_search == 1 */
195                     *km_match = km;
196                     return(EXP_MATCH);
197                 }
198             } else if (!km->re) {
199                 /* fixed string */
200                 for (s = start_search,ks = km->keys ;;s++,ks++) {
201                         /* if we hit the end of this map, must've matched! */
202                         if (*ks == 0) {
203                                 *skip = start_search-string;
204                                 *match_length = s-start_search;
205                                 *km_match = km;
206                                 return(EXP_MATCH);
207                         }
208
209                         /* if we ran out of user-supplied characters, and */
210                         /* still haven't matched, it might match if the user */
211                         /* supplies more characters next time */
212
213                         if (s == string_end) {
214                                 /* skip to next key entry, but remember */
215                                 /* possibility that this entry might match */
216                                 if (!*km_match) *km_match = km;
217                                 break;
218                         }
219
220                         /* if this is a problem for you, use exp_parity command */
221 /*                      if ((*s & 0x7f) == *ks) continue;*/
222                         if (*s == *ks) continue;
223                         if ((*s == '\0') && rm_nulls) {
224                                 ks--;
225                                 continue;
226                         }
227                         break;
228                 }
229             } else {
230                 /* regexp */
231                 int r;  /* regtry status */
232                 Expect_regexp *prog = km->re;
233
234                 /* if anchored, but we're not at beginning, skip pattern */
235                 if (prog->reganch) {
236                         if (string != start_search) continue;
237                 }
238
239                 /* known starting char - quick test 'fore lotta work */
240                 if (prog->regstart) {
241                         /* if this is a problem for you, use exp_parity command */
242 /*                      /* if ((*start_search & 0x7f) != prog->regstart) continue; */
243                         if (*start_search != prog->regstart) continue;
244                 }
245                 r = exp_regtry(prog,start_search,match_length);
246                 if (r == EXP_MATCH) {
247                         *km_match = km;
248                         *skip = start_search-string;
249                         return(EXP_MATCH);
250                 }
251                 if (r == EXP_CANMATCH) {
252                         if (!*km_match) *km_match = km;
253                 }
254             }
255         }
256     }
257
258         if (*km_match) {
259                 /* report a can-match */
260
261                 char *p;
262
263                 *skip = (start_search-string)-1;
264 #if 0
265                 *match_length = stringlen - *skip;
266 #else
267                 /*
268                  * there may be nulls in the string in which case
269                  * the pattern matchers can report CANMATCH when
270                  * the null is hit.  So find the null and compute
271                  * the length of the possible match.
272                  *
273                  * Later, after we squeeze out the nulls, we will
274                  * retry the match, but for now, go along with
275                  * calling it a CANMATCH
276                  */
277                 p = start_search;
278                 while (*p) {
279                         p++;
280                 }
281                 *match_length = (p - start_search) + 1;
282                 /*printf(" match_length = %d\n",*match_length);*/
283 #endif
284                 return(EXP_CANMATCH);
285         }
286
287         *skip = start_search-string;
288         return(EXP_CANTMATCH);
289 }
290
291 #ifdef SIMPLE_EVENT
292
293 /*
294
295 The way that the "simple" interact works is that the original Expect
296 process reads from the tty and writes to the spawned process.  A child
297 process is forked to read from the spawned process and write to the
298 tty.  It looks like this:
299
300                         user
301                     --> tty >--
302                    /           \
303                   ^             v
304                 child        original
305                process        Expect
306                   ^          process
307                   |             v
308                    \           /
309                     < spawned <
310                       process
311
312 */
313
314
315
316 #ifndef WEXITSTATUS
317 #define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
318 #endif
319
320 #include <setjmp.h>
321
322 static jmp_buf env;             /* for interruptable read() */
323 static int reading;             /* while we are reading */
324                                 /* really, while "env" is valid */
325 static int deferred_interrupt = FALSE;  /* if signal is received, but not */
326                                 /* in i_read record this here, so it will */
327                                 /* be handled next time through i_read */
328
329 void sigchld_handler()
330 {
331         if (reading) longjmp(env,1);
332
333         deferred_interrupt = TRUE;
334 }
335
336 #define EXP_CHILD_EOF -100
337
338 /* interruptable read */
339 static int
340 i_read(fd,buffer,length)
341 int fd;
342 char *buffer;
343 int length;
344 {
345         int cc = EXP_CHILD_EOF;
346
347         if (deferred_interrupt) return(cc);
348
349         if (0 == setjmp(env)) {
350                 reading = TRUE;
351                 cc = read(fd,buffer,length);
352         }
353         reading = FALSE;
354         return(cc);
355 }
356
357 /* exit status for the child process created by cmdInteract */
358 #define CHILD_DIED              -2
359 #define SPAWNED_PROCESS_DIED    -3
360
361 static void
362 clean_up_after_child(interp,master)
363 Tcl_Interp *interp;
364 int master;
365 {
366 /* should really be recoded using the common wait code in command.c */
367         int status;
368         int pid;
369         int i;
370
371         pid = wait(&status);    /* for slave */
372         for (i=0;i<=exp_fd_max;i++) {
373                 if (exp_fs[i].pid == pid) {
374                         exp_fs[i].sys_waited = TRUE;
375                         exp_fs[i].wait = status;
376                 }
377         }
378         pid = wait(&status);    /* for child */
379         for (i=0;i<=exp_fd_max;i++) {
380                 if (exp_fs[i].pid == pid) {
381                         exp_fs[i].sys_waited = TRUE;
382                         exp_fs[i].wait = status;
383                 }
384         }
385
386         deferred_interrupt = FALSE;
387         exp_close(interp,master);
388         master = -1;
389 }
390 #endif /*SIMPLE_EVENT*/
391
392 static int
393 update_interact_fds(interp,fd_count,fd_to_input,fd_list,input_base,
394                         do_indirect,config_count,real_tty_caller)
395 Tcl_Interp *interp;
396 int *fd_count;
397 struct input ***fd_to_input;    /* map from fd's to "struct input"s */
398 int **fd_list;
399 struct input *input_base;
400 int do_indirect;                /* if true do indirects */
401 int *config_count;
402 int *real_tty_caller;
403 {
404         struct input *inp;
405         struct output *outp;
406         struct exp_fd_list *fdp;
407         int count;
408
409         int real_tty = FALSE;
410
411         *config_count = exp_configure_count;
412
413         count = 0;
414         for (inp = input_base;inp;inp=inp->next) {
415
416                 if (do_indirect) {
417                         /* do not update "direct" entries (again) */
418                         /* they were updated upon creation */
419                         if (inp->i_list->direct == EXP_INDIRECT) {
420                                 exp_i_update(interp,inp->i_list);
421                         }
422                         for (outp = inp->output;outp;outp=outp->next) {
423                                 if (outp->i_list->direct == EXP_INDIRECT) {
424                                         exp_i_update(interp,outp->i_list);
425                                 }
426                         }
427                 }
428
429                 /* revalidate all input descriptors */
430                 for (fdp = inp->i_list->fd_list;fdp;fdp=fdp->next) {
431                         count++;
432                         /* have to "adjust" just in case spawn id hasn't had */
433                         /* a buffer sized yet */
434                         if (!exp_fd2f(interp,fdp->fd,1,1,"interact"))
435                                 return(TCL_ERROR);
436                 }
437
438                 /* revalidate all output descriptors */
439                 for (outp = inp->output;outp;outp=outp->next) {
440                         for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
441                                 /* make user_spawn_id point to stdout */
442                                 if (fdp->fd == 0) {
443                                         fdp->fd = 1;
444                                 } else if (fdp->fd == 1) {
445                                         /* do nothing */
446                                 } else if (!exp_fd2f(interp,fdp->fd,1,0,"interact"))
447                                         return(TCL_ERROR);
448                         }
449                 }
450         }
451         if (!do_indirect) return TCL_OK;
452
453         if (*fd_to_input == 0) {
454                 *fd_to_input = (struct input **)ckalloc(
455                                 (exp_fd_max+1) * sizeof(struct input *));
456                 *fd_list = (int *)ckalloc(count * sizeof(int));
457         } else {
458                 *fd_to_input = (struct input **)ckrealloc((char *)*fd_to_input,
459                                 (exp_fd_max+1) * sizeof(struct input *));
460                 *fd_list = (int *)ckrealloc((char *)*fd_list,count * sizeof(int));
461         }
462
463         count = 0;
464         for (inp = input_base;inp;inp=inp->next) {
465                 for (fdp = inp->i_list->fd_list;fdp;fdp=fdp->next) {
466                         /* build map to translate from spawn_id to struct input */
467                         (*fd_to_input)[fdp->fd] = inp;
468
469                         /* build input to ready() */
470                         (*fd_list)[count] = fdp->fd;
471
472                         if (real_tty_input(fdp->fd)) real_tty = TRUE;
473
474                         count++;
475                 }
476         }
477         *fd_count = count;
478
479         *real_tty_caller = real_tty; /* tell caller if we have found that */
480                                         /* we are using real tty */
481
482         return TCL_OK;
483 }
484
485 /*ARGSUSED*/
486 static char *
487 inter_updateproc(clientData, interp, name1, name2, flags)
488 ClientData clientData;
489 Tcl_Interp *interp;     /* Interpreter containing variable. */
490 char *name1;            /* Name of variable. */
491 char *name2;            /* Second part of variable name. */
492 int flags;              /* Information about what happened. */
493 {
494         exp_configure_count++;
495         return 0;
496 }
497                         
498 #define finish(x)       { status = x; goto done; }
499
500 static char return_cmd[] = "return";
501 static char interpreter_cmd[] = "interpreter";
502
503 /*ARGSUSED*/
504 int
505 Exp_InteractCmd(clientData, interp, argc, argv)
506 ClientData clientData;
507 Tcl_Interp *interp;
508 int argc;
509 char **argv;
510 {
511         char *arg;      /* shorthand for current argv */
512 #ifdef SIMPLE_EVENT
513         int pid;
514 #endif /*SIMPLE_EVENT*/
515
516         /*declarations*/
517         int input_count;        /* count of struct input descriptors */
518         struct input **fd_to_input;     /* map from fd's to "struct input"s */
519         int *fd_list;
520         struct keymap *km;      /* ptr for above while parsing */
521 /*      extern char *tclRegexpError;    /* declared in tclInt.h */
522         int master = EXP_SPAWN_ID_BAD;
523         char *master_string = 0;/* string representation of master */
524         int need_to_close_master = FALSE;       /* if an eof is received */
525                                 /* we use this to defer close until later */
526
527         int next_tty_reset = FALSE;     /* if we've seen a single -reset */
528         int next_iread = FALSE;/* if we've seen a single -iread */
529         int next_iwrite = FALSE;/* if we've seen a single -iread */
530         int next_re = FALSE;    /* if we've seen a single -re */
531         int next_null = FALSE;  /* if we've seen the null keyword */
532         int next_writethru = FALSE;/*if macros should also go to proc output */
533         int next_indices = FALSE;/* if we should write indices */
534         int next_echo = FALSE;  /* if macros should be echoed */
535         int next_timestamp = FALSE; /* if we should generate a timestamp */
536 /*      int next_case_sensitive = TRUE;*/
537         char **oldargv = 0;     /* save original argv here if we split it */
538         int status = TCL_OK;    /* final return value */
539         int i;                  /* trusty temp */
540
541         int timeout_simple = TRUE;      /* if no or global timeout */
542
543         int real_tty;           /* TRUE if we are interacting with real tty */
544         int tty_changed = FALSE;/* true if we had to change tty modes for */
545                                 /* interact to work (i.e., to raw, noecho) */
546         int was_raw;
547         int was_echo;
548         exp_tty tty_old;
549
550         char *replace_user_by_process = 0; /* for -u flag */
551
552         struct input *input_base;
553 #define input_user input_base
554         struct input *input_default;
555         struct input *inp;      /* overused ptr to struct input */
556         struct output *outp;    /* overused ptr to struct output */
557
558         int dash_input_count = 0; /* # of "-input"s seen */
559         int arbitrary_timeout;
560         int default_timeout;
561         struct action action_timeout;   /* common to all */
562         struct action action_eof;       /* common to all */
563         struct action **action_eof_ptr; /* allow -input/ouput to */
564                 /* leave their eof-action assignable by a later */
565                 /* -eof */
566         struct action *action_base = 0;
567         struct keymap **end_km;
568
569         int key;
570         int configure_count;    /* monitor reconfigure events */
571
572         if ((argc == 2) && exp_one_arg_braced(argv[1])) {
573                 return(exp_eval_with_one_arg(clientData,interp,argv));
574         } else if ((argc == 3) && streq(argv[1],"-brace")) {
575                 char *new_argv[2];
576                 new_argv[0] = argv[0];
577                 new_argv[1] = argv[2];
578                 return(exp_eval_with_one_arg(clientData,interp,new_argv));
579         }
580
581         argv++;
582         argc--;
583
584         default_timeout = EXP_TIME_INFINITY;
585         arbitrary_timeout = EXP_TIME_INFINITY;  /* if user specifies */
586                 /* a bunch of timeouts with EXP_TIME_INFINITY, this will be */
587                 /* left around for us to find. */
588
589         input_user = new(struct input);
590         input_user->i_list = exp_new_i_simple(0,EXP_TEMPORARY); /* stdin by default */
591         input_user->output = 0;
592         input_user->action_eof = &action_eof;
593         input_user->timeout_nominal = EXP_TIME_INFINITY;
594         input_user->action_timeout = 0;
595         input_user->keymap = 0;
596
597         end_km = &input_user->keymap;
598         inp = input_user;
599         action_eof_ptr = &input_user->action_eof;
600
601         input_default = new(struct input);
602         input_default->i_list = exp_new_i_simple(EXP_SPAWN_ID_BAD,EXP_TEMPORARY); /* fix up later */
603         input_default->output = 0;
604         input_default->action_eof = &action_eof;
605         input_default->timeout_nominal = EXP_TIME_INFINITY;
606         input_default->action_timeout = 0;
607         input_default->keymap = 0;
608         input_default->next = 0;                /* no one else */
609         input_user->next = input_default;
610
611         /* default and common -eof action */
612         action_eof.statement = return_cmd;
613         action_eof.tty_reset = FALSE;
614         action_eof.iread = FALSE;
615         action_eof.iwrite = FALSE;
616         action_eof.timestamp = FALSE;
617
618         for (;argc>0;argc--,argv++) {
619                 arg = *argv;
620                 if (exp_flageq("eof",arg,3)) {
621                         struct action *action;
622
623                         argc--;argv++;
624                         *action_eof_ptr = action = new_action(&action_base);
625
626                         action->statement = *argv;
627
628                         action->tty_reset = next_tty_reset;
629                         next_tty_reset = FALSE;
630                         action->iwrite = next_iwrite;
631                         next_iwrite = FALSE;
632                         action->iread = next_iread;
633                         next_iread = FALSE;
634                         action->timestamp = next_timestamp;
635                         next_timestamp = FALSE;
636                         continue;
637                 } else if (exp_flageq("timeout",arg,7)) {
638                         int t;
639                         struct action *action;
640
641                         argc--;argv++;
642                         if (argc < 1) {
643                                 exp_error(interp,"timeout needs time");
644                                 return(TCL_ERROR);
645                         }
646                         t = atoi(*argv);
647                         argc--;argv++;
648
649                         /* we need an arbitrary timeout to start */
650                         /* search for lowest one later */
651                         if (t != -1) arbitrary_timeout = t;
652
653                         timeout_simple = FALSE;
654                         action = inp->action_timeout = new_action(&action_base);
655                         inp->timeout_nominal = t;
656
657                         action->statement = *argv;
658
659                         action->tty_reset = next_tty_reset;
660                         next_tty_reset = FALSE;
661                         action->iwrite = next_iwrite;
662                         next_iwrite = FALSE;
663                         action->iread = next_iread;
664                         next_iread = FALSE;
665                         action->timestamp = next_timestamp;
666                         next_timestamp = FALSE;
667                         continue;
668                 } else if (exp_flageq("null",arg,4)) {
669                         next_null = TRUE;                       
670                 } else if (arg[0] == '-') {
671                         arg++;
672                         if (exp_flageq1('-',arg)                /* "--" */
673                          || (exp_flageq("exact",arg,3))) {
674                                 argc--;argv++;
675                         } else if (exp_flageq("regexp",arg,2)) {
676                                 if (argc < 1) {
677                                         exp_error(interp,"-re needs pattern");
678                                         return(TCL_ERROR);
679                                 }
680                                 next_re = TRUE;
681                                 argc--;
682                                 argv++;
683                         } else if (exp_flageq("input",arg,2)) {
684                                 dash_input_count++;
685                                 if (dash_input_count == 2) {
686                                         inp = input_default;
687                                         input_user->next = input_default;
688                                 } else if (dash_input_count > 2) {
689                                         struct input *previous_input = inp;
690                                         inp = new(struct input);
691                                         previous_input->next = inp;
692                                 }
693                                 inp->output = 0;
694                                 inp->action_eof = &action_eof;
695                                 action_eof_ptr = &inp->action_eof;
696                                 inp->timeout_nominal = default_timeout;
697                                 inp->action_timeout = &action_timeout;
698                                 inp->keymap = 0;
699                                 end_km = &inp->keymap;
700                                 inp->next = 0;
701                                 argc--;argv++;
702                                 if (argc < 1) {
703                                         exp_error(interp,"-input needs argument");
704                                         return(TCL_ERROR);
705                                 }
706 /*                              inp->spawn_id = atoi(*argv);*/
707                                 inp->i_list = exp_new_i_complex(interp,*argv,
708                                                 EXP_TEMPORARY,inter_updateproc);
709                                 continue;
710                         } else if (exp_flageq("output",arg,3)) {
711                                 struct output *tmp;
712
713                                 /* imply a "-input" */
714                                 if (dash_input_count == 0) dash_input_count = 1;
715
716                                 outp = new(struct output);
717
718                                 /* link new output in front of others */
719                                 tmp = inp->output;
720                                 inp->output = outp;
721                                 outp->next = tmp;
722
723                                 argc--;argv++;
724                                 if (argc < 1) {
725                                         exp_error(interp,"-output needs argument");
726                                         return(TCL_ERROR);
727                                 }
728                                 outp->i_list = exp_new_i_complex(interp,*argv,
729                                         EXP_TEMPORARY,inter_updateproc);
730
731                                 outp->action_eof = &action_eof;
732                                 action_eof_ptr = &outp->action_eof;
733                                 continue;
734                         } else if (exp_flageq1('u',arg)) {      /* treat process as user */
735                                 argc--;argv++;
736                                 if (argc < 1) {
737                                         exp_error(interp,"-u needs argument");
738                                         return(TCL_ERROR);
739                                 }
740                                 replace_user_by_process = *argv;
741
742                                 /* imply a "-input" */
743                                 if (dash_input_count == 0) dash_input_count = 1;
744
745                                 continue;
746                         } else if (exp_flageq1('o',arg)) {
747                                 /* apply following patterns to opposite side */
748                                 /* of interaction */
749
750                                 end_km = &input_default->keymap;
751
752                                 /* imply two "-input" */
753                                 if (dash_input_count < 2) {
754                                         dash_input_count = 2;
755                                         inp = input_default;
756                                         action_eof_ptr = &inp->action_eof;
757                                 }
758                                 continue;
759                         } else if (exp_flageq1('i',arg)) {
760                                 /* substitute master */
761
762                                 argc--;argv++;
763 /*                              master = atoi(*argv);*/
764                                 master_string = *argv;
765                                 /* will be used later on */
766
767                                 end_km = &input_default->keymap;
768
769                                 /* imply two "-input" */
770                                 if (dash_input_count < 2) {
771                                         dash_input_count = 2;
772                                         inp = input_default;
773                                         action_eof_ptr = &inp->action_eof;
774                                 }
775                                 continue;
776 /*                      } else if (exp_flageq("nocase",arg,3)) {*/
777 /*                              next_case_sensitive = FALSE;*/
778 /*                              continue;*/
779                         } else if (exp_flageq("echo",arg,4)) {
780                                 next_echo = TRUE;
781                                 continue;
782                         } else if (exp_flageq("nobuffer",arg,3)) {
783                                 next_writethru = TRUE;
784                                 continue;
785                         } else if (exp_flageq("indices",arg,3)) {
786                                 next_indices = TRUE;
787                                 continue;
788                         } else if (exp_flageq1('f',arg)) {
789                                 /* leftover from "fast" days */
790                                 continue;
791                         } else if (exp_flageq("reset",arg,5)) {
792                                 next_tty_reset = TRUE;
793                                 continue;
794                         } else if (exp_flageq1('F',arg)) {
795                                 /* leftover from "fast" days */
796                                 continue;
797                         } else if (exp_flageq("iread",arg,2)) {
798                                 next_iread = TRUE;
799                                 continue;
800                         } else if (exp_flageq("iwrite",arg,2)) {
801                                 next_iwrite = TRUE;
802                                 continue;
803                         } else if (exp_flageq("eof",arg,3)) {
804                                 struct action *action;
805
806                                 argc--;argv++;
807                                 debuglog("-eof is deprecated, use eof\r\n");
808                                 *action_eof_ptr = action = new_action(&action_base);
809                                 action->statement = *argv;
810                                 action->tty_reset = next_tty_reset;
811                                 next_tty_reset = FALSE;
812                                 action->iwrite = next_iwrite;
813                                 next_iwrite = FALSE;
814                                 action->iread = next_iread;
815                                 next_iread = FALSE;
816                                 action->timestamp = next_timestamp;
817                                 next_timestamp = FALSE;
818
819                                 continue;
820                         } else if (exp_flageq("timeout",arg,7)) {
821                                 int t;
822                                 struct action *action;
823                                 debuglog("-timeout is deprecated, use timeout\r\n");
824
825                                 argc--;argv++;
826                                 if (argc < 1) {
827                                         exp_error(interp,"-timeout needs time");
828                                         return(TCL_ERROR);
829                                 }
830
831                                 t = atoi(*argv);
832                                 argc--;argv++;
833                                 if (t != -1)
834                                         arbitrary_timeout = t;
835                                 /* we need an arbitrary timeout to start */
836                                 /* search for lowest one later */
837
838 #if 0
839                                 /* if -timeout comes before "-input", then applies */
840                                 /* to all descriptors, else just the current one */
841                                 if (dash_input_count > 0) {
842                                         timeout_simple = FALSE;
843                                         action = inp->action_timeout = 
844                                                 new_action(&action_base);
845                                         inp->timeout_nominal = t;
846                                 } else {
847                                         action = &action_timeout;
848                                         default_timeout = t;
849                                 }
850 #endif
851                                 timeout_simple = FALSE;
852                                 action = inp->action_timeout = new_action(&action_base);
853                                 inp->timeout_nominal = t;
854
855                                 action->statement = *argv;
856                                 action->tty_reset = next_tty_reset;
857                                 next_tty_reset = FALSE;
858                                 action->iwrite = next_iwrite;
859                                 next_iwrite = FALSE;
860                                 action->iread = next_iread;
861                                 next_iread = FALSE;
862                                 action->timestamp = next_timestamp;
863                                 next_timestamp = FALSE;
864                                 continue;
865                         } else if (exp_flageq("timestamp",arg,2)) {
866                                 debuglog("-timestamp is deprecated, use exp_timestamp command\r\n");
867                                 next_timestamp = TRUE;
868                                 continue;
869                         } else if (exp_flageq("nobrace",arg,7)) {
870                                 /* nobrace does nothing but take up space */
871                                 /* on the command line which prevents */
872                                 /* us from re-expanding any command lines */
873                                 /* of one argument that looks like it should */
874                                 /* be expanded to multiple arguments. */
875                                 continue;
876                         }
877                 }
878
879                 /*
880                  * pick up the pattern
881                  */
882
883                 km = new(struct keymap);
884
885                 /* so that we can match in order user specified */
886                 /* link to end of keymap list */
887                 *end_km = km;
888                 km->next = 0;
889                 end_km = &km->next;
890
891                 km->echo = next_echo;
892                 km->writethru = next_writethru;
893                 km->indices = next_indices;
894                 km->action.tty_reset = next_tty_reset;
895                 km->action.iwrite = next_iwrite;
896                 km->action.iread = next_iread;
897                 km->action.timestamp = next_timestamp;
898 /*              km->case_sensitive = next_case_sensitive;*/
899
900                 next_indices = next_echo = next_writethru = FALSE;
901                 next_tty_reset = FALSE;
902                 next_iwrite = next_iread = FALSE;
903 /*              next_case_sensitive = TRUE;*/
904
905                 km->keys = *argv;
906
907                 km->null = FALSE;
908                 km->re = 0;
909                 if (next_re) {
910                         Expect_TclRegError((char *)0);
911                         if (0 == (km->re = Expect_TclRegComp(*argv))) {
912                                 exp_error(interp,"bad regular expression: %s",
913                                                                 TclGetRegError());
914                                 return(TCL_ERROR);
915                         }
916                         next_re = FALSE;
917                 } if (next_null) {
918                         km->null = TRUE;
919                         next_null = FALSE;
920                 }
921
922                 argc--;argv++;
923
924                 km->action.statement = *argv;
925                 debuglog("defining key %s, action %s\r\n",
926                  km->keys,
927                  km->action.statement?(dprintify(km->action.statement))
928                                    :interpreter_cmd);
929
930                 /* imply a "-input" */
931                 if (dash_input_count == 0) dash_input_count = 1;
932         }
933
934         /* if the user has not supplied either "-output" for the */
935         /* default two "-input"s, fix them up here */
936
937         if (!input_user->output) {
938                 struct output *o = new(struct output);
939                 if (master_string == 0) {
940                         if (0 == exp_update_master(interp,&master,1,1)) {
941                                 return(TCL_ERROR);
942                         }
943                         o->i_list = exp_new_i_simple(master,EXP_TEMPORARY);
944                 } else {
945                         o->i_list = exp_new_i_complex(interp,master_string,
946                                         EXP_TEMPORARY,inter_updateproc);
947                 }
948 #if 0
949                 if (master == EXP_SPAWN_ID_BAD) {
950                         if (0 == exp_update_master(interp,&master,1,1)) {
951                                 return(TCL_ERROR);
952                         }
953                 }
954                 o->i_list = exp_new_i_simple(master,EXP_TEMPORARY);
955 #endif
956                 o->next = 0;    /* no one else */
957                 o->action_eof = &action_eof;
958                 input_user->output = o;
959         }
960
961         if (!input_default->output) {
962                 struct output *o = new(struct output);
963                 o->i_list = exp_new_i_simple(1,EXP_TEMPORARY);/* stdout by default */
964                 o->next = 0;    /* no one else */
965                 o->action_eof = &action_eof;
966                 input_default->output = o;
967         }
968
969         /* if user has given "-u" flag, substitute process for user */
970         /* in first two -inputs */
971         if (replace_user_by_process) {
972                 /* through away old ones */
973                 exp_free_i(interp,input_user->i_list,   inter_updateproc);
974                 exp_free_i(interp,input_default->output->i_list,inter_updateproc);
975
976                 /* replace with arg to -u */
977                 input_user->i_list = exp_new_i_complex(interp,
978                                 replace_user_by_process,
979                                 EXP_TEMPORARY,inter_updateproc);
980                 input_default->output->i_list = exp_new_i_complex(interp,
981                                 replace_user_by_process,
982                                 EXP_TEMPORARY,inter_updateproc);
983         }
984
985         /*
986          * now fix up for default spawn id
987          */
988
989         /* user could have replaced it with an indirect, so force update */
990         if (input_default->i_list->direct == EXP_INDIRECT) {
991                 exp_i_update(interp,input_default->i_list);
992         }
993
994         if    (input_default->i_list->fd_list
995            && (input_default->i_list->fd_list->fd == EXP_SPAWN_ID_BAD)) {
996                 if (master_string == 0) {
997                         if (0 == exp_update_master(interp,&master,1,1)) {
998                                 return(TCL_ERROR);
999                         }
1000                         input_default->i_list->fd_list->fd = master;
1001                 } else {
1002                         /* discard old one and install new one */
1003                         exp_free_i(interp,input_default->i_list,inter_updateproc);
1004                         input_default->i_list = exp_new_i_complex(interp,master_string,
1005                                 EXP_TEMPORARY,inter_updateproc);
1006                 }
1007 #if 0
1008                 if (master == EXP_SPAWN_ID_BAD) {
1009                         if (0 == exp_update_master(interp,&master,1,1)) {
1010                                 return(TCL_ERROR);
1011                         }
1012                 }
1013                 input_default->i_list->fd_list->fd = master;
1014 #endif
1015         }
1016
1017         /*
1018          * check for user attempting to interact with self
1019          * they're almost certainly just fooling around
1020          */
1021
1022         /* user could have replaced it with an indirect, so force update */
1023         if (input_user->i_list->direct == EXP_INDIRECT) {
1024                 exp_i_update(interp,input_user->i_list);
1025         }
1026
1027         if (input_user->i_list->fd_list && input_default->i_list->fd_list
1028             && (input_user->i_list->fd_list->fd == input_default->i_list->fd_list->fd)) {
1029                 exp_error(interp,"cannot interact with self - set spawn_id to a spawned process");
1030                 return(TCL_ERROR);
1031         }
1032
1033         fd_list = 0;
1034         fd_to_input = 0;
1035
1036         /***************************************************************/
1037         /* all data structures are sufficiently set up that we can now */
1038         /* "finish()" to terminate this procedure                      */
1039         /***************************************************************/
1040
1041         status = update_interact_fds(interp,&input_count,&fd_to_input,&fd_list,input_base,1,&configure_count,&real_tty);
1042         if (status == TCL_ERROR) finish(TCL_ERROR);
1043
1044         if (real_tty) {
1045                 tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1046         }
1047
1048         for (inp = input_base,i=0;inp;inp=inp->next,i++) {
1049                 /* start timers */
1050                 inp->timeout_remaining = inp->timeout_nominal;
1051             }
1052
1053         key = expect_key++;
1054
1055         /* declare ourselves "in sync" with external view of close/indirect */
1056         configure_count = exp_configure_count;
1057
1058 #ifndef SIMPLE_EVENT
1059         /* loop waiting (in event handler) for input */
1060         for (;;) {
1061                 int te; /* result of Tcl_Eval */
1062                 struct exp_f *u;
1063                 int rc; /* return code from ready.  This is further */
1064                         /* refined by matcher. */
1065                 int cc; /* chars count from read() */
1066                 int m;  /* master */
1067                 int m_out; /* where master echoes to */
1068                 struct action *action = 0;
1069                 time_t previous_time;
1070                 time_t current_time;
1071                 int match_length, skip;
1072                 int change;     /* if action requires cooked mode */
1073                 int attempt_match = TRUE;
1074                 struct input *soonest_input;
1075                 int print;              /* # of chars to print */
1076                 int oldprinted;         /* old version of u->printed */
1077
1078                 int timeout;    /* current as opposed to default_timeout */
1079
1080                 /* calculate how long to wait */
1081                 /* by finding shortest remaining timeout */
1082                 if (timeout_simple) {
1083                         timeout = default_timeout;
1084                 } else {
1085                         timeout = arbitrary_timeout;
1086
1087                         for (inp=input_base;inp;inp=inp->next) {
1088                                 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
1089                                     (inp->timeout_remaining <= timeout)) {
1090                                         soonest_input = inp;
1091                                         timeout = inp->timeout_remaining;
1092                                 }
1093                         }
1094
1095                         time(&previous_time);
1096                         /* timestamp here rather than simply saving old */
1097                         /* current time (after ready()) to account for */
1098                         /* possibility of slow actions */
1099
1100                         /* timeout can actually be EXP_TIME_INFINITY here if user */
1101                         /* explicitly supplied it in a few cases (or */
1102                         /* the count-down code is broken) */
1103                 }
1104
1105                 /* update the world, if necessary */
1106                 if (configure_count != exp_configure_count) {
1107                         status = update_interact_fds(interp,&input_count,
1108                                         &fd_to_input,&fd_list,input_base,1,
1109                                         &configure_count,&real_tty);
1110                         if (status) finish(status);
1111                 }
1112
1113                 rc = exp_get_next_event(interp,fd_list,input_count,&m,timeout,key);
1114                 if (rc == EXP_TCLERROR) return(TCL_ERROR);
1115
1116                 if (rc == EXP_RECONFIGURE) continue;
1117
1118                 if (rc == EXP_TIMEOUT) {
1119                         if (timeout_simple) {
1120                                 action = &action_timeout;
1121                                 goto got_action;
1122                         } else {
1123                                 action = soonest_input->action_timeout;
1124                                 /* arbitrarily pick first fd out of list */
1125                                 m = soonest_input->i_list->fd_list->fd;
1126                         }
1127                 }
1128                 if (!timeout_simple) {
1129                         int time_diff;
1130
1131                         time(&current_time);
1132                         time_diff = current_time - previous_time;
1133
1134                         /* update all timers */
1135                         for (inp=input_base;inp;inp=inp->next) {
1136                                 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
1137                                         inp->timeout_remaining -= time_diff;
1138                                         if (inp->timeout_remaining < 0)
1139                                                 inp->timeout_remaining = 0;
1140                                 }
1141                         }
1142                 }
1143
1144                 /* at this point, we have some kind of event which can be */
1145                 /* immediately processed - i.e. something that doesn't block */
1146
1147                 /* figure out who we are */
1148                 inp = fd_to_input[m];
1149 /*              u = inp->f;*/
1150                 u = exp_fs+m;
1151
1152                 /* reset timer */
1153                 inp->timeout_remaining = inp->timeout_nominal;
1154
1155                 switch (rc) {
1156                 case EXP_DATA_NEW:
1157                         if (u->size == u->msize) {
1158                             /* In theory, interact could be invoked when this situation */
1159                             /* already exists, hence the "probably" in the warning below */
1160
1161                             debuglog("WARNING: interact buffer is full, probably because your\r\n");
1162                             debuglog("patterns have matched all of it but require more chars\r\n");
1163                             debuglog("in order to complete the match.\r\n");
1164                             debuglog("Dumping first half of buffer in order to continue\r\n");
1165                             debuglog("Recommend you enlarge the buffer or fix your patterns.\r\n");
1166                             exp_buffer_shuffle(interp,u,0,INTER_OUT,"interact");
1167                         }
1168                         cc = read(m,    u->buffer + u->size,
1169                                         u->msize - u->size);
1170                         if (cc > 0) {
1171                                 u->key = key;
1172                                 u->size += cc;
1173                                 u->buffer[u->size] = '\0';
1174
1175                                 /* strip parity if requested */
1176                                 if (u->parity == 0) {
1177                                         /* do it from end backwards */
1178                                         char *p = u->buffer + u->size - 1;
1179                                         int count = cc;
1180                                         while (count--) {
1181                                                 *p-- &= 0x7f;
1182                                         }
1183                                 }
1184
1185                                 /* avoid another function call if possible */
1186                                 if (debugfile || is_debugging) {
1187                                         debuglog("spawn id %d sent <%s>\r\n",m,
1188                                                 exp_printify(u->buffer + u->size - cc));
1189                                 }
1190                                 break;
1191                         }
1192
1193                         rc = EXP_EOF;
1194                         /* Most systems have read() return 0, allowing */
1195                         /* control to fall thru and into this code.  On some */
1196                         /* systems (currently HP and new SGI), read() does */
1197                         /* see eof, and it must be detected earlier.  Then */
1198                         /* control jumps directly to this EXP_EOF label. */
1199
1200                         /*FALLTHRU*/
1201                 case EXP_EOF:
1202                         action = inp->action_eof;
1203                         attempt_match = FALSE;
1204                         skip = u->size;
1205                         debuglog("interact: received eof from spawn_id %d\r\n",m);
1206                         /* actual close is done later so that we have a */
1207                         /* chance to flush out any remaining characters */
1208                         need_to_close_master = TRUE;
1209
1210 #if EOF_SO
1211                         /* should really check for remaining chars and */
1212                         /* flush them but this will only happen in the */
1213                         /* unlikely scenario that there are partially */
1214                         /* matched buffered chars. */
1215                         /* So for now, indicate no chars to skip. */
1216                         skip = 0;
1217                         exp_close(interp,m);
1218 #endif
1219                         break;
1220                 case EXP_DATA_OLD:
1221                         cc = 0;
1222                         break;
1223                 case EXP_TIMEOUT:
1224                         action = inp->action_timeout;
1225                         attempt_match = FALSE;
1226                         skip = u->size;
1227                         break;
1228                 }
1229
1230                 km = 0;
1231
1232                 if (attempt_match) {
1233                         rc = in_keymap(u->buffer,u->size,inp->keymap,
1234                                 &km,&match_length,&skip,u->rm_nulls);
1235                 } else {
1236                         attempt_match = TRUE;
1237                 }
1238
1239                 /* put regexp result in variables */
1240                 if (km && km->re) {
1241 #define out(var,val)  debuglog("expect: set %s(%s) \"%s\"\r\n",INTER_OUT,var, \
1242                                                 dprintify(val)); \
1243                     Tcl_SetVar2(interp,INTER_OUT,var,val,0);
1244
1245                         char name[20], value[20];
1246                         Expect_regexp *re = km->re;
1247                         char match_char;/* place to hold char temporarily */
1248                                         /* uprooted by a NULL */
1249
1250                         for (i=0;i<NSUBEXP;i++) {
1251                                 int offset;
1252
1253                                 if (re->startp[i] == 0) continue;
1254
1255                                 if (km->indices) {
1256                                   /* start index */
1257                                   sprintf(name,"%d,start",i);
1258                                   offset = re->startp[i]-u->buffer;
1259                                   sprintf(value,"%d",offset);
1260                                   out(name,value);
1261
1262                                   /* end index */
1263                                   sprintf(name,"%d,end",i);
1264                                   sprintf(value,"%d",re->endp[i]-u->buffer-1);
1265                                   out(name,value);
1266                                 }
1267
1268                                 /* string itself */
1269                                 sprintf(name,"%d,string",i);
1270                                 /* temporarily null-terminate in */
1271                                 /* middle */
1272                                 match_char = *re->endp[i];
1273                                 *re->endp[i] = 0;
1274                                 out(name,re->startp[i]);
1275                                 *re->endp[i] = match_char;
1276                         }
1277                 }
1278
1279                 /*
1280                  * dispose of chars that should be skipped
1281                  * i.e., chars that cannot possibly be part of a match.
1282                  */
1283                 
1284                 /* "skip" is count of chars not involved in match */
1285                 /* "print" is count with chars involved in match */
1286
1287                 if (km && km->writethru) {
1288                         print = skip + match_length;
1289                 } else print = skip;
1290
1291                 /*
1292                  * echo chars if appropriate
1293                  */
1294                 if (km && km->echo) {
1295                         int seen;       /* either printed or echoed */
1296
1297                         /* echo to stdout rather than stdin */
1298                         m_out = (m == 0)?1:m;
1299
1300                         /* write is unlikely to fail, since we just read */
1301                         /* from same descriptor */
1302                         seen = u->printed + u->echoed;
1303                         if (skip >= seen) {
1304                                 write(m_out,u->buffer+skip,match_length);
1305                         } else if ((match_length + skip - seen) > 0) {
1306                                 write(m_out,u->buffer+seen,match_length+skip-seen);
1307                         }
1308                         u->echoed = match_length + skip - u->printed;
1309                 }
1310
1311                 oldprinted = u->printed;
1312
1313                 /* If expect has left characters in buffer, it has */
1314                 /* already echoed them to the screen, thus we must */
1315                 /* prevent them being rewritten.  Unfortunately this */
1316                 /* gives the possibility of matching chars that have */
1317                 /* already been output, but we do so since the user */
1318                 /* could have avoided it by flushing the output */
1319                 /* buffers directly. */
1320                 if (print > u->printed) {       /* usual case */
1321                         int wc; /* return code from write() */
1322                         for (outp = inp->output;outp;outp=outp->next) {
1323                             struct exp_fd_list *fdp;
1324                             for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
1325                                 int od; /* output descriptor */
1326
1327                                 /* send to logfile if open */
1328                                 /* and user is seeing it */
1329                                 if (logfile && real_tty_output(fdp->fd)) {
1330                                         fwrite(u->buffer+u->printed,1,
1331                                                print - u->printed,logfile);
1332                                 }
1333
1334                                 /* send to each output descriptor */
1335                                 od = fdp->fd;
1336                                 /* if opened by Tcl, it may use a different */
1337                                 /* output descriptor */
1338                                 od = (exp_fs[od].tcl_handle?exp_fs[od].tcl_output:od);
1339
1340                                 wc = write(od,u->buffer+u->printed,
1341                                         print - u->printed);
1342                                 if (wc <= 0) {
1343                                         debuglog("interact: write on spawn id %d failed (%s)\r\n",fdp->fd,Tcl_PosixError(interp));
1344                                         action = outp->action_eof;
1345                                         change = (action && action->tty_reset);
1346
1347                                         if (change && tty_changed)
1348                                                 exp_tty_set(interp,&tty_old,was_raw,was_echo);
1349                                         te = inter_eval(interp,action,m);
1350
1351                                         if (change && real_tty) tty_changed =
1352                                            exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1353                                         switch (te) {
1354                                         case TCL_BREAK:
1355                                         case TCL_CONTINUE:
1356                                                 finish(te);
1357                                         case EXP_TCL_RETURN:
1358                                                 finish(TCL_RETURN);
1359                                         case TCL_RETURN:
1360                                                 finish(TCL_OK);
1361                                         case TCL_OK:
1362                                                 /* god knows what the user might */
1363                                                 /* have done to us in the way of */
1364                                                 /* closed fds, so .... */
1365                                                 action = 0;     /* reset action */
1366                                                 continue;
1367                                         default:
1368                                                 finish(te);
1369                                         }
1370                                 }
1371                             }
1372                         }
1373                         u->printed = print;
1374                 }
1375
1376                 /* u->printed is now accurate with respect to the buffer */
1377                 /* However, we're about to shift the old data out of the */
1378                 /* buffer.  Thus, u->size, printed, and echoed must be */
1379                 /* updated */
1380
1381                 /* first update size based on skip information */
1382                 /* then set skip to the total amount skipped */
1383
1384                 if (rc == EXP_MATCH) {
1385                         action = &km->action;
1386
1387                         skip += match_length;
1388                         u->size -= skip;
1389
1390                         if (u->size) {
1391                                 memcpy(u->buffer, u->buffer + skip, u->size);
1392                                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
1393                         }
1394                 } else {
1395                         if (skip) {
1396                                 u->size -= skip;
1397                                 memcpy(u->buffer, u->buffer + skip, u->size);
1398                                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
1399                         }
1400                 }
1401
1402 #if EOF_SO
1403                 /* as long as buffer is still around, null terminate it */
1404                 if (rc != EXP_EOF) {
1405                         u->buffer[u->size] = '\0';
1406                         u->lower [u->size] = '\0';
1407                 }
1408 #else
1409                 u->buffer[u->size] = '\0';
1410                 u->lower [u->size] = '\0';
1411 #endif
1412
1413                 /* now update printed based on total amount skipped */
1414
1415                 u->printed -= skip;
1416                 /* if more skipped than printed (i.e., keymap encountered) */
1417                 /* for printed positive */
1418                 if (u->printed < 0) u->printed = 0;
1419
1420                 /* if we are in the middle of a match, force the next event */
1421                 /* to wait for more data to arrive */
1422                 u->force_read = (rc == EXP_CANMATCH);
1423
1424                 /* finally reset echoed if necessary */
1425                 if (rc != EXP_CANMATCH) {
1426                         if (skip >= oldprinted + u->echoed) u->echoed = 0;
1427                 }
1428
1429                 if (rc == EXP_EOF) {
1430                         exp_close(interp,m);
1431                         need_to_close_master = FALSE;
1432                 }
1433
1434                 if (action) {
1435 got_action:
1436                         change = (action && action->tty_reset);
1437                         if (change && tty_changed)
1438                                 exp_tty_set(interp,&tty_old,was_raw,was_echo);
1439
1440                         te = inter_eval(interp,action,m);
1441
1442                         if (change && real_tty) tty_changed =
1443                            exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
1444                         switch (te) {
1445                         case TCL_BREAK:
1446                         case TCL_CONTINUE:
1447                                 finish(te);
1448                         case EXP_TCL_RETURN:
1449                                 finish(TCL_RETURN);
1450                         case TCL_RETURN:
1451                                 finish(TCL_OK);
1452                         case TCL_OK:
1453                                 /* god knows what the user might */
1454                                 /* have done to us in the way of */
1455                                 /* closed fds, so .... */
1456                                 action = 0;     /* reset action */
1457                                 continue;
1458                         default:
1459                                 finish(te);
1460                         }
1461                 }
1462         }
1463
1464 #else /* SIMPLE_EVENT */
1465 /*      deferred_interrupt = FALSE;*/
1466 {
1467                 int te; /* result of Tcl_Eval */
1468                 struct exp_f *u;
1469                 int rc; /* return code from ready.  This is further */
1470                         /* refined by matcher. */
1471                 int cc; /* chars count from read() */
1472                 int m;  /* master */
1473                 struct action *action = 0;
1474                 time_t previous_time;
1475                 time_t current_time;
1476                 int match_length, skip;
1477                 int change;     /* if action requires cooked mode */
1478                 int attempt_match = TRUE;
1479                 struct input *soonest_input;
1480                 int print;              /* # of chars to print */
1481                 int oldprinted;         /* old version of u->printed */
1482
1483                 int timeout;    /* current as opposed to default_timeout */
1484
1485         if (-1 == (pid = fork())) {
1486                 exp_error(interp,"fork: %s",Tcl_PosixError(interp));
1487                 finish(TCL_ERROR);
1488         }
1489         if (pid == 0) { /* child - send process output to user */
1490             exp_close(interp,0);
1491
1492             m = fd_list[1];     /* get 2nd fd */
1493             input_count = 1;
1494
1495             while (1) {
1496
1497                 /* calculate how long to wait */
1498                 /* by finding shortest remaining timeout */
1499                 if (timeout_simple) {
1500                         timeout = default_timeout;
1501                 } else {
1502                         timeout = arbitrary_timeout;
1503
1504                         for (inp=input_base;inp;inp=inp->next) {
1505                                 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
1506                                     (inp->timeout_remaining < timeout))
1507                                         soonest_input = inp;
1508                                         timeout = inp->timeout_remaining;
1509                         }
1510
1511                         time(&previous_time);
1512                         /* timestamp here rather than simply saving old */
1513                         /* current time (after ready()) to account for */
1514                         /* possibility of slow actions */
1515
1516                         /* timeout can actually be EXP_TIME_INFINITY here if user */
1517                         /* explicitly supplied it in a few cases (or */
1518                         /* the count-down code is broken) */
1519                 }
1520
1521                 /* +1 so we can look at the "other" file descriptor */
1522                 rc = exp_get_next_event(interp,fd_list+1,input_count,&m,timeout,key);
1523                 if (!timeout_simple) {
1524                         int time_diff;
1525
1526                         time(&current_time);
1527                         time_diff = current_time - previous_time;
1528
1529                         /* update all timers */
1530                         for (inp=input_base;inp;inp=inp->next) {
1531                                 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
1532                                         inp->timeout_remaining -= time_diff;
1533                                         if (inp->timeout_remaining < 0)
1534                                                 inp->timeout_remaining = 0;
1535                                 }
1536                         }
1537                 }
1538
1539                 /* at this point, we have some kind of event which can be */
1540                 /* immediately processed - i.e. something that doesn't block */
1541
1542                 /* figure out who we are */
1543                 inp = fd_to_input[m];
1544 /*              u = inp->f;*/
1545                 u = exp_fs+m;
1546
1547                 switch (rc) {
1548                 case EXP_DATA_NEW:
1549                         cc = read(m,    u->buffer + u->size,
1550                                         u->msize - u->size);
1551                         if (cc > 0) {
1552                                 u->key = key;
1553                                 u->size += cc;
1554                                 u->buffer[u->size] = '\0';
1555
1556                                 /* strip parity if requested */
1557                                 if (u->parity == 0) {
1558                                         /* do it from end backwards */
1559                                         char *p = u->buffer + u->size - 1;
1560                                         int count = cc;
1561                                         while (count--) {
1562                                                 *p-- &= 0x7f;
1563                                         }
1564                                 }
1565
1566                                 /* avoid another function call if possible */
1567                                 if (debugfile || is_debugging) {
1568                                         debuglog("spawn id %d sent <%s>\r\n",m,
1569                                                 exp_printify(u->buffer + u->size - cc));
1570                                 }
1571                                 break;
1572                         }
1573                         /*FALLTHRU*/
1574
1575                         /* Most systems have read() return 0, allowing */
1576                         /* control to fall thru and into this code.  On some */
1577                         /* systems (currently HP and new SGI), read() does */
1578                         /* see eof, and it must be detected earlier.  Then */
1579                         /* control jumps directly to this EXP_EOF label. */
1580                 case EXP_EOF:
1581                         action = inp->action_eof;
1582                         attempt_match = FALSE;
1583                         skip = u->size;
1584                         rc = EXP_EOF;
1585                         debuglog("interact: child received eof from spawn_id %d\r\n",m);
1586                         exp_close(interp,m);
1587                         break;
1588                 case EXP_DATA_OLD:
1589                         cc = 0;
1590                         break;
1591                 }
1592
1593                 km = 0;
1594
1595                 if (attempt_match) {
1596                         rc = in_keymap(u->buffer,u->size,inp->keymap,
1597                                 &km,&match_length,&skip);
1598                 } else {
1599                         attempt_match = TRUE;
1600                 }
1601
1602                 /* put regexp result in variables */
1603                 if (km && km->re) {
1604 #define INTER_OUT "interact_out"
1605 #define out(i,val)  debuglog("expect: set %s(%s) \"%s\"\r\n",INTER_OUT,i, \
1606                                                 dprintify(val)); \
1607                     Tcl_SetVar2(interp,INTER_OUT,i,val,0);
1608
1609                         char name[20], value[20];
1610                         regexp *re = km->re;
1611                         char match_char;/* place to hold char temporarily */
1612                                         /* uprooted by a NULL */
1613
1614                         for (i=0;i<NSUBEXP;i++) {
1615                                 int offset;
1616
1617                                 if (re->startp[i] == 0) continue;
1618
1619                                 if (km->indices) {
1620                                   /* start index */
1621                                   sprintf(name,"%d,start",i);
1622                                   offset = re->startp[i]-u->buffer;
1623                                   sprintf(value,"%d",offset);
1624                                   out(name,value);
1625
1626                                   /* end index */
1627                                   sprintf(name,"%d,end",i);
1628                                   sprintf(value,"%d",re->endp[i]-u->buffer-1);
1629                                   out(name,value);
1630                                 }
1631
1632                                 /* string itself */
1633                                 sprintf(name,"%d,string",i);
1634                                 /* temporarily null-terminate in */
1635                                 /* middle */
1636                                 match_char = *re->endp[i];
1637                                 *re->endp[i] = 0;
1638                                 out(name,re->startp[i]);
1639                                 *re->endp[i] = match_char;
1640                         }
1641                 }
1642
1643                 /* dispose of chars that should be skipped */
1644                 
1645                 /* skip is chars not involved in match */
1646                 /* print is with chars involved in match */
1647
1648                 if (km && km->writethru) {
1649                         print = skip + match_length;
1650                 } else print = skip;
1651
1652                 /* figure out if we should echo any chars */
1653                 if (km && km->echo) {
1654                         int seen;       /* either printed or echoed */
1655
1656                         /* echo to stdout rather than stdin */
1657                         if (m == 0) m = 1;
1658
1659                         /* write is unlikely to fail, since we just read */
1660                         /* from same descriptor */
1661                         seen = u->printed + u->echoed;
1662                         if (skip >= seen) {
1663                                 write(m,u->buffer+skip,match_length);
1664                         } else if ((match_length + skip - seen) > 0) {
1665                                 write(m,u->buffer+seen,match_length+skip-seen);
1666                         }
1667                         u->echoed = match_length + skip - u->printed;
1668                 }
1669
1670                 oldprinted = u->printed;
1671
1672                 /* If expect has left characters in buffer, it has */
1673                 /* already echoed them to the screen, thus we must */
1674                 /* prevent them being rewritten.  Unfortunately this */
1675                 /* gives the possibility of matching chars that have */
1676                 /* already been output, but we do so since the user */
1677                 /* could have avoided it by flushing the output */
1678                 /* buffers directly. */
1679                 if (print > u->printed) {       /* usual case */
1680                         int wc; /* return code from write() */
1681                         for (outp = inp->output;outp;outp=outp->next) {
1682                             struct exp_fd_list *fdp;
1683                             for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
1684                                 int od; /* output descriptor */
1685
1686                                 /* send to logfile if open */
1687                                 /* and user is seeing it */
1688                                 if (logfile && real_tty_output(fdp->fd)) {
1689                                         fwrite(u->buffer+u->printed,1,
1690                                                print - u->printed,logfile);
1691                                 }
1692
1693                                 /* send to each output descriptor */
1694                                 od = fdp->fd;
1695                                 /* if opened by Tcl, it may use a different */
1696                                 /* output descriptor */
1697                                 od = (exp_fs[od].tcl_handle?exp_fs[od].tcl_output:od);
1698
1699                                 wc = write(od,u->buffer+u->printed,
1700                                         print - u->printed);
1701                                 if (wc <= 0) {
1702                                         debuglog("interact: write on spawn id %d failed (%s)\r\n",fdp->fd,Tcl_PosixError(interp));
1703                                         action = outp->action_eof;
1704
1705                                         te = inter_eval(interp,action,m);
1706
1707                                         switch (te) {
1708                                         case TCL_BREAK:
1709                                         case TCL_CONTINUE:
1710                                                 finish(te);
1711                                         case EXP_TCL_RETURN:
1712                                                 finish(TCL_RETURN);
1713                                         case TCL_RETURN:
1714                                                 finish(TCL_OK);
1715                                         case TCL_OK:
1716                                                 /* god knows what the user might */
1717                                                 /* have done to us in the way of */
1718                                                 /* closed fds, so .... */
1719                                                 action = 0;     /* reset action */
1720                                                 continue;
1721                                         default:
1722                                                 finish(te);
1723                                         }
1724                                 }
1725                             }
1726                         }
1727                         u->printed = print;
1728                 }
1729
1730                 /* u->printed is now accurate with respect to the buffer */
1731                 /* However, we're about to shift the old data out of the */
1732                 /* buffer.  Thus, u->size, printed, and echoed must be */
1733                 /* updated */
1734
1735                 /* first update size based on skip information */
1736                 /* then set skip to the total amount skipped */
1737
1738                 if (rc == EXP_MATCH) {
1739                         action = &km->action;
1740
1741                         skip += match_length;
1742                         u->size -= skip;
1743
1744                         if (u->size)
1745                                 memcpy(u->buffer, u->buffer + skip, u->size);
1746                                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
1747                 } else {
1748                         if (skip) {
1749                                 u->size -= skip;
1750                                 memcpy(u->buffer, u->buffer + skip, u->size);
1751                                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
1752                         }
1753                 }
1754
1755                 /* as long as buffer is still around, null terminate it */
1756                 if (rc != EXP_EOF) {
1757                         u->buffer[u->size] = '\0';
1758                         u->lower [u->size] = '\0';
1759                 }
1760                 /* now update printed based on total amount skipped */
1761
1762                 u->printed -= skip;
1763                 /* if more skipped than printed (i.e., keymap encountered) */
1764                 /* for printed positive */
1765                 if (u->printed < 0) u->printed = 0;
1766
1767                 /* if we are in the middle of a match, force the next event */
1768                 /* to wait for more data to arrive */
1769                 u->force_read = (rc == EXP_CANMATCH);
1770
1771                 /* finally reset echoed if necessary */
1772                 if (rc != EXP_CANMATCH) {
1773                         if (skip >= oldprinted + u->echoed) u->echoed = 0;
1774                 }
1775
1776                 if (action) {
1777                         te = inter_eval(interp,action,m);
1778                         switch (te) {
1779                         case TCL_BREAK:
1780                         case TCL_CONTINUE:
1781                                 finish(te);
1782                         case EXP_TCL_RETURN:
1783                                 finish(TCL_RETURN);
1784                         case TCL_RETURN:
1785                                 finish(TCL_OK);
1786                         case TCL_OK:
1787                                 /* god knows what the user might */
1788                                 /* have done to us in the way of */
1789                                 /* closed fds, so .... */
1790                                 action = 0;     /* reset action */
1791                                 continue;
1792                         default:
1793                                 finish(te);
1794                         }
1795                 }
1796             }
1797         } else { /* parent - send user keystrokes to process */
1798 #include <signal.h>
1799
1800 #if defined(SIGCLD) && !defined(SIGCHLD)
1801 #define SIGCHLD SIGCLD
1802 #endif
1803                 debuglog("fork = %d\r\n",pid);
1804                 signal(SIGCHLD,sigchld_handler);
1805 /*      restart:*/
1806 /*              tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);*/
1807
1808             m = fd_list[0];     /* get 1st fd */
1809             input_count = 1;
1810
1811             while (1) {
1812                 /* calculate how long to wait */
1813                 /* by finding shortest remaining timeout */
1814                 if (timeout_simple) {
1815                         timeout = default_timeout;
1816                 } else {
1817                         timeout = arbitrary_timeout;
1818
1819                         for (inp=input_base;inp;inp=inp->next) {
1820                                 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
1821                                     (inp->timeout_remaining < timeout))
1822                                         soonest_input = inp;
1823                                         timeout = inp->timeout_remaining;
1824                         }
1825
1826                         time(&previous_time);
1827                         /* timestamp here rather than simply saving old */
1828                         /* current time (after ready()) to account for */
1829                         /* possibility of slow actions */
1830
1831                         /* timeout can actually be EXP_TIME_INFINITY here if user */
1832                         /* explicitly supplied it in a few cases (or */
1833                         /* the count-down code is broken) */
1834                 }
1835
1836                 rc = exp_get_next_event(interp,fd_list,input_count,&m,timeout,key);
1837                 if (!timeout_simple) {
1838                         int time_diff;
1839
1840                         time(&current_time);
1841                         time_diff = current_time - previous_time;
1842
1843                         /* update all timers */
1844                         for (inp=input_base;inp;inp=inp->next) {
1845                                 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
1846                                         inp->timeout_remaining -= time_diff;
1847                                         if (inp->timeout_remaining < 0)
1848                                                 inp->timeout_remaining = 0;
1849                                 }
1850                         }
1851                 }
1852
1853                 /* at this point, we have some kind of event which can be */
1854                 /* immediately processed - i.e. something that doesn't block */
1855
1856                 /* figure out who we are */
1857                 inp = fd_to_input[m];
1858 /*              u = inp->f;*/
1859                 u = exp_fs+m;
1860
1861                 switch (rc) {
1862                 case EXP_DATA_NEW:
1863                         cc = i_read(m,  u->buffer + u->size,
1864                                         u->msize - u->size);
1865                         if (cc > 0) {
1866                                 u->key = key;
1867                                 u->size += cc;
1868                                 u->buffer[u->size] = '\0';
1869
1870                                 /* strip parity if requested */
1871                                 if (u->parity == 0) {
1872                                         /* do it from end backwards */
1873                                         char *p = u->buffer + u->size - 1;
1874                                         int count = cc;
1875                                         while (count--) {
1876                                                 *p-- &= 0x7f;
1877                                         }
1878                                 }
1879
1880                                 /* avoid another function call if possible */
1881                                 if (debugfile || is_debugging) {
1882                                         debuglog("spawn id %d sent <%s>\r\n",m,
1883                                                 exp_printify(u->buffer + u->size - cc));
1884                                 }
1885                                 break;
1886                         } else if (cc == EXP_CHILD_EOF) {
1887                                 /* user could potentially have two outputs in which */
1888                                 /* case we might be looking at the wrong one, but */
1889                                 /* the likelihood of this is nil */
1890                                 action = inp->output->action_eof;
1891                                 attempt_match = FALSE;
1892                                 skip = u->size;
1893                                 rc = EXP_EOF;
1894                                 debuglog("interact: process died/eof\r\n");
1895                                 clean_up_after_child(interp,fd_list[1]);
1896                                 break;
1897                         }
1898                         /*FALLTHRU*/
1899
1900                         /* Most systems have read() return 0, allowing */
1901                         /* control to fall thru and into this code.  On some */
1902                         /* systems (currently HP and new SGI), read() does */
1903                         /* see eof, and it must be detected earlier.  Then */
1904                         /* control jumps directly to this EXP_EOF label. */
1905                 case EXP_EOF:
1906                         action = inp->action_eof;
1907                         attempt_match = FALSE;
1908                         skip = u->size;
1909                         rc = EXP_EOF;
1910                         debuglog("user sent EOF or disappeared\n\n");
1911                         break;
1912                 case EXP_DATA_OLD:
1913                         cc = 0;
1914                         break;
1915                 }
1916
1917                 km = 0;
1918
1919                 if (attempt_match) {
1920                         rc = in_keymap(u->buffer,u->size,inp->keymap,
1921                                 &km,&match_length,&skip);
1922                 } else {
1923                         attempt_match = TRUE;
1924                 }
1925
1926                 /* put regexp result in variables */
1927                 if (km && km->re) {
1928                         char name[20], value[20];
1929                         regexp *re = km->re;
1930                         char match_char;/* place to hold char temporarily */
1931                                         /* uprooted by a NULL */
1932
1933                         for (i=0;i<NSUBEXP;i++) {
1934                                 int offset;
1935
1936                                 if (re->startp[i] == 0) continue;
1937
1938                                 if (km->indices) {
1939                                   /* start index */
1940                                   sprintf(name,"%d,start",i);
1941                                   offset = re->startp[i]-u->buffer;
1942                                   sprintf(value,"%d",offset);
1943                                   out(name,value);
1944
1945                                   /* end index */
1946                                   sprintf(name,"%d,end",i);
1947                                   sprintf(value,"%d",re->endp[i]-u->buffer-1);
1948                                   out(name,value);
1949                                 }
1950
1951                                 /* string itself */
1952                                 sprintf(name,"%d,string",i);
1953                                 /* temporarily null-terminate in */
1954                                 /* middle */
1955                                 match_char = *re->endp[i];
1956                                 *re->endp[i] = 0;
1957                                 out(name,re->startp[i]);
1958                                 *re->endp[i] = match_char;
1959                         }
1960                 }
1961
1962                 /* dispose of chars that should be skipped */
1963                 
1964                 /* skip is chars not involved in match */
1965                 /* print is with chars involved in match */
1966
1967                 if (km && km->writethru) {
1968                         print = skip + match_length;
1969                 } else print = skip;
1970
1971                 /* figure out if we should echo any chars */
1972                 if (km && km->echo) {
1973                         int seen;       /* either printed or echoed */
1974
1975                         /* echo to stdout rather than stdin */
1976                         if (m == 0) m = 1;
1977
1978                         /* write is unlikely to fail, since we just read */
1979                         /* from same descriptor */
1980                         seen = u->printed + u->echoed;
1981                         if (skip >= seen) {
1982                                 write(m,u->buffer+skip,match_length);
1983                         } else if ((match_length + skip - seen) > 0) {
1984                                 write(m,u->buffer+seen,match_length+skip-seen);
1985                         }
1986                         u->echoed = match_length + skip - u->printed;
1987                 }
1988
1989                 oldprinted = u->printed;
1990
1991                 /* If expect has left characters in buffer, it has */
1992                 /* already echoed them to the screen, thus we must */
1993                 /* prevent them being rewritten.  Unfortunately this */
1994                 /* gives the possibility of matching chars that have */
1995                 /* already been output, but we do so since the user */
1996                 /* could have avoided it by flushing the output */
1997                 /* buffers directly. */
1998                 if (print > u->printed) {       /* usual case */
1999                         int wc; /* return code from write() */
2000                         for (outp = inp->output;outp;outp=outp->next) {
2001                             struct exp_fd_list *fdp;
2002                             for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
2003                                 int od; /* output descriptor */
2004
2005                                 /* send to logfile if open */
2006                                 /* and user is seeing it */
2007                                 if (logfile && real_tty_output(fdp->fd)) {
2008                                         fwrite(u->buffer+u->printed,1,
2009                                                print - u->printed,logfile);
2010                                 }
2011
2012                                 /* send to each output descriptor */
2013                                 od = fdp->fd;
2014                                 /* if opened by Tcl, it may use a different */
2015                                 /* output descriptor */
2016                                 od = (exp_fs[od].tcl_handle?exp_fs[od].tcl_output:od);
2017
2018                                 wc = write(od,u->buffer+u->printed,
2019                                         print - u->printed);
2020                                 if (wc <= 0) {
2021                                         debuglog("interact: write on spawn id %d failed (%s)\r\n",fdp->fd,Tcl_PosixError(interp));
2022                                         clean_up_after_child(interp,fdp->fd);
2023                                         action = outp->action_eof;
2024                                         change = (action && action->tty_reset);
2025                                         if (change && tty_changed)
2026                                                 exp_tty_set(interp,&tty_old,was_raw,was_echo);
2027                                         te = inter_eval(interp,action,m);
2028
2029                                         if (change && real_tty) tty_changed =
2030                                            exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
2031                                         switch (te) {
2032                                         case TCL_BREAK:
2033                                         case TCL_CONTINUE:
2034                                                 finish(te);
2035                                         case EXP_TCL_RETURN:
2036                                                 finish(TCL_RETURN);
2037                                         case TCL_RETURN:
2038                                                 finish(TCL_OK);
2039                                         case TCL_OK:
2040                                                 /* god knows what the user might */
2041                                                 /* have done to us in the way of */
2042                                                 /* closed fds, so .... */
2043                                                 action = 0;     /* reset action */
2044                                                 continue;
2045                                         default:
2046                                                 finish(te);
2047                                         }
2048                                 }
2049                             }
2050                         }
2051                         u->printed = print;
2052                 }
2053
2054                 /* u->printed is now accurate with respect to the buffer */
2055                 /* However, we're about to shift the old data out of the */
2056                 /* buffer.  Thus, u->size, printed, and echoed must be */
2057                 /* updated */
2058
2059                 /* first update size based on skip information */
2060                 /* then set skip to the total amount skipped */
2061
2062                 if (rc == EXP_MATCH) {
2063                         action = &km->action;
2064
2065                         skip += match_length;
2066                         u->size -= skip;
2067
2068                         if (u->size)
2069                                 memcpy(u->buffer, u->buffer + skip, u->size);
2070                                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
2071                 } else {
2072                         if (skip) {
2073                                 u->size -= skip;
2074                                 memcpy(u->buffer, u->buffer + skip, u->size);
2075                                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
2076                         }
2077                 }
2078
2079                 /* as long as buffer is still around, null terminate it */
2080                 if (rc != EXP_EOF) {
2081                         u->buffer[u->size] = '\0';
2082                         u->lower [u->size] = '\0';
2083                 }
2084                 /* now update printed based on total amount skipped */
2085
2086                 u->printed -= skip;
2087                 /* if more skipped than printed (i.e., keymap encountered) */
2088                 /* for printed positive */
2089                 if (u->printed < 0) u->printed = 0;
2090
2091                 /* if we are in the middle of a match, force the next event */
2092                 /* to wait for more data to arrive */
2093                 u->force_read = (rc == EXP_CANMATCH);
2094
2095                 /* finally reset echoed if necessary */
2096                 if (rc != EXP_CANMATCH) {
2097                         if (skip >= oldprinted + u->echoed) u->echoed = 0;
2098                 }
2099
2100                 if (action) {
2101                         change = (action && action->tty_reset);
2102                         if (change && tty_changed)
2103                                 exp_tty_set(interp,&tty_old,was_raw,was_echo);
2104
2105                         te = inter_eval(interp,action,m);
2106
2107                         if (change && real_tty) tty_changed =
2108                            exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
2109                         switch (te) {
2110                         case TCL_BREAK:
2111                         case TCL_CONTINUE:
2112                                 finish(te);
2113                         case EXP_TCL_RETURN:
2114                                 finish(TCL_RETURN);
2115                         case TCL_RETURN:
2116                                 finish(TCL_OK);
2117                         case TCL_OK:
2118                                 /* god knows what the user might */
2119                                 /* have done to us in the way of */
2120                                 /* closed fds, so .... */
2121                                 action = 0;     /* reset action */
2122                                 continue;
2123                         default:
2124                                 finish(te);
2125                         }
2126                 }
2127             }
2128         }
2129 }
2130 #endif /* SIMPLE_EVENT */
2131
2132  done:
2133 #ifdef SIMPLE_EVENT
2134         /* force child to exit upon eof from master */
2135         if (pid == 0) {
2136                 exit(SPAWNED_PROCESS_DIED);
2137         }
2138 #endif /* SIMPLE_EVENT */
2139
2140         if (need_to_close_master) exp_close(interp,master);
2141
2142         if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo);
2143         if (oldargv) ckfree((char *)argv);
2144         if (fd_list) ckfree((char *)fd_list);
2145         if (fd_to_input) ckfree((char *)fd_to_input);
2146         free_input(interp,input_base);
2147         free_action(action_base);
2148
2149         return(status);
2150 }
2151
2152 /* version of Tcl_Eval for interact */ 
2153 static int
2154 inter_eval(interp,action,spawn_id)
2155 Tcl_Interp *interp;
2156 struct action *action;
2157 int spawn_id;
2158 {
2159         int status;
2160         char value[20];
2161
2162         /* deprecated */
2163         if (action->timestamp) {
2164                 time_t current_time;
2165                 time(&current_time);
2166                 exp_timestamp(interp,&current_time,INTER_OUT);
2167         }
2168         /* deprecated */
2169
2170         if (action->iwrite) {
2171                 sprintf(value,"%d",spawn_id);
2172                 out("spawn_id",value);
2173         }
2174
2175         if (action->statement) {
2176                 status = Tcl_Eval(interp,action->statement);
2177         } else {
2178                 exp_nflog("\r\n",1);
2179                 status = exp_interpreter(interp);
2180         }
2181
2182         return status;
2183 }
2184
2185 static void
2186 free_keymap(km)
2187 struct keymap *km;
2188 {
2189         if (km == 0) return;
2190         free_keymap(km->next);
2191
2192         ckfree((char *)km);
2193 }
2194
2195 static void
2196 free_action(a)
2197 struct action *a;
2198 {
2199         struct action *next;
2200
2201         while (a) {
2202                 next = a->next;
2203                 ckfree((char *)a);
2204                 a = next;
2205         }
2206 }
2207
2208 static void
2209 free_input(interp,i)
2210 Tcl_Interp *interp;
2211 struct input *i;
2212 {
2213         if (i == 0) return;
2214         free_input(interp,i->next);
2215
2216         exp_free_i(interp,i->i_list,inter_updateproc);
2217         free_output(interp,i->output);
2218         free_keymap(i->keymap);
2219         ckfree((char *)i);
2220 }
2221
2222 static struct action *
2223 new_action(base)
2224 struct action **base;
2225 {
2226         struct action *o = new(struct action);
2227
2228         /* stick new action into beginning of list of all actions */
2229         o->next = *base;
2230         *base = o;
2231
2232         return o;
2233 }
2234
2235 static void
2236 free_output(interp,o)
2237 Tcl_Interp *interp;
2238 struct output *o;
2239 {
2240         if (o == 0) return;
2241         free_output(interp,o->next);
2242         exp_free_i(interp,o->i_list,inter_updateproc);
2243
2244         ckfree((char *)o);
2245 }
2246
2247 static struct exp_cmd_data cmd_data[]  = {
2248 {"interact",    exp_proc(Exp_InteractCmd),      0,      0},
2249 {0}};
2250
2251 void
2252 exp_init_interact_cmds(interp)
2253 Tcl_Interp *interp;
2254 {
2255         exp_create_commands(interp,cmd_data);
2256 }