/*************************************************************
 * vt100 terminal emulator - Script file support
 *				:ts=8
 *
 *	v2.9 ACS - Add support for font size, external transfer program
 *		   support, new ASIS option to LACE cmd and AREXX support.
 *	v2.8a 880331 ACS - Allow comments on XPROTO and FONT.
 *	v2.7 870825 ACS - Wait for the reply from AbortIO().
 *			  Use the *InfoMsg*() routines in window.c.  Provide
 *			  for multiple script files on command line
 *			  (companion to the changes in init.c).  Add the
 *			  ability to set shortcuts from init file.
 *	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"

#define DNMAXLEN	9

extern struct MsgPort *mySerPort;	/* in vt100.c */

struct COMMAND {
    int (*func)();
    char *cname;
    int	minlen;
    int	validfrom;
};

#define INIT		1
#define SCRIPT		2
#define NONREXX		4
#define REXXONLY	8

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

extern long atol();

#if MANX
extern char *index();
#endif	/* MANX */

#if LATTICE
#define index(a,b) stpchr(a,b)
#endif	/* LATTICE */

struct SHORT_CUTS {
    char *cname;
    char *pos;
};
    
/*   Following variables are set up in init.c's InitDefaults.  They tell
** us if there are any other script files listed on the command line so
** that we can execute them when a prior script is done.  */

extern int script_files_todo;
extern char **script_files;

/****************  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 = NULL;	     /* file pointer for script file	 */
struct LABEL	*lbase = NULL;	     /* will point to first label	 */
struct LABEL	*labels;	     /* current label pointer		 */
char		errmsg[80];	     /* Error msg from routines		 */
#if AREXX
int		implied_rexx;	    /* cmd_rx() invoked as impled cmd	 */
#endif /* AREXX */

/************************ command table *********************************/
static struct COMMAND commands[] = {
cmd_appcur,	"appcursor",	3, INIT|SCRIPT,	/* turn app. cursor on/off	*/
cmd_as,		"ascii",	3, SCRIPT,	/* ascii send			*/
cmd_ac,		"autochop",	3, INIT|SCRIPT,	/* XMODEM autochop		*/
cmd_bkg,	"background",	3, INIT,	/* set background color		*/
cmd_baud,	"baud",		3, INIT|SCRIPT,	/* Set Baud Rate		*/
cmd_beep,	"beep",		3, SCRIPT,	/* Beep				*/
cmd_bold,	"bold",		3, INIT,	/* set bold color		*/
cmd_bt,		"breaktime",	3, INIT|SCRIPT,	/* Set Break Time		*/
cmd_buf,	"buffer",	3, INIT,	/* set buffer size		*/
cmd_cap,	"capture",	3, SCRIPT,	/* ascii capture on/off		*/
cmd_cd,		"cd",		2, SCRIPT,	/* change directory		*/
cmd_conv,	"convert",	4, INIT|SCRIPT,	/* convert fn to lowercase	*/
cmd_cursor,	"cursor",	3, INIT,	/* set cursor color		*/
cmd_delay,	"delay",	3, SCRIPT,	/* delay amount of seconds	*/
cmd_depth,	"depth",	3, INIT,	/* set screen depth		*/
cmd_device,	"device",	3, INIT,	/* set serial device name	*/
cmd_display,	"display",	3, SCRIPT,	/* Display data on screen	*/
cmd_echo,	"echo",		3, INIT|SCRIPT,	/* turn echo on or off		*/
cmd_exit,	"exit",		3, INIT|SCRIPT|NONREXX,	/* exit script file	*/
cmd_ext,	"external",	4, INIT|SCRIPT,	/* external xfer pgms		*/
cmd_fnc,	"f",		1, INIT|SCRIPT,	/* define function key		*/
cmd_font,	"font",		3, INIT,	/* set font			*/
cmd_fonts,	"fontsize",	5, INIT,	/* set fontsize			*/
cmd_fore,	"foreground",	3, INIT,	/* set foreground color		*/
#if AREXX
cmd_fwd,	"forward",	3, REXXONLY,	/* forward data to rexx		*/
#endif /* AREXX */
cmd_goto,	"goto",		3, SCRIPT|NONREXX,/* goto label			*/
cmd_inter,	"interlace",	3, INIT,	/* interlace ON/OFF		*/
cmd_kb,		"kb",		2, SCRIPT,	/* kermit bye (for server)	*/
cmd_key,	"keyscript",	3, INIT|SCRIPT,	/* keyscript character		*/
cmd_kg,		"kg",		2, SCRIPT,	/* kermit get file		*/
cmd_kmaxpk,	"kmaxpack",	8, INIT|SCRIPT,	/* Kermit maximum packet size	*/
cmd_kr,		"kr",		2, SCRIPT,	/* kermit receive file		*/
cmd_ks,		"ks",		2, SCRIPT,	/* kermit send file		*/
cmd_lines,	"lines",	3, INIT,	/* num lines			*/
cmd_mode,	"mode",		3, INIT|SCRIPT,	/* KERMIT transfer mode		*/
cmd_mouse,	"mouse",	3, INIT|SCRIPT,	/* Mouse control		*/
cmd_msg,	"msg",		3, SCRIPT,	/* Display a message		*/
cmd_numkey,	"numkey",	6, INIT|SCRIPT,	/* turn numeric kpad on/off	*/
cmd_on,		"on",		2, SCRIPT,	/* on a 'string' do a cmd	*/
cmd_parity,	"parity",	6, INIT|SCRIPT,	/* Set Parity			*/
cmd_recf,	"recfile",	4, SCRIPT,	/* receive a file from host	*/
cmd_rx,		"rx",		2, INIT|SCRIPT,	/* Specific invocation		*/
cmd_sb,		"sb",		2, SCRIPT,	/* Send a break			*/
cmd_screen,	"screen",	3, INIT,	/* Screen WB/CUST		*/
cmd_send,	"send",		4, SCRIPT,	/* send string to host		*/
cmd_sendf,	"sendfile",	5, SCRIPT,	/* send a file to host		*/
cmd_share,	"shareserial",	5, INIT|SCRIPT,	/* ShareSerial device		*/
cmd_short,	"shortcut",	3, INIT,	/* Set shortcuts		*/
cmd_swap,	"swap",		4, INIT|SCRIPT,	/* Swap BS and DEL		*/
cmd_unit,	"unit",		4, INIT,	/* Unit of serial.device to use */
cmd_volume,	"volume",	3, INIT,	/* set volume			*/
cmd_wait,	"wait",		4, SCRIPT|NONREXX, /* wait for a host string	*/
cmd_wb,		"wbcolors",	3, INIT,	/* use WB colors		*/
cmd_wrap,	"wrap",		4, INIT|SCRIPT,	/* turn wrap on or off		*/
cmd_xbeep,	"xbeep",	4, INIT|SCRIPT,	/* Beep at end of xfer		*/
cmd_xproto,	"xprotocol",	5, INIT|SCRIPT,	/* Xfer Protocol		*/
cmd_xr,		"xr",		2, SCRIPT,	/* xmodem receive file		*/
cmd_xs,		"xs",		2, SCRIPT,	/* xmodem send file		*/
cmd_null,	NULL,		0, 0 	/* mark the end of the list	*/
};

