
/*
 * SCRIPTCMDS.C
 * (c) 1992 J.Harper
 */

#include "jed.h"
#include "jed_protos.h"

Prototype   VALUE *	cmd_script	(LONG, VALUE *);
Prototype   VALUE *	cmd_macro	(LONG, VALUE *);
Prototype   VALUE *	cmd_nargs	(LONG, VALUE *);
Prototype   VALUE *	cmd_arg		(LONG, VALUE *);
Prototype   VALUE *	cmd_if		(LONG, VALUE *);
Prototype   VALUE *	cmd_while	(LONG, VALUE *);
Prototype   VALUE *	cmd_select	(LONG, VALUE *);
Prototype   VALUE *	cmd_dowhile	(LONG, VALUE *);
Prototype   VALUE *	cmd_return	(LONG, VALUE *);
Prototype   VALUE *	cmd_break	(LONG, VALUE *);
Prototype   VALUE *	cmd_local	(LONG, VALUE *);
Prototype   VALUE *	cmd_addsym	(LONG, VALUE *);
Prototype   VALUE *	cmd_remsym	(LONG, VALUE *);
Prototype   VALUE *	cmd_plus	(LONG, VALUE *);
Prototype   VALUE *	cmd_minus	(LONG, VALUE *);
Prototype   VALUE *	cmd_product	(LONG, VALUE *);
Prototype   VALUE *	cmd_divide	(LONG, VALUE *);
Prototype   VALUE *	cmd_mod		(LONG, VALUE *);
Prototype   VALUE *	cmd_b_not	(LONG, VALUE *);
Prototype   VALUE *	cmd_l_not	(LONG, VALUE *);
Prototype   VALUE *	cmd_b_or	(LONG, VALUE *);
Prototype   VALUE *	cmd_l_or	(LONG, VALUE *);
Prototype   VALUE *	cmd_b_and	(LONG, VALUE *);
Prototype   VALUE *	cmd_l_and	(LONG, VALUE *);
Prototype   VALUE *	cmd_b_eor	(LONG, VALUE *);
Prototype   VALUE *	cmd_l_eor	(LONG, VALUE *);
Prototype   VALUE *	cmd_lshift	(LONG, VALUE *);
Prototype   VALUE *	cmd_rshift	(LONG, VALUE *);
Prototype   VALUE *	cmd_equals	(LONG, VALUE *);
Prototype   VALUE *	cmd_equality	(LONG, VALUE *);
Prototype   VALUE *	cmd_inequality	(LONG, VALUE *);
Prototype   VALUE *	cmd_gtthan	(LONG, VALUE *);
Prototype   VALUE *	cmd_ltthan	(LONG, VALUE *);
Prototype   VALUE *	cmd_gethan	(LONG, VALUE *);
Prototype   VALUE *	cmd_lethan	(LONG, VALUE *);
Prototype   VALUE *	cmd_nop		(LONG, VALUE *);

/*
 * (script `sectType')
 * (script `x' `file')  - x stands for external (f is a section type)
 */
VALUE *
cmd_script(LONG	argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_ANY))
    {
	if(*(ARG1.val_Value.String) == 'x')
	{
	    if(ARG2.val_Type ==	VTF_STRING)
	    {
		STRPTR file;
		if(!(file = squirrelfile(ARG2.val_Value.String)))
		{
		    UBYTE othername[200];
		    strcpy(othername, "s:jed/");
		    AddPart(othername, ARG2.val_Value.String, 200);
		    file = squirrelfile(othername);
		}
		if(file)
		{
		    if(!execstring(file, &RES))
		    {
			freestring(file);
			return(FALSE);
		    }
		    freestring(file);
		}
		else
		    doserror();
	    }
	    else
		settitle("syntax error: argument 2 should be a string.");
	}
	else if(*(ARG1.val_Value.String) == 's')
	{
	    if(ARG2.val_Type ==	VTF_STRING)
	    {
		if(!execstring(ARG2.val_Value.String, &RES))
		    return(FALSE);
	    }
	    else
		settitle("syntax error: argument 2 should be a string.");
	}
	else
	{
	    POS	temp = CurrVW->vw_CursorPos;
	    POS	endpos;
	    if(getsection(ARG1.val_Value.String, &endpos))
	    {
		POS startpos = CurrVW->vw_CursorPos;
		LONG length;
		STRPTR text;
		CurrVW->vw_CursorPos = temp;
		length = sectionlength(&startpos, &endpos);
		if(text	= AllocVec(length + 1, 0L))
		{
		    copysection(&startpos, &endpos, text);
		    text[length] = 0;
		    if(!execstring(text, &RES))
		    {
			FreeVec(text);
			return(FALSE);
		    }
		    FreeVec(text);
		}
		else
		    settitle(NoMemMsg);
	    }
	}
    }
    return(&RES);
}

