/*************************************************************
 * vt100 terminal emulator - Script file support
 *
 *	v2.6 870227 DBW - bug fixes for all the stuff in v2.5
 *	v2.5 870214 DBW - more additions (see readme file)
 *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
 *	v2.3 861101 DBW - minor bug fixes
 *	v2.2 861012 DBW - more of the same
 *	v2.1 860915 DBW - new features (see README)
 *	     860901 ACS - Added BAUD, PARITY and WORD commands & handling
 *	     860823 DBW - Integrated and rewrote lots of code
 *	     860815 Steve Drew: Initial version written of SCRIPT.C
 *	v2.0 860809 DBW - Major rewrite
 *	v1.1 860720 DBW	- Switches, 80 cols, colors, bug fixes
 *	v1.0 860712 DBW	- First version released
 *
 *************************************************************/

#include "vt100.h"

struct COMMAND {
    void (*func)();
    char *cname;
    };

struct LABEL  {
    struct LABEL *next;
    char *name;
    long pos;
    };

extern long atol();

/****************  globals  needed  ******************/

char		on_string[20];       /* string to match on for on cmd    */
char 		wait_string[20];     /* string to match of for wait cmd  */
char 		golabel[20];         /* label we are looking for in goto */
char 		on_cmd[20];          /* command to execute when on matchs*/
int 		onsize;		     /* size of on_string                */
int 		waitsize;            /* size of wait_string              */
int 		onpos;               /* position in on string for search */
int 		waitpos;             /* pos in wait_string for search    */
int 		on_match;            /* flag set while doing on_cmd      */
FILE 		*sf;                 /* file pointer for script file     */
struct LABEL 	*lbase = NULL;       /* will point to first label	 */
struct LABEL 	*labels;             /* current label pointer		 */

/********************** command tables *******************************/
static struct COMMAND inicmds[] = {	/* initialization commands */
    cmd_bkg,	"bac",		/* set background color	    */
    cmd_bold,	"bol",		/* set bold color 	    */
    cmd_buf,	"buf",		/* set buffer size 	    */
    cmd_cursor,	"cur",		/* set cursor color	    */
    cmd_depth,	"dep",		/* set screen depth 	    */
    cmd_fore,	"for",		/* set foreground color	    */
    cmd_inter,	"int",		/* interlace ON/OFF	    */
    cmd_lines,	"lin",		/* num lines 		    */
    cmd_screen,	"scr",		/* Screen WB/CUST 	    */
    cmd_volume,	"vol",		/* set volume		    */
    cmd_wb,	"wb",		/* use WB colors	    */
    cmd_null,   NULL		/* mark the end of the list */
    };
static struct COMMAND scrcmds[] = {	/* script only commands */
    cmd_as,     "asc",		/* ascii send               */
    cmd_beep,	"bee",		/* Beep			    */
    cmd_cap,    "cap",		/* ascii capture on/off     */
    cmd_cd,     "cd",		/* change directory	    */
    cmd_delay,  "del",		/* delay amount of seconds  */
    cmd_goto,   "got",		/* goto label               */
    cmd_kb,	"kb",		/* kermit bye (for server)  */
    cmd_kg,     "kg",           /* kermit get file          */
    cmd_kr,     "kr",           /* kermit receive file      */
    cmd_ks,     "ks",           /* kermit send file         */
    cmd_on,     "on", 		/* on a 'string' do a cmd   */
    cmd_sb,     "sb",		/* Send a break		    */
    cmd_send,   "send",         /* send string to host      */
    cmd_wait,   "wait",         /* wait for a host string   */
    cmd_xr,     "xr",           /* xmodem receive file      */
    cmd_xs,     "xs",           /* xmodem send file         */
    cmd_null,   NULL		/* mark the end of the list */
    };
