OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [expect/] [exp_inter.c] - Blame information for rev 1776

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/* 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 "tclRegexp.h"
49
#include "exp_regexp.h"
50
 
51
extern char *TclGetRegError();
52
extern void 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
        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
                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
                        TclRegError((char *)0);
911
                        if (0 == (km->re = 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
                        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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.