/*
 *	Run.c - Copyright © 1990 by S.R. & P.C.
 *
 *	Created:	16 Jun 1990
 *	Modified:	30 Nov 1990  18:56:38
 *
 *	Make>> make
 */


#include "Menu.h"
#include "Tokens.h"
#include "WB.h"


/*****					external functions					*****/

extern char *copystr(char *);
extern void UpDateMenus(void);
extern void ParseLine(char *);

/*****					 global functions					*****/

void DoExtMenu(USHORT);
void WBFree(struct WBStartup *);


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

extern struct Menu Menu1;
extern struct MenuItem SubItem1;
extern struct Process *MyProcess;
extern struct ParmMsgPort *ParMPort;
extern char CurCfg[];
extern char CmdWindow[];
extern char ShellWindow[];
extern char ShellCmd[];
extern char WaitCmd[];
extern char TmpDir[];
extern char *ReqTitle;
extern long DefaultStack;
extern BOOL DoNextSelect;


/* Warn user of a load error */

static void LoadError(char *cmd)
{
	long err;
	char *msg;
	char buf[40];

	switch(err = IoErr()) {
	case ERROR_NO_FREE_STORE:
		msg = "Not enough memory.";
		break;
	case ERROR_FILE_NOT_OBJECT:
		msg = "File is not an object module.";
		break;
	case ERROR_OBJECT_NOT_FOUND:
		msg = "File not found.";
		break;
	default:
		SPrintf(buf, "Error code %ld", err);
		msg = buf;
	}
	SimpleRequest(ReqTitle, "Couldn't load '%s'\n%s", cmd, msg);
}


/* Execute a CLI command as background process */

static void ARun(struct Extended_MenuItem *emi)
{
	static struct ProcessControlBlock PCB;
	long CLI;

	PCB.pcb_StackSize = emi->emi_Stack;
	PCB.pcb_Pri = emi->emi_Pri;
	PCB.pcb_Console.pcb_ConName = emi->emi_Window;
	PCB.pcb_Control = (emi->emi_Window) ? PRF_STDIO : PRF_SAVEIO;
	if ((CLI = ASyncRun(emi->emi_Cmd, emi->emi_Args, &PCB)) <= 0) {
		if (CLI == PR_NOSTDIO)
			SimpleRequest(ReqTitle, "Couldn't open window:\n\"%s\"", emi->emi_Window);
		else
			LoadError(emi->emi_Cmd);
	}
}


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

static void Run(char *cmd, char *win, long stk, BYTE pri, BYTE mode)
{
	static struct NewShell NS;
	BPTR fh;
	short i=0;
	char FromFile[32], CmdBuf[128];

	NS.nsh_StackSize = stk;
	NS.nsh_Pri = pri;
	NS.nsh_Input = MyProcess->pr_CIS;
	NS.nsh_Output = MyProcess->pr_COS;
	NS.nsh_Control = BACKGROUND_SHELL;

	if (mode == TOK_SHELL) {
		for(;;) {
			SPrintf(FromFile, "%sParMCmd%d", TmpDir, i++);
			fh = Open(FromFile, MODE_NEWFILE);
			if (fh)
				break;
			else if (IoErr() != ERROR_OBJECT_IN_USE || i>32) {
				SimpleRequest(ReqTitle, "Unable to open script file");
				return;
			}
		}
		FPrintf(fh, "%s\nEndCLI >NIL:\n", cmd);
		Close(fh);
		if (!win)
			win = ShellWindow;
		SPrintf(CmdBuf, "\"%s\" \"%s\" From %s", ShellCmd, win, FromFile);
		cmd = CmdBuf;
	}
	ASyncRun(cmd, NULL, (struct ProcessControlBlock *)&NS);
}


/* 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 WBStartup *WBStartup)
{
	register BPTR lock;
	register int i;
	register char *cp;

	if (WBStartup->sm_ArgList) {
		for( i=0 ; i<2 ; i++ ) {
			if (lock = WBStartup->sm_ArgList[i].wa_Lock)
				UnLock(lock);
			if (cp = WBStartup->sm_ArgList[i].wa_Name)
				FreeMem(cp, strlen(cp)+1);
		}
		FreeMem(WBStartup->sm_ArgList, 2*sizeof(struct WBArg));
	}
	if (WBStartup->sm_Segment)
		UnLoadSeg(WBStartup->sm_Segment);
	FreeMem(WBStartup, sizeof(struct WBStartup));
}


/* load and run a workbench program */