static struct COMMAND commands[]= {	/* generally available commands */
    cmd_appcur,	"app",		/* turn app. cursor on/off  */
    cmd_baud,   "bau",		/* Set Baud Rate            */
    cmd_bt,     "bre",		/* Set Break Time	    */
    cmd_conv,	"con",		/* convert bs to del	    */
    cmd_echo,	"ech",		/* turn echo on or off	    */
    cmd_exit,   "exi",		/* exit script file         */
    cmd_fnc,	"f",		/* define function key	    */
    cmd_key,	"key",		/* keyscript character	    */
    cmd_mode,	"mod",		/* KERMIT transfer mode	    */
    cmd_numkey,	"numkey",	/* turn numeric kpad on/off */
    cmd_parity, "parity",	/* Set Parity		    */
    cmd_swap,	"swap",		/* Swap BS and DEL	    */
    cmd_wrap,	"wrap",		/* turn wrap on or off	    */
    cmd_null,   NULL		/* mark the end of the list */
    };

/********************************************************************/
/* checks char to see if match with on string or wait_string        */
/* if on string match oncmd gets executed imediately,               */
/* if wait_string match script_wait is set.                         */
/********************************************************************/

chk_script(c)
char c;
    {
    if (on_string[0] != '\0') {
        if (on_string[onpos] == c) {
            onpos++;
            if (onpos == onsize) {
                on_match = TRUE;
                do_script_cmd(ONCOMMAND); 
                on_match = FALSE;
                return(0);
		}
	    }
        else onpos = 0;
	}
    if (wait_string[0] != '\0') {
       if (wait_string[waitpos] != c) {
            waitpos = 0;
            return(0);
	    }
        waitpos++;
        if (waitpos != waitsize) return(0);
        wait_string[0] = '\0';
        script_wait = FALSE;
	}
    }

script_start(file)
char *file;
    {
    if (strlen(file) == 0 || *file == '#') return(0);
    if ((sf = fopen(file, "r")) == NULL) {
        req("Can't open script file",file,0);
        return(0);
	}
    script_on = TRUE;
    script_wait = FALSE;
    wait_string[0] = '\0';
    on_string[0] = '\0';
    on_match = FALSE;
    lbase = NULL;
    }

/* return pointer to next word. set l to size of the word */

char *next_wrd(s,l)
char *s;
int *l;
    {
    char *p;

    while(*s && (*s == ' ' || *s == '\t')) s++;
    p = s;
    while(*s && (*s != ' ' && *s != '\t')) s++;
    *l = s-p;
    return(p);
    }

exe_cmd(p,l)
char *p;
int l;
    {
    register int i,l2;

    /* downcase the command */
    for (i=0; i<l; i++) p[i] |= ' ';

    /* now search for it (first in the init command list) */
    if (doing_init)
	for (i=0; inicmds[i].func != cmd_null; ++i) {
	    l2 = strlen(inicmds[i].cname);
	    if (l >= l2 && strncmp(p, inicmds[i].cname, l2) == 0) {
		(*inicmds[i].func)(next_wrd(p+l, &l));
		return(TRUE);
		}
	    }

    /* or the script command list */
    else
	for (i=0; scrcmds[i].func != cmd_null; ++i) {
	    l2 = strlen(scrcmds[i].cname);
	    if (l >= l2 && strncmp(p, scrcmds[i].cname, l2) == 0) {
		(*scrcmds[i].func)(next_wrd(p+l, &l));
		return(TRUE);
		}
	    }

    /* now search for it (in the standard command list) */
    for (i=0; commands[i].func != cmd_null; ++i) {
	l2 = strlen(commands[i].cname);
        if (l >= l2 && strncmp(p, commands[i].cname, l2) == 0) {
            (*commands[i].func)(next_wrd(p+l, &l));
            return(TRUE);
	    }
	}
    if (doing_init) {
	puts("INIT - unknown command:");
	puts(p);
	}
    else req("Script - unknown command:",p,0);

    return(FALSE);
    }

struct LABEL *find_label(lname)
char *lname;
    {
    struct LABEL *label;
  
    label = lbase;
    while(label != NULL) {
	if (strcmp(label->name, lname) == 0) return (label);
	label = label->next;
	}
    return(NULL);
    }

