#include <exec/memory.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

UBYTE *AllocMem();
BPTR LoadSeg();
struct MsgPort *CreateProc();

/* Launch(rport, name, argv, argc, pri, win, stack) -- launch a program.
 *
 * Launch builds a startup message, loads a program, and launches it.
 *
 * Arguments:
 *	rport -- reply port to receive startup message back when the program
 *	         finishes.
 *	name  -- File name for the program to load.
 *	argv  -- list of filenames to pass to loaded program.
 *	argc  -- length of argument list.
 *	pri   -- priority of the new process.
 *	win   -- ToolWindow for the new process, or NULL.
 *	stack -- Stack size for the new process.
 *
 * argv[0] should be the same as the program. pri should normally be 0.
 * stack should normally be at least 4K.
 */
struct WBStartup *
Launch(rport, name, argv, argc, pri, win, stack)
struct MsgPort *rport;
char *name;
char **argv;
int argc;
long pri;
char *win;
ULONG stack;
{
	ULONG flock;
	struct WBStartup *msg;
	char *s, *namep;
	int i;

	if(!rport)
		return 0;

	/* Get some space to work in -- the startup message */
	msg = (struct WBStartup *)
		AllocMem(sizeof(struct WBStartup), MEMF_PUBLIC|MEMF_CLEAR);
	if(!msg)
		return 0;

	/* Now load the program */
	msg->sm_Segment = LoadSeg(name);
	if(!msg->sm_Segment) {
		FreeStartup(msg);
		return 0;
	}

	/* Allocate and link in the window description */
	if(win) {
		msg->sm_ToolWindow = (char *)AllocMem(strlen(win)+1, MEMF_PUBLIC);
		if(!msg->sm_ToolWindow) {
			FreeStartup(msg);
			return 0;
		}
		strcpy(msg->sm_ToolWindow, win);
	} else
		msg->sm_ToolWindow = 0;

	/* Allocate the arg list */
	msg->sm_ArgList = (struct WBArg *)
		AllocMem(sizeof(struct WBArg) * argc, MEMF_PUBLIC | MEMF_CLEAR);
	if(!msg->sm_ArgList) {
		FreeStartup(msg);
		return 0;
	}

	/* Empty out all args, just in case this aborts, so cleanup
	 * can clean it up
	 */
	msg->sm_NumArgs = argc;
	for(i = 0; i < argc; i++) {
		msg->sm_ArgList[i].wa_Lock = 0;
		msg->sm_ArgList[i].wa_Name = 0;
	}

	/* Get lock and name for wbargs */
	for(i = 0; i < argc; i++) {
		flock = Lock(argv[i]);
		if(!flock) {
			FreeStartup(msg);
			return 0;
		}
		msg->sm_ArgList[i].wa_Lock = ParentDir(flock);
		UnLock(flock);
		if(!msg->sm_ArgList[i].wa_Lock) {
			FreeStartup(msg);
			return 0;
		}

		/* Get the tail end of the file name. I could also do an Examine
		 * on the lock, but didn't want to bother allocating a File Info
		 * Block that wouldn't fit under the tracking via the startup
		 * message.
		 */
		namep = argv[i];
		for(s = argv[i]; *s; s++)
			if(*s=='/' || *s==':')
				namep = s+1;
		msg->sm_ArgList[i].wa_Name =
			(char *)AllocMem(strlen(namep)+1, MEMF_PUBLIC);
		if(!msg->sm_ArgList[i].wa_Name) {
			FreeStartup(msg);
			return 0;
		}
		strcpy(msg->sm_ArgList[i].wa_Name, namep);
	}

	/* Create the process. It is now running, but will wait for the
	 * startup message.
	 */
	msg->sm_Process =
		CreateProc(msg->sm_ArgList[0].wa_Name, pri, msg->sm_Segment, stack);
	if(!msg->sm_Process) {
		FreeStartup(msg);
		return 0;
	}

	/* Initialise the message part of the startup message, and pass it to
	 * the process. At this point it will actually start ding work
	 */
	msg->sm_Message.mn_ReplyPort = rport;
	msg->sm_Message.mn_Length = sizeof(struct WBStartup);
	msg->sm_Message.mn_Node.ln_Type = NT_MESSAGE;

	PutMsg(msg->sm_Process, msg);

	/* return message. Not very useful, but it's as meaningful a response as
	 * any.
	 */
	return msg;
}