/*
 * (macro `macName' `macDef')
 */
VALUE *
cmd_macro(LONG argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_STRING))
    {
	STRPTR macdef;
	if(macdef = savestring(ARG2.val_Value.String))
	{
	    if(addsymbol(ARG1.val_Value.String,	STF_COMMAND, VTF_STRING, macdef))
	    {
		settitlefmt("created macro %s", (LONG)ARG1.val_Value.String);
		setnumres(TRUE);
	    }
	    else
	    {
		freestring(macdef);
		settitle("error: can't create symbol");
	    }
	}
	else
	    settitle(NoMemMsg);
    }
    return(&RES);
}

/*
 * returns the number of arguments passed to the innermost macro
 * (nargs)
 */
VALUE *
cmd_nargs(LONG argc, VALUE *argv)
{
    if(ExeList[MacDepth])
    {
	setnumres(ExeList[MacDepth]->mc_Argc);
    }
    return(&RES);
}

/*
 * returns an argument (prompts	for it if it wasn't given)
 * (arg	n `type' [`prompt'])
 *
 * type	defines	the type of value required -- `s' = string, `n' = number.
 * if an argument of the proper	type can't be got then the macro is
 * aborted.
 */
VALUE *
cmd_arg(LONG argc, VALUE *argv)
{
    VALUE *res = &RES;
    if(TPLATE2(VTF_NUMBER, VTF_STRING))
    {
	if(MacDepth)
	{
	    MACCONTEXT *thismac	= ExeList[MacDepth];
	    LONG argnum	= ARG1.val_Value.Number;

	    if(toupper(*(ARG2.val_Value.String)) == 'S')
	    {
		STRPTR arg = NULL;
		if(thismac->mc_Argv[argnum].val_Type ==	VTF_STRING)
		    arg	= savestring(thismac->mc_Argv[argnum].val_Value.String);
		if(!arg)
		{
		    if(ARG3.val_Type ==	VTF_STRING)
			arg = docmdline(ARG3.val_Value.String);
		    else
			arg = docmdline("string> ");
		}
		if(arg)
		{
		    setstrres(arg);
		}
	    }
	    else
	    {
		STRPTR str;
		if(thismac->mc_Argv[argnum].val_Type ==	VTF_NUMBER)
		{
		    setnumres(thismac->mc_Argv[argnum].val_Value.Number);
		}
		else
		{
		    if(ARG3.val_Type ==	VTF_STRING)
			str = docmdline(ARG3.val_Value.String);
		    else
			str = docmdline("number> ");
		    if(str)
		    {
			if(isdigit(*str) || (*str == '-'))
			{
			    APTR dummy;
			    setnumres(strtol(str, &dummy, 0));
			}
			freestring(str);
		    }
		}
	    }
	    if(RES.val_Type == VTF_NONE)
	    {
		settitlefmt("syntax error: no arg %ld provided", ARG1.val_Value.Number);
		res = FALSE;
	    }
	}
	else
	{
	    settitle("error: not in macro - can't use arg command");
	    res	= FALSE;
	}
    }
    else
	res = FALSE;
    return(res);
}

/*
 * (if condition-clause	`true-clause' `false-clause')
 *
 * result is value of evaluated	clause.
 */
