/*
 *	Run.c - Copyright © 1990 by S.R. & P.C.
 *
 *	Created:	16 Jun 1990
 *	Modified:	28 Jul 1991  18:36:43
 *
 *	Make>> make
 */

#include "ParMBase.h"


/*****					 global variables					*****/

extern struct ExecBase *SysBase;
extern struct ParMBase *ParMBase;


/* Execute a CLI command as background process */

void ARun(struct ParMConfig *PCfg, struct RunInfo *Command)
{
	struct ParMBase *ParMBase;
	struct ProcessControlBlock *PCB;
	long CLI;

	if (!(PCB = AllocMem(sizeof(struct ProcessControlBlock), MEMF_PUBLIC|MEMF_CLEAR)))
		return;
	PCB->pcb_StackSize = Command->ri_Stack;
	PCB->pcb_Pri = Command->ri_Pri;
	PCB->pcb_Console.pcb_ConName = Command->ri_Window;
	PCB->pcb_Control = (Command->ri_Window) ? PRF_STDIO : PRF_SAVEIO;
	if ((CLI = ASyncRun(Command->ri_Cmd, Command->ri_Args, PCB)) <= 0) {
		if (CLI == PR_NOSTDIO)
			SimpleRequest(PCfg->ReqTitle, "Couldn't open window:\n\"%s\"", Command->ri_Window);
		else
			SimpleRequest(PCfg->ReqTitle, "Couldn't load \"%s\"\n%s.", Command->ri_Cmd, StrIoErr());
	}
	FreeMem(PCB, sizeof(struct ProcessControlBlock));
}


/* Execute a CLI command as background or interactive shell */

void Run(struct ParMConfig *PCfg, struct RunInfo *Command, BYTE Mode)
{
	struct ParMBase *ParMBase;
	struct NewShell *NS;
	struct Process *pp;
	char *Window, *Cmd;
	BPTR fh;
	short i=0, err;
	char FromFile[32], CmdBuf[128];

	if (!(NS = AllocMem(sizeof(struct NewShell), MEMF_PUBLIC|MEMF_CLEAR)))
		return;
	pp = (struct Process *)SysBase->ThisTask;
	NS->nsh_StackSize = Command->ri_Stack;
	NS->nsh_Pri = Command->ri_Pri;
	NS->nsh_Input = pp->pr_CIS;
	NS->nsh_Output = pp->pr_COS;
	NS->nsh_Control = BACKGROUND_SHELL;

	Cmd = Command->ri_Cmd;
	if (Mode == TOK_SHELL) {
		for(;;) {
			SPrintf(FromFile, "%sParMCmd%d", PCfg->TmpDir, i++);
			fh = Open(FromFile, MODE_NEWFILE);
			if (fh)
				break;
			else if ((err = IoErr()) != ERROR_OBJECT_IN_USE || i>32) {
				SimpleRequest(PCfg->ReqTitle, "Unable to open script file\n%s.", DosError(err));
				FreeMem(NS, sizeof(struct NewShell));
				return;
			}
		}
		FPrintf(fh, "%s\nEndCLI >NIL:\n", Cmd);
		Close(fh);
		if (!(Window = Command->ri_Window))
			Window = PCfg->ShellWindow;
		SPrintf(CmdBuf, "\"%s\" \"%s\" From %s", PCfg->ShellCmd, Window, FromFile);
		Cmd = CmdBuf;
	}
	ASyncRun(Cmd, NULL, (struct ProcessControlBlock *)NS);
	FreeMem(NS, sizeof(struct NewShell));
}


/* procedures to support running WorkBench programs */

/*
 *	Free up space used by a workbench startup message.  Called whenever
 *	a workbench program replies to the startup message, and whenever
 *	WBRun() gets an error.
 */

