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

/*
#define DO_ARP_COPIES
#include <libraries/arpbase.h>
#include <functions.h>
#include <exec/memory.h>
#include <workbench/startup.h>
#include <libraries/dosextens.h>
#include <workbench/workbench.h>
*/

#include <dos_functions.h>
#include <arpdos_pragmas.h>

#include "ParM.h"

/* Error messages */

static const char *ErrNoMem = "Out of memory!";
static const char *NoCmd = "Can't execute command!";
static const char *NoInfo = "Can't open info file!";


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

extern char *copystr( char * );
extern void Warn( const char * );


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

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


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

extern struct Menu Menu1;
extern struct Process *MyProcess;
extern struct MsgPort *WBReplyPort;
extern short WBCnt;


/* execute a RB command */

static BOOL RunBack(char *cmd, char *args)
{
	BPTR NilFh;
	char buf[256];
	BOOL ret;

	SPrintf(buf,"Run >NIL: <NIL: \"%s\" >NIL: <NIL: %s",cmd,args);
	NilFh = Open("NIL:",MODE_NEWFILE);
	if( NilFh ) {
		ret = Execute(buf,NULL,(struct FileHandle *)NilFh);
		Close(NilFh);
	}
	else
		ret = FALSE;
	return ret;
}


/* execute a CLI command */

static void CLIRun(struct Extended_MenuItem *emi)
{
	struct ProcessControlBlock *pcb;
	long CLI;

	if( pcb=AllocMem(sizeof(struct ProcessControlBlock),MEMF_PUBLIC|MEMF_CLEAR) ) {
		pcb->pcb_StackSize = emi->emi_Stack;
		pcb->pcb_Pri = emi->emi_Pri;
		pcb->pcb_Console.pcb_ConName = emi->emi_Window;
		pcb->pcb_Control = PRF_STDIO;
		CLI = ASyncRun(emi->emi_Cmd, emi->emi_Args, pcb);
		FreeMem(pcb, sizeof(struct ProcessControlBlock));
		if (CLI<=0) {
			switch( CLI ) {
			case PR_NOFILE :
				Warn("Command not found");
				break;
			case PR_NOSTDIO :
				Warn("Can't open window");
				break;
			default :
				Warn(NoCmd);
			}
		}
	}
	else
		Warn(ErrNoMem);
}


/* 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 ) {
		if( WBStartup->sm_ArgList ) {
			for (i=0; i<WBStartup->sm_NumArgs; 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,sizeof(struct WBArg)*WBStartup->sm_NumArgs);
		}
		if( WBStartup->sm_Segment ) UnLoadSeg(WBStartup->sm_Segment);
		if( cp=WBStartup->sm_ToolWindow ) FreeMem(cp, strlen(cp)+1);
		FreeMem( WBStartup , sizeof(struct WBStartup) );
	}
}

/*	take the path passed, and split it into a lock and name,
	suitable for putting into a WBArg structure */

static void SplitPath( char *path, BPTR *dir_lock, char **name)
{
	register BPTR lock;

	*name = BaseName(path);
	lock = Lock(path, ACCESS_READ);
	if( lock ) {
		*dir_lock = (BPTR)ParentDir((struct FileLock *)lock);
		UnLock(lock);
	} else
		*dir_lock = DupLock(MyProcess->pr_CurrentDir);
}

/* load and run a workbench program */

