/*
 * fixslinks.c - Fix symlinks for several dos.library calls
 *
 * Author: Todd Vierling <amigagod@grove.ufl.edu>
 *
 * Do not link with startup code--compile and link as-is!
 */

#include <proto/exec.h>
#include <exec/execbase.h>
#include <proto/dos.h>
#include <dos/dosextens.h>
#include <dos.h>

#define D1 register __d1
#define D2 register __d2
#define D3 register __d3
#define D4 register __d4
#define A0 register __a0

struct DosLibrary *DOSBase;
char verstag[] = "\0$VER: fixslinks 0.5 (12.08.95) by Todd Vierling\n\0";

struct olddosfuncs {
	__asm BPTR (*odf_Open)(D1 STRPTR,D2 LONG);
	__asm BPTR (*odf_DeleteFile)(D1 STRPTR);
	__asm BPTR (*odf_Lock)(D1 STRPTR,D2 LONG);
	__asm BPTR (*odf_CreateDir)(D1 STRPTR);
	__asm BPTR (*odf_LoadSeg)(D1 STRPTR);
	__asm BPTR (*odf_SetComment)(D1 STRPTR,D2 STRPTR);
	__asm BPTR (*odf_SetProtection)(D1 STRPTR,D2 LONG);
	__asm BPTR (*odf_MakeLink)(D1 STRPTR,D2 LONG,D3 LONG);
	__asm BPTR (*odf_NewLoadSeg)(D1 STRPTR,D2 struct TagItem *);
} odf;

#define LVOOpen -0x1E
#define LVODeleteFile -0x48
#define LVOLock -0x54
#define LVOCreateDir -0x78
#define LVOLoadSeg -0x96
#define LVOSetComment -0xB4
#define LVOSetProtection -0xBA
#define LVOMakeLink -0x1BC
#define LVONewLoadSeg -0x300

void setfuncs(void);
#define erronf (IoErr() == ERROR_OBJECT_NOT_FOUND)

/* This function must be first (entrypoint) */

__asm int START(void) {
	if (!(DOSBase = (APTR)OpenLibrary("dos.library",37)))
		return 50;
	setfuncs();
	PutStr("fixslinks 0.5 now active.\n");

/*
 * This is one way to "wait forever safely". Later here will likely be put
 * a WaitPort() to interpret locked-softlink packet calls.
 */

	for (;;) {
		Wait(0x80000000);
		SetSignal(0,0x80000000);
	}
	return 0;
}

__asm BPTR lockpathbut1(A0 char **pathptr) {
	register char *path = *pathptr;
	BPTR cdir, newlock;
	char pathpart[36];
	register int i;

	if (!path) {
		SetIoErr(122);
		return NULL;
	}

	cdir = CurrentDir(NULL);
	newlock = DupLock(cdir);
	if (!*path)
		return newlock;

	for (;;) {
		CurrentDir(newlock);
		for (i = 0; i < 32; i++) {
			pathpart[i] = *path++;
			if (!pathpart[i] || pathpart[i] == '/' ||
			    pathpart[i] == ':')
				goto gotpath;
		}
		while (*path && *path != ':' && *path != '/')
			++path;
		++path;
	    gotpath:
		if (!path[-1])
			return cdir;

		*pathptr = path;
		pathpart[i+1] = 0;
		if (!(newlock = (*odf.odf_Lock)(pathpart,SHARED_LOCK))) {
			UnLock(CurrentDir(cdir));
			return NULL;
		}
		UnLock(CurrentDir(NULL));
	}
}

__asm BPTR SLOpen(D1 STRPTR nameptr, D2 LONG mode) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_Open)(name,mode)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_Open)(name,mode);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

__asm LONG SLDeleteFile(D1 STRPTR nameptr) {
	BPTR cdir;
	LONG rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_DeleteFile)(name)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return 0;
		rc = (*odf.odf_DeleteFile)(name);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

/*
 * __asm LONG SLRename(D1 STRPTR oldname, D2 STRPTR newname) {
 * }
 */

__asm BPTR SLLock(D1 STRPTR nameptr, D2 LONG type) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_Lock)(name,type)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_Lock)(name,type);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

__asm BPTR SLCreateDir(D1 STRPTR nameptr) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_CreateDir)(name)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_CreateDir)(name);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

/* This must preserve D1! A little SAS/C assumption is done here. */

__asm BPTR SLLoadSeg(D1 STRPTR nameptr) {
	BPTR cdir, rc;
	ULONG d1;
	char *name = nameptr;

	if (!(rc = (*odf.odf_LoadSeg)(name)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_LoadSeg)(name);
		d1 = getreg(REG_D1);
		UnLock(CurrentDir(cdir));
		putreg(REG_D1,d1);
	}
	return rc;
}

__asm LONG SLSetComment(D1 STRPTR nameptr, D2 STRPTR comment) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_SetComment)(name,comment)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_SetComment)(name,comment);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

__asm LONG SLSetProtection(D1 STRPTR nameptr, D2 LONG protect) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_SetProtection)(name,protect)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_SetProtection)(name,protect);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

/*
 * __asm LONG SLExecute(D1 STRPTR command, D2 BPTR file1, D3 BPTR file2) {
 * }
 */

__asm LONG SLMakeLink(D1 STRPTR nameptr, D2 LONG dest, D3 LONG soft) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_MakeLink)(name,dest,soft)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_MakeLink)(name,dest,soft);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

/*
 * __asm LONG SLSystem(D1 STRPTR command, D2 struct TagItem *tags) {
 * }
 */

__asm BPTR SLNewLoadSeg(D1 STRPTR nameptr, D2 struct TagItem *tags) {
	BPTR cdir, rc;
	char *name = nameptr;

	if (!(rc = (*odf.odf_NewLoadSeg)(name,tags)) && erronf) {
		if (!(cdir = lockpathbut1(&name)))
			return NULL;
		rc = (*odf.odf_NewLoadSeg)(name,tags);
		UnLock(CurrentDir(cdir));
	}
	return rc;
}

/*
 * __asm LONG SLSetOwner(D1 STRPTR name, D2 LONG owner_info) {
 * }
 */

/*
 * Pardon the (buuuuuurp!) 'getodfp' kludge, but it optimizes better this
 * way under SAS/C as a far data model.
 */

#define dosetfunc(x) \
	odfp->odf_ ## x = SetFunction(\
		dosbase, LVO ## x, (APTR)SL ## x)

struct olddosfuncs *getodfp(void) { return &odf; }

void setfuncs(void) {
	APTR dosbase = DOSBase;
	struct olddosfuncs *odfp = getodfp();

	dosetfunc(Lock);

	dosetfunc(Open);
	dosetfunc(DeleteFile);
	dosetfunc(CreateDir);
	dosetfunc(LoadSeg);
	dosetfunc(SetComment);
	dosetfunc(SetProtection);
	dosetfunc(MakeLink);
	dosetfunc(NewLoadSeg);
}
