/*
 * Source generated with ARexxBox 1.00 (Jun 19 1992)
 * which is Copyright (c) 1992 Michael Balzer
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <rexx/storage.h>
#include <rexx/rxslib.h>

#ifdef __GNUC__
/* GCC needs all struct defs */
#include <dos/exall.h>
#include <graphics/graphint.h>
#include <intuition/classes.h>
#include <devices/keymap.h>
#include <exec/semaphores.h>
#endif

#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/rexxsyslib_protos.h>

#ifndef __NO_PRAGMAS

#ifdef AZTEC_C
#include <pragmas/exec_lib.h>
#include <pragmas/dos_lib.h>
#include <pragmas/rexxsyslib_lib.h>
#endif

#ifdef LATTICE
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/rexxsyslib_pragmas.h>
#endif

#endif /* __NO_PRAGMAS */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifndef _toupper
#define _toupper(x) ((x) & 0x5f)
#endif

#include <dos/rdargs.h>

#define NO_GLOBALS
#include "rx_test2.h"

struct rxs_stemnode
{
	struct rxs_stemnode *succ;
	char *name;
	char *value;
};


extern struct Library *SysBase, *DOSBase, *RexxSysBase;

char RexxPortBaseName[80] = "TEST2";

struct rxs_command rxs_commandlist[] =
{
	{ "ALIAS", "GLOBAL/S,NAME/A,COMMAND/F", NULL, 0, rx_alias, 1 },
	{ "CMDSHELL", "OPEN/S,CLOSE/S", NULL, 0, rx_cmdshell, 1 },
	{ "DISABLE", "GLOBAL/S,NAMES/M", NULL, 0, rx_disable, 1 },
	{ "ENABLE", "GLOBAL/S,NAMES/M", NULL, 0, rx_enable, 1 },
	{ "FAULT", "NUMBER/N/A", "DESCRIPTION", RESINDEX(rxd_fault), rx_fault, 1 },
	{ "HELP", "COMMAND,PROMPT/S", "COMMANDDESC,COMMANDLIST/M", RESINDEX(rxd_help), rx_help, 1 },
	{ "RX", "CONSOLE/S,ASYNC/S,COMMAND/F", "RC/N,RESULT", RESINDEX(rxd_rx), rx_rx, 1 },
	{ NULL, NULL, NULL, NULL, NULL }
};


void ReplyRexxCommand(
	struct RexxMsg	*rexxmessage,
	long			primary,
	long			secondary,
	char			*result )
{
	if( rexxmessage->rm_Action & RXFF_RESULT )
	{
		if( primary == 0 )
		{
			secondary = result
				? (long) CreateArgstring( result, strlen(result) )
				: (long) NULL;
		}
		else
		{
			char buf[16];
			
			if( primary > 0 )
			{
				sprintf( buf, "%ld", secondary );
				result = buf;
			}
			else
			{
				primary = -primary;
				result = (char *) secondary;
			}
			
			SetRexxVar( (struct Message *) rexxmessage,
				"RC2", result, strlen(result) );
			
			secondary = 0;
		}
	}
	else if( primary < 0 )
		primary = -primary;
	
	rexxmessage->rm_Result1 = primary;
	rexxmessage->rm_Result2 = secondary;
	ReplyMsg( (struct Message *) rexxmessage );
}


void FreeRexxCommand( struct RexxMsg *rexxmessage )
{
	if( !rexxmessage->rm_Result1 && rexxmessage->rm_Result2 )
		DeleteArgstring( (char *) rexxmessage->rm_Result2 );

	if( rexxmessage->rm_Stdin )
		Close( rexxmessage->rm_Stdin );

	if( rexxmessage->rm_Stdout &&
		rexxmessage->rm_Stdout != rexxmessage->rm_Stdin )
		Close( rexxmessage->rm_Stdout );

	DeleteArgstring( (char *) ARG0(rexxmessage) );
	DeleteRexxMsg( rexxmessage );
}


