#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include "roguestar.h"

extern void *AllocMem();
BPTR LoadSeg();
struct Task *FindTask();
struct MsgPort *CreateProc();

/* Launch(rport, name, argv, argc, env, pri, win, stack, in, out, err) --
 *                                               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.
 *      env   -- list of environment variables
 *      pri   -- priority of the new process.
 *      win   -- ToolWindow for the new process, or NULL.
 *      stack -- Stack size for the new process.
 *      in    -- Where to find Standard input
 *      out   -- Where to find Standard output
 *      err   -- Where to find Standard error
 *
 * argv[0] should be the same as the program. pri should normally be 0.
 * stack should normally be at least 4K.
 */

struct RogueStartup *
RogueLaunch(rport, name, argv, argc, env, pri, win, stack, in, out, err, mask)
struct MsgPort *rport;
char *name;
char **argv;
int argc;
char **env;
long pri;
char *win;
ULONG stack;
long in, out, err;
long mask;
{
  ULONG flock;
  struct Process *pp;
  struct RogueStartup *msg;
  char *s, *namep;
  int i;

  if(!rport) return 0;

  /* Get some space to work in -- the startup message */
  msg = (struct RogueStartup *)AllocMem(sizeof(struct RogueStartup),
                                        MEMF_PUBLIC|MEMF_CLEAR);
  if(!msg) return 0;
  /* Now load the program */
  msg->rs_Segment = LoadSeg(name);
  if(!msg->rs_Segment) {
    RogueFreeStartup(msg);
    return 0;
  }

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

  /* Allocate the arg list */
  msg->rs_argv=(char **)
       AllocMem((argc+1)*sizeof(char *),MEMF_PUBLIC|MEMF_CLEAR);
  if(!msg->rs_argv) {
    RogueFreeStartup(msg);
    return 0;
  }
  msg->rs_argc=argc;

  /* Allocate the FileHandle list */
  msg->rs_fhv=(long *)AllocMem(3*sizeof(long),MEMF_PUBLIC|MEMF_CLEAR);
  if(!msg->rs_fhv) {
    RogueFreeStartup(msg);
    return 0;
  }
  msg->rs_fhc=3;

  /* Empty out all args, just in case this aborts, so cleanup
   * can clean it up
   */

  for(i = 0; i<=argc; i++) {
    msg->rs_argv[i]=NULL;
  }

  for(i=0; i < argc; i++) {
    if((msg->rs_argv[i]=(char *)
        AllocMem(strlen(argv[i])+1,MEMF_PUBLIC|MEMF_CLEAR))==NULL) {
      RogueFreeStartup(msg);
      return 0;
    }
    strcpy(msg->rs_argv[i],argv[i]);
  }
  msg->rs_argv[argc]=NULL;

  /* Now the file handles */
  msg->rs_fhv[0]=in;
  msg->rs_fhv[1]=out;
  msg->rs_fhv[2]=err;
  msg->rs_closebits[0]=mask;

  /* Find the new process a current directory. A copy of mine will
   * do fine.
   */
  msg->rs_CurrentDir=Lock("",ACCESS_READ);

  /* Make a copy of the environment */
  if(env!=NULL) {
    int len;
    int num;
    char *b;
    len=num=0;

    while(env[num]!=NULL) {
      len+=strlen(env[num])+2;
      num++;
    }
    num+=2;
    if((msg->rs_envspace=AllocMem(len,MEMF_CLEAR|MEMF_PUBLIC))==NULL ||
        (msg->rs_environ=AllocMem(num*sizeof(char *),MEMF_CLEAR|MEMF_PUBLIC))==NULL) {
      RogueFreeStartup(msg);
      return 0;
    }
    b=msg->rs_envspace;
    msg->rs_envsize=len;
    msg->rs_envcount=num;
    num=0;
    while(env[num]!=NULL) {
      msg->rs_environ[num]=b;
      strcpy(b,env[num]);
      b+=strlen(b)+1;
      num++;
    }
    msg->rs_environ[num]=NULL;
  }
  else {
    msg->rs_environ=NULL;
    msg->rs_envspace=NULL;
  }

  /* Create the process. It is now running, but will wait for the
   * startup message.
   */
  msg->rs_Process = CreateProc(argv[0], pri, msg->rs_Segment, stack);
  if(!msg->rs_Process) {
    RogueFreeStartup(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->rs_Message.mn_ReplyPort = rport;
  msg->rs_Message.mn_Length = sizeof(struct RogueStartup);
  msg->rs_Message.mn_Node.ln_Type = NT_MESSAGE;

  PutMsg(msg->rs_Process, msg);

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


