/*
**	loadwb.c - start Workbench
**	$VER: loadwb.c 42.2 (23.7.96)
**	Copyright © 1996 Michal Letowski
**
**	42.1 (10.2.96) - initial version
**	42.2 (23.7.96)
**		+ added RAMICON/S option
*/

#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <workbench/workbench.h>
#include <support/types.h>
#include <support/dos.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/icon.h>
#include <proto/wb.h>

#include "loadwb.rev.h"


/*
**	Constants
*/
#define DOS_NAME			"dos.library"
#define DOS_VERN			37L
#define WB_NAME				"workbench.library"
#define WB_VERN				39L
#define ICON_NAME			"icon.library"
#define ICON_VERN			37L

#define TEMPLATE			"DEBUG=-DEBUG/S,DELAY/S,CLEANUP/S,NEWPATH/S,HIDE/S,RAMICON/S"

#define SRC_ICON			"ENV:Sys/def_ramdisk"
#define DST_ICON			"RAM:Disk"

#define WBB_DEBUG			0													/* Turn on debug menu */
#define WBF_DEBUG			(1<<WBB_DEBUG)
#define WBB_DELAY			1													/* Wait a moment after starting */
#define WBF_DELAY			(1<<WBB_DELAY)
#define WBB_CLEANUP		2													/* Cleanup after starting */
#define WBF_CLEANUP		(1<<WBB_CLEANUP)
#define WBB_NEWPATH		3													/* Set new Workbench path */
#define WBF_NEWPATH		(1<<WBB_NEWPATH)


/*
**	Private structures
*/
struct Options
{
	LBOOL opt_Debug;															/* DEBUG/S */
	LBOOL opt_Delay;															/* DELAY/S */
	LBOOL opt_CleanUp;														/* CLEANUP/S */
	LBOOL opt_NewPath;														/* NEWPATH/S */
	LBOOL opt_Hide;																/* HIDE/S */
	LBOOL opt_RAMIcon;														/* RAMICON/S */
};	/* Options */

struct PathEntry
{
	BPTR pe_Next;																	/* Next entry */
	BPTR pe_Lock;																	/* Lock on directory */
};	/* PathEntry */

struct MyDevNode
{
	struct MinNode  mdn_Node;											/* Link */
	struct DosList *mdn_Device;										/* DOS device pointer */
};	/* MyDevNode */


/*
**	Private macros
*/
#define InitList(l)	((l)->lh_Head=(struct Node *)&(l)->lh_Tail,\
											(l)->lh_Tail=NULL,\
											(l)->lh_TailPred=(struct Node *)&(l)->lh_Head)


/*
**	Global data
*/
STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);


/*
**	Missing WB prototypes/pragmas
*/
LONG StartWorkbench(LONG flags, BPTR ptr);

#pragma libcall WorkbenchBase UpdateWorkbench 1e 00
#pragma libcall WorkbenchBase QuoteWorkbench 24 00
#pragma libcall WorkbenchBase StartWorkbench 2a 1002
#pragma libcall WorkbenchBase WBConfig 54 00


/*
**	Private functions prototypes
*/
/* Not all functions INLINED because of bug in SAS/S 6.50  */
STATIC BPTR GetPathCopy(BPTR path, struct Library *DOSBase);
STATIC VOID FreePathCopy(BPTR path, struct Library *DOSBase);
STATIC INLINE VOID AddDosEntries(struct List *list, struct Library *DOSBase);
STATIC INLINE VOID RemDosEntries(struct List *list, struct Library *DOSBase);
STATIC LBOOL CopyIcon(struct Library *IconBase);
STATIC INLINE LBOOL DeleteIcon(struct Library *IconBase);


