/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by Olaf Barthel & MXM
 *
 *	Name .....: Format.c
 *	Created ..: Wednesday 29-May-91 17:06
 *	Revision .: 1
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	06-Jun-91	Olsen		Kept the panel from being created
 *					twice.
 *	29-May-91	Olsen		Created this file!
 *
 * $Revision Header ********************************************************/

	/* A handy keyboard shortcut. */

struct Shortcut
{
	UBYTE	Key;
	SHORT	GadgetID,GadgetType;
};

	/* A custom node, contains both the DeviceNode and the change
	 * count of a disk drive.
	 */

struct DevNode
{
	struct Node		 VanillaNode;
	struct DeviceNode	*DevNode;
	ULONG			 ChangeCount;
};

	/* Gadget IDs. */

enum	{	GAD_DRIVES,GAD_SIZE,GAD_NAME,GAD_WHOLEDISK,GAD_VERIFY,
		GAD_CREATEICONS,GAD_INSTALLDISK,GAD_AUTOSTART,
		GAD_EJECTDISK,GAD_USEFFS,GAD_STATUS,GAD_START,
		GAD_STOP };

	/* Size of the Format window. */

#define WIDTH	556
#define HEIGHT	125

	/* Shell interface. */

enum	{	ARG_DRIVE,ARG_NAME,ARG_FFS,ARG_NOICONS,ARG_QUICK,ARG_NOVERIFY,ARG_INSTALL,ARG_EJECT };

#define NUM_ARGS	ARG_EJECT + 1
#define ARG_TEMPLATE	"DRIVE/K/A,NAME/K,FFS/S,NOICONS/S,QUICK/S,NOVERIFY/S,INSTALL/S,EJECT/S"

	/* Just another macro. */

#define SetWait(Window)	SetPointer((Window),&Stopwatch[0],16,16,-6,0)

	/* A set of keyboard macros. */

struct Shortcut Shortcuts[] =
{
	'N',	GAD_NAME,		STRING_KIND,
	'F',	GAD_WHOLEDISK,		CHECKBOX_KIND,
	'V',	GAD_VERIFY,		CHECKBOX_KIND,
	'C',	GAD_CREATEICONS,	CHECKBOX_KIND,
	'I',	GAD_INSTALLDISK,	CHECKBOX_KIND,
	'A',	GAD_AUTOSTART,		CHECKBOX_KIND,
	'E',	GAD_EJECTDISK,		CHECKBOX_KIND,
	'U',	GAD_USEFFS,		CHECKBOX_KIND
};

	/* Global library identifiers. */

extern struct ExecBase	*SysBase;
struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Library		*GadToolsBase;
struct Library		*IconBase;
struct Library		*WorkbenchBase;

	/* Part of the Workbench interface. */

struct MsgPort		*WBenchPort;
struct AppWindow	*WBenchWindow;

	/* Graphics & Intuition data. */

struct Screen		*DefaultScreen;
APTR			 VisualInfo;
struct TextFont		*Topaz;
struct Gadget		*GadgetList;
struct Gadget		*GadgetArray[GAD_STOP + 1];
struct Window		*Window;

	/* List of filing devices. */

struct List		*DevList;

	/* Some more global data and flags. */

UBYTE			*DiskName;
struct DeviceNode	*LastNode;
BYTE			 Formatting;
BYTE			 WholeDisk,Verify,CreateIcons,InstallDisk,AutoStart,EjectDisk,UseFFS;
BYTE			 FromShell;

	/* The default font. */

struct TextAttr DefaultFont =
{
	(UBYTE *)"topaz.font",
	8,
	FS_NORMAL,
	FPF_ROMFONT
};

	/* The stopwatch sprite pointer. */

UWORD __chip Stopwatch[(2 + 16) * 2] =
{
	0x0000,0x0000,

	0x0400,0x07C0,
	0x0000,0x07C0,
	0x0100,0x0380,
	0x0000,0x07E0,
	0x07C0,0x1FF8,
	0x1FF0,0x3FEC,
	0x3FF8,0x7FDE,
	0x3FF8,0x7FBE,
	0x7FFC,0xFF7F,
	0x7EFC,0xFFFF,
	0x7FFC,0xFFFF,
	0x3FF8,0x7FFE,
	0x3FF8,0x7FFE,
	0x1FF0,0x3FFC,
	0x07C0,0x1FF8,
	0x0000,0x07E0,

	0x0000,0x0000
};

	/* Run-dump of a standard filing system bootblock. */

ULONG Bootblock_OFS[13] =
{
	0x444F5300,0xC0200F19,0x00000370,0x43FA0018,
	0x4EAEFFA0,0x4A80670A,0x20402068,0x00167000,
	0x4E7570FF,0x60FA646F,0x732E6C69,0x62726172,
	0x79000000
};

	/* Run-dump of an FFS bootblock (the checksum's different). */

ULONG Bootblock_FFS[13] =
{
	0x444F5301,0xC0200F18,0x00000370,0x43FA0018,
	0x4EAEFFA0,0x4A80670A,0x20402068,0x00167000,
	0x4E7570FF,0x60FA646F,0x732E6C69,0x62726172,
	0x79000000
};

	/* Disable the SAS/C ^C trapping. */

int CXBRK(VOID) { return(0); }

	/* UpdateInfo():
	 *
	 *	Check the checkbox gadgets and set the formatting
	 *	flags accordingly.
	 */

VOID
UpdateInfo()
{
	if(!FromShell)
	{
		if(GadgetArray[GAD_WHOLEDISK] -> Flags & GFLG_SELECTED)
			WholeDisk = TRUE;
		else
			WholeDisk = FALSE;

		if(GadgetArray[GAD_VERIFY] -> Flags & GFLG_SELECTED)
			Verify = TRUE;
		else
			Verify = FALSE;

		if(GadgetArray[GAD_CREATEICONS] -> Flags & GFLG_SELECTED)
			CreateIcons = TRUE;
		else
			CreateIcons = FALSE;

		if(GadgetArray[GAD_INSTALLDISK] -> Flags & GFLG_SELECTED)
			InstallDisk = TRUE;
		else
			InstallDisk = FALSE;

		if(GadgetArray[GAD_AUTOSTART] -> Flags & GFLG_SELECTED)
			AutoStart = TRUE;
		else
			AutoStart = FALSE;

		if(GadgetArray[GAD_EJECTDISK] -> Flags & GFLG_SELECTED)
			EjectDisk = TRUE;
		else
			EjectDisk = FALSE;

		if(GadgetArray[GAD_USEFFS] -> Flags & GFLG_SELECTED)
			UseFFS = TRUE;
		else
			UseFFS = FALSE;
	}
}

	/* CloseDrive(struct IOExtTD *DevRequest):
	 *
	 *	Close a disk driver opened using OpenDrive.
	 */

VOID
CloseDrive(struct IOExtTD *DevRequest)
{
	CloseDevice(DevRequest);

	DeleteMsgPort(DevRequest -> iotd_Req . io_Message . mn_ReplyPort);

	DeleteIORequest(DevRequest);
}

	/* OpenDrive(struct DeviceNode *DevNode):
	 *
	 *	Open a filing system driver 
	 */

