/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: RexxHostLib.c
 *	Created ..: Sunday 07-Jan-90 18:55
 *	Revision .: 12
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	26-Apr-90       Olsen           Supports short integers (16 bits)
 *	13-Apr-90       Olsen           Added assembly language routines
 *	31-Mar-90       Olsen           Rewrote RexxStrCmp
 *	19-Mar-90       Olsen           More sanity checks
 *	17-Mar-90       Olsen           Added RexxStrCmp, rewrote
 *	                                CreateRexxHost/DeleteRexxHost,
 *	                                ANSIfication
 *	16-Mar-90       Olsen           Rework for Aztec 5.0 release
 *	07-Jan-90       Olsen           Added string conversion functions
 *	07-Jan-90       Olsen           Added GetToken
 *	07-Jan-90       Olsen           - Empty log message -
 *	07-Jan-90       Olsen           Removed bug in SendRexxCommand
 *	07-Jan-90       Olsen           - Empty log message -
 *	07-Jan-90       Olsen           Created this file!
 *
 ****************************************************************************
 *
 *	This Amiga shared library is based on example source code
 *	written by Gary Samad & Bill Hawes.
 *
 * $Revision Header ********************************************************/
 #define REVISION 12

	/* The protos. */

struct RexxMsg	*CreateRexxMsg(struct MsgPort *,char *,char *);
STRPTR		 CreateArgstring(char *,LONG);
VOID		 DeleteRexxMsg(struct RexxMsg *);
VOID		 DeleteArgstring(char *);
STATIC LONG	 AmigaToUpper(LONG);

	/* The magic pragma. */

#pragma regcall(AmigaToUpper(d0))

	/* Bill Hawes didn't distribute an .fd file along
	 * with the 1.10 release of ARexx. I had to put these
	 * pragmas together by hand.
	 */

#pragma amicall(RexxSysBase, 0x90, CreateRexxMsg(a0,a1,d0))
#pragma amicall(RexxSysBase, 0x7e, CreateArgstring(a0,d0))
#pragma amicall(RexxSysBase, 0x96, DeleteRexxMsg(a0))
#pragma amicall(RexxSysBase, 0x84, DeleteArgstring(a0))

	/* Global revision identifier. */

LONG Revision = REVISION;

	/* CreateRexxHost(HostName):
	 *
	 *	Creates a RexxHost (special MsgPort) with a given name.
	 *	Returns NULL if port already exists.
	 */

struct RexxHost *
CreateRexxHost(STRPTR HostName)
{
	struct RexxHostBase	*RexxHostBase;
	struct RexxHost		*RexxHost = NULL;

		/* Our pointer to ExecBase. */

	extern struct ExecBase	*SysBase;

	if(HostName && HostName[0])
	{
			/* Already present? */

		if(!FindPort((char *)HostName))
		{
			LONG SigBit;

				/* Allocate a signal bit. */

			if((SigBit = AllocSignal(-1)) == -1)
				goto Quit;

				/* Allocate the port body. */

			if(!(RexxHost = (struct RexxHost *)AllocMem(sizeof(struct RexxHost),MEMF_PUBLIC | MEMF_CLEAR)))
			{
				FreeSignal(SigBit);
				goto Quit;
			}

				/* Initialize the MsgPort node head. */

			RexxHost -> rh_Port . mp_Node . ln_Type	= NT_MSGPORT;
			RexxHost -> rh_Port . mp_Node . ln_Pri	= 1;

				/* Allocate memory for MsgPort name. */

			if(!(RexxHost -> rh_Port . mp_Node . ln_Name = (char *)AllocMem(StrLen((char *)HostName) + 1,MEMF_PUBLIC)))
			{
				FreeMem(RexxHost,sizeof(struct RexxHost));
				FreeSignal(SigBit);

				RexxHost = NULL;

				goto Quit;
			}

				/* Copy the name. */

			StrCpy(RexxHost -> rh_Port . mp_Node . ln_Name,(char *)HostName);

				/* Deal with the rest of the flags. */

			RexxHost -> rh_Port . mp_Flags		= PA_SIGNAL;
			RexxHost -> rh_Port . mp_SigBit		= SigBit;
			RexxHost -> rh_Port . mp_SigTask	= SysBase -> ThisTask;

				/* A dummy ID. */

			RexxHost -> rh_SpecialID = 'REXX';

				/* Finally add it to the public port list. */

			AddPort(&RexxHost -> rh_Port);
		}
	}

Quit:	return(RexxHost);
}

	/* DeleteRexxHost(RexxHost):
	 *
	 *	Deletes a MsgPort as created by CreateRexxHost().
	 *	Returns NULL, so user can do 'Host = DeleteRexxHost(Host);'.
	 */