/*
**	Public functions
*/
/****** C/LoadWB ************************************************************
*
*   NAME
*       LoadWB -- start Workbench. (V42)
*
*   SYNOPSIS
*       LoadWB [-DEBUG] [DELAY] [CLEANUP] [NEWPATH] [HIDE] [RAMICON]
*
*   TEMPLATE
*       LoadWB "DEBUG=-DEBUG/S,DELAY/S,CLEANUP/S,NEWPATH/S,HIDE/S,RAMICON/S"
*
*   FUNCTION
*       This command will try to load and start Workbench. If RAMICON option
*       is used, LoadWB will use 'ENV:Sys/def_ramdisk.info' icon for the
*       Ram-Disk icon. If NEWPATH option is used, LoadWB will replace a
*       current Workbench search path with a search path of Shell from which
*       the LoadWB was issued. If Workbench is already running, the command
*       will do nothing (unless NEWPATH or RAMICON option is used).
*
*   INPUTS
*       -DEBUG  - adds 'Debug' menu to the Workbench menus. There are two
*                 items under this menu:
*                     'ROMWack' starts internal ROM debugger, which requires
*                     a terminal to be connected to the Amiga internal serial
*                     port.
*                     'Flushlibs' removes all libraries, devices and fonts
*                     currently not in use thus freeing some memory.
*       DELAY   - forces LoadWB command not to return until Workbench is
*                 completely initialized (i.e. all disk icons are loaded and
*                 all WBStartup programs are started). If DELAY option does 
*                 not appear, LoadWB command will return as soon as Workbench
*                 is started.
*       CLEANUP - forces Workbench to execute 'Clean Up' function for main
*                 Workbench window after starting.
*       NEWPATH - lets you change current Workbench path. Normally, Workbench
*                 remembers path of the Shell that started Workbench. This
*                 path is inherited by all programs strated from Workbench.
*                 To change path of already running Workbench, you must use
*                 NEWPATH option. If Workbench has not been started yet, this
*                 option is ignored.
*       HIDE    - Workbench has a habit of putting 'NDOS:???' icons for all
*                 unrecognized disks. This is very annoying for users of
*                 CrossDOS and DiskSpare disks. HIDE will force Workbench
*                 to hide all those 'NDOS:???' icons, including the ones for
*                 unformatted disks. This option also implies DELAY.
*       RAMICON - changes the icon for a Ram-Disk. 'ENV:Sys/def_ramdisk.info'
*                 is used to represent the new Ram-Disk icon. This option
*                 can be used to set an icon for newly started Workbench
*                 or to change the Ram-Disk icon of running Workbench.
*                 This option also implies DELAY.
*
*   RESULT
*       RETURN_FAIL  - if 'dos.library' or 'workbench.library' couldn't be
*                      opened or command line arguments couldn't be processed.
*       RETURN_ERROR - NEWPATH option was given but copy of current path
*                      couldn't be made (due to lack of memory).
*       RETURN_WARN  - Workbench was already running and NEWPATH option was
*                      not given or RAMICON option was given and there were
*                      some problems with the Ram-Disk icon.
*
*   EXAMPLE
*       LoadWB HIDE
*           ; Load Workbench, hide all 'NDOS:???' icons. Command will not
*           ; return until Workbench is completely initialized.
*
*       LoadWB DEBUG CLEANUP
*           ; Start Workbench with 'Debug' menu, move all icons after
*           ; starting.
*
*       LoadWB NEWPATH
*           ; Set path of already running Workbench or just start it.
*
*       LoadWB RAMICON
*           ; Start Workbench and use 'ENV:Sys/def_ramdisk.info' as a
*           ; Ram-Disk icon
*
*   NOTES
*       To hide those 'NDOS:???' icons all DOS devices are removed before
*       starting Workbench and restored afterwards. This might affect
*       multitasking!
*       RAMICON works by copying 'ENV:Sys/def_ramdisk.info' to
*       'RAM:Disk.info' before Workench is started and deleting
*       'RAM:Disk.info' afterwards.
*       MakeLink is pure and can be made resident.
*
*   BUGS
*       LoadWB NEWPATH sometimes hangs for a few seconds. This bug also
*       appears in LoadWB 38.9 command.
*
*****************************************************************************
*
*/
LONG LoadWB(VOID)
{
	struct ExecBase *SysBase=INITSYSBASE;
	struct Library *DOSBase;
	struct Library *WorkbenchBase;
	struct Library *IconBase=NULL;

	struct Options Opts;
	struct List DevList;
	struct RDArgs *Args;
	struct CommandLineInterface *CLI;
	APTR ConTask;
	BPTR OldLock,PathLock=NULL;
	LONG WBFlags=0,RC=RETURN_FAIL;
	LBOOL Warn=FALSE;
	
	/* Open DOS */
	unless(DOSBase=OpenLibrary(DOS_NAME,DOS_VERN))
		throw2(SetResult2(ERROR_INVALID_RESIDENT_LIBRARY),	NO_DOS);

	/* Open Workbench */
	unless(WorkbenchBase=OpenLibrary(WB_NAME,WB_VERN))
		throw2(CauseIoErr(ERROR_INVALID_RESIDENT_LIBRARY,PROG_NAME),	NO_WB);

	/* Read arguments */
	clear(&Opts);																	/* Clear out buffer */
	unless(Args=ReadArgs(TEMPLATE,(LONG *)&Opts,NULL))
		throw2(PrintFault(IoErr(),PROG_NAME),	NO_ARGS);

	/* Process options */
	if(Opts.opt_Debug)		fset(WBFlags,WBF_DEBUG);
	if(Opts.opt_Delay)		fset(WBFlags,WBF_DELAY);
	if(Opts.opt_CleanUp)	fset(WBFlags,WBF_CLEANUP);
	if(Opts.opt_NewPath)	fset(WBFlags,WBF_NEWPATH);
	if(Opts.opt_Hide)			fset(WBFlags,WBF_DELAY);
	if(Opts.opt_RAMIcon)	fset(WBFlags,WBF_DELAY);

	RC=RETURN_OK;																	/* Working for now... */

	/* Get copy of path */
	if(CLI=(struct CommandLineInterface *)BADDR(ThisProcessD()->pr_CLI))
		if(CLI->cli_CommandDir)
			unless(PathLock=GetPathCopy(CLI->cli_CommandDir,DOSBase))
			{
				PrintFault(IoErr(),PROG_NAME);
				RC=RETURN_ERROR;
			}

	/* Start Workbench */
	if(RC<RETURN_ERROR)														/* No error copying path */
	{
		if(Opts.opt_RAMIcon)												/* RAMICON option? */
		{
			IconBase=OpenLibrary(ICON_NAME,ICON_VERN);
			unless(CopyIcon(IconBase))								/* Try to copy icon */
				Warn=TRUE;															/* No success - set warning */
		}

		InitList(&DevList);													/* Set up devices list */
		if(Opts.opt_Hide)														/* HIDE option? */
			RemDosEntries(&DevList,DOSBase);					/* Remove all devices */

		ConTask=SetConsoleTask(NULL);								/* No console task */
		OldLock=CurrentDir(NULL);										/* Change to root dir */
		unless(StartWorkbench(WBFlags,PathLock))		/* StartWorkbench() unsuccessfull*/
		{
			FreePathCopy(PathLock,DOSBase);						/* Free path copy (if any) */
			Warn=TRUE;																/* Set warning flag */
		}
		CurrentDir(OldLock);												/* Change back to old dir */
		SetConsoleTask(ConTask);										/* Restore console task */

		if(Opts.opt_Hide)														/* HIDE option? */
			AddDosEntries(&DevList,DOSBase);					/* Add back all devices */

		if(Opts.opt_RAMIcon)												/* RAMICON option? */
		{
			unless(DeleteIcon(IconBase))							/* Deletion successfull? */
				Warn=TRUE;															/* No - set warning */
			CloseLibrary(IconBase);										/* Close icon.library */
		}

		/* Set result appropriately */
		if(Warn)																		/* Warning condition? */
			RC=RETURN_WARN;														/* Set result if so */
	}

	/* Exceptions */
	catch(NO_ARGS,	FreeArgs(Args));
	catch(NO_WB,		CloseLibrary(WorkbenchBase));
	catch(NO_DOS,		CloseLibrary(DOSBase));
	return(RC);
}	/* LoadWB */