struct IOExtTD *
OpenDrive(struct DeviceNode *DevNode)
{
	struct FileSysStartupMsg	*Startup = (struct FileSysStartupMsg *)BADDR(DevNode -> dn_Startup);
	struct MsgPort			*DevPort;
	struct IOExtTD			*DevRequest;

	if(DevPort = CreateMsgPort())
	{
		if(DevRequest = (struct IOExtTD *)CreateIORequest(DevPort,sizeof(struct IOExtTD)))
		{
			if(!OpenDevice(&((UBYTE *)BADDR(Startup -> fssm_Device))[1],Startup -> fssm_Unit,DevRequest,Startup -> fssm_Flags))
				return(DevRequest);

			DeleteIORequest(DevRequest);
		}

		DeleteMsgPort(DevPort);
	}

	return(NULL);
}

	/* GetChangedDrive():
	 *
	 *	Walk through the list of filing devices and obtain
	 *	the change count of each drive. Return the device
	 *	which has recently had a disk-change.
	 */

struct DevNode *
GetChangedDrive()
{
	struct DevNode	*ChangedDrive = NULL;
	struct DevNode	*SomeNode;
	struct IOExtTD	*DevRequest;

	SomeNode = (struct DevNode *)DevList -> lh_Head;

		/* Walk through the list.. */

	while(SomeNode -> VanillaNode . ln_Succ)
	{
			/* Open the driver. */

		if(DevRequest = OpenDrive(SomeNode -> DevNode))
		{
			DevRequest -> iotd_Req . io_Command = TD_CHANGENUM;

				/* Obtain the disk change count. */

			if(!DoIO(DevRequest))
			{
					/* Is it different from the
					 * value last obtained?
					 */

				if(SomeNode -> ChangeCount != DevRequest -> iotd_Req . io_Actual)
				{
					ChangedDrive = SomeNode;

					SomeNode -> ChangeCount = DevRequest -> iotd_Req . io_Actual;
				}
			}

			CloseDrive(DevRequest);
		}

		SomeNode = (struct DevNode *)SomeNode -> VanillaNode . ln_Succ;
	}

	return(ChangedDrive);
}

	/* FindDevices():
	 *
	 *	Scan the DosList for block-mapped filing devices.
	 */

struct List *
FindDevices()
{
	struct DosList		*DosList;
	struct DeviceNode	*DevNode;
	struct List		*SomeList;
	struct DevNode		*SomeNode;
	UBYTE			*Pointer;
	SHORT			 i;

		/* Allocate a list for the DevNode entries. */

	if(SomeList = (struct List *)AllocVec(sizeof(struct List),MEMF_PUBLIC|MEMF_CLEAR))
	{
		NewList(SomeList);

			/* Lock the DosList for reading. */

		DosList = LockDosList(LDF_DEVICES|LDF_READ);

			/* Scan the list for device entries. */

		while(DosList = NextDosEntry(DosList,LDF_DEVICES|LDF_READ))
		{
				/* Swap the entry type. */

			DevNode = (struct DeviceNode *)DosList;

				/* Kludge: RAW:, PRT:, PAR: have dn_Startup
				 * set to a nonzero value.
				 */

			if(DevNode -> dn_Startup > 1000 && (DevNode -> dn_Task || DevNode -> dn_Handler || DevNode -> dn_SegList))
			{
				Pointer = (UBYTE *)BADDR(DevNode -> dn_Name);

					/* If it has a name, add it to the list. */

				if(Pointer[0])
				{
					if(SomeNode = AllocVec(sizeof(struct DevNode) + Pointer[0] + 2,MEMF_PUBLIC|MEMF_CLEAR))
					{
						SomeNode -> VanillaNode . ln_Name = (UBYTE *)(SomeNode + 1);

						for(i = 0 ; i < Pointer[0] ; i++)
							SomeNode -> VanillaNode . ln_Name[i] = Pointer[i + 1];

						SomeNode -> VanillaNode . ln_Name[Pointer[0]    ]	= ':';
						SomeNode -> VanillaNode . ln_Name[Pointer[0] + 1]	= 0;
						SomeNode -> DevNode					= DevNode;

						AddTail(SomeList,SomeNode);
					}
				}
			}
		}

		UnLockDosList(LDF_DEVICES|LDF_READ);
	}

	return(SomeList);
}

	/* FreeDevices(struct List *SomeList):
	 *
	 *	Free the list of devices obtained through FindDevices.
	 */

VOID
FreeDevices(struct List *SomeList)
{
	struct Node *SomeNode,*NextNode;

	SomeNode = SomeList -> lh_Head;

	while(SomeNode -> ln_Succ)
	{
		NextNode = SomeNode -> ln_Succ;

		FreeVec(SomeNode);

		SomeNode = NextNode;
	}

	FreeVec(SomeList);
}

	/* CreateAllGadgets():
	 *
	 *	Create all the gadgets for the Format window.
	 */

