#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#if 0
#include <setjmp.h>
#endif
#include "expect_cf.h"
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "tcl.h"
#include "string.h"
#include "tclRegexp.h"
#include "exp_rename.h"
#include "exp_prog.h"
#include "exp_command.h"
#include "exp_log.h"
#include "exp_event.h"
#include "exp_tty.h"
#include "exp_tstamp.h"
#ifdef TCL_DEBUGGER
#include "Dbg.h"
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
#define SIMPLE_EVENT
#endif
int exp_default_match_max = 2000;
#define INIT_EXPECT_TIMEOUT_LIT "10"
#define INIT_EXPECT_TIMEOUT 10
int exp_default_parity = TRUE;
int exp_default_rm_nulls = TRUE;
#define EXPECT_TIMEOUT "timeout"
#define EXPECT_OUT "expect_out"
struct ecase {
struct exp_i *i_list;
char *pat;
char *body;
#define PAT_EOF 1
#define PAT_TIMEOUT 2
#define PAT_DEFAULT 3
#define PAT_FULLBUFFER 4
#define PAT_GLOB 5
#define PAT_RE 6
#define PAT_EXACT 7
#define PAT_NULL 8
#define PAT_TYPES 9
int use;
int simple_start;
int transfer;
int indices;
int iread;
int timestamp;
#define CASE_UNKNOWN 0
#define CASE_NORM 1
#define CASE_LOWER 2
int Case;
regexp *re;
};
char *pattern_style[PAT_TYPES];
struct exp_cases_descriptor {
int count;
struct ecase **cases;
};
static
struct exp_cmd_descriptor {
int cmdtype;
int duration;
int timeout_specified_by_flag;
int timeout;
struct exp_cases_descriptor ecd;
struct exp_i *i_list;
} exp_cmds[4];
static void
exp_cmd_init(cmd,cmdtype,duration)
struct exp_cmd_descriptor *cmd;
int duration;
int cmdtype;
{
cmd->duration = duration;
cmd->cmdtype = cmdtype;
cmd->ecd.cases = 0;
cmd->ecd.count = 0;
cmd->i_list = 0;
}
static int i_read_errno;
#if 0
static jmp_buf env;
static int env_valid = FALSE;
#endif
#ifdef SIMPLE_EVENT
static int alarm_fired;
#endif
void exp_background_filehandlers_run_all();
static char *exp_indirect_update1();
static char *exp_indirect_update2();
static int exp_i_read _ANSI_ARGS_((Tcl_Interp *,int,int,int));
#ifdef SIMPLE_EVENT
static RETSIGTYPE
sigalarm_handler(n)
int n;
{
alarm_fired = TRUE;
#if 0
if (env_valid) longjmp(env,1);
#endif
}
#endif
#if 0
static RETSIGTYPE
sigalarm_handler(n)
int n;
{
#ifdef REARM_SIG
signal(SIGALRM,sigalarm_handler);
#endif
if (env_valid) longjmp(env,1);
}
#endif
#if 0
static RETSIGTYPE
sigint_handler(n)
int n;
{
#ifdef REARM_SIG
signal(SIGINT,sigint_handler);
#endif
#ifdef TCL_DEBUGGER
if (exp_tcl_debugger_available) {
Dbg_On(exp_interp,env_valid);
if (env_valid) longjmp(env,2);
return;
}
#endif
#if 0
if (env_valid) longjmp(env,1);
#endif
exp_exit(exp_interp,0);
}
#endif
static int
rm_nulls(s,c)
char *s;
int c;
{
char *s2 = s;
int count = 0;
int i;
for (i=0;i<c;i++,s++) {
if (0 == *s) {
count++;
continue;
}
if (count) *s2 = *s;
s2++;
}
return(count);
}
static void
free_ecase(interp,ec,free_ilist)
Tcl_Interp *interp;
struct ecase *ec;
int free_ilist;
{
if (ec->re) ckfree((char *)ec->re);
if (ec->i_list->duration == EXP_PERMANENT) {
if (ec->pat) ckfree(ec->pat);
if (ec->body) ckfree(ec->body);
}
if (free_ilist) {
ec->i_list->ecount--;
if (ec->i_list->ecount == 0)
exp_free_i(interp,ec->i_list,exp_indirect_update2);
}
ckfree((char *)ec);
}
static void
free_ecases(interp,eg,free_ilist)
Tcl_Interp *interp;
struct exp_cmd_descriptor *eg;
int free_ilist;
{
int i;
if (!eg->ecd.cases) return;
for (i=0;i<eg->ecd.count;i++) {
free_ecase(interp,eg->ecd.cases[i],free_ilist);
}
ckfree((char *)eg->ecd.cases);
eg->ecd.cases = 0;
eg->ecd.count = 0;
}
#if 0
static char *exp_strdup(s)
char *s;
{
char *news = ckalloc(strlen(s) + 1);
strcpy(news,s);
return(news);
}
#endif
static void
save_str(lhs,rhs,nosave)
char **lhs;
char *rhs;
int nosave;
{
if (nosave || (rhs == 0)) {
*lhs = rhs;
} else {
*lhs = ckalloc(strlen(rhs) + 1);
strcpy(*lhs,rhs);
}
}
int
exp_one_arg_braced(p)
char *p;
{
int seen_nl = FALSE;
for (;*p;p++) {
if (*p == '\n') {
seen_nl = TRUE;
continue;
}
if (!isspace(*p)) {
return(seen_nl);
}
}
return FALSE;
}
int
exp_eval_with_one_arg(clientData,interp,argv)
ClientData clientData;
Tcl_Interp *interp;
char **argv;
{
char *buf;
int rc;
char *a;
buf = ckalloc(strlen(argv[0]) + strlen(argv[1]) + 11);
sprintf(buf,"%s -nobrace %s",argv[0],argv[1]);
for (a=buf;*a;) {
extern char *TclWordEnd();
for (;isspace(*a);a++) {
if (*a == '\n') *a = ' ';
}
#if TCL_MAJOR_VERSION < 8
a = TclWordEnd(a,0,(int *)0)+1;
#else
a = TclWordEnd(a,&a[strlen(a)],0,(int *)0)+1;
#endif
}
rc = Tcl_Eval(interp,buf);
ckfree(buf);
return(rc);
}
static void
ecase_clear(ec)
struct ecase *ec;
{
ec->i_list = 0;
ec->pat = 0;
ec->body = 0;
ec->transfer = TRUE;
ec->indices = FALSE;
ec->iread = FALSE;
ec->timestamp = FALSE;
ec->re = 0;
ec->Case = CASE_NORM;
ec->use = PAT_GLOB;
}
static struct ecase *
ecase_new()
{
struct ecase *ec = (struct ecase *)ckalloc(sizeof(struct ecase));
ecase_clear(ec);
return ec;
}
static int
parse_expect_args(interp,eg,default_spawn_id,argc,argv)
Tcl_Interp *interp;
struct exp_cmd_descriptor *eg;
int default_spawn_id;
int argc;
char **argv;
{
int i;
char *arg;
struct ecase ec;
argv++;
argc--;
eg->timeout_specified_by_flag = FALSE;
ecase_clear(&ec);
eg->ecd.cases = (struct ecase **)ckalloc(
sizeof(struct ecase *) * (1+(argc/2)));
eg->ecd.count = 0;
for (i = 0;i<argc;i++) {
arg = argv[i];
if (exp_flageq("timeout",arg,7)) {
ec.use = PAT_TIMEOUT;
} else if (exp_flageq("eof",arg,3)) {
ec.use = PAT_EOF;
} else if (exp_flageq("full_buffer",arg,11)) {
ec.use = PAT_FULLBUFFER;
} else if (exp_flageq("default",arg,7)) {
ec.use = PAT_DEFAULT;
} else if (exp_flageq("null",arg,4)) {
ec.use = PAT_NULL;
} else if (arg[0] == '-') {
arg++;
if (exp_flageq1('-',arg)
|| exp_flageq("glob",arg,2)) {
i++;
} else if (exp_flageq("regexp",arg,2)) {
i++;
ec.use = PAT_RE;
TclRegError((char *)0);
if (!(ec.re = TclRegComp(argv[i]))) {
exp_error(interp,"bad regular expression: %s",
TclGetRegError());
goto error;
}
} else if (exp_flageq("exact",arg,2)) {
i++;
ec.use = PAT_EXACT;
} else if (exp_flageq("notransfer",arg,1)) {
ec.transfer = 0;
continue;
} else if (exp_flageq("nocase",arg,3)) {
ec.Case = CASE_LOWER;
continue;
} else if (exp_flageq1('i',arg)) {
i++;
if (i>=argc) {
exp_error(interp,"-i requires following spawn_id");
goto error;
}
ec.i_list = exp_new_i_complex(interp,argv[i],
eg->duration,exp_indirect_update2);
ec.i_list->cmdtype = eg->cmdtype;
ec.i_list->next = eg->i_list;
eg->i_list = ec.i_list;
continue;
} else if (exp_flageq("indices",arg,2)) {
ec.indices = TRUE;
continue;
} else if (exp_flageq("iwrite",arg,2)) {
continue;
} else if (exp_flageq("iread",arg,2)) {
ec.iread = TRUE;
continue;
} else if (exp_flageq("timestamp",arg,2)) {
ec.timestamp = TRUE;
continue;
} else if (exp_flageq("timeout",arg,2)) {
i++;
if (i>=argc) {
exp_error(interp,"-timeout requires following # of seconds");
goto error;
}
eg->timeout = atoi(argv[i]);
eg->timeout_specified_by_flag = TRUE;
continue;
} else if (exp_flageq("nobrace",arg,7)) {
continue;
} else {
exp_error(interp,"usage: unrecognized flag <%s>",arg);
goto error;
}
}
if (!ec.i_list) {
if (!eg->i_list) {
if (default_spawn_id != EXP_SPAWN_ID_BAD) {
eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
} else {
(void) exp_update_master(interp,&default_spawn_id,0,0);
eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
}
}
ec.i_list = eg->i_list;
}
ec.i_list->ecount++;
save_str(&ec.pat,argv[i],eg->duration == EXP_TEMPORARY);
save_str(&ec.body,argv[i+1],eg->duration == EXP_TEMPORARY);
i++;
*(eg->ecd.cases[eg->ecd.count] = ecase_new()) = ec;
ecase_clear(&ec);
eg->ecd.count++;
}
if (eg->i_list == 0) {
if (default_spawn_id != EXP_SPAWN_ID_BAD) {
eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
} else {
(void) exp_update_master(interp,&default_spawn_id,0,0);
eg->i_list = exp_new_i_simple(default_spawn_id,eg->duration);
}
}
return(TCL_OK);
error:
free_ecases(interp,eg,0);
if (ec.re) ckfree((char *)ec.re);
if (eg->duration == EXP_PERMANENT) {
if (ec.pat) ckfree(ec.pat);
if (ec.body) ckfree(ec.body);
}
if (eg->i_list)
exp_free_i(interp,eg->i_list,exp_indirect_update2);
return(TCL_ERROR);
}
#define EXP_IS_DEFAULT(x) ((x) == EXP_TIMEOUT || (x) == EXP_EOF)
static char yes[] = "yes\r\n";
static char no[] = "no\r\n";
struct eval_out {
struct ecase *e;
struct exp_f *f;
char *buffer;
int match;
};
static int
eval_case_string(interp,e,m,o,last_f,last_case,suffix)
Tcl_Interp *interp;
struct ecase *e;
int m;
struct eval_out *o;
struct exp_f **last_f;
int *last_case;
char *suffix;
{
struct exp_f *f = exp_fs + m;
char *buffer;
buffer = ((e->Case == CASE_NORM)?f->buffer:f->lower);
if ((f != *last_f) || e->Case != *last_case) {
debuglog("\r\nexpect%s: does \"%s\" (spawn_id %d) match %s ",
suffix,
dprintify(buffer),f-exp_fs,
pattern_style[e->use]);
*last_f = f;
*last_case = e->Case;
}
if (e->use == PAT_RE) {
debuglog("\"%s\"? ",dprintify(e->pat));
TclRegError((char *)0);
if (buffer && TclRegExec(e->re,buffer,buffer)) {
o->e = e;
o->match = e->re->endp[0]-buffer;
o->buffer = buffer;
o->f = f;
debuglog(yes);
return(EXP_MATCH);
} else {
debuglog(no);
if (TclGetRegError()) {
exp_error(interp,"-re failed: %s",TclGetRegError());
return(EXP_TCLERROR);
}
}
} else if (e->use == PAT_GLOB) {
int match;
debuglog("\"%s\"? ",dprintify(e->pat));
if (buffer && (-1 != (match = Exp_StringMatch(
buffer,e->pat,&e->simple_start)))) {
o->e = e;
o->match = match;
o->buffer = buffer;
o->f = f;
debuglog(yes);
return(EXP_MATCH);
} else debuglog(no);
} else if (e->use == PAT_EXACT) {
char *p = strstr(buffer,e->pat);
debuglog("\"%s\"? ",dprintify(e->pat));
if (p) {
e->simple_start = p - buffer;
o->e = e;
o->match = strlen(e->pat);
o->buffer = buffer;
o->f = f;
debuglog(yes);
return(EXP_MATCH);
} else debuglog(no);
} else if (e->use == PAT_NULL) {
int i = 0;
debuglog("null? ");
for (;i<f->size;i++) {
if (buffer[i] == 0) {
o->e = e;
o->match = i+1;
o->buffer = buffer;
o->f = f;
debuglog(yes);
return EXP_MATCH;
}
}
debuglog(no);
} else if ((f->size == f->msize) && (f->size > 0)) {
debuglog("%s? ",e->pat);
o->e = e;
o->match = f->umsize;
o->buffer = f->buffer;
o->f = f;
debuglog(yes);
return(EXP_FULLBUFFER);
}
return(EXP_NOMATCH);
}
static int
eval_cases(interp,eg,m,o,last_f,last_case,status,masters,mcount,suffix)
Tcl_Interp *interp;
struct exp_cmd_descriptor *eg;
int m;
struct eval_out *o;
struct exp_f **last_f;
int *last_case;
int status;
int *masters;
int mcount;
char *suffix;
{
int i;
int em;
struct ecase *e;
if (o->e || status == EXP_TCLERROR || eg->ecd.count == 0) return(status);
if (status == EXP_TIMEOUT) {
for (i=0;i<eg->ecd.count;i++) {
e = eg->ecd.cases[i];
if (e->use == PAT_TIMEOUT || e->use == PAT_DEFAULT) {
o->e = e;
break;
}
}
return(status);
} else if (status == EXP_EOF) {
for (i=0;i<eg->ecd.count;i++) {
e = eg->ecd.cases[i];
if (e->use == PAT_EOF || e->use == PAT_DEFAULT) {
struct exp_fd_list *fdl;
for (fdl=e->i_list->fd_list; fdl ;fdl=fdl->next) {
em = fdl->fd;
if (em == EXP_SPAWN_ID_ANY || em == m) {
o->e = e;
return(status);
}
}
}
}
return(status);
}
for (i=0;i<eg->ecd.count;i++) {
struct exp_fd_list *fdl;
int j;
e = eg->ecd.cases[i];
if (e->use == PAT_TIMEOUT ||
e->use == PAT_DEFAULT ||
e->use == PAT_EOF) continue;
for (fdl = e->i_list->fd_list; fdl; fdl = fdl->next) {
em = fdl->fd;
if (em == EXP_SPAWN_ID_ANY) {
for (j=0;j<mcount;j++) {
status = eval_case_string(interp,e,masters[j],o,last_f,last_case,suffix);
if (status != EXP_NOMATCH) return(status);
}
} else {
if (em != m) continue;
status = eval_case_string(interp,e,m,o,last_f,last_case,suffix);
if (status != EXP_NOMATCH) return(status);
}
}
}
return(EXP_NOMATCH);
}
static void
ecases_remove_by_expi(interp,ecmd,exp_i)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
struct exp_i *exp_i;
{
int i;
for (i=0;i<ecmd->ecd.count;) {
struct ecase *e = ecmd->ecd.cases[i];
if (e->i_list == exp_i) {
free_ecase(interp,e,0);
if (i+1 != ecmd->ecd.count) {
memcpy(&ecmd->ecd.cases[i],
&ecmd->ecd.cases[i+1],
((ecmd->ecd.count - i) - 1) *
sizeof(struct exp_cmd_descriptor *));
}
ecmd->ecd.count--;
if (0 == ecmd->ecd.count) {
ckfree((char *)ecmd->ecd.cases);
ecmd->ecd.cases = 0;
}
} else {
i++;
}
}
}
static void
exp_i_remove(interp,ei,exp_i)
Tcl_Interp *interp;
struct exp_i **ei;
struct exp_i *exp_i;
{
for (;*ei; ei = &(*ei)->next) {
if (*ei == exp_i) {
*ei = exp_i->next;
exp_i->next = 0;
exp_free_i(interp,exp_i,exp_indirect_update2);
break;
}
}
}
static void
exp_i_remove_with_ecases(interp,ecmd,exp_i)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
struct exp_i *exp_i;
{
ecases_remove_by_expi(interp,ecmd,exp_i);
exp_i_remove(interp,&ecmd->i_list,exp_i);
}
static void
ecmd_remove_fd(interp,ecmd,m,direct)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
int m;
int direct;
{
struct exp_i *exp_i, *next;
struct exp_fd_list **fdl;
for (exp_i=ecmd->i_list;exp_i;exp_i=next) {
next = exp_i->next;
if (!(direct & exp_i->direct)) continue;
for (fdl = &exp_i->fd_list;*fdl;) {
if (m == ((*fdl)->fd)) {
struct exp_fd_list *tmp = *fdl;
*fdl = (*fdl)->next;
exp_free_fd_single(tmp);
if ((ecmd->cmdtype == EXP_CMD_BG) && (m != EXP_SPAWN_ID_ANY)) {
exp_fs[m].bg_ecount--;
if (exp_fs[m].bg_ecount == 0) {
exp_disarm_background_filehandler(m);
exp_fs[m].bg_interp = 0;
}
}
continue;
}
fdl = &(*fdl)->next;
}
if (exp_i->direct == EXP_DIRECT && !exp_i->fd_list) {
exp_i_remove_with_ecases(interp,ecmd,exp_i);
}
}
}
void
exp_ecmd_remove_fd_direct_and_indirect(interp,m)
Tcl_Interp *interp;
int m;
{
ecmd_remove_fd(interp,&exp_cmds[EXP_CMD_BEFORE],m,EXP_DIRECT|EXP_INDIRECT);
ecmd_remove_fd(interp,&exp_cmds[EXP_CMD_AFTER],m,EXP_DIRECT|EXP_INDIRECT);
ecmd_remove_fd(interp,&exp_cmds[EXP_CMD_BG],m,EXP_DIRECT|EXP_INDIRECT);
exp_disarm_background_filehandler_force(m);
}
static void
fd_list_arm(interp,fdl)
Tcl_Interp *interp;
struct exp_fd_list *fdl;
{
for (;fdl;fdl=fdl->next) {
int m = fdl->fd;
if (m == EXP_SPAWN_ID_ANY) continue;
if (exp_fs[m].bg_ecount == 0) {
exp_arm_background_filehandler(m);
exp_fs[m].bg_interp = interp;
}
exp_fs[m].bg_ecount++;
}
}
static int
exp_i_uses_fd(exp_i,fd)
struct exp_i *exp_i;
int fd;
{
struct exp_fd_list *fdp;
for (fdp = exp_i->fd_list;fdp;fdp=fdp->next) {
if (fdp->fd == fd) return 1;
}
return 0;
}
static void
ecase_append(interp,ec)
Tcl_Interp *interp;
struct ecase *ec;
{
if (!ec->transfer) Tcl_AppendElement(interp,"-notransfer");
if (ec->indices) Tcl_AppendElement(interp,"-indices");
if (!ec->Case) Tcl_AppendElement(interp,"-nocase");
if (ec->re) Tcl_AppendElement(interp,"-re");
else if (ec->use == PAT_GLOB) Tcl_AppendElement(interp,"-gl");
else if (ec->use == PAT_EXACT) Tcl_AppendElement(interp,"-ex");
Tcl_AppendElement(interp,ec->pat);
Tcl_AppendElement(interp,ec->body?ec->body:"");
}
static void
ecase_by_exp_i_append(interp,ecmd,exp_i)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
struct exp_i *exp_i;
{
int i;
for (i=0;i<ecmd->ecd.count;i++) {
if (ecmd->ecd.cases[i]->i_list == exp_i) {
ecase_append(interp,ecmd->ecd.cases[i]);
}
}
}
static void
exp_i_append(interp,exp_i)
Tcl_Interp *interp;
struct exp_i *exp_i;
{
Tcl_AppendElement(interp,"-i");
if (exp_i->direct == EXP_INDIRECT) {
Tcl_AppendElement(interp,exp_i->variable);
} else {
struct exp_fd_list *fdp;
if (exp_i->fd_list->next)
Tcl_AppendResult(interp," {",(char *)0);
for (fdp = exp_i->fd_list;fdp;fdp=fdp->next) {
char buf[10];
sprintf(buf,"%d",fdp->fd);
Tcl_AppendElement(interp,buf);
}
if (exp_i->fd_list->next)
Tcl_AppendResult(interp,"} ",(char *)0);
}
}
#if 0
int
expect_delete(interp,ecmd,argc,argv)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
int argc;
char **argv;
{
while (*argv) {
if (streq(argv[0],"-i") && argv[1]) {
iflag = argv[1];
argc-=2; argv+=2;
} else if (streq(argv[0],"-all")) {
all = TRUE;
argc--; argv++;
} else if (streq(argv[0],"-noindirect")) {
direct &= ~EXP_INDIRECT;
argc--; argv++;
} else {
exp_error(interp,"usage: -delete [-all | -i spawn_id]\n");
return TCL_ERROR;
}
}
if (all) {
free_ecases(interp,ecmd,0);
exp_free_i(interp,ecmd->i_list,exp_indirect_update2);
return TCL_OK;
}
if (!iflag) {
if (0 == exp_update_master(interp,&m,0,0)) {
return TCL_ERROR;
}
} else if (Tcl_GetInt(interp,iflag,&m) != TCL_OK) {
struct exp_i **old_i;
for (old_i=&ecmd->i_list;*old_i;) {
struct exp_i *tmp;
if ((*old_i)->direct == EXP_DIRECT) continue;
if (!streq((*old_i)->variable,iflag)) continue;
ecases_remove_by_expi(interp,ecmd,*old_i);
tmp = *old_i;
*old_i = tmp->next;
tmp->next = 0;
exp_free_i(interp,tmp_i,exp_indirect_update2);
} else {
old_i = &(*old_i)->next;
}
return TCL_OK;
}
for (exp_i=ecmd->i_list;exp_i;exp_i=exp_i->next) {
if (!(direct & exp_i->direct)) continue;
if (!exp_i_uses_fd(exp_i,m)) continue;
ecase_by_exp_i_append(interp,ecmd,exp_i);
}
return TCL_OK;
}
#endif
int
expect_info(interp,ecmd,argc,argv)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
int argc;
char **argv;
{
struct exp_i *exp_i;
int i;
int direct = EXP_DIRECT|EXP_INDIRECT;
char *iflag = 0;
int all = FALSE;
int m;
while (*argv) {
if (streq(argv[0],"-i") && argv[1]) {
iflag = argv[1];
argc-=2; argv+=2;
} else if (streq(argv[0],"-all")) {
all = TRUE;
argc--; argv++;
} else if (streq(argv[0],"-noindirect")) {
direct &= ~EXP_INDIRECT;
argc--; argv++;
} else {
exp_error(interp,"usage: -info [-all | -i spawn_id]\n");
return TCL_ERROR;
}
}
if (all) {
struct exp_i *previous = 0;
for (i=0;i<ecmd->ecd.count;i++) {
if (previous != ecmd->ecd.cases[i]->i_list) {
exp_i_append(interp,ecmd->ecd.cases[i]->i_list);
previous = ecmd->ecd.cases[i]->i_list;
}
ecase_append(interp,ecmd->ecd.cases[i]);
}
return TCL_OK;
}
if (!iflag) {
if (0 == exp_update_master(interp,&m,0,0)) {
return TCL_ERROR;
}
} else if (Tcl_GetInt(interp,iflag,&m) != TCL_OK) {
Tcl_ResetResult(interp);
for (i=0;i<ecmd->ecd.count;i++) {
if (ecmd->ecd.cases[i]->i_list->direct == EXP_INDIRECT &&
streq(ecmd->ecd.cases[i]->i_list->variable,iflag)) {
ecase_append(interp,ecmd->ecd.cases[i]);
}
}
return TCL_OK;
}
for (exp_i=ecmd->i_list;exp_i;exp_i=exp_i->next) {
if (!(direct & exp_i->direct)) continue;
if (!exp_i_uses_fd(exp_i,m)) continue;
ecase_by_exp_i_append(interp,ecmd,exp_i);
}
return TCL_OK;
}
int
Exp_ExpectGlobalCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int result = TCL_OK;
struct exp_i *exp_i, **eip;
struct exp_fd_list *fdl;
struct exp_cmd_descriptor eg;
int count;
struct exp_cmd_descriptor *ecmd = (struct exp_cmd_descriptor *) clientData;
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));
}
if (argc > 1 && (argv[1][0] == '-')) {
if (exp_flageq("info",&argv[1][1],4)) {
return(expect_info(interp,ecmd,argc-2,argv+2));
}
}
exp_cmd_init(&eg,ecmd->cmdtype,EXP_PERMANENT);
if (TCL_ERROR == parse_expect_args(interp,&eg,EXP_SPAWN_ID_BAD,
argc,argv)) {
return TCL_ERROR;
}
for (exp_i=eg.i_list;exp_i;exp_i=exp_i->next) {
if (exp_i->direct == EXP_INDIRECT) continue;
for (fdl=exp_i->fd_list;fdl;fdl=fdl->next) {
int m = fdl->fd;
if (m != EXP_SPAWN_ID_ANY) {
if (!exp_fd2f(interp,m,1,1,"expect")) {
result = TCL_ERROR;
goto cleanup;
}
}
ecmd_remove_fd(interp,ecmd,m,EXP_DIRECT);
}
}
for (exp_i=eg.i_list;exp_i;exp_i=exp_i->next) {
struct exp_i **old_i;
if (exp_i->direct == EXP_DIRECT) continue;
for (old_i = &ecmd->i_list;*old_i;) {
struct exp_i *tmp;
if (((*old_i)->direct == EXP_DIRECT) ||
(!streq((*old_i)->variable,exp_i->variable))) {
old_i = &(*old_i)->next;
continue;
}
ecases_remove_by_expi(interp,ecmd,*old_i);
tmp = *old_i;
*old_i = tmp->next;
tmp->next = 0;
exp_free_i(interp,tmp,exp_indirect_update2);
}
if (exp_i->ecount) {
char *msg = exp_indirect_update1(interp,ecmd,exp_i);
if (msg) {
strcpy(interp->result,msg);
result = TCL_ERROR;
goto indirect_update_abort;
}
}
}
indirect_update_abort:
for (exp_i=eg.i_list;exp_i;) {
struct exp_i *next = exp_i->next;
if (exp_i->ecount == 0) {
exp_i_remove(interp,&eg.i_list,exp_i);
}
exp_i = next;
}
if (result == TCL_ERROR) goto cleanup;
if (ecmd->cmdtype == EXP_CMD_BG) {
for (exp_i=eg.i_list;exp_i;exp_i=exp_i->next) {
if (exp_i->direct == EXP_DIRECT) {
fd_list_arm(interp,exp_i->fd_list);
}
}
}
count = ecmd->ecd.count + eg.ecd.count;
if (eg.ecd.count) {
int start_index;
if (ecmd->ecd.count) {
ecmd->ecd.cases = (struct ecase **)ckrealloc((char *)ecmd->ecd.cases, count * sizeof(struct ecase *));
start_index = ecmd->ecd.count;
} else {
ecmd->ecd.cases = (struct ecase **)ckalloc(eg.ecd.count * sizeof(struct ecase *));
start_index = 0;
}
memcpy(&ecmd->ecd.cases[start_index],eg.ecd.cases,
eg.ecd.count*sizeof(struct ecase *));
ecmd->ecd.count = count;
}
for (eip = &ecmd->i_list;*eip;eip = &(*eip)->next) {
}
*eip = eg.i_list;
cleanup:
if (result == TCL_ERROR) {
for (exp_i=eg.i_list;exp_i;) {
struct exp_i *next = exp_i->next;
exp_i->next = 0;
exp_i = next;
}
free_ecases(interp,&eg,1);
} else {
if (eg.ecd.cases) ckfree((char *)eg.ecd.cases);
}
if (ecmd->cmdtype == EXP_CMD_BG) {
exp_background_filehandlers_run_all();
}
return(result);
}
void
exp_adjust(f)
struct exp_f *f;
{
int new_msize;
new_msize = f->umsize*2 - 1;
if (new_msize != f->msize) {
if (!f->buffer) {
f->buffer = ckalloc((unsigned)new_msize+1);
f->lower = ckalloc((unsigned)new_msize+1);
f->size = 0;
} else {
if (f->size > new_msize) {
memmove(f->buffer,f->buffer+(f->size - new_msize),new_msize);
memmove(f->lower, f->lower +(f->size - new_msize),new_msize);
f->size = new_msize;
f->key = expect_key++;
}
f->buffer = ckrealloc(f->buffer,new_msize+1);
f->lower = ckrealloc(f->lower,new_msize+1);
}
f->msize = new_msize;
f->buffer[f->size] = '\0';
f->lower[f->size] = '\0';
}
}
int
expect_read(interp,masters,masters_max,m,timeout,key)
Tcl_Interp *interp;
int *masters;
int masters_max;
int *m;
int timeout;
int key;
{
struct exp_f *f;
int cc;
int write_count;
int tcl_set_flags;
if (masters == 0) {
cc = exp_get_next_event_info(interp,*m,masters_max);
tcl_set_flags = TCL_GLOBAL_ONLY;
} else {
cc = exp_get_next_event(interp,masters,masters_max,m,timeout,key);
tcl_set_flags = 0;
}
if (cc == EXP_DATA_NEW) {
cc = exp_i_read(interp,*m,timeout,tcl_set_flags);
if (cc == 0) {
cc = EXP_EOF;
} else if (cc > 0) {
f = exp_fs + *m;
f->buffer[f->size += cc] = '\0';
if (f->parity == 0) {
char *p = f->buffer + f->size - 1;
int count = cc;
while (count--) {
*p-- &= 0x7f;
}
}
}
} else if (cc == EXP_DATA_OLD) {
f = exp_fs + *m;
cc = 0;
} else if (cc == EXP_RECONFIGURE) {
return EXP_RECONFIGURE;
}
if (cc == EXP_ABEOF) {
if (i_read_errno == EIO) {
cc = EXP_EOF;
} else if (i_read_errno == EINVAL) {
cc = EXP_EOF;
} else {
if (i_read_errno == EBADF) {
exp_error(interp,"bad spawn_id (process died earlier?)");
} else {
exp_error(interp,"i_read(spawn_id=%d): %s",*m,
Tcl_PosixError(interp));
exp_close(interp,*m);
}
return(EXP_TCLERROR);
}
}
if (cc < 0) return (cc);
if (f->size) write_count = f->size - f->printed;
else write_count = 0;
if (write_count) {
if (logfile_all || (loguser && logfile)) {
fwrite(f->buffer + f->printed,1,write_count,logfile);
}
if (loguser && !exp_is_stdinfd(*m) && !exp_is_devttyfd(*m))
fwrite(f->buffer + f->printed,
1,write_count,stdout);
if (debugfile) fwrite(f->buffer + f->printed,
1,write_count,debugfile);
if (f->rm_nulls) {
f->size -= rm_nulls(f->buffer + f->printed,write_count);
}
f->buffer[f->size] = '\0';
exp_lowmemcpy(f->lower+f->printed,
f->buffer+f->printed,
1 + f->size - f->printed);
f->printed = f->size;
}
return(cc);
}
void
exp_buffer_shuffle(interp,f,save_flags,array_name,caller_name)
Tcl_Interp *interp;
struct exp_f *f;
int save_flags;
char *array_name;
char *caller_name;
{
char spawn_id[10];
char match_char;
int first_half = f->size/2;
int second_half = f->size - first_half;
sprintf(spawn_id,"%d",f-exp_fs);
debuglog("%s: set %s(spawn_id) \"%s\"\r\n",
caller_name,array_name,dprintify(spawn_id));
Tcl_SetVar2(interp,array_name,"spawn_id",spawn_id,save_flags);
match_char = f->buffer[first_half];
f->buffer[first_half] = 0;
debuglog("%s: set %s(buffer) \"%s\"\r\n",
caller_name,array_name,dprintify(f->buffer));
Tcl_SetVar2(interp,array_name,"buffer",f->buffer,save_flags);
f->buffer[first_half] = match_char;
memcpy(f->buffer,f->buffer+first_half,second_half);
memcpy(f->lower, f->lower +first_half,second_half);
f->size = second_half;
f->printed -= first_half;
if (f->printed < 0) f->printed = 0;
}
int
exp_tcl2_returnvalue(x)
int x;
{
switch (x) {
case TCL_ERROR: return EXP_TCLERROR;
case TCL_RETURN: return EXP_TCLRET;
case TCL_BREAK: return EXP_TCLBRK;
case TCL_CONTINUE: return EXP_TCLCNT;
case EXP_CONTINUE: return EXP_TCLCNTEXP;
case EXP_CONTINUE_TIMER: return EXP_TCLCNTTIMER;
case EXP_TCL_RETURN: return EXP_TCLRETTCL;
}
}
int
exp_2tcl_returnvalue(x)
int x;
{
switch (x) {
case EXP_TCLERROR: return TCL_ERROR;
case EXP_TCLRET: return TCL_RETURN;
case EXP_TCLBRK: return TCL_BREAK;
case EXP_TCLCNT: return TCL_CONTINUE;
case EXP_TCLCNTEXP: return EXP_CONTINUE;
case EXP_TCLCNTTIMER: return EXP_CONTINUE_TIMER;
case EXP_TCLRETTCL: return EXP_TCL_RETURN;
}
}
static int
exp_i_read(interp,m,timeout,save_flags)
Tcl_Interp *interp;
int m;
int timeout;
int save_flags;
{
struct exp_f *f;
int cc = EXP_TIMEOUT;
f = exp_fs + m;
if (f->size == f->msize)
exp_buffer_shuffle(interp,f,save_flags,EXPECT_OUT,"expect");
#ifdef SIMPLE_EVENT
restart:
alarm_fired = FALSE;
if (timeout > -1) {
signal(SIGALRM,sigalarm_handler);
alarm((timeout > 0)?timeout:1);
}
#endif
cc = read(m,f->buffer+f->size, f->msize-f->size);
i_read_errno = errno;
#ifdef SIMPLE_EVENT
alarm(0);
if (cc == -1) {
if (i_read_errno == EINTR) {
if (alarm_fired) {
return EXP_TIMEOUT;
} else {
if (Tcl_AsyncReady()) {
int rc = Tcl_AsyncInvoke(interp,TCL_OK);
if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
}
if (!f->valid) {
exp_error(interp,"spawn_id %d no longer valid",f-exp_fs);
return EXP_TCLERROR;
}
goto restart;
}
}
}
#endif
return(cc);
}
char *
exp_get_var(interp,var)
Tcl_Interp *interp;
char *var;
{
char *val;
if (NULL != (val = Tcl_GetVar(interp,var,0 )))
return(val);
return(Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY));
}
static int
get_timeout(interp)
Tcl_Interp *interp;
{
static int timeout = INIT_EXPECT_TIMEOUT;
char *t;
if (NULL != (t = exp_get_var(interp,EXPECT_TIMEOUT))) {
timeout = atoi(t);
}
return(timeout);
}
static int
update_expect_fds(i_list,fd_union)
struct exp_i *i_list;
struct exp_fd_list **fd_union;
{
struct exp_i *p;
for (p=i_list;p;p=p->next) {
struct exp_fd_list *fdl;
for (fdl=p->fd_list;fdl;fdl=fdl->next) {
struct exp_fd_list *tmpfdl;
struct exp_fd_list *u;
if (fdl->fd == EXP_SPAWN_ID_ANY) continue;
for (u = *fd_union;u;u=u->next) {
if (fdl->fd == u->fd) goto found;
}
tmpfdl = exp_new_fd(fdl->fd);
tmpfdl->next = *fd_union;
*fd_union = tmpfdl;
found:;
}
}
return TCL_OK;
}
char *
exp_cmdtype_printable(cmdtype)
int cmdtype;
{
switch (cmdtype) {
case EXP_CMD_FG: return("expect");
case EXP_CMD_BG: return("expect_background");
case EXP_CMD_BEFORE: return("expect_before");
case EXP_CMD_AFTER: return("expect_after");
}
#ifdef LINT
return("unknown expect command");
#endif
}
static char *
exp_indirect_update2(clientData, interp, name1, name2, flags)
ClientData clientData;
Tcl_Interp *interp;
char *name1;
char *name2;
int flags;
{
char *msg;
struct exp_i *exp_i = (struct exp_i *)clientData;
exp_configure_count++;
msg = exp_indirect_update1(interp,&exp_cmds[exp_i->cmdtype],exp_i);
exp_background_filehandlers_run_all();
return msg;
}
static char *
exp_indirect_update1(interp,ecmd,exp_i)
Tcl_Interp *interp;
struct exp_cmd_descriptor *ecmd;
struct exp_i *exp_i;
{
struct exp_fd_list *fdl;
if (ecmd->cmdtype == EXP_CMD_BG) {
for (fdl=exp_i->fd_list;fdl;fdl=fdl->next) {
int m = fdl->fd;
if (m == EXP_SPAWN_ID_ANY) continue;
if (!exp_fd2f(interp,fdl->fd,1,0,"")) continue;
exp_fs[m].bg_ecount--;
if (exp_fs[m].bg_ecount == 0) {
exp_disarm_background_filehandler(m);
exp_fs[m].bg_interp = 0;
}
}
}
exp_i_update(interp,exp_i);
for (fdl=exp_i->fd_list;fdl;fdl=fdl->next) {
if (fdl->fd == EXP_SPAWN_ID_ANY) continue;
if (!exp_fd2f(interp,fdl->fd,1,1,
exp_cmdtype_printable(ecmd->cmdtype))) {
static char msg[200];
sprintf(msg,"%s from indirect variable (%s)",
interp->result,exp_i->variable);
return msg;
}
}
if (ecmd->cmdtype == EXP_CMD_BG) {
fd_list_arm(interp,exp_i->fd_list);
}
return (char *)0;
}
void
exp_background_filehandlers_run_all()
{
int m;
struct exp_f *f;
for (m=0;m<=exp_fd_max;m++) {
f = exp_fs + m;
if (!f->valid) continue;
if (f->bg_interp && (f->size > 0)) {
exp_background_filehandler((ClientData)f->fd_ptr,0);
}
}
}
void
exp_background_filehandler(clientData,mask)
ClientData clientData;
int mask;
{
int m;
Tcl_Interp *interp;
int cc;
struct exp_f *f;
int i;
struct eval_out eo;
struct exp_f *last_f;
int last_case;
m = *(int *)clientData;
f = exp_fs + m;
interp = f->bg_interp;
exp_block_background_filehandler(m);
if (mask == 0) {
cc = 0;
} else {
cc = expect_read(interp,(int *)0,mask,&m,EXP_TIME_INFINITY,0);
}
do_more_data:
eo.e = 0;
eo.f = 0;
eo.match = 0;
last_f = 0;
if (cc == EXP_EOF) {
} else if (cc < 0) {
goto finish;
} else {
cc = EXP_NOMATCH;
}
cc = eval_cases(interp,&exp_cmds[EXP_CMD_BEFORE],
m,&eo,&last_f,&last_case,cc,&m,1,"_background");
cc = eval_cases(interp,&exp_cmds[EXP_CMD_BG],
m,&eo,&last_f,&last_case,cc,&m,1,"_background");
cc = eval_cases(interp,&exp_cmds[EXP_CMD_AFTER],
m,&eo,&last_f,&last_case,cc,&m,1,"_background");
if (cc == EXP_TCLERROR) {
Tcl_BackgroundError(interp);
goto finish;
}
if (cc == EXP_EOF) {
eo.f = exp_fs + m;
eo.match = eo.f->size;
eo.buffer = eo.f->buffer;
debuglog("expect_background: read eof\r\n");
goto matched;
}
if (!eo.e) {
goto finish;
}
matched:
#define out(i,val) debuglog("expect_background: set %s(%s) \"%s\"\r\n",EXPECT_OUT,i, \
dprintify(val)); \
Tcl_SetVar2(interp,EXPECT_OUT,i,val,TCL_GLOBAL_ONLY);
{
char *body = 0;
char *buffer;
struct ecase *e = 0;
int match = -1;
char match_char;
char *eof_body = 0;
if (eo.e) {
e = eo.e;
body = e->body;
if (cc != EXP_TIMEOUT) {
f = eo.f;
match = eo.match;
buffer = eo.buffer;
}
#if 0
if (e->timestamp) {
char value[20];
time(¤t_time);
elapsed_time = current_time - start_time;
elapsed_time_total = current_time - start_time_total;
sprintf(value,"%d",elapsed_time);
out("seconds",value);
sprintf(value,"%d",elapsed_time_total);
out("seconds_total",value);
exp_timestamp(interp,¤t_time,EXPECT_OUT);
}
#endif
} else if (cc == EXP_EOF) {
f = eo.f;
match = eo.match;
buffer = eo.buffer;
}
if (match >= 0) {
char name[20], value[20];
if (e && e->use == PAT_RE) {
regexp *re = e->re;
for (i=0;i<NSUBEXP;i++) {
int offset;
if (re->startp[i] == 0) continue;
if (e->indices) {
sprintf(name,"%d,start",i);
offset = re->startp[i]-buffer;
sprintf(value,"%d",offset);
out(name,value);
sprintf(name,"%d,end",i);
sprintf(value,"%d",
re->endp[i]-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;
}
match = re->endp[0]-buffer;
} else if (e && (e->use == PAT_GLOB || e->use == PAT_EXACT)) {
char *str;
if (e->indices) {
sprintf(value,"%d",e->simple_start);
out("0,start",value);
sprintf(value,"%d",e->simple_start + match - 1);
out("0,end",value);
}
str = f->buffer + e->simple_start;
match_char = str[match];
str[match] = 0;
out("0,string",str);
str[match] = match_char;
match += e->simple_start;
} else if (e && e->use == PAT_NULL && e->indices) {
sprintf(value,"%d",match-1);
out("0,start",value);
sprintf(value,"%d",match-1);
out("0,end",value);
} else if (e && e->use == PAT_FULLBUFFER) {
debuglog("expect_background: full buffer\r\n");
}
}
if (eo.f) {
char spawn_id[10];
sprintf(spawn_id,"%d",f-exp_fs);
out("spawn_id",spawn_id);
match_char = f->buffer[match];
f->buffer[match] = 0;
out("buffer",f->buffer);
f->buffer[match] = match_char;
if (!e || e->transfer) {
f->size -= match;
f->printed -= match;
if (f->size != 0) {
memmove(f->buffer,f->buffer+match,f->size);
memmove(f->lower,f->lower+match,f->size);
}
f->buffer[f->size] = '\0';
f->lower[f->size] = '\0';
}
if (cc == EXP_EOF) {
if (body) {
eof_body = ckalloc(strlen(body)+1);
strcpy(eof_body,body);
body = eof_body;
}
exp_close(interp,f - exp_fs);
}
}
if (body) {
int result = Tcl_GlobalEval(interp,body);
if (result != TCL_OK) Tcl_BackgroundError(interp);
if (eof_body) ckfree(eof_body);
}
if (exp_fs[m].valid && (exp_fs[m].bg_status == blocked)
&& (f->size > 0)) {
cc = f->size;
goto do_more_data;
}
}
finish:
if (exp_fs[m].valid)
exp_unblock_background_filehandler(m);
}
#undef out
int
Exp_ExpectCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int cc;
int m;
struct exp_f *f;
int i;
struct exp_cmd_descriptor eg;
struct exp_fd_list *fd_list;
struct exp_fd_list *fdl;
int *masters;
int mcount;
struct eval_out eo;
int result;
time_t start_time_total;
time_t start_time = 0;
time_t current_time = 0;
time_t end_time;
time_t elapsed_time_total;
time_t elapsed_time;
struct exp_f *last_f;
int last_case;
int first_time = 1;
int key;
int configure_count;
int timeout;
int remtime;
int reset_timer;
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));
}
time(&start_time_total);
start_time = start_time_total;
reset_timer = TRUE;
exp_cmd_init(&eg,EXP_CMD_FG,EXP_TEMPORARY);
fd_list = 0;
masters = 0;
if (TCL_ERROR == parse_expect_args(interp,&eg,
*(int *)clientData,argc,argv))
return TCL_ERROR;
restart_with_update:
if ((TCL_ERROR == update_expect_fds(exp_cmds[EXP_CMD_BEFORE].i_list,&fd_list))
|| (TCL_ERROR == update_expect_fds(exp_cmds[EXP_CMD_AFTER].i_list, &fd_list))
|| (TCL_ERROR == update_expect_fds(eg.i_list,&fd_list))) {
result = TCL_ERROR;
goto cleanup;
}
configure_count = exp_configure_count;
mcount = 0;
for (fdl=fd_list;fdl;fdl=fdl->next) {
mcount++;
if (!exp_fd2f(interp,fdl->fd,1,1,"expect")) {
result = TCL_ERROR;
goto cleanup;
}
}
masters = (int *)ckalloc(mcount * sizeof(int));
for (fdl=fd_list,i=0;fdl;fdl=fdl->next,i++) {
masters[i] = fdl->fd;
}
restart:
if (first_time) first_time = 0;
else time(&start_time);
if (eg.timeout_specified_by_flag) {
timeout = eg.timeout;
} else {
timeout = get_timeout(interp);
}
key = expect_key++;
result = TCL_OK;
last_f = 0;
eo.e = 0;
eo.f = 0;
eo.match = 0;
if (timeout != EXP_TIME_INFINITY) {
if (reset_timer) {
time(¤t_time);
end_time = current_time + timeout;
} else {
reset_timer = TRUE;
}
}
remtime = timeout;
for (;;) {
if ((timeout != EXP_TIME_INFINITY) && (remtime < 0)) {
cc = EXP_TIMEOUT;
} else {
cc = expect_read(interp,masters,mcount,&m,remtime,key);
}
if (cc == EXP_EOF) {
} else if (cc == EXP_TIMEOUT) {
debuglog("expect: timed out\r\n");
} else if (cc == EXP_RECONFIGURE) {
reset_timer = FALSE;
goto restart_with_update;
} else if (cc < 0) {
goto error;
} else {
f = exp_fs + m;
cc = EXP_NOMATCH;
last_f = 0;
}
cc = eval_cases(interp,&exp_cmds[EXP_CMD_BEFORE],
m,&eo,&last_f,&last_case,cc,masters,mcount,"");
cc = eval_cases(interp,&eg,
m,&eo,&last_f,&last_case,cc,masters,mcount,"");
cc = eval_cases(interp,&exp_cmds[EXP_CMD_AFTER],
m,&eo,&last_f,&last_case,cc,masters,mcount,"");
if (cc == EXP_TCLERROR) goto error;
if (cc == EXP_EOF) {
eo.f = exp_fs + m;
eo.match = eo.f->size;
eo.buffer = eo.f->buffer;
debuglog("expect: read eof\r\n");
break;
} else if (cc == EXP_TIMEOUT) break;
if (eo.e) break;
f->force_read = TRUE;
if (timeout != EXP_TIME_INFINITY) {
time(¤t_time);
remtime = end_time - current_time;
}
}
goto done;
error:
result = exp_2tcl_returnvalue(cc);
done:
#define out(i,val) debuglog("expect: set %s(%s) \"%s\"\r\n",EXPECT_OUT,i, \
dprintify(val)); \
Tcl_SetVar2(interp,EXPECT_OUT,i,val,0);
if (result != TCL_ERROR) {
char *body = 0;
char *buffer;
struct ecase *e = 0;
int match = -1;
char match_char;
char *eof_body = 0;
if (eo.e) {
e = eo.e;
body = e->body;
if (cc != EXP_TIMEOUT) {
f = eo.f;
match = eo.match;
buffer = eo.buffer;
}
if (e->timestamp) {
char value[20];
time(¤t_time);
elapsed_time = current_time - start_time;
elapsed_time_total = current_time - start_time_total;
sprintf(value,"%d",elapsed_time);
out("seconds",value);
sprintf(value,"%d",elapsed_time_total);
out("seconds_total",value);
exp_timestamp(interp,¤t_time,EXPECT_OUT);
}
} else if (cc == EXP_EOF) {
f = eo.f;
match = eo.match;
buffer = eo.buffer;
}
if (match >= 0) {
char name[20], value[20];
if (e && e->use == PAT_RE) {
regexp *re = e->re;
for (i=0;i<NSUBEXP;i++) {
int offset;
if (re->startp[i] == 0) continue;
if (e->indices) {
sprintf(name,"%d,start",i);
offset = re->startp[i]-buffer;
sprintf(value,"%d",offset);
out(name,value);
sprintf(name,"%d,end",i);
sprintf(value,"%d",
re->endp[i]-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;
}
match = re->endp[0]-buffer;
} else if (e && (e->use == PAT_GLOB || e->use == PAT_EXACT)) {
char *str;
if (e->indices) {
sprintf(value,"%d",e->simple_start);
out("0,start",value);
sprintf(value,"%d",e->simple_start + match - 1);
out("0,end",value);
}
str = f->buffer + e->simple_start;
match_char = str[match];
str[match] = 0;
out("0,string",str);
str[match] = match_char;
match += e->simple_start;
} else if (e && e->use == PAT_NULL && e->indices) {
sprintf(value,"%d",match-1);
out("0,start",value);
sprintf(value,"%d",match-1);
out("0,end",value);
} else if (e && e->use == PAT_FULLBUFFER) {
debuglog("expect: full buffer\r\n");
}
}
if (eo.f) {
char spawn_id[10];
sprintf(spawn_id,"%d",f-exp_fs);
out("spawn_id",spawn_id);
match_char = f->buffer[match];
f->buffer[match] = 0;
out("buffer",f->buffer);
f->buffer[match] = match_char;
if (!e || e->transfer) {
f->size -= match;
f->printed -= match;
if (f->size != 0) {
memmove(f->buffer,f->buffer+match,f->size);
memmove(f->lower,f->lower+match,f->size);
}
f->buffer[f->size] = '\0';
f->lower[f->size] = '\0';
}
if (cc == EXP_EOF) {
if (body) {
eof_body = ckalloc(strlen(body)+1);
strcpy(eof_body,body);
body = eof_body;
}
exp_close(interp,f - exp_fs);
}
}
if (body) {
result = Tcl_Eval(interp,body);
if (eof_body) ckfree(eof_body);
}
}
cleanup:
if (result == EXP_CONTINUE_TIMER) {
reset_timer = FALSE;
result = EXP_CONTINUE;
}
if ((result == EXP_CONTINUE)
&& (configure_count == exp_configure_count)) {
debuglog("expect: continuing expect\r\n");
goto restart;
}
if (fd_list) {
exp_free_fd(fd_list);
fd_list = 0;
}
if (masters) {
ckfree((char *)masters);
masters = 0;
}
if (result == EXP_CONTINUE) {
debuglog("expect: continuing expect after update\r\n");
goto restart_with_update;
}
free_ecases(interp,&eg,0);
exp_free_i(interp,eg.i_list,exp_indirect_update2);
return(result);
}
#undef out
#define out(elt) Tcl_SetVar2(interp,array,elt,ascii,0);
void
exp_timestamp(interp,timeval,array)
Tcl_Interp *interp;
time_t *timeval;
char *array;
{
struct tm *tm;
char *ascii;
tm = localtime(timeval);
ascii = asctime(tm);
ascii[24] = '\0';
out("timestamp");
sprintf(ascii,"%ld",*timeval);
out("epoch");
sprintf(ascii,"%d",tm->tm_sec);
out("sec");
sprintf(ascii,"%d",tm->tm_min);
out("min");
sprintf(ascii,"%d",tm->tm_hour);
out("hour");
sprintf(ascii,"%d",tm->tm_mday);
out("mday");
sprintf(ascii,"%d",tm->tm_mon);
out("mon");
sprintf(ascii,"%d",tm->tm_year);
out("year");
sprintf(ascii,"%d",tm->tm_wday);
out("wday");
sprintf(ascii,"%d",tm->tm_yday);
out("yday");
sprintf(ascii,"%d",tm->tm_isdst);
out("isdst");
}
static int
Exp_TimestampCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *format = 0;
time_t seconds = -1;
int gmt = FALSE;
struct tm *tm;
Tcl_DString dstring;
argc--; argv++;
while (*argv) {
if (streq(*argv,"-format")) {
argc--; argv++;
if (!*argv) goto usage_error;
format = *argv;
argc--; argv++;
} else if (streq(*argv,"-seconds")) {
argc--; argv++;
if (!*argv) goto usage_error;
seconds = atoi(*argv);
argc--; argv++;
} else if (streq(*argv,"-gmt")) {
gmt = TRUE;
argc--; argv++;
} else break;
}
if (argc) goto usage_error;
if (seconds == -1) {
time(&seconds);
}
Tcl_DStringInit(&dstring);
if (format) {
if (gmt) {
tm = gmtime(&seconds);
} else {
tm = localtime(&seconds);
}
exp_strftime(format,tm,&dstring);
Tcl_DStringResult(interp,&dstring);
} else {
sprintf(interp->result,"%ld",seconds);
}
return TCL_OK;
usage_error:
exp_error(interp,"args: [-seconds #] [-format format]");
return TCL_ERROR;
}
void
exp_lowmemcpy(dest,src,n)
char *dest;
char *src;
int n;
{
for (;n>0;n--) {
*dest = ((isascii(*src) && isupper(*src))?tolower(*src):*src);
src++; dest++;
}
}
int
Exp_MatchMaxCmd(clientData,interp,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int size = -1;
int m = -1;
struct exp_f *f;
int Default = FALSE;
argc--; argv++;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-d")) {
Default = TRUE;
} else if (streq(*argv,"-i")) {
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-i needs argument");
return(TCL_ERROR);
}
m = atoi(*argv);
} else break;
}
if (!Default) {
if (m == -1) {
if (!(f = exp_update_master(interp,&m,0,0)))
return(TCL_ERROR);
} else {
if (!(f = exp_fd2f(interp,m,0,0,"match_max")))
return(TCL_ERROR);
}
} else if (m != -1) {
exp_error(interp,"cannot do -d and -i at the same time");
return(TCL_ERROR);
}
if (argc == 0) {
if (Default) {
size = exp_default_match_max;
} else {
size = f->umsize;
}
sprintf(interp->result,"%d",size);
return(TCL_OK);
}
if (argc > 1) {
exp_error(interp,"too many arguments");
return(TCL_OK);
}
size = atoi(argv[0]);
if (size <= 0) {
exp_error(interp,"must be positive");
return(TCL_ERROR);
}
if (Default) exp_default_match_max = size;
else f->umsize = size;
return(TCL_OK);
}
int
Exp_RemoveNullsCmd(clientData,interp,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int value = -1;
int m = -1;
struct exp_f *f;
int Default = FALSE;
argc--; argv++;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-d")) {
Default = TRUE;
} else if (streq(*argv,"-i")) {
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-i needs argument");
return(TCL_ERROR);
}
m = atoi(*argv);
} else break;
}
if (!Default) {
if (m == -1) {
if (!(f = exp_update_master(interp,&m,0,0)))
return(TCL_ERROR);
} else {
if (!(f = exp_fd2f(interp,m,0,0,"remove_nulls")))
return(TCL_ERROR);
}
} else if (m != -1) {
exp_error(interp,"cannot do -d and -i at the same time");
return(TCL_ERROR);
}
if (argc == 0) {
if (Default) {
value = exp_default_match_max;
} else {
value = f->rm_nulls;
}
sprintf(interp->result,"%d",value);
return(TCL_OK);
}
if (argc > 1) {
exp_error(interp,"too many arguments");
return(TCL_OK);
}
value = atoi(argv[0]);
if (value != 0 && value != 1) {
exp_error(interp,"must be 0 or 1");
return(TCL_ERROR);
}
if (Default) exp_default_rm_nulls = value;
else f->rm_nulls = value;
return(TCL_OK);
}
int
Exp_ParityCmd(clientData,interp,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int parity;
int m = -1;
struct exp_f *f;
int Default = FALSE;
argc--; argv++;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-d")) {
Default = TRUE;
} else if (streq(*argv,"-i")) {
argc--;argv++;
if (argc < 1) {
exp_error(interp,"-i needs argument");
return(TCL_ERROR);
}
m = atoi(*argv);
} else break;
}
if (!Default) {
if (m == -1) {
if (!(f = exp_update_master(interp,&m,0,0)))
return(TCL_ERROR);
} else {
if (!(f = exp_fd2f(interp,m,0,0,"parity")))
return(TCL_ERROR);
}
} else if (m != -1) {
exp_error(interp,"cannot do -d and -i at the same time");
return(TCL_ERROR);
}
if (argc == 0) {
if (Default) {
parity = exp_default_parity;
} else {
parity = f->parity;
}
sprintf(interp->result,"%d",parity);
return(TCL_OK);
}
if (argc > 1) {
exp_error(interp,"too many arguments");
return(TCL_OK);
}
parity = atoi(argv[0]);
if (Default) exp_default_parity = parity;
else f->parity = parity;
return(TCL_OK);
}
#if DEBUG_PERM_ECASES
void
exp_fd_print(fdl)
struct exp_fd_list *fdl;
{
if (!fdl) return;
printf("%d ",fdl->fd);
exp_fd_print(fdl->next);
}
void
exp_i_print(exp_i)
struct exp_i *exp_i;
{
if (!exp_i) return;
printf("exp_i %x",exp_i);
printf((exp_i->direct == EXP_DIRECT)?" direct":" indirect");
printf((exp_i->duration == EXP_PERMANENT)?" perm":" tmp");
printf(" ecount = %d\n",exp_i->ecount);
printf("variable %s, value %s\n",
((exp_i->variable)?exp_i->variable:"--"),
((exp_i->value)?exp_i->value:"--"));
printf("fds: ");
exp_fd_print(exp_i->fd_list); printf("\n");
exp_i_print(exp_i->next);
}
void
exp_ecase_print(ecase)
struct ecase *ecase;
{
printf("pat <%s>\n",ecase->pat);
printf("exp_i = %x\n",ecase->i_list);
}
void
exp_ecases_print(ecd)
struct exp_cases_descriptor *ecd;
{
int i;
printf("%d cases\n",ecd->count);
for (i=0;i<ecd->count;i++) exp_ecase_print(ecd->cases[i]);
}
void
exp_cmd_print(ecmd)
struct exp_cmd_descriptor *ecmd;
{
printf("expect cmd type: %17s",exp_cmdtype_printable(ecmd->cmdtype));
printf((ecmd->duration==EXP_PERMANENT)?" perm ": "tmp ");
exp_ecases_print(&ecmd->ecd);
exp_i_print(ecmd->i_list);
}
void
exp_cmds_print()
{
exp_cmd_print(&exp_cmds[EXP_CMD_BEFORE]);
exp_cmd_print(&exp_cmds[EXP_CMD_AFTER]);
exp_cmd_print(&exp_cmds[EXP_CMD_BG]);
}
int
cmdX(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
exp_cmds_print();
return TCL_OK;
}
#endif
static int spawn_id_bad = EXP_SPAWN_ID_BAD;
static int spawn_id_user = EXP_SPAWN_ID_USER;
static struct exp_cmd_data
cmd_data[] = {
{"expect", exp_proc(Exp_ExpectCmd), (ClientData)&spawn_id_bad, 0},
{"expect_after",exp_proc(Exp_ExpectGlobalCmd),(ClientData)&exp_cmds[EXP_CMD_AFTER],0},
{"expect_before",exp_proc(Exp_ExpectGlobalCmd),(ClientData)&exp_cmds[EXP_CMD_BEFORE],0},
{"expect_user", exp_proc(Exp_ExpectCmd), (ClientData)&spawn_id_user, 0},
{"expect_tty", exp_proc(Exp_ExpectCmd), (ClientData)&exp_dev_tty, 0},
{"expect_background",exp_proc(Exp_ExpectGlobalCmd),(ClientData)&exp_cmds[EXP_CMD_BG],0},
{"match_max", exp_proc(Exp_MatchMaxCmd), 0, 0},
{"remove_nulls",exp_proc(Exp_RemoveNullsCmd), 0, 0},
{"parity", exp_proc(Exp_ParityCmd), 0, 0},
{"timestamp", exp_proc(Exp_TimestampCmd), 0, 0},
{0}};
void
exp_init_expect_cmds(interp)
Tcl_Interp *interp;
{
exp_create_commands(interp,cmd_data);
Tcl_SetVar(interp,EXPECT_TIMEOUT,INIT_EXPECT_TIMEOUT_LIT,0);
Tcl_SetVar(interp,EXP_SPAWN_ID_ANY_VARNAME,EXP_SPAWN_ID_ANY_LIT,0);
exp_cmd_init(&exp_cmds[EXP_CMD_BEFORE],EXP_CMD_BEFORE,EXP_PERMANENT);
exp_cmd_init(&exp_cmds[EXP_CMD_AFTER ],EXP_CMD_AFTER, EXP_PERMANENT);
exp_cmd_init(&exp_cmds[EXP_CMD_BG ],EXP_CMD_BG, EXP_PERMANENT);
exp_cmd_init(&exp_cmds[EXP_CMD_FG ],EXP_CMD_FG, EXP_TEMPORARY);
exp_cmds[EXP_CMD_BEFORE].ecd.cases = 0;
exp_cmds[EXP_CMD_AFTER ].ecd.cases = 0;
exp_cmds[EXP_CMD_BG ].ecd.cases = 0;
pattern_style[PAT_EOF] = "eof";
pattern_style[PAT_TIMEOUT] = "timeout";
pattern_style[PAT_DEFAULT] = "default";
pattern_style[PAT_FULLBUFFER] = "full buffer";
pattern_style[PAT_GLOB] = "glob pattern";
pattern_style[PAT_RE] = "regular expression";
pattern_style[PAT_EXACT] = "exact string";
pattern_style[PAT_NULL] = "null";
#if 0
Tcl_CreateCommand(interp,"x",
cmdX,(ClientData)0,exp_deleteProc);
#endif
}
void
exp_init_sig() {
#if 0
signal(SIGALRM,sigalarm_handler);
signal(SIGINT,sigint_handler);
#endif
}