/*
 * starttools.c   V1.5
 *
 * CLI & WB startup procedure
 *
 * (c) 1991 by Stefan Becker
 *
 */
#include "ToolManager.h"

#define CMDLINELEN 4096

/* Structure for path list */
struct PathList {
                 BPTR NextPath; /* Pointer to next PathList */
                 BPTR PathLock; /* Lock on directory */
                };

/* Build a path list from a string */
static BOOL BuildPathList(struct PathList **pla, struct PathList **plc, char *s)
{
 /* Path string valid? */
 if (s)
  {
   char *ps;

   /* Yes, copy path string */
   if (ps=strdup(s))
    {
     struct FileInfoBlock *fib;

     /* Get memory for FIB */
     if (fib=calloc(sizeof(struct FileInfoBlock),1))
      {
       char *cp1,*cp2=ps;
       struct PathList *pl1=*plc,*pl2=NULL;

       /* For every path part */
       while (cp1=cp2)
        {
         /* Search next path part */
         cp2=strchr(cp1,',');
         if (cp2)
          {
           *cp2='\0'; /* Add string end character */
           cp2++;     /* Move to next character */
          }

         /* Get memory for path list entry */
         if (!pl2 &&
             !(pl2=AllocVec(sizeof(struct PathList),MEMF_PUBLIC|MEMF_CLEAR)))
          {
           free(fib);
           free(ps);
           return(FALSE);
          }

         /* Get lock */
         if (pl2->PathLock=Lock(cp1,SHARED_LOCK))
          /* Is it a directory? */
          if (Examine(pl2->PathLock,fib) && (fib->fib_DirEntryType>0))
           {
            /* Yes, it is a directory, append it to the list. Head of list? */
            if (*pla)
             {
              pl1->NextPath=MKBADDR(pl2); /* No, chain new entry into list */
              pl1=pl2;
             }
            else
             *pla=pl1=pl2;                /* Yes, start of list */

            /* Invalidate pointer, next time a new PathList will be allocated */
            pl2=NULL;
           }
          else UnLock(pl2->PathLock); /* No, it is a file */
        }

       *plc=pl1;              /* Save end of list */
       if (pl2) FreeVec(pl2); /* Last Lock() failed, free memory */
       free(fib);
      }

     free(ps);
    }
  }

 return(TRUE);
}