struct RexxMsg *SendRexxCommand( struct RexxHost *host, char *buff, BPTR fh )
{
	struct MsgPort *rexxport;
	struct RexxMsg *rexx_command_message;

	Forbid();

	if( (rexxport = FindPort(RXSDIR)) == NULL )
	{
		Permit();
		return( NULL );
	}

	if( (rexx_command_message = CreateRexxMsg( host->port,
		REXX_EXTENSION, host->port->mp_Node.ln_Name)) == NULL )
	{
		Permit();
		return( NULL );
	}

	if( (rexx_command_message->rm_Args[0] =
		CreateArgstring(buff,strlen(buff))) == NULL )
	{
		DeleteRexxMsg(rexx_command_message);
		Permit();
		return( NULL );
	}

	rexx_command_message->rm_Action = RXCOMM | RXFF_RESULT;
	rexx_command_message->rm_Stdin  = fh;
	rexx_command_message->rm_Stdout = fh;
	
	PutMsg( rexxport, &rexx_command_message->rm_Node );
	Permit();
	
	++host->replies;
	return( rexx_command_message );
}


void CloseDownARexxHost( struct RexxHost *host )
{
	struct RexxMsg *rexxmsg;

	while( host->replies > 0 )
	{
		WaitPort( host->port );
		
		while( rexxmsg = (struct RexxMsg *)GetMsg(host->port) )
		{
			if( rexxmsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG )
			{
				FreeRexxCommand( rexxmsg );
				--host->replies;
			}
			else
				ReplyRexxCommand( rexxmsg, -20, (long) "Host closing down", NULL );
		}
	}
	
	if( host->rdargs ) FreeDosObject( DOS_RDARGS, host->rdargs );
	if( host->port ) DeletePort( host->port );
	free( host );
}


struct RexxHost *SetupARexxHost( char *basename )
{
	struct RexxHost *host;
	int ext = 0;
	
	if( !(host = calloc(sizeof *host, 1)) )
		return NULL;
	
	if( !basename )
		basename = RexxPortBaseName;
	
	strcpy( host->portname, basename );
	
	if( !host->portname[0] )
	{
		free( host );
		return NULL;
	}
	
	while( FindPort(host->portname) )
		sprintf( host->portname, "%s.%d", basename, ++ext );
	
	if( !(host->port = CreatePort(host->portname, 0)) )
	{
		free( host );
		return NULL;
	}
	
	if( !(host->rdargs = AllocDosObject(DOS_RDARGS, NULL)) )
	{
		DeletePort( host->port );
		free( host );
		return NULL;
	}
	
	host->rdargs->RDA_Flags = RDAF_NOPROMPT;
	
	return( host );
}


struct rxs_command *FindRXCommand( char *com )
{
	struct rxs_command *rxc = rxs_commandlist;
	int n = strlen( com );
	
	while( rxc->command )
	{
		if( strnicmp(com, rxc->command, n) == 0 )
			break;
		rxc++;
	}
	
	if( rxc->command )
		return( rxc );
	else
		return( NULL );
}


static struct rxs_command *ParseRXCommand( char **arg )
{
	char com[256], *s, *t;
	
	s = *arg;
	t = com;
	
	while( *s && *s != ' ' && *s != '\n' )
		*t++ = *s++;
	
	*t = '\0';
	while( *s == ' ' ) ++s;
	*arg = s;
	
	return( FindRXCommand( com ) );
}


static char *CreateVAR( struct rxs_stemnode *stem )
{
	char *var;
	struct rxs_stemnode *s;
	long size = 0;
	
	if( !stem || stem == (struct rxs_stemnode *) -1L )
		return( (char *) stem );
	
	for( s = stem; s; s = s->succ )
		size += strlen( s->value ) + 1;
	
	var = malloc( size + 1 );
	*var = '\0';
	
	for( s = stem; s; s = s->succ )
	{
		strcat( var, s->value );
		if( s->succ )
			strcat( var, " " );
	}
	
	return( var );
}


