/* frx -- like rx but different -- see frx.doc */


#include <exec/exec.h>
#include <libraries/dosextens.h>
#include <rexx/storage.h>
#include <rexx/rxslib.h>
#include <paul.h>


struct RxsLib *RexxSysBase;


struct RexxMsg * CreateRexxMsg(struct MsgPort *reply, str extension, str host);
void             DeleteRexxMsg(struct RexxMsg *message);
long		 FillRexxMsg(struct RexxMsg *message, long count, long mask);
void		 ClearRexxMsg(struct RexxMsg *message, long count);
void /* ubyte */ StrcpyU(str dest, str source, long length);
short            StrcmpU(str a, str b, long length);
char             ToUpper(char c);
struct RexxRsrc *FindRsrcNode(struct List *list, char *name, long type);
void             DeleteArgstring(str argstring);

#pragma amicall(RexxSysBase, 0x90, CreateRexxMsg(a0, a1, d0))
#pragma amicall(RexxSysBase, 0x96, DeleteRexxMsg(a0))
#pragma amicall(RexxSysBase, 0xA2, FillRexxMsg(a0, d0, d1))
#pragma amicall(RexxSysBase, 0x9C, ClearRexxMsg(a0, d0))
#pragma amicall(RexxSysBase, 0x114, StrcpyU(a0, a1, d0))
#pragma amicall(RexxSysBase, 0x102, StrcmpU(a0, a1, d0))
#pragma amicall(RexxSysBase, 0x126, ToUpper(d0))
#pragma amicall(RexxSysBase, 0xB4, FindRsrcNode(a0, a1, d0))
#pragma amicall(RexxSysBase, 0x84, DeleteArgstring(a0))

#asm
		public		_ErrorMsg	 ; glue routine
_ErrorMsg:	move.l		_RexxSysBase,a6
		jsr		-$60(a6)
		move.l		a0,d0
		addq.l		#8,d0		 ; NexxStr *  =>  str 
		rts

		public		_CVa2i		; glue routine
_CVa2i:		move.l		_RexxSysBase,a6
		jsr		-$12C(a6)
		move.l		d1,(a3)		; number of digits scanned
		rts
#endasm

str ErrorMsg(long code);
long CVa2i(str buf, long *digits);

#pragma regcall(ErrorMsg(d0))
#pragma regcall(CVa2i(a0,a3))



typedef struct {
    short argc, bargc;
    char argline[600];		/* bigger than necessary */
    str argv[32];		/* all we can use */
    str barg[16];
    ubyte bits[16];
} glob;

#define NOGOBBLE  2
#define MATCHONLY 4
#define REQUIRED  8



void spew(str s)
{
    register BPTR o = ThisProcess()->pr_COS;
    if (o) Write(o, s, (long) strlen(s));
}



/* This version of cliparse features handling of internal quotes BCPL-style
   with *" instead of Manx "".  *N and *E are supported.  With all args
   except argv[0] (the command name) you can see if it was quoted by looking
   at whether argv[n][-1] is a double-quote or a nul. */

void cliparse(glob *g, long alen, register str ap, bool skippy)
{
    register short coml, al;
    bool quoted, star;
    register ubyte c;
    register str poik;

    if (skippy) {
	g->argc = 2;
	coml = strlen(g->argline) + 1;
	coml += strlen(g->argline + coml);
    } else {
	poik = bip(char, bip(struct CommandLineInterface,
			     ThisProcess()->pr_CLI)->cli_CommandName);
	coml = *poik;
	strncpy(g->argline, poik + 1, (size_t) coml);
	g->argline[coml] = '\0';
	*g->argv = &g->argline[0];
	g->argc = 1;
    }
    if (al = alen) {
	poik = g->argline + coml + 1;
	for (;;) {
	    while (al && *ap <= ' ') ap++, al--;
	    if (!al) break;
	    star = false;
	    if (quoted = *ap == '"')
		*(poik++) = *(ap++), al--;
	    g->argv[g->argc] = poik;
	    if (++g->argc > 30) break;
	    while (al && (c = (ubyte) *ap) &&
				(quoted ? star || c != '"' : c > ' ')) {
		if (star) {
		    if (ToUpper(c) == 'E') c = 27;
		    else if (ToUpper(c) == 'N') c = '\n';
		    star = false;
		    *(poik++) = c;
		} else if (!(star = quoted && c == '*'))
		    *(poik++) = c;
		ap++, al--;
	    }
	    if (*ap == '"') ap++, al--;
	    *(poik++) = '\0';
	}
    }
}