char *xprotos[] = {	/* Order *must* be same as corresponding p_xproto values */
    "ascii", "xmodem", "xmodemcrc", "kermit" };

/*   NB: The structures referenced in the structure may be found in
** init.c */
extern struct filecmd {
    char mo;	/* Mode			*/
    char se;	/* Send			*/
    char re;	/* Receive		*/
    char kg;	/* Kermit Get		*/
    char kb;	/* Kermit Bye		*/
    char ca;	/* Capture or Capturing */
    char nl;
} filecmd_chars;

extern struct mode_cmd {
    char as;	/* ascii mode		*/
    char xm;	/* xmodem mode		*/
    char xmc;	/* xmodem crc mode	*/
    char ke;	/* kermit mode		*/
    char nl;
} modecmd_chars;

extern struct baducmd {
    char b03;	/* 0300			*/
    char b12;	/* 1200			*/
    char b24;	/* 2400			*/
    char b48;	/* 4800			*/
    char b96;	/* 9600			*/
    char bnl;
} baudcmd_chars;

extern struct parcmd {
    char no;	/* NOne			*/
    char ma;	/* MArk			*/
    char sp;	/* SPace		*/
    char ev;	/* EVen			*/
    char od;	/* ODd			*/
    char nl;
} parcmd_chars;

extern struct commcmd {
    char nl0;	/* Baud Sub-Item	*/
    char nl1;	/* Parity Sub-Item	*/
    char nl2;	/* Xfer mode Sub-Item	*/
    char sh;	/* Shared item		*/
    char nl;
} commcmd_chars;

extern struct modcmd {
    char im;	/* IMage		*/
    char tx;	/* TeXt			*/
    char cn;	/* CoNvert		*/
    char ac;	/* AutoChop		*/
    char nl;
} modcmd_chars;

extern struct scrcmd {
    char em;	/* Execute Macro	*/
    char ab;	/* Abort Macro		*/
    char rx;	/* AREXX Macro		*/
    char nl;
} scrcmd_chars;

extern struct {
    char sb;	/* Send Break	*/
    char hu;	/* Hang Up	*/
    char cd;	/* Change Dir	*/
    char cs;	/* Clear Screen	*/
    char ec;	/* ECho		*/
    char wr;	/* WRap		*/
    char nk;	/* Num Key	*/
    char ac;	/* App Cur	*/
    char bs;	/* BS<->DEL	*/
    char xb;	/* Beep at end of Xfer	*/
    char mu;	/* Mouse up events	*/
    char md;	/* Mouse down events	*/
    char nl;
} utilcmd_chars;