VALUE *
cmd_if(LONG argc, VALUE	*argv)
{
    if(TPLATE3(VTF_NUMBER, VTF_ANY, VTF_ANY))
    {
	if(ARG1.val_Value.Number)
	{
	    if(ARG2.val_Type ==	VTF_STRING)
	    {
		if(!execstring(ARG2.val_Value.String, &RES))
		    return(FALSE);
	    }
	}
	else
	{
	    if(ARG3.val_Type ==	VTF_STRING)
	    {
		if(!execstring(ARG3.val_Value.String, &RES))
		    return(FALSE);
	    }
	}
    }
    return(&RES);
}

/*
 * (while `condition-clause' `true-clause')
 *
 * result is number of iterations before stopping.
 * loop	will be	broken after MAX_ITS number of iterations or a ^C break
 * signal is received.
 */
#define	MAX_ITS	(1000000)

VALUE *
cmd_while(LONG argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_STRING))
    {
	LONG i;
	for(i =	0; i < MAX_ITS;	i++)
	{
	    VALUE value;
	    if(!execstring(ARG1.val_Value.String, &value))
	    {
		releasevalue(&value);
		return(FALSE);
	    }
	    if(!value.val_Value.Number)
	    {
		releasevalue(&value);
		returnnum(i);
	    }
	    releasevalue(&value);
	    if(!execstring(ARG2.val_Value.String, &value))
	    {
		releasevalue(&value);
		return(FALSE);
	    }
	    releasevalue(&value);
	    if(CheckSignal(SIGBREAKF_CTRL_C))
	    {
		settitle("^C: while aborted");
		break;
	    }
	}
	if(i ==	MAX_ITS)
	    settitle("wow... a million iterations, while loop aborted");
    }
    return(FALSE);
}

/*
 *  (select
 *	(cond1)
 *	    `reaction1'
 *	(cond2)
 *	    `reaction2'
 *	...
 *	`default'
 *  )
 */
VALUE *
cmd_select(LONG	argc, VALUE *argv)
{
    VALUE *res;
    WORD i;
    for(i = 0; i < argc; i += 2)
    {
	if((argv[i + 1].val_Type == VTF_NUMBER)	&& (argv[i + 2].val_Type == VTF_STRING))
	{
	    if(argv[i +	1].val_Value.Number)
		return(execstring(argv[i + 2].val_Value.String,	&RES));
	}
	else if(argv[i + 1].val_Type ==	VTF_STRING)
	    return(execstring(argv[i + 1].val_Value.String, &RES));
    }
    return(&RES);
}

/*
 * (dowhile `command' `condition')
 *
 * similar to a	C 'do...while' loop.
 */
VALUE *
cmd_dowhile(LONG argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_STRING))
    {
	LONG i;
	for(i =	0; i < MAX_ITS;	i++)
	{
	    VALUE value;
	    if(!execstring(ARG1.val_Value.String, &value))
	    {
		releasevalue(&value);
		return(FALSE);
	    }
	    releasevalue(&value);
	    if(!execstring(ARG2.val_Value.String, &value))
	    {
		releasevalue(&value);
		return(FALSE);
	    }
	    if(!value.val_Value.Number)
	    {
		releasevalue(&value);
		returnnum(i);
	    }
	    if(CheckSignal(SIGBREAKF_CTRL_C))
	    {
		settitle("^C: dowhile aborted");
		break;
	    }
	}
	if(i ==	MAX_ITS)
	    settitle("wow... a million iterations, dowhile loop aborted");
    }
    return(FALSE);
}

/*
 * breaks from the macro, returns ARG1
 */
VALUE *
cmd_return(LONG	argc, VALUE *argv)
{
    dupvalue(&RES, &ARG1);
    return(FALSE);
}

/*
 * (break n)
 * Breaks out of n depths of strings that are currently	being executed.
 */
VALUE *
cmd_break(LONG argc, VALUE *argv)
{
    RES.val_Type = VTF_BREAK;
    if(ARG1.val_Type ==	VTF_NUMBER)
    {
	if((RES.val_Value.Number = ARG1.val_Value.Number) <= 0)
	{
	    RES.val_Type = VTF_NONE;
	    return(&RES);
	}
    }
    else
	RES.val_Value.Number = 1;
    return(FALSE);
}

