/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: RexxHostLib.c
 *	Created ..: Sunday 07-Jan-90 18:55
 *	Revision .: 14
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	09-Sep-90       Olsen           Bug fixed in GetToken, added
 *	                                IsSpace() and GetRexxClip()
 *	24-May-90       Olsen           General rewrite, added new functions
 *	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 14

	/* Main rexx library base. */

extern struct RxsLib *RexxSysBase;

	/* 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;

		/* Valid name given? */

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

		if(!FindPort((char *)HostName))
		{
				/* Allocate the port body. */

			if(RexxHost = (struct RexxHost *)AllocMem(sizeof(struct RexxHost),MEMF_PUBLIC | MEMF_CLEAR))
			{
					/* Allocate a signal bit. */

				if((RexxHost -> rh_Port . mp_SigBit = AllocSignal(-1)) != -1)
				{
						/* 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))
					{
							/* 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_SigTask	= SysBase -> ThisTask;

							/* A dummy ID. */

						RexxHost -> rh_SpecialID = 'REXX';

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

						AddPort(&RexxHost -> rh_Port);

							/* And return it to the caller. */

						return(RexxHost);
					}

					FreeSignal(RexxHost -> rh_Port . mp_SigBit);
				}

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

	return(NULL);
}

	/* 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;

		/* Valid host port given? */

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

		RemPort(&RexxHost -> rh_Port);

			/* 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)
	{
			/* 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))
		{
				/* Add the command. */

			if(HostMessage -> rm_Args[0] = CreateArgstring((char *)CommandString,StrLen((char *)CommandString)))
			{
					/* This is a command, not a function. */

				HostMessage -> rm_Action = RXCOMM;

					/* Release it... */

				PutMsg(RexxPort,HostMessage);

					/* Successful action. */

				return(TRUE);
			}

			DeleteRexxMsg(HostMessage);
		}
	}

	return(FALSE);
}

	/* 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);
}

	/* IsSpace():
	 *
	 *	Returns TRUE if the input character is a space, tab,
	 *	carriage return, newline, form feed or vertical tab.
	 */

