/*
**	AssignWedge - AmigaDOS 2.04 utility
**
**	Copyright © 1992-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
** :ts=4
*/

#include <intuition/intuitionbase.h>

#include <workbench/startup.h>

#include <dos/dosextens.h>
#include <dos/dostags.h>

#include <exec/execbase.h>
#include <exec/memory.h>

#include <libraries/locale.h>
#include <libraries/asl.h>

#include <clib/intuition_protos.h>
#include <clib/utility_protos.h>
#include <clib/locale_protos.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/asl_protos.h>

#include <pragmas/intuition_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/locale_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/asl_pragmas.h>

#include <string.h>
#include <stdarg.h>

	/* Create the requester strings. */

#define CATCOMP_ARRAY

#include "AssignWedge.h"

	/* The requester selection IDs. */

enum	{	REQ_CANCEL, REQ_RETRY, REQ_ASSIGN, REQ_MOUNT, REQ_DENY };

	/* Some handy signal macros. */

#define SIG_KILL	SIGBREAKF_CTRL_C
#define SIG_NOTIFY	(1L << MainPort -> mp_SigBit)

	/* The MC680x0 `jump to absolute address' opcode. */

#define JMP_ABS 0x4EF9

	/* A simple wedge definition which is to consist of a jmp
	 * instruction and the destination of the jump.
	 */

struct Wedge
{
	UWORD Command;
	ULONG Location;
};

	/* Process and command names which are no longer allowed to
	 * access certain paths will be identified by information
	 * to be found in a list. The following structure definition
	 * holds the necessary data (name and process base address).
	 */

struct DenyNode
{
	struct MinNode	 Node;

	struct Process	*Process;
	LONG			 DenyEmpty;
	STRPTR			 Name,
					 ProgramName;
};

	/* The library vector offset of the intuition.library routine to patch. */

extern ULONG __far	 LVOEasyRequestArgs;

	/* The version ID tag. */

STATIC UBYTE Version[]	= "\0$VER: AssignWedge 1.5 (18.1.95)\r\n";

	/* Global and shared library identifiers. */

struct IntuitionBase	*IntuitionBase;
struct ExecBase			*SysBase;
struct DosLibrary		*DOSBase;
struct Library			*UtilityBase,
						*AslBase;

	/* Locale support. */

struct LocaleBase		*LocaleBase;
struct Catalog			*Catalog;

	/* Registration of programs which are not allowed to
	 * access certain devices.
	 */

struct SignalSemaphore	 DenySemaphore;
struct MinList			 DenyList;

	/* The following counter and the associated access semaphore help
	 * to keep track of the number of programs currently using the
	 * patched EasyRequestArgs() routine.
	 */

struct SignalSemaphore	 RunSemaphore;
LONG					 RunCount;

	/* Handshake data. */

struct Process			*MainProcess;
struct MsgPort			*MainPort;
BOOL					 Removed;

	/* To compensate for possible incompatibilities introduced by internationalized
	 * requester texts, we will try to determine the text to turn up when an
	 * `please insert volume' requester is opened. The `SearchName' will receive
	 * the string to look for.
	 */

UBYTE					 SearchName[256];

	/* Function prototypes. */

LONG __saveds			 Main(VOID);
STRPTR __regargs		 GetString(LONG ID);
LONG __saveds __asm		 NewEasyRequestArgs(register __a0 struct Window *Window,register __a1 struct EasyStruct *EasyStruct,register __a2 ULONG *IDCMPPtr,register __a3 APTR *Args);
LONG					(* __asm OldEasyRequestArgs)(register __a0 struct Window *,register __a1 struct EasyStruct *,register __a2 ULONG *,register __a3 APTR *,register __a6 struct IntuitionBase *);
LONG __stdargs			 StackCall(LONG *Success,LONG StackSize,LONG ArgCount,LONG (* __stdargs Function)(),...);
VOID __stdargs			 SPrintf(STRPTR buffer,STRPTR formatString,...);
VOID					 CheckIn(VOID);
VOID					 CheckOut(VOID);