struct Gadget *
CreateAllGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0,LeftEdge;

	if(Gadget = CreateContext(GadgetList))
	{
		if(DevList = FindDevices())
			GetChangedDrive();

		NewGadget . ng_Width		= 268;
		NewGadget . ng_Height		= 60;
		NewGadget . ng_GadgetText	= "Drives";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_LEFT;
		NewGadget . ng_LeftEdge		= LeftEdge = (strlen(NewGadget . ng_GadgetText) + 2) * 8 + 1;
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(LISTVIEW_KIND,Gadget,&NewGadget,
			GTLV_Labels,		DevList,
			GTLV_Selected,		DevList ? 0 : ~0,
			GTLV_ShowSelected,	NULL,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Size";
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 13;

		GadgetArray[Counter++] = Gadget = CreateGadget(TEXT_KIND,Gadget,&NewGadget,
			GTTX_Text,		"   -- Tracks   --.--- MBytes",
			GTTX_Border,		TRUE,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Name";
		NewGadget . ng_Height		= 14;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,	31,
			GTST_String,	"Empty",
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Format Whole Disk";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_RIGHT;
		NewGadget . ng_LeftEdge		= LeftEdge + 272;
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTCB_Checked,	TRUE,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Verify Writes";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTCB_Checked,	TRUE,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Create Icons";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GTCB_Checked,	TRUE,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Install Disk";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Auto Start";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Eject Disk";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "_Use Fast Filing System";
		NewGadget . ng_Width		= 244;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(CHECKBOX_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Status";
		NewGadget . ng_Width		= 268;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= LeftEdge;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 2;

		GadgetArray[Counter++] = Gadget = CreateGadget(TEXT_KIND,Gadget,&NewGadget,
			GTTX_Text,		"Idle",
			GTTX_Border,		TRUE,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Start (_S)";
		NewGadget . ng_Width		= 133;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= LeftEdge;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 1;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Stop (_S)";
		NewGadget . ng_Width		= 133;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= LeftEdge + 135;

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			GT_Underscore,	'_',
			GA_Disabled,	TRUE,
		TAG_DONE);
	}

	return(Gadget);
}

	/* CloseAll(LONG ReturnCode):
	 *
	 *	Return all resources obtained at startup and exit
	 *	gracefully.
	 */

VOID
CloseAll(LONG ReturnCode)
{
	if(WBenchWindow)
		RemoveAppWindow(WBenchWindow);

	if(WBenchPort)
	{
		struct Message *Massage;

		while(Massage = GetMsg(WBenchPort))
			ReplyMsg(Massage);

		DeleteMsgPort(WBenchPort);
	}

	if(WorkbenchBase)
		CloseLibrary(WorkbenchBase);

	if(Window)
		CloseWindow(Window);

	if(GadgetList)
		FreeGadgets(GadgetList);

	if(VisualInfo)
		FreeVisualInfo(VisualInfo);

	if(DefaultScreen)
		UnlockPubScreen(NULL,DefaultScreen);

	if(DevList)
		FreeDevices(DevList);

	if(IconBase)
		CloseLibrary(IconBase);

	if(GadToolsBase)
		CloseLibrary(GadToolsBase);

	if(GfxBase)
		CloseLibrary(GfxBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	exit(ReturnCode);
}

	/* OpenAll():
	 *
	 *	Obtain all resourced required to run this program.
	 */

VOID
OpenAll()
{
	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)))
		CloseAll(RETURN_FAIL + 1);

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37)))
		CloseAll(RETURN_FAIL + 2);

	if(!(GadToolsBase = (struct Library *)OpenLibrary("gadtools.library",37)))
		CloseAll(RETURN_FAIL + 3);

	IconBase = OpenLibrary("icon.library",37);

	if(!(Topaz = (struct TextFont *)OpenFont(&DefaultFont)))
		CloseAll(RETURN_FAIL + 4);

	if(!(DefaultScreen = (struct Screen *)LockPubScreen(NULL)))
		CloseAll(RETURN_FAIL + 5);

	if(!(VisualInfo = GetVisualInfo(DefaultScreen,TAG_DONE)))
		CloseAll(RETURN_FAIL + 6);

	if(!CreateAllGadgets(&GadgetArray[0],&GadgetList,VisualInfo,DefaultScreen -> WBorTop + DefaultScreen -> Font -> ta_YSize + 1))
		CloseAll(RETURN_FAIL + 7);

	if(!(Window = OpenWindowTags(NULL,
		WA_Width,	WIDTH,
		WA_Height,	HEIGHT + DefaultScreen -> Font -> ta_YSize - 8,

		WA_Activate,	TRUE,
		WA_DragBar,	TRUE,
		WA_DepthGadget,	TRUE,
		WA_CloseGadget,	TRUE,
		WA_RMBTrap,	TRUE,

		WA_IDCMP,	IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_DISKINSERTED | CHECKBOXIDCMP | BUTTONIDCMP | LISTVIEWIDCMP,

		WA_Title,	"Format v1.1 © Copyright 1991 by MXM",
	TAG_DONE)))
		CloseAll(RETURN_FAIL + 8);

	if(WorkbenchBase = OpenLibrary("workbench.library",37))
	{
		if(!(WBenchPort = CreateMsgPort()))
			CloseAll(RETURN_FAIL + 9);

		WBenchWindow = AddAppWindow(0,0,Window,WBenchPort,NULL);
	}

	SetFont(Window -> RPort,Topaz);

	AddGList(Window,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
	RefreshGList(GadgetList,Window,NULL,(UWORD)-1);
	GT_RefreshWindow(Window,NULL);

	UpdateInfo();
}

	/* LockWindow(struct Window *Window):
	 *
	 *	Disable all gadgets in the Format window and set the
	 *	stopwatch mouse pointer.
	 */

VOID
LockWindow(struct Window *Window)
{
	SetWait(Window);

	GT_SetGadgetAttrs(GadgetArray[GAD_NAME],Window,NULL,
		GA_Disabled,	TRUE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_WHOLEDISK],Window,NULL,
		GA_Disabled,	TRUE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_USEFFS],Window,NULL,
		GA_Disabled,	TRUE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_START],Window,NULL,
		GA_Disabled,	TRUE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_STOP],Window,NULL,
		GA_Disabled,	FALSE,
	TAG_DONE);
}

	/* UnLockWindow(struct Window *Window):
	 *
	 *	Re-enable the gadgets in the Format window and clear
	 *	the mouse pointer.
	 */

VOID
UnLockWindow(struct Window *Window)
{
	ClearPointer(Window);

	GT_SetGadgetAttrs(GadgetArray[GAD_NAME],Window,NULL,
		GA_Disabled,	FALSE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_WHOLEDISK],Window,NULL,
		GA_Disabled,	FALSE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_USEFFS],Window,NULL,
		GA_Disabled,	FALSE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_STOP],Window,NULL,
		GA_Disabled,	TRUE,
	TAG_DONE);

	GT_SetGadgetAttrs(GadgetArray[GAD_START],Window,NULL,
		GA_Disabled,	FALSE,
	TAG_DONE);
}

	/* GetUsedBlocks(struct DosEnvec *DosEnvec,BYTE UseFFS):
	 *
	 *	Calculate the number of blocks which will be
	 *	occupied if a filing system is formatted.
	 */

ULONG
GetUsedBlocks(struct DosEnvec *DosEnvec,BYTE UseFFS)
{
	ULONG Blocks,BitmapBlocks,ExtensionBlocks = 0;

		/* Number of blocks available for the
		 * filing system.
		 */

	Blocks = (DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1) * DosEnvec -> de_BlocksPerTrack * DosEnvec -> de_Surfaces;

		/* Calculate the number of bitmap blocks. */

	BitmapBlocks = (((Blocks + 31) / 32) + 126) / 127;

		/* The old filing system only allows 25 bitmap
		 * blocks.
		 */

	if(UseFFS && BitmapBlocks > 25)
		BitmapBlocks = 25;
	else
	{
			/* If there are more than 25 bitmap blocks,
			 * calculate the number of bitmap extension
			 * blocks required to chain them.
			 */

		if(BitmapBlocks > 25)
			ExtensionBlocks = (BitmapBlocks - 25 + 126) / 127;
	}

	return(DosEnvec -> de_Reserved + BitmapBlocks + ExtensionBlocks + 1);
}

	/* SizeInfo(struct DeviceNode *DevNode):
	 *
	 *	Give a brief size info on a given filing system.
	 */