static struct rxs_stemnode *new_stemnode( struct rxs_stemnode **first, struct rxs_stemnode **old )
{
	struct rxs_stemnode *new;
	
	if( !(new = calloc(sizeof(struct rxs_stemnode), 1)) )
	{
		return( NULL );
	}
	else
	{
		if( *old )
		{
			(*old)->succ = new;
			(*old) = new;
		}
		else
		{
			*first = *old = new;
		}
	}
	
	return( new );
}


static void free_stemlist( struct rxs_stemnode *first )
{
	struct rxs_stemnode *next;
	
	for( ; first; first = next )
	{
		next = first->succ;
		free( first->name );
		free( first->value );
		free( first );
	}
}


static struct rxs_stemnode *CreateSTEM( struct rxs_command *rxc, LONG *resarray, char *stembase )
{
	struct rxs_stemnode *first = NULL, *old = NULL, *new;
	char resb[512], *rs, *rb;
	char longbuff[16];

	if( stembase )
		strcpy( resb, stembase );
	else
		*resb = '\0';
	
	rb = resb + strlen(resb);
	rs = rxc->results;
	
	while( *rs )
	{
		char *t = rb;
		BOOL optn = FALSE, optm = FALSE;
		
		while( *rs && *rs != ',' )
		{
			if( *rs == '/' )
			{
				++rs;
				if( *rs == 'N' ) optn = TRUE;
				else if( *rs == 'M' ) optm = TRUE;
			}
			else
				*t++ = *rs;
			
			++rs;
		}
		
		if( *rs == ',' ) ++rs;
		*t = '\0';
		
		/*
		 * Resultat(e) erzeugen
		 */
		
		if( !*resarray )
		{
			++resarray;
			continue;
		}
		
		if( optm )
		{
			long *r, index = 0;
			LONG **subarray = (LONG **) *resarray++;
			struct rxs_stemnode *countnd;
			
			/* Anzahl der Elemente */
			
			if( !(new = new_stemnode(&first, &old)) )
			{
				free_stemlist( first );
				return( (struct rxs_stemnode *) -1L );
			}
			countnd = new;
			
			/* Die Elemente selbst */
			
			while( r = *subarray++ )
			{
				if( !(new = new_stemnode(&first, &old)) )
				{
					free_stemlist( first );
					return( (struct rxs_stemnode *) -1L );
				}
				
				sprintf( t, ".%ld", index++ );
				new->name = strdup( resb );
				
				if( optn )
				{
					sprintf( longbuff, "%ld", *r );
					new->value = strdup( longbuff );
				}
				else
				{
					new->value = strdup( (char *) r );
				}
			}
			
			/* Die Count-Node */
			
			strcpy( t, ".COUNT" );
			countnd->name = strdup( resb );
			
			sprintf( longbuff, "%ld", index );
			countnd->value = strdup( longbuff );
		}
		else
		{
			/* Neue Node anlegen */
			if( !(new = new_stemnode(&first, &old)) )
			{
				free_stemlist( first );
				return( (struct rxs_stemnode *) -1L );
			}
			
			new->name = strdup( resb );
			
			if( optn )
			{
				sprintf( longbuff, "%ld", *((long *) *resarray) );
				new->value = strdup( longbuff );
				++resarray;
			}
			else
			{
				new->value = strdup( (char *) (*resarray++) );
			}
		}
	}
	
	return( first );
}