LONG __saveds
Main()
{
	struct WBStartup	*WBenchMsg;
	LONG				 ReturnCode = RETURN_FAIL;

		/* Set up ExecBase */

	SysBase = *(struct ExecBase **)4;

		/* Determine current process identifier. */

	MainProcess = (struct Process *)SysBase -> ThisTask;

		/* Are we running from CLI? If not, wait for Workbench
		 * startup message.
		 */

	if(!MainProcess -> pr_CLI)
	{
		WaitPort(&MainProcess -> pr_MsgPort);

		WBenchMsg = (struct WBStartup *)GetMsg(&MainProcess -> pr_MsgPort);
	}
	else
		WBenchMsg = NULL;

		/* Try to find the global handshake port and if present
		 * send a termination signal.
		 */

	Forbid();

	if(MainPort = FindPort("AssignWedge Rendezvous"))
	{
		Signal(MainPort -> mp_SigTask,SIG_KILL);

		ReturnCode = RETURN_OK;

		Permit();
	}
	else
	{
		Permit();

			/* Are we running under Kickstart version 37 or higher? */

		if(SysBase -> LibNode . lib_Version > 36)
		{
				/* Create the global handshake port. */

			if(MainPort = CreateMsgPort())
			{
					/* Give it a name and add it to the public list. */

				MainPort -> mp_Node . ln_Name = "AssignWedge Rendezvous";

				AddPort(MainPort);

					/* Open the required libraries. */

				if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",37))
				{
					if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37))
					{
						if(UtilityBase = OpenLibrary("utility.library",37))
						{
							if(AslBase = OpenLibrary(AslName,37))
							{
								struct Wedge *Wedge;

									/* Try to open locale.library, but don't panic
									 * if it's not available.
									 */

								if(LocaleBase = (struct LocaleBase *)OpenLibrary("locale.library",38))
								{
									if(!(Catalog = OpenCatalog(NULL,"assignwedge.catalog",
										OC_BuiltInLanguage,	"english",
										OC_BuiltInCodeSet,	0,
									TAG_DONE)))
									{
										CloseLibrary((struct Library *)LocaleBase);

										LocaleBase = NULL;
									}
								}

									/* Initialize the access semaphore. */

								InitSemaphore(&RunSemaphore);

								RunCount = 0;

									/* Initialize the access semaphore and
									 * the list of programs to which access
									 * to certain devices has been denied.
									 */

								InitSemaphore(&DenySemaphore);

								NewList((struct List *)&DenyList);

									/* Allocate a system library function wedge. */

								if(Wedge = AllocMem(sizeof(struct Wedge),MEMF_PUBLIC))
								{
									struct DenyNode	*NextNode,
													*DenyNode;
									UBYTE			 LocalBuffer[80];

										/* Initialize the wedge. */

									Wedge -> Command	= JMP_ABS;
									Wedge -> Location	= (ULONG)NewEasyRequestArgs;

									Removed = FALSE;

										/* Install the wedge. */

									Forbid();

									OldEasyRequestArgs = (LONG (* __asm)())SetFunction((struct Library *)IntuitionBase,(LONG)&LVOEasyRequestArgs,(ULONG (*)())Wedge);

										/* Make sure the data gets written to memory. */

									CacheClearU();

										/* Put up an example requester. Note: this requester
										 * will be trapped by the wedge routine, giving us
										 * the string to look for in the future.
										 */

									SPrintf(LocalBuffer,"AssignWedge.%08lx:",MainProcess);

									ErrorReport(ERROR_DEVICE_NOT_MOUNTED,REPORT_INSERT,(ULONG)LocalBuffer,NULL);

										/* We're up and running now. Note that the
										 * Forbid() will be broken by the Wait() for
										 * a ^C signal.
										 */

									Wait(SIG_KILL);

										/* We are no longer running,
										 * tell the wedge routine to
										 * skip the `access denied'
										 * part which requires the
										 * list to be initialized.
										 */

									Removed = TRUE;

										/* Redirect the wedge pointer to
										 * the original routine.
										 */

									Wedge -> Location = (ULONG)OldEasyRequestArgs;

										/* Make sure that the data
										 * gets written to memory.
										 */

									CacheClearU();

										/* Clear pending signals. */

									SetSignal(0,SIG_NOTIFY);

										/* Turn the multitasking back on. */

									Permit();

										/* Wait until our wedge routine
										 * is no longer in use.
										 */

									while(RunCount)
										Wait(SIG_NOTIFY);

										/* Clear the `access denied' list. */

									DenyNode = (struct DenyNode *)DenyList . mlh_Head;

									while(NextNode = (struct DenyNode *)DenyNode -> Node . mln_Succ)
									{
										FreeVec(DenyNode);

										DenyNode = NextNode;
									}

										/* Successful termination. */

									ReturnCode = RETURN_OK;
								}

									/* Close the resources
									 * we had allocated,
									 * but *not* the wedge
									 * memory.
									 */

								if(Catalog)
									CloseCatalog(Catalog);

								if(LocaleBase)
									CloseLibrary((struct Library *)LocaleBase);

								CloseLibrary(AslBase);
							}

							CloseLibrary(UtilityBase);
						}

						CloseLibrary((struct Library *)IntuitionBase);
					}

					CloseLibrary((struct Library *)DOSBase);
				}

					/* Remove the global handshake port. */

				RemPort(MainPort);

				DeleteMsgPort(MainPort);
			}
		}
	}

		/* If run from Workbench, reply the startup message. */

	if(WBenchMsg)
	{
		Forbid();

		ReplyMsg(&WBenchMsg -> sm_Message);
	}

		/* That's all folks. */

	return(ReturnCode);
}

	/* GetString(LONG ID):
	 *
	 *	Fetch a text from the database.
	 */

