/* OpenAmiga.org
 *  This file is part of the OpenAmiga "xfdmaster library project"
 *
 * For more information look at:
 *   http://openamiga.org/?function=viewproject&projectid=67
 */

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

static const char USED version[] = "\0$VER: xfdUnlink 53.1 (11.9.2014)";

static const char template[] = "SOURCE/A,DEST1/A,DEST2/A";

enum {
	ARG_SOURCE,
	ARG_DEST1,
	ARG_DEST2,
	ARG_MAX
};

struct global_data {
	struct xfdMasterIFace *ixfd;
	struct xfdLinkerInfo *xli;
	APTR buffer;
	ULONG buflen;
	LONG args[ARG_MAX];
};

static APTR read_file(const char *fn, ULONG *buflen);
static BOOL unlink(struct global_data *gd);
static BOOL write_file(const char *fn, CONST_APTR buffer, ULONG buflen);

int main(void) {
	struct global_data gd;
	struct xfdMasterBase *XfdBase;
	struct xfdMasterIFace *IXfd;
	struct RDArgs *rda;
	int rc = RETURN_ERROR;

	signal(SIGINT, SIG_IGN);

	memset(&gd, 0, sizeof(gd));

	IDOS->Printf(CSI"1m%s"CSI"0;32m (C) 2014 Fredrik Wikstrom"CSI";31m\n\n",
		version + 7);

	XfdBase = (struct xfdMasterBase *)IExec->OpenLibrary("xfdmaster.library", 53);
	IXfd = (struct xfdMasterIFace *)IExec->GetInterface((struct Library *)XfdBase, "main", 1, NULL);
	if (IXfd == NULL || !LIB_IS_AT_LEAST(&XfdBase->LibNode, 53, 7)) {
		IExec->DropInterface((struct Interface *)IXfd);
		IExec->CloseLibrary((struct Library *)XfdBase);
		IDOS->PutErrStr("You need xfdmaster.library v53.7+\n");
		return RETURN_FAIL;
	}

	rda = IDOS->ReadArgs(template, gd.args, NULL);
	if (rda != NULL) {
		gd.ixfd = IXfd;

		gd.xli = IXfd->xfdAllocObject(XFDOBJ_BUFFERINFO);
		if (gd.xli != NULL) {
			gd.buffer = read_file((const char *)gd.args[ARG_SOURCE], &gd.buflen);
			if (gd.buffer != NULL) {
				if (unlink(&gd)) {
					rc = RETURN_OK;
				}
				IExec->FreeVec(gd.buffer);
			}
			IXfd->xfdFreeObject(gd.xli);
		} else {
			IDOS->PrintFault(ERROR_NO_FREE_STORE, NULL);
		}

		IDOS->FreeArgs(rda);
	} else {
		IDOS->PrintFault(IDOS->IoErr(), NULL);
	}

	IExec->DropInterface((struct Interface *)IXfd);
	IExec->CloseLibrary((struct Library *)XfdBase);

	return rc;
}

static APTR read_file(const char *fn, ULONG *buflen) {
	BPTR fh;
	int64 len;
	APTR buffer = NULL;

	*buflen = 0;
	IDOS->Printf("Reading \"%s\"...\n", fn);

	fh = IDOS->Open(fn, MODE_OLDFILE);
	if (fh != ZERO) {
		len = IDOS->GetFileSize(fh);

		if (len >= 0 && len <= 0x7fffffff) {
			buffer = IExec->AllocVecTags(len,
				AVT_Type, MEMF_SHARED,
				AVT_Lock, FALSE,
				TAG_END);
			if (buffer != NULL) {
				if (IDOS->Read(fh, buffer, len) == len) {
					*buflen = len;
				} else {
					IExec->FreeVec(buffer);
					buffer = NULL;
					IDOS->PrintFault(IDOS->IoErr(), fn);
				}
			} else {
				IDOS->PrintFault(ERROR_NO_FREE_STORE, fn);
			}
		} else {
			if (len == -1) {
				IDOS->PrintFault(IDOS->IoErr(), fn);
			} else {
				IDOS->PrintFault(ERROR_OBJECT_TOO_LARGE, fn);
			}
		}

		IDOS->Close(fh);
	} else {
		IDOS->PrintFault(IDOS->IoErr(), fn);
	}

	return buffer;
}

static BOOL unlink(struct global_data *gd) {
	struct xfdMasterIFace *IXfd = gd->ixfd;
	struct xfdLinkerInfo *xli = gd->xli;
	BOOL res = FALSE;

	xli->xfdli_Buffer = gd->buffer;
	xli->xfdli_BufLen = gd->buflen;

	if (IXfd->xfdRecogLinker(xli)) {
		IDOS->Printf("Unlinking \"%s\" files\n", (const char *)gd->args[ARG_SOURCE]);

		if (IXfd->xfdUnlink(xli)) {
			if (write_file((const char *)gd->args[ARG_DEST1], xli->xfdli_Save1, xli->xfdli_SaveLen1) &&
				write_file((const char *)gd->args[ARG_DEST2], xli->xfdli_Save2, xli->xfdli_SaveLen2))
			{
				res = TRUE;
			}
		} else {
			IXfd->xfdPrintFault(xli->xfdli_Error, "Unlinking failed");
		}
	} else {
		IDOS->PutErrStr("Not linked\n");
	}

	return res;
}

static BOOL write_file(const char *fn, CONST_APTR buffer, ULONG buflen) {
	BPTR fh;
	BOOL res = FALSE;

	IDOS->Printf("Writing \"%s\"...\n", fn);

	fh = IDOS->Open(fn, MODE_NEWFILE);
	if (fh != ZERO) {
		if (IDOS->Write(fh, buffer, buflen) == buflen) {
			res = TRUE;
		}
		IDOS->Close(fh);
	}

	if (!res) IDOS->PrintFault(IDOS->IoErr(), fn);

	return res;
}

