/*
**	loadwb.c - start Workbench
**	$VER: loadwb.c 42.1 (10.2.96)
**	Copyright © 1996 Michal Letowski
**
**	42.1 (10.2.96) - initial version
*/

#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 <support/types.h>
#include <support/dos.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/wb.h>
#include <clib/support_protos.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 TEMPLATE			"DEBUG=-DEBUG/S,DELAY/S,CLEANUP/S,NEWPATH/S,HIDE/S"

#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 */
};	/* 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 */


/*
**	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
*/
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);


/*
**	Public functions
*/
/****** C/LoadWB ************************************************************
*
*   NAME
*       LoadWB -- start Workbench. (V42)
*
*   SYNOPSIS
*       LoadWB [-DEBUG] [DELAY] [CLEANUP] [NEWPATH] [HIDE]
*
*   TEMPLATE
*       LoadWB "DEBUG=-DEBUG/S,DELAY/S,CLEANUP/S,NEWPATH/S,HIDE/S"
*
*   FUNCTION
*       This command will try to load and start Workbench. If Workbench was
*       already running, it will do nothing (unless NEWPATH option was used).
*       If NEWPATH option was used, LoadWB will make Workbench path a copy of
*       current Shell path.
*
*   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 Warkbench was already running, 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. HIDE 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.
*
*   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.
*
*   NOTES
*       To hide those 'NDOS:???' icons all DOS devices are removed before
*       starting Workbench and restored afterwards. This mght affect
*       multitasking!
*       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 Options Opts;
	struct List DevList;
	struct RDArgs *Args;
	struct CommandLineInterface *CLI;
	APTR ConTask;
	BPTR OldLock,PathLock=NULL;
	LONG WBFlags=0;
	LONG RC=RETURN_FAIL;
	
	/* 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);

	RC=RETURN_WARN;																/* 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_FAIL)														/* No error copying path */
	{
		NewList(&DevList);													/* Set up devices list */
		ConTask=SetConsoleTask(NULL);								/* No console task */
		OldLock=CurrentDir(NULL);										/* Change to root dir */
		if(Opts.opt_Hide)														/* HIDE option? */
			RemDosEntries(&DevList,DOSBase);					/* Remove all devices */
		if(StartWorkbench(WBFlags,PathLock))				/* StartWorkbench() successfull*/
			RC=RETURN_OK;
		else																				/* Unsuccessfull */
			FreePathCopy(PathLock,DOSBase);						/* Free path copy (if any) */
		if(Opts.opt_Hide)														/* HIDE option? */
			AddDosEntries(&DevList,DOSBase);					/* Add back all devices */
		CurrentDir(OldLock);												/* Change back to old dir */
		SetConsoleTask(ConTask);										/* Restore console task */
	}

	/* 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 */