STRPTR __regargs
GetString(LONG ID)
{
	STRPTR	Builtin = "";
	WORD	i;

		/* Try to find the builtin string to match the ID we received. */

	if(CatCompArray[ID] . cca_ID != ID)
	{
		for(i = 0 ; i < (sizeof(CatCompArray) / sizeof(struct CatCompArrayType)) ; i++)
		{
			if(CatCompArray[i] . cca_ID == ID)
			{
				Builtin = CatCompArray[i] . cca_Str;

				break;
			}
		}
	}
	else
		Builtin = CatCompArray[ID] . cca_Str;

		/* If locale.library is installed and the database catalog was
		 * successfully opened, query the library's idea of the corresponding
		 * text string. Otherwise, return the builtin string.
		 */

	if(LocaleBase)
		return(GetCatalogStr(Catalog,ID,Builtin));
	else
		return(Builtin);
}

	/* SPrintf(STRPTR buffer,STRPTR formatString,...):
	 *
	 *	Your easy text formatting routine.
	 */

VOID __stdargs
SPrintf(STRPTR buffer,STRPTR formatString,...)
{
	va_list varArgs;

	va_start(varArgs,formatString);
	RawDoFmt(formatString,varArgs,(VOID (*)())(VOID (*))"\x16\xC0\x4E\x75",buffer);
	va_end(varArgs);
}

	/* CheckIn():
	 *
	 *	Mark the patch as having another customer.
	 */

VOID
CheckIn()
{
		/* Increment the usage count. */

	ObtainSemaphore(&RunSemaphore);

	RunCount++;

	ReleaseSemaphore(&RunSemaphore);
}

	/* CheckOut():
	 *
	 *	Decrement the patch usage counter and signal the
	 *	main process to check the usage counter.
	 */

VOID
CheckOut()
{
		/* Decrement usage count. */

	ObtainSemaphore(&RunSemaphore);

	RunCount--;

	ReleaseSemaphore(&RunSemaphore);

		/* Tell the main process to take a
		 * look at the usage count.
		 */

	Signal((struct Task *)MainProcess,SIG_NOTIFY);
}

	/* RealEasyRequestArgs():
	 *
	 *	Custom version of EasyRequestArgs().
	 */