void WBFree(struct Extended_WBStartup *EWBS)
{
	struct ParMBase *ParMBase;
	register BPTR lock;
	register int i;

	if (EWBS->WBStartup.sm_ArgList) {
		for( i=0 ; i<EWBS->WBStartup.sm_NumArgs ; i++ ) {
			if (lock = EWBS->WBStartup.sm_ArgList[i].wa_Lock)
				UnLock(lock);
			FreeStr(EWBS->WBStartup.sm_ArgList[i].wa_Name);
		}
		FreeMem(EWBS->ArgArray, EWBS->ArrayLength*sizeof(struct WBArg));
	}
	if (EWBS->WBStartup.sm_Segment)
		UnLoadPrg(EWBS->WBStartup.sm_Segment);
	FreeMem(EWBS, sizeof(struct Extended_WBStartup));
}


long MakeWBArg(struct ParMConfig *PCfg, struct WBArg *WBArg, char *Cmd)
{
	struct ParMBase *ParMBase;
	struct Process *pp;
	BPTR DirLock;
	char Dir[256], *s, *n;

	strcpy(Dir, Cmd);
	s = BaseName(Dir);
	if (!(n = CopyStr(s)))
		return FALSE;
	pp = (struct Process *)SysBase->ThisTask;
	if (s == Dir)
		DirLock = DupLock(pp->pr_CurrentDir);
	else {
		if (*(s-1) == '/') s--;
		*s = '\0';
		if (!(DirLock = Lock(Dir, ACCESS_READ))) {
			SimpleRequest(PCfg->ReqTitle, "Couldn't access \"%s\"", Dir);
			FreeStr(n);
			return FALSE;
		}
	}
	WBArg->wa_Name = n;
	WBArg->wa_Lock = DirLock;
	return TRUE;
}


long GetTool(struct ParMConfig *PCfg, struct Extended_WBStartup *EWBS, long Stack)
{
	struct ParMBase *ParMBase;
	struct Process *pp;
	struct DiskObject *DiskObject;
	long IconStack = 0;
	BPTR OldDir;
	BOOL ToolFound = FALSE;
	BOOL ProjectFound = FALSE;
	BOOL Error = FALSE;

	pp = (struct Process *)SysBase->ThisTask;
	OldDir = pp->pr_CurrentDir;
	do {
		CurrentDir(EWBS->WBStartup.sm_ArgList->wa_Lock);
		if (!(DiskObject = GetDiskObject(EWBS->WBStartup.sm_ArgList->wa_Name))) {
			ToolFound = TRUE;	/* No icon, assume tool */
		}
		else if (DiskObject->do_Type == WBTOOL) {
			ToolFound = TRUE;
			IconStack = DiskObject->do_StackSize;
		}
		else if (ProjectFound == FALSE && DiskObject->do_Type == WBPROJECT) {
			ProjectFound = TRUE;
			EWBS->WBStartup.sm_NumArgs++;
			EWBS->WBStartup.sm_ArgList = EWBS->ArgArray;
			if (!(MakeWBArg(PCfg, EWBS->ArgArray, DiskObject->do_DefaultTool)))
				Error = TRUE;
		}
		else {
			SimpleRequest(PCfg->ReqTitle, "No tool found!");
			break;
		}
		if (DiskObject)
			FreeDiskObject(DiskObject);
	} while(!ToolFound && !Error);
	CurrentDir(OldDir);
	if (Stack)
		EWBS->Stack = Stack;
	else if (IconStack < 4000)
		EWBS->Stack = PCfg->DefaultStack;
	else
		EWBS->Stack = IconStack;
	return ToolFound;
}


struct Extended_WBStartup *MakeWBStartup(struct ParMConfig *PCfg, struct RunInfo *Command)
{
	struct ParMBase *ParMBase;
	struct Extended_WBStartup *EWBS;