static struct SHORT_CUTS shortkeys[] = {	/* Short-cut keys */
	/* File items: */
    "se", &(filecmd_chars.se),		/* Send file			*/
    "re", &(filecmd_chars.re),		/* Receive file			*/
    "kb", &(filecmd_chars.kb),		/* kermit bye (for server)	*/
    "kg", &(filecmd_chars.kg),		/* kermit get (for server)	*/
    "cap", &(filecmd_chars.ca),		/* Capture			*/
	/* Mode items:	*/
    "asc", &(modecmd_chars.as),		/* ascii mode			*/
    "xmc", &(modecmd_chars.xmc),	/* xmodemcrc mode		*/
    "xm", &(modecmd_chars.xm),		/* xmodem mode			*/
    "ke", &(modecmd_chars.ke),		/* kermit mode			*/
	/* Comm items:	*/
    "share", &(commcmd_chars.sh),	/* Shared			*/
    "300", &(baudcmd_chars.b03),	/* Set Baud Rate		*/
    "1200", &(baudcmd_chars.b12),	/* Set Baud Rate		*/
    "2400", &(baudcmd_chars.b24),	/* Set Baud Rate		*/
    "4800", &(baudcmd_chars.b48),	/* Set Baud Rate		*/
    "9600", &(baudcmd_chars.b96),	/* Set Baud Rate		*/
    "none", &(parcmd_chars.no),		/* Set Parity			*/
    "mark", &(parcmd_chars.ma),		/* Set Parity			*/
    "space", &(parcmd_chars.sp),	/* Set Parity			*/
    "even", &(parcmd_chars.ev),		/* Set Parity			*/
    "odd", &(parcmd_chars.od),		/* Set Parity			*/
    "image", &(modcmd_chars.im),	/* KERMIT transfer mode		*/
    "text", &(modcmd_chars.tx),		/* KERMIT transfer mode		*/
    "convert", &(modcmd_chars.cn),	/* KERMIT transfer mode		*/
    "autochop", &(modcmd_chars.ac),	/* XMODEM autochop		*/
	/* Script items:	*/
    "execute", &(scrcmd_chars.em),	/* execute macro		*/
    "abort", &(scrcmd_chars.ab),	/* abort macro			*/
    "rx", &(scrcmd_chars.rx),		/* rexx macro			*/
	/* Util items: */
    "sb", &(utilcmd_chars.sb),		/* send break			*/
    "hang", &(utilcmd_chars.hu),	/* hang up			*/
    "cd", &(utilcmd_chars.cd),		/* change directory		*/
    "clear", &(utilcmd_chars.cs),	/* clear screen			*/
    "ech", &(utilcmd_chars.ec),		/* turn echo on or off		*/
    "wrap", &(utilcmd_chars.wr),	/* turn wrap on or off		*/
    "numkey", &(utilcmd_chars.nk),	/* turn numeric kpad on/off	*/
    "app", &(utilcmd_chars.ac),		/* turn app. cursor on/off	*/
    "con", &(utilcmd_chars.bs),		/* convert bs to del		*/
    "swap", &(utilcmd_chars.bs),	/* Swap BS and DEL		*/
    "xbeep", &(utilcmd_chars.xb),	/* Beep at end of xfer		*/
    "mouseup", &(utilcmd_chars.mu),	/* Mouse up events		*/
    "mousedn", &(utilcmd_chars.md),	/* Mouse dn events		*/
    NULL, NULL
};

static int cmd_from_script;		/* != 0 iff cmd from a script	*/


/********************************************************************/
/* 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;
		on_string[0] = '\0';
		on_cmd[0] = '\0';
		onpos = 0;
		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;
{
    char *sfile = NULL;

    if (strlen(file) == 0 || *file == '#') return(0);
    if ((sf = fopen(file, "r")) == NULL) {
	    sfile = AllocMem((LONG)(strlen(file)+3), MEMF_PUBLIC|MEMF_CLEAR);
	    strcpy(sfile, "S:");
	    strcat(sfile, file);
	    if((sf = fopen(sfile, "r")) == NULL) {
		InfoMsg2Line("Can't open script file",file);
		return(0);
	    }
    }
    script_on = TRUE;
    script_wait = FALSE;
    wait_string[0] = '\0';
    on_string[0] = '\0';
    on_match = FALSE;
    lbase = NULL;
    if(sfile)
	FreeMem(sfile, (LONG)strlen(file)+3);
}

/* 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)
register char *p;
int l;
{
    int i, l2, cmdrc = CMDOK;
    register struct COMMAND *cmd;

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

    errmsg[0] = '\0';

    /* now search for it */
    for (cmd = commands; cmd->func != cmd_null; cmd++) {
	int validfrom = cmd->validfrom;

	if(*p != cmd->cname[0])
	    continue;

	l2 = strlen(cmd->cname);
	if(l >= cmd->minlen && l <= l2
	  && strncmp(p, cmd->cname, l) == 0) {
	    if( (!doing_init && (validfrom == INIT))
	    ||  (((validfrom & NONREXX)  == NONREXX)  &&  CmdFromRexx)
	    ||  (((validfrom & REXXONLY) == REXXONLY) && !CmdFromRexx) )
		return(CMDBS);

	    cmd_from_script = 1;
	    cmdrc = (*(cmd->func))(next_wrd(p+l, &l));
	    cmd_from_script = 0;
	    if(errmsg[0])
		if(doing_init) {
		    puts("Init:");
		    puts(errmsg);
		} else
		    InfoMsg2Line("Script:", errmsg);
	    return(cmdrc);
	}
    }
#if AREXX
    if(implied_rexx == 0 && RexxSysBase != NULL) {
	implied_rexx = 1;
	cmdrc = cmd_rx(p);
	implied_rexx = 0;
	return(cmdrc);
    }
#endif /* AREXX */

    if (doing_init) {
	puts("Init: unknown command:");
	puts(p);
    } else
	InfoMsg2Line("Script - unknown command:",p);

    return(CMDNF);
}

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)  {
	    if(!CheckIO((struct IORequest *)&Script_Timer))
		AbortIO((struct IORequest *)&Script_Timer);
	    Wait (1L << Script_Timer_Port->mp_SigBit);
	    WaitIO((struct IORequest *)&Script_Timer);

	    /* script will proceed after on command    */
	    script_wait = FALSE;
	}
	exe_cmd(p,l);
	return(0);
    }
    script_wait = FALSE;

    if(CmdFromRexx)
	return;

    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)
	InfoMsg2Line("Script: label not found:",golabel);
    exit_script();
    if(script_files_todo > 0) {
	script_files_todo--;
	script_start(*(script_files++));
    }
}

