#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/semaphores.h>
#include <dos/dos.h>
#include <libraries/commodities.h>
#include <string.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/commodities.h>

#define ASM __asm __saveds
#define REG(x) register __## x

/*#define DEBUG*/

#ifdef DEBUG
#define BEEP	DisplayBeep(NULL);
#else
#define BEEP
#endif

/* Local protos */
VOID _main(VOID);
LONG ProcessMsg(void);
BOOL InstallWedge(VOID);
BOOL RemoveWedge(VOID);
struct JumpTable *GetJumpTable(UBYTE *);

/* Libraries */
extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
struct Library *CxBase;
#ifdef DEBUG
struct IntuitionBase *IntuitionBase;
#endif
/* The number of 'replacement' functions */
#define NUMBEROFFUNCTIONS (15)

/* prototypes for the functions to be SetFunction()'ed. */
#define DEC_LVO(lvoname)   extern lvoname

/* declare stuff for standard one-parameter function */
/* UBYTE * in d1 */
#define DEC11_OLD(oldname) LONG (*ASM oldname) (REG(d1) UBYTE *, REG(a6) struct Library *base)
#define DEC11_NEW(newname) LONG ASM newname(REG(d1) UBYTE *, REG(a6) struct Library *base)
#define DEC11(name) DEC11_OLD(old## name); DEC11_NEW(new## name); DEC_LVO(LVO## name)

/* declare stuff for standard two-parameter function */
/* UBYTE * in d1 */
#define DEC21_OLD(oldname) LONG (*ASM oldname) (REG(d1) UBYTE *, REG(d2) LONG, REG(a6) struct Library *base)
#define DEC21_NEW(newname) LONG ASM newname(REG(d1) UBYTE *, REG(d2) LONG, REG(a6) struct Library *base)
#define DEC21(name) DEC21_OLD(old## name); DEC21_NEW(new## name); DEC_LVO(LVO## name)

/* declare stuff for standard two-parameter function */
/* UBYTE * in d2 */
#define DEC22_OLD(oldname) LONG (*ASM oldname) (REG(d1) LONG, REG(d2) UBYTE *, REG(a6) struct Library *base)
#define DEC22_NEW(newname) LONG ASM newname(REG(d1) LONG, REG(d2) UBYTE *, REG(a6) struct Library *base)
#define DEC22(name) DEC22_OLD(old## name); DEC22_NEW(new## name); DEC_LVO(LVO## name)

/* the patched functions themselves */
DEC11(CreateDir);
DEC11(DeleteFile);
DEC11(LoadSeg);
DEC21(Lock);
DEC21(NewLoadSeg);
DEC21(Open);
DEC21(SetComment);
DEC21(SetFileDate);
DEC21(SetFileSize);
DEC21(SetProtection);
DEC22(AssignLate);
DEC22(AssignPath);
DEC21(MatchFirst);

/* manually decalred, because different from standard */
LONG (*ASM oldRename) (REG(d1) UBYTE *, REG(d2) UBYTE *, REG(a6) struct Library *);
LONG ASM newRename(REG(d1) UBYTE *, REG(d2) UBYTE *, REG(a6) struct Library *);
extern LVORename;

LONG (*ASM oldParsePattern) (REG(d1) UBYTE *, REG(d2) UBYTE *, REG(d3) LONG, REG(a6) struct Library *);
LONG ASM newParsePattern(REG(d1) UBYTE *, REG(d2) UBYTE *, REG(d3) LONG, REG(a6) struct Library *);
extern LVOParsePattern;

/* Use a table and an array to make it a little more generic and easier to
 * add functions.
 */
struct LVOTable
{
	LONG lt_LVO;
	struct Library *lt_LibBase;
	ULONG lt_oldFunction;
	ULONG lt_newFunction;
};

struct LVOTable LVOArray[] =
{
    {&LVOCreateDir, (struct Library *) & DOSBase, &oldCreateDir, &newCreateDir },
    {&LVODeleteFile, (struct Library *) & DOSBase, &oldDeleteFile, &newDeleteFile },
    {&LVOLoadSeg, (struct Library *) & DOSBase, &oldLoadSeg, &newLoadSeg },