STATIC BYTE
IsSpace(UBYTE c)
{
	if((c >= 13 && c <= 17) || c == 32)
		return(TRUE);
	else
		return(FALSE);
}

	/* 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;

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

		/* Last counter position. */

	if(MaxPos >= MaxLength + *StartChar)
		MaxPos = MaxLength + *StartChar - 1;

		/* Already finished with argument string? */

	if(*StartChar <= StrLen((char *)String) - 1 && String && String[0] && AuxBuff && MaxLength)
	{
			/* Parse the argument string... */

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

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

					(*StartChar)++;
				}
			}

				/* Found an argument. */

			if(IsSpace(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	Value;
	BYTE	Sign = 1;
	SHORT	i;

		/* Valid argument given? */

	if(String && !String[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);
	}

	return(0);
}

	/* 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;

	SHORT	i = 0,j;
	LONG	Sign;
	UBYTE	c;

		/* Valid argument given? */

	if(String)
	{
			/* 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;
		}
	}

	return(String);
}

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

STATIC UBYTE
AmigaToUpper(UBYTE 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));
}

	/* GetRexxMsg():
	 *
	 *	Picks up pending RexxMessages from a RexxHost and
	 *	returns them to the caller. I desired, will wait
	 *	for new messages to arrive if none is present yet.
	 */

struct RexxMsg *
GetRexxMsg(struct RexxHost *RexxHost,LONG Wait)
{
	struct RexxHostBase	*RexxHostBase;
	struct RexxMsg		*RexxMessage = NULL;

		/* Valid pointer given? */

	if(RexxHost && RexxHost -> rh_SpecialID == 'REXX')
	{
			/* Try to pick up a message. */

		while(!(RexxMessage = (struct RexxMsg *)GetMsg((struct MsgPort *)RexxHost)))
		{
				/* No message available. Are we to wait? */

			if(Wait)
				WaitPort((struct MsgPort *)RexxHost);
			else
				break;
		}
	}

		/* Return the result (may be NULL). */

	return(RexxMessage);
}

	/* SendRexxMsg():
	 *
	 *	Sends a single (or a list of) command(s) to Rexx host
	 *	and returns the secondary result.
	 */

ULONG
SendRexxMsg(STRPTR HostName,STRPTR *MsgList,STRPTR SingleMsg,LONG GetResult)
{
	struct RexxHostBase	*RexxHostBase;
	struct RexxMsg		*RexxMessage;
	struct MsgPort		*HostPort,*ReplyPort;
	ULONG			 Result = 0;
	SHORT			 i;

		/* Valid pointers given? */

	if(HostName && (MsgList || SingleMsg))
	{
			/* Can we find the host? */

		if(HostPort = (struct MsgPort *)FindPort((char *)HostName))
		{
				/* Allocate a reply port. */

			if(ReplyPort = (struct MsgPort *)AllocMem(sizeof(struct MsgPort),MEMF_PUBLIC | MEMF_CLEAR))
			{
				if((ReplyPort -> mp_SigBit = AllocSignal(-1)) != -1)
				{
					ReplyPort -> mp_Node . ln_Type	= NT_MSGPORT;
					ReplyPort -> mp_Flags		= PA_SIGNAL;
					ReplyPort -> mp_SigTask		= SysBase -> ThisTask;

					InitList(&ReplyPort -> mp_MsgList);

						/* Create a Rexx message. */

					if(RexxMessage = (struct RexxMsg *)CreateRexxMsg(ReplyPort,"",(char *)HostName))
					{
							/* A list of arguments or only a single arg? */

						if(MsgList)
						{
							for(i = 0 ; i < 16 ; i++)
								RexxMessage -> rm_Args[i] = MsgList[i];
						}
						else
							RexxMessage -> rm_Args[0] = SingleMsg;

							/* Do we want result codes? */

						if(GetResult)
							RexxMessage -> rm_Action = RXFF_RESULT;

							/* Send packet and wait for the reply. */

						PutMsg(HostPort,RexxMessage);
						WaitPort(ReplyPort);

							/* Remember result. */

						if(GetResult && !RexxMessage -> rm_Result1)
							Result = RexxMessage -> rm_Result2;

							/* Remove Rexx message. */

						DeleteRexxMsg(RexxMessage);
					}

						/* Free reply port signal bit. */

					FreeSignal(ReplyPort -> mp_SigBit);
				}

					/* Free the replyport itself. */

				FreeMem(ReplyPort,sizeof(struct MsgPort));
			}
		}
	}

		/* Return the result. */

	return(Result);
}

	/* GetRexxString():
	 *
	 *	Copy the result string returned by SendRexxMsg to user
	 *	buffer and/or remove the original string.
	 */

VOID
GetRexxString(STRPTR SourceString,STRPTR DestString)
{
	struct RexxHostBase *RexxHostBase;

		/* Valid pointer given? */

	if(SourceString)
	{
			/* Destination memory buffer given? */

		if(DestString)
			StrCpy((char *)DestString,(char *)SourceString);

			/* Deallocate the original string. */

		DeleteArgstring((char *)SourceString);
	}
}

	/* GetRexxClip():
	 *
	 *	Searches the rexx clip list for a node with given
	 *	name.
	 */

LONG
GetRexxClip(char *Name,LONG WhichArg)
{
	struct RexxHostBase *RexxHostBase;

		/* Do we have valid buffers and size? */

	if(Name && Name[0])
	{
		struct RexxRsrc *Node;

			/* Can we find the clip node? */

		if(Node = (struct RexxRsrc *)FindRsrcNode(&RexxSysBase -> rl_ClipList,Name,RRT_CLIP))
		{
			if(WhichArg)
				return(Node -> rr_Arg1);
			else
				return(Node -> rr_Arg2);
		}
	}

	return(NULL);
}