exit_script()
{
    if (script_wait == WAIT_TIMER) {	/* timer not done yet */
	if(!CheckIO((struct IORequest *)&Script_Timer))
	    AbortIO((struct IORequest *)&Script_Timer); /* so abort it */
	Wait (1L << Script_Timer_Port->mp_SigBit); /* Wait for the sig */
	WaitIO((struct IORequest *)&Script_Timer); /* Get my reply back */
    }
    InfoMsg1Line("Script: terminated");
    script_on = FALSE;
    script_wait = TRUE;
    on_match = FALSE;
    wait_string[0] = '\0';
    on_string[0] = '\0';
    on_cmd[0] = '\0';
    fclose(sf);
    sf = NULL;
    if (reqwinup && ((reqwindow->Flags) & WINDOWACTIVE))
	ActivateWindow(mywindow);
    return 0;
}

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

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

    if(*ptr == '\0')
	return ptr;

    s1 = ptr;
    if (*ptr == '"') {
	while(*ptr++) {
	    if(*ptr == '"')
		if(*(ptr+1) == '"')
		    ptr++;
		else
		    break;
	}
	/*   Only 2 ways out: encounter an single " or hit \0. */
	if (*ptr == '"')
	    *ptr = '\0';
	ptr = s2 = ++s1;
	while(*s2) {
	    switch(*s2) {
	    case '"':
		if(*(s2+1) == '"')
		    *s1++ = *s2++;
		break;
	    case '^':
		if(*(s2+1) == '^')
		    *s1++ = *s2++;
		else
		    *s1++ = ((*++s2) | ' ') - 96;
		break;
	    default:
		*s1++ = *s2;
		break;
	    }
	    s2++;
	}
	*s1 = '\0';
	return(ptr);
    }
    if (*s1 == '^') {
	*s1 = (*(s1+1)|' ')-96;
	*(s1+1) = '\0';
	return(s1);
    }
    *(s1+1) = '\0';
    return(s1);
}

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

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);
    }
    return 0;
}

cmd_send(str)
char *str;
{
    register char *p = tostring(str);
    int i, len = strlen(p);

    if(p_echo)
	for(i = 0; i < len; i++) {
	    doremote(*p);
	    sendchar(*(p++));
	}
    else
	sendstring(p);
    return 0;
}

cmd_wait(str)
char *str;
{
    int waitstrsize = sizeof(wait_string) - 1;

    str = tostring(str);
    strncpy(wait_string, str, waitstrsize);
    wait_string[waitstrsize] = '\0';
    waitsize = strlen(wait_string);
    script_wait = WAIT_STRING;
    return 0;
}

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

    p = tostring(str);
    if( (onsize = strlen(p)) == 0) {
	on_string[0] = '\0';
	on_cmd[0] = '\0';
    } else {
	int onstrsize = sizeof(on_string) - 1,
	    oncmdsize = sizeof(on_cmd) - 1,
	    len;

	strncpy(on_string, p, onstrsize);
	on_string[onstrsize] = '\0';
	p = next_wrd(p+onsize+1, &len);
	strncpy(on_cmd, p, oncmdsize);
	on_cmd[oncmdsize] = '\0';
    }
    return 0;
}

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((struct IORequest *)&Script_Timer.tr_node);
    return 0;
}

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

    if (doing_init)
	return 0;

    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();
	if(script_files_todo > 0) {
	    script_files_todo--;
	    script_start(*(script_files++));
	}
    }
    return 0;
}

cmd_ext(parms)
char *parms;
{
    struct ExternalXfer *cexp;
    int i, j, len, nxfer, ci_strcmp(), cmdrc = CMDOK;
    char *p, temp[80], bound, *send, *dn;
    unsigned long mem_type = MEMF_PUBLIC | MEMF_CLEAR;

    parms = next_wrd(parms, &len);	/* Step to start of display name */
    if(len <= 0) {	/* No next word...error */
	strcpy(errmsg, "No display name for EXT command.");
	return CMDFAIL;
    }

    dn = parms;		/* Remember start of display name		*/
    parms += len;	/* Step to end of word				*/
    *(parms++) = '\0';	/* Mark end of display name and step beyond it	*/
    if(len > DNMAXLEN) {
	*(dn+DNMAXLEN) = '\0';
	sprintf(temp, "Display name more than %d chars, 1st %d used in EXT %s",
		DNMAXLEN, DNMAXLEN, dn);
	if(doing_init) {
	    puts("Init:");
	    puts(temp);
	} else
	    InfoMsg2Line("Script:", temp);
	cmdrc = CMDWARN;
    }

    parms = next_wrd(parms, &len);	/* Step to start of SEND cmd */

    /* Find a matching ExternalXfer structure based on display name */
    for(i = 0; i < NumExts; i++) {
	if(ci_strcmp(ExtXfer[i]->downname, dn) == 0)
	    if(len > 0)	/* Replace this entry */
		break;
	    else {	/* remove this entry from the list */
		struct ExternalXfer *p = ExtXfer[i];
		FreeMem(p->dispname, (long)(strlen(p->dispname)+1));
		FreeMem(p->downname, (long)(strlen(p->downname)+1));
		FreeMem(p->send, (long)(strlen(p->send)+1));
		FreeMem(p->receive, (long)(strlen(p->receive)+1));
		FreeMem(p, (long)sizeof(struct ExternalXfer));
		ExtXfer[i] = NULL;
		for(j = i+1; j < NumExts; j++)
		    ExtXfer[j-1] = ExtXfer[j];
		NumExts--;
		if(!doing_init)
		    InitFileItems();
		return;
	    }
    }
    nxfer = i;			/* Remember index of match or where to put new... */
    cexp = ExtXfer[nxfer];	/* ...and its location. */

    bound = *(parms++);	/* Remember starting quote for SEND command and step over it */
    if((p = index(parms, bound)) == NULL) {
	sprintf(errmsg, "No ending quote for SEND cmd in EXT %s", dn);
	return CMDFAIL;
    }
    *p = '\0';		/* Replace ending quote with NULL */
    send = parms;	/* Remember start of SEND cmd... */
    parms = ++p;	/* ...and step over the NULL */

    parms = next_wrd(parms, &len);	/* Step to start of RECEIVE cmd */
    if(len <= 0) {
	sprintf(errmsg, "No RECEIVE command for EXT %s", dn);
	return CMDFAIL;
    }
    bound = *(parms++);
    if((p =index(parms, bound)) == NULL) {
	sprintf(errmsg, "No ending quote for RECEIVE cmd in EXT %s", dn);
	return CMDFAIL;
    }
    *p = '\0';

    if(cexp) {	/* changing a current entry */
	FreeMem(cexp->dispname, (long)(strlen(cexp->dispname)+1));
	FreeMem(cexp->downname, (long)(strlen(cexp->downname)+1));
	FreeMem(cexp->send, (long)(strlen(cexp->send)+1));
	FreeMem(cexp->receive, (long)(strlen(cexp->receive)+1));
	cexp->cmdkey = '\0';
    } else if(nxfer >= EXTMAX) {
	sprintf(errmsg, "EXT %s would add more than %d external protocols",
		dn, EXTMAX);
	return CMDFAIL;
    } else {
	cexp = (struct ExternalXfer *)
		AllocMem((long)sizeof(struct ExternalXfer), mem_type);
	ExtXfer[nxfer] = cexp;
	NumExts++;
    }

    /* Initialize the new ExternalXfer entry.  Leave room in dispname for
    ** a checkmark (2 character widths). */
    len = strlen(dn);
    cexp->dispname = (char *)AllocMem((long)len+3, mem_type);
    strcpy(cexp->dispname, "  ");
    strcat(cexp->dispname, dn);
    cexp->downname = (char *)AllocMem((long)len+1, mem_type);
    for(i = 0; i < len; i++)
	cexp->downname[i] = dn[i] | ' ';
    cexp->send = (char *)AllocMem((long)strlen(send)+1, mem_type);
    strcpy(cexp->send, send);
    cexp->receive = (char *)AllocMem((long)strlen(parms)+1, mem_type);
    strcpy(cexp->receive, parms);

    if(!doing_init)
	InitFileItems();
    return cmdrc;
}