	if (!(EWBS = AllocMem(sizeof(struct Extended_WBStartup), MEMF_PUBLIC|MEMF_CLEAR)))
		return NULL;
	/* Allocate array for 2 args. Only one may be needed */
	if (!(EWBS->ArgArray = AllocMem(2*sizeof(struct WBArg), MEMF_PUBLIC|MEMF_CLEAR))) {
		WBFree(EWBS);
		return NULL;
	}
	EWBS->ArrayLength = 2;
	EWBS->WBStartup.sm_NumArgs = 1;
	EWBS->WBStartup.sm_ArgList = &EWBS->ArgArray[1];
	if (!MakeWBArg(PCfg, &EWBS->ArgArray[1], Command->ri_Cmd)) {
		WBFree(EWBS);
		return NULL;
	}
	if (!GetTool(PCfg, EWBS, Command->ri_Stack)) {
		WBFree(EWBS);
		return NULL;
	}
	return EWBS;
}


/* load and run a workbench program */

void WBRun(struct ParMConfig *PCfg, struct Extended_WBStartup *EWBS, BYTE Pri)
{
	struct ParMBase *ParMBase;
	struct Process *pp;
	APTR OldPtr;
	BPTR OldDir, CmdDir;
	char *Cmd;
	char Path[256];

	OldDir = CurrentDir(CmdDir = EWBS->WBStartup.sm_ArgList->wa_Lock);
	Cmd = EWBS->WBStartup.sm_ArgList->wa_Name;
	PathName(CmdDir, Path, 255-32);
	TackOn(Path, Cmd);
	EWBS->WBStartup.sm_Message.mn_ReplyPort = &ParMBase->pb_MsgPort;
	EWBS->WBStartup.sm_Message.mn_Length = sizeof(struct Extended_WBStartup);
	EWBS->WBStartup.sm_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (EWBS->WBStartup.sm_Segment = LoadPrg(Cmd)) {
		pp = (struct Process *)SysBase->ThisTask;
		OldPtr = pp->pr_WindowPtr;
		pp->pr_WindowPtr = NULL;
		if (EWBS->WBStartup.sm_Process = CreateProc(Cmd, Pri, EWBS->WBStartup.sm_Segment, EWBS->Stack)) {
			PutMsg(EWBS->WBStartup.sm_Process, (struct Message *)EWBS);
			ParMBase->pb_MsgCnt++;		/* keep track of unreplied startup messages */
		}
		else {
			SimpleRequest(PCfg->ReqTitle, "Couldn't execute \"%s\"\n%s.", Path, StrIoErr());
			WBFree(EWBS);
		}
		pp->pr_WindowPtr = OldPtr;
	}
	else {
		SimpleRequest(PCfg->ReqTitle, "Couldn't load \"%s\"\n%s.", Path, StrIoErr());
		WBFree(EWBS);
	}
	CurrentDir(OldDir);
}


void Command(struct ParMConfig *PCfg)
{
	BPTR fh = NULL;
	struct RunInfo Command;
	char *Cmd, CmdBuf[CMDBUFSIZE+40];

	if (PCfg->SimpleCmdMode && !(fh = Open(PCfg->CmdWindow, MODE_NEWFILE)))
		return;
	Command.ri_Cmd = CmdBuf;
	Command.ri_Args = 0;
	Command.ri_Window = PCfg->CmdWindow;
	Command.ri_Stack = PCfg->DefaultStack;
	Command.ri_Pri = 0;
	Cmd = PCfg->CommandBuffer;
	if (GetString(Cmd, "Enter Command...", NULL, 60, CMDBUFSIZE) && Cmd[0]) {
		strcpy(CmdBuf, Cmd);
		ParseLine(CmdBuf);
		if (PCfg->SimpleCmdMode) {
			Execute(CmdBuf, 0, fh);
			FPrintf(fh, "[33mHit return...[30m›0 p");
			Read(fh, CmdBuf, 1L);
		}
		else {
			SPrintf(&CmdBuf[strlen(CmdBuf)], "\n%s", PCfg->WaitCmd);
			Run(PCfg, &Command, TOK_SHELL);
		}
	}
	if (PCfg->SimpleCmdMode)
		Close(fh);
}

