/*
** PatchOpen von Patrick Ohly
** Beispiel-Patch der Open-Funktion
** © 1994 Patrick Ohly
*/


#ifdef _DCC
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#else
#include <proto/exec.h>
#include <proto/dos.h>
#endif
#include <clib/alib_protos.h>
#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/semaphores.h>
#include <dos/dos.h>

#include "PatchUtil.h"

#define PATCH_PORT "PatchDosOpen Port"

/* in amiga.lib enthalten */
extern __far LONG LVOOpen;

/* CTRL-C-Handling unterdrücken */
#ifdef _DCC
void chkabort(void) { }
#endif

/*
** Ausgabe über dieses FileHandle
** muß durch Semaphore geschuetzt werden.
*/
struct SignalSemaphore FH_Sema;
BPTR FH;

/*
** die vorherige Funktion
** !!! Achtung !!!
** Register A6 muß weiterhin einen Zeiger auf die
** jeweilige Library enthalten!
** Da der Compiler dies bei Zeigern auf die
** Funktionen nicht automatisch macht, muß ein
** weiteres Argument hinzugefügt werden!
*/

__regargs BPTR (* OldOpen)(__D1 char *name,
                           __D2 LONG mode,
            __A6 struct DosLibrary *DOSBase);

/* Anzahl der gerade aktiven Funktions-Aufrufe */
WORD FunctionCounter = 0;

/*
** gibt bei jedem Aufruf von Open() Filename und
** Modus aus - verwendet dazu Standardausgabe
*/
__regargs __geta4 __interrupt BPTR NewOpen(
            __D1 char *name, __D2 LONG mode)
{
	char buffer[120];
	BPTR fh;

	FunctionCounter++;

	sprintf(buffer, "\nFileName: %.68s\n"
                   "Zugriff: ", name);
	switch(mode)
	{
		case MODE_OLDFILE:
			strcat(buffer, "MODE_OLDFILE\n");
			break;
		case MODE_NEWFILE:
			strcat(buffer, "MODE_NEWFILE\n");
			break;
		case MODE_READWRITE:
			strcat(buffer, "MODE_READWRITE\n");
			break;
		default:
			strcat(buffer, "unbekannt\n");
			break;
	}

	/* vor Ausgabe Semaphore anfordern */
	ObtainSemaphore(&FH_Sema);
	Write(FH, buffer, strlen(buffer));
	ReleaseSemaphore(&FH_Sema);

	/* Original-Funktion aufrufen und deren
	   Resultat zurückgeben                 */
	fh = OldOpen(name, mode, DOSBase);
	FunctionCounter--;
	return(fh);
}

int main(int argc, char **argv)
{
	struct MsgPort *msg_port;

	if(msg_port = FindPort(PATCH_PORT))
	{
		/* bereits installiertes PatchDosOpen
		   entfernen                          */
		Signal(msg_port->mp_SigTask,
		       1L<<msg_port->mp_SigBit);
		puts("Versuche bereits installiertes"
		     " PatchOpen zu entfernen...");
	}
	else
	{
		/* neu installieren
		   erzeuge öffentlichen Port */
		if(msg_port = CreatePort(PATCH_PORT, 0))
		{
			APTR handle;

			InitSemaphore(&FH_Sema);
			FH = Output();

			Forbid();
			OldOpen = SafeSetFunction((struct Library *)DOSBase,
			          (LONG)&LVOOpen, NewOpen,
			          &handle);
			Permit();

			if(OldOpen)
			{
				puts("PatchOpen wurde installiert.");

				/* warte auf Ctrl-C oder nochmaligen
				   Start von PatchDosOpen            */
				Wait(SIGBREAKF_CTRL_C |
				     1L<<msg_port->mp_SigBit);
				SetSignal(0, SIGBREAKF_CTRL_C);

				RemoveFunction(OldOpen, handle);
				DeletePort(msg_port);

				/* unsere Funktion steht nicht mehr im
				   Vektor, aber andere Tasks könnten immer
				   noch unseren Code benutzen
				   => noch warten                       */
				do Delay(10);
				while(FunctionCounter);
				puts("PatchOpen wurde entfernt.");
			}
			else
				puts("Konnte Open() nicht patchen!");
		}
	}
	return(0);
}