str ClipNodeVal(str name)		/* why wasn't this provided? */
{
    struct RexxRsrc *cn = FindRsrcNode(&RexxSysBase->rl_ClipList, name, 0);
    if (cn) return (str) cn->rr_Arg1;		/****** ?????? ******/
    else return null;			/* What about the arg's LENGTH? */
}



short WhatKeyword(str word, register str plate, glob *g)
{
    short w;
    bool start = true;
    for (w = 0; *plate; plate++) {
	if (start && !g->barg[w]) {
	    short l = 0;
	    register str p = plate;
	    while (*p && *p != '/' && *p != ',' && *p != '=')
		p++, l++;
	    if (l == strlen(word) && !StrcmpU(word, plate, (long) l))
		return w;
	    start = false;
	} else if (*plate == '=')
	    start = true;
	else if (*plate == ',')
	    start = true, w++;
	else start = false;
    }
    return -1;
}



void Chew(glob *g, str plate)
{
    short i, n;
    str t;
    bool slash = false;

    for (t = plate; *t; t++)
        if (*t == ',')
	    g->bargc++;
	else if (*t == '/')
	    slash = true;
	else if (slash) {
	    char tt = ToUpper(*t);
	    if (tt == 'S')
		g->bits[g->bargc] |= NOGOBBLE | MATCHONLY;
	    else if (tt == 'K')
		g->bits[g->bargc] |= MATCHONLY;
	    else if (tt == 'A')
		g->bits[g->bargc] |= REQUIRED;
	    slash = false;
	}
    g->bargc++;
    for (i = 2; i < g->argc; i++)
	if (!g->argv[i][-1] && ~(n = WhatKeyword(g->argv[i], plate, g))) {
	    if (!(g->bits[n] & NOGOBBLE)) {
		g->argv[i] = null;
		if (!g->argv[++i]) {
		    g->argc = 1001;
		    spew("Bad arguments:  no value after template keyword.\n");
		    return;
		}
	    } else {			/*  /S  */
		register str a = g->argv[i];
		while (*a) *(a++) = ToUpper(*a);
	    }
	    g->barg[n] = g->argv[i];
	    g->argv[i] = null;
	}
    for (i = 2; i < g->argc; i++)
	if (g->argv[i]) {
	    for (n = 0; n < g->bargc; n++)
		if (!g->barg[n] && !(g->bits[n] & MATCHONLY)) break;
	    if (n >= g->bargc) {
		g->argc = 1002;
		spew("Bad arguments:  too many for template.\n");
		return;
	    }
	    g->barg[n] = g->argv[i];
	}
    for (n = 0; n < g->bargc; n++)
	if (!g->barg[n] && g->bits[n] & REQUIRED) {
	    g->argc = 1003;
	    spew("Bad arguments:  required argument not given.\n");
	    return;
	}
    g->argc = g->bargc + 2;
    for (n = 0; n < g->bargc; n++)
	g->argv[n + 2] = g->barg[n];
    g->argv[g->argc] = null;
}



