#include <ascii.h>
#include "fido.h"
#include "fidomem.h"
#include <xfbuf.h>

/* 

Script file processor. This processes characters from a text file
and provides a character stream for output to the modem, and performs
functions on this incoming text stream. 

*/

char *cpyscrarg();

#define MAXLEVEL 10	/* maximum call depth */
#define MAXARG 8	/* max. arguments (&1 - &8) */
#define MAXSIZE 32	/* max. size of string registers */

/*
Our machine description:

&A		the numeric accumulator, runs 0 - 65535
&C		a timer that continuously increments by seconds
&T		the trap register, script execution time limit
&E		the boolean register, set by various instructions (READ ONLY)
&S		the character shift register from the input stream (READ ONLY)
&1 - &8		general purpose registers
*/

static int accum;		/* the accumulator */
static char sr[MAXSIZE];	/* modem stream string compare shift register */
static char error;		/* the error register */

static scrfile = -1;		/* opened file */
static scrfn[SS];		/* its name */

static long trap_reg;		/* trap time register */
static char trap_label[SS];	/* trap label */
static int trap_level;		/* trap subroutine level */

static char clkflag = 0;	/* to get it started first time (KLUDGE!) */
static long clk_tick;		/* millisecond counter */

static long chr_timer;		/* character output time */

static unsigned throttle = 10;	/* character output speed */
static long char_tick;		/* character output timer */

struct _stk {
	int file;		/* file handle, */
	char fname[SS];		/* file name, */
	char arg[MAXARG][SS];	/* the string registers */
};
static struct _stk *stk;	/* the stack itself, */
static int level = 0;		/* call/return levels */

static char recog[MAXARG];	/* the recognizer */
static char sreg[MAXARG][MAXLEVEL]; /* the data stack */
static int slevel;		/* data-stack pointer, */

static char scrtext[SS] = "";	/* buffer for text output */
static char *scrptr = scrtext;	/* pointer to the text */

static int scrkdel;		/* counter to slow keyboard poll */
static int scrunatt;		/* 1 == unattended mode */

/* Return true if there is a script file running. */

isscript() {
	return(level);
}

/* If there is a character waiting, return it. */

getscr() {

	if (*scrptr) {
		if (char_tick > throttle) {	/* issue characters no faster */
			char_tick= 0L;		/* than every N mS */
			return(*scrptr++);
		}
	}
	return(NUL);
}

/* Put a string in the buffer for output to the modem. Dont overflow
the buffer. */

static scrset(s)
char *s;
{
int n;

	strcpy(scrtext,scrptr);		/* copy remaining text to buff start */
	n= strlen(scrtext);		/* to increase avail. room */
	scrptr= &scrtext[n];
	while (++n < sizeof(scrtext)) {	/* add new text to the end */
		if (! *s) break;	/* but dont exceed buff size */
		*scrptr++= *s++;
	}
	*scrptr= NUL;
	scrptr= scrtext;		/* point to next char */
}

/* Check to see if the trap has gone off; if it has, pass control to
the previously specified label in the correct subroutine. */

static check_trap() {

	if (trap_reg != 0L) {			/* if the trap is set, */
		if (clk_tick >= trap_reg) {	/* and has gone off, */
			trap_reg= 0;		/* disable it, */
			while (level > trap_level)
				doret("");
			dojmp(trap_label);
		}
	}
}

/* Open a script file, try the current directory first, then look
through the path. Return the handle or -1 if error. */

static openp(fn,mode)
char *fn;
int mode;
{
int f;
char *cp,buff[SS];
char *getenv();

	if ((f= open(fn,mode)) != -1) 	/* if in current directory, */
		return(f);		/* just return it */

	cp= getenv("PATH"); 		/* else find the PATH, */
	if ((int)cp == 0) cp= "\\";	/* if none, use the root */

	while (*cp) {			/* try each path, */
		cpyarg(buff,cp);	/* copy one pathname, */
		if (buff[strlen(buff) - 1] != '\\')
			strcat(buff,"\\"); /* add a \ if none in the path */
		strcat(buff,fn);	/* add the filename, */
		f= open(buff,mode);	/* try to open, */
		if (f != -1) return(f);	/* return the open handle, */
		cp= next_arg(cp);	/* next ... */
	}
	return(-1);
}


/* Output a character to the modem. */