/* Start tool as a CLI process */
static BOOL StartCLITool(struct AppMessage *msg, struct ToolNode *tn,
                         char *name)
{
 BPTR fl,ofh,ifh;               /* AmigaDOS file handles */
 struct MsgPort *newct=NULL;    /* New ConsoleTask pointer */
 struct PathList *pla=NULL,*plc=NULL;
 char *cmd;                     /* Buffer for command line */
 struct WBArg *wa=msg->am_ArgList;
 int cmdlen;                    /* Command line length */
 int i;
 char dir[NAMELEN];

 /* Get memory for command line */
 if (!(cmd=malloc(CMDLINELEN))) return(FALSE);

 fl=CurrentDir(tn->tn_DirLock); /* Change to tool's directory */

 strcpy(cmd,name);              /* Command name first */
 cmdlen=strlen(cmd);

 for (i=msg->am_NumArgs; i; i--,wa++)
  {
   char *cp;
   int namelen;

   if (!wa->wa_Lock) continue;  /* Skip arguments which don't support locks! */

   if (cmdlen>CMDLINELEN-2) break; /* Append a space for each parameter */
   strcat(cmd," ");
   cmdlen++;

   /* Build parameter from Lock & name */
   if (SameLock(tn->tn_DirLock,wa->wa_Lock)==LOCK_SAME) cp=wa->wa_Name;
   else
    {
     if (!NameFromLock(wa->wa_Lock,dir,NAMELEN)) continue;
     if (!AddPart(dir,wa->wa_Name,NAMELEN)) continue;
     cp=dir;
    }

   namelen=strlen(cp);

   if (strchr(cp,' '))          /* Special case: Space in a filename */
    if (namelen>NAMELEN-3) break;
    else
     {
      strins(cp,"\"");          /* Set parameter in double quotes */
      strcat(cp,"\"");
      namelen+=2;
     }

   if (cmdlen+namelen>CMDLINELEN-2) break;
   strcat(cmd,cp);              /* Append parameter */
   cmdlen+=namelen;             /* New command line length */
  }

 /* Open input & output file */
 if (tn->tn_Flags&TNFLAGS_COUT)
  if (tn->tn_OutFile)
   ofh=Open(tn->tn_OutFile,MODE_NEWFILE); /* Use tool output file */
  else
   ofh=Open(CLIOutputFile,MODE_NEWFILE); /* Use standard output file */
 else
  ofh=Open("NIL:",MODE_NEWFILE); /* Ignore output */
 if (!ofh) goto ste1;

 /* Is the output file an interactive file? */
 if (IsInteractive(ofh))
  {
   struct MsgPort *oldct;  /* Old ConsoleTask pointer */

   /* Yes. We need the same file as input file for CTRL-C/D/E/F redirection */
   /* Set our ConsoleTask to the new output file, so that we can re-open it */
   newct=((struct FileHandle *) BADDR(ofh))->fh_Type;
   oldct=SetConsoleTask(newct);

   /* Open the new input file (Now ifh points to the same file as ofh) */
   ifh=Open("CONSOLE:",MODE_OLDFILE);

   /* Change back to old ConsoleTask */
   SetConsoleTask(oldct);
  }
 else
  /* Non-interactive output, open dummy input file */
  ifh=Open("NIL:",MODE_OLDFILE);
 if (!ifh) goto ste2;

 /* Build path list */
 if (!BuildPathList(&pla,&plc,tn->tn_Path)) goto ste3; /* Tool path first */
 if (!BuildPathList(&pla,&plc,GlobalPath)) goto ste3;

 /* Start tool */
 if (SystemTags(cmd,SYS_Output,ofh,
                    SYS_Input,ifh,
                    SYS_Asynch,TRUE,    /* Run tools asynchronously */
                    SYS_UserShell,TRUE, /* Use user specified shell */
                    NP_StackSize,tn->tn_Stack,
                    NP_Path,MKBADDR(pla),
                    NP_ConsoleTask,newct,
                    TAG_DONE)==-1)
  goto ste3;

 /* Tool started */
 CurrentDir(fl);                /* Change to old directory */
 free(cmd);
 return(TRUE);

ste3: if (pla)                  /* Free path list */
       {
        struct PathList *pl1=pla,*pl2;

        do
         {
          pl2=BADDR(pl1->NextPath);
          UnLock(pl1->PathLock);
          FreeVec(pl1);
         }
        while (pl1=pl2);
       }
      Close(ifh);
ste2: Close(ofh);
ste1: CurrentDir(fl);           /* Change to old directory */
      free(cmd);
      return(FALSE);
}