static void WBRun(struct Extended_MenuItem *emi)
{
	struct WBStartup *WBStartup;
	struct WBArg *ArgList;
	struct DiskObject *DiskObject;
	BPTR DirLock;
	char Cmd[256], Dir[256], Name[32], *s;
	long stack;
	short arg;		/* Tool: arg=0, Project: arg=1 */

	strcpy(Cmd, emi->emi_Cmd);
	stack = emi->emi_Stack;
	if (!(WBStartup = AllocMem(sizeof(struct WBStartup), MEMF_PUBLIC|MEMF_CLEAR)))
		return;
	/* Allocate array for 2 args. Only one may be needed */
	if (!(ArgList = AllocMem(2*sizeof(struct WBArg), MEMF_PUBLIC|MEMF_CLEAR))) {
		WBFree(WBStartup);
		return;
	}
	WBStartup->sm_ArgList = ArgList;
	WBStartup->sm_NumArgs = 1;
	do {
		strcpy(Dir, Cmd);
		strcpy(Name, s = BaseName(Dir));
		if (s == Dir)
			DirLock = DupLock(MyProcess->pr_CurrentDir);
		else {
			if (*(s-1) == '/') s--;
			*s = '\0';
			if (!(DirLock = Lock(Dir, ACCESS_READ))) {
				SimpleRequest(ReqTitle, "Couldn't access '%s'", Dir);
				WBFree(WBStartup);
				return;
			}
		}
		if (!(DiskObject = GetDiskObject(Cmd))) {
			arg = 0;		/* No icon, assume Tool */
			if (stack == 0)
				stack = DefaultStack;
		}
		else if (DiskObject->do_Type == WBTOOL) {
			arg = 0;
			if (stack == 0) {	/* Take icon stack only if not user defined */
				stack = DiskObject->do_StackSize;
				if (stack < 4000) stack = DefaultStack;
			}
		}
		else if (arg != 1 && DiskObject->do_Type == WBPROJECT) {
			arg = 1;
			WBStartup->sm_NumArgs = 2;
			strcpy(Cmd, DiskObject->do_DefaultTool);
		}
		else {
			SimpleRequest(ReqTitle, "No tool found!");
			if (DiskObject)
				FreeDiskObject(DiskObject);
			WBFree(WBStartup);
			return;
		}
		ArgList[arg].wa_Lock = DirLock;
		ArgList[arg].wa_Name = copystr(Name);
		if (DiskObject)
			FreeDiskObject(DiskObject);
	} while(arg);

	WBStartup->sm_Message.mn_ReplyPort = (struct MsgPort *)ParMPort;
	WBStartup->sm_Message.mn_Length = sizeof(struct WBStartup);
	WBStartup->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (!(WBStartup->sm_Segment = LoadSeg(Cmd))) {
		LoadError(Cmd);
		WBFree(WBStartup);
		return;
	}
	if (!(WBStartup->sm_Process = (struct MsgPort *)CreateProc(Name, emi->emi_Pri, WBStartup->sm_Segment, stack))) {
		SimpleRequest(ReqTitle, "Couldn't execute '%s'", Cmd);
		WBFree(WBStartup);
		return;
	}
	PutMsg(WBStartup->sm_Process, (struct Message *)WBStartup);
	ParMPort->pmp_MsgCnt++;		/* keep track of unreplied startup messages */
}


void DoExtMenu(USHORT MenuNum)
{
	struct Extended_MenuItem *Item;

	Item = (struct Extended_MenuItem *) ItemAddress(&Menu1, MenuNum);
	switch (Item->emi_Mode) {
	case TOK_ARUN:
		ARun(Item);
		break;
	case TOK_RUN:
	case TOK_SHELL:
		Run(Item->emi_Cmd, Item->emi_Window, Item->emi_Stack, Item->emi_Pri, Item->emi_Mode);
		break;
	case TOK_WB:
		WBRun(Item);
		break;
	case TOK_CFG:	/* new cfg */
		strcpy(CurCfg, Item->emi_Cmd);
		UpDateMenus();
		DoNextSelect = FALSE;	/* Tell DoIntuiMsg not to execute next menu selection */
	}
}


#define CMDBUFSIZE	255

void Command(void)
{
	static struct NewShell NS;
	static char Buffer[CMDBUFSIZE+1];
	BPTR fh = NULL;
	char CmdBuf[CMDBUFSIZE+12];
	USHORT ExecMode;

	ExecMode = SubItem1.Flags & CHECKED;
	if (ExecMode && !(fh = Open(CmdWindow, MODE_NEWFILE)))
		return;
	if (GetString(Buffer, "Enter command...", NULL, 45, CMDBUFSIZE)) {
		if (ExecMode) {
			Execute(Buffer, 0, fh);
			FPrintf(fh, "[33mHit return...[30m›0 p");
			Read(fh, CmdBuf, 1L);
		}
		else {
			SPrintf(CmdBuf, "%s;%s", Buffer, WaitCmd);
			ParseLine(CmdBuf);
			Run(CmdBuf, CmdWindow, DefaultStack, 0, TOK_SHELL);
		}
	}
	if (ExecMode)
		Close(fh);
}