static void WBRun( char *cmd )
{
	BPTR lock = NULL, OldLock = NULL;
	struct WBStartup *WBStartup = NULL;
	struct DiskObject *disk_object = NULL;
	char *name, buf[128];
	BOOL error = TRUE;	/* assume the worst */

	/* allocate the startup message */
	if( !(WBStartup = (struct WBStartup *)AllocMem(sizeof(struct WBStartup),MEMF_PUBLIC|MEMF_CLEAR)) ) {
		Warn(ErrNoMem);
		return;	/* don't go to finish:, nothing allocated yet */
	}

	/* find the directory and name of the program to run */
	SplitPath(cmd, &lock, &name);
	if( !lock ) {
		Warn(NoCmd);
		goto finish;
	}

	/* try to load in the .info file */
	OldLock = (BPTR)CurrentDir((struct FileLock *)lock);
	if( !(disk_object = GetDiskObject(name)) ) {
		Warn(NoInfo);
		goto finish;
	}

	/* allocate the WBArgs - if we are a tool, we allocate one */
	/* of these, else two (one for tool, one for project)      */

	if( disk_object->do_Type == WBTOOL ) {
		if( !(WBStartup->sm_ArgList = (struct WBArg *)AllocMem(sizeof(struct WBArg), MEMF_PUBLIC|MEMF_CLEAR)) ) {
			Warn(ErrNoMem);
			goto finish;
		}
		WBStartup->sm_NumArgs = 1;
	} else if( disk_object->do_Type == WBPROJECT ) {
		if( !(WBStartup->sm_ArgList = (struct WBArg *)AllocMem(sizeof(struct WBArg)*2, MEMF_PUBLIC|MEMF_CLEAR)) ) {
			Warn(ErrNoMem);
			goto finish;
		}
		WBStartup->sm_NumArgs = 2;

		/* fill in arg #2 with the info we already have */

		WBStartup->sm_ArgList[1].wa_Lock = (BPTR)lock;
		WBStartup->sm_ArgList[1].wa_Name = copystr(name);

		/* now find the tool */

		strcpy( buf , disk_object->do_DefaultTool );
		SplitPath( buf , &lock , &name );
		FreeDiskObject( disk_object );
		CurrentDir( (struct FileLock *)lock );
		if( !(disk_object = GetDiskObject(name)) ) {
			Warn(NoInfo);
			goto finish;
		}

		/* we have to have a tool at this point - or else */

		if( disk_object->do_Type != WBTOOL ) {
			Warn("Not a tool or project!");
			goto finish;
		}
	}

	/* fill in arguments */

	WBStartup->sm_ArgList[0].wa_Lock = (BPTR)lock;
	WBStartup->sm_ArgList[0].wa_Name = copystr(name);

	/* initialize rest of startup message */

	WBStartup->sm_Message.mn_ReplyPort = WBReplyPort;
	WBStartup->sm_Message.mn_Length = sizeof(struct WBStartup);
	WBStartup->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
	WBStartup->sm_ToolWindow = copystr(disk_object->do_ToolWindow);

	/* get a decent stack size, there are a few progs that set this to zero */

	if( disk_object->do_StackSize < 4000 ) disk_object->do_StackSize = 4000;

	/* load in the program */

	if( !(WBStartup->sm_Segment = (BPTR)LoadPrg(name)) ) {
		Warn(NoCmd);
		goto finish;
	}

	/* create process */
	if( !(WBStartup->sm_Process = (struct MsgPort *)CreateProc(name, 0, WBStartup->sm_Segment, disk_object->do_StackSize)) ) {
		Warn(NoCmd);
		goto finish;
	}

	/* everything's ok -- start 'er up */

	PutMsg( WBStartup->sm_Process , (struct Message *)WBStartup );
	error = FALSE;
	WBCnt++;	/* keep track of unreplied startup messages */

finish:
	if( disk_object ) FreeDiskObject( disk_object );
	CurrentDir( (struct FileLock *)OldLock );
	if( error ) WBFree( WBStartup );
}


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

	Item = (struct Extended_MenuItem *)ItemAddress( &Menu1 , MenuNum );
	switch( Item->emi_Mode ) {
	case 'r' :
		if( !RunBack(Item->emi_Cmd,Item->emi_Args) ) Warn(NoCmd);
		break;
	case 'c' :
		CLIRun(Item);
		break;
	case 'w' :
		WBRun( Item->emi_Cmd );
		break;
	}
}