    {&LVOLock, (struct Library *) & DOSBase, &oldLock, &newLock },
    {&LVONewLoadSeg, (struct Library *) & DOSBase, &oldNewLoadSeg, &newNewLoadSeg },
    {&LVOOpen, (struct Library *) & DOSBase, &oldOpen, &newOpen },

    {&LVOSetComment, (struct Library *) & DOSBase, &oldSetComment, &newSetComment },
    {&LVOSetFileDate, (struct Library *) & DOSBase, &oldSetFileDate, &newSetFileDate },
    {&LVOSetFileSize, (struct Library *) & DOSBase, &oldSetFileSize, &newSetFileSize },

    {&LVOSetProtection, (struct Library *) & DOSBase, &oldSetProtection, &newSetProtection },
    {&LVOAssignLate, (struct Library *) & DOSBase, &oldAssignLate, &newAssignLate },
    {&LVOAssignPath, (struct Library *) & DOSBase, &oldAssignPath, &newAssignPath },

    {&LVOMatchFirst, (struct Library *) & DOSBase, &oldMatchFirst, &newMatchFirst },
    {&LVORename, (struct Library *) & DOSBase, &oldRename, &newRename },
    {&LVOParsePattern, (struct Library *) & DOSBase, &oldParsePattern, &newParsePattern },

/*    {&LVO, (struct Library *) & DOSBase, &old, &new }*/
};

struct JumpTable
{
	struct SignalSemaphore jt_Semaphore;
	UWORD pad_word;
	struct Task *jt_Owner;
	UBYTE jt_Function[NUMBEROFFUNCTIONS * 6];
};

/* Strings */
/* The name this JumpTable/Semaphore will get. */
static UBYTE *JTName = "UnixDirsII-JT";
static UBYTE *VersTag = "\0$VER: UnixDirsII 1.0";

/* Commodity globals */
CxObj *broker;
struct MsgPort *broker_mp;

struct NewBroker newbroker =
{
	NB_VERSION,
	"UnixDirsII",
	"UnixDirsII v1.0, © Martin Scott, 1993",
	"Patch DOS to use Unix-style paths",
	NBU_UNIQUE | NBU_NOTIFY
};

ULONG cxsigflag;

int wedged = 1;			/* initially, wedges will be in place */

VOID
_main(VOID)
{
#ifdef DEBUG
    if (IntuitionBase = OpenLibrary("intuition.library", 37L))
    {
#endif
	if (CxBase = OpenLibrary("commodities.library", 37L))
	{
		if (broker_mp = CreateMsgPort())
		{
			newbroker.nb_Port = broker_mp;
			if (broker = CxBroker(&newbroker, NULL))
			{
				cxsigflag = 1L << broker_mp->mp_SigBit;
				ActivateCxObj(broker, 1L);

				if (InstallWedge())
					while (ProcessMsg());
				DeleteCxObj(broker);

				if (wedged)
					RemoveWedge();
			}
			DeleteMsgPort(broker_mp);
		}
		CloseLibrary(CxBase);
	}
#ifdef DEBUG
    CloseLibrary(IntuitionBase);
    }
#endif
}

LONG
ProcessMsg(void)
{
	CxMsg *msg;

	ULONG sigrcvd, msgid, msgtype;
	LONG returnvalue = 1L;

	/* wait for something to happen */
	sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag);

	/* process any messages */
	while (msg = (CxMsg *) GetMsg(broker_mp))
	{
		/* Extract necessary information from the CxMessage and return it */
		msgid = CxMsgID(msg);
		msgtype = CxMsgType(msg);
		ReplyMsg((struct Message *) msg);

		if (msgtype == CXM_COMMAND)	/* all we care about */
		{
			switch (msgid)
			{
			case CXCMD_DISABLE:
				if (wedged)
				{
					RemoveWedge();
					wedged = 0;
				}
				ActivateCxObj(broker, 0L);
				break;

			case CXCMD_ENABLE:
				if (!wedged)
				{
					InstallWedge();
					wedged = 1;
				}
				ActivateCxObj(broker, 1L);
				break;

			case CXCMD_UNIQUE:
			case CXCMD_KILL:
				returnvalue = 0L;
				break;
			}
		}
	}

	/* Test to see if user tried to break */
	if (sigrcvd & SIGBREAKF_CTRL_C)
		returnvalue = 0L;

	return (returnvalue);
}