LONG __stdargs
RealEasyRequestArgs(struct Window *Window,struct EasyStruct *EasyStruct,ULONG *IDCMPPtr,APTR *Args)
{
	struct Process	*ThisProcess = (struct Process *)SysBase -> ThisTask;
	LONG			 Result;

		/* Increment the usage count. */

	CheckIn();

		/* Is the caller a process, i.e. will it be able to use all
		 * DOS routines?
		 */

	if(ThisProcess -> pr_Task . tc_Node . ln_Type == NT_PROCESS)
	{
			/* Do the arguments match the pattern we had expected and
			 * are DOS requesters enabled?
			 */

		if(!Strnicmp(EasyStruct -> es_TextFormat,"%s",2) && Args && ThisProcess -> pr_WindowPtr != (APTR)-1)
		{
				/* Did we get any calling parameters? */

			if(Args[0])
			{
					/* Does the first argument match the `please insert volume...' title? */

				if(!Stricmp(Args[0],SearchName))
				{
					struct EasyStruct __aligned	 Easy = *EasyStruct;
					UBYTE						 DirBuffer[512 + 60];
					STRPTR						 HailBuffer;
					struct DenyNode				*DenyNode;
					struct FileRequester		*AslFileRequest;
					struct Screen				*FirstScreen;
					ULONG						 IntuiLock;
					BPTR						 In,Out;

						/* Gain access to the list of
						 * programs to which access to
						 * some devices has been denied.
						 */

					ObtainSemaphore(&DenySemaphore);

						/* If this process has a CLI structure
						 * attached, look for a command name
						 * to match a list entry.
						 */

					if(ThisProcess -> pr_CLI)
					{
							/* Process the list... */

						for(DenyNode = (struct DenyNode *)DenyList . mlh_Head ; DenyNode -> Node . mln_Succ ; DenyNode = (struct DenyNode *)DenyNode -> Node . mln_Succ)
						{
								/* Does this entry refer to
								 * a command name?
								 */

							if(DenyNode -> ProgramName[0])
							{
									/* Does the name of the device
									 * in question match the name
									 * in the list entry?
									 */

								if(!Stricmp(DenyNode -> Name,Args[1]))
								{
										/* Obtain the name of the program
										 * currently running.
										 */

									if(GetProgramName(DirBuffer,512))
									{
											/* Does the name match the one
											 * in the list entry?
											 */

										if(!Stricmp(FilePart(DirBuffer),DenyNode -> ProgramName))
										{
												/* Release the access semaphore. */

											ReleaseSemaphore(&DenySemaphore);

												/* Decrement usage count. */

											CheckOut();

												/* Return failure. */

											return(REQ_CANCEL);
										}
									}
								}
							}
							else
							{
									/* This might be an `empty' Shell. Let's have a look. */

								if(!GetProgramName(DirBuffer,512))
									DirBuffer[0] = 0;

									/* No command loaded? */

								if(!DirBuffer[0])
								{
										/* Release the access semaphore. */

									ReleaseSemaphore(&DenySemaphore);

										/* Decrement usage count. */

									CheckOut();

										/* Return failure. */

									return(REQ_CANCEL);
								}
							}
						}
					}
					else
					{
							/* Run down the list. */

						for(DenyNode = (struct DenyNode *)DenyList . mlh_Head ; DenyNode -> Node . mln_Succ ; DenyNode = (struct DenyNode *)DenyNode -> Node . mln_Succ)
						{
								/* Does the process identifier match the
								 * one in the list entry?
								 */

							if(DenyNode -> Process == ThisProcess)
							{
									/* Does the name of the device in
									 * question match the one in the
									 * list entry?
									 */

								if(!Stricmp(DenyNode -> Name,Args[1]))
								{
										/* Release the access semaphore. */

									ReleaseSemaphore(&DenySemaphore);

										/* Decrement usage count. */

									CheckOut();

										/* Return failure. */

									return(REQ_CANCEL);
								}
							}
						}
					}

						/* Release the access semaphore. */

					ReleaseSemaphore(&DenySemaphore);

						/* Split the buffer to create the directory
						 * requester title string.
						 */

					HailBuffer = DirBuffer + 512;

						/* Fill in the `Retry|Assign|Mount|Deny|Cancel' buttons. */

					Easy . es_GadgetFormat = GetString(MSG_PROMPT_GAD);

						/* Put up the requester and decide what to do. */

					switch(Result = OldEasyRequestArgs(Window,&Easy,IDCMPPtr,Args,IntuitionBase))
					{
						case REQ_ASSIGN:

								/* Try to obtain the name of the current directory,
								 * most programs to look for assignments or volume
								 * names will be happy if assignments are made
								 * referring to their home directories.
								 */

							if(ThisProcess -> pr_HomeDir)
							{
								if(!NameFromLock(ThisProcess -> pr_HomeDir,DirBuffer,512))
									DirBuffer[0] = 0;
							}
							else
								DirBuffer[0] = 0;

								/* No success? */

							if(!DirBuffer[0])
							{
								if(!GetCurrentDirName(DirBuffer,512))
									DirBuffer[0] = 0;
							}

								/* Set up the window title. */

							SPrintf(HailBuffer,GetString(MSG_HAIL_GAD),Args[1]);

								/* create the directory requester. */

							if(AslFileRequest = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
								ASLFR_InitialDrawer,	DirBuffer,
								ASLFR_TitleText,		HailBuffer,
								ASLFR_PositiveText,		GetString(MSG_ASSIGN_GAD),
								ASLFR_DrawersOnly,		TRUE,
								ASLFR_PrivateIDCMP,		TRUE,

									/* These are actually for asl.library v36/v37. */

								ASL_FuncFlags,			FILF_NEWIDCMP,
								ASL_ExtFlags1,			FIL1F_NOFILES,

								Window ? ASLFR_Window : TAG_IGNORE,		Window,
							TAG_DONE))
							{
									/* Remember the first screen ID. */

								IntuiLock = LockIBase(NULL);

								FirstScreen = IntuitionBase -> FirstScreen;

								UnlockIBase(IntuiLock);

									/* Did we receive a window pointer to work with? */

								if(Window)
								{
										/* Move the screen the requester is to appear on
										 * up if necessary.
										 */

									if(Window -> WScreen -> TopEdge > 0)
										MoveScreen(Window -> WScreen,0,-Window -> WScreen -> TopEdge);

										/* Move the screen to the front. */

									ScreenToFront(Window -> WScreen);
								}
								else
								{
										/* Get the current default public screen. */

									struct Screen *Workbench = LockPubScreen(NULL);

										/* Did we succeed in locking it? */

									if(Workbench)
									{
											/* Move the screen the requester is to appear on
											 * up if necessary.
											 */

										if(Workbench -> TopEdge > 0)
											MoveScreen(Workbench,0,-Workbench -> TopEdge);

											/* Move the screen to the front. */

										ScreenToFront(Workbench);

											/* Release the screen again. */

										UnlockPubScreen(NULL,Workbench);
									}
								}

									/* Display the requester. */

								while(AslRequest(AslFileRequest,NULL))
								{
									APTR OldWindowPtr = ThisProcess -> pr_WindowPtr;
									BPTR FileLock;

										/* Disable the system requesters. */

									ThisProcess -> pr_WindowPtr = (APTR)-1;

										/* Try to access the directory the
										 * user has just selected.
										 */

									if(FileLock = Lock(AslFileRequest -> fr_Drawer,ACCESS_READ))
									{
											/* Try to create the assignment. */

										if(!AssignLock(Args[1],FileLock))
										{
												/* Oops, something went wrong. */

											if(Window)
												DisplayBeep(Window -> WScreen);
											else
												DisplayBeep(NULL);

											UnLock(FileLock);
										}
										else
										{
												/* Restore the window pointer. */

											ThisProcess -> pr_WindowPtr = OldWindowPtr;

											break;
										}
									}
									else
									{
										if(Window)
											DisplayBeep(Window -> WScreen);
										else
											DisplayBeep(NULL);
									}

										/* Restore the window pointer. */

									ThisProcess -> pr_WindowPtr = OldWindowPtr;
								}

									/* Free the requester data. */

								FreeAslRequest(AslFileRequest);

									/* Pop the screen which was frontmost
									 * before the requester was displayed
									 * back to the front.
									 */

								IntuiLock = LockIBase(0);

								if(FirstScreen == IntuitionBase -> FirstScreen)
									UnlockIBase(IntuiLock);
								else
								{
									struct Screen	*Screen = IntuitionBase -> FirstScreen;
									BOOL			 IsValid = FALSE;

										/* Try to determine if the screen ID we
										 * remembered is still valid.
										 */

									while(Screen && !IsValid)
									{
										if(Screen == FirstScreen)
											IsValid = TRUE;
										else
											Screen = Screen -> NextScreen;
									}

									Forbid();

									UnlockIBase(IntuiLock);

										/* Push the screen to the front. */

									if(IsValid)
										ScreenToFront(FirstScreen);

									Permit();
								}
							}

								/* Tell AmigaDOS to take a second look at the
								 * device list.
								 */

							Result = REQ_RETRY;

							break;

						case REQ_MOUNT:

								/* Open input stream. */

							if(In = Open("NIL:",MODE_OLDFILE))
							{
									/* Open output stream. */

								if(Out = Open("NIL:",MODE_OLDFILE))
								{
									APTR OldPtr = ThisProcess -> pr_WindowPtr;

										/* Enter the mount command string. */

									SPrintf(DirBuffer,"Mount >NIL: <NIL: %s:",Args[1]);

										/* Disable DOS requesters. */

									ThisProcess -> pr_WindowPtr = (APTR)-1;

										/* Execute the mount command. */

									SystemTags(DirBuffer,
										NP_Cli,			TRUE,
										NP_CloseInput,	TRUE,
										NP_CloseOutput,	TRUE,

										SYS_UserShell,	TRUE,
										SYS_Input,		In,
										SYS_Output,		Out,
									TAG_DONE);

										/* Restore window pointer. */

									if(ThisProcess -> pr_WindowPtr == (APTR)-1)
										ThisProcess -> pr_WindowPtr = OldPtr;
								}
								else
									Close(In);
							}

								/* Tell AmigaDOS to take a second look at the
								 * device list.
								 */

							Result = REQ_RETRY;

							break;

						case REQ_DENY:

								/* If not about to be removed, allocate an `access denied' node. */

							if(!Removed)
							{
								WORD	ProgramNameLen = 0,
										DeviceNameLen;
								LONG	DenyEmpty = FALSE;

									/* Remember the length of the device name. */

								DeviceNameLen = strlen(Args[1]) + 1;

									/* If this process has a CLI structure attached,
									 * remember the name of the program.
									 */

								if(ThisProcess -> pr_CLI)
								{
										/* Is there a command making the request? */

									if(!GetProgramName(DirBuffer,512))
										DirBuffer[0] = 0;

										/* Keep the name. */

									if(DirBuffer[0])
										ProgramNameLen = strlen(FilePart(DirBuffer)) + 1;
									else
										DenyEmpty = TRUE;
								}

									/* Allocate a new deny node. */

								if(DenyNode = (struct DenyNode *)AllocVec(sizeof(struct DenyNode) + DeviceNameLen + ProgramNameLen,MEMF_CLEAR))
								{
										/* Deny `empty' Shells? */

									DenyNode -> DenyEmpty = DenyEmpty;

										/* Fill in the current process ID. */

									DenyNode -> Process = ThisProcess;

										/* Copy the name of the device in question. */

									strcpy(DenyNode -> Name = (STRPTR)(DenyNode + 1),Args[1]);

										/* Copy the program name if possible. */

									if(ProgramNameLen)
										strcpy(DenyNode -> ProgramName = DenyNode -> Name + DeviceNameLen,FilePart(DirBuffer));
									else
										DenyNode -> ProgramName = "";

										/* Gain access to the list. */

									ObtainSemaphore(&DenySemaphore);

										/* Add the entry to the list. */

									AddTail((struct List *)&DenyList,(struct Node *)DenyNode);

										/* Release the semaphore again. */

									ReleaseSemaphore(&DenySemaphore);
								}
							}

								/* Return failure. */

							Result = REQ_CANCEL;
							break;
					}

						/* Decrement usage count. */

					CheckOut();

						/* Return the result. */

					return(Result);
				}
			}
		}
	}

		/* In any other case, use the standard call. */

	Result = OldEasyRequestArgs(Window,EasyStruct,IDCMPPtr,Args,IntuitionBase);

		/* Decrement usage count. */

	CheckOut();

		/* Return the result. */

	return(Result);
}

	/* NewEasyRequestArgs():
	 *
	 *	A custom version of the original EasyRequest() routine which
	 *	is to provide enhanced options whenever a DOS handler puts
	 *	up a `REPORT_INSERT' style requester.
	 */

LONG __saveds __asm
NewEasyRequestArgs(register __a0 struct Window *Window,register __a1 struct EasyStruct *EasyStruct,register __a2 ULONG *IDCMPPtr,register __a3 APTR *Args)
{
	struct Process *ThisProcess = (struct Process *)SysBase -> ThisTask;

		/* This may be the first call to the new routine, made by the
		 * main program trying to determine the text to look for in
		 * future requesters.
		 */

	if(!SearchName[0] && ThisProcess == MainProcess)
	{
		strcpy(SearchName,Args[0]);

		return(REQ_CANCEL);
	}
	else
	{
		LONG Success,Result;

			/* Allocate extra stack space, call the special routine. */

		Result = StackCall(&Success,8192,4,RealEasyRequestArgs,Window,EasyStruct,IDCMPPtr,Args);

			/* If the call did not succeed, do it the standard way. */

		if(!Success)
		{
				/* Increment the usage count. */

			CheckIn();

				/* Call the original routine. */

			Result = OldEasyRequestArgs(Window,EasyStruct,IDCMPPtr,Args,IntuitionBase);

				/* Decrement usage count. */

			CheckOut();
		}

			/* Return the result. */

		return(Result);
	}
}