do_script_cmd(stat)
int stat;
    {
    int len,l;
    char line[256];
    char *p;

    /* if ON command is matched and we were     */
    /* doing a DELAY then abort the delay timer,*/
    /* except if on_cmd was just a SEND.        */
    if (stat == ONCOMMAND) {
        strcpy(line,on_cmd);
        p = next_wrd(line,&l);
	if (*p != 'S' && script_wait == WAIT_TIMER)  {
            AbortIO((char *) &Script_Timer);
            Wait (1L << Script_Timer_Port->mp_SigBit);

	    /* script will proceed after on command    */
            script_wait = FALSE;
	    }
	exe_cmd(p,l);
        return(0);
	}
    script_wait = FALSE;
    while(fgets(line,256,sf) != NULL) {
       len = strlen(line);
       line[--len] = '\0';
       p = next_wrd(&line[0], &l);
       if (*(p + l - 1) == ':') {       	/* its a label */
           *(p + l - 1) = '\0';
           if (find_label(p) == NULL) {   	/* it's a new label */
		if (lbase == NULL)  {  		/* it's the first label */
		    labels = lbase = (struct LABEL *) 
			malloc(sizeof (struct LABEL));
		    }
		else {
		    labels->next = (struct LABEL *)
			malloc(sizeof (struct LABEL));
		    labels = labels->next;
		    }
		labels->pos  = ftell(sf);
		labels->name = malloc(l);
		labels->next = NULL;
		strcpy(labels->name, p);
		if (stat == GOTOLABEL && strcmp(p, golabel) == 0) 
                      stat = NEXTCOMMAND;
		}
	    p = next_wrd(p+l+1, &l);
	    } 	/* end of it's a label */
	if (stat == GOTOLABEL || *p == '#') continue;
	if (*p) exe_cmd(p,l);
	return(0);
	} 		/* end of while */
    if (stat == GOTOLABEL) req("Script - label not found: ",golabel,0);
    exit_script();
    }

exit_script()
    {
    if (script_wait == WAIT_TIMER)      /* timer not done yet */
       AbortIO((char *) &Script_Timer); /* so abort it */
    req("Script - terminated","",0);    
    script_on = FALSE;
    script_wait = TRUE;
    fclose(sf);
    }

/* remove quotes terminate string & return pointer to start */

char *tostring(ptr)
char *ptr;
    {
    char *s1,*s2;

    s1 = ptr;
    if (*ptr == '"') {
        while(*ptr++  && *ptr != '"') ;
        if (*ptr == '"') {
            *ptr = '\0';  
            ptr = s2 = ++s1;
            while(*s2) {
		if	(*s2 != '^')	 *s1++ = *s2;
		else if (*(s2+1) == '^') *s1++ = *s2++;
		else			 *s1++ = ((*++s2)|' ')-96;
		s2++;
		}
	    *s1 = '\0';
            return(ptr);
	    }
	}
    if (*s1 == '^') {
        *s1 = (*(s1+1)|' ')-96;
        *(s1+1) = '\0';
        return(s1);
        }
    *(s1+1) = '\0';
    return(s1);
    }   

/***************************** SCRIPT COMMANDS ********************/

void cmd_goto(lname)
char *lname;
    {
    struct LABEL *label;
   	                    /* if on_cmd was a goto kill wait state */
    if (on_match) { wait_string[0] = '\0'; script_wait = FALSE; }
    if ((label = find_label(lname)) == NULL) {  /* is it forward */
        strcpy(golabel,lname);
        do_script_cmd(GOTOLABEL);
	}
    else {
        fseek(sf,(long)(label->pos),0);
	}
    }

void cmd_send(str)
char *str;
    {
    sendstring(tostring(str));
    }