static send_kc() {
char c;

	if (c) modout(c);		/* output any chars, */
}

/* Check for incoming characters, clear the character counter
and shift them in. */

static check_mc() {

	if (modstat()) {		/* if a character available, */
		chr_timer= 0L;		/* clear the time-since counter, */
		shiftin(modin(0));	/* shift it */
	}
}

/* Display a message */

static mesg(m)
char *m;
{
char buff[SS];

	clputs(" $ ");
	_fspr(buff,0,&m);				/* format it, */
	clputs(buff);
	clputs("\r\n");
}

/* Execute the given script file. Return 1 if connected OK, 0 if not
connected and -1 if error. */

script(fn)
char *fn;
{
char ln[256],c;
int h1,h2,h3;
int l;
	
	trap_reg= 0;				/* turn off the trap */
	scrunatt= 1;				/* assume unattended */
	stk= (struct _stk *)text;		/* use this for work */
	stk-> file= -1;				/* no file open */
	trap_reg= 0;				/* turn off the trap */
	slevel= 0;				/* stack is empty */
	clrsr();				/* clear the shift register */
	doclose("");				/* close any open file */

	docall(fn);				/* start the script */
	if (level) mesg("Running script %s",fn);
	else return(-1);			/* something went wrong */

	l= limit; limit= 0;			/* disable the time limit */
	h1= set_tick(&clk_tick);		/* start the tickers running */
	h2= set_tick(&char_tick);
	h3= set_tick(&chr_timer);

	while (level) {
		abort_scr();			/* check manual abort */
		check_trap();			/* check trap timer */
		if (rline(stk-> file,ln,sizeof(ln))) /* read a line */
			script_line(ln);	/* if empty file, */
		else doret("");			/* close and return */
	}
	clr_tick(h1);
	clr_tick(h2);
	clr_tick(h3);
	limit= l;				/* restore the orig. limit */

	switch (error) {			/* convert the error */
		case 0: return(1);		/* connected */
		case 1: return(0);		/* not connected */
		default: return(-1);		/* error! */
	}
}

/* Process one line in the script file. */

