/* Copyright ) Darin Johnson, 1989 */

/* DoRun.c -- I met her on a Monday, and my heart stood still... */

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <intuition/intuition.h>
#include <workbench/icon.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <functions.h>
#include "mymenu.h"

#ifdef AZTEC_C
#define strlen _BUILTIN_strlen
#define strcpy _BUILTIN_strcpy
#endif

#ifdef DO_PATH
#define CMDSIZ 256
#define CBUFSZ 80
#endif

extern struct Process *MyProcess;

/* text to put into autorequesters */
struct IntuiText err_nocmd = { 0,1,JAM2, 20,10, NULL, 
	(UBYTE*)"MyMenu: can't execute command", NULL };
struct IntuiText err_nomem = { 0,1,JAM2, 20,10, NULL, 
	(UBYTE*)"MyMenu: out of memory!", NULL };
struct IntuiText err_noinfo = { 0,1,JAM2, 20,10, NULL, 
	(UBYTE*)"MyMenu: can't open info file", NULL };
struct IntuiText err_notool = { 0,1,JAM2, 20,10, NULL, 
	(UBYTE*)"MyMenu: not a tool or project", NULL };
struct IntuiText req_neg = { 0,1,JAM2, 7,3, NULL,
	(UBYTE*)"Aw, shucks", NULL };

/* process a menu item */
int run_item(item)
  struct ext_MenuItem *item;
{
  if (item->cmd) {
    if (item->type == 'C') {
      do_clirun(item->cmd, item->args);
    }
#ifdef DO_WB
    else if (item->type == 'W') {
      do_wbrun(item->cmd);
    }
#endif
  }
}

#ifdef DO_PATH
char *FindIt();
#endif

/* execute a CLI command */
do_clirun(cmd, args)
  char *cmd, *args;
{
  struct FileHandle *NilFh, *Open();
  register char *buf;
  register short siz;

#ifdef DO_PATH
  cmd = FindIt(cmd);
#endif
  if (cmd==NULL) {
    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
    return;
  }

  siz = 32 + strlen(cmd);
  if (args)
    siz += strlen(args);
  buf = (char *)AllocMem(siz, MEMF_PUBLIC);
  if (buf==NULL) {
    AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
    return;
  }
  strcpy(buf, "RUN <NIL: >NIL: \"");
  strcat(buf, cmd);
  strcat(buf, "\" <NIL: >NIL: ");
  if (args)
    strcat(buf, args);
    
  NilFh = Open("NIL:", MODE_NEWFILE);
  if (NilFh) {
    Execute(buf, NULL, NilFh);
    Close(NilFh);
  }
  FreeMem(buf, siz);
}

/* procedures to support running WorkBench programs */

#ifdef DO_WB
extern int wb_cnt;
extern struct MsgPort *wb_reply_port;

/* create (and allocate) a copy of a string */
char *copystr(str) {
  char *tmpstr;
  if (!str)
    return NULL;
  tmpstr = (char *)AllocMem(strlen(str)+1, MEMF_PUBLIC);
  strcpy(tmpstr, str);
  return tmpstr;
}

/* Free up space used by a workbench startup message.  Called whenever
   a workbench program replies to the startup message, and whenever
   do_wbrun() gets an error */
wbfree(WBStartup)
  struct WBStartup *WBStartup;
{
  register BPTR lock;
  register int i;
  register char *cp;
  if (WBStartup != NULL) {
    if (WBStartup->sm_ArgList != NULL) {
      for (i=0; i<WBStartup->sm_NumArgs; i++) {
        if ((lock=WBStartup->sm_ArgList[i].wa_Lock) != NULL) {
          UnLock(lock);
	}
	if ((cp=WBStartup->sm_ArgList[i].wa_Name) != NULL)
	  FreeMem(cp, strlen(cp)+1);
      }
      FreeMem(WBStartup->sm_ArgList,
  	sizeof(struct WBArg)*WBStartup->sm_NumArgs);
    }
    if (WBStartup->sm_Segment != NULL) {
      UnLoadSeg(WBStartup->sm_Segment);
    }
    if ((cp=WBStartup->sm_ToolWindow) != NULL)
      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 */
split_path(path, dir_lock, name)
  char *path, **name;
  BPTR *dir_lock;
{
  register char *p;
  register BPTR lock;
  
  for (p=path, *name=path; *p; p++)
    if (*p == '/' || *p == ':')
      *name = p+1;
  lock = (BPTR)Lock(path, ACCESS_READ);
  if (lock) {
    *dir_lock = (BPTR)ParentDir(lock);
    UnLock(lock);
  } else {
    *dir_lock = (BPTR)DupLock(MyProcess->pr_CurrentDir);
  }
}

/* load and run a workbench program */
int do_wbrun(cmd)
  char *cmd;
{
  BPTR lock, oldlock;
  struct WBStartup *WBStartup;
  struct DiskObject *disk_object;
  char argc, *name, buf[128];
  char error;
    
  disk_object = WBStartup = NULL;
  lock = oldlock = NULL;
  name = NULL;
  error = TRUE;	/* assume the worst */

    /* allocate the startup message */
  if ((WBStartup = (struct WBStartup *)AllocMem(sizeof(struct WBStartup),
  			MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
    AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
    return TRUE;	/* don't go to finish:, nothing allocated yet */
  }
#ifdef DO_PATH
  if ((cmd = FindIt(cmd)) == NULL)
    goto finish;
#endif

    /* find the directory and name of the program to run */
  split_path(cmd, &lock, &name);
  if (lock == NULL) {
    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
    goto finish;
  }

    /* try to load in the .info file */
  oldlock = (BPTR)CurrentDir(lock);
  if ((disk_object = GetDiskObject(name)) == NULL) {
    AutoRequest(NULL, &err_noinfo, NULL, &req_neg, 0,0,300,60);
    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)) == NULL) {
      AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
      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)) == NULL) {
      AutoRequest(NULL, &err_nomem, NULL, &req_neg, 0,0,300,60);
      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);
    split_path(buf, &lock, &name);
    FreeDiskObject(disk_object);
    CurrentDir(lock);
    if ((disk_object = GetDiskObject(name)) == NULL) {
      AutoRequest(NULL, &err_noinfo, NULL, &req_neg, 0,0,300,60);
      goto finish;
    }
      /* we have to have a tool at this point - or else */
    if (disk_object->do_Type != WBTOOL) {
      AutoRequest(NULL, &err_notool, NULL, &req_neg, 0,0,300,60);
      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 = wb_reply_port;
  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)LoadSeg(name)) == NULL) {
    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
    goto finish;
  }

   /* create process */
  if ((WBStartup->sm_Process = (struct MsgPort *)CreateProc(name,
	 0, WBStartup->sm_Segment, disk_object->do_StackSize)) == NULL) {
    AutoRequest(NULL, &err_nocmd, NULL, &req_neg, 0,0,300,60);
    goto finish;
  }
    /* everything's ok -- start 'er up */
  PutMsg(WBStartup->sm_Process, WBStartup);
  error = FALSE;
  wb_cnt++;	/* keep track of unreplied startup messages */