void cmd_wait(str)
char *str;
    {
    str = tostring(str);
    *(str+20) = '\0';         /* 20 characters max */
    strcpy(wait_string, str);
    waitsize = strlen(str);
    script_wait = WAIT_STRING;
    }

void cmd_on(str)
char *str;
    {
   char *p;

    p = tostring(str);
    strcpy(on_string, p);
    onsize = strlen(p);
    *(p+onsize+2+20) = '\0';        /* 20 characters max */
    strcpy(on_cmd,p+onsize+2);
    }

void cmd_delay(seconds)
char *seconds;
    {
    script_wait = WAIT_TIMER;
    Script_Timer.tr_time.tv_secs = atoi(seconds);
    Script_Timer.tr_time.tv_micro = 0;
    SendIO((char *) &Script_Timer.tr_node);
    }

void cmd_exit(option)
char *option;
    {
    char *p;
    int  l;

    if (doing_init) return;

    if (*option) {
	p = next_wrd(option,&l);
	*(p+l) = '\000';
	if  (strcmp(p,"vt100") == 0 || strcmp(p,"VT100") == 0)
	    cleanup("Exit vt100 from script",0);
	exit_script();
	script_start(p);
	}
    else exit_script();
    }

void cmd_ks(file)
char *file;
    {
    multi_xfer(file, doksend, 1);
    }

void cmd_kr(file)
char *file;
    {
    multi_xfer(file, dokreceive, 0);
    }

void cmd_kg(file)
char *file;
    {
    server = TRUE;
    multi_xfer(file, dokreceive, 0);
    }

void cmd_kb()
    {
    saybye();
    }

void cmd_xs(file)
char *file;
    {
    multi_xfer(file, XMODEM_Send_File, 1);
    }

void cmd_xr(file)
char *file;
    {
    multi_xfer(file, XMODEM_Read_File, 1);
    }

void cmd_cap(file)
char *file;
    {
    do_capture(file);
    }

void cmd_as(file)
char *file;
    {
    do_send(file);
    }

void cmd_cd(name)
char *name;
    {
    set_dir(name);
    }

void cmd_sb(str)
char *str;
    {
    sendbreak();
    }

void cmd_baud(rate)
char *rate;
    {
    int i = atoi(rate);

    switch( i )
	{
	case  300:
	case 1200:
	case 2400:
	case 4800:
	case 9600:
	if (doing_init) p_baud = i;
	else	        setserbaud(i, TRUE);
	break;

	default:
	if (doing_init) {
	    puts("INIT - invalid baud rate:");
	    puts(rate);
	    }
	else req("Script - invalid baud rate: ",rate,0);
	break;
	}
    }

void cmd_parity(par)
char *par;
    {
    int i;

    switch( *par|' ' )
	{
	case 'n': i =  0; break;
	case 'm': i =  1; break;
	case 's': i =  2; break;
	case 'e': i =  3; break;
	case 'o': i =  4; break;

	default:
	if (doing_init) {
	    puts("INIT - invalid parity:");
	    puts(par);
	    }
	else req("Script - invalid parity: ",par,0);
	return;
	}
    p_parity = i;
    if (doing_init) return;

    ClearMenuStrip( mywindow );         /* Remove old menu */
    InitCommItems();                    /* Re-do comm menu   */
    SetMenuStrip(mywindow,&menu[0]);    /* Re-display the menu */	
    }

void cmd_bt(breaklength)
char *breaklength;
    {
    p_break = atol(breaklength);
    if (doing_init) return;

    AbortIO(Read_Request);
    Read_Request->io_BrkTime = Write_Request->io_BrkTime = p_break;
    setparams();
    }

void cmd_mode(tmode)
char *tmode;
    {
    switch (*tmode|' ') {
	case 'i':
	p_mode = 0;
	break;

	case 'c':
	p_mode = 1;
	break;

	default:
	if (doing_init) {
	    puts("INIT - invalid transfer mode: ");
	    puts(tmode);
	    }
	else req("Script - invalid transfer mode: ",tmode,0);
	return;
	}
    if (doing_init) return;

    ClearMenuStrip(mywindow);
    InitCommItems();                    /* Re-do comm menu   */
    SetMenuStrip(mywindow,&menu[0]);
    }