VOID
SizeInfo(struct DeviceNode *DevNode)
{
	if(Formatting)
	{
		struct DevNode	*SomeNode;
		SHORT		 i = 0;

			/* This is a nuisance: you cannot disable
			 * gadtools listviews. As a fix we will
			 * simply ignore any clicks in this area
			 * and revert to the previously selected
			 * entry.
			 */

		SomeNode = (struct DevNode *)DevList -> lh_Head;

		while(SomeNode -> VanillaNode . ln_Succ)
		{
			if(SomeNode -> DevNode != LastNode)
			{
				i++;

				SomeNode = (struct DevNode *)SomeNode -> VanillaNode . ln_Succ;
			}
			else
				break;
		}

		GT_SetGadgetAttrs(GadgetArray[GAD_DRIVES],Window,NULL,
			GTLV_Selected,	i,
		TAG_DONE);
	}
	else
	{
		struct DosEnvec	*DosEnvec;
		ULONG		 Blocks;
		UBYTE		 Buffer[60];

		UpdateInfo();

			/* Neat casting, innit? */

		DosEnvec = (struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(DevNode -> dn_Startup)) -> fssm_Environ);

			/* Determine the number of blocks which are actually
			 * not in use.
			 */

		Blocks = (DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1) * DosEnvec -> de_Surfaces * DosEnvec -> de_SectorPerBlock * DosEnvec -> de_BlocksPerTrack - GetUsedBlocks(DosEnvec,UseFFS);

			/* Multiply by the verbatim size of a DOS block. */

		if(UseFFS)
			Blocks = Blocks * (DosEnvec -> de_SizeBlock << 2) / 1000;
		else
			Blocks = Blocks * 488 / 1000;

		SPrintf(Buffer,"%5ld Tracks %4ld.%03ld MBytes",DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1,Blocks / 1000,Blocks % 1000);

		GT_SetGadgetAttrs(GadgetArray[GAD_SIZE],Window,NULL,
			GTTX_Text,	Buffer,
		TAG_DONE);

		LastNode = DevNode;
	}
}

	/* ShowInfo(UBYTE *Format,...):
	 *
	 *	Show a text in the status field of the Format window.
	 */

VOID
ShowInfo(UBYTE *Format,...)
{
	UBYTE	Buffer[60];
	va_list	VarArgs;

	va_start(VarArgs,Format);
	VSPrintf(Buffer,Format,VarArgs);
	va_end(VarArgs);

	if(FromShell)
		Printf("\33[A%s\33[K\n",Buffer);
	else
	{
		GT_SetGadgetAttrs(GadgetArray[GAD_STATUS],Window,NULL,
			GTTX_Text,	Buffer,
		TAG_DONE);
	}
}

	/* MyEasyRequest():
	 *
	 *	Varargs version of the EasyRequest call.
	 */

SHORT __stdargs
MyEasyRequest(struct Window *Window,UBYTE *Text,UBYTE *Gadgets,...)
{
	struct EasyStruct __aligned	Easy;
	SHORT				Result;
	ULONG				IDCMP = NULL;
	va_list	 			VarArgs;

	Easy . es_StructSize	= sizeof(struct EasyStruct);
	Easy . es_Flags		= NULL;
	Easy . es_Title		= (UBYTE *)"Format Request";
	Easy . es_TextFormat	= (UBYTE *)Text;
	Easy . es_GadgetFormat	= (UBYTE *)Gadgets;

	va_start(VarArgs,Gadgets);
	Result = EasyRequestArgs(Window,&Easy,&IDCMP,VarArgs);
	va_end(VarArgs);

	return(Result);
}

	/* HandleInput():
	 *
	 *	Tiny subroutine which handles all the input coming
	 *	while we are formatting.
	 */

BYTE
HandleInput()
{
	BYTE Finished = FALSE;

	if(FromShell)
	{
		if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
		{
			SetSignal(0,SIGBREAKF_CTRL_C);

			Finished = TRUE;

			Printf("\n***BREAK: Format\n\n");
		}
	}
	else
	{
		if(WBenchPort)
		{
			if(SetSignal(0,0) & (1 << WBenchPort -> mp_SigBit))
			{
				struct Message *Massage;

				SetSignal(0,(1 << WBenchPort -> mp_SigBit));

				while(Massage = GetMsg(WBenchPort))
					ReplyMsg(Massage);
			}
		}

		if(SetSignal(0,0) & (1 << Window -> UserPort -> mp_SigBit))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;

			SetSignal(0,(1 << Window -> UserPort -> mp_SigBit));

			while(Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
			{
				Class	= Massage -> Class;
				Code	= Massage -> Code;
				Gadget	= (struct Gadget *)Massage -> IAddress;

				GT_ReplyIMsg(Massage);

				if(Class == IDCMP_GADGETUP)
				{
					UpdateInfo();

					if(Gadget -> GadgetID == GAD_STOP)
						Finished = TRUE;

					if(Gadget -> GadgetID == GAD_DRIVES)
						SizeInfo(NULL);
				}

				if(Class == IDCMP_VANILLAKEY)
				{
					if(toupper(Code) == 'S')
						Finished = TRUE;
					else
					{
						SHORT i;

						for(i = 0 ; i < (sizeof(Shortcuts) / sizeof(struct Shortcut)) ; i++)
						{
							if(Shortcuts[i] . Key == toupper(Code))
							{
								if(Shortcuts[i] . GadgetType == CHECKBOX_KIND)
								{
									if(!(GadgetArray[Shortcuts[i] . GadgetID] -> Flags & GFLG_DISABLED))
									{
										GT_SetGadgetAttrs(GadgetArray[Shortcuts[i] . GadgetID],Window,NULL,
											GTCB_Checked,	(GadgetArray[Shortcuts[i] . GadgetID] -> Flags & GFLG_SELECTED) ? FALSE : TRUE,
										TAG_DONE);

										UpdateInfo();
									}
								}
							}
						}
					}
				}
			}
		}
	}

	return(Finished);
}

	/* Initialize():
	 *
	 *	Initialize a filing system, check if there is a disk in the
	 *	drive and if it's write enabled. Format the whole disk if
	 *	necessary.
	 */