finish:
  if (disk_object) FreeDiskObject(disk_object);
  if (oldlock) CurrentDir(oldlock);
  if (error)
    wbfree(WBStartup);
  return error;
}
#endif

#ifdef DO_PATH
/* This section is largely taken from RunBack
  (which was largely taken from which.c by Carolyn Scheppner).
  This handles path searching.
*/

/***** currently these routines can cause guru's
       Run a workbench program, and then run a program that
       uses the path searching - often causes a Guru, but can't
       tell why.  If the wbfree() does NOT free its locks, then
       it doesn't guru.
*****/

static char sbuf[CMDSIZ];

/* search for a command in our path */
char *FindIt(command)
  char *command;
{
  BOOL getPath();
  struct Path *path;
  struct FileInfoBlock *fib;
  BPTR lock, startcd;
  BOOL Found, InitialCD;

     /* Were we given full path in command name (':' in name) ? */
  {
    register char *p;
    for (p=command; *p; p++) {
      if (*p == ':') {
        lock = (BPTR)Lock(command,ACCESS_READ);
	if (lock==NULL)
	  return NULL;		/* command not found */
	UnLock(lock);
        return command;
      }
    }
  }
  
    /* allocate this for Examine() calls */ 
  fib = (struct FileInfoBlock *)
	AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC|MEMF_CLEAR);
  if (fib == NULL)
    return NULL;
    
    /* Check current directory */
  Found = getPath(command, fib, sbuf);

    /* Check along paths */
  if (!Found) {
    InitialCD = TRUE;

    for (path = MM->CLI_path; (path) && (!Found); path = path->path_Next) {
        /* CD to each path */
      lock = (BPTR)CurrentDir(path->path_Lock);
      if (InitialCD) {
        startcd = lock; InitialCD = FALSE;
      }
        /* See if command is there */
      Found = getPath(command, fib, sbuf);
    }
      /* If we CD'd anywhere, restore initial CD */
    if (!InitialCD) 
      CurrentDir(startcd);
  }

    /* Check C: */
  if (!Found) {
    char cbuf[CBUFSZ];
    strcpy(cbuf,"C:");
    strcpy(&cbuf[2], command);
    Found = getPath(cbuf, fib, sbuf);
  }

    /* Free fib */
  if (fib != NULL)
    FreeMem(fib, sizeof(struct FileInfoBlock));

  if (Found)
    return(sbuf);
  else
    return(NULL);
}

/* see if command is in current directory, if so, return path name
   in 'buf' */
BOOL
getPath(command,fib,buf)
  char *command;
  struct FileInfoBlock *fib;
  char *buf;
{
  BPTR lock;
  BOOL success = FALSE;
  if (lock = (BPTR)Lock(command, ACCESS_READ)) {
    if (Examine(lock, fib)) {
      buildPath(lock, fib, buf);
      success = TRUE;
    }
    UnLock(lock);
  }
  return(success);
}

/* builds up a path name from 'inlock' and returns it in 'buf' */
buildPath(inlock,fib,buf)
  BPTR inlock;
  struct FileInfoBlock *fib;
  char *buf;
{
  int i;
  BPTR lock, oldlock;
  buf[0] = NULL;
  lock = inlock;

    /* follow path up, building up name as we go */
  while (lock) {
    if (Examine(lock, fib)) {
      if (fib->fib_FileName[0] > ' ') {
        if (buf[0]) insert(buf,"/");
        insert(buf,fib->fib_FileName);
      }
    }
    oldlock = lock;
    lock = (BPTR)ParentDir(lock);
      /* make sure we don't unlock the lock passed to us */
    if (oldlock != inlock)
       UnLock(oldlock);
  }

    /* fix up path name */
  if (fib->fib_FileName[0] > ' ') {
    register char *p;
    for (p=buf; *p; p++) {
      if (*p == '/') {
        *p = ':';
	break;
      }
    }
  } else
    insert(buf,"RAM:");	/* why? */
}

/* insert 's' at the beginning of 'buf' */
insert(buf,s)
  char *buf,*s;
{
  char tmp[CMDSIZ];
  strcpy(tmp, buf);
  strcpy(buf, s);
  strcpy(&buf[strlen(s)], tmp);
}
#endif
