#include "expect_cf.h"
#include <stdio.h>
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include <ctype.h>
#include "tcl.h"
#include "string.h"
#include "exp_tty_in.h"
#include "exp_rename.h"
#include "exp_prog.h"
#include "exp_command.h"
#include "exp_log.h"
#include "exp_tstamp.h"
#include "tclRegexp.h"
#include "exp_regexp.h"
extern char *TclGetRegError();
extern void TclRegError();
#define INTER_OUT "interact_out"
#if 0
#define real_tty_output(x) (exp_stdout_is_tty && (((x)==1) || ((x)==exp_dev_tty)))
#define real_tty_input(x) (exp_stdin_is_tty && (((x)==0) || ((x)==exp_dev_tty)))
#endif
#define real_tty_output(x) (((x)==1) || ((x)==exp_dev_tty))
#define real_tty_input(x) (exp_stdin_is_tty && (((x)==0) || ((x)==exp_dev_tty)))
#define new(x) (x *)ckalloc(sizeof(x))
struct action {
char *statement;
int tty_reset;
int iread;
int iwrite;
int timestamp;
struct action *next;
};
struct keymap {
char *keys;
regexp *re;
int null;
int case_sensitive;
int echo;
int writethru;
int indices;
struct action action;
struct keymap *next;
};
struct output {
struct exp_i *i_list;
struct action *action_eof;
struct output *next;
};
struct input {
struct exp_i *i_list;
struct output *output;
struct action *action_eof;
struct action *action_timeout;
struct keymap *keymap;
int timeout_nominal;
int timeout_remaining;
struct input *next;
};
static void free_input();
static void free_keymap();
static void free_output();
static void free_action();
static struct action *new_action();
static int inter_eval();
static int
in_keymap(string,stringlen,keymap,km_match,match_length,skip,rm_nulls)
char *string;
int stringlen;
struct keymap *keymap;
struct keymap **km_match;
int *match_length;
int *skip;
int rm_nulls;
{
struct keymap *km;
char *ks;
char *start_search;
char *string_end;
if (!keymap) {
*skip = stringlen;
return(EXP_CANTMATCH);
}
string_end = string + stringlen;
regbol = string;
for (start_search = string;start_search<string_end;start_search++) {
if (*km_match) break;
for (km=keymap;km;km=km->next) {
char *s;
if (km->null) {
if (*start_search == 0) {
*skip = start_search-string;
*match_length = 1;
*km_match = km;
return(EXP_MATCH);
}
} else if (!km->re) {
for (s = start_search,ks = km->keys ;;s++,ks++) {
if (*ks == 0) {
*skip = start_search-string;
*match_length = s-start_search;
*km_match = km;
return(EXP_MATCH);
}
if (s == string_end) {
if (!*km_match) *km_match = km;
break;
}
if (*s == *ks) continue;
if ((*s == '\0') && rm_nulls) {
ks--;
continue;
}
break;
}
} else {
int r;
regexp *prog = km->re;
if (prog->reganch) {
if (string != start_search) continue;
}
if (prog->regstart) {
if (*start_search != prog->regstart) continue;
}
r = exp_regtry(prog,start_search,match_length);
if (r == EXP_MATCH) {
*km_match = km;
*skip = start_search-string;
return(EXP_MATCH);
}
if (r == EXP_CANMATCH) {
if (!*km_match) *km_match = km;
}
}
}
}
if (*km_match) {
char *p;
*skip = (start_search-string)-1;
#if 0
*match_length = stringlen - *skip;
#else
p = start_search;
while (*p) {
p++;
}
*match_length = (p - start_search) + 1;
#endif
return(EXP_CANMATCH);
}
*skip = start_search-string;
return(EXP_CANTMATCH);
}
#ifdef SIMPLE_EVENT
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
#endif
#include <setjmp.h>
static jmp_buf env;
static int reading;
static int deferred_interrupt = FALSE;
void sigchld_handler()
{
if (reading) longjmp(env,1);
deferred_interrupt = TRUE;
}
#define EXP_CHILD_EOF -100
static int
i_read(fd,buffer,length)
int fd;
char *buffer;
int length;
{
int cc = EXP_CHILD_EOF;
if (deferred_interrupt) return(cc);
if (0 == setjmp(env)) {
reading = TRUE;
cc = read(fd,buffer,length);
}
reading = FALSE;
return(cc);
}
#define CHILD_DIED -2
#define SPAWNED_PROCESS_DIED -3
static void
clean_up_after_child(interp,master)
Tcl_Interp *interp;
int master;
{
int status;
int pid;
int i;
pid = wait(&status);
for (i=0;i<=exp_fd_max;i++) {
if (exp_fs[i].pid == pid) {
exp_fs[i].sys_waited = TRUE;
exp_fs[i].wait = status;
}
}
pid = wait(&status);
for (i=0;i<=exp_fd_max;i++) {
if (exp_fs[i].pid == pid) {
exp_fs[i].sys_waited = TRUE;
exp_fs[i].wait = status;
}
}
deferred_interrupt = FALSE;
exp_close(interp,master);
master = -1;
}
#endif
static int
update_interact_fds(interp,fd_count,fd_to_input,fd_list,input_base,
do_indirect,config_count,real_tty_caller)
Tcl_Interp *interp;
int *fd_count;
struct input ***fd_to_input;
int **fd_list;
struct input *input_base;
int do_indirect;
int *config_count;
int *real_tty_caller;
{
struct input *inp;
struct output *outp;
struct exp_fd_list *fdp;
int count;
int real_tty = FALSE;
*config_count = exp_configure_count;
count = 0;
for (inp = input_base;inp;inp=inp->next) {
if (do_indirect) {
if (inp->i_list->direct == EXP_INDIRECT) {
exp_i_update(interp,inp->i_list);
}
for (outp = inp->output;outp;outp=outp->next) {
if (outp->i_list->direct == EXP_INDIRECT) {
exp_i_update(interp,outp->i_list);
}
}
}
for (fdp = inp->i_list->fd_list;fdp;fdp=fdp->next) {
count++;
if (!exp_fd2f(interp,fdp->fd,1,1,"interact"))
return(TCL_ERROR);
}
for (outp = inp->output;outp;outp=outp->next) {
for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
if (fdp->fd == 0) {
fdp->fd = 1;
} else if (fdp->fd == 1) {
} else if (!exp_fd2f(interp,fdp->fd,1,0,"interact"))
return(TCL_ERROR);
}
}
}
if (!do_indirect) return TCL_OK;
if (*fd_to_input == 0) {
*fd_to_input = (struct input **)ckalloc(
(exp_fd_max+1) * sizeof(struct input *));
*fd_list = (int *)ckalloc(count * sizeof(int));
} else {
*fd_to_input = (struct input **)ckrealloc((char *)*fd_to_input,
(exp_fd_max+1) * sizeof(struct input *));
*fd_list = (int *)ckrealloc((char *)*fd_list,count * sizeof(int));
}
count = 0;
for (inp = input_base;inp;inp=inp->next) {
for (fdp = inp->i_list->fd_list;fdp;fdp=fdp->next) {
(*fd_to_input)[fdp->fd] = inp;
(*fd_list)[count] = fdp->fd;
if (real_tty_input(fdp->fd)) real_tty = TRUE;
count++;
}
}
*fd_count = count;
*real_tty_caller = real_tty;
return TCL_OK;
}
static char *
inter_updateproc(clientData, interp, name1, name2, flags)
ClientData clientData;
Tcl_Interp *interp;
char *name1;
char *name2;
int flags;
{
exp_configure_count++;
return 0;
}
#define finish(x) { status = x; goto done; }
static char return_cmd[] = "return";
static char interpreter_cmd[] = "interpreter";
int
Exp_InteractCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *arg;
#ifdef SIMPLE_EVENT
int pid;
#endif
int input_count;
struct input **fd_to_input;
int *fd_list;
struct keymap *km;
int master = EXP_SPAWN_ID_BAD;
char *master_string = 0;
int need_to_close_master = FALSE;
int next_tty_reset = FALSE;
int next_iread = FALSE;
int next_iwrite = FALSE;
int next_re = FALSE;
int next_null = FALSE;
int next_writethru = FALSE;
int next_indices = FALSE;
int next_echo = FALSE;
int next_timestamp = FALSE;
char **oldargv = 0;
int status = TCL_OK;
int i;
int timeout_simple = TRUE;
int real_tty;
int tty_changed = FALSE;
int was_raw;
int was_echo;
exp_tty tty_old;
char *replace_user_by_process = 0;
struct input *input_base;
#define input_user input_base
struct input *input_default;
struct input *inp;
struct output *outp;
int dash_input_count = 0;
int arbitrary_timeout;
int default_timeout;
struct action action_timeout;
struct action action_eof;
struct action **action_eof_ptr;
struct action *action_base = 0;
struct keymap **end_km;
int key;
int configure_count;
if ((argc == 2) && exp_one_arg_braced(argv[1])) {
return(exp_eval_with_one_arg(clientData,interp,argv));
} else if ((argc == 3) && streq(argv[1],"-brace")) {
char *new_argv[2];
new_argv[0] = argv[0];
new_argv[1] = argv[2];
return(exp_eval_with_one_arg(clientData,interp,new_argv));
}
argv++;
argc--;
default_timeout = EXP_TIME_INFINITY;
arbitrary_timeout = EXP_TIME_INFINITY;
input_user = new(struct input);
input_user->i_list = exp_new_i_simple(0,EXP_TEMPORARY);
input_user->output = 0;
input_user->action_eof = &action_eof;
input_user->timeout_nominal = EXP_TIME_INFINITY;
input_user->action_timeout = 0;
input_user->keymap = 0;
end_km = &input_user->keymap;
inp = input_user;
action_eof_ptr = &input_user->action_eof;
input_default = new(struct input);
input_default->i_list = exp_new_i_simple(EXP_SPAWN_ID_BAD,EXP_TEMPORARY);
input_default->output = 0;
input_default->action_eof = &action_eof;
input_default->timeout_nominal = EXP_TIME_INFINITY;
input_default->action_timeout = 0;
input_default->keymap = 0;
input_default->next = 0;
input_user->next = input_default;
action_eof.statement = return_cmd;
action_eof.tty_reset = FALSE;
action_eof.iread = FALSE;
action_eof.iwrite = FALSE;
action_eof.timestamp = FALSE;
for (;argc>0;argc--,argv++) {
arg = *argv;
if (exp_flageq("eof",arg,3)) {
struct action *action;
argc--;argv++;
*action_eof_ptr = action = new_action(&action_base);
action->statement = *argv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
action->timestamp = next_timestamp;
next_timestamp = FALSE;
continue;
} else if (exp_flageq("timeout",arg,7)) {
int t;
struct action *action;
argc--;argv++;
if (argc < 1) {
exp_error(interp,"timeout needs time");
return(TCL_ERROR);
}
t = atoi(*argv);
argc--;argv++;
if (t != -1) arbitrary_timeout = t;
timeout_simple = FALSE;
action = inp->action_timeout = new_action(&action_base);
inp->timeout_nominal = t;
action->statement = *argv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
action->timestamp = next_timestamp;
next_timestamp = FALSE;
continue;
} else if (exp_flageq("null",arg,4)) {
next_null = TRUE;
} else if (arg[0] == '-') {
arg++;
if (exp_flageq1('-',arg)
|| (exp_flageq("exact",arg,3))) {
argc--;argv++;
} else if (exp_flageq("regexp",arg,2)) {
if (argc < 1) {
exp_error(interp,"-re needs pattern");
return(TCL_ERROR);
}
next_re = TRUE;
argc--;
argv++;
} else if (exp_flageq("input",arg,2)) {
dash_input_count++;
if (dash_input_count == 2) {
inp = input_default;
input_user->next = input_default;
} else if (dash_input_count > 2) {
struct input *previous_input = inp;
inp = new(struct input);
previous_input->next = inp;
}
inp->output = 0;
inp->action_eof = &action_eof;
action_eof_ptr = &inp->action_eof;
inp->timeout_nominal = default_timeout;
inp->action_timeout = &action_timeout;
inp->keymap = 0;
end_km = &inp->keymap;
inp->next = 0;
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-input needs argument");
return(TCL_ERROR);
}
inp->i_list = exp_new_i_complex(interp,*argv,
EXP_TEMPORARY,inter_updateproc);
continue;
} else if (exp_flageq("output",arg,3)) {
struct output *tmp;
if (dash_input_count == 0) dash_input_count = 1;
outp = new(struct output);
tmp = inp->output;
inp->output = outp;
outp->next = tmp;
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-output needs argument");
return(TCL_ERROR);
}
outp->i_list = exp_new_i_complex(interp,*argv,
EXP_TEMPORARY,inter_updateproc);
outp->action_eof = &action_eof;
action_eof_ptr = &outp->action_eof;
continue;
} else if (exp_flageq1('u',arg)) {
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-u needs argument");
return(TCL_ERROR);
}
replace_user_by_process = *argv;
if (dash_input_count == 0) dash_input_count = 1;
continue;
} else if (exp_flageq1('o',arg)) {
end_km = &input_default->keymap;
if (dash_input_count < 2) {
dash_input_count = 2;
inp = input_default;
action_eof_ptr = &inp->action_eof;
}
continue;
} else if (exp_flageq1('i',arg)) {
argc--;argv++;
master_string = *argv;
end_km = &input_default->keymap;
if (dash_input_count < 2) {
dash_input_count = 2;
inp = input_default;
action_eof_ptr = &inp->action_eof;
}
continue;
} else if (exp_flageq("echo",arg,4)) {
next_echo = TRUE;
continue;
} else if (exp_flageq("nobuffer",arg,3)) {
next_writethru = TRUE;
continue;
} else if (exp_flageq("indices",arg,3)) {
next_indices = TRUE;
continue;
} else if (exp_flageq1('f',arg)) {
continue;
} else if (exp_flageq("reset",arg,5)) {
next_tty_reset = TRUE;
continue;
} else if (exp_flageq1('F',arg)) {
continue;
} else if (exp_flageq("iread",arg,2)) {
next_iread = TRUE;
continue;
} else if (exp_flageq("iwrite",arg,2)) {
next_iwrite = TRUE;
continue;
} else if (exp_flageq("eof",arg,3)) {
struct action *action;
argc--;argv++;
debuglog("-eof is deprecated, use eof\r\n");
*action_eof_ptr = action = new_action(&action_base);
action->statement = *argv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
action->timestamp = next_timestamp;
next_timestamp = FALSE;
continue;
} else if (exp_flageq("timeout",arg,7)) {
int t;
struct action *action;
debuglog("-timeout is deprecated, use timeout\r\n");
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-timeout needs time");
return(TCL_ERROR);
}
t = atoi(*argv);
argc--;argv++;
if (t != -1)
arbitrary_timeout = t;
#if 0
if (dash_input_count > 0) {
timeout_simple = FALSE;
action = inp->action_timeout =
new_action(&action_base);
inp->timeout_nominal = t;
} else {
action = &action_timeout;
default_timeout = t;
}
#endif
timeout_simple = FALSE;
action = inp->action_timeout = new_action(&action_base);
inp->timeout_nominal = t;
action->statement = *argv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
action->timestamp = next_timestamp;
next_timestamp = FALSE;
continue;
} else if (exp_flageq("timestamp",arg,2)) {
debuglog("-timestamp is deprecated, use exp_timestamp command\r\n");
next_timestamp = TRUE;
continue;
} else if (exp_flageq("nobrace",arg,7)) {
continue;
}
}
km = new(struct keymap);
*end_km = km;
km->next = 0;
end_km = &km->next;
km->echo = next_echo;
km->writethru = next_writethru;
km->indices = next_indices;
km->action.tty_reset = next_tty_reset;
km->action.iwrite = next_iwrite;
km->action.iread = next_iread;
km->action.timestamp = next_timestamp;
next_indices = next_echo = next_writethru = FALSE;
next_tty_reset = FALSE;
next_iwrite = next_iread = FALSE;
km->keys = *argv;
km->null = FALSE;
km->re = 0;
if (next_re) {
TclRegError((char *)0);
if (0 == (km->re = TclRegComp(*argv))) {
exp_error(interp,"bad regular expression: %s",
TclGetRegError());
return(TCL_ERROR);
}
next_re = FALSE;
} if (next_null) {
km->null = TRUE;
next_null = FALSE;
}
argc--;argv++;
km->action.statement = *argv;
debuglog("defining key %s, action %s\r\n",
km->keys,
km->action.statement?(dprintify(km->action.statement))
:interpreter_cmd);
if (dash_input_count == 0) dash_input_count = 1;
}
if (!input_user->output) {
struct output *o = new(struct output);
if (master_string == 0) {
if (0 == exp_update_master(interp,&master,1,1)) {
return(TCL_ERROR);
}
o->i_list = exp_new_i_simple(master,EXP_TEMPORARY);
} else {
o->i_list = exp_new_i_complex(interp,master_string,
EXP_TEMPORARY,inter_updateproc);
}
#if 0
if (master == EXP_SPAWN_ID_BAD) {
if (0 == exp_update_master(interp,&master,1,1)) {
return(TCL_ERROR);
}
}
o->i_list = exp_new_i_simple(master,EXP_TEMPORARY);
#endif
o->next = 0;
o->action_eof = &action_eof;
input_user->output = o;
}
if (!input_default->output) {
struct output *o = new(struct output);
o->i_list = exp_new_i_simple(1,EXP_TEMPORARY);
o->next = 0;
o->action_eof = &action_eof;
input_default->output = o;
}
if (replace_user_by_process) {
exp_free_i(interp,input_user->i_list, inter_updateproc);
exp_free_i(interp,input_default->output->i_list,inter_updateproc);
input_user->i_list = exp_new_i_complex(interp,
replace_user_by_process,
EXP_TEMPORARY,inter_updateproc);
input_default->output->i_list = exp_new_i_complex(interp,
replace_user_by_process,
EXP_TEMPORARY,inter_updateproc);
}
if (input_default->i_list->direct == EXP_INDIRECT) {
exp_i_update(interp,input_default->i_list);
}
if (input_default->i_list->fd_list
&& (input_default->i_list->fd_list->fd == EXP_SPAWN_ID_BAD)) {
if (master_string == 0) {
if (0 == exp_update_master(interp,&master,1,1)) {
return(TCL_ERROR);
}
input_default->i_list->fd_list->fd = master;
} else {
exp_free_i(interp,input_default->i_list,inter_updateproc);
input_default->i_list = exp_new_i_complex(interp,master_string,
EXP_TEMPORARY,inter_updateproc);
}
#if 0
if (master == EXP_SPAWN_ID_BAD) {
if (0 == exp_update_master(interp,&master,1,1)) {
return(TCL_ERROR);
}
}
input_default->i_list->fd_list->fd = master;
#endif
}
if (input_user->i_list->direct == EXP_INDIRECT) {
exp_i_update(interp,input_user->i_list);
}
if (input_user->i_list->fd_list && input_default->i_list->fd_list
&& (input_user->i_list->fd_list->fd == input_default->i_list->fd_list->fd)) {
exp_error(interp,"cannot interact with self - set spawn_id to a spawned process");
return(TCL_ERROR);
}
fd_list = 0;
fd_to_input = 0;
status = update_interact_fds(interp,&input_count,&fd_to_input,&fd_list,input_base,1,&configure_count,&real_tty);
if (status == TCL_ERROR) finish(TCL_ERROR);
if (real_tty) {
tty_changed = exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
}
for (inp = input_base,i=0;inp;inp=inp->next,i++) {
inp->timeout_remaining = inp->timeout_nominal;
}
key = expect_key++;
configure_count = exp_configure_count;
#ifndef SIMPLE_EVENT
for (;;) {
int te;
struct exp_f *u;
int rc;
int cc;
int m;
int m_out;
struct action *action = 0;
time_t previous_time;
time_t current_time;
int match_length, skip;
int change;
int attempt_match = TRUE;
struct input *soonest_input;
int print;
int oldprinted;
int timeout;
if (timeout_simple) {
timeout = default_timeout;
} else {
timeout = arbitrary_timeout;
for (inp=input_base;inp;inp=inp->next) {
if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
(inp->timeout_remaining <= timeout)) {
soonest_input = inp;
timeout = inp->timeout_remaining;
}
}
time(&previous_time);
}
if (configure_count != exp_configure_count) {
status = update_interact_fds(interp,&input_count,
&fd_to_input,&fd_list,input_base,1,
&configure_count,&real_tty);
if (status) finish(status);
}
rc = exp_get_next_event(interp,fd_list,input_count,&m,timeout,key);
if (rc == EXP_TCLERROR) return(TCL_ERROR);
if (rc == EXP_RECONFIGURE) continue;
if (rc == EXP_TIMEOUT) {
if (timeout_simple) {
action = &action_timeout;
goto got_action;
} else {
action = soonest_input->action_timeout;
m = soonest_input->i_list->fd_list->fd;
}
}
if (!timeout_simple) {
int time_diff;
time(¤t_time);
time_diff = current_time - previous_time;
for (inp=input_base;inp;inp=inp->next) {
if (inp->timeout_remaining != EXP_TIME_INFINITY) {
inp->timeout_remaining -= time_diff;
if (inp->timeout_remaining < 0)
inp->timeout_remaining = 0;
}
}
}
inp = fd_to_input[m];
u = exp_fs+m;
inp->timeout_remaining = inp->timeout_nominal;
switch (rc) {
case EXP_DATA_NEW:
if (u->size == u->msize) {
debuglog("WARNING: interact buffer is full, probably because your\r\n");
debuglog("patterns have matched all of it but require more chars\r\n");
debuglog("in order to complete the match.\r\n");
debuglog("Dumping first half of buffer in order to continue\r\n");
debuglog("Recommend you enlarge the buffer or fix your patterns.\r\n");
exp_buffer_shuffle(interp,u,0,INTER_OUT,"interact");
}
cc = read(m, u->buffer + u->size,
u->msize - u->size);
if (cc > 0) {
u->key = key;
u->size += cc;
u->buffer[u->size] = '\0';
if (u->parity == 0) {
char *p = u->buffer + u->size - 1;
int count = cc;
while (count--) {
*p-- &= 0x7f;
}
}
if (debugfile || is_debugging) {
debuglog("spawn id %d sent <%s>\r\n",m,
exp_printify(u->buffer + u->size - cc));
}
break;
}
rc = EXP_EOF;
case EXP_EOF:
action = inp->action_eof;
attempt_match = FALSE;
skip = u->size;
debuglog("interact: received eof from spawn_id %d\r\n",m);
need_to_close_master = TRUE;
#if EOF_SO
skip = 0;
exp_close(interp,m);
#endif
break;
case EXP_DATA_OLD:
cc = 0;
break;
case EXP_TIMEOUT:
action = inp->action_timeout;
attempt_match = FALSE;
skip = u->size;
break;
}
km = 0;
if (attempt_match) {
rc = in_keymap(u->buffer,u->size,inp->keymap,
&km,&match_length,&skip,u->rm_nulls);
} else {
attempt_match = TRUE;
}
if (km && km->re) {
#define out(var,val) debuglog("expect: set %s(%s) \"%s\"\r\n",INTER_OUT,var, \
dprintify(val)); \
Tcl_SetVar2(interp,INTER_OUT,var,val,0);
char name[20], value[20];
regexp *re = km->re;
char match_char;
for (i=0;i<NSUBEXP;i++) {
int offset;
if (re->startp[i] == 0) continue;
if (km->indices) {
sprintf(name,"%d,start",i);
offset = re->startp[i]-u->buffer;
sprintf(value,"%d",offset);
out(name,value);
sprintf(name,"%d,end",i);
sprintf(value,"%d",re->endp[i]-u->buffer-1);
out(name,value);
}
sprintf(name,"%d,string",i);
match_char = *re->endp[i];
*re->endp[i] = 0;
out(name,re->startp[i]);
*re->endp[i] = match_char;
}
}
if (km && km->writethru) {
print = skip + match_length;
} else print = skip;
if (km && km->echo) {
int seen;
m_out = (m == 0)?1:m;
seen = u->printed + u->echoed;
if (skip >= seen) {
write(m_out,u->buffer+skip,match_length);
} else if ((match_length + skip - seen) > 0) {
write(m_out,u->buffer+seen,match_length+skip-seen);
}
u->echoed = match_length + skip - u->printed;
}
oldprinted = u->printed;
if (print > u->printed) {
int wc;
for (outp = inp->output;outp;outp=outp->next) {
struct exp_fd_list *fdp;
for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
int od;
if (logfile && real_tty_output(fdp->fd)) {
fwrite(u->buffer+u->printed,1,
print - u->printed,logfile);
}
od = fdp->fd;
od = (exp_fs[od].tcl_handle?exp_fs[od].tcl_output:od);
wc = write(od,u->buffer+u->printed,
print - u->printed);
if (wc <= 0) {
debuglog("interact: write on spawn id %d failed (%s)\r\n",fdp->fd,Tcl_PosixError(interp));
action = outp->action_eof;
change = (action && action->tty_reset);
if (change && tty_changed)
exp_tty_set(interp,&tty_old,was_raw,was_echo);
te = inter_eval(interp,action,m);
if (change && real_tty) tty_changed =
exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
switch (te) {
case TCL_BREAK:
case TCL_CONTINUE:
finish(te);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
case TCL_OK:
action = 0;
continue;
default:
finish(te);
}
}
}
}
u->printed = print;
}
if (rc == EXP_MATCH) {
action = &km->action;
skip += match_length;
u->size -= skip;
if (u->size) {
memcpy(u->buffer, u->buffer + skip, u->size);
exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
}
} else {
if (skip) {
u->size -= skip;
memcpy(u->buffer, u->buffer + skip, u->size);
exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
}
}
#if EOF_SO
if (rc != EXP_EOF) {
u->buffer[u->size] = '\0';
u->lower [u->size] = '\0';
}
#else
u->buffer[u->size] = '\0';
u->lower [u->size] = '\0';
#endif
u->printed -= skip;
if (u->printed < 0) u->printed = 0;
u->force_read = (rc == EXP_CANMATCH);
if (rc != EXP_CANMATCH) {
if (skip >= oldprinted + u->echoed) u->echoed = 0;
}
if (rc == EXP_EOF) {
exp_close(interp,m);
need_to_close_master = FALSE;
}
if (action) {
got_action:
change = (action && action->tty_reset);
if (change && tty_changed)
exp_tty_set(interp,&tty_old,was_raw,was_echo);
te = inter_eval(interp,action,m);
if (change && real_tty) tty_changed =
exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
switch (te) {
case TCL_BREAK:
case TCL_CONTINUE:
finish(te);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
case TCL_OK:
action = 0;
continue;
default:
finish(te);
}
}
}
#else
{
int te;
struct exp_f *u;
int rc;
int cc;
int m;
struct action *action = 0;
time_t previous_time;
time_t current_time;
int match_length, skip;
int change;
int attempt_match = TRUE;
struct input *soonest_input;
int print;
int oldprinted;
int timeout;
if (-1 == (pid = fork())) {
exp_error(interp,"fork: %s",Tcl_PosixError(interp));
finish(TCL_ERROR);
}
if (pid == 0) {
exp_close(interp,0);
m = fd_list[1];
input_count = 1;
while (1) {
if (timeout_simple) {
timeout = default_timeout;
} else {
timeout = arbitrary_timeout;
for (inp=input_base;inp;inp=inp->next) {
if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
(inp->timeout_remaining < timeout))
soonest_input = inp;
timeout = inp->timeout_remaining;
}
time(&previous_time);
}
rc = exp_get_next_event(interp,fd_list+1,input_count,&m,timeout,key);
if (!timeout_simple) {
int time_diff;
time(¤t_time);
time_diff = current_time - previous_time;
for (inp=input_base;inp;inp=inp->next) {
if (inp->timeout_remaining != EXP_TIME_INFINITY) {
inp->timeout_remaining -= time_diff;
if (inp->timeout_remaining < 0)
inp->timeout_remaining = 0;
}
}
}
inp = fd_to_input[m];
u = exp_fs+m;
switch (rc) {
case EXP_DATA_NEW:
cc = read(m, u->buffer + u->size,
u->msize - u->size);
if (cc > 0) {
u->key = key;
u->size += cc;
u->buffer[u->size] = '\0';
if (u->parity == 0) {
char *p = u->buffer + u->size - 1;
int count = cc;
while (count--) {
*p-- &= 0x7f;
}
}
if (debugfile || is_debugging) {
debuglog("spawn id %d sent <%s>\r\n",m,
exp_printify(u->buffer + u->size - cc));
}
break;
}
case EXP_EOF:
action = inp->action_eof;
attempt_match = FALSE;
skip = u->size;
rc = EXP_EOF;
debuglog("interact: child received eof from spawn_id %d\r\n",m);
exp_close(interp,m);
break;
case EXP_DATA_OLD:
cc = 0;
break;
}
km = 0;
if (attempt_match) {
rc = in_keymap(u->buffer,u->size,inp->keymap,
&km,&match_length,&skip);
} else {
attempt_match = TRUE;
}
if (km && km->re) {
#define INTER_OUT "interact_out"
#define out(i,val) debuglog("expect: set %s(%s) \"%s\"\r\n",INTER_OUT,i, \
dprintify(val)); \
Tcl_SetVar2(interp,INTER_OUT,i,val,0);
char name[20], value[20];
regexp *re = km->re;
char match_char;
for (i=0;i<NSUBEXP;i++) {
int offset;
if (re->startp[i] == 0) continue;
if (km->indices) {
sprintf(name,"%d,start",i);
offset = re->startp[i]-u->buffer;
sprintf(value,"%d",offset);
out(name,value);
sprintf(name,"%d,end",i);
sprintf(value,"%d",re->endp[i]-u->buffer-1);
out(name,value);
}
sprintf(name,"%d,string",i);
match_char = *re->endp[i];
*re->endp[i] = 0;
out(name,re->startp[i]);
*re->endp[i] = match_char;
}
}
if (km && km->writethru) {
print = skip + match_length;
} else print = skip;
if (km && km->echo) {
int seen;
if (m == 0) m = 1;
seen = u->printed + u->echoed;
if (skip >= seen) {
write(m,u->buffer+skip,match_length);
} else if ((match_length + skip - seen) > 0) {
write(m,u->buffer+seen,match_length+skip-seen);
}
u->echoed = match_length + skip - u->printed;
}
oldprinted = u->printed;
if (print > u->printed) {
int wc;
for (outp = inp->output;outp;outp=outp->next) {
struct exp_fd_list *fdp;
for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
int od;
if (logfile && real_tty_output(fdp->fd)) {
fwrite(u->buffer+u->printed,1,
print - u->printed,logfile);
}
od = fdp->fd;
od = (exp_fs[od].tcl_handle?exp_fs[od].tcl_output:od);
wc = write(od,u->buffer+u->printed,
print - u->printed);
if (wc <= 0) {
debuglog("interact: write on spawn id %d failed (%s)\r\n",fdp->fd,Tcl_PosixError(interp));
action = outp->action_eof;
te = inter_eval(interp,action,m);
switch (te) {
case TCL_BREAK:
case TCL_CONTINUE:
finish(te);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
case TCL_OK:
action = 0;
continue;
default:
finish(te);
}
}
}
}
u->printed = print;
}
if (rc == EXP_MATCH) {
action = &km->action;
skip += match_length;
u->size -= skip;
if (u->size)
memcpy(u->buffer, u->buffer + skip, u->size);
exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
} else {
if (skip) {
u->size -= skip;
memcpy(u->buffer, u->buffer + skip, u->size);
exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
}
}
if (rc != EXP_EOF) {
u->buffer[u->size] = '\0';
u->lower [u->size] = '\0';
}
u->printed -= skip;
if (u->printed < 0) u->printed = 0;
u->force_read = (rc == EXP_CANMATCH);
if (rc != EXP_CANMATCH) {
if (skip >= oldprinted + u->echoed) u->echoed = 0;
}
if (action) {
te = inter_eval(interp,action,m);
switch (te) {
case TCL_BREAK:
case TCL_CONTINUE:
finish(te);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
case TCL_OK:
action = 0;
continue;
default:
finish(te);
}
}
}
} else {
#include <signal.h>
#if defined(SIGCLD) && !defined(SIGCHLD)
#define SIGCHLD SIGCLD
#endif
debuglog("fork = %d\r\n",pid);
signal(SIGCHLD,sigchld_handler);
m = fd_list[0];
input_count = 1;
while (1) {
if (timeout_simple) {
timeout = default_timeout;
} else {
timeout = arbitrary_timeout;
for (inp=input_base;inp;inp=inp->next) {
if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
(inp->timeout_remaining < timeout))
soonest_input = inp;
timeout = inp->timeout_remaining;
}
time(&previous_time);
}
rc = exp_get_next_event(interp,fd_list,input_count,&m,timeout,key);
if (!timeout_simple) {
int time_diff;
time(¤t_time);
time_diff = current_time - previous_time;
for (inp=input_base;inp;inp=inp->next) {
if (inp->timeout_remaining != EXP_TIME_INFINITY) {
inp->timeout_remaining -= time_diff;
if (inp->timeout_remaining < 0)
inp->timeout_remaining = 0;
}
}
}
inp = fd_to_input[m];
u = exp_fs+m;
switch (rc) {
case EXP_DATA_NEW:
cc = i_read(m, u->buffer + u->size,
u->msize - u->size);
if (cc > 0) {
u->key = key;
u->size += cc;
u->buffer[u->size] = '\0';
if (u->parity == 0) {
char *p = u->buffer + u->size - 1;
int count = cc;
while (count--) {
*p-- &= 0x7f;
}
}
if (debugfile || is_debugging) {
debuglog("spawn id %d sent <%s>\r\n",m,
exp_printify(u->buffer + u->size - cc));
}
break;
} else if (cc == EXP_CHILD_EOF) {
action = inp->output->action_eof;
attempt_match = FALSE;
skip = u->size;
rc = EXP_EOF;
debuglog("interact: process died/eof\r\n");
clean_up_after_child(interp,fd_list[1]);
break;
}
case EXP_EOF:
action = inp->action_eof;
attempt_match = FALSE;
skip = u->size;
rc = EXP_EOF;
debuglog("user sent EOF or disappeared\n\n");
break;
case EXP_DATA_OLD:
cc = 0;
break;
}
km = 0;
if (attempt_match) {
rc = in_keymap(u->buffer,u->size,inp->keymap,
&km,&match_length,&skip);
} else {
attempt_match = TRUE;
}
if (km && km->re) {
char name[20], value[20];
regexp *re = km->re;
char match_char;
for (i=0;i<NSUBEXP;i++) {
int offset;
if (re->startp[i] == 0) continue;
if (km->indices) {
sprintf(name,"%d,start",i);
offset = re->startp[i]-u->buffer;
sprintf(value,"%d",offset);
out(name,value);
sprintf(name,"%d,end",i);
sprintf(value,"%d",re->endp[i]-u->buffer-1);
out(name,value);
}
sprintf(name,"%d,string",i);
match_char = *re->endp[i];
*re->endp[i] = 0;
out(name,re->startp[i]);
*re->endp[i] = match_char;
}
}
if (km && km->writethru) {
print = skip + match_length;
} else print = skip;
if (km && km->echo) {
int seen;
if (m == 0) m = 1;
seen = u->printed + u->echoed;
if (skip >= seen) {
write(m,u->buffer+skip,match_length);
} else if ((match_length + skip - seen) > 0) {
write(m,u->buffer+seen,match_length+skip-seen);
}
u->echoed = match_length + skip - u->printed;
}
oldprinted = u->printed;
if (print > u->printed) {
int wc;
for (outp = inp->output;outp;outp=outp->next) {
struct exp_fd_list *fdp;
for (fdp = outp->i_list->fd_list;fdp;fdp=fdp->next) {
int od;
if (logfile && real_tty_output(fdp->fd)) {
fwrite(u->buffer+u->printed,1,
print - u->printed,logfile);
}
od = fdp->fd;
od = (exp_fs[od].tcl_handle?exp_fs[od].tcl_output:od);
wc = write(od,u->buffer+u->printed,
print - u->printed);
if (wc <= 0) {
debuglog("interact: write on spawn id %d failed (%s)\r\n",fdp->fd,Tcl_PosixError(interp));
clean_up_after_child(interp,fdp->fd);
action = outp->action_eof;
change = (action && action->tty_reset);
if (change && tty_changed)
exp_tty_set(interp,&tty_old,was_raw,was_echo);
te = inter_eval(interp,action,m);
if (change && real_tty) tty_changed =
exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
switch (te) {
case TCL_BREAK:
case TCL_CONTINUE:
finish(te);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
case TCL_OK:
action = 0;
continue;
default:
finish(te);
}
}
}
}
u->printed = print;
}
if (rc == EXP_MATCH) {
action = &km->action;
skip += match_length;
u->size -= skip;
if (u->size)
memcpy(u->buffer, u->buffer + skip, u->size);
exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
} else {
if (skip) {
u->size -= skip;
memcpy(u->buffer, u->buffer + skip, u->size);
exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
}
}
if (rc != EXP_EOF) {
u->buffer[u->size] = '\0';
u->lower [u->size] = '\0';
}
u->printed -= skip;
if (u->printed < 0) u->printed = 0;
u->force_read = (rc == EXP_CANMATCH);
if (rc != EXP_CANMATCH) {
if (skip >= oldprinted + u->echoed) u->echoed = 0;
}
if (action) {
change = (action && action->tty_reset);
if (change && tty_changed)
exp_tty_set(interp,&tty_old,was_raw,was_echo);
te = inter_eval(interp,action,m);
if (change && real_tty) tty_changed =
exp_tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
switch (te) {
case TCL_BREAK:
case TCL_CONTINUE:
finish(te);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
case TCL_OK:
action = 0;
continue;
default:
finish(te);
}
}
}
}
}
#endif
done:
#ifdef SIMPLE_EVENT
if (pid == 0) {
exit(SPAWNED_PROCESS_DIED);
}
#endif
if (need_to_close_master) exp_close(interp,master);
if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo);
if (oldargv) ckfree((char *)argv);
if (fd_list) ckfree((char *)fd_list);
if (fd_to_input) ckfree((char *)fd_to_input);
free_input(interp,input_base);
free_action(action_base);
return(status);
}
static int
inter_eval(interp,action,spawn_id)
Tcl_Interp *interp;
struct action *action;
int spawn_id;
{
int status;
char value[20];
if (action->timestamp) {
time_t current_time;
time(¤t_time);
exp_timestamp(interp,¤t_time,INTER_OUT);
}
if (action->iwrite) {
sprintf(value,"%d",spawn_id);
out("spawn_id",value);
}
if (action->statement) {
status = Tcl_Eval(interp,action->statement);
} else {
exp_nflog("\r\n",1);
status = exp_interpreter(interp);
}
return status;
}
static void
free_keymap(km)
struct keymap *km;
{
if (km == 0) return;
free_keymap(km->next);
ckfree((char *)km);
}
static void
free_action(a)
struct action *a;
{
struct action *next;
while (a) {
next = a->next;
ckfree((char *)a);
a = next;
}
}
static void
free_input(interp,i)
Tcl_Interp *interp;
struct input *i;
{
if (i == 0) return;
free_input(interp,i->next);
exp_free_i(interp,i->i_list,inter_updateproc);
free_output(interp,i->output);
free_keymap(i->keymap);
ckfree((char *)i);
}
static struct action *
new_action(base)
struct action **base;
{
struct action *o = new(struct action);
o->next = *base;
*base = o;
return o;
}
static void
free_output(interp,o)
Tcl_Interp *interp;
struct output *o;
{
if (o == 0) return;
free_output(interp,o->next);
exp_free_i(interp,o->i_list,inter_updateproc);
ckfree((char *)o);
}
static struct exp_cmd_data cmd_data[] = {
{"interact", exp_proc(Exp_InteractCmd), 0, 0},
{0}};
void
exp_init_interact_cmds(interp)
Tcl_Interp *interp;
{
exp_create_commands(interp,cmd_data);
}