/*
 * KillReq (C) Copyright Eddy Carroll 1989, may be Freely Redistributed.
 *
 * KillReq disables Intuition's AutoRequest() function, such that any calls
 * to it will always return FALSE (as if the user had selected CANCEL).
 * This is primarily of use for those who use their Amiga from a remote
 * location, since it stops DOS putting up any requesters saying "Disk Full",
 * "Read/Write error", "Please insert volume xyz" etc. Similar programs
 * exist which will disable these requesters for a particular CLI, but
 * KillReq disables them across the whole system.
 *
 * Usage: killreq          -- Displays usage message
 *        killreq disable  -- Disables requesters
 *        killreq enable   -- Enables requesters again
 *
 * Note that in this source, tabstops are set every 4 spaces.
 * Compiles under Lattice C V5.04.
 * (Optimisation causes intermediate file error, for some reason.)
 *
 */

#ifndef LATTICE_50
#include "system.h"
typedef void (*__fptr)(); /* The sort of thing returned by SetFunction */
#endif

#define AutoRequestOffset	(-(0x15C))

/*
 *		Assembly language defines
 */
#define MOVEQ_0_D0	0x7000
#define RTS			0x4E75
#define JMP			0x4EF9

#define YES		1
#define NO		0

char Portname[] = "killreq by Eddy Carroll";
char usage[] = "\
killreq (C) Copyright Eddy Carroll 1989, disables Intuition/DOS Requesters\n\
\n\
Usage:  killreq disable         Disables requesters\n\
        killreq enable          Enables requesters\n\
\n";

struct MyMsgPort {
	struct	MsgPort mp;				/* A genuine message port			*/
	char	portname[40];			/* Text of port name				*/
	int		active;					/* True if patch currently active	*/
	__fptr	OldAutoRequest;			/* Address of original routine		*/
	union {
		short mmu_code[3];			/* Space for MOVEQ #0,D0 / RTS		*/
		struct {
			short	mmu_jmp;		/* Alternatively, use JMP $xxxxxx	*/
			__fptr	mmu_jmpdest;
		} portstruct;
	} portunion;
} port;

#define mmp_code		portunion.mmu_code
#define mmp_jmp			portunion.portstruct.mmu_jmp
#define mmp_jmpdest		portunion.portstruct.mmu_jmpdest

typedef struct MessagePort		MP;
typedef struct MyMsgPort		MyMP;
typedef struct IntuitionBase	IBASE;

IBASE *IntuitionBase;


/*
 *		print()
 *		-------
 *		Outputs a string to the console
 */
void print(s)
char *s;
{
	Write(Output(), s, strlen(s));
}

/*
 *		help()
 *		------
 *		Prints a simple usage message. If the parameter is TRUE, then
 *		indicates the patch is currently active, else indicates it is
 *		inactive.
 */
void help(active)
int active;
{
	print(usage);
	if (active)
		print("(Requesters are currently disabled.)\n");
	else
		print("(Requesters are currently enabled.)\n");
}

/*
 *		cleanup()
 *		---------
 *		Cleans up and exits to DOS.
 */
void cleanup(err)
int err;
{
	if (IntuitionBase != NULL)
		CloseLibrary((IBASE *)IntuitionBase);
	exit(err);
}

/*
 *		main()
 *		------
 *		The mainline (actually, this is the whole program).
 */		
void main(argc,argv)
int argc;
char *argv[];
{
	MyMP	*myport;		/* Pointer to our message port				*/
	__fptr	lastfunc;		/* Previous function for AutoRequest()		*/
	int		active;			/* True if requesters currently disabled	*/

	/*
	 *		Check if we are already running, and set active
	 *		to reflect whether we are or not.
	 */
	myport = (MyMP *)FindPort(Portname);
	active = (myport != NULL) && (myport->active == YES);

	if (argc != 2) {
		help(active);
		cleanup(5);
	}

	IntuitionBase = (IBASE *)OpenLibrary("intuition.library",0L);
	if (!IntuitionBase) {
		print("Can't open intuition.library. What's happening??\n");
		cleanup(5);
	}

	switch (argv[1][0]) {

		case 'e':		/* --- Enable Intuition AutoRequesters --- */
		case 'E':
			if (!active) {
				print("Intuition requesters are already enabled\n");
				cleanup(5);
			}
			/*
			 *		Our patch is current installed, so we can go ahead and
			 *		unlink it from Intuition's library.
			 */
			Forbid();
			lastfunc = SetFunction(IntuitionBase, AutoRequestOffset,
													myport->OldAutoRequest);
			if (lastfunc != (__fptr)myport->mmp_code) {
				/*
				 *		Oh oh..someone has SetFunction()'d after us. We'd
				 *		better put back their patch, and change our code to
				 *		simply be a JMP back to the original AutoRequest()
				 *		function.
				 */
				myport->active		= NO;
				myport->mmp_jmp		= JMP;
				myport->mmp_jmpdest	= myport->OldAutoRequest;
				SetFunction(IntuitionBase, AutoRequestOffset, lastfunc);
			} else {
				/*
				 *		Nobody has SetFunction()'d us so just remove our
				 *		message port from Public access and deallocate
				 *		its memory. The SetFunction call before this `if'
				 *		has already restored the AutoRequest vector to its
				 *		original state.
				 */
				RemPort(myport);
				FreeMem(myport, sizeof(MyMP));
			}
			Permit();
			break;

		case 'd':		/* --- Disable Intuition AutoRequesters --- */
		case 'D':
			if (myport != NULL) {
				if (myport->active == YES) {
					print("Intuition requesters are already disabled\n");
					cleanup(5);
				} else {
					/*
					 *		If we get here, someone SetFunctioned after us a
					 *		while ago, and at some stage after that, the user
					 *		enabled requesters again. So, just modify our
					 *		existing patch to disable requesters again.
					 */
					Forbid();
					myport->mmp_code[0] = MOVEQ_0_D0;
					myport->mmp_code[1] = RTS;
					myport->active		= YES;
					Permit();
				}
			} else {
				/*
				 *		If we get here, then this is the first time the
				 *		user has run killreq, so install a completely
				 *		new patch.
				 */
				myport = AllocMem(sizeof(MyMP), MEMF_CLEAR);
				if (!myport) {
					print("Killreq: Out of memory!\n");
					cleanup(5);
				}
				strcpy(myport->portname, Portname);
				myport->mp.mp_Node.ln_Name	= myport->portname;
				myport->mmp_code[0]			= MOVEQ_0_D0;
				myport->mmp_code[1]			= RTS;
				myport->active				= YES;
				Forbid();
				myport->OldAutoRequest		= SetFunction(IntuitionBase,
								AutoRequestOffset, (__fptr)myport->mmp_code);
				Permit();
				AddPort(myport);
			}
			break;

		default:		/* --- Silly user didn't give a valid option --- */
			help(active);
			cleanup(5);
	}
	cleanup(0);
}
