/* 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 <dos/obsolete.h> /* Needed for FIB_IS_#? macros */
#include <string.h>

static const char USED version[] = "\0$VER: xfdList 53.3 (9.9.2014)";

static const char template[] = "FILE,ALL/S,NE=NOEXTERN/S";

enum {
	ARG_FILE,
	ARG_ALL,
	ARG_NOEXTERN,
	ARG_MAX
};

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

static BOOL list_dir(struct global_data *gd, const char *dir_name);
static const char *xfd_recog(struct global_data *gd, const char *fn);

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) {
		IExec->CloseLibrary((struct Library *)XfdBase);
		IDOS->PutErrStr("You need xfdmaster.library v53+\n");
		return RETURN_FAIL;
	}

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

		gd.buffer = IExec->AllocVecTags(gd.buflen,
			AVT_Type, MEMF_SHARED,
			TAG_END);
		gd.xbi = IXfd->xfdAllocObject(XFDOBJ_BUFFERINFO);
		gd.xli = IXfd->xfdAllocObject(XFDOBJ_LINKERINFO);
		gd.ap = IDOS->AllocDosObjectTags(DOS_ANCHORPATH,
			ADO_Strlen, PATHMAX,
			TAG_END);

		if (gd.buffer != NULL && gd.xbi != NULL && gd.xli != NULL && gd.ap != NULL) {
			if (list_dir(&gd, (const char *)gd.args[ARG_FILE])) {
				rc = RETURN_OK;
			}
		} else {
			IDOS->PrintFault(ERROR_NO_FREE_STORE, NULL);
		}

		IExec->FreeVec(gd.buffer);
		IXfd->xfdFreeObject(gd.xbi);
		IXfd->xfdFreeObject(gd.xli);
		IDOS->FreeDosObject(DOS_ANCHORPATH, gd.ap);

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

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

	return rc;
}

static BOOL list_dir(struct global_data *gd, const char *dir_name) {
	struct AnchorPath *ap = gd->ap;
	BOOL res = FALSE;
	LONG error, i;
	LONG level = 0;

	if (dir_name == NULL) dir_name = "";

	ap->ap_BreakBits = SIGBREAKF_CTRL_C;
    ap->ap_Flags |= APF_DOWILD;

	if ((error = IDOS->MatchFirst(dir_name, ap)) == 0) {
		if (!(ap->ap_Flags & APF_ITSWILD) && FIB_IS_DRAWER(&ap->ap_Info)) {
			ap->ap_Flags |= APF_DODIR;
			if ((error = IDOS->MatchNext(ap)) == 0 && (ap->ap_Flags & APF_DIDDIR)) {
				ap->ap_Flags &= ~APF_DIDDIR;
			}
		}
	}

	if (error == 0) {
		ap->ap_Flags &= ~APF_DIDDIR;

		do {
			if (ap->ap_Flags & APF_DIDDIR) {
				ap->ap_Flags &= ~APF_DIDDIR;
				level--;
				continue;
			}
			for (i = 0; i < level; i++) {
				IDOS->PutStr("  ");
			}
			if (FIB_IS_FILE(&ap->ap_Info)) {
				IDOS->Printf("%-36s %10lu  %s\n",
					ap->ap_Info.fib_FileName,
					ap->ap_Info.fib_Size,
					xfd_recog(gd, ap->ap_Buffer));
			} else {
				IDOS->Printf(CSI"3m"CSI"1m%-41s (Dir)"CSI"0m\n",
					ap->ap_Info.fib_FileName);

				if (gd->args[ARG_ALL]) {
					ap->ap_Flags |= APF_DODIR;
					level++;
				}
			}
		} while ((error = IDOS->MatchNext(ap)) == 0);
	}

	if (error == 0) {
		res = TRUE;
	} else if (error != ERROR_NO_MORE_ENTRIES) {
		IDOS->PrintFault(error, dir_name);
	}

	IDOS->MatchEnd(ap);

	return res;
}

static const char *xfd_recog(struct global_data *gd, const char *fn) {
	LONG datalen = 0;
	BPTR fh;

	fh = IDOS->Open(fn, MODE_OLDFILE);
	if (fh != ZERO) {
		datalen = IDOS->Read(fh, gd->buffer, gd->buflen);
		IDOS->Close(fh);
	}

	if (datalen > 0) {
		struct xfdMasterIFace *IXfd = gd->ixfd;
		struct xfdBufferInfo *xbi = gd->xbi;
		struct xfdLinkerInfo *xli = gd->xli;

		xbi->xfdbi_SourceBuffer = gd->buffer;
		xbi->xfdbi_SourceBufLen = datalen;
		xbi->xfdbi_Flags = 0;
		if (!gd->args[ARG_NOEXTERN]) xbi->xfdbi_Flags |= XFDFF_RECOGEXTERN;

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

		if (IXfd->xfdRecogBuffer(xbi)) {
			return xbi->xfdbi_PackerName;
		} else if (IXfd->xfdRecogLinker(xli)) {
			return xli->xfdli_LinkerName;
		}
	}

	return "Not crunched/linked";
}

