	        /*********************************
		 *                               *
		 *   Visual Shell v1.17  10/92   *
		 *                               *
		 *     by Torsten Jürgeleit      *
		 *                               *
		 *         vsh main part         *
		 *                               *
		 *********************************/

	/* Includes */

#include "includes.h"

	/* Defines */

#define MAX_ARGUMENTS		4

#define ARGUMENT_LEFT		0
#define ARGUMENT_RIGHT		1
#define ARGUMENT_HIDE		2
#define ARGUMENT_REMOVE		3

#define	CTOB(cptr)		((BPTR)((ULONG)cptr >> 2))   /* Convert C ptr to BCPL ptr */
#define	BTOC(bptr)		((VOID *)((ULONG)bptr << 2))   /* Convert BCPL ptr to C ptr */

#define MIN_VSH_WIDTH		(USHORT)640   /* NTSC resolution */
#define MIN_VSH_HEIGHT		(USHORT)200

#define VSH_PROCESS_NAME	"VSh v1.17 for CLI %ld"
#define VSH_PROCESS_NAME_LEN	25
#define VSH_PROCESS_STACK_SIZE	4096
#define VSH_PROCESS_PRIORITY	0

	/* Structures */

struct StartupMsg {
	struct Message  sm_Message;
	struct Process  *sm_CLIProcess;
	struct Window   *sm_ConWindow;
	struct Device   *sm_ConDevice;
	struct ConUnit  *sm_ConUnit;
	BYTE	*sm_LeftPath;	/* path for left file requester */
	BYTE	*sm_RightPath;	/* path for right file requester */
	USHORT	sm_Flags;	/* used for HIDE flag */
};
	/* Globals */

struct ArpBase  *ArpBase;

	/* Statics */

STATIC struct ProcessControlBlock  seg_pcb;
STATIC struct StartupMsg           seg_sm;

STATIC BYTE process_name[VSH_PROCESS_NAME_LEN + 1],
	    *vsh_segment = "l:VShSegment",
	    *template = "Left,Right,HIDE/S,REMOVE/S",
	    *xtra_help = "\nVSh v1.17 - Copyright © 1992 Torsten Jürgeleit\n\n"
			 "Usage: VSh [Left] [Right] [REMOVE] [HIDE]\n"
			 "\t[Left]   = path for left file requester\n"
			 "\t[Right]  = path for right file requester\n"      
			 "\t[HIDE]   = start VSh in hide mode\n"
			 "\t[REMOVE] = remove VSh segment from resident list\n";
	/* Prototypes */

LONG _main(BYTE *arg_line, LONG arg_len);
BYTE *check_path(BYTE *path);
BOOL check_valid_start(struct Process  *proc, struct StartupMsg  *sm);
struct Window  *get_con_window(struct Process  *proc, struct StartupMsg  *sm);
LONG start_vsh_segment(struct Process  *proc, struct StartupMsg  *sm);

	/* Pragmas */

#pragma regcall(_main(a0,d0))
#pragma regcall(check_path(a0))
#pragma regcall(check_valid_start(a0,a1))
#pragma regcall(get_con_window(a0,a1))
#pragma regcall(start_vsh_segment(a0,a1))

	/* Main - parses arguments and starts main segment */

   LONG