VOID *
DeleteRexxHost(struct RexxHost *RexxHost)
{
	struct RexxHostBase *RexxHostBase;

	if(RexxHost && RexxHost -> rh_SpecialID == 'REXX')
	{
			/* Remove it from the public list. */

		RemPort(&RexxHost -> rh_Port);

			/* Make it difficult to reuse it by accident. */

		RexxHost -> rh_Port . mp_Node . ln_Type		= 0xFF;
		RexxHost -> rh_Port . mp_MsgList . lh_Head	= (struct Node *)-1;

			/* Free the name. */

		FreeMem(RexxHost -> rh_Port . mp_Node . ln_Name,StrLen(RexxHost -> rh_Port . mp_Node . ln_Name) + 1);

			/* Free the allocated signal bit. */

		FreeSignal(RexxHost -> rh_Port . mp_SigBit);

			/* Free the body. */

		FreeMem(RexxHost,sizeof(struct RexxHost));
	}

	return(NULL);
}

	/* SendRexxCommand(HostPort,CommandString,FileExtension,HostName):
	 *
	 *	Sends a command to the rexx server, requires pointers
	 *	to the MsgPort of the calling Host and the command string.
	 *	File extension and host name are optional and may be
	 *	NULL.
	 */

LONG
SendRexxCommand(struct RexxHost *HostPort,STRPTR CommandString,STRPTR FileExtension,STRPTR HostName)
{
	struct RexxHostBase *RexxHostBase;

	struct MsgPort	*RexxPort = (struct MsgPort *)FindPort(RXSDIR);
	struct RexxMsg	*HostMessage;

		/* Valid pointers given? */

	if(!CommandString || !HostPort || HostPort -> rh_SpecialID != 'REXX' || !RexxPort)
		return(FALSE);

		/* No special host name given? Take the MsgPort name. */

	if(!HostName)
		HostName = (STRPTR)HostPort -> rh_Port . mp_Node . ln_Name;

		/* No file name extension? Take the default. */

	if(!FileExtension)
		FileExtension = (STRPTR)"rexx";

		/* Create the message. */

	if(!(HostMessage = CreateRexxMsg((struct MsgPort *)HostPort,(char *)FileExtension,(char *)HostName)))
		return(FALSE);

		/* Add the command. */

	if(!(HostMessage -> rm_Args[0] = CreateArgstring((char *)CommandString,StrLen((char *)CommandString))))
	{
		DeleteRexxMsg(HostMessage);
		return(FALSE);
	}

		/* This is a command, not a function. */

	HostMessage -> rm_Action = RXCOMM;

		/* Release it... */

	PutMsg(RexxPort,HostMessage);

		/* Successful action. */

	return(TRUE);
}

	/* FreeRexxCommand(RexxMessage):
	 *
	 *	Frees the contents of a RexxMsg.
	 */

VOID
FreeRexxCommand(struct RexxMsg *RexxMessage)
{
	struct RexxHostBase *RexxHostBase;

		/* Valid pointer given? */

	if(RexxMessage && RexxMessage -> rm_Node . mn_Node . ln_Type == NT_REPLYMSG)
	{
			/* Remove argument. */

		if(RexxMessage -> rm_Args[0])
			DeleteArgstring((char *)RexxMessage -> rm_Args[0]);

			/* Free the message. */

		DeleteRexxMsg(RexxMessage);
	}
}

	/* ReplyRexxCommand(RexxMessage,Primary,Secondary,Result):
	 *
	 *	Sends a RexxMsg back to the rexx server, can include
	 *	result codes.
	 */

VOID
ReplyRexxCommand(struct RexxMsg *RexxMessage,LONG Primary,LONG Secondary,STRPTR Result)
{
	struct RexxHostBase *RexxHostBase;

		/* Valid pointer given? */

	if(RexxMessage && RexxMessage -> rm_Node . mn_Node . ln_Type == NT_MESSAGE)
	{
			/* No secondary result and results wanted? */

		if(Secondary == NULL && (RexxMessage -> rm_Action & RXFF_RESULT))
		{
				/* Build result string... */

			if(Result)
				Secondary = (LONG)CreateArgstring((char *)Result,StrLen((char *)Result));
		}

			/* Set both results... */

		RexxMessage -> rm_Result1 = Primary;
		RexxMessage -> rm_Result2 = Secondary;

			/* ...and reply the message. */

		ReplyMsg(RexxMessage);
	}
}

	/* GetRexxCommand(RexxMessage):
	 *
	 *	Returns a pointer to the command string if
	 *	the RexxMsg is a command request.
	 */

STRPTR
GetRexxCommand(struct RexxMsg *RexxMessage)
{
	struct RexxHostBase *RexxHostBase;

	if(!RexxMessage || RexxMessage -> rm_Node . mn_Node . ln_Type == NT_REPLYMSG)
		return(NULL);
	else
		return(RexxMessage -> rm_Args[0]);
}

	/* GetRexxArg(RexxMessage):
	 *
	 *	Returns a pointer to the first RexxMsg argument.
	 */

STRPTR
GetRexxArg(struct RexxMsg *RexxMessage)
{
	struct RexxHostBase *RexxHostBase;

	if(!RexxMessage)
		return(NULL);
	else
		return(RexxMessage -> rm_Args[0]);
}

	/* GetRexxResult1(RexxMessage):
	 *
	 *	Returns the 1st RexxMsg result.
	 */