/*
 * (local `local-varname' value)
 *
 * local variables are killed when the current macro is	exited.
 * Local variables take	precedence over	global variables.
 */
VALUE *
cmd_local(LONG argc, VALUE *argv)
{
    VALUE *res = &RES;
    while(res && (argc >= 2))
    {
	if(TPLATE1(VTF_STRING))
	{
	    if(MacDepth)
	    {
		LOCALSYM *sym;
		if(sym = AllocVec(sizeof(LOCALSYM), MEMF_CLEAR))
		{
		    sym->l_Node.ln_Name	= savestring(ARG1.val_Value.String);
		    if(dupvalue(&sym->l_Value, &ARG2))
			AddTail(&ExeList[MacDepth]->mc_Locals, &sym->l_Node);
		    else
		    {
			FreeVec(sym);
			settitle(NoMemMsg);

			/* I reckon that not being able	to create a variable
			 * is serious enough to	warrant	aborting the macro.
			 */
			res = FALSE;
		    }
		}
		else
		{
		    settitle(NoMemMsg);
		    res	= FALSE;
		}
	    }
	    else
		settitle("error: only macros can use local symbols");
	}
	argc -=	2;
	argv +=	2;
    }
    if(res)
    {
	res->val_Type =	VTF_NUMBER;
	res->val_Value.Number =	TRUE;
    }
    return(res);
}

/*
 * (addsym {`sym-name' value-clause})
 */
VALUE *
cmd_addsym(LONG	argc, VALUE *argv)
{
    VALUE *res = &RES;
    while(res && (argc >= 2))
    {
	if(TPLATE2(VTF_STRING, VTF_ANY))
	{
	    VALUE copy;
	    if(dupvalue(&copy, &ARG2))
	    {
		if(addsymbol(ARG1.val_Value.String, STF_GLOBAL,	copy.val_Type, copy.val_Value.Generic))
		    settitlefmt("added symbol %s", (LONG)ARG1.val_Value.String);
		else
		{
		    res	= FALSE;
		    if(copy.val_Type ==	VTF_STRING)
			freestring(copy.val_Value.String);
		}
	    }
	    else
	    {
		settitle(NoMemMsg);
		res = FALSE;
	    }
	}
	argc -=	2;
	argv +=	2;
    }
    if(res)
    {
	res->val_Type =	VTF_NUMBER;
	res->val_Value.Number =	TRUE;
    }
    return(res);
}

/*
 * (remsym {`sym-name'})
 *
 * note	that if	you want you can remove	the primitive commands!
 */
VALUE *
cmd_remsym(LONG	argc, VALUE *argv)
{
    VALUE *res = &RES;
    BOOL rc = TRUE;
    while(rc &&	(argc >= 1))
    {
	if(TPLATE1(VTF_STRING))
	{
	    if(remsymbol(ARG1.val_Value.String))
		settitlefmt("removed symbol %s", (LONG)ARG1.val_Value.String);
	    else
		rc = FALSE;
	}
	argc--;
	argv++;
    }
    res->val_Type = VTF_NUMBER;
    res->val_Value.Number = rc;
    return(res);
}

/*
 * some	arithmetic commands
 */

/* (+ 1	2) */
VALUE *
cmd_plus(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number + ARG2.val_Value.Number);
}

/* (- 1	[2]) */
VALUE *
cmd_minus(LONG argc, VALUE *argv)
{
    if(argc == 1)
    {
	returnnum(-(ARG1.val_Value.Number));
    }
    else
    {
	returnnum(ARG1.val_Value.Number	- ARG2.val_Value.Number);
    }
}

/* (* 1	2) */
VALUE *
cmd_product(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number * ARG2.val_Value.Number);
}

/* (/ 1	2) */
VALUE *
cmd_divide(LONG	argv, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number / ARG2.val_Value.Number);
}

/* (% 1	2) */
VALUE *
cmd_mod(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number % ARG2.val_Value.Number);
}

/* (~ 1) */
VALUE *
cmd_b_not(LONG argc, VALUE *argv)
{
    returnnum(~ARG1.val_Value.Number);
}

/* (! 1) */
VALUE *
cmd_l_not(LONG argc, VALUE *argv)
{
    returnnum(!ARG1.val_Value.Number);
}

