/*
** OpenURL-Handler - Asynch ARexx handler for openurl.library
** Written by Troels Walsted Hansen <troels@thule.no>
** Placed in the public domain.
**
** This is the one and only source file for the handler.
*/

#include <proto/exec.h>

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

#include <pragmas/dos_pragmas.h>
#include <pragmas/rexxsyslib_pragmas.h>

#include <exec/memory.h>
#include <rexx/rxslib.h>
#include <rexx/errors.h>

#include <string.h>

#include "handler.h"

#define SAVEDS __saveds

extern void kprintf(const char *, ...);

/**************************************************************************
*
* Prototypes for library and Local, non-library functions.
*
*/

VOID SAVEDS HandlerProcessEntry(VOID);
static BOOL SendRexxMsg(struct MsgPort *replyport, STRPTR rxport, STRPTR rxcmd);

/**************************************************************************
*
* Definitions and global variables.
*
*/

struct ExecBase *SysBase;
struct DosLibrary *DOSBase = NULL;
struct RxsLib *RexxSysBase = NULL;
static const char *version = "$VER: OpenURL-Handler 1.0 " __AMIGADATE__;

/**************************************************************************
*
* This function will be called when the handler is started.
*
*/

VOID SAVEDS HandlerProcessEntry(VOID)
{
	struct Process *me;
	struct HandlerMsg *starthm = NULL;
	struct SignalSemaphore *ss;
	ULONG sigs, sigmask, msgcount = 0;
	struct MsgPort *mp = NULL;
	struct Message *msg;

	SysBase = *(struct ExecBase **)4;
	me = (struct Process *)FindTask(NULL);

	/* get startupmsg */

	while(!starthm)
	{
		/* should the handler accidentally be launched from somewhere other
		   than the library, it can be broken with CTRL-C */

		sigs = Wait((1 << me->pr_MsgPort.mp_SigBit) | SIGBREAKF_CTRL_C);
		if(sigs & SIGBREAKF_CTRL_C) return;
		starthm = (struct HandlerMsg *)GetMsg(&me->pr_MsgPort);
	}

	ss = starthm->hm_Semaphore;
	ObtainSemaphore(ss);

	/* initialise everything */

	if(!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36)))
		goto done;

	if(!(RexxSysBase = (struct RxsLib *)OpenLibrary("rexxsyslib.library", 33)))
		goto done;

	if(!(mp = CreateMsgPort()))
		goto done;

	/* reply startup msg to signal readyness */

	starthm->hm_Success = TRUE;
	starthm->hm_MsgPort = mp;
	ReplyMsg((struct Message *)starthm);
	starthm = NULL;

	/* enter input loop */

	sigmask = (1 << mp->mp_SigBit) | SIGBREAKF_CTRL_C;

	for(;;)
	{
		sigs = Wait(sigmask);

		if(sigs & SIGBREAKF_CTRL_C) break;
		else 
		{
			while((msg = GetMsg(mp)))
			{
				if(msg->mn_Node.ln_Type == NT_REPLYMSG)
				{
					DeleteArgstring(((struct RexxMsg *)msg)->rm_Args[0]);
					DeleteRexxMsg((struct RexxMsg *)msg);
					msgcount--;
				}
				else
				{
					struct HandlerMsg *hm = (struct HandlerMsg *)msg;

					if(hm->hm_Type	== HMT_AREXX)
					{
						hm->hm_Success = SendRexxMsg(mp, hm->hm_ARexxPort, hm->hm_ARexxCmd);
						if(hm->hm_Success) msgcount++;
					}

					ReplyMsg(msg);
				}
			}
		}
	}

done:
	/* release semaphore to indicate we're no longer running.
	   this will stop the library from sending any more msgs */

	ReleaseSemaphore(ss);

	/* wait for return of all msgs */

	while(msgcount)
	{
		WaitPort(mp);

		while((msg = GetMsg(mp)))
		{
			if(msg->mn_Node.ln_Type == NT_REPLYMSG)
			{
				DeleteArgstring(((struct RexxMsg *)msg)->rm_Args[0]);
				DeleteRexxMsg((struct RexxMsg *)msg);
				msgcount--;
			}
			else
			{
				((struct HandlerMsg *)msg)->hm_Success = FALSE;
				ReplyMsg(msg);
			}
		}
	}

	/* empty out msgport */

	while((msg = GetMsg(mp)))
	{
		((struct HandlerMsg *)msg)->hm_Success = FALSE;
		ReplyMsg(msg);
	}

	/* free resources */

	if(mp) DeleteMsgPort(mp);
	CloseLibrary((struct Library *)RexxSysBase);
	CloseLibrary((struct Library *)DOSBase);

	/* the startup msg needs to be replied now if a resource 
	   allocation failed */

	if(starthm)
	{
		starthm->hm_Success = FALSE;
		ReplyMsg((struct Message *)starthm);
	}
}

/**************************************************************************/

static BOOL SendRexxMsg(struct MsgPort *replyport, STRPTR rxport, STRPTR rxcmd)
{
	struct RexxMsg *rxmsg;

	if((rxmsg = CreateRexxMsg(replyport, NULL, NULL)))
	{
		rxmsg->rm_Action = RXCOMM | RXFF_STRING | RXFF_NOIO;

		/* create the arg string */

		if((rxmsg->rm_Args[0] = CreateArgstring(rxcmd, strlen(rxcmd))))
		{
			struct MsgPort *targetport;

			/* find the named rexx port and put the rexx msg to it */

			Forbid();

			if((targetport = FindPort(rxport)))
			{
				PutMsg(targetport, (struct Message *)rxmsg);
				Permit();
				return(TRUE);
			}

			Permit();

			DeleteArgstring(rxmsg->rm_Args[0]);
		}
		DeleteRexxMsg(rxmsg);
	}

	return(FALSE);
}