static void DoRXCommand( struct RexxHost *host, struct RexxMsg *rexxmsg )
{
	struct rxs_command *rxc;
	char *argb = NULL, *arg;
	
	LONG *array = NULL;
	LONG *argarray;
	LONG *resarray;
	
	char *cargstr = NULL;
	long rc=20, rc2=0;
	char *result = NULL;
	
	if( !(argb = malloc(strlen(ARG0(rexxmsg)) + 2)) )
	{
		rc2 = ERROR_NO_FREE_STORE;
		goto drc_cleanup;
	}
	
	/* welches Kommando? */
	
	strcpy( argb, ARG0(rexxmsg) );
	strcat( argb, "\n" );
	arg = argb;
	
	if( !( rxc = ParseRXCommand( &arg ) ) )
	{
		if( arg = ExpandRXCommand( host, ARG0(rexxmsg) ) )
		{
			free( argb );
			if( !(argb = malloc( strlen(arg) + 2 )) )
			{
				rc2 = ERROR_NO_FREE_STORE;
				goto drc_cleanup;
			}
			
			strcpy( argb, arg );
			strcat( argb, "\n" );
			free( arg );
			arg = argb;
			
			rxc = ParseRXCommand( &arg );
		}
	}
	
	if( !rxc )
	{
		rc2 = ERROR_NOT_IMPLEMENTED;
		goto drc_cleanup;
	}
	
	if( !(rxc->flags & ARB_CF_ENABLED) )
	{
		rc = -10;
		rc2 = (long) "Command disabled";
		goto drc_cleanup;
	}
	
	/* Speicher für Argumente etc. holen */
	
	(rxc->function)( host, (void **) &array, RXIF_INIT );
	cargstr = malloc( rxc->args ? 15+strlen(rxc->args) : 15 );
	
	if( !array || !cargstr )
	{
		rc2 = ERROR_NO_FREE_STORE;
		goto drc_cleanup;
	}
	
	argarray = array + 2;
	resarray = array + rxc->resindex;
	
	/* Argumente parsen */
	
	if( rxc->results )
		strcpy( cargstr, "VAR/K,STEM/K" );
	else
		*cargstr = '\0';
	
	if( rxc->args )
	{
		if( *cargstr )
			strcat( cargstr, "," );
		strcat( cargstr, rxc->args );
	}
	
	if( *cargstr )
	{
		host->rdargs->RDA_Source.CS_Buffer = arg;
		host->rdargs->RDA_Source.CS_Length = strlen(arg);
		host->rdargs->RDA_Source.CS_CurChr = 0;
		host->rdargs->RDA_DAList = NULL;
		host->rdargs->RDA_Buffer = NULL;
		
		if( !ReadArgs(cargstr, argarray, host->rdargs) )
		{
			rc = 10;
			rc2 = IoErr();
			goto drc_cleanup;
		}
	}
	
	/* Funktion aufrufen */
	
	(rxc->function)( host, (void **) &array, RXIF_ACTION );
	
	rc = array[0];
	rc2 = array[1];
	
	/* Resultat(e) auswerten */
	
	if( rxc->results && rc==0 &&
		(rexxmsg->rm_Action & RXFF_RESULT) )
	{
		struct rxs_stemnode *stem, *s;
		
		stem = CreateSTEM( rxc, resarray, (char *)argarray[1] );
		result = CreateVAR( stem );
		
		if( result == (char *) -1 )
		{
			result = NULL;
			rc = 20;
			rc2 = ERROR_NO_FREE_STORE;
		}
		else if( result )
		{
			if( argarray[0] )
			{
				/* VAR */
				
				if( SetRexxVar( (struct Message *) rexxmsg, (char *)argarray[0], result, strlen(result) ) )
				{
					rc = -10;
					rc2 = (long) "Unable to set Rexx variable";
				}
				
				free( result );
				result = NULL;
			}
			
			if( argarray[1] )
			{
				/* STEM */
				for( s = stem; s; s = s->succ )
					rc |= SetRexxVar( (struct Message *) rexxmsg, s->name, s->value, strlen(s->value) );
				
				if( rc )
				{
					rc = -10;
					rc2 = (long) "Unable to set Rexx variable";
				}
				
				if( result ) free( result );
				result = NULL;
			}
		}
		
		if( stem > (struct rxs_stemnode *) NULL )
			free_stemlist( stem );
	}
	
drc_cleanup:

	/* Nur RESULT, wenn weder VAR noch STEM */
	
	ReplyRexxCommand( rexxmsg, rc, rc2, result );
			
	/* benutzten Speicher freigeben */
	
	if( result ) free( result );
	FreeArgs( host->rdargs );
	if( cargstr ) free( cargstr );
	if( array ) (rxc->function)( host, (void **) &array, RXIF_FREE );
	if( argb ) free( argb );
}