/* (| 1	2) */
VALUE *
cmd_b_or(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number | ARG2.val_Value.Number);
}

/* (|| 1 2) */
VALUE *
cmd_l_or(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number || ARG2.val_Value.Number);
}

/* (& 1	2) */
VALUE *
cmd_b_and(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number & ARG2.val_Value.Number);
}

/* (&& 1 2) */
VALUE *
cmd_l_and(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number && ARG2.val_Value.Number);
}

/* (^ 1	2) */
VALUE *
cmd_b_eor(LONG argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number ^ ARG2.val_Value.Number);
}

/* (^^ 1 2) */
VALUE *
cmd_l_eor(LONG argc, VALUE *argv)
{
    if(ARG1.val_Value.Number)
    {
	if(ARG2.val_Value.Number)
	{
	    setnumres(FALSE);
	}
	else
	{
	    setnumres(TRUE);
	}
    }
    else
    {
	setnumres(TRUE);
    }
    return(&RES);
}

/* (<< 1 2) */
VALUE *
cmd_lshift(LONG	argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number << ARG2.val_Value.Number);
}

/* (>> 1 2) */
VALUE *
cmd_rshift(LONG	argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number >> ARG2.val_Value.Number);
}

/*
 * (= `varname' value)
 * returns whatever was	put in the var.
 */
VALUE *
cmd_equals(LONG	argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_ANY))
    {
	LOCALSYM *lsym;

	if(MacDepth && (lsym = (LOCALSYM *)FindName(&(ExeList[MacDepth]->mc_Locals), ARG1.val_Value.String)))
	{
	    releasevalue(&lsym->l_Value);
	    dupvalue(&lsym->l_Value, &ARG2);
	    dupvalue(&RES, &ARG2);
	}
	else if(addsymbol(ARG1.val_Value.String, STF_GLOBAL, ARG2.val_Type, ARG2.val_Value.Generic))
	    dupvalue(&RES, &ARG2);
    }
    return(&RES);
}

/*
 * comparison operators
 */

/*
 * (== `string1' `STRING2')
 * (== 1 2)
 */
VALUE *
cmd_equality(LONG argc,	VALUE *argv)
{
    if((ARG1.val_Type == VTF_STRING) &&	(ARG2.val_Type == VTF_STRING))
    {
	returnnum(!stricmp(ARG1.val_Value.String, ARG2.val_Value.String));
    }
    else if((ARG1.val_Type == VTF_NUMBER) && (ARG2.val_Type == VTF_NUMBER))
    {
	returnnum(ARG1.val_Value.Number	== ARG2.val_Value.Number);
    }
    settitle("syntax error: incorrect arguments to ==");
    returnno();
}

/*
 * (!= `string1' `STRING2')
 * (!= 1 2)
 */
VALUE *
cmd_inequality(LONG argc, VALUE	*argv)
{
    if((ARG1.val_Type == VTF_STRING) &&	(ARG2.val_Type == VTF_STRING))
    {
	returnnum(stricmp(ARG1.val_Value.String, ARG2.val_Value.String));
    }
    else if((ARG1.val_Type == VTF_NUMBER) && (ARG2.val_Type == VTF_NUMBER))
    {
	returnnum(ARG1.val_Value.String	!= ARG2.val_Value.String);
    }
    settitle("syntax error: incorrect arguments to !=");
    returnno();
}

/* (> 1	2) */
VALUE *
cmd_gtthan(LONG	argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number > ARG2.val_Value.Number);
}

/* (< 1	2) */
VALUE *
cmd_ltthan(LONG	argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number < ARG2.val_Value.Number);
}

/* (>= 1 2) */
VALUE *
cmd_gethan(LONG	argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number >= ARG2.val_Value.Number);
}

/* (<= 1 2) */
VALUE *
cmd_lethan(LONG	argc, VALUE *argv)
{
    returnnum(ARG1.val_Value.Number <= ARG2.val_Value.Number);
}

/*
 * (nop)
 */
VALUE *
cmd_nop(LONG argc, VALUE *argv)
{
    returnnum(0);
}