script_line(ln)
char *ln;
{
char buff[SS];
char *ra;		/* raw instruction arguments */
char fa[SS];		/* first expanded arg */
char na[SS];		/* next expanded arg */
char c,*ip;
int n;
unsigned a;			/* ASSUMED TO BE AT LEAST TWO CHARS WIDE!!! */

	while (delim(*ln)) {		/* skip delimiters */
		if (*ln == ';') return;	/* ignore comment lines */
		++ln;
	}
	if (! *ln) return;		/* and blank lines */
	if (*ln == ':') return;		/* ignore labels */

	ip= ln;				/* pointer to the instruction */
	a= tolower(*ip) << 8;		/* convert first two chars */
	a |= tolower(ip[1]);		/* into a word for switch() */

	ln= next_arg(ln);		/* skip the instruction */
	ra= ln;				/* ra == raw arguments */
	ln= cpyscrarg(buff,ln);
	scrmac(fa,buff);		/* fa == first argument */
	n= atoi(fa);			/*  n == numeric first arg */
	cpyscrarg(buff,skip_delim(ln));
	scrmac(na,buff);		/* na == next argument */

	cmdflush();			/* flush extraneous keybd input */

	switch (a) {

	case 'pu': dopush(fa); break;			/* PUSH string */
	case 'po': dopop(ra); break;			/* POP register */

	case 'fi': dofiles(fa); break;			/* FILES filespec */
	case 'un': scrunatt= n; break;			/* UNATTENDED n */
	case 'th': if (n <= 500) throttle= n; break;	/* THROTTLE n */
	case 'br': mbreak(); break;			/* BREAK */
	case 'dt': lower_dtr(); delay(50); raise_dtr(); break; /* DTR */
	case 'cd': cd_bit= n; break;			/* CD-BIT n */
	case 'io': uninit(); iodev= n - 1; init();	/* IO-PORT n */
		baud(linkrate); setbaud(datarate); break;

	case 'fo': doopen(fa); break;			/* FOPEN filename */
	case 'fn': docreat(fa); break;			/* FNEW filename */
	case 'fc': doclose(fa); break;			/* FCLOSE */
	case 'fr': doread(fa); break;			/* FREAD */
	case 'fw': dowrite(fa); break;			/* FWRITE */

	case 'de': dodelay(n); break;			/* DELAY milliseconds */
	case 'qu': doquiet(n); break;			/* QUIET millisecs */
	case 'sa': if (c= getscr()) send_kc(c);		/* SAMPLE */
		check_mc(); break;

	case 'me': domesg(fa); break;			/* MESSAGE message */
	case 'ou': scrset(fa); break;			/* OUTPUT string */
	case 'co': for (ln= fa; *ln; ) modout(*ln++); break; /* COPY string */

	case 'ca': docall(ra); break;			/* CALL scriptfile */
	case 're': doret(fa); return;			/* RETURN value */
	case 'tr': dotrap(fa); break;			/* TRAP label */

	case '&r': setpat(recog,fa); break;		/* &R= pattern */
	case '&s': clrsr(); for (ln= fa; *ln; ++ln) 	/* &S= pattern */
			shiftin(*ln); break;
	case '&1': setpat(stk-> arg[0],fa); break;	/* &1= pattern */
	case '&2': setpat(stk-> arg[1],fa); break;	/* &2= pattern */
	case '&3': setpat(stk-> arg[2],fa); break;	/* &3= pattern */
	case '&4': setpat(stk-> arg[3],fa); break;	/* &4= pattern */
	case '&5': setpat(stk-> arg[4],fa); break;	/* &5= pattern */
	case '&6': setpat(stk-> arg[5],fa); break;	/* &6= pattern */
	case '&7': setpat(stk-> arg[6],fa); break;	/* &7= pattern */
	case '&8': setpat(stk-> arg[7],fa); break;	/* &8= pattern */

	case 'ma': domatch(fa,na); break;		/* MATCH pattern limit */
	case 'if': setpat(buff,fa); 
		if (match(buff)) dojmp(na); break;	/* IF pattern label */

	case '&a': accum= n; break;			/* &A= value */
	case '&b': if (setbaud(n)) datarate= n; break;	/* &B= value */
	case '&c': clk_tick= 1000L * n; break;		/* &C= value */
	case '&e': error= (n ? 1 : 0); break;		/* &E= value */
	case '&t': trap_reg= n * 1000L; clk_tick= 0L; break; /* &T= value */
	case 'ad': accum += atoi(fa); break;		/* ADD value */
	case 'an': accum &= atoi(fa); break;		/* AND value */
	case 'su': accum -= atoi(fa); break;		/* SUB value */
	case 'jm': dojmp(fa); break;			/* JMP label */
	case 'jz': if (!accum) dojmp(fa); break;	/* JZ label */
	case 'jn': if (accum) dojmp(fa); break;		/* JNZ label */
	case 'dj': if (accum) --accum; 			/* DJNZ label */
		if (!accum) dojmp(fa); break;
	case 'je': if (error) { error= 0; dojmp(fa); }	/* JERROR label */
		break;

/* Commands not supported in Fido's script language. */

	case 'ti': oops("TIME"); break;			/* TIME hh:mm */
	case 'ke': oops("KEYBD"); break;		/* KEYBD char */
	case 'as': oops("ASK"); break;			/* ASK question */
	case 'pa': oops("PAUSE"); break;		/* PAUSE message */

	default: mesg("Unknown instruction \"%s\"",ip);
		stop_script();
		break;
	}
}

/* Complain we can't do this. */

static oops(m)
char *m;
{
	mesg("\"%s\" instruction: no effect",m);
}

/*
	PUSH string

Put the string onto the stack; error if overflow. */

static dopush(cp)
char *cp;
{
	if (slevel >= MAXLEVEL) {
		mesg("Stack overflow, can't PUSH \"%s\"",cp);
		stop_script();
		return;
	}
	cp[MAXSIZE]= NUL;
	strcpy(sreg[slevel++],cp);
}

/*
	POP register

Pop the stack into the specified register; error if underflow. We are
passed the raw register name so we have to parse a bit. */