/*
**	Private functions
*/
STATIC BPTR GetPathCopy(BPTR path, struct Library *DOSBase)
{
	struct ExecBase *SysBase=INITSYSBASE;

	struct PathEntry *PE,*NewPE=NULL,*OldPE=NULL;
	BPTR Result=NULL;

	/* Make a copy of each path entry */
	for(PE=BADDR(path); PE; PE=BADDR(PE->pe_Next))
		if(new(NewPE,MEMF_PUBCLR))
		{
			if(OldPE)		OldPE->pe_Next=MKBADDR(NewPE);
			else				Result=MKBADDR(NewPE);
			unless(NewPE->pe_Lock=DupLock(PE->pe_Lock))
				break;
			OldPE=NewPE;
		}

	/* Check for success */
	if(!NewPE || !NewPE->pe_Lock)									/* AllocMem() or DupLock() error */
	{
		FreePathCopy(Result,DOSBase);								/* Free alloated memory */
		Result=NULL;																/* Mark error */
	}

	/* Return from function */
	return(Result);
}	/* GetPathCopy */

STATIC VOID FreePathCopy(BPTR path, struct Library *DOSBase)
{
	struct ExecBase *SysBase=INITSYSBASE;

	struct PathEntry *PE=BADDR(path),*OldPE;

	while(PE)
	{
		OldPE=PE;
		PE=BADDR(PE->pe_Next);
		UnLock(OldPE->pe_Lock);
		delete(OldPE);
	}
}	/* FreePathCopy */