BYTE
Initialize(struct DevNode *DriveNode,struct IOExtTD *DevRequest)
{
		/* Is there a disk in the drive? */

	FOREVER
	{
		DevRequest -> iotd_Req . io_Command = TD_CHANGESTATE;

		if(!DoIO(DevRequest))
		{
			if(DevRequest -> iotd_Req . io_Actual)
			{
				if(FromShell)
				{
					Printf("No disk present in drive.\n",DriveNode -> VanillaNode . ln_Name);

					return(FALSE);
				}
				else
				{
					if(!MyEasyRequest(Window,"No disk present in drive\n%s","Retry|Cancel",DriveNode -> VanillaNode . ln_Name))
						return(FALSE);
				}
			}
			else
				break;
		}
		else
			break;
	}

		/* Is the disk write enabled? */

	FOREVER
	{
		DevRequest -> iotd_Req . io_Command = TD_PROTSTATUS;

		if(!DoIO(DevRequest))
		{
			if(DevRequest -> iotd_Req . io_Actual)
			{
				if(FromShell)
				{
					Printf("Disk in drive %s is write protected.\n",DriveNode -> VanillaNode . ln_Name);

					return(FALSE);
				}
				else
				{
					if(!MyEasyRequest(Window,"Disk in drive\n%s\nis write protected.","Retry|Cancel",DriveNode -> VanillaNode . ln_Name))
						return(FALSE);
				}
			}
			else
				break;
		}
		else
			break;
	}

		/* Are we to format the whole disk? */

	if(WholeDisk)
	{
		ULONG		*Track1,*Track2,*Src,*Dst;
		ULONG		 SizeTrack,i,j;
		struct DosEnvec	*DosEnvec;
		ULONG		 LowTrack,NumTracks;
		BYTE		 Result = TRUE,Match;

		DosEnvec = (struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(DriveNode -> DevNode -> dn_Startup)) -> fssm_Environ);

			/* Determine the size of a disk track. */

		SizeTrack = (DosEnvec -> de_SizeBlock << 2) * DosEnvec -> de_Surfaces * DosEnvec -> de_BlocksPerTrack;

			/* Allocate a write track... */

		if(Track1 = (ULONG *)AllocVec(SizeTrack,DosEnvec -> de_BufMemType|MEMF_CLEAR))
		{
				/* ...and a verification track. */

			if(Track2 = (ULONG *)AllocVec(SizeTrack,DosEnvec -> de_BufMemType))
			{
					/* Look up the first track and the number of
					 * tracks to format.
					 */

				LowTrack	= DosEnvec -> de_LowCyl * SizeTrack;
				NumTracks	= DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1;

					/* Run down the number of tracks to format. */

				for(i = 0 ; i < NumTracks ; i++)
				{
					if(HandleInput())
					{
						Result = FALSE;

						break;
					}

Retry:					ShowInfo("Formatting %ld, %ld To Go",i,NumTracks - i - 1);

						/* Format a track. */

					DevRequest -> iotd_Req . io_Command	= TD_FORMAT;
					DevRequest -> iotd_Req . io_Data	= Track1;
					DevRequest -> iotd_Req . io_Offset	= LowTrack;
					DevRequest -> iotd_Req . io_Length	= SizeTrack;

					if(DoIO(DevRequest))
					{
						if(FromShell)
						{
							Printf("Error %ld occured while formatting track %ld.\n",DevRequest -> iotd_Req . io_Error,i);

							Result = FALSE;

							break;
						}
						else
						{
							if(MyEasyRequest(Window,"Error %ld occured while formatting track %ld.","Rewrite|Abort",DevRequest -> iotd_Req . io_Error,i))
								goto Retry;
							else
							{
								Result = FALSE;

								break;
							}
						}
					}

						/* Check for abort. */

					if(HandleInput())
					{
						Result = FALSE;

						break;
					}

						/* If verification is enabled,
						 * reread the track.
						 */

					if(Verify)
					{
						DevRequest -> iotd_Req . io_Command = CMD_UPDATE;

						if(DoIO(DevRequest))
						{
							if(FromShell)
							{
								Printf("Error %ld occured while formatting track %ld.\n",DevRequest -> iotd_Req . io_Error,i);

								Result = FALSE;

								break;
							}
							else
							{
								if(MyEasyRequest(Window,"Error %ld occured while formatting track %ld.","Rewrite|Abort",DevRequest -> iotd_Req . io_Error,i))
									goto Retry;
								else
								{
									Result = FALSE;

									break;
								}
							}
						}

Retry2:						ShowInfo("Verifying  %ld, %ld To Go",i,NumTracks - i - 1);

						DevRequest -> iotd_Req . io_Command	= CMD_READ;
						DevRequest -> iotd_Req . io_Data	= Track2;
						DevRequest -> iotd_Req . io_Offset	= LowTrack;
						DevRequest -> iotd_Req . io_Length	= SizeTrack;

						if(DoIO(DevRequest))
						{
							if(FromShell)
							{
								Printf("Error %ld occured while verifying track %ld.\n",DevRequest -> iotd_Req . io_Error,i);

								Result = FALSE;

								break;
							}
							else
							{
								if(MyEasyRequest(Window,"Error %ld occured while verifying track %ld.","Reread|Abort",DevRequest -> iotd_Req . io_Error,i))
									goto Retry2;
								else
								{
									Result = FALSE;

									break;
								}
							}
						}

						Match = TRUE;

						j = SizeTrack >> 2;

						Src = Track1;
						Dst = Track2;

							/* Compare both
							 * tracks. If the
							 * data compared in
							 * this loop was
							 * residing in
							 * chip ram, we
							 * could use the
							 * blitter to do
							 * the job.
							 */

						while(j--)
						{
							if(*Src++ != *Dst++)
							{
								Match = FALSE;
								break;
							}
						}

							/* Are both tracks equal? */

						if(!Match)
						{
							if(FromShell)
							{
								Printf("Verification error on track %ld.\n",i);

								Result = FALSE;

								break;
							}
							else
							{
								if(MyEasyRequest(Window,"Verification error on track %ld.","Rewrite|Abort",i))
									goto Retry2;
								else
								{
									Result = FALSE;

									break;
								}
							}
						}
					}

					LowTrack += SizeTrack;
				}

				FreeVec(Track2);
			}

			FreeVec(Track1);
		}

		return(Result);
	}
	else
		return(TRUE);
}

	/* Install():
	 *
	 *	Handle the rest of the initialization, write the root block,
	 *	the bitmap blocks and install the disk if necessary.
	 */

BYTE
Install(struct DevNode *DriveNode,struct IOExtTD *DevRequest)
{
	struct DosEnvec	*DosEnvec;
	ULONG		*Block;
	UBYTE		*Name;

	ShowInfo("Preparing Disk");

	DosEnvec = (struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(DriveNode -> DevNode -> dn_Startup)) -> fssm_Environ);

	if(HandleInput())
		return(FALSE);

	if(DiskName)
		Name = DiskName;
	else
		Name = ((struct StringInfo *)GadgetArray[GAD_NAME] -> SpecialInfo) -> Buffer;

	if(HandleInput())
		return(FALSE);

		/* Tell the handler to initialize the underlying data media. */

	if(!Format(DriveNode -> VanillaNode . ln_Name,Name[0] ? Name : "Empty",UseFFS ? ID_FFS_DISK : ID_DOS_DISK))
	{
		if(FromShell)
			Printf("Failed to format drive %s.\n",DriveNode -> VanillaNode . ln_Name);
		else
			MyEasyRequest(Window,"Failed to format drive\n%s","Proceed",DriveNode -> VanillaNode . ln_Name);

		return(FALSE);
	}

		/* Are we to install the disk and is there enough space left
		 * to install it?
		 */

	if(DosEnvec -> de_Reserved > 0 && InstallDisk)
	{
			/* Allocate a bootblock-sized chunk. */

		if(Block = (ULONG *)AllocVec((DosEnvec -> de_SizeBlock << 2) * DosEnvec -> de_Reserved,DosEnvec -> de_BufMemType|MEMF_CLEAR))
		{
			ShowInfo("Installing Disk");

				/* Install the approriate bootblock type. */

			if(UseFFS)
				CopyMem(Bootblock_FFS,Block,sizeof(Bootblock_FFS));
			else
				CopyMem(Bootblock_OFS,Block,sizeof(Bootblock_OFS));

			DevRequest -> iotd_Req . io_Command	= CMD_WRITE;
			DevRequest -> iotd_Req . io_Data	= Block;
			DevRequest -> iotd_Req . io_Length	= (DosEnvec -> de_SizeBlock << 2) * DosEnvec -> de_Reserved;
			DevRequest -> iotd_Req . io_Offset	= DosEnvec -> de_LowCyl * DosEnvec -> de_Surfaces * (DosEnvec -> de_SizeBlock << 2);

			if(DoIO(DevRequest))
			{
				if(FromShell)
					Printf("Error %ld occured while writing the boot block.\n",DevRequest -> iotd_Req . io_Error);
				else
					MyEasyRequest(Window,"Error %ld occured while writing the boot block.","Proceed",DevRequest -> iotd_Req . io_Error);

				FreeVec(Block);

				return(FALSE);
			}

			FreeVec(Block);
		}
	}

	if(EjectDisk)
	{
		DevRequest -> iotd_Req . io_Command = TD_EJECT;

		DoIO(DevRequest);
	}

	return(TRUE);
}

	/* FormatDrive(struct DevNode *DriveNode,BYTE Query):
	 *
	 *	Format a filing system, handle the user-interface part.
	 */