void cmd_beep(dummy)
char	*dummy;
    {
    if (p_volume == 0) DisplayBeep(NULL);
    else {
	BeginIO(&Audio_Request);
	WaitIO(&Audio_Request);
	}
    }

void setvar(par,typ,var)
char	*par;
int	typ,*var;
    {
    int	i;

    switch (typ) {
	case 0: /* ON/OFF or YES/NO */
	case 1: /* not case */
	if ((par[1]|' ') == 'n' || (par[0]|' ') == 'y') *var = 1-typ;
	else						*var = typ;
	break;

	case 2: /* read hex number */
	if (sscanf(par,"%x",&i) == 1) *var = i;
	    
	break;

	case 3: /* read decimal number */
	if (sscanf(par,"%d",&i) == 1) *var = i;
	break;
	}
    }

void cmd_echo(par)
char	*par;
    {
    setvar(par,0,&p_echo);
    if (doing_init == 0) redoutil();
    }

void cmd_wrap(par)
char	*par;
    {
    setvar(par,0,&p_wrap);
    if (doing_init == 0) redoutil();
    }

void cmd_numkey(par)
char	*par;
    {
    setvar(par,1,&p_keyapp);
    if (doing_init == 0) redoutil();
    }

void cmd_appcur(par)
char	*par;
    {
    setvar(par,0,&p_curapp);
    if (doing_init == 0) redoutil();
    }

void cmd_swap(par)
char	*par;
    {
    setvar(par,0,&p_bs_del);
    if (doing_init == 0) redoutil();
    }

void cmd_bkg(par)
char	*par;
    {
    setvar(par,2,&p_background);
    }

void cmd_bold(par)
char	*par;
    {
    setvar(par,2,&p_bold);
    }

void cmd_buf(par)
char	*par;
    {
    setvar(par,3,&p_buffer);
    }

void cmd_cursor(par)
char	*par;
    {
    setvar(par,2,&p_cursor);
    }

void cmd_depth(par)
char	*par;
    {
    setvar(par,3,&p_depth);
    }

void cmd_fore(par)
char	*par;
    {
    setvar(par,2,&p_foreground);
    }

void cmd_inter(par)
char	*par;
    {
    setvar(par,0,&p_interlace);
    }

void cmd_lines(par)
char	*par;
    {
    setvar(par,3,&p_lines);
    }

void cmd_screen(par)
char	*par;
    {
    if ((par[0]|' ') == 'w') p_screen = 0;
    else		     p_screen = 1;
    }

void cmd_wb(par)
char	*par;
    {
    setvar(par,0,&p_wbcolors);
    }

void cmd_key(par)
char	*par;
    {
    int	i;

    if (sscanf(par,"%x",&i) == 1) p_keyscript = (char)(i & 0x7f);
    }

void cmd_volume(par)
char	*par;
    {
    setvar(par,3,&p_volume);
    }

void cmd_conv(par)
char	*par;
    {
    setvar(par,0,&p_bs_del);
    if (doing_init == 0) redoutil();
    }

void cmd_fnc(par)
char	*par;
    {
    char    *s;
    int	    l;
    int	    i = atoi(par);

    s = par;
    if (*s) s = next_wrd(s,&l);		/* skip key number */
    if (*s) s = next_wrd(s+l+1,&l);	/* point at desired string */
    if (*s) s = tostring(s);		/* convert the string */
    if (*s && i > 0 && i < 21) {
	if (i > 10) {
	    p_F[i-11] = malloc(strlen(s)+1);
	    strcpy(p_F[i-11],s);
	    }
	else {
	    p_f[i-1] = malloc(strlen(s)+1);
	    strcpy(p_f[i-1],s);
	    }
	}
    }

void cmd_null(dummy)
char *dummy;
    {
    }