void ARexxDispatch( struct RexxHost *host )
{
	struct RexxMsg *rexxmsg;

	while( rexxmsg = (struct RexxMsg *) GetMsg(host->port) )
	{
		if( rexxmsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG )
		{
			FreeRexxCommand( rexxmsg );
			--host->replies;
		}
		
		else if( ARG0(rexxmsg) )
		{
			DoRXCommand( host, rexxmsg );
		}
	}
}


void DoShellCommand( struct RexxHost *host, char *comline, BPTR fhout )
{
	struct rxs_command *rxc;
	char *argb = NULL, *arg;
	
	LONG *array = NULL;
	LONG *argarray;
	LONG *resarray;
	
	struct RDArgs *rdargs = NULL;
	char *cargstr = NULL;
	long rc=10, rc2=0;
	
	rdargs = AllocDosObject( DOS_RDARGS, NULL );
	argb   = malloc( strlen(comline) + 2 );
	
	if( !rdargs || !argb )
	{
		rc2 = ERROR_NO_FREE_STORE;
		goto dsc_cleanup;
	}
	
	/* welches Kommando? */
	
	strcpy( argb, comline );
	strcat( argb, "\n" );
	arg = argb;
	
	if( !( rxc = ParseRXCommand( &arg ) ) )
	{
		if( arg = ExpandRXCommand( host, comline ) )
		{
			free( argb );
			if( !(argb = malloc( strlen(arg) + 2 )) )
			{
				rc2 = ERROR_NO_FREE_STORE;
				goto dsc_cleanup;
			}
			
			strcpy( argb, arg );
			strcat( argb, "\n" );
			free( arg );
			arg = argb;
			
			rxc = ParseRXCommand( &arg );
		}
	}
	
	if( !rxc )
	{
		rc2 = ERROR_NOT_IMPLEMENTED;
		goto dsc_cleanup;
	}
	
	if( !(rxc->flags & ARB_CF_ENABLED) )
	{
		rc = -10;
		rc2 = (long) "Command disabled";
		goto dsc_cleanup;
	}
	
	/* Speicher für Argumente etc. holen */
	
	(rxc->function)( host, (void **) &array, RXIF_INIT );
	cargstr = malloc( rxc->args ? 512+strlen(rxc->args) : 512 );
	
	if( !array || !cargstr )
	{
		rc2 = ERROR_NO_FREE_STORE;
		goto dsc_cleanup;
	}
	
	argarray = array + 2;
	resarray = array + rxc->resindex;
	
	/* Argumente parsen */
	
	*cargstr = '\0';
	
	if( rxc->results )
		strcpy( cargstr, "VAR/K,STEM/K" );
	
	if( rxc->args )
	{
		if( *cargstr )
			strcat( cargstr, "," );
		strcat( cargstr, rxc->args );
	}
	
	if( *cargstr )
	{
		rdargs->RDA_Source.CS_Buffer = arg;
		rdargs->RDA_Source.CS_Length = strlen(arg);
		rdargs->RDA_Source.CS_CurChr = 0;
		rdargs->RDA_DAList = NULL;
		rdargs->RDA_Flags  = RDAF_NOPROMPT;
		
		if( !ReadArgs(cargstr, argarray, rdargs) )
		{
			rc = 10;
			rc2 = IoErr();
			goto dsc_cleanup;
		}
	}
	
	/* Funktion aufrufen */
	
	(rxc->function)( host, (void **) &array, RXIF_ACTION );
	
	/* Resultat(e) ausgeben */
	
	if( rxc->results && array[0]==0 && fhout )
	{
		struct rxs_stemnode *stem, *s;
		char *result;
		
		stem = CreateSTEM( rxc, resarray, (char *)argarray[1] );
		result = CreateVAR( stem );
		
		if( (long) result == -1L )
		{
			if( stem > (struct rxs_stemnode *) NULL )
				free_stemlist( stem );
			rc2 = ERROR_NO_FREE_STORE;
			goto dsc_cleanup;
		}
		
		if( result )
		{
			if( argarray[0] )
			{
				/* VAR */
				FPrintf( fhout, "%s = %s\n", argarray[0], result );
				free( result );
				result = NULL;
			}
			
			if( argarray[1] )
			{
				/* STEM */
				for( s = stem; s; s = s->succ )
					FPrintf( fhout, "%s = %s\n", s->name, s->value );
				if( result ) free( result );
				result = NULL;
			}
		}
		
		/* Nur RESULT, wenn weder VAR noch STEM sein soll */
		if( result )
		{
			FPrintf( fhout, "%s\n", result );
			free( result );
		}
		
		if( stem > (struct rxs_stemnode *) NULL )
			free_stemlist( stem );
	}
	
	/* Rückgabewert */

	rc  = array[0];
	rc2 = array[1];

dsc_cleanup:

	arg = NULL;
	
	if( rc2 )
	{
		if( !cargstr ) cargstr = malloc( 512 );
		
		if( !cargstr )
		{
			arg = "ERROR: Absolutely out of memory";
		}
		else if( rc > 0 )
		{
			if( Fault( rc2, "ERROR", cargstr, 512 ) )
				arg = cargstr;
			else
				arg = "ERROR: Unknown Problem";
		}
		else if( rc < 0 )
		{
			strcpy( cargstr, "ERROR: " );
			strncat( cargstr, (char *) rc2, 500 );
			arg = cargstr;
		}
	}
	
	if( arg ) FPrintf( fhout, "%s\n", arg );
	
	/* benutzten Speicher freigeben */
	
	if( cargstr ) free( cargstr );
	if( array ) (rxc->function)( host, (void **) &array, RXIF_FREE );
	if( argb ) free( argb );
	if( rdargs )
	{
		FreeArgs( rdargs );
		FreeDosObject( DOS_RDARGS, rdargs );
	}
}