static dopop(ra)
char *ra;
{
char *cp,e;
int n;

	if (slevel <= 0) {
		mesg("Can't POP %s, stack underflow",ra);
		stop_script();
		return;
	}
	e= 0;					/* assume no error */
	cp= sreg[--slevel]; n= atoi(cp);	/* prepare the stack value */

	if (*ra == '"') ++ra;			/* skip any leading quote */
	if (*ra != '&') ++e;			/* oops, must be reg name! */
	else switch (ra[1]) {
		case 'a': accum= n; break;
		case 'c': clk_tick= n * 1000L; break;
		case 't': trap_reg= n * 1000L; clk_tick= 0L; break;
		case 'b': if (setbaud(n)) datarate= n; break;
		case 'e': error= (n ? 1 : 0); break;

		case 'r': strcpy(recog,cp); break;
		case 's': while (*cp) shiftin(*cp++); break;

		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			n= atoi(&ra[1]) - 1;
			if (n <= MAXARG) setpat(stk-> arg[n],cp);
			else ++e;
			break;

		default: ++e; break;
	}
	if (e) {
		mesg("No such register: POP \"%s\"",ra);
		stop_script();
	}
}
						
/*
	FOPEN filename

Open the specified file, error if one is already open. */

static doopen(np)
char *np;
{
	if (scrfile != -1) {
		mesg("File %s is already open",scrfn);
		stop_script();
		return;
	}
	cpyarg(scrfn,np);			/* make a clean filename */
	scrfile= openp(scrfn,2);		/* open it, */
	error= (scrfile == -1);
	if (error && !scrunatt) {
		mesg("Can't find file \"%s\"",scrfn);
		stop_script();
		return;
	}
}

/*
	FNEW filename

Open the specified file, error if one is already open. */

static docreat(np)
char *np;
{
	if (scrfile != -1) {
		mesg("File %s is already open",scrfn);
		stop_script();
		return;
	}
	cpyarg(scrfn,np);			/* make a clean filename */
	scrfile= creat(scrfn,2);
	if (scrfile == -1) {
		mesg("Can't create file \"%s\"?",scrfn);
		stop_script();
	}
}

/*
	READ

Read a line from the open file, parse it into the &1 - &n variables. */

static doread(np)
char *np;
{
char buff[256];

	if (scrfile == -1) {
		mesg("There is no file open to READ from!");
		stop_script();
		return;
	}
	error= rline(scrfile,buff,sizeof(buff)) ? 0 : 1;/* set &E if EOF */
	scrargs(buff,level);				/* set possible arguments */
}

/*
	WRITE 

Write a line to the open file. */

static dowrite(np)
char *np;
{
int i;
char buff[SS];

	if (scrfile == -1) {
		mesg("There is no file open to WRITE to!");
		stop_script();
		return;
	}
	lseek(scrfile,0L,2);			/* write at EOF only */
	for (i= 0; i < MAXARG; i++) {		/* write each arg */
		sprintf(buff,0,"\"%s\"",stk-> arg[i]);
		error= write(scrfile,buff,strlen(buff)) == strlen(buff) ? 0 : 1;
		if (error) {
			if (! scrunatt) {
				mesg("Error writing file\"%s\"",scrfn);
				stop_script();
				return;

			} else break;
		}
		if (i < MAXARG - 1) write(scrfile,",",1);
	}
	write(scrfile,"\r\n",2);
}
/*
	CLOSE

Close any open file. */

static doclose(np)
char *np;
{
	if (scrfile != -1) close(scrfile);
	scrfile= -1;
}

/*
	TRAP label

	Set the trap to go off to the label in the current script file. */

static dotrap(np)
char *np;
{
	if (!*np) {
		mesg("No TRAP label in script %s",stk-> fname);
		stop_script();
		return;
	}
	strcpy(trap_label,np);			/* remember the label */
	trap_level= level;			/* remember the level */
}

/*
	MESSAGE <message>

	Output the message to the command line. */

static domesg(np)
char *np;
{
char *cp,buff[SS];

	cp= buff;
	while (*np) {
		if (*np >= ' ') *cp++= *np;	/* printable chars as-is */
		else {				/* expand control chars */
			*cp++= '\\';
			switch (*np) {
				case CR: *cp++= 'r'; break;
				case LF: *cp++= 'n'; break;
				case TAB: *cp++= 't'; break;
				default:
					sprintf(cp,0,"%02d",*np);
					while (*cp) ++cp;
					break;
			}
		}
		++np;
	}
	*cp= NUL;
	mesg(buff);
}

/* 
	CALL <scriptfile> <args ...>

	Start execution of a script file. If successful, this pushes the
current file handle, and sets the new one as the current file. Note that
this gets the raw, unprocessed, command line. */