long _main(long alen, str aptr)
{
    struct MsgPort *duh, *zex;
    struct RexxMsg *mess, *m2;
    long air = 0, ret = 0;
    short i;
    glob g;
    char clipname[300], newargline[300];
    str a1, template;

    memset(&g, 0, sizeof(g));
    if (!(zex = FindPort("REXX")) ||
		!(RexxSysBase = (adr) OpenLibrary("rexxsyslib.library", 0))) {
	spew("No ARexx!\n");
	air = ERROR_INVALID_RESIDENT_LIBRARY;
	ret = 20;
	goto quit5;
    }
    cliparse(&g, alen, aptr, false);
    if (!g.argv[1] || (*g.argv[1] == '?' && !g.argv[1][1]
					&& !g.argv[1][-1] && !g.argv[2])) {
	spew("Usage: ");
	spew(*g.argv);
	spew(" RexxFunction [arg] [arg] ...\n");
	ret = 20;
	air = ERROR_LINE_TOO_LONG;
	goto quit3;
    }

    a1 = g.argv[1];
    if (!StrcmpU(a1, "REXX:", 5))
	a1 += 5;
    i = strlen(a1);
    StrcpyU(clipname, a1, (long) i);
    strcpy(clipname + i, "-TEMPLATE");
    i = 0;
    template = ClipNodeVal(clipname);
    while (*g.argv[2] == '?' && !g.argv[2][1] && !g.argv[2][-1] && !g.argv[3]) {
	if (Input()) {
	    if (template) {
		spew(template);
		spew(": ");
	    } else {
		spew(clipname);
		spew(" not found\nArguments: ");
	    }
	    alen = Read(Input(), newargline, 299);
	    if (alen < 0) alen = 0;
	} else
	    alen = 0;
	cliparse(&g, alen, newargline, true);
	g.argv[g.argc] = null;
    }

    if (template)
	Chew(&g, template);
    /* we'll just ASSUME nobody deletes the template while we're using it */
    if (g.argc > 17) {
	if (g.argc < 100) spew("Fifteen function arguments maximum!\n");
	air = ERROR_LINE_TOO_LONG;
	ret = 20;
	goto quit3;
    }
    if (!(duh = CreatePort(null, 0))) {
	spew("Can't open message port!\n");
	air = ERROR_NO_FREE_STORE;
	ret = 20;
	goto quit3;
    }
    if (!(mess = CreateRexxMsg(duh, null, null))) {
	spew("Can't create RexxMsg!\n");
	air = ERROR_NO_FREE_STORE;
	ret = 20;
	goto quit2;
    }

    for (i = 0; i < g.argc - 1; i++)
	mess->rm_Args[i] = (adr) g.argv[i + 1];
    if (!FillRexxMsg(mess, (long) g.argc - 1, 0)) {
	spew("No memory!\n");
	air = ERROR_NO_FREE_STORE;
	ret = 20;
	goto quit1;
    }
    mess->rm_Action = RXFUNC | RXFF_RESULT | g.argc - 1;
    PutMsg(zex, (adr) mess);
    WaitPort(duh);
    while (!(m2 = (adr) GetMsg(duh)) || m2 != mess)
	if (!m2) WaitPort(duh);

    if (mess->rm_Result2)
	if (mess->rm_Result1) {
	    str s = ErrorMsg(mess->rm_Result2);
	    spew("ARexx error: ");
	    spew((str) s);
	    spew(".\n");
	    ret = mess->rm_Result1;
	    if ((air = mess->rm_Result2) == 1)		/* program not found */
		ret = 10;				/* instead of 5 */
	} else {
	    struct RexxArg *r = (adr) ((str) mess->rm_Result2 - 8);
	    long l;
	    long x = CVa2i(r->ra_Buff, &l);
	    if (l == r->ra_Length)
		ret = air = x;
	    else {
		air = 47;
		ret = 10;
		spew("ARexx error: arithmetic conversion error.\n");
	    }
	    DeleteArgstring((str) mess->rm_Result2);
	}
    else
	air = ret = mess->rm_Result1;

    ClearRexxMsg(mess, (long) g.argc - 1);
quit1:
    DeleteRexxMsg(mess);
quit2:
    DeletePort(duh);
quit3:
    CloseLibrary((adr) RexxSysBase);
quit5:
    ThisProcess()->pr_Result2 = air;
    return ret;
}