_main(BYTE *arg_line, LONG arg_len)
{
   LONG return_code = RETURN_FAIL;

   /* First open ARP library */
   if (!(ArpBase = OpenLibrary(ArpName, ArpVersion))) {
      Write(Output(), "Need arp.library v39+\n", 22L);
   } else {
      BYTE   *argv[MAX_ARGUMENTS];
      USHORT i;

      /* Clear argument array */
      for (i = 0; i < MAX_ARGUMENTS; i++) {
	 argv[i] = NULL;
      }

      /* Parse command line arguments */
      if (GADS(arg_line, arg_len, xtra_help, &argv[0], template) < 0) {
	 Puts(argv[0]);
      } else {

	 /* Check arguments */
	 if (argv[ARGUMENT_REMOVE]) {
	    LONG usage;

	    /* Remove VSh segment from ARP resident list */
	    if (usage = RemResidentPrg(vsh_segment)) {
	       Printf("%s is [%ld] in use\n", vsh_segment, usage);
	    } else {
	       return_code = RETURN_OK;
	    }
	 } else {
	    struct StartupMsg  *sm = &seg_sm;
	    BOOL error = FALSE;

	    if (argv[ARGUMENT_HIDE]) {

	       /* Set HIDE flag */
	       sm->sm_Flags = 1;
	    }
	    if (argv[ARGUMENT_LEFT]) {

	       /* Check given path for left file requester */
	       if (!(sm->sm_LeftPath = check_path(argv[ARGUMENT_LEFT]))) {
		  error = TRUE;
	       }
	    } else {
	       sm->sm_LeftPath = NULL;
	    }
	    if (argv[ARGUMENT_RIGHT]) {

	       /* Check given path for right file requester */
	       if (!(sm->sm_RightPath = check_path(argv[ARGUMENT_RIGHT]))) {
		  error = TRUE;
	       }
	    } else {
	       sm->sm_RightPath = NULL;
	    }
	    if (error == FALSE) {
	       struct ResidentProgramNode  *rpn;
	       BPTR seg;

	       /* Check if VSh segment is already in ARP resident list */
	       if (rpn = ObtainResidentPrg(vsh_segment)) {
		  ReleaseResidentPrg(rpn->rpn_Segment);
	       } else {

		  /* Add VSh segment to ARP resident list */
		  if (!(seg = LoadPrg(vsh_segment + 2)) &&
					    !(seg = LoadPrg(vsh_segment))) {
		     Printf("Can't load '%s'\n", vsh_segment);
		  } else {
		     if (!(rpn = AddResidentPrg(seg, vsh_segment))) {
			Printf("Can't add '%s' to ARP resident list\n",
							       vsh_segment);
			UnLoadPrg(seg);
		     }
		  }
	       }

	       /* If VSh segment found then start it */
	       if (rpn) {
		  struct Process  *proc = (struct Process *)FindTask(NULL);

		  if (check_valid_start(proc, sm) == TRUE) {
		     return_code = start_vsh_segment(proc, sm);
		  }
	       }
	    }
	 }
      }
      CloseLibrary(ArpBase);
   }
   return(return_code);
}
	/* Check path for file requester */

   STATIC BYTE *
check_path(BYTE *path)
{
   struct FileInfoBlock  *fib;
   BPTR lock;
   BYTE *error_msg = NULL;

   if (!(lock = Lock(path, (LONG)SHARED_LOCK))) {
      error_msg = "Can't lock path '%s'\n";
   } else {
      if (!(fib = AllocMem((LONG)sizeof(struct FileInfoBlock), (LONG)
							    MEMF_PUBLIC))) {
	 error_msg = "Out of memory\n";
      } else {
	 if (Examine(lock, fib) == DOSFALSE) {
	    error_msg = "Examine() failed\n";
	 } else {
	    if (fib->fib_DirEntryType < 0) {
	       error_msg = "'%s' isn't a directory\n";
	    }
	 }
	 FreeMem(fib, (LONG)sizeof(struct FileInfoBlock));
      }
      UnLock(lock);
   }
   if (error_msg) {
      Printf(error_msg, path);
      path = NULL;
   }
   return(path);
}
	/* Check for a valid start of VSh */

   STATIC BOOL