static docall(rp)
char *rp;
{
int f,e;
char *cp,buff[SS],name[SS];

	cp= cpyscrarg(buff,rp);		/* copy off the script name */
	scrmac(name,buff);		/* expand it, etc */
	scrmac(buff,skip_delim(cp));	/* expand possible arguments */
	cp= name;
	while (*cp) {
		if (*cp == '.') break;	/* find the extention */
		++cp;
	}
	strcpy(cp,".SCR");		/* make .SCR */
	stoupper(name);			/* for error messages, etc */
	f= openp(name,0);		/* try to open it */
	if (f == -1) {
		mesg("Can't find %s script file",name);
		stop_script();		/* abort processing */

	} else if (level + 1 >= MAXLEVEL) {
		mesg("Too many nested script files: CALL %s from %s",name,stk-> fname);
		close(f);
		stop_script();

	} else {
		++level;			/* next level */
		++stk;				/* next stack frame */
		stk-> file= f;			/* set new file handle */
		strcpy(stk-> fname,name);	/* current script name */
		scrargs(buff,level);		/* set possible arguments */
	}
}

/*
	RETURN <n>

	Close a script file, pop back up one level. If <n> is non zero,
set the ERROR value. */

static doret(np)
char *np;
{
	if (isdigit(*np)) error= atoi(np);	/* set error value */

	if (stk-> file != -1) 
		close(stk-> file);		/* close the file, */
	if (level > 0) {
		--stk;
		--level;			/* execute a return */
	} else level= 0;
}

/*
	FILES <filespec>

	Set the accumulator to the number of files that match the
file specification. */

static dofiles(np)
char *np;
{
char path[SS];
struct _xfbuf xfbuf;

	cpyarg(path,np);
	np= next_arg(np);
	xfbuf.s_attrib= 0;
	accum= 0;
	while(! _find(path,accum,&xfbuf)) ++accum;
}
	
/*
	TIME hh:mm

	Wait until the specified time of day. */

static dotime(np)
char *np;
{
int h,m,lmin,hour,mins;
char *cp;

	cp= np;
	hour= atoi(cp);			/* read hour first, */
	while (*cp) {			/* look for the colon */
		if (*cp == ':') break;
		++cp;
	}
	if (*cp++ != ':') {
		mesg("Illegal time \"%s\"",np);
		stop_script();
		return;
	}
	mins= atoi(cp);			/* get minutes */

	if ((hour > 23) || (mins > 59)) {
		mesg("Illegal time \"%s\"; 23:59 maximum!",np);
		stop_script();
		return;
	}

	lmin= -1;
	do {
		do {
			h= gtod3(4);		/* read the hour, */
			m= gtod3(5);		/* and the minute */
		} while ((h != gtod3(4)) || (m != gtod3(5))); 

		if (abort_scr()) break;		/* keyboard abort */

		if (m != lmin) {
			lmin= m;
			mesg("Time is now %02d:%02d, waiting for %02d:%02d",
				h,m, hour,mins);
		}

	} while ((h != hour) || (m != mins));	/* wait til time */
}

/*
	DELAY <n>

	Delay <n> milliseconds. Delay 1 second if no argument is passed. */

static dodelay(n)
int n;
{
int h;
long timer;
char c;

	if (!n) n= 1000;			/* default to 1 sec, */

	h= set_tick(&timer);			/* get a milisec timer */
	if (h == -1) {
		mesg("FidoTerm error: Cant allocate a DELAY timer");
		stop_script();
		return;
	}
	timer= 0L;
	while (timer < n) {
		if (abort_scr()) break;		/* check keyboard */
		if (c= getscr()) send_kc(c);	/* script output, */
		check_mc();			/* input modem characters */
	}
	clr_tick(h);				/* stop the timer */
}

/*
	MATCH <pattern> <n>

	Search for <pattern>, for up to one minute or <n> seconds
if present. If the string is not found &E is set true. */