VOID
FormatDrive(struct DevNode *DriveNode,BYTE Query)
{
	struct DosEnvec	*DosEnvec;
	ULONG		 Blocks;

	DosEnvec = (struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(DriveNode -> DevNode -> dn_Startup)) -> fssm_Environ);

	Blocks = (DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1) * DosEnvec -> de_Surfaces * DosEnvec -> de_BlocksPerTrack;

	Formatting = TRUE;

	if(!FromShell)
		LockWindow(Window);

	UpdateInfo();

		/* Is the user trying to format filing system larger than
		 * 52 MBytes using the old filing system?
		 */

	if(Blocks > 101600 && !UseFFS)
	{
		if(FromShell)
			Printf("You cannot format partitions larger than approximately 52 MBytes\nusing the old filing system. Use Fast File System instead.\n");
		else
			MyEasyRequest(Window,"You cannot format partitions larger than\napproximately  52  MBytes  using the old\nfiling  system.   Use  Fast  File System\ninstead.","Proceed");
	}
	else
	{
		SHORT Result;

			/* Give the user a last chance to abort the
			 * formatting.
			 */

		if(Query)
		{
				/* Display a special text if the
				 * data media to be formatted is not
				 * a standard 3½" disk.
				 */

			if(Blocks > 1760)
			{
				DisplayBeep(Window -> WScreen);

				Result = MyEasyRequest(Window,"                Caution!\n\n\
Drive  %s   is  not  a  standard-sized\n\
floppy disk drive but rather a hard-disk\n\
or high-density-floppy-disk drive.\n\n\
       OK to format disk in drive\n\
       %s\n\
       (all data will be erased)?","OK|OK-Quick|Cancel",DriveNode -> VanillaNode . ln_Name,DriveNode -> VanillaNode . ln_Name);
			}
			else
				Result = MyEasyRequest(Window,"OK to format disk in drive\n%s\n(all data will be erased)?","OK|OK-Quick|Cancel",DriveNode -> VanillaNode . ln_Name);

			if(Result == 2)
			{
				GT_SetGadgetAttrs(GadgetArray[GAD_WHOLEDISK],Window,NULL,
					GTCB_Checked,	FALSE,
				TAG_DONE);

				WholeDisk = FALSE;
			}
		}
		else
			Result = 1;

		UpdateInfo();

			/* Start to format the disk. */

		if(Result)
		{
			struct MsgPort *Proc;

			ShowInfo("Preparing to format drive %s",DriveNode -> VanillaNode . ln_Name);

				/* Tell the filing system to sleep. */

			if(Proc = DeviceProc(DriveNode -> VanillaNode . ln_Name))
			{
				if(DoPkt1(Proc,ACTION_INHIBIT,DOSTRUE))
				{
					struct IOExtTD	*DevRequest;
					BYTE		 Success = FALSE;

					if(DevRequest = OpenDrive(DriveNode -> DevNode))
					{
						ShowInfo("Initializing Disk");

						if(Initialize(DriveNode,DevRequest))
							Success = Install(DriveNode,DevRequest);

						CloseDrive(DevRequest);
					}
					else
					{
						if(FromShell)
							Printf("Failed to open unit driver for drive %s.",DriveNode -> VanillaNode . ln_Name);
						else
							MyEasyRequest(Window,"Failed to open unit driver for drive %s.","Proceed",DriveNode -> VanillaNode . ln_Name);
					}

						/* Reenable the filing system. */

					DoPkt1(Proc,ACTION_INHIBIT,DOSFALSE);

						/* Are we to create icons? */

					if(Success && CreateIcons && IconBase)
					{
						struct DiskObject	*Object;
						UBYTE			 Buffer[60];

						ShowInfo("Creating Icons");

							/* Create a disk icon. */

						if(Object = GetDefDiskObject(WBDISK))
						{
							SPrintf(Buffer,"%sDisk",DriveNode -> VanillaNode . ln_Name);

							Success = PutDiskObject(Buffer,Object);

							FreeDiskObject(Object);
						}

							/* Create a trashcan icon. */

						if(Success)
						{
							if(Object = GetDefDiskObject(WBGARBAGE))
							{
								SPrintf(Buffer,"%sTrashcan",DriveNode -> VanillaNode . ln_Name);

								if(PutDiskObject(Buffer,Object))
									UnLock(CreateDir(Buffer));

								FreeDiskObject(Object);
							}
						}
					}

					if(!FromShell)
						ShowInfo("Done!");
				}
			}
		}
	}

	if(!FromShell)
	{
		ShowInfo("Idle");

		UnLockWindow(Window);
	}

	Formatting = FALSE;
}

	/* FormatFromWB(struct WBArg *Arg):
	 *
	 *	Grab a Workbench argument pointer and try to format
	 *	the associated filing system.
	 */

VOID
FormatFromWB(struct WBArg *Arg)
{
	struct DevNode	*SomeNode;
	SHORT		 i = 0;
	UBYTE		 DriverName[256];

		/* If it has got a lock take the name of the
		 * associated filing system, else take a look
		 * at the name passed to us.
		 */

	if(Arg -> wa_Lock)
	{
		struct FileLock *FileLock = BADDR(Arg -> wa_Lock);

		strcpy(DriverName,((struct Task *)FileLock -> fl_Task -> mp_SigTask) -> tc_Node . ln_Name);
		strcat(DriverName,":");
	}
	else
		strcpy(DriverName,Arg -> wa_Name);

	SomeNode = (struct DevNode *)DevList -> lh_Head;

		/* Look for the node corresponding to the name. */

	while(SomeNode -> VanillaNode . ln_Succ)
	{
		if(stricmp(DriverName,SomeNode -> VanillaNode . ln_Name))
		{
			i++;
			SomeNode = (struct DevNode *)SomeNode -> VanillaNode . ln_Succ;
		}
		else
			break;
	}

		/* Did we find a device of that name? */

	if(SomeNode -> VanillaNode . ln_Succ)
	{
		GT_SetGadgetAttrs(GadgetArray[GAD_DRIVES],Window,NULL,
			GTLV_Selected,	i,
		TAG_DONE);

		GT_SetGadgetAttrs(GadgetArray[GAD_USEFFS],Window,NULL,
			GTCB_Checked,	((struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(SomeNode -> DevNode -> dn_Startup)) -> fssm_Environ)) -> de_DosType == ID_FFS_DISK ? TRUE : FALSE,
		TAG_DONE);

		SizeInfo(SomeNode -> DevNode);

		FormatDrive(SomeNode,TRUE);
	}
	else
		MyEasyRequest(Window,"Drive %s is not a block-mapped\ndisk filing system.","Proceed",DriverName);
}

	/* main(int argc,char **argv):
	 *
	 *	The main routine (what else?).
	 */