STATIC INLINE VOID AddDosEntries(struct List *list, struct Library *DOSBase)
{
	struct ExecBase *SysBase=INITSYSBASE;

	struct DosList *DL;
	struct MyDevNode *MDN;

	if(DL=LockDosList(LDF_DEVICES | LDF_WRITE))		/* Lock list of DOS devices */
	{
		while(MDN=(struct MyDevNode *)RemHead(list))/* For each node on list */
		{
			if(MDN->mdn_Device)												/* Device pointer valid */
				AddDosEntry(MDN->mdn_Device);						/* Add to DOS list */
			delete(MDN);															/* Free memory */
		}
		UnLockDosList(LDF_DEVICES | LDF_WRITE);			/* Unlock list of DOS devices */
	}
}	/* AddDosEntries */

STATIC INLINE VOID RemDosEntries(struct List *list, struct Library *DOSBase)
{
	struct ExecBase *SysBase=INITSYSBASE;

	struct DosList *DL;
	struct MyDevNode *MDN;

	if(DL=LockDosList(LDF_DEVICES | LDF_WRITE))		/* Lock list of DOS devices */
	{																							/* Locking successfull */
		while(DL=NextDosEntry(DL,LDF_DEVICES))			/* For each device on list */
			if(new(MDN,MEMF_PUBCLR))									/* Try to allocate memory for it */
			{																					/* Allocation successfull */
				if(RemDosEntry(DL))											/* Try to remove entry */
					MDN->mdn_Device=DL;										/* Remember device pointer */
				AddHead(list,(struct Node *)MDN);				/* Add to list */
			}
		UnLockDosList(LDF_DEVICES | LDF_WRITE);			/* Unlock list of DOS devices */
	}
}	/* RemDosEntries */

STATIC LBOOL CopyIcon(struct Library *IconBase)
{
	struct DiskObject *Icon;
	LBOOL RC=FALSE;

	if(IconBase)
		if(Icon=GetDiskObject(SRC_ICON))
			RC=PutDiskObject(DST_ICON,Icon);
	return(RC);
}	/* CopyIcon */

STATIC INLINE LBOOL DeleteIcon(struct Library *IconBase)
{
	LBOOL RC=FALSE;

	if(IconBase)
		RC=DeleteDiskObject(DST_ICON);
	return(RC);
}	/* DeleteIcon */