check_valid_start(struct Process  *proc, struct StartupMsg  *sm)
{
   struct CommandLineInterface  *con_cli;
   struct Window                *con_win;
   BYTE *error_msg = NULL;
   BOOL success = FALSE;

   /* VSh startet from CLI ? */
   if (con_cli = BTOC(proc->pr_CLI)) {

      /* VSh started with RUN ? */
      if (con_cli->cli_Background == DOSTRUE) {
	 error_msg = "Don't start VSh with RUN !";
      } else {
	 struct FileHandle  *con_output_fh = BTOC(proc->pr_COS);

	 /* VSh startet from ConMan console window ? */
	 if ((struct FileHandle *)con_output_fh->fh_Arg1 != con_output_fh) {
	    error_msg = "Need a ConMan console window !";
	 } else {
	    if (!(con_win = get_con_window(proc, sm))) {
	       error_msg = "Can't find console window !";
	    } else {

	       /* Check window dimension */
	       if (con_win->WScreen->Width < MIN_VSH_WIDTH) {
		  error_msg = "Workbench screen to small (min. 640) !";
	       } else {
		  struct TextFont  *con_tf = con_win->RPort->Font;

		  /* Check text font used by this CLI */
		  if (con_tf->tf_XSize != 8 || con_tf->tf_YSize != 8 ||
				    (con_tf->tf_Flags & FPF_PROPORTIONAL)) {
		     error_msg = "Only 8x8 nonproportional fonts allowed !";
		  } else {

		     /* Check if VSh already started for this CLI */
		     SPrintf(&process_name[0], VSH_PROCESS_NAME,
							  proc->pr_TaskNum);
		     if (FindPort(&process_name[0])) {
			error_msg = "Can't start VSh for this CLI twice !";
		     } else {
			success = TRUE;
		     }
		  }
	       }
	    }
	 }
      }
      if (error_msg) {
	 Puts(error_msg);
      }
   }
   return(success);
}
	/* Get pointer to the console window our process started from */

   STATIC struct Window *
get_con_window(struct Process  *proc, struct StartupMsg  *sm)
{
   struct Window    *win = NULL;
   struct IOStdReq  *req;
   struct InfoData  *id;

   if (((struct Task *)proc)->tc_Node.ln_Type == NT_PROCESS) {
      if (id = AllocMem((LONG)sizeof(struct InfoData), (LONG)MEMF_PUBLIC)) {
	 if (dos_packet((struct MsgPort *)proc->pr_ConsoleTask, (LONG)
				   ACTION_DISK_INFO, CTOB(id)) == DOSTRUE) {
	    win              = (struct Window *)id->id_VolumeNode;
	    req              = (struct IOStdReq *)id->id_InUse;
	    sm->sm_ConWindow = win;
	    sm->sm_ConDevice = req->io_Device;
	    sm->sm_ConUnit   = (struct ConUnit *)req->io_Unit;
	 }
	 FreeMem(id, (LONG)sizeof(struct InfoData));
      }
   }
   return(win);
}
	/* Start VSh segment */

   STATIC LONG
start_vsh_segment(struct Process  *proc, struct StartupMsg  *sm)
{
   struct ProcessControlBlock  *pcb = &seg_pcb;
   LONG return_code = RETURN_FAIL;

   /* Init process control block and start VSh segment */
   pcb->pcb_StackSize = VSH_PROCESS_STACK_SIZE;
   pcb->pcb_Pri       = VSH_PROCESS_PRIORITY;
   pcb->pcb_Control   = PRF_NOCLI;
   if (ASyncRun(vsh_segment, NULL, pcb) < 0) {
      Printf("Can't run '%s'\n", vsh_segment);
   } else {
      struct MsgPort  *mp = &proc->pr_MsgPort;

      /* Init startup msg and send it to VSh segment */
      sm->sm_Message.mn_ReplyPort = mp;
      sm->sm_Message.mn_Length    = sizeof(struct StartupMsg);
      sm->sm_CLIProcess           = proc;
      PutMsg(pcb->pcb_WBProcess, &sm->sm_Message);

      /* Wait for reply of startup msg */
      WaitPort(mp);
      GetMsg(mp);
      return_code = RETURN_OK;
   }
   return(return_code);
}