void CommandShell( struct RexxHost *host, BPTR fhin, BPTR fhout, char *prompt )
{
	char comline[512], *s;
	struct RexxMsg *rm;

	if( !fhin )
		return;
	
	host->flags |= ARB_HF_CMDSHELL;
	do
	{
		/* Prompt ausgeben */
		
		if( fhout && prompt )
			FPuts( fhout, prompt );
		
		/* Befehl einlesen und abarbeiten */
		
		if( s = FGets(fhin, comline, 512) )
		{
			while( *s==' ' || *s=='\t' ) s++;
			
			if( *s != '\n' )
				DoShellCommand( host, s, fhout );
		}
		else
		{
			host->flags &= ~ARB_HF_CMDSHELL;
		}
		
		/* Port des Hosts leeren */
		
		while( rm = (struct RexxMsg *) GetMsg(host->port) )
		{
			/* Reply? */
			if( rm->rm_Node.mn_Node.ln_Type == NT_REPLYMSG )
			{
				FreeRexxCommand( rm );
				--host->replies;
			}
			
			/* sonst Kommando -> Fehler */
			else
			{
				ReplyRexxCommand( rm, -20, (long)
					"CommandShell Port", NULL );
			}
		}
	}
	while( host->flags & ARB_HF_CMDSHELL );
}