cmd_ks(file)
char *file;
{
    char name[MAXGADSTR], *p;

    if(file)
	strcpy(name, file);
    else
	name[0] = '\0';

    if(file == NULL || *file == '\0' || !cmd_from_script) {
	req("Kermit Send:",name,1);
	p = name;
	if(file && !cmd_from_script)
	    strcpy(file, name);
    } else
	p = file;
    multi_xfer(p, doksend, 1);
    return CMDOK;
}

cmd_kr(file)
char *file;
{
    char name[80];

    name[0] = '\0';

    multi_xfer(name, dokreceive, 0);
    return CMDOK;
}

cmd_kg(file)
char *file;
{
    char name[MAXGADSTR], *p;

    if(file)
	strcpy(name, file);
    else
	name[0] = '\0';

    server = TRUE;
    if(file == NULL || *file == '\0' || !cmd_from_script) {
	req("Kermit GET remote file(s):", name, 1);
	p = name;
	if(file && !cmd_from_script)
	    strcpy(file, name);
    } else
	p = file;
    multi_xfer(p, dokreceive, 0);
    return CMDOK;
}

cmd_kb()
{
    saybye();
    return CMDOK;
}

cmd_recf(file)
char *file;
{
    int cmdrc = CMDOK;

    switch(p_xproto) {
    case 0:	/* Ascii */
	do_capture(file);
	break;
    case 1:	/* Xmodem */
    case 2:	/* XmodemCRC */
	if(p_parity > 0) {
	    InfoMsg1Line("Parity setting prevents XMODEM receive.");
	    break;
	}
	cmd_xr(file);
	break;
    case 3:	/* Kermit */
	cmd_kr(file);
	break;
    default:
	if(p_xproto > MODEMAX + NumExts - 1)
	    cmd_kr(file);
	else
	    cmdrc = do_ext(ExtXfer[p_xproto-MODEMAX],
			   ExtXfer[p_xproto-MODEMAX]->receive,
			   file);
    }
    return cmdrc;
}

cmd_sendf(file)
char *file;
{
    int cmdrc = CMDOK;

    switch(p_xproto) {
    case 0:	/* Ascii */
	do_send(file);
	break;
    case 1:	/* Xmodem */
    case 2:	/* XmodemCRC */
	if(p_parity > 0) {
	    InfoMsg1Line("Parity setting prevents XMODEM send.");
	    cmdrc = CMDFAIL;
	    break;
	}
	cmd_xs(file);
	break;
    case 3:	/* Kermit */
	cmd_ks(file);
	break;
    default:
	if(p_xproto > MODEMAX + NumExts - 1)
	    cmd_ks(file);
	else
	    cmdrc = do_ext(ExtXfer[p_xproto-MODEMAX],
			   ExtXfer[p_xproto-MODEMAX]->send,
			   file);
    }
    return cmdrc;
}

cmd_xs(file)
char *file;
{
    char name[MAXGADSTR], *p;

    if(file)
	strcpy(name, file);
    else
	name[0] = '\0';

    if(file == NULL || *file == '\0' || !cmd_from_script) {
	req("Xmodem Send:",name,1);
	p = name;
	if(file && !cmd_from_script)
	    strcpy(file, name);
    } else
	p = file;
    multi_xfer(p, XMODEM_Send_File, 1);
    return CMDOK;
}