static domatch(np,na)
char *np;
{
int i,h;
long timer,limit;
char buff[SS],c;

	setpat(buff,np);				/* working pattern */
	limit= atoi(na) * 1000L;			/* check possible 2nd arg */
	if (limit == 0L) limit= 60000L;			/* else default to 1 minute */

	h= set_tick(&timer);				/* get a milisec timer */
	if (h == -1) {
		mesg("FidoTerm error: Cant allocate a MATCH timer");
		stop_script();
		return;
	}
	timer= 0L;				/* global timer */
	clrsr();
	while ((timer < limit) || (limit == 0L)) { /* start counting */
		if (abort_scr()) break;		/* check keyboard */
		if (c= getscr()) send_kc(c);	/* script output, */
		check_mc();			/* process modem characters */
		if (match(buff)) break;		/* compare against SR */
	}
	clr_tick(h);				/* stop the timer */
	error= ((timer >= limit) && 		/* error if timeout */
	  (limit > 0L));			/* and non-zero limit */
}

/* Install one character in the shift register. */

static shiftin(c)
char c;
{
int i;
	for (i= 0; i < (sizeof(sr) - 2); i++) 	/* shift full length */
		sr[i]= sr[i + 1];		/* shift the register */
	sr[i]= tolower(c & 0x7f);		/* install the new char */
}

/* Clear the shift register. */

static clrsr() {
int i;

	for (i= 0; i < sizeof(sr); i++) sr[i]= NUL;
}

/* Install a pattern in a string register; convert to all lower case
and bound to maximum length. Return the string length. */

static setpat(reg,np)
char *reg,*np;
{
int i;
char *cp;

	cp= reg;
	for (i= MAXSIZE; i--;) 
		if (! (*cp++= *np++)) break;
	*cp= NUL;				/* force an end, */
	stolower(reg);				/* make lower case */
	return(strlen(reg));			/* return the length */
}

/* Return true if the pattern matches the text now in the shift register. */

static match(r)
char *r;	/* the pattern to match, */
{
char *p;

	if (! *r) {				/* special case null string */
		p= sr + sizeof(sr) - 2;		/* check for empty SR */
		return(*p ? 0 : 1);		/* match only if both null */
	}
	p= sr + sizeof(sr) - strlen(r) - 1;	/* the input stream */
	while (*r) {				/* match characters */
		if ((*r != '?') && (*r != *p))	/* if not a wildcard or a match */
			return(0);		/* return mismatch */
		++r; ++p;
	}
	return(1);
}

/* 
	JMP <label>

	Search for the line in the file that defines the
label. Execution will proceed with the next line following. */

static dojmp(np)
char *np;
{
char *cp,label[SS],arg[SS],ln[SS];

	cpyarg(label,np);			/* clean copy of the label */
	if (! *label) {
		mesg("No label to JMP to in script %s",stk-> fname);
		stop_script();
		return;
	}
	stoupper(label);			/* for comparison */
	lseek(stk-> file,0L,0);		/* seek BOF */

	while (rline(stk-> file,ln,sizeof(ln))) { /* read lines, */
		cp= skip_delim(ln);		/* skip leading junk */
		if (*cp != ':') continue;	/* : must be first */
		cp= skip_delim(++cp);		/* skip : and spaces */
		cpyarg(arg,cp);			/* copy out label, */
		stoupper(arg);			/* for comparison */
		if (strcmp(label,arg) == 0) 
			return;			/* found it */
	}
	mesg("Can't find label %s in script %s",label,stk-> fname);
	stop_script();
}

/*
	KEYBD <c>

	Let the user type to the modem; terminate when <c> is hit,
or CR if none specified. */

static dokeybd(np)
char *np;
{
char c,e;

	c= *np; if (! c) c= CR;			/* default is CR */
	do {
		e= keyhit();			/* get a keyboard key */
		if (e == ESC) {			/* check for manual abort */
			if (abort_kc()) break;

		} else if (e) send_kc(e);	/* send to the modem */
		check_mc();			/* check incoming */

	} while (e != c);
}

/*
	QUIET <n>

	Wait for <n> milliseconds of quiet line. */

static doquiet(n)
int n;
{
char c;

	if (! n) n= 1000;			/* default to 1 sec */
	chr_timer= 0L;				/* start afresh */
	while (chr_timer < n) {			/* wait for quiet */
		if (abort_scr()) break;		/* check keyboard */
		if (c= getscr()) send_kc(c);	/* script output, */
		check_mc();			/* display incoming characters */
	}
}

/*
	PAUSE "string"

	Display the message, wait for a key to be hit. */