VOID
main(int argc,char **argv)
{
	struct IntuiMessage	*Massage;
	ULONG			 Class,Code;
	struct Gadget		*Gadget;
	BYTE			 Terminated = FALSE;
	SHORT			 i;
	struct DevNode		*DriveNode;
	extern struct ExecBase	*SysBase;

		/* Check to see if it's below our preferred
		 * revision number.
		 */

	if(SysBase -> LibNode . lib_Version < 37)
		exit(RETURN_FAIL);

		/* We were run from Workbench, have a look at the
		 * startup packet being passed.
		 */

	if(!argc)
	{
		extern struct WBStartup *WBenchMsg;

			/* We are asked to format data media. */

		if(WBenchMsg -> sm_NumArgs > 1)
		{
			OpenAll();

				/* Run down the list of
				 * arguments.
				 */

			for(i = 1 ; i < WBenchMsg -> sm_NumArgs ; i++)
				FormatFromWB(&WBenchMsg -> sm_ArgList[i]);

			CloseAll(RETURN_OK);
		}
	}

		/* If the argument count is below two, then we're
		 * probably to display the shiny gadtools panel.
		 */

	if(argc < 2)
	{
		ULONG SignalSet;

			/* Open the libs and create the panel. */

		OpenAll();

			/* Pick up the first filing system in the list. */

		DriveNode = (struct DevNode *)DevList -> lh_Head;

			/* Toggle the `Use FFS' button if necessary. */

		GT_SetGadgetAttrs(GadgetArray[GAD_USEFFS],Window,NULL,
			GTCB_Checked,	((struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(DriveNode -> DevNode -> dn_Startup)) -> fssm_Environ)) -> de_DosType == ID_FFS_DISK ? TRUE : FALSE,
		TAG_DONE);

			/* Display the size of the data media. */

		SizeInfo(DriveNode -> DevNode);

			/* Go into loop waiting for input... */

		while(!Terminated)
		{
				/* Piece the signal mask to wait
				 * for together.
				 */

			SignalSet = (1 << Window -> UserPort -> mp_SigBit);

				/* Wait for any messages sent by Workbench. */

			if(WBenchPort)
				SignalSet |= (1 << WBenchPort -> mp_SigBit);

			SignalSet = Wait(SignalSet);

				/* Have a look at the Workbench port. */

			if(WBenchPort)
			{
				struct AppMessage *AppMassage;

					/* Remove the application window
					 * messages.
					 */

				while(AppMassage = (struct AppMessage *)GetMsg(WBenchPort))
				{
						/* Format the associated media. */

					for(i = 0 ; i < AppMassage -> am_NumArgs ; i++)
						FormatFromWB(&AppMassage -> am_ArgList[i]);

					ReplyMsg(&AppMassage -> am_Message);
				}
			}

				/* Remove all the messages pending
				 * at the panel window.
				 */

			while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort)))
			{
				Class	= Massage -> Class;
				Code	= Massage -> Code;
				Gadget	= (struct Gadget *)Massage -> IAddress;

				GT_ReplyIMsg(Massage);

					/* Terminate the program? */

				if(Class == IDCMP_CLOSEWINDOW)
					Terminated = TRUE;

					/* A disk has been inserted and
					 * we're probably autostart-enabled.
					 */

				if(Class == IDCMP_DISKINSERTED)
				{
					struct DevNode *Node;

						/* To insure that all the
						 * disk change counts are
						 * kept even, even if auto-
						 * start is not enabled we
						 * will call the checkup
						 * routine and abort if
						 * autostart is not enabled.
						 * If were not playing the
						 * game this way, we could
						 * accidentally assume that
						 * a disk changed long ago
						 * before autostart was
						 * enabled was a candidat
						 * for formatting...
						 */

					if((Node = GetChangedDrive()) && AutoStart)
					{
						struct DevNode *SomeNode;

							/* Try to find the
							 * index of the
							 * DevNode whose
							 * device we are to
							 * initialize (to
							 * keep the listview
							 * happy).
							 */

						i = 0;

						SomeNode = (struct DevNode *)DevList -> lh_Head;

						while(SomeNode -> VanillaNode . ln_Succ)
						{
							if(SomeNode != Node)
							{
								i++;
								SomeNode = (struct DevNode *)SomeNode -> VanillaNode . ln_Succ;
							}
							else
								break;
						}

						DriveNode = SomeNode;

							/* Display the
							 * size of the
							 * data media.
							 */

						SizeInfo(SomeNode -> DevNode);

							/* Display the
							 * approriate listview
							 * entry.
							 */

						GT_SetGadgetAttrs(GadgetArray[GAD_DRIVES],Window,NULL,
							GTLV_Selected,	i,
						TAG_DONE);

							/* Bring the screen
							 * to the front...
							 */

						MoveScreen(Window -> WScreen,-Window -> WScreen -> LeftEdge,-Window -> WScreen -> TopEdge);
						ScreenToFront(Window -> WScreen);

							/* Flash the display. */

						DisplayBeep(Window -> WScreen);

							/* Bring the window to
							 * the front.
							 */

						WindowToFront(Window);

						LockWindow(Window);

							/* Wait two seconds
							 * before actually
							 * starting the
							 * formatting process.
							 * This little break
							 * will (hopefully!)
							 * allow the user to
							 * abort the
							 * initialization of
							 * precious data media.
							 */

						for(i = 0 ; i < 2 ; i++)
						{
							ShowInfo("%ld seconds to format %s",2 - i,DriveNode -> VanillaNode . ln_Name);

							if(HandleInput())
							{
								Node = NULL;

								break;
							}

							Delay(TICKS_PER_SECOND);
						}

							/* Format the drive
							 * or don't...
							 */

						if(Node)
							FormatDrive(DriveNode,FALSE);
						else
						{
							UnLockWindow(Window);

							ShowInfo("Idle");
						}
					}
				}

					/* The user hit one of the gadgets
					 * displayed in the panel.
					 */

				if(Class == IDCMP_GADGETUP)
				{
					UpdateInfo();

						/* Start formatting? */

					if(Gadget -> GadgetID == GAD_START)
						FormatDrive(DriveNode,TRUE);

						/* Select another disk
						 * drive?
						 */

					if(Gadget -> GadgetID == GAD_DRIVES)
					{
						struct DevNode *SomeNode;

						i = 0;

						SomeNode = (struct DevNode *)DevList -> lh_Head;

						while(SomeNode -> VanillaNode . ln_Succ)
						{
							if(i++ != Code)
								SomeNode = (struct DevNode *)SomeNode -> VanillaNode . ln_Succ;
							else
								break;
						}

						DriveNode = SomeNode;

						GT_SetGadgetAttrs(GadgetArray[GAD_USEFFS],Window,NULL,
							GTCB_Checked,	((struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(SomeNode -> DevNode -> dn_Startup)) -> fssm_Environ)) -> de_DosType == ID_FFS_DISK ? TRUE : FALSE,
						TAG_DONE);

						SizeInfo(SomeNode -> DevNode);
					}

						/* Toggle the effects
						 * of the FFS gadget.
						 */

					if(Gadget -> GadgetID == GAD_USEFFS)
						SizeInfo(LastNode);
				}

					/* The following block handles
					 * the keyboard shortcuts available.
					 * Most important among these
					 * are `S' (= Start/Stop formatting)
					 * and Escape (= terminate the
					 * program.
					 */

				if(Class == IDCMP_VANILLAKEY)
				{
					if(toupper(Code) == 'S')
						FormatDrive(DriveNode,TRUE);
					else
					{
						if(Code == '\033')
							Terminated = TRUE;
						else
						{
							for(i = 0 ; i < (sizeof(Shortcuts) / sizeof(struct Shortcut)) ; i++)
							{
								if(Shortcuts[i] . Key == toupper(Code))
								{
									switch(Shortcuts[i] . GadgetType)
									{
										case STRING_KIND:	ActivateGadget(GadgetArray[Shortcuts[i] . GadgetID],Window,NULL);
													break;

										case CHECKBOX_KIND:	if(!(GadgetArray[Shortcuts[i] . GadgetID] -> Flags & GFLG_DISABLED))
													{
														GT_SetGadgetAttrs(GadgetArray[Shortcuts[i] . GadgetID],Window,NULL,
															GTCB_Checked,	(GadgetArray[Shortcuts[i] . GadgetID] -> Flags & GFLG_SELECTED) ? FALSE : TRUE,
														TAG_DONE);

														UpdateInfo();

														if(Shortcuts[i] . GadgetID == GAD_USEFFS)
															SizeInfo(LastNode);
													}

													break;

										default:		break;
									}

									break;
								}
							}
						}
					}
				}
			}
		}
	}
	else
	{
		UBYTE **Arg;

			/* Let's come to the Shell interface,
			 * start by allocating the argument vector
			 * table to pass to ReadArgs.
			 */

		if(Arg = (UBYTE **)AllocVec(sizeof(UBYTE *) * NUM_ARGS,MEMF_PUBLIC | MEMF_CLEAR))
		{
			struct RDArgs *ArgsPtr;

				/* Perform command line parsing. */

			if(ArgsPtr = ReadArgs(ARG_TEMPLATE,(LONG *)Arg,NULL))
			{
				struct DevNode *SomeNode;

					/* Let it be known: no GUI this
					 * time!
					 */

				FromShell = TRUE;

					/* Set up the list of legal
					 * filing devices.
					 */

				if(DevList = FindDevices())
				{
						/* Try to find the device
						 * we are to format.
						 */

					SomeNode = (struct DevNode *)DevList -> lh_Head;

					while(SomeNode -> VanillaNode . ln_Succ)
					{
						if(!stricmp(SomeNode -> VanillaNode . ln_Name,Arg[ARG_DRIVE]))
							break;
						else
							SomeNode = (struct DevNode *)SomeNode -> VanillaNode . ln_Succ;
					}

						/* Did we find the device? */

					if(SomeNode -> VanillaNode . ln_Succ)
					{
						struct MsgPort	*Proc;
						UBYTE		 TinyBuffer[5];

							/* Turn up the driver behind the
							 * data media.
							 */

						if(Proc = DeviceProc(DriveNode -> VanillaNode . ln_Name))
						{
								/* Disable the restart validation process. */

							if(DoPkt1(Proc,ACTION_INHIBIT,DOSTRUE))
							{
									/* Prompt for <return> */

								Printf("Insert disk to be formatted in drive %s and press RETURN",SomeNode -> VanillaNode . ln_Name);
								Flush(((struct Process *)SysBase -> ThisTask) -> pr_CIS);

								FGets(((struct Process *)SysBase -> ThisTask) -> pr_COS,TinyBuffer,5);

									/* Abort the process? */

								if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
								{
									SetSignal(0,SIGBREAKF_CTRL_C);

									DoPkt1(Proc,ACTION_INHIBIT,DOSFALSE);

									Printf("\n*** BREAK: Format\a\n\n");

									FreeArgs(ArgsPtr);
									FreeVec(Arg);

									CloseAll(RETURN_WARN);
								}

									/* Make sure that we will get a
									 * valid disk name ("" expands
									 * to `Empty').
									 */

								if(Arg[ARG_NAME])
									DiskName = Arg[ARG_NAME];
								else
									DiskName = "";

									/* See if by default FFS is
									 * enabled.
									 */

								UseFFS = ((struct DosEnvec *)BADDR(((struct FileSysStartupMsg *)BADDR(SomeNode -> DevNode -> dn_Startup)) -> fssm_Environ)) -> de_DosType == ID_FFS_DISK ? TRUE : FALSE;

									/* Transform the remaining
									 * arguments into flags.
									 */

								if(Arg[ARG_FFS])
									UseFFS = TRUE;

								if(!Arg[ARG_NOICONS])
									CreateIcons = TRUE;

								if(!Arg[ARG_QUICK])
									WholeDisk = TRUE;

								if(!Arg[ARG_NOVERIFY])
									Verify = TRUE;

								if(Arg[ARG_INSTALL])
									InstallDisk = TRUE;

								if(Arg[ARG_EJECT])
									EjectDisk = TRUE;

									/* Turn off the console cursor. */

								Printf("\33[0 p\n");

									/* Format the drive. */

								FormatDrive(SomeNode,FALSE);

									/* Turn the console cursor back on. */

								Printf("\33[ p");

									/* Reenable the disk validation process. */

								DoPkt1(Proc,ACTION_INHIBIT,DOSFALSE);
							}
						}
					}
					else
					{
						Printf("Format: Cannot format drive %s.\a\n",SomeNode -> VanillaNode . ln_Name);

						FreeArgs(ArgsPtr);
						FreeVec(Arg);

						CloseAll(RETURN_FAIL);
					}
				}
				else
				{
					Printf("Format: Out of memory!\a\n");

					FreeArgs(ArgsPtr);
					FreeVec(Arg);

					CloseAll(RETURN_FAIL);
				}

				FreeArgs(ArgsPtr);
			}
			else
			{
				PrintFault(IoErr(),"Format\a");

				FreeVec(Arg);

				CloseAll(RETURN_FAIL);
			}

			FreeVec(Arg);
		}
		else
		{
			Printf("Format: Out of memory!\a\n");

			CloseAll(RETURN_FAIL);
		}
	}

	CloseAll(RETURN_OK);
}

	/* Et c'est tout! */