cmd_xr(file)
char *file;
{
    char name[MAXGADSTR], *p;

    if(file)
	strcpy(name, file);
    else
	name[0] = '\0';

    if(file == NULL || *file == '\0' || !cmd_from_script) {
	req("Xmodem Receive:",name,1);
	p = name;
	if(file && !cmd_from_script)
	    strcpy(file, name);
    } else
	p = file;
    multi_xfer(p, XMODEM_Read_File, 1);
    return CMDOK;
}

cmd_cd(name)
char *name;
{
    set_dir(name);
    return CMDOK;
}

cmd_sb(str)
char *str;
{
    sendbreak();
    return CMDOK;
}

cmd_baud(rate)
char *rate;
{
    int i = atoi(rate),
	cmdrc = CMDOK;

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

	default:
	strcpy(errmsg, "Invalid baud rate: ");
	strcat(errmsg, rate);
	cmdrc = CMDFAIL;
	break;
    }
    return cmdrc;
}

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:
	strcpy(errmsg, "Init: invalid parity: ");
	strcat(errmsg, par);
	return CMDFAIL;
    }
    p_parity = i;
    if (doing_init == 0)
	redocomm();
    return CMDOK;
}

#if AREXX
cmd_rx(cmd)
char	*cmd;
{
    int	cmdrc = CMDOK,
	ret = do_rx(cmd, implied_rexx);
    char *msg = NULL;

    errmsg[0] = '\0';
    if(ret) {
	cmdrc = CMDFAIL;
	if(doing_init)
	    printf("Init:\n%s\n", rexxerrmsgs[ret]);
	else
	    InfoMsg2Line("Script:", rexxerrmsgs[ret]);
    }
    return cmdrc;
}
#else
cmd_rx(cmd)
char	*cmd;
{
    char *msg = "AREXX not compiled into this version.";

    if(doing_init)
	printf("Init:\n%s\n", msg);
    else
	InfoMsg2Line("Script:", msg);
    return CMDFAIL;
}
#endif

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

    AbortIO((struct IORequest *)Read_Request);
    Wait(1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
    WaitIO((struct IORequest *)Read_Request);
    Read_Request->io_BrkTime = Write_Request->io_BrkTime = p_break;
    setparams();
    return CMDOK;
}

cmd_kmaxpk(pks)
char *pks;
{
    int i = atoi(pks),
	cmdrc = CMDOK;

    if(i <= MAXLONGPKS)
	p_kmaxpack = i;
    else {
	sprintf(errmsg, "Kermit packet size too big: %d", pks);
	cmdrc = CMDFAIL;
    }
    return cmdrc;
}

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

	case 'c':
	p_mode = 1;
	break;

	default:
	strcpy(errmsg, "Init: invalid transfer mode: ");
	strcat(errmsg, tmode);
	return CMDFAIL;
    }
    if (doing_init == 0)
	redocomm();
    return CMDOK;
}

cmd_as(file)
char *file;
{
    do_send(file);
    return CMDOK;
}

cmd_cap(file)
char *file;
{
    do_capture(file);
    return CMDOK;
}

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

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;
    }
}

cmd_ac(par)
char *par;
{
    setvar(par,0,&p_autochop);
    if (doing_init == 0)
	redoutil();
    return CMDOK;
}

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

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

cmd_share(parm)
char *parm;
{
    int oldval = p_shared;

    setvar(parm, 0, &p_shared);

    if(Read_Request && (oldval ^ p_shared)) {
	/* Port is going from shared->exclusive or vice-versa.  Close it and
	** re-open */
	if(!CheckIO((struct IORequest *)Read_Request))
	    AbortIO((struct IORequest *)Read_Request);
	Wait (1L <<mySerPort->mp_SigBit);
	WaitIO((struct IORequest *)Read_Request);
	CloseDevice((struct IORequest *)Read_Request);
	if(p_shared)
	    Read_Request->io_SerFlags |= SERF_SHARED;
	else
	    Read_Request->io_SerFlags &= (-1L ^ SERF_SHARED);
	if(OpenDevice(mysername,(LONG)p_unit,(struct IORequest *)Read_Request,NULL))
	    cleanup("Can't re-open Read device",0);
	InitCommItems();
	setparams();
    }
    return CMDOK;
}

cmd_xbeep(par)
char	*par;
{
    setvar(par,0,&p_xbeep);
    if (doing_init == 0)
	redoutil();
    return CMDOK;
}

cmd_xproto(par)
char	*par;
{
    int i, len, save, cmdrc = CMDOK;

    next_wrd(par, &len);

    p_xproto = MODEMAX + 1;	/* Establish an out of bounds default */

    if(len) {
	par[len] = '\0';

	/* downcase the parameter */
	for(i = 0; i < len; i++) par[i] |= ' ';

	for(i = 0; i < MODEMAX; i++)
	    if(strcmp(par, &(*(xprotos[i]))) == 0) {
		p_xproto = i;
		break;
	    }
    }
    if(p_xproto >= MODEMAX) {
	/* Not a built-in protocol...check the defined external protocols */
	p_xproto = 0;
	if(len > DNMAXLEN) {
	    save = *(par+DNMAXLEN);
	    *(par+DNMAXLEN) = '\0';
	}
	for(i = 0; i < NumExts; i++) {
	    if(strcmp(par, ExtXfer[i]->downname) == 0) {
		p_xproto = MODEMAX + i;
		break;
	    }
	}

	if(p_xproto == 0) {
	    p_xproto = MODEMAX - 1;
	    if(len > DNMAXLEN)
		*(par+DNMAXLEN) = save;
	    sprintf(errmsg, "XPROTO beginning \"%.10s\" unknown, %s used",
		    par, &(*(xprotos[p_xproto])));
	    cmdrc = CMDFAIL;
	}
    }
    if(!doing_init)
	redofile();
    return cmdrc;
}

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

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