static dopause(np)
char *np;
{
char c;

	mesg(np);				/* display the message, */
	while (! (c= keyhit()));		/* get a key, */
	if (c == ESC) abort_kc();		/* if ESC abort */
}

/* Stop all script files. */

static stop_script() {

	while (level) doret("");
	doclose("");		/* close any open file */
	scrfile= -1;
	level= 0;		/* initially clear */
	error= 0;		/* no error */
	scrkdel= 0;		/* reset key delay */
	scrunatt= 0;		/* query on error */
}

/* Check the keyboard; if ESCape is hit, ask to abort the current script file. */

static abort_scr() {

	if (bdos(6,0xff) == ETX) return(abort_kc());
	else return(0);
}

/* Ask if we should abort the running script file. Return true if so. */

static abort_kc() {
int n;

	while (1) {
		cprintf(SM+219);			/* "Abort the script?" */
		n= lconin();
		cputs("\r\n");
		if (tolower(n) == 'y') {
			stop_script();
			return(1);

		} else if (tolower(n) == 'n') return(0);
	}
}

/* Set the string variables %1 to %9 from the passed string. If no
argument, set the string to null. The level passed is the new one,
that is being set. This allows %n to reference the current string
variables when setting the next levels args. The args are unprocessed,
since they are parsed just before each funtion is called. */

static scrargs(np,level)
char *np;
int level;
{
int i;

	np= skip_delim(np);
	for (i= 0; i < MAXARG; i++) {
		*stk-> arg[i]= NUL;				/* zap it first */
		if (*np) {					/* if an arg, */
			np= cpyscrarg(stk-> arg[i],np);
			np= skip_delim(np);			/* skip spaces */
		}
	}
}

/* Copy a script arg; either a single word or a quoted string. Return a pointer
to the start of the next arg. */

static char *cpyscrarg(dp,sp)
char *dp,*sp;
{
char lastc,c,q;

	if (*sp == '"') q= *sp++; else q= NUL;		/* optional quote stripping */
	lastc= NUL;
	while (c= *sp) {
		++sp;				/* next ... */
		if ((c == q) && (lastc != '\\')) /* if the quote char & not quoted */
			break;			/* end of argument */
		if (!q && delim(c)) break;	/* else stop if a delimiter */
		*dp++= c;			/* else part of same arg */
		lastc= c;			/* remember last char */
	}
	*dp= NUL;

	return(sp);
}

/* Expand the macro substitutions from any command line args. */

static scrmac(dp,sp)
char *dp,*sp;
{
char *cp,c;
int i;

	while (c= *sp++) {
		if (c == '^') {				/* control characters */
			c= *sp;				/* get the next char */
			if (c) ++sp;			/* (dont skip the NUL!) */
			c= toupper(c) & 0xbf;		/* convert it */

		} else if (c == '\\') {			/* if literal, */
			c= *sp;				/* get the next char */
			if (c) ++sp;			/* (dont skip the NUL!) */
			switch (tolower(c)) {
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					c= atoi(--sp); 
					while (isdigit(*sp)) ++sp;
					break;

				case 'r': c= CR; break;
				case 'n': c= LF; break;
				case 'b': c= BS; break;
				case 'e': c= ESC; break;
				case 'c': c= ETX; break;

				default: break;		/* else leave literal as-is */
			}

		} else if (c == '&') {			/* if a register */
			c= *sp;				/* get the next char */
			if (c) ++sp;			/* (dont skip the NUL!) */
			switch (tolower(c)) {
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					i= c - '0' - 1;
					if (i >= MAXARG) break;
					cp= stk-> arg[i];
copystr:;				while (*dp= *cp++) ++dp;
					break;

				case 'r': cp= recog; goto copystr;
				case 's': for (i= sizeof(sr) - 2; i >= 0; i--)
						if (! sr[i]) break;
					cp= &sr[++i]; goto copystr; 

				case 'a': i= accum; goto copynum;
				case 'b': i= datarate; goto copynum;
				case 'c': i= clk_tick / 1000L; goto copynum;
				case 'e': i= error; goto copynum;
				case 't': i= trap_reg; goto copynum;
copynum:;				sprintf(dp,0,"%ld",0L+i);/* a number */
					while (*dp) dp++;	/* go to the end */
					break;
			}
			c= NUL;					/* we dont use it */
		}
		if (c) *dp++= c;
	}
	*dp= NUL;
}