LONG
GetRexxResult1(struct RexxMsg *RexxMessage)
{
	struct RexxHostBase *RexxHostBase;

	if(!RexxMessage)
		return(NULL);
	else
		return(RexxMessage -> rm_Result1);
}

	/* GetRexxResult2(RexxMessage):
	 *
	 *	Returns the 2nd RexxMsg result.
	 */

LONG
GetRexxResult2(struct RexxMsg *RexxMessage)
{
	struct RexxHostBase *RexxHostBase;

	if(!RexxMessage)
		return(NULL);
	else
		return(RexxMessage -> rm_Result2);
}

	/* GetToken(String,StartChar,AuxBuff,MaxLength):
	 *
	 *	Fills a string with the next given string
	 *	argument.
	 */

STRPTR
GetToken(STRPTR String,LONG *StartChar,STRPTR AuxBuff,LONG MaxLength)
{
	struct RexxHostBase *RexxHostBase;

	LONG i,StrEnd = 0,MaxPos = StrLen((char *)String);

		/* Last counter position. */

	if(MaxPos >= MaxLength)
		MaxPos = MaxLength - 1;

		/* Already finished with argument string? */

	if(*StartChar > StrLen((char *)String) - 1 || !String || !String[0] || !AuxBuff || !MaxLength)
		return(NULL);

		/* Parse the argument string... */

	for(i = *StartChar ; i <= MaxPos ; i++)
	{
			/* Skip leading blanks... */

		if(!StrEnd && String[i] == ' ')
		{
			while(String[i] == ' ' && i < MaxPos)
			{
				i++;

				(*StartChar)++;
			}
		}

			/* Found an argument. */

		if(String[i] == ' ' || String[i] == 0)
		{
				/* Copy it to the auxiliary buffer. */

			StrNCpy((char *)AuxBuff,(char *)(String + *StartChar),StrEnd);
			AuxBuff[StrEnd] = 0;

				/* Change the position counter (since
				 * we can't use static data initialisation
				 * calling program has to supply a
				 * counter variable).
				 */

			(*StartChar) += StrEnd;

			return(AuxBuff);
		}

			/* Increment character counter. */

		StrEnd++;
	}

	return(NULL);
}

	/* GetStringValue(String):
	 *
	 *	Returns the numeric value taken from given string
	 *	(just like atoi(), taken from example source code
	 *	by K&R).
	 */

LONG
GetStringValue(STRPTR String)
{
	struct RexxHostBase *RexxHostBase;

	LONG i,Value,Sign = 1;

		/* Valid argument given? */

	if(!String || !String[0])
		return(0);

		/* Skip leading blank characters. */

	for(i = 0 ; String[i] == ' ' || String[i] == '\n' || String[i] == '\t' ; i++);

		/* Remember sign extension. */

	if(String[i] == '+' || String[i] == '-')
		Sign = (String[i++] == '+') ? 1 : -1;

		/* Convert from ASCII to decimal. */

	for(Value = 0 ; String[i] >= '0' && String[i] <= '9' ; i++)
		Value = 10 * Value + String[i] - '0';

		/* Return real value. */

	return(Sign * Value);
}

	/* BuildValueString(Value,String):
	 *
	 *	Puts a numeric value in decimal form into a
	 *	given string (similar to itoa(), taken from
	 *	example source code by K&R).
	 */

STRPTR
BuildValueString(LONG Value,STRPTR String)
{
	struct RexxHostBase *RexxHostBase;

	LONG i = 0,j,c,Sign;

		/* Valid argument given? */

	if(!String)
		return(NULL);

		/* Remember sign extension. */

	if((Sign = Value) < 0)
		Value = -Value;

		/* Convert it into ASCII characters (in
		 * reverse order, i.e. 1234 = "4321").
		 */

	do
		String[i++] = Value % 10 + '0';
	while((Value /= 10) > 0);

		/* Add sign extension. */

	if(Sign < 0)
		String[i++] = '-';

		/* String NULL-termination. */

	String[i] = 0;

		/* Reverse the string. */

	for(i = 0, j = StrLen((char *)String) - 1 ; i < j ; i++, j--)
	{
		c		= String[i];
		String[i]	= String[j];
		String[j]	= c;
	}

		/* Finished, return the string. */

	return(String);
}

	/* AmigaToUpper(c):
	 *
	 *	Replacement for toupper() macro, also knows how to
	 *	map international characters to uppercase. Note: not
	 *	a real library module.
	 */

STATIC LONG
AmigaToUpper(LONG c)
{
	/* -------- DEC ---------    -------- ASCII ------- */

	if((c >= 224 && c <= 254) || (c >= 'a' && c <= 'z'))
		c -= 32;

	return(c);
}

	/* RexxStrCmp(Source,Target):
	 *
	 *	Compares two strings ignoring case.
	 */

LONG
RexxStrCmp(STRPTR Source,STRPTR Target)
{
	struct RexxHostBase *RexxHostBase;

		/* Do the string comparison ignoring case. */

	for( ; AmigaToUpper(*Source) == AmigaToUpper(*Target) ; Source++, Target++)
	{
		if(!(*Source))
			return(0);
	}

	return(AmigaToUpper(*Source) - AmigaToUpper(*Target));
}