BOOL
InstallWedge(VOID)
{
	struct JumpTable *jumptable;
	ULONG *addressptr;
	UCOUNT i, j;

	Forbid();

	/* Get pointer to JumpTable. Create it if necessary */
	if (jumptable = GetJumpTable(JTName))
	{
		/* Try to get exclusive lock on semaphore, in case it already existed. */
		if (AttemptSemaphore((struct SignalSemaphore *) jumptable))
		{
			/* Make sure nobody else has function addresses in the jumptable */
			if (jumptable->jt_Owner == NULL)
			{
				jumptable->jt_Owner = FindTask(0);
				/* Don't want to disable any longer than necessary */
				Disable();

				for (i = 2, j = 0; i < NUMBEROFFUNCTIONS * 6; i += 6, j++)
				{
					/* Replace addresses in the jumptable with my own. */
					addressptr = (ULONG *) ((UBYTE *) jumptable->jt_Function + i);
					(*((ULONG *) LVOArray[j].lt_oldFunction)) = (ULONG) * addressptr;
					*addressptr = (ULONG) LVOArray[j].lt_newFunction;
				}
				Enable();
			} else
				PutStr("Already running.\n");
			ReleaseSemaphore((struct SignalSemaphore *) jumptable);
		} else
			PutStr("Can't lock table.\n");
	} else
		PutStr("Can't create jumptable\n");
	Permit();
	return ((BOOL) jumptable);
}

BOOL
RemoveWedge(VOID)
{
	struct JumpTable *jumptable;
	ULONG *addressptr;
	UCOUNT i, j;

	Forbid();

	if (jumptable = GetJumpTable(JTName))
	{
		/* Check if this task owns this jumptable */
		if (jumptable->jt_Owner == FindTask(0))
		{

			/* Get the semaphore exclusively.
	                 * Depending on what got SetFunction()'ed this could take some time.
	                 * Also note that shared locks are used to indicate the code is
	                 * being executed and that shared locks can jump ahead of queue'ed
	                 * exclusive locks, adding to the waittime.
	                 */
			ObtainSemaphore((struct SignalSemaphore *) jumptable);

			Disable();

			/* Restore old pointers in jumptable */

			for (i = 2, j = 0; i < NUMBEROFFUNCTIONS * 6; i += 6, j++)
			{
				addressptr = (ULONG *) ((UBYTE *) jumptable->jt_Function + i);
				*addressptr = (*((ULONG *) LVOArray[j].lt_oldFunction));
			}

			Enable();

			jumptable->jt_Owner = NULL;
			ReleaseSemaphore((struct SignalSemaphore *) jumptable);
		}
	}
	Permit();

	return (TRUE);
}