cmd_swap(par)
char	*par;
{
    setvar(par,0,&p_bs_del);
    if (doing_init == 0)
	redoutil();	/* Calls InitUtilItems which does swap_bs_del() */
    else {
    	swap_bs_del();	/* Setup keys[] properly... */
    	swap_bs_del();	/* ...for new mode	    */
    }
    return CMDOK;
}

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

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

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

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

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

cmd_device(par)
char	*par;
{
    int	len, cmdrc = CMDOK;
    char	temp[80];

    next_wrd(par, &len);
    if(len > SERNAMESIZE) {
	printf(temp, "Init:\nDevice parm too long, %s used", SERIALNAME);
	strcpy(mysername, SERIALNAME);
	cmdrc = CMDFAIL;
    } else
	strcpy(mysername, par);
    return cmdrc;
}

cmd_display(str)
char	*str;
{
    register char *p;

    for(p = tostring(str); *p; p++)
	doremote(*p);

    return CMDOK;
}

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

#if AREXX
/*   This routine *depends* on the fact that the FORWARD command is only
** valid from AREXX.  It then assumes that it can open RXSNAME.
**/
cmd_fwd(par)
char *par;
{
    int i;

    if(ForwardPortName != NULL) {
	FreeMem(ForwardPortName, (LONG)strlen(ForwardPortName) + 1);
	ForwardPortName = NULL;
    }

    forwarding = 0;

    if(RexxSysBase == NULL) {
	RexxSysBase = (struct RxsLib *)OpenLibrary(RXSNAME, 0L);
	if( (i = makerexxport()) != 0) {
	    strcpy(errmsg, rexxerrmsgs[i]);
	    return CMDFAIL;
	}
    }

    if(par && *par) {
	if(ForwardPortName = AllocMem((LONG)strlen(par)+1,
				MEMF_PUBLIC | MEMF_CLEAR)) {
	    strcpy(ForwardPortName, par);
	    forwarding = 1;
	} else {
	    strcpy(errmsg, "Can't allocate memory for an AREXX forward port");
	    return CMDFAIL;
	}
    }

    return CMDOK;
}
#else
cmd_fwd(par)
{
    return CMDFAIL;
}
#endif /* AREXX */

cmd_font(par)
char	*par;
{
    char temp[80];
    int  len, cmdrc = CMDOK;

    next_wrd(par, &len);

    temp[0] = '\0';

    /*  myfontname has been initialized from p_font in InitDefaults() in
    ** init.c */

    if(!len) {
	sprintf(temp, "Init:\nNo font specified, \"%s\" used", myfontname);
	cmdrc = CMDWARN;
    } else {
	if(*par && len) {
	    if(len < MAXFONTVARLEN) {
	    	par[len] = '\0';
		strcpy(myfontname, par);
		strcat(myfontname,FONTSUFFIX);
	    } else {
		sprintf(temp, "Init:\nFont specification too long, \"%s\" used",
			myfontname);
		cmdrc = CMDWARN;
	    }
        }
    }
    myattr.ta_Name = (STRPTR)myfontname;
    if(*temp)
	puts(temp);
    return cmdrc;
}

cmd_fonts(par)
char	*par;
{
    char temp[80];
    int  len, size, cmdrc = CMDOK;

    next_wrd(par, &len);

    temp[0] = '\0';

    /*  myfontname has been initialized from p_font in InitDefaults() in
    ** init.c */

    if(!len) {
	strcpy(temp, "Init:\nNo font size specified, \"8\" used");
	cmdrc = CMDWARN;
    } else {
	if(*par && len) {
	    size = atoi(par);
	    if(size > 0)
		myattr.ta_YSize = size;
	    else {
	    	myattr.ta_YSize = 8;
		sprintf(temp, "Init:\nFont size \"%s\" invalid, \"8\" used",
			par);
		cmdrc = CMDWARN;
	    }
        }
    }
    if(*temp)
	puts(temp);
    return cmdrc;
}

cmd_inter(par)
char	*par;
{
    if(strcmp(par, "asis") == 0)
	p_interlace = 2;
    else
	setvar(par,0,&p_interlace);
    return CMDOK;
}

cmd_mouse(par)
char *par;
{
    switch (par[0]|' ') {
	case 'b':
	    p_mouse_up = p_mouse_down = 1;		/* send both	*/
	    break;

	case 'o':
	    p_mouse_up = p_mouse_down = 0;		/* mouse off	*/
	    break;

	case 'u':
	    p_mouse_up = 1;				/* up only      */
	    p_mouse_down = 0;
	    break;

	case 'd':					/* down only	*/
	    p_mouse_down = 1;
	    p_mouse_up = 0;
	    break;
	default:
	    return CMDFAIL;
    }
    return CMDOK;
}

cmd_msg(par)
char *par;
{
    register char *p, *s;
    int newline = 1;		/* Whether we need to scroll */

    for(s = p = tostring(par); *p; p++) {
	if(*p == '\n') {
	    *p = '\0';
	    InfoMsgNoScroll(s);
	    ScrollInfoMsg(1);
	    newline = 0;
	    s = p + 1;
	} else if(*p == '\r') {
	    *p = '\0';
	    InfoMsgNoScroll(s);
	    newline = 0;
	    s = p + 1;
	} else
	    newline = 1;
    }
    if(*s)
	InfoMsgNoScroll(s);

    if(newline)
	ScrollInfoMsg(1);
    return CMDOK;
}

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

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

