#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"
typedef struct ThreadSpecificData {
Tcl_Obj *cmdObjReturn;
Tcl_Obj *cmdObjInterpreter;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
#define INTER_OUT "interact_out"
#define out(var,val) \
expDiagLog("interact: set %s(%s) ",INTER_OUT,var); \
expDiagLogU(expPrintify(val)); \
expDiagLogU("\"\r\n"); \
Tcl_SetVar2(interp,INTER_OUT,var,val,0);
#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->fdout == 1) || (expDevttyIs(x)))
#define real_tty_input(x) (exp_stdin_is_tty && ((x->fdin==0) || (expDevttyIs(x))))
#define new(x) (x *)ckalloc(sizeof(x))
struct action {
Tcl_Obj *statement;
int tty_reset;
int iread;
int iwrite;
struct action *next;
};
struct keymap {
Tcl_Obj *keys;
int 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;
};
struct input *
expStateToInput(hash,esPtr)
ExpState *esPtr;
Tcl_HashTable *hash;
{
Tcl_HashEntry *entry = Tcl_FindHashEntry(hash,(char *)esPtr);
if (!entry) {
return 0;
}
return ((struct input *)Tcl_GetHashValue(entry));
}
void
expCreateStateToInput(hash,esPtr,inp)
ExpState *esPtr;
Tcl_HashTable *hash;
struct input *inp;
{
Tcl_HashEntry *entry;
int newPtr;
entry = Tcl_CreateHashEntry(hash,(char *)esPtr,&newPtr);
Tcl_SetHashValue(entry,(ClientData)inp);
}
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
intMatch(esPtr,keymap,km_match,matchLen,skip,info)
ExpState *esPtr;
struct keymap *keymap;
struct keymap **km_match;
int *matchLen;
int *skip;
Tcl_RegExpInfo *info;
{
char *string;
struct keymap *km;
char *ks;
char *start_search;
int offset;
char *string_end;
int stringBytes, bytesThisChar;
int rm_nulls;
Tcl_UniChar ch;
string = Tcl_GetStringFromObj(esPtr->buffer,&stringBytes);
if (!keymap) {
*skip = stringBytes;
return(EXP_CANTMATCH);
}
rm_nulls = esPtr->rm_nulls;
string_end = string + stringBytes;
for (start_search = string, offset = 0;
start_search < string_end;
start_search += bytesThisChar, offset++) {
bytesThisChar = Tcl_UtfToUniChar(start_search, &ch);
if (*km_match) break;
for (km=keymap;km;km=km->next) {
char *s;
if (km->null) {
if (ch == 0) {
*skip = start_search-string;
*matchLen = 1;
*km_match = km;
return(EXP_MATCH);
}
} else if (!km->re) {
int slen, kslen;
Tcl_UniChar sch, ksch;
ks = Tcl_GetString(km->keys);
for (s = start_search;; s += slen, ks += kslen) {
if (*ks == 0) {
*skip = start_search-string;
*matchLen = s-start_search;
*km_match = km;
return(EXP_MATCH);
}
if (s == string_end) {
if (!*km_match) *km_match = km;
break;
}
slen = Tcl_UtfToUniChar(s, &sch);
kslen = Tcl_UtfToUniChar(ks, &ksch);
if (sch == ksch) continue;
if ((sch == '\0') && rm_nulls) {
kslen = 0;
continue;
}
break;
}
} else {
Tcl_RegExp re;
int flags;
int result;
re = Tcl_GetRegExpFromObj(NULL, km->keys,
TCL_REG_ADVANCED|TCL_REG_BOSONLY|TCL_REG_CANMATCH);
flags = (offset > 0) ? TCL_REG_NOTBOL : 0;
result = Tcl_RegExpExecObj(NULL, re, esPtr->buffer, offset,
-1 , flags);
if (result > 0) {
*km_match = km;
*skip = start_search-string;
Tcl_RegExpGetInfo(re, info);
*matchLen = Tcl_UtfAtIndex(start_search,info->matches[0].end) - start_search;
return EXP_MATCH;
} else if (result == 0) {
Tcl_RegExpGetInfo(re, info);
if (info->extendStart == 0) {
if (!*km_match) *km_match = km;
}
}
}
}
}
if (*km_match) {
start_search--;
*skip = start_search - string;
*matchLen = string_end - start_search;
return(EXP_CANMATCH);
}
*skip = start_search-string;
return(EXP_CANTMATCH);
}
static void
intRegExpMatchProcess(interp,esPtr,km,info,offset)
Tcl_Interp *interp;
ExpState *esPtr;
struct keymap *km;
Tcl_RegExpInfo *info;
int offset;
{
char name[20], value[20];
int i;
for (i=0;i<=info->nsubs;i++) {
int start, end;
Tcl_Obj *val;
start = info->matches[i].start + offset;
if (start == -1) continue;
end = (info->matches[i].end-1) + offset;
if (km->indices) {
sprintf(name,"%d,start",i);
sprintf(value,"%d",start);
out(name,value);
sprintf(name,"%d,end",i);
sprintf(value,"%d",end);
out(name,value);
}
sprintf(name,"%d,string",i);
val = Tcl_GetRange(esPtr->buffer, start, end);
expDiagLog("expect_background: set %s(%s) \"",INTER_OUT,name);
expDiagLogU(expPrintifyObj(val));
expDiagLogU("\"\r\n");
Tcl_SetVar2Ex(interp,INTER_OUT,name,val,0);
}
}
static void
intEcho(esPtr,skipBytes,matchBytes)
ExpState *esPtr;
int skipBytes;
int matchBytes;
{
int seenBytes;
int echoBytes;
int offsetBytes;
seenBytes = esPtr->printed + esPtr->echoed;
if (skipBytes >= seenBytes) {
echoBytes = matchBytes;
offsetBytes = skipBytes;
} else if ((matchBytes + skipBytes - seenBytes) > 0) {
echoBytes = matchBytes + skipBytes - seenBytes;
offsetBytes = seenBytes;
}
Tcl_WriteChars(esPtr->channel,
Tcl_GetString(esPtr->buffer) + offsetBytes,
echoBytes);
esPtr->echoed = matchBytes + skipBytes - esPtr->printed;
}
static int
intRead(interp,esPtr,warnOnBufferFull,interruptible,key)
Tcl_Interp *interp;
ExpState *esPtr;
int warnOnBufferFull;
int interruptible;
int key;
{
char *eobOld;
int cc;
int size;
char *str;
str = Tcl_GetStringFromObj(esPtr->buffer,&size);
eobOld = str+size;
if (size + TCL_UTF_MAX >= esPtr->msize) {
if (warnOnBufferFull) {
expDiagLogU("WARNING: interact buffer is full, probably because your\r\n");
expDiagLogU("patterns have matched all of it but require more chars\r\n");
expDiagLogU("in order to complete the match.\r\n");
expDiagLogU("Dumping first half of buffer in order to continue\r\n");
expDiagLogU("Recommend you enlarge the buffer or fix your patterns.\r\n");
}
exp_buffer_shuffle(interp,esPtr,0,INTER_OUT,"interact");
}
if (!interruptible) {
cc = Tcl_ReadChars(esPtr->channel,
esPtr->buffer,
esPtr->msize - (size / TCL_UTF_MAX),
1 );
} else {
#ifdef SIMPLE_EVENT
cc = intIRead(esPtr->channel,
esPtr->buffer,
esPtr->msize - (size / TCL_UTF_MAX),
1 );
#endif
}
if (cc > 0) {
expDiagLog("spawn id %s sent <",esPtr->name);
expDiagLogU(expPrintify(eobOld));
expDiagLogU(">\r\n");
esPtr->key = key;
}
return cc;
}
#ifdef SIMPLE_EVENT
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
#endif
#include <setjmp.h>
#ifdef HAVE_SIGLONGJMP
static sigjmp_buf env;
#else
static jmp_buf env;
#endif
static int reading;
static int deferred_interrupt = FALSE;
static void
sigchld_handler()
{
if (reading) {
#ifdef HAVE_SIGLONGJMP
siglongjmp(env,1);
#else
longjmp(env,1);
#endif
}
deferred_interrupt = TRUE;
}
#define EXP_CHILD_EOF -100
static int
intIRead(channel,obj,size,flags);
Tcl_Channel channel;
Tcl_Obj *obj;
int size;
int flags;
{
int cc = EXP_CHILD_EOF;
if (deferred_interrupt) return(cc);
#ifdef HAVE_SIGLONGJMP
if (0 == sigsetjmp(env,1)) {
#else
if (0 == setjmp(env)) {
#endif
reading = TRUE;
cc = Tcl_ReadChars(channel,obj,size,flags);
}
reading = FALSE;
return(cc);
}
#define CHILD_DIED -2
#define SPAWNED_PROCESS_DIED -3
static void
clean_up_after_child(interp,esPtr)
Tcl_Interp *interp;
ExpState *esPtr;
{
expWaitOnOne();
expWaitOnOne();
deferred_interrupt = FALSE;
exp_close(interp,esPtr);
}
#endif
static int
update_interact_fds(interp,esPtrCount,esPtrToInput,esPtrs,input_base,
do_indirect,config_count,real_tty_caller)
Tcl_Interp *interp;
int *esPtrCount;
Tcl_HashTable **esPtrToInput;
ExpState ***esPtrs;
struct input *input_base;
int do_indirect;
int *config_count;
int *real_tty_caller;
{
struct input *inp;
struct output *outp;
struct exp_state_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->state_list;fdp;fdp=fdp->next) {
count++;
if (!expStateCheck(interp,fdp->esPtr,1,1,"interact")) {
return(TCL_ERROR);
}
}
for (outp = inp->output;outp;outp=outp->next) {
for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
if (!expStdinoutIs(fdp->esPtr)) {
if (!expStateCheck(interp,fdp->esPtr,1,0,"interact"))
return(TCL_ERROR);
}
}
}
}
if (!do_indirect) return TCL_OK;
if (*esPtrToInput == 0) {
*esPtrToInput = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
*esPtrs = (ExpState **)ckalloc(count * sizeof(ExpState *));
} else {
Tcl_DeleteHashTable(*esPtrToInput);
*esPtrs = (ExpState **)ckrealloc((char *)*esPtrs,count * sizeof(ExpState *));
}
Tcl_InitHashTable(*esPtrToInput,TCL_ONE_WORD_KEYS);
count = 0;
for (inp = input_base;inp;inp=inp->next) {
for (fdp = inp->i_list->state_list;fdp;fdp=fdp->next) {
expCreateStateToInput(*esPtrToInput,fdp->esPtr,inp);
(*esPtrs)[count] = fdp->esPtr;
if (real_tty_input(fdp->esPtr)) real_tty = TRUE;
count++;
}
}
*esPtrCount = 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_InteractObjCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
Tcl_Obj *CONST *objv_copy;
char *string;
#ifdef SIMPLE_EVENT
int pid;
#endif
int input_count;
Tcl_HashTable *esPtrToInput = 0;
ExpState **esPtrs;
struct keymap *km;
Tcl_RegExpInfo reInfo;
ExpState *u = 0;
ExpState *esPtr = 0;
Tcl_Obj *chanName = 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 status = TCL_OK;
int i;
int size;
int timeout_simple = TRUE;
int real_tty;
int tty_changed = FALSE;
int was_raw;
int was_echo;
exp_tty tty_old;
Tcl_Obj *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 ((objc == 2) && exp_one_arg_braced(objv[1])) {
return(exp_eval_with_one_arg(clientData,interp,objv));
} else if ((objc == 3) && streq(Tcl_GetString(objv[1]),"-brace")) {
Tcl_Obj *new_objv[2];
new_objv[0] = objv[0];
new_objv[1] = objv[2];
return(exp_eval_with_one_arg(clientData,interp,new_objv));
}
objv_copy = objv;
objv++;
objc--;
default_timeout = EXP_TIME_INFINITY;
arbitrary_timeout = EXP_TIME_INFINITY;
input_user = new(struct input);
input_user->i_list = exp_new_i_simple(expStdinoutGet(),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((ExpState *)0,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 = tsdPtr->cmdObjReturn;
action_eof.tty_reset = FALSE;
action_eof.iread = FALSE;
action_eof.iwrite = FALSE;
for (;objc>0;objc--,objv++) {
string = Tcl_GetString(*objv);
if (string[0] == '-') {
static char *switches[] = {
"--", "-exact", "-re", "-input",
"-output", "-u", "-o", "-i",
"-echo", "-nobuffer", "-indices", "-f",
"-reset", "-F", "-iread", "-iwrite",
"-eof", "-timeout", "-nobrace", (char *)0
};
enum switches {
EXP_SWITCH_DASH, EXP_SWITCH_EXACT,
EXP_SWITCH_REGEXP, EXP_SWITCH_INPUT,
EXP_SWITCH_OUTPUT, EXP_SWITCH_USER,
EXP_SWITCH_OPPOSITE, EXP_SWITCH_SPAWN_ID,
EXP_SWITCH_ECHO, EXP_SWITCH_NOBUFFER,
EXP_SWITCH_INDICES, EXP_SWITCH_FAST,
EXP_SWITCH_RESET, EXP_SWITCH_CAPFAST,
EXP_SWITCH_IREAD, EXP_SWITCH_IWRITE,
EXP_SWITCH_EOF, EXP_SWITCH_TIMEOUT,
EXP_SWITCH_NOBRACE
};
int index;
if (Tcl_GetIndexFromObj(interp, *objv, switches, "switch", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum switches) index) {
case EXP_SWITCH_DASH:
case EXP_SWITCH_EXACT:
objc--;
objv++;
goto pattern;
case EXP_SWITCH_REGEXP:
if (objc < 1) {
Tcl_WrongNumArgs(interp,1,objv_copy,"-re pattern");
return(TCL_ERROR);
}
next_re = TRUE;
objc--;
objv++;
if (!(Tcl_GetRegExpFromObj(interp, *objv,
TCL_REG_ADVANCED|TCL_REG_BOSONLY))) {
return TCL_ERROR;
}
goto pattern;
case EXP_SWITCH_INPUT:
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;
objc--;objv++;
if (objc < 1) {
Tcl_WrongNumArgs(interp,1,objv_copy,"-input spawn_id");
return(TCL_ERROR);
}
inp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv),
EXP_TEMPORARY,inter_updateproc);
if (!inp->i_list) return TCL_ERROR;
break;
case EXP_SWITCH_OUTPUT: {
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;
objc--;objv++;
if (objc < 1) {
Tcl_WrongNumArgs(interp,1,objv_copy,"-output spawn_id");
return(TCL_ERROR);
}
outp->i_list = exp_new_i_complex(interp,Tcl_GetString(*objv),
EXP_TEMPORARY,inter_updateproc);
if (!outp->i_list) return TCL_ERROR;
outp->action_eof = &action_eof;
action_eof_ptr = &outp->action_eof;
break;
}
case EXP_SWITCH_USER:
objc--;objv++;
if (objc < 1) {
Tcl_WrongNumArgs(interp,1,objv_copy,"-u spawn_id");
return(TCL_ERROR);
}
replace_user_by_process = *objv;
if (dash_input_count == 0) dash_input_count = 1;
break;
case EXP_SWITCH_OPPOSITE:
end_km = &input_default->keymap;
if (dash_input_count < 2) {
dash_input_count = 2;
inp = input_default;
action_eof_ptr = &inp->action_eof;
}
break;
case EXP_SWITCH_SPAWN_ID:
objc--;objv++;
chanName = *objv;
end_km = &input_default->keymap;
if (dash_input_count < 2) {
dash_input_count = 2;
inp = input_default;
action_eof_ptr = &inp->action_eof;
}
break;
case EXP_SWITCH_ECHO:
next_echo = TRUE;
break;
case EXP_SWITCH_NOBUFFER:
next_writethru = TRUE;
break;
case EXP_SWITCH_INDICES:
next_indices = TRUE;
break;
case EXP_SWITCH_RESET:
next_tty_reset = TRUE;
break;
case EXP_SWITCH_IREAD:
next_iread = TRUE;
break;
case EXP_SWITCH_IWRITE:
next_iwrite= TRUE;
break;
case EXP_SWITCH_EOF: {
struct action *action;
objc--;objv++;
expDiagLogU("-eof is deprecated, use eof\r\n");
*action_eof_ptr = action = new_action(&action_base);
action->statement = *objv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
break;
}
case EXP_SWITCH_TIMEOUT: {
int t;
struct action *action;
expDiagLogU("-timeout is deprecated, use timeout\r\n");
objc--;objv++;
if (objc < 1) {
Tcl_WrongNumArgs(interp,1,objv_copy,"-timeout time");
return(TCL_ERROR);
}
if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) {
return TCL_ERROR;
}
objc--;objv++;
if (t != -1)
arbitrary_timeout = t;
timeout_simple = FALSE;
action = inp->action_timeout = new_action(&action_base);
inp->timeout_nominal = t;
action->statement = *objv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
break;
}
case EXP_SWITCH_FAST:
case EXP_SWITCH_CAPFAST:
break;
case EXP_SWITCH_NOBRACE:
break;
}
continue;
} else {
static char *options[] = {
"eof", "timeout", "null", (char *)0
};
enum options {
EXP_OPTION_EOF, EXP_OPTION_TIMEOUT, EXP_OPTION_NULL
};
int index;
if (Tcl_GetIndexFromObj(interp, *objv, options, "option",
1 , &index) != TCL_OK) {
Tcl_ResetResult(interp);
goto pattern;
}
switch ((enum options) index) {
case EXP_OPTION_EOF: {
struct action *action;
objc--;objv++;
*action_eof_ptr = action = new_action(&action_base);
action->statement = *objv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
break;
}
case EXP_OPTION_TIMEOUT: {
int t;
struct action *action;
objc--;objv++;
if (objc < 1) {
Tcl_WrongNumArgs(interp,1,objv_copy,"timeout time");
return(TCL_ERROR);
}
if (Tcl_GetIntFromObj(interp, *objv, &t) != TCL_OK) {
return TCL_ERROR;
}
objc--;objv++;
if (t != -1) arbitrary_timeout = t;
timeout_simple = FALSE;
action = inp->action_timeout = new_action(&action_base);
inp->timeout_nominal = t;
action->statement = *objv;
action->tty_reset = next_tty_reset;
next_tty_reset = FALSE;
action->iwrite = next_iwrite;
next_iwrite = FALSE;
action->iread = next_iread;
next_iread = FALSE;
break;
}
case EXP_OPTION_NULL:
next_null = TRUE;
goto pattern;
}
continue;
}
pattern:
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;
next_indices = next_echo = next_writethru = FALSE;
next_tty_reset = FALSE;
next_iwrite = next_iread = FALSE;
km->keys = *objv;
km->null = FALSE;
km->re = 0;
if (next_re) {
km->re = TRUE;
next_re = FALSE;
}
if (next_null) {
km->null = TRUE;
next_null = FALSE;
}
objc--;objv++;
if (objc >= 1) {
km->action.statement = *objv;
} else {
km->action.statement = 0;
}
expDiagLogU("defining key ");
expDiagLogU(Tcl_GetString(km->keys));
expDiagLogU(", action ");
expDiagLogU(km->action.statement?expPrintify(Tcl_GetString(km->action.statement)):"interpreter");
expDiagLogU("\r\n");
if (dash_input_count == 0) dash_input_count = 1;
}
if (!input_user->output) {
struct output *o = new(struct output);
if (!chanName) {
if (!(esPtr = expStateCurrent(interp,1,1,0))) {
return(TCL_ERROR);
}
o->i_list = exp_new_i_simple(esPtr,EXP_TEMPORARY);
} else {
o->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName),
EXP_TEMPORARY,inter_updateproc);
if (!o->i_list) return TCL_ERROR;
}
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(expStdinoutGet(),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,
Tcl_GetString(replace_user_by_process),
EXP_TEMPORARY,inter_updateproc);
if (!input_user->i_list) return TCL_ERROR;
input_default->output->i_list = exp_new_i_complex(interp,
Tcl_GetString(replace_user_by_process),
EXP_TEMPORARY,inter_updateproc);
if (!input_default->output->i_list) return TCL_ERROR;
}
if (input_default->i_list->direct == EXP_INDIRECT) {
exp_i_update(interp,input_default->i_list);
}
if (input_default->i_list->state_list
&& (input_default->i_list->state_list->esPtr == EXP_SPAWN_ID_BAD)) {
if (!chanName) {
if (!(esPtr = expStateCurrent(interp,1,1,0))) {
return(TCL_ERROR);
}
input_default->i_list->state_list->esPtr = esPtr;
} else {
exp_free_i(interp,input_default->i_list,inter_updateproc);
input_default->i_list = exp_new_i_complex(interp,Tcl_GetString(chanName),
EXP_TEMPORARY,inter_updateproc);
if (!input_default->i_list) return TCL_ERROR;
}
}
if (input_user->i_list->direct == EXP_INDIRECT) {
exp_i_update(interp,input_user->i_list);
}
if (input_user->i_list->state_list && input_default->i_list->state_list
&& (input_user->i_list->state_list->esPtr == input_default->i_list->state_list->esPtr)) {
exp_error(interp,"cannot interact with self - set spawn_id to a spawned process");
return(TCL_ERROR);
}
esPtrs = 0;
status = update_interact_fds(interp,&input_count,&esPtrToInput,&esPtrs,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;
int rc;
int cc;
struct action *action = 0;
time_t previous_time;
time_t current_time;
int matchLen;
int skip;
int print;
int oldprinted;
int change;
int attempt_match = TRUE;
struct input *soonest_input;
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,
&esPtrToInput,&esPtrs,input_base,1,
&configure_count,&real_tty);
if (status) finish(status);
}
rc = exp_get_next_event(interp,esPtrs,input_count,&u,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;
u = soonest_input->i_list->state_list->esPtr;
}
}
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 = expStateToInput(esPtrToInput,u);
inp->timeout_remaining = inp->timeout_nominal;
switch (rc) {
case EXP_DATA_NEW:
cc = intRead(interp,u,1,0,key);
if (cc > 0) break;
rc = EXP_EOF;
case EXP_EOF:
action = inp->action_eof;
attempt_match = FALSE;
skip = expSizeGet(u);
expDiagLog("interact: received eof from spawn_id %s\r\n",u->name);
need_to_close_master = TRUE;
break;
case EXP_DATA_OLD:
cc = 0;
break;
case EXP_TIMEOUT:
action = inp->action_timeout;
attempt_match = FALSE;
skip = expSizeGet(u);
break;
}
km = 0;
if (attempt_match) {
rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo);
if ((rc == EXP_MATCH) && km && km->re) {
intRegExpMatchProcess(interp,u,km,&reInfo,skip);
}
} else {
attempt_match = TRUE;
}
if (km && km->writethru) {
print = skip + matchLen;
} else print = skip;
if (km && km->echo) {
intEcho(u,skip,matchLen);
}
oldprinted = u->printed;
if (print > u->printed) {
for (outp = inp->output;outp;outp=outp->next) {
struct exp_state_list *fdp;
for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
Tcl_GetString(u->buffer) + u->printed,
print - u->printed);
if (wc <= 0) {
expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,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,u);
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;
}
size = expSizeGet(u);
if (rc == EXP_MATCH) {
action = &km->action;
skip += matchLen;
size -= skip;
if (size) {
string = Tcl_GetString(u->buffer);
memmove(string, string + skip, size);
}
} else {
string = Tcl_GetString(u->buffer);
if (skip) {
size -= skip;
memcpy(string, string + skip, size);
}
}
Tcl_SetObjLength(u->buffer,size);
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,u);
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,u);
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;
ExpState *u;
int rc;
int cc;
struct action *action = 0;
time_t previous_time;
time_t current_time;
int matchLen, 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,expStdinoutGet());
u = esPtrs[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,esPtrs+1,input_count,&u,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 = expStateToInput(esPtrToInput,u);
switch (rc) {
case EXP_DATA_NEW:
cc = intRead(interp,u,0,0,key);
if (cc > 0) break;
case EXP_EOF:
action = inp->action_eof;
attempt_match = FALSE;
skip = expSizeGet(u);
rc = EXP_EOF;
expDiagLog("interact: child received eof from spawn_id %s\r\n",u->name);
exp_close(interp,u);
break;
case EXP_DATA_OLD:
cc = 0;
break;
}
km = 0;
if (attempt_match) {
rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo);
if ((rc == EXP_MATCH) && km && km->re) {
intRegExpMatchProcess(interp,u,km,&reInfo,skip);
}
} else {
attempt_match = TRUE;
}
if (km && km->writethru) {
print = skip + matchLen;
} else print = skip;
if (km && km->echo) {
intEcho(u,skip,matchLen);
}
oldprinted = u->printed;
if (print > u->printed) {
for (outp = inp->output;outp;outp=outp->next) {
struct exp_state_list *fdp;
for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
Tcl_GetString(u->buffer) + u->printed,
print - u->printed);
if (wc <= 0) {
expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp));
action = outp->action_eof;
te = inter_eval(interp,action,u);
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;
}
size = expSizeGet(u);
if (rc =n= EXP_MATCH) {
action = &km->action;
skip += matchLen;
size -= skip;
if (size) {
memcpy(u->buffer, u->buffer + skip, size);
}
} else {
if (skip) {
size -= skip;
memcpy(u->buffer, u->buffer + skip, size);
}
}
Tcl_SetObjLength(size);
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,u);
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
expDiagLog("fork = %d\r\n",pid);
signal(SIGCHLD,sigchld_handler);
u = esPtrs[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,esPtrs,input_count,&u,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 = expStateToInput(esPtrToInput,u);
switch (rc) {
case EXP_DATA_NEW:
cc = intRead(interp,u,0,1,key);
if (cc > 0) {
break;
} else if (cc == EXP_CHILD_EOF) {
action = inp->output->action_eof;
attempt_match = FALSE;
skip = expSizeGet(u);
rc = EXP_EOF;
expDiagLogU("interact: process died/eof\r\n");
clean_up_after_child(interp,esPtrs[1]);
break;
}
case EXP_EOF:
action = inp->action_eof;
attempt_match = FALSE;
skip = expSizeGet(u);
rc = EXP_EOF;
expDiagLogU("user sent EOF or disappeared\n\n");
break;
case EXP_DATA_OLD:
cc = 0;
break;
}
km = 0;
if (attempt_match) {
rc = intMatch(u,inp->keymap,&km,&matchLen,&skip,&reInfo);
if ((rc == EXP_MATCH) && km && km->re) {
intRegExpMatchProcess(interp,u,km,&reInfo,skip);
}
} else {
attempt_match = TRUE;
}
if (km && km->writethru) {
print = skip + matchLen;
} else print = skip;
if (km && km->echo) {
intEcho(u,skip,matchLen);
}
oldprinted = u->printed;
if (print > u->printed) {
for (outp = inp->output;outp;outp=outp->next) {
struct exp_state_list *fdp;
for (fdp = outp->i_list->state_list;fdp;fdp=fdp->next) {
int wc = expWriteBytesAndLogIfTtyU(fdp->esPtr,
Tcl_GetString(u->buffer) + u->printed,
print - u->printed);
if (wc <= 0) {
expDiagLog("interact: write on spawn id %s failed (%s)\r\n",fdp->esPtr->name,Tcl_PosixError(interp));
clean_up_after_child(interp,fdp->esPtr);
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,u);
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;
}
size = expSizeGet(u);
if (rc == EXP_MATCH) {
action = &km->action;
skip += matchLen;
size -= skip;
if (size) {
memcpy(u->buffer, u->buffer + skip, size);
}
} else {
if (skip) {
size -= skip;
memcpy(u->buffer, u->buffer + skip, size);
}
}
Tcl_SetObjLength(size);
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,u);
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,u);
if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo);
if (esPtrs) ckfree((char *)esPtrs);
if (esPtrToInput) Tcl_DeleteHashTable(esPtrToInput);
free_input(interp,input_base);
free_action(action_base);
return(status);
}
static int
inter_eval(interp,action,esPtr)
Tcl_Interp *interp;
struct action *action;
ExpState *esPtr;
{
int status;
if (action->iwrite) {
out("spawn_id",esPtr->name);
}
if (action->statement) {
status = Tcl_EvalObjEx(interp,action->statement,0);
} else {
expStdoutLogU("\r\n",1);
status = exp_interpreter(interp,(Tcl_Obj *)0);
}
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_InteractObjCmd, 0, 0, 0},
{0}};
void
exp_init_interact_cmds(interp)
Tcl_Interp *interp;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
exp_create_commands(interp,cmd_data);
tsdPtr->cmdObjReturn = Tcl_NewStringObj("return",6);
Tcl_IncrRefCount(tsdPtr->cmdObjReturn);
#if 0
tsdPtr->cmdObjInterpreter = Tcl_NewStringObj("interpreter",11);
Tcl_IncrRefCount(tsdPtr->cmdObjInterpreter);
#endif
}