struct JumpTable *
GetJumpTable(UBYTE * name)
{
	struct JumpTable *jumptable;
	ULONG *addressptr;
	UWORD *jmpinstr;
	UBYTE *jtname;
	UCOUNT i, j;

	/* Not really necessary to forbid again, just to indicate that I don't
         * want another task to create the semaphore while I'm trying to do the
         * same. Here GetJumpTable() is only called from InstallWedge(), so it
         * will just bump the forbid count.
         */
	Forbid();

	if (!(jumptable = (struct JumpTable *) FindSemaphore(name)))
	{
		if (jumptable = AllocMem(sizeof(struct JumpTable), MEMF_PUBLIC | MEMF_CLEAR))
		{
			if (jtname = AllocMem(strlen(name) + 1, MEMF_PUBLIC | MEMF_CLEAR))
			{

				for (i = 0, j = 0; i < NUMBEROFFUNCTIONS * 6; i += 6, j++)
				{
					jmpinstr = (UWORD *) ((UBYTE *) jumptable->jt_Function + i);
					*jmpinstr = 0x4EF9;

					addressptr = (ULONG *) ((UBYTE *) jumptable->jt_Function + i + 2);
					*addressptr = (ULONG) SetFunction(
										 (struct Library *) (*((ULONG *) LVOArray[j].lt_LibBase)),
							 LVOArray[j].lt_LVO,
										 (VOID *) ((UBYTE *) jumptable->jt_Function + i));
				}

				jumptable->jt_Semaphore.ss_Link.ln_Pri = 0;

				strcpy(jtname, name);
				jumptable->jt_Semaphore.ss_Link.ln_Name = jtname;
				AddSemaphore((struct SignalSemaphore *) jumptable);
			} else
			{
				FreeMem(jumptable, sizeof(struct JumpTable));
				jumptable = NULL;
			}
		}
	}
	Permit();

	/* If succeeded, you now have a jumptable which entries point to the original
         * library functions. If another task SetFunction()'ed one or more of those
         * already, that task can never go away anymore.
         */
	return (jumptable);
}


/******************************************************************************/
/*******************                                 **************************/
/*******************   NEW DOS ROUTINES START HERE   **************************/
/*******************                                 **************************/
/******************************************************************************/

/*
 * These are the generic patch routines.
 * They handle most patches automatically.
 */

extern BOOL adjustpath(UBYTE *path, UBYTE *newpath, LONG len);
#define PATHSIZE	512L	/* enough for most purposes? */

/* 1 args, convert arg in d1 */
LONG ASM Wedge11(REG(a0) LONG (*ASM dosroutine)(REG(d1) UBYTE *, REG(a6) struct Library *),
		    REG(d1) UBYTE *);

LONG ASM
Wedge11(REG(a0) LONG (*ASM dosroutine)(REG(d1) UBYTE *, REG(a6) struct Library *),
	   REG(d1) UBYTE *arg1)
{
	struct SignalSemaphore *jt;
	UBYTE *newpath;
	LONG rc = 0;

	if (jt = FindSemaphore(JTName))
	{
		ObtainSemaphoreShared(jt);

		if (newpath = AllocVec(PATHSIZE, 0L))
		{
			if (adjustpath(arg1, newpath, PATHSIZE))
				rc = dosroutine(newpath, DOSBase);
			else
				rc = dosroutine(arg1, DOSBase);

			FreeVec(newpath);
		}
		else SetIoErr(ERROR_NO_FREE_STORE);

		ReleaseSemaphore(jt);
	}
	return rc;
}

/* 2 args, convert arg in d1 */
LONG ASM Wedge21(REG(a0) LONG (*ASM dosroutine)(REG(d1) UBYTE *, REG(d2) LONG, REG(a6) struct Library *),
		    REG(d1) UBYTE *, REG(d2) LONG);

LONG ASM
Wedge21(REG(a0) LONG (*ASM dosroutine)(REG(d1) UBYTE *, REG(d2) LONG, REG(a6) struct Library *),
	   REG(d1) UBYTE *arg1, REG(d2) LONG arg2)
{
	struct SignalSemaphore *jt;
	UBYTE *newpath;
	LONG rc = 0;

	if (jt = FindSemaphore(JTName))
	{
		ObtainSemaphoreShared(jt);

		if (newpath = AllocVec(PATHSIZE, 0L))
		{
			if (adjustpath(arg1, newpath, PATHSIZE))
				rc = dosroutine(newpath, arg2, DOSBase);
			else
				rc = dosroutine(arg1, arg2, DOSBase);
			FreeVec(newpath);
		}
		else SetIoErr(ERROR_NO_FREE_STORE);

		ReleaseSemaphore(jt);
	}
	return rc;
}

/* 2 args, convert arg in d2 */
LONG ASM Wedge22(REG(a0) LONG (*ASM dosroutine)(REG(d1) LONG, REG(d2) UBYTE *, REG(a6) struct Library *),
		    REG(d1) LONG, REG(d2) UBYTE *);