cmd_unit(par)
char	*par;
{
    setvar(par, 3, &p_unit);
    return CMDOK;
}

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

cmd_short(par)	/* Set keyboard shortcuts */
char	*par;
{
    int	i, l, l2;
    register char *p = par;
    
    /* downcase the next word */
    for (i=0; p[i] && (p[i] != ' ') && (p[i] != '\t'); i++)
	p[i] |= ' ';
    l = i;
    
    /*   Find the command name.  If found set the shortcut key to the
    ** user's value.  If no value then set the key to ' ' to indicate no
    ** shortcut available. */
    for(i = 0; shortkeys[i].cname != NULL; i++) {
	l2 = strlen(shortkeys[i].cname);
	if (l >= l2 && strncmp(p, shortkeys[i].cname, l2) == 0) {
	    for( ; p[l] && ((p[l] == ' ') || (p[l] == '\t')) ; l++)
		;
	    if(p[l])
		*(shortkeys[i].pos) = p[l];
	    else
		*(shortkeys[i].pos) = ' ';
	    return CMDOK;
	}
    }

    /* Not a "normal" item so try an ExternalXfer item */
    p = next_wrd(par, &l);
    if(l > 0) {
	*(p+l) = '\0';
	for(i = 0; i < NumExts; i++)
	    if(strcmp(p, ExtXfer[i]->downname) == 0) {
		p = next_wrd(p+l+1, &l);
		if(l <= 0)
		    break;
		ExtXfer[i]->cmdkey = *p;
		return CMDOK;
	    }
    }

    sprintf(errmsg, "Unknown shortcut: %s", par);
    return CMDFAIL;
}

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

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

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

cmd_conv(par)
char	*par;
{
    setvar(par,0,&p_convert);
    if (doing_init == 0)
	redoutil();
    return CMDOK;
}

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(*par == 'm') {	/* Mouse prefix */
	p_f[10] = malloc(strlen(s) + 1);
	strcpy(p_f[10], s);
    } else 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);
	}
    }
    return CMDOK;
}

cmd_null(dummy)
char *dummy;
{
    return CMDOK;
}

/* Case-insensitive strcmp() */
ci_strcmp(p1, p2)
char *p1, *p2;
{
    while( *p1 && ((*p1 | ' ') == (*p2 | ' ')) ) {
	++p1;
	++p2;
    }
    return(*p1 != *p2);
}

do_ext(exp, pgmname, file)
struct ExternalXfer *exp;
char *pgmname, *file;
{
    char *local = "@LOCAL",
	 *remote = "@REMOTE",
	 *command,
	 localfn[MAXGADSTR],
	 remotefn[MAXGADSTR],
	 *p,
	 *p1;
    int len = 0,
	locallen = strlen(local),
	remotelen = strlen(remote);

    if(file)
	strcpy(localfn, file);
    else
	localfn[0] = '\0';
    remotefn[0] = '\0';

    /* Get the initial length of the command then go through the user's
    ** command string looking for @LOCAL and @REMOTE.  When we encounter one of
    ** these strings then, if we don't already have the appropriate filename,
    ** we'll put up a requester asking for it.  Whether we put up the requester
    ** or not we must still account for the length of the replacement filename
    ** so that when we AllocMem() for the command we'll only get what we need.
    */
    len = strlen(pgmname) + 1;
    for(p = pgmname; *p; p++) {
	if(*p == '@')
	    if(strncmp(p+1, local+1, locallen-1) == 0) {
		if(!cmd_from_script || localfn[0] == '\0')
		    req("Local file name:", localfn, 1);
		if(!localfn[0])	/* No filename specified */
		    return CMDFAIL;
		if(file && !cmd_from_script)
		    strcpy(file, localfn);
		len += strlen(localfn);
		len -= locallen;
		p += locallen;
	    } else if(strncmp(p+1, remote+1, remotelen-1) == 0) {
		req("Remote file name:", remotefn, 1);
		if(!remotefn[0])	/* No filename specified */
		    return CMDFAIL;
		len += strlen(remotefn);
		len -= remotelen;
		p += remotelen;
	    }
    }	/* for p = pgmname */

    /* Allocate space for the command and copy the command string to it
    ** replacing @LOCAL and @REMOTE with localfn and remotefn, respectively */
    command = AllocMem((long)len, MEMF_PUBLIC | MEMF_CLEAR);
    if(localfn[0] || remotefn[0]) {
	for(p = pgmname, p1 = p; *p; p++) {
	    if(strncmp(p, local, locallen) == 0) {
		strncat(command, p1, p - p1);
		strcat(command, localfn);
		p += locallen;
		p1 = p;
	    } else if(strncmp(p, remote, remotelen) == 0) {
		strncat(command, p1, p - p1);
		strcat(command, remotefn);
		p += remotelen;
		p1 = p;
	    }
	}
	strcat(command, p1);	/* Don't forget the tail of the command! */
    } else strcpy(command, pgmname);

    /* Close down VT100's use of the serial port */
    if(!CheckIO((struct IORequest *)Read_Request))
	AbortIO((struct IORequest *)Read_Request);
    Wait (1L <<mySerPort->mp_SigBit);
    WaitIO((struct IORequest *)Read_Request);
    if(!p_shared)
	CloseDevice((struct IORequest *)Read_Request);

    Execute(command, 0L, 0L);	/* Invoke the external xfer pgm */
    FreeMem(command, (long)len);
    if(!p_shared && OpenDevice(mysername,(LONG)p_unit,(struct IORequest *)Read_Request,NULL))
	cleanup("Can't re-open Read device",0);
    setparams();
    return CMDOK;
}
