#include "expect_cf.h"
#include <stdio.h>
#include <errno.h>
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include "tcl.h"
#include "tclInt.h"
#include "exp_rename.h"
#include "exp_prog.h"
#include "exp_command.h"
#include "exp_tty_in.h"
#include "exp_log.h"
#include "exp_event.h"
#ifdef TCL_DEBUGGER
#include "tcldbg.h"
#endif
#ifdef __CENTERLINE__
#undef EXP_VERSION
#define EXP_VERSION "5.0.3"
#undef SCRIPTDIR
#define SCRIPTDIR "example/"
#undef EXECSCRIPTDIR
#define EXECSCRIPTDIR "example/"
#endif
char exp_version[] = EXP_VERSION;
#define NEED_TCL_MAJOR 7
#define NEED_TCL_MINOR 5
char *exp_argv0 = "this program";
void (*exp_app_exit)() = 0;
void (*exp_event_exit)() = 0;
FILE *exp_cmdfile = 0;
char *exp_cmdfilename = 0;
int exp_cmdlinecmds = FALSE;
int exp_interactive = FALSE;
int exp_buffer_command_input = FALSE;
int exp_fgets();
Tcl_Interp *exp_interp;
int exp_tcl_debugger_available = FALSE;
int exp_getpid;
static void
usage(interp)
Tcl_Interp *interp;
{
char buffer [] = "exit 1";
expErrorLog("usage: expect [-div] [-c cmds] [[-f] cmdfile] [args]\r\n");
Tcl_Eval(interp, buffer);
}
static
void
exp_pty_exit_for_tcl(clientData)
ClientData clientData;
{
exp_pty_exit();
}
static
void
exp_init_pty_exit()
{
Tcl_CreateExitHandler(exp_pty_exit_for_tcl,(ClientData)0);
}
void
exp_exit_handlers(clientData)
ClientData clientData;
{
extern int exp_forked;
Tcl_Interp *interp = (Tcl_Interp *)clientData;
static int did_app_exit = FALSE;
static int did_expect_exit = FALSE;
if (!did_expect_exit) {
did_expect_exit = TRUE;
if (exp_onexit_action) {
int result = Tcl_GlobalEval(interp,exp_onexit_action);
if (result != TCL_OK) Tcl_BackgroundError(interp);
}
} else {
expDiagLogU("onexit handler called recursively - forcing exit\r\n");
}
if (exp_app_exit) {
if (!did_app_exit) {
did_app_exit = TRUE;
(*exp_app_exit)(interp);
} else {
expDiagLogU("application exit handler called recursively - forcing exit\r\n");
}
}
if (!exp_disconnected
&& !exp_forked
&& (exp_dev_tty != -1)
&& isatty(exp_dev_tty)
&& exp_ioctled_devtty) {
exp_tty_set(interp,&exp_tty_original,exp_dev_tty,0);
}
exp_close_all(interp);
}
static int
history_nextid(interp)
Tcl_Interp *interp;
{
static int nextid = 0;
CONST char *nextidstr = Tcl_GetVar2(interp,"tcl::history","nextid",0);
if (nextidstr) {
(void) sscanf(nextidstr,"%d",&nextid);
}
return ++nextid;
}
static char prompt1[] = "prompt1";
static char prompt2[] = "prompt2";
static char *prompt2_default = "+> ";
static char prompt1_default[] = "expect%d.%d> ";
int
Exp_Prompt1Cmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
static char buffer[200];
Interp *iPtr = (Interp *)interp;
sprintf(buffer,prompt1_default,iPtr->numLevels,history_nextid(interp));
Tcl_SetResult(interp,buffer,TCL_STATIC);
return(TCL_OK);
}
int
Exp_Prompt2Cmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
Tcl_SetResult(interp,prompt2_default,TCL_STATIC);
return(TCL_OK);
}
static int
ignore_procs(interp,s)
Tcl_Interp *interp;
char *s;
{
return ((s[0] == 'p') &&
(s[1] == 'r') &&
(s[2] == 'o') &&
(s[3] == 'm') &&
(s[4] == 'p') &&
(s[5] == 't') &&
((s[6] == '1') ||
(s[6] == '2')) &&
(s[7] == '\0')
);
}
static void
handle_eval_error(interp,check_for_nostack)
Tcl_Interp *interp;
int check_for_nostack;
{
char *msg;
msg = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
if (!msg) msg = interp->result;
else if (check_for_nostack) {
if (0 == strncmp("-nostack",msg,8)) return;
if (exp_nostack_dump) {
exp_nostack_dump = FALSE;
return;
}
}
expErrorLogU(exp_cook(msg,(int *)0));
expErrorLogU("\r\n");
}
int
exp_interpreter(interp,eofObj)
Tcl_Interp *interp;
Tcl_Obj *eofObj;
{
Tcl_Obj *commandPtr = NULL;
int code;
int gotPartial;
Interp *iPtr = (Interp *)interp;
int tty_changed = FALSE;
exp_tty tty_old;
int was_raw, was_echo;
Tcl_Channel inChannel, outChannel;
ExpState *esPtr = expStdinoutGet();
expect_key++;
commandPtr = Tcl_NewObj();
Tcl_IncrRefCount(commandPtr);
gotPartial = 0;
while (TRUE) {
outChannel = expStdinoutGet()->channel;
if (outChannel) {
Tcl_Flush(outChannel);
}
if (!esPtr->open) {
code = EXP_EOF;
goto eof;
}
tty_changed = exp_tty_cooked_echo(interp,&tty_old,&was_raw,&was_echo);
if (!gotPartial) {
code = Tcl_Eval(interp,prompt1);
if (code == TCL_OK) {
expStdoutLogU(Tcl_GetStringResult(interp),1);
}
else expStdoutLog(1,prompt1_default,iPtr->numLevels,history_nextid(interp));
} else {
code = Tcl_Eval(interp,prompt2);
if (code == TCL_OK) {
expStdoutLogU(Tcl_GetStringResult(interp),1);
}
else expStdoutLogU(prompt2_default,1);
}
esPtr->force_read = 1;
code = exp_get_next_event(interp,&esPtr,1,&esPtr,EXP_TIME_INFINITY,
esPtr->key);
if (code != EXP_EOF) {
inChannel = expStdinoutGet()->channel;
code = Tcl_GetsObj(inChannel, commandPtr);
#ifdef SIMPLE_EVENT
if (code == -1 && errno == EINTR) {
if (Tcl_AsyncReady()) {
(void) Tcl_AsyncInvoke(interp,TCL_OK);
}
continue;
}
#endif
if (code < 0) code = EXP_EOF;
if ((code == 0) && Tcl_Eof(inChannel) && !gotPartial) code = EXP_EOF;
}
eof:
if (code == EXP_EOF) {
if (eofObj) {
code = Tcl_EvalObjEx(interp,eofObj,0);
} else {
code = TCL_OK;
}
goto done;
}
expDiagWriteObj(commandPtr);
if (expLogChannelGet()) {
Tcl_WriteObj(expLogChannelGet(),commandPtr);
}
Tcl_AppendToObj(commandPtr, "\n", 1);
if (!TclObjCommandComplete(commandPtr)) {
gotPartial = 1;
continue;
}
Tcl_AppendToObj(commandPtr, "\n", 1);
if (!TclObjCommandComplete(commandPtr)) {
gotPartial = 1;
continue;
}
gotPartial = 0;
if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo);
code = Tcl_RecordAndEvalObj(interp, commandPtr, 0);
Tcl_SetObjLength(commandPtr, 0);
switch (code) {
char *str;
case TCL_OK:
str = Tcl_GetStringResult(interp);
if (*str != 0) {
expStdoutLogU(exp_cook(str,(int *)0),1);
expStdoutLogU("\r\n",1);
}
continue;
case TCL_ERROR:
handle_eval_error(interp,1);
continue;
#define finish(x) {code = x; goto done;}
case TCL_BREAK:
case TCL_CONTINUE:
finish(code);
case EXP_TCL_RETURN:
finish(TCL_RETURN);
case TCL_RETURN:
finish(TCL_OK);
default:
expErrorLog("error %d: ",code);
expErrorLogU(Tcl_GetString(Tcl_GetObjResult(interp)));
expErrorLogU("\r\n");
continue;
}
}
done:
if (tty_changed) exp_tty_set(interp,&tty_old,was_raw,was_echo);
Tcl_DecrRefCount(commandPtr);
return(code);
}
int
Exp_ExpVersionCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int emajor, umajor;
char *user_version;
if (argc == 1) {
Tcl_SetResult(interp,exp_version,TCL_STATIC);
return(TCL_OK);
}
if (argc > 3) {
exp_error(interp,"usage: expect_version [[-exit] version]");
return(TCL_ERROR);
}
user_version = argv[argc==2?1:2];
emajor = atoi(exp_version);
umajor = atoi(user_version);
if (emajor == umajor) {
int u, e;
char *dot = strchr(user_version,'.');
if (!dot) {
exp_error(interp,"version number must include a minor version number");
return TCL_ERROR;
}
u = atoi(dot+1);
dot = strchr(exp_version,'.');
e = atoi(dot+1);
if (e >= u) return(TCL_OK);
}
if (argc == 2) {
exp_error(interp,"%s requires Expect version %s (but using %s)",
exp_argv0,user_version,exp_version);
return(TCL_ERROR);
}
expErrorLog("%s requires Expect version %s (but is using %s)\r\n",
exp_argv0,user_version,exp_version);
{
char buffer [] = "exit 1";
Tcl_Eval(interp, buffer);
}
}
static char init_auto_path[] = "\
if {$exp_library != \"\"} {\n\
lappend auto_path $exp_library\n\
}\n\
if {$exp_exec_library != \"\"} {\n\
lappend auto_path $exp_exec_library\n\
}";
int
Expect_Init(interp)
Tcl_Interp *interp;
{
static int first_time = TRUE;
if (first_time) {
int tcl_major = atoi(TCL_VERSION);
char *dot = strchr(TCL_VERSION,'.');
int tcl_minor = atoi(dot+1);
if (tcl_major < NEED_TCL_MAJOR ||
(tcl_major == NEED_TCL_MAJOR && tcl_minor < NEED_TCL_MINOR)) {
sprintf(interp->result,
"%s compiled with Tcl %d.%d but needs at least Tcl %d.%d\n",
exp_argv0,tcl_major,tcl_minor,
NEED_TCL_MAJOR,NEED_TCL_MINOR);
return TCL_ERROR;
}
}
if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
if (Tcl_PkgProvide(interp, "Expect", EXP_VERSION) != TCL_OK) {
return TCL_ERROR;
}
Tcl_Preserve(interp);
Tcl_CreateExitHandler(Tcl_Release,(ClientData)interp);
if (first_time) {
exp_getpid = getpid();
exp_init_pty();
exp_init_pty_exit();
exp_init_tty();
exp_init_stdio();
exp_init_sig();
exp_init_event();
exp_init_trap();
exp_init_unit_random();
exp_init_spawn_ids(interp);
expChannelInit();
expDiagInit();
expLogInit();
expDiagLogPtrSet(expDiagLogU);
expErrnoMsgSet(Tcl_ErrnoMsg);
Tcl_CreateExitHandler(exp_exit_handlers,(ClientData)interp);
first_time = FALSE;
}
exp_interp = interp;
exp_init_most_cmds(interp);
exp_init_expect_cmds(interp);
exp_init_main_cmds(interp);
exp_init_trap_cmds(interp);
exp_init_tty_cmds(interp);
exp_init_interact_cmds(interp);
exp_init_spawn_id_vars(interp);
expExpectVarsInit();
if (Tcl_GetVar(interp, "expect_library", TCL_GLOBAL_ONLY) == NULL) {
Tcl_SetVar(interp,"expect_library",SCRIPTDIR,0);
}
if (Tcl_GetVar(interp, "exp_library", TCL_GLOBAL_ONLY) == NULL) {
Tcl_SetVar(interp,"exp_library",SCRIPTDIR,0);
}
if (Tcl_GetVar(interp, "exp_exec_library", TCL_GLOBAL_ONLY) == NULL) {
Tcl_SetVar(interp,"exp_exec_library",EXECSCRIPTDIR,0);
}
Tcl_Eval(interp,init_auto_path);
Tcl_ResetResult(interp);
#ifdef TCL_DEBUGGER
Dbg_IgnoreFuncs(interp,ignore_procs);
#endif
return TCL_OK;
}
static char sigexit_init_default[] = "trap exit {SIGINT SIGTERM}";
static char debug_init_default[] = "trap {exp_debug 1} SIGINT";
void
exp_parse_argv(interp,argc,argv)
Tcl_Interp *interp;
int argc;
char **argv;
{
char argc_rep[10];
int sys_rc = TRUE;
int my_rc = TRUE;
int c;
int rc;
extern int optind;
extern char *optarg;
char *args;
char *debug_init;
exp_argv0 = argv[0];
#ifdef TCL_DEBUGGER
Dbg_ArgcArgv(argc,argv,1);
#endif
Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
Tcl_Eval(interp,sigexit_init_default);
while ((c = getopt(argc, argv, "+b:c:dD:f:inN-v")) != EOF) {
switch(c) {
case '-':
goto abort_getopt;
case 'c':
exp_cmdlinecmds = TRUE;
rc = Tcl_Eval(interp,optarg);
if (rc != TCL_OK) {
expErrorLogU(exp_cook(Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY),(int *)0));
expErrorLogU("\r\n");
}
break;
case 'd': expDiagToStderrSet(TRUE);
expDiagLog("expect version %s\r\n",exp_version);
break;
#ifdef TCL_DEBUGGER
case 'D':
exp_tcl_debugger_available = TRUE;
if (Tcl_GetInt(interp,optarg,&rc) != TCL_OK) {
expErrorLog("%s: -D argument must be 0 or 1\r\n",exp_argv0);
{
char buffer [] = "exit 1";
Tcl_Eval(interp, buffer);
}
}
if (0 == (debug_init = getenv("EXPECT_DEBUG_INIT"))) {
debug_init = debug_init_default;
}
Tcl_Eval(interp,debug_init);
if (rc == 1) Dbg_On(interp,0);
break;
#endif
case 'f':
exp_cmdfilename = optarg;
break;
case 'b':
exp_cmdfilename = optarg;
exp_buffer_command_input = TRUE;
break;
case 'i':
exp_interactive = TRUE;
break;
case 'n':
my_rc = FALSE;
break;
case 'N':
sys_rc = FALSE;
break;
case 'v':
printf("expect version %s\n", exp_version);
{
char buffer [] = "exit 0";
Tcl_Eval(interp, buffer);
}
break;
default: usage(interp);
}
}
abort_getopt:
for (c = 0;c<argc;c++) {
expDiagLog("argv[%d] = ",c);
expDiagLogU(argv[c]);
expDiagLogU(" ");
}
expDiagLogU("\r\n");
if (!exp_interactive) {
if (!exp_cmdfilename && (optind < argc)) {
exp_cmdfilename = argv[optind];
optind++;
if ((optind < argc) &&
(0 == strcmp ("--", argv[optind]))) {
optind++;
}
}
if (exp_cmdfilename) {
if (streq(exp_cmdfilename,"-")) {
exp_cmdfile = stdin;
exp_cmdfilename = 0;
} else if (exp_buffer_command_input) {
errno = 0;
exp_cmdfile = fopen(exp_cmdfilename,"r");
if (exp_cmdfile) {
exp_cmdfilename = 0;
expCloseOnExec(fileno(exp_cmdfile));
} else {
CONST char *msg;
if (errno == 0) {
msg = "could not read - odd file name?";
} else {
msg = Tcl_ErrnoMsg(errno);
}
expErrorLog("%s: %s\r\n",exp_cmdfilename,msg);
{
char buffer [] = "exit 1";
Tcl_Eval(interp, buffer);
}
}
}
} else if (!exp_cmdlinecmds) {
if (isatty(0)) {
exp_interactive = TRUE;
} else {
exp_cmdfile = stdin;
}
}
}
if (exp_interactive) {
Tcl_SetVar(interp, "tcl_interactive","1",TCL_GLOBAL_ONLY);
}
sprintf(argc_rep,"%d",argc-optind);
Tcl_SetVar(interp,"argc",argc_rep,0);
expDiagLog("set argc %s\r\n",argc_rep);
if (exp_cmdfilename) {
Tcl_SetVar(interp,"argv0",exp_cmdfilename,0);
expDiagLog("set argv0 \"%s\"\r\n",exp_cmdfilename);
} else {
Tcl_SetVar(interp,"argv0",exp_argv0,0);
expDiagLog("set argv0 \"%s\"\r\n",exp_argv0);
}
args = Tcl_Merge(argc-optind,argv+optind);
expDiagLogU("set argv \"");
expDiagLogU(args);
expDiagLogU("\"\r\n");
Tcl_SetVar(interp,"argv",args,0);
Tcl_Free(args);
exp_interpret_rcfiles(interp,my_rc,sys_rc);
}
void
exp_interpret_rcfiles(interp,my_rc,sys_rc)
Tcl_Interp *interp;
int my_rc;
int sys_rc;
{
int rc;
if (sys_rc) {
char file[200];
int fd;
sprintf(file,"%s/expect.rc",SCRIPTDIR);
if (-1 != (fd = open(file,0))) {
if (TCL_ERROR == (rc = Tcl_EvalFile(interp,file))) {
expErrorLog("error executing system initialization file: %s\r\n",file);
if (rc != TCL_ERROR)
expErrorLog("Tcl_Eval = %d\r\n",rc);
if (*interp->result != 0) {
expErrorLogU(interp->result);
expErrorLogU("\r\n");
}
{
char buffer [] = "exit 1";
Tcl_Eval(interp, buffer);
}
}
close(fd);
}
}
if (my_rc) {
char file[200];
char *home;
int fd;
char *getenv();
if ((NULL != (home = getenv("DOTDIR"))) ||
(NULL != (home = getenv("HOME")))) {
sprintf(file,"%s/.expect.rc",home);
if (-1 != (fd = open(file,0))) {
if (TCL_ERROR == (rc = Tcl_EvalFile(interp,file))) {
expErrorLog("error executing file: %s\r\n",file);
if (rc != TCL_ERROR)
expErrorLog("Tcl_Eval = %d\r\n",rc);
if (*interp->result != 0) {
expErrorLogU(interp->result);
expErrorLogU("\r\n");
}
{
char buffer [] = "exit 1";
Tcl_Eval(interp, buffer);
}
}
close(fd);
}
}
}
}
int
exp_interpret_cmdfilename(interp,filename)
Tcl_Interp *interp;
char *filename;
{
int rc;
expDiagLog("executing commands from command file %s\r\n",filename);
Tcl_ResetResult(interp);
if (TCL_OK != (rc = Tcl_EvalFile(interp,filename))) {
Tcl_AddErrorInfo(interp, "");
handle_eval_error(interp,0);
}
return rc;
}
int
exp_interpret_cmdfile(interp,fp)
Tcl_Interp *interp;
FILE *fp;
{
int rc = 0;
int gotPartial;
int eof;
Tcl_DString dstring;
Tcl_DStringInit(&dstring);
expDiagLogU("executing commands from command file\r\n");
gotPartial = 0;
eof = FALSE;
while (1) {
char line[BUFSIZ];
char *ccmd;
if (fgets(line,BUFSIZ,fp) == NULL) {
if (!gotPartial) break;
eof = TRUE;
}
ccmd = Tcl_DStringAppend(&dstring,line,-1);
if (!Tcl_CommandComplete(ccmd) && !eof) {
gotPartial = 1;
continue;
}
gotPartial = 0;
rc = Tcl_Eval(interp,ccmd);
Tcl_DStringFree(&dstring);
if (rc != TCL_OK) {
handle_eval_error(interp,0);
break;
}
if (eof) break;
}
Tcl_DStringFree(&dstring);
return rc;
}
static struct exp_cmd_data cmd_data[] = {
{"exp_version", exp_proc(Exp_ExpVersionCmd), 0, 0},
{"prompt1", exp_proc(Exp_Prompt1Cmd), 0, EXP_NOPREFIX},
{"prompt2", exp_proc(Exp_Prompt2Cmd), 0, EXP_NOPREFIX},
{0}};
void
exp_init_main_cmds(interp)
Tcl_Interp *interp;
{
exp_create_commands(interp,cmd_data);
}