LONG ASM
Wedge22(REG(a0) LONG (*ASM dosroutine)(REG(d1) LONG, REG(d2) UBYTE *, REG(a6) struct Library *),
	   REG(d1) LONG arg1, REG(d2) UBYTE *arg2)
{
	struct SignalSemaphore *jt;
	UBYTE *newpath;
	LONG rc = 0;

	if (jt = FindSemaphore(JTName))
	{
		ObtainSemaphoreShared(jt);

		if (newpath = AllocVec(PATHSIZE, 0L))
		{
			if (adjustpath(arg2, newpath, PATHSIZE))
				rc = dosroutine(arg1, newpath, DOSBase);
			else
				rc = dosroutine(arg1, arg2, DOSBase);

			FreeVec(newpath);
		}
		else SetIoErr(ERROR_NO_FREE_STORE);

		ReleaseSemaphore(jt);
	}
	return rc;
}

#define PATCH11(name) \
LONG ASM \
new## name(REG(d1) UBYTE *arg1, \
	REG(a6) struct Library *base) \
{ return Wedge11(old## name, arg1); }

#define PATCH21(name) \
LONG ASM \
new## name(REG(d1) UBYTE *arg1, REG(d2) LONG arg2, \
	REG(a6) struct Library *base) \
{ return Wedge21(old## name, arg1, arg2); }

#define PATCH22(name) \
LONG ASM \
new## name(REG(d1) LONG arg1, REG(d2) UBYTE *arg2, \
	REG(a6) struct Library *base) \
{ return Wedge22(old## name, arg1, arg2); }

PATCH11(CreateDir)
PATCH11(DeleteFile)
PATCH11(LoadSeg)
PATCH21(Lock)
PATCH21(NewLoadSeg)
PATCH21(Open)
PATCH21(SetComment)
PATCH21(SetFileDate)
PATCH21(SetFileSize)
PATCH21(SetProtection)
PATCH22(AssignLate)
PATCH22(AssignPath)
PATCH21(MatchFirst)

/*** And the following routines are handmade ***/

/* two to do here... */
LONG ASM
newRename(REG(d1) UBYTE *oldname, REG(d2) UBYTE *newname,
	  REG(a6) struct Library *base)
{
	struct SignalSemaphore *jt;
	UBYTE *from, *to;
	LONG rc = 0;

	if (jt = FindSemaphore(JTName))
	{
		ObtainSemaphoreShared(jt);

		if (from = AllocVec(PATHSIZE, 0L))
		{
			adjustpath(oldname, from, PATHSIZE);
			if (to = AllocVec(PATHSIZE, 0L))
			{
				adjustpath(newname, to, PATHSIZE);
				rc = oldRename(from, to, DOSBase);
				FreeVec(to);
			}
			else SetIoErr(ERROR_NO_FREE_STORE);
			FreeVec(from);
		}
		else SetIoErr(ERROR_NO_FREE_STORE);

		ReleaseSemaphore(jt);
	}
	return rc;
}

/* a bit more suspect this; will buflen be big enough given a . substitution? */
LONG ASM
newParsePattern(REG(d1) UBYTE *pat, REG(d2) UBYTE *buf, REG(d3) LONG buflen,
		REG(a6) struct Library *base)
{
	struct SignalSemaphore *jt;
	UBYTE *newpat;
	LONG rc = 0;

	if (jt = FindSemaphore(JTName))
	{
		ObtainSemaphoreShared(jt);

		if (newpat = AllocVec(PATHSIZE, 0L))
		{
			adjustpath(pat, newpat, PATHSIZE);
			rc = oldParsePattern(newpat, buf, buflen, DOSBase);
			FreeVec(newpat);
		}
		else SetIoErr(ERROR_NO_FREE_STORE);

		ReleaseSemaphore(jt);
	}
	return rc;
}

/*
LONG ASM
new(REG(d1) LONG arg1, REG(d2) LONG arg2,
	    REG(a6) struct Library *base)
{
	return old(arg1, arg2, base);
}
*/