/* Start tool as a WB process */
static BOOL StartWBTool(struct AppMessage *msg, struct ToolNode *tn,
                        char *name)
{
 BPTR fl;                        /* AmigaDOS file handle */
 register struct WBStartup *wbs; /* WBStartup message for tool */
 struct DiskObject *tdob;        /* Tool icon */
 LONG ssize;                     /* StackSize, default */
 struct MsgPort *proc;           /* Process descriptor for tool */
/* struct Process *proc;  Process descriptor for CreateNewProc */
 struct WBArg *wbad,*wbas;       /* Pointers to WB arguments */
 char *proname=NULL;             /* Name of Project icon */
 int i;

 /* Allocate memory for WBStartup */
 if (!(wbs=calloc(sizeof(struct WBStartup)+
                  sizeof(struct WBArg)*(msg->am_NumArgs+2),1))) return (FALSE);

 /* Change to tool's directory */
 fl=CurrentDir(tn->tn_DirLock);

 /* Is it a project? */
 if (tdob=GetDiskObject(name))
  if (tdob->do_Type==WBPROJECT)
   {
    proname=name;                      /* Save original name */
    name=strdup(tdob->do_DefaultTool); /* Get name of default tool */
    FreeDiskObject(tdob);
    if (!name) goto e1;                /* Enough memory? */
    tdob=GetDiskObject(name);          /* Get icon of the default tool */
   }

 /* Is it a tool? */
 ssize=tn->tn_Stack;
 if (tdob)
  {
   if (tdob->do_Type==WBTOOL)          /* Only tools supply this information */
    {
     if (tdob->do_ToolWindow) wbs->sm_ToolWindow=strdup(tdob->do_ToolWindow);
     if (tdob->do_StackSize>ssize) ssize=tdob->do_StackSize;
    }

   FreeDiskObject(tdob);
  }

 /* Load tool code */
 /* if (!(wbs->sm_Segment=NewLoadSeg(name,NULL))) goto e2; */
 if (!(wbs->sm_Segment=LoadSeg(name))) goto e2;

 /* Build WBStartup message */
 /* wbs->sm_Message.mn_Node.ln_Type=NT_MESSAGE; PutMsg() does this for us! */
 wbs->sm_Message.mn_ReplyPort=MyMP;
 wbs->sm_Message.mn_Length=sizeof(struct WBStartup);
 wbs->sm_NumArgs=msg->am_NumArgs+1;
 wbs->sm_ArgList=wbs+1;             /* WBArg array starts after WBStartup */

 /* Initialize WBArg pointers */
 wbas=msg->am_ArgList;
 wbad=wbs->sm_ArgList;

 /* 1. argument is the tool itself! */
 if (!(wbad->wa_Lock=DupLock(tn->tn_DirLock))) goto e3;
 if (!(wbad->wa_Name=strdup(name))) goto e4;
 wbad++;

 /* If tool is a project, add it as 2. parameter to the WBArg list */
 if (proname)
  {
   if (!(wbad->wa_Lock=DupLock(tn->tn_DirLock))) goto e4;
   if (!(wbad->wa_Name=strdup(proname))) goto e4;
   wbad++;
   wbs->sm_NumArgs++;
  }

 /* Copy WB arguments */
 for (i=msg->am_NumArgs; i; i--,wbas++,wbad++)
  {
   if (!(wbad->wa_Lock=DupLock(wbas->wa_Lock)))
    {
     wbad--;             /* Skip parameters, which don't support a lock */
     wbs->sm_NumArgs--;
     continue;           /* Next parameter */
    }

   /* Sanity check for name string... Enforcer is watching you! */
   if (!wbas->wa_Name || !(wbad->wa_Name=strdup(wbas->wa_Name))) goto e4;
  }

 /* Create process */
 if (!(wbs->sm_Process=CreateProc(wbs->sm_ArgList->wa_Name,0,wbs->sm_Segment,
                                  ssize))) goto e4;

/* if (!(proc=CreateNewProcTags(NP_Seglist,wbs->sm_Segment,
                              NP_FreeSeglist,TRUE,
  Maybe someday I will know   NP_StackSize,ssize,
  what Tags I need to make    NP_Name,wbs->sm_ArgList->wa_Name,
  CreateNewProc() behave      NP_CloseInput,FALSE,
  like CreateProc()           NP_CloseOutput,FALSE,
                              TAG_DONE))) goto e4; */

 /* Send WBStartup message to tool */
/* wbs->sm_Process=&proc->pr_MsgPort; for CreateNewProc() */
 PutMsg(wbs->sm_Process,(struct Message *) wbs);
 if (proname) free(name);       /* If project, then free default tool name */
 CurrentDir(fl);                /* Change to old directory */
 wbactive++;                    /* Tool started! */
 return(TRUE);

 /* An error occurred. Free all resources */
e4: wbas=wbs->sm_ArgList;
    for (i=wbs->sm_NumArgs; i; i--,wbas++)
     {
      UnLock(wbas->wa_Lock);
      if (wbas->wa_Name) free(wbas->wa_Name);
     }
e3: UnLoadSeg(wbs->sm_Segment);
e2: if (proname) free(name);
e1: CurrentDir(fl);
    free(wbs);
    return(FALSE);
}

/* Start a tool with arguments */
void StartTool(struct ToolNode *tn, struct AppMessage *msg)
{
 BOOL rc=TRUE;
 char *cp;
 struct AppMessage emsg;

 /* No Parameters? */
 if (!msg || tn->tn_Flags&TNFLAGS_NARG)
  {
   emsg.am_NumArgs=0;       /* Dummy AppMessage has 0 arguments */
   msg=&emsg;               /* Replace pointer to real message */
  }

 /* Get tool name */
 if (!(cp=tn->tn_RealName))
  cp=tn->tn_Node.ln_Name;

 /* Tool type? */
 switch(tn->tn_Type)
  {
   case TNTYPE_CLI:
    rc=StartCLITool(msg,tn,cp);
    break;
   case TNTYPE_WB:
    rc=StartWBTool(msg,tn,cp);
    break;
  }

 if (!rc) DisplayBeep(NULL); /* An error occured */
}
