
/**************************************************************************/
/*				  Format				  */
/*			       Version 1.00				  */
/*									  */
/* This is a replacement for the AmigaDOS "Format" command.  It sports    */
/* (for the Workbench user) more friendly user-interface, although it is  */
/* virtually identical to the standard Format command where the CLI	  */
/* interface is concerned.						  */
/*									  */
/* This program is Copyright 1992 by Dave Schreiber, All Rights Reserved. */
/* This program may not be sold for more than a small copying and shipping*/
/* and handling fee, except by written permission of Dave Schreiber.	  */
/*									  */
/* Version list:							  */
/*    1.00 - First release (August 31, 1992)                              */
/**************************************************************************/


/*System header files*/
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <dos/dos.h>
#include <dos/filehandler.h>
#include <workbench/startup.h>
#include <libraries/gadtools.h>
#include <workbench/icon.h>
#include <devices/trackdisk.h>
#include <dos/rdargs.h>

/*Prototypes for system functions*/
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/gadtools.h>
#include <proto/icon.h>
#include <proto/graphics.h>

/*Other headers*/
#include "Format.h"
#include "GUI.h"

/*These hold the user's selections*/
/*They are initialized in main()*/
BOOL FFS;
BOOL QuickFmt;
BOOL Verify;
BOOL Icon;

/*This holds the pointer to the Workbench startup message, if we're started*/
/*from Workbench*/
extern struct WBStartup *WBenchMsg;

/*Library base pointers*/
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *GadToolsBase,*IconBase;

/*This is initialized in GUI.c and holds the location and size*/
/*of the progress indicator box in the status window*/
extern Rect box;

BPTR StdErr=NULL;

/*For use with the CLI "Version" command*/
char *Version = "$VER: NewFormat V1.00 (31.8.92)";

/*The entry point for the program (_main to keep a CLI window from opening*/
/*when the program is run from Workbench)*/
_main()
{
   UWORD disk;
   char volumeName[256];
   prepResult stat;
   char statusString[80];

   /*Open the various shared libraries that are needed*/
   IntuitionBase=(struct Library *)OpenLibrary("intuition.library",37L);
   GfxBase=(struct Library *)OpenLibrary("graphics.library",37L);
   IconBase=(struct Library *)OpenLibrary("icon.library",37L);
   GadToolsBase=(struct Library *)OpenLibrary("gadtools.library",37L);

   /*Check to see that they all opened, and exit if any did not*/
   if(IntuitionBase==NULL || GfxBase==NULL || IconBase==NULL ||
	 GadToolsBase==NULL)
      cleanup(100);

   if(WBenchMsg!=NULL)
   {
      /*If we're running from Workbench, and the user did not select*/
      /*a drive, print an error and exit*/
      if(WBenchMsg->sm_NumArgs==1)
      {
	 printError("Please select a drive to format and try again.",NULL,NULL);
	 cleanup(100);
      }

      /*Get the visual info, etc.*/
      SetupScreen();

      /*For every selected disk...*/
      for(disk=1;disk<WBenchMsg->sm_NumArgs && stat!=eQuit;disk++)
      {
	 /*This will hold the disk's new name*/
	 char newName[36];

	 /*Initialize the format settings*/
	 FFS=FALSE;
	 QuickFmt=FALSE;
	 Verify=TRUE;
	 Icon=TRUE;

	 /*Get the volume/device name of the disk to format*/
	 getVolumeName(volumeName,WBenchMsg->sm_ArgList,disk);

	 /*If there is no lock to the volume, that means it is */
	 /*unformatted, so just use the name given to us by Workbench*/
	 if(WBenchMsg->sm_ArgList[disk].wa_Lock==NULL)
	    strcpy(volumeName,WBenchMsg->sm_ArgList[disk].wa_Name);

	 /*Open the options window*/
	 if((stat=OpenPrepWindow(volumeName))==0)
	 {
	    /*Get the user's input*/
	    stat=getPrepInput();

	    /*And close the window*/
	    ClosePrepWindow();

	    /*If the user selected 'OK'*/
	    if(stat==eOK)
	    {
	       /*Get the new name of the disk*/
	       strcpy(newName,((struct StringInfo *)
			 (PrepGadgets[GD_NameGadget]->SpecialInfo))->Buffer);

	       /*Ask the user for verification*/
	       if(askAreYouSure(volumeName,
		     (WBenchMsg->sm_ArgList[disk].wa_Lock!=NULL)))
	       {

		  /*If everything's OK, open the status window*/
		  if((stat=OpenStatusWindow(statusString))==0)
		  {
		     /*And format the disk*/
		     formatVolume(&(WBenchMsg->sm_ArgList[disk].wa_Lock),
				   volumeName,newName,FFS,QuickFmt,Verify,Icon,
				statusString);

		     /*We're done, so close the status window*/
		     CloseStatusWindow();
		  }
	       }
	    }
	 }
      }
      /*Free the visual info, etc.*/
      CloseDownScreen();
   }
   else     /*We've been run from the CLI*/
   {
      char driveName[64];
      BPTR driveLock;
      struct Process *process;
      APTR oldWdw;
      char temp[32];
      char temp2[4];
      DriveLayout junk;

      /*Open a 'stderr' I/O channel to the shell*/
      /*(the normal stderr was not opened since we used _main() ) */
      StdErr=Open("CONSOLE:",MODE_OLDFILE);

      /*Make sure requestors don't open*/
      process=(struct Process *)FindTask(0L);
      oldWdw=process->pr_WindowPtr;
      process->pr_WindowPtr=(APTR)(-1);

      /*Get the command-line arguments*/
      parseArgs(driveName,volumeName,&FFS,&Icon,&QuickFmt,&Verify);

      /*Get a lock on the selected drive*/
      /*(note:  NULL is a valid return value;  it means that the disk we*/
      /*want to format is itself unformatted)*/
      driveLock=Lock(driveName,ACCESS_READ);

      strcpy(temp,driveName);

      /*Get the volume/drive name*/
      if(volumeToDevName(driveLock,temp,&junk))
      {
	 /*Wait for the user to tell us to go ahead*/
	 Write(Output(),"Insert the disk to be formatted in drive ",41);
	 Write(Output(),temp,strlen(temp));
	 Write(Output()," and press RETURN ",18);
	 Read(Input(),temp2,1);

	 /*Format the disk*/
	 formatVolume(&driveLock,temp,volumeName,FFS,QuickFmt,Verify,Icon,
			statusString);
      }
      else
	 Write(Output(),"Cannot find the specified drive!\n",33);

      Close(StdErr);

      /*Restore the old contents of this pointer*/
      process->pr_WindowPtr=(APTR)(-1);

      Write(Output(),"\n",1);
   }
   cleanup(0);
   return(0);
}

/*This functions handles the low-level format, the high-level format, the*/
/*creation of icons, etc.*/
void formatVolume(BPTR *volumeLock,char *volumeName,char *newName,BOOL ffs,
		  BOOL quick,BOOL verify,BOOL icon,char *statString)
{
   struct IOExtTD *io1;
   BOOL fmtResult=TRUE;
   int result;
   char deviceName[64];
   DriveLayout layout;
   struct MsgPort *devPort;
   BOOL writeProtCont=TRUE;

   /*If the volume lock is NULL, assume that the volumeName string holds*/
   /*a valid device pointer*/
   if(*volumeLock==NULL)
      strcpy(deviceName,volumeName);

   /*Get the drive name (if possible)*/
   if(!volumeToDevName(*volumeLock,deviceName,&layout))
   {
      printError("Can't find the drive!",NULL,NULL);
      return;
   }

   /*This port will be used to communicate with the filesystem*/
   devPort=DeviceProc(deviceName);
   if(devPort==NULL)
   {
      printError("Can't find the drive",NULL,NULL);
      return;
   }

   /*Inhibit the drive*/
   DoPkt(devPort,ACTION_INHIBIT,DOSTRUE,NULL,NULL,NULL);

   /*If we got a lock to the volume that we're going to format it, destroy*/
   /*it, since the volume that it points to is about to be erased anyway*/
   if(*volumeLock!=NULL)
   {
      UnLock(*volumeLock);
      *volumeLock=NULL;
   }

   /*Open the disk device*/
   if((io1=OpenDrive(layout.devName,layout.unit,layout.flags))!=NULL)
   {
      /*Determine the write protect status*/
      io1->iotd_Req.io_Data=NULL;
      io1->iotd_Req.io_Length=0;
      io1->iotd_Req.io_Command=TD_PROTSTATUS;
      DoIO((struct IOReq *)io1);

      /*Loop while the disk stays protected and user keeps pressing Retry*/
      while(io1->iotd_Req.io_Actual!=0 &&
		 (writeProtCont=alertIsWriteProtected(volumeName)))
	 DoIO((struct IOReq *)io1);

      /*If the disk is not write-protected*/
      if(writeProtCont)
      {
	 /*Do a full format if the user didn't select the 'Quick' option*/
	 if(!quick)
	    fmtResult=doFullFormat(&layout,statString,volumeName,io1);

	 /*If the format was successful*/
	 if(fmtResult)
	 {
	    /*Write an extra carriage return, if run from the CLI*/
	    if(WBenchMsg==NULL)
	       Write(Output(),"\n",1);

	    /*Tell the user that we're doing the high-level format*/
	    fmtResult=!updateStatWindow("Formatting disk...",1000);

	    /*If the user hasn't clicked on 'Stop', do the high-level format*/
	    if(fmtResult)
	       result=Format(deviceName,newName,
					   ffs ? ID_FFS_DISK : ID_DOS_DISK);
	 }
      }
      else
	 printError("Cannot format volume",volumeName,
			"because the disk is write protected");
      /*Close the disk device*/
      CloseDrive(io1);
   }
   else
   {
      printError("Couldn't access the device",NULL,NULL);
      return;
   }

   /*Uninhibit the drive*/
   DoPkt(devPort,ACTION_INHIBIT,DOSFALSE,NULL,NULL,NULL);

   /*Wait for the drive to come on line*/
   Delay(50);

   /*If the disk was never unprotected, return*/
   if(!writeProtCont)
      return;

   /*If the format was successful and the user wants icons created*/
   if(icon && result==DOSTRUE)
   {
      struct DiskObject *trashIcon;
      BPTR trashLock;

      /*Update the user*/
      fmtResult=!updateStatWindow("Creating Trashcan  ",1000);

      /*If she didn't press 'Stop'*/
      if(fmtResult)
      {
	 /*Create the trashcan name (<volume>:Trashcan)*/
	 strcpy(deviceName,newName);
	 strcat(deviceName,":");
	 strcat(deviceName,"Trashcan");

	 /*Create the trashcan directory*/
	 trashLock=CreateDir(deviceName);

	 /*If it was successfully created*/
	 if(trashLock!=NULL)
	 {
	    UnLock(trashLock);
	    /*Get the trashcan icon*/
	    trashIcon=GetDefDiskObject(WBGARBAGE);

	    /*If there wasn't an error*/
	    if(trashIcon!=NULL)
	    {
	       /*Write the icon to disk*/
	       if(!PutDiskObject(deviceName,trashIcon))
		  printError("There was an error while creating the trashcan",
				 NULL,NULL);

	       /*and free it*/
	       FreeDiskObject(trashIcon);
	    }
	    else
	       printError("There was an error while creating the trashcan",
			      NULL,NULL);
	 }
	 else
	    printError("There was an error while creating the trashcan directory",
			   NULL,NULL);
      }
   }
   return;
}

/*This function does a full (low-level) format of a disk*/
BOOL doFullFormat(DriveLayout *layout,char *statString,char *devName,
		     struct IOExtTD *io1)
{
   ULONG *write;
   ULONG trackSize,sectorPos,numTracks;
   LONG track;
   int c;
   UBYTE error=2;
   BOOL errorB=FALSE;

   /*Get the size of a track (#surfaces*#blocks*#longwordsPerTrack*4*/
   trackSize=layout->surfaces*layout->blockSize*4*layout->BPT;

   /*Allocate enough memory for one track*/
   write=AllocMem(trackSize,layout->memType|MEMF_CLEAR);
   if(write!=NULL)
   {
      /*Initialize the IORequest*/
      io1->iotd_Req.io_Data=write;
      io1->iotd_Req.io_Length=trackSize;

      /*Get the starting byte position in the volume*/
      sectorPos=layout->lowCyl*trackSize;

      /*Get the number of tracks to format*/
      numTracks=layout->highCyl-layout->lowCyl+1;

      /*Clear the status window*/
      error=(updateStatWindow(" ",0)) ? 0 : error;

      /*For each track*/
      for(track=0;track<numTracks && error!=0;track++)
      {
	 /*Setup to format the disk*/
	 io1->iotd_Req.io_Command=TD_FORMAT;
	 io1->iotd_Req.io_Offset=sectorPos;

	 /*Update the status window*/
	 strcpy(statString,"Formatting cylinder ");
	 stci_d(&statString[20],track+layout->lowCyl);
	 strcat(statString,", ");
	 stci_d(&statString[strlen(statString)],numTracks-track-1);
	 strcat(statString," to go  ");

	 error=(updateStatWindow(statString,track*1000/numTracks)) ?0:error;

	 /*If the user didn't press 'Stop'*/
	 if(error!=0)
	 {
	    error=(DoIO((struct IOReq *)io1)) ? 0 : error;
	    if(error==0)
	       printError("A formatting error occured",NULL,NULL);
	 }

	 /*If we're suppossed to verify, and the user didn't press 'Stop'*/
	 if(Verify && error!=0)
	 {
	    /*Setup the same IORequest to do a read*/
	    io1->iotd_Req.io_Command=CMD_READ;

	    /*Update the status*/
	    strcpy(statString," Verifying cylinder ");
	    stci_d(&statString[20],track+layout->lowCyl);
	    strcat(statString,", ");
	    stci_d(&statString[strlen(statString)],numTracks-track-1);
	    strcat(statString," to go  ");

	    error=(updateStatWindow(statString,
		       ( (1+(track*2))*1000)/(2*numTracks) )) ? 0 : error;

	    /*If the user hasn't pressed 'Stop'*/
	    if(error!=0)
	    {
	       /*Do the read*/
	       if(DoIO((struct IOReq *)io1))
	       {
		  if(--error==0)
		     printError("A verify error occurred",NULL,NULL);
		  errorB=TRUE;
	       }
	    }

	    /*Check the data that was read back.*/
	    for(c=0;c<trackSize>>2 && error!=0;c++)
	    {
	       /*If there was an error*/
	       if(write[c]!=0L)
	       {
		  /*Record the fact*/
		  if(!errorB)
		  {
		     errorB=TRUE;
		     if(--error==0)
			/*Print an error message*/
			printError("A verify error occurred",NULL,NULL);
		  }
		  write[c]=0L;
	       }
	    }
	    if(!errorB && error!=2)
	       error=2;
	 }
	 /*Get the byte position of the next track*/
	 if(errorB)
	 {
	    errorB=FALSE;
	    track--;
	 }
	 else
	    sectorPos+=trackSize;
      }

      /*We're done, so free the track memory*/
      FreeMem(write,trackSize);
   }
   else
      printError("Couldn't allocate write buffer",NULL,NULL);

   return(error!=0);
}

/*Used in askAreYouSure()*/
struct EasyStruct areYouSure=
{
   sizeof(struct EasyStruct),
   0,
   "Format Request...",
   "Are you sure you want to format volume\n%s\n(the contents of the disk will be destroyed)?",
   "Yes|No"
};

/*Ask the user if she really wants to format the disk*/
BOOL askAreYouSure(char *volumeName,BOOL truncColon)
{
   char name[36];
   APTR args[2];
   UBYTE result;

   /*Get the device name*/
   strcpy(name,volumeName);

   /*Truncate the trailing colon if so specified*/
   if(truncColon)
      name[strlen(name)-1]=NULL;

   /*Setup the device name as the argument to the requestor*/
   args[0]=name;
   args[1]=NULL;

   /*Put up the requestor*/
   result=EasyRequestArgs(NULL,&areYouSure,NULL,args);

   /*Return the result*/
   return(result==1);
}

/*Used in askAreYouSure()*/
struct EasyStruct writeProtected=
{
   sizeof(struct EasyStruct),
   0,
   "Format Request...",
   "Volume\n%s\nis write protected",
   "Retry|Cancel"
};

/*Alert the user to the fact that the disk is write protected, and give*/
/*the user a chance to unprotect the disk*/
BOOL alertIsWriteProtected(char *devName)
{
   APTR args[2];
   BYTE result;

   /*Setup the device name as the argument to the requestor*/
   args[0]=devName;
   args[1]=NULL;

   /*Put up the requestor*/
   result=EasyRequestArgs(NULL,&writeProtected,NULL,args);

   /*Return the result*/
   return(result==1);
}

/*This is used to print errors*/
struct EasyStruct errorReq=
{
   sizeof(struct EasyStruct),
   0,
   "Format Request...",
   NULL,
   "Ok"
};

char *oneLine="%s";
char *twoLine="%s\n%s";
char *threeLine="%s\n%s\n%s";

/*Print one, two, or three lines of error messages in an EasyRequestor*/
/*or to 'StdErr'*/
void printError(char *first,char *second,char *third)
{
   APTR args[4];
   args[0]=args[3]=NULL;

   /*If we're running from the CLI*/
   if(WBenchMsg==NULL)
   {
      /*And a StdErr handle was opened successfully*/
      if(StdErr!=NULL)
      {
	 char LF=0x0A;

	 /*Print the first line*/
	 if(first!=NULL)
	 {
	    Write(StdErr,first,strlen(first));
	    Write(StdErr," ",1);
	 }

	 /*Print the second line*/
	 if(second!=NULL)
	 {
	    Write(StdErr,second,strlen(second));
	    Write(StdErr," ",1);
	 }

	 /*Print the third line*/
	 if(third!=NULL)
	 {
	    Write(StdErr,third,strlen(third));
	    Write(StdErr," ",1);
	 }

	 /*Print the terminating carriage return*/
	 Write(StdErr,&LF,1);
      }
   }
   else  /*Otherwise, we're running from Workbench, so put up the requestor*/
   {
      /*Three lines*/
      if(third!=NULL)
      {
	 args[2]=third;
	 args[1]=second;
	 args[0]=first;
	 errorReq.es_TextFormat=threeLine;
      }
      else if(second!=NULL)   /*Two lines*/
      {
	 args[1]=second;
	 args[0]=first;
	 errorReq.es_TextFormat=twoLine;
      }
      else if(first!=NULL)    /*One line*/
      {
	 args[0]=first;
	 errorReq.es_TextFormat=oneLine;
      }
      /*Put up the requestor*/
      EasyRequestArgs(NULL,&errorReq,NULL,args);
   }

   return;
}

/*Get the name of a volume, given a lock to that volume*/
void getVolumeName(char *name,struct WBArg *argList,UWORD disk)
{
   /*Get the name*/
   if(NameFromLock(argList[disk].wa_Lock,name,256)==DOSFALSE)
      /*Or return <unknown> if the name couldn't be determined*/
      strcpy(name,"<unknown>");

   return;
}

/*Exit from the program, closing any open libraries*/
void cleanup(ULONG err)
{
   if(IntuitionBase!=NULL)
      CloseLibrary((struct Library *)IntuitionBase);

   if(GfxBase!=NULL)
      CloseLibrary((struct Library *)GfxBase);

   if(GadToolsBase!=NULL)
      CloseLibrary(GadToolsBase);

   if(IconBase!=NULL)
      CloseLibrary(IconBase);

   exit(err);
}

/*Get input from the original window*/
prepResult getPrepInput(void)
{
   struct IntuiMessage *mesg;
   ULONG class;
   ULONG code;
   struct Gadget *gadget;
   struct TagItem tags[2];

   /*Setup tags that will be used to toggle the states of checkbox gadgets*/
   tags[0].ti_Tag=GTCB_Checked;
   tags[1].ti_Tag=TAG_DONE;
   tags[1].ti_Data=NULL;

   /*Loop until the user presses 'OK' or 'Cancel'*/
   for(;;)
   {
      /*Wait for input*/
      Wait(1<<PrepWnd->UserPort->mp_SigBit);

      /*Get the input*/
      mesg=GT_GetIMsg(PrepWnd->UserPort);

      /*Loop while there are messages to be processed*/
      while(mesg!=NULL)
      {
	 /*Get the message type, etc.*/
	 class=mesg->Class;
	 code=mesg->Code;
	 gadget=(struct Gadget *)mesg->IAddress;

	 /*Reply to the message*/
	 GT_ReplyIMsg(mesg);

	 /*Act on the message*/
	 switch(class)
	 {
	    /*User clicked on close gadget.  Treat it as a click on 'Cancel'*/
	    case CLOSEWINDOW:
	       return(eQuit);

	    /*User pressed a gadget*/
	    case GADGETUP:
	       switch(gadget->GadgetID)
	       {
		  /*Checkbox gadgets*/
		  /*(each toggles the appropriate status flag)*/
		  case GD_FFSGadget:
		     FFS=!FFS;
		     break;
		  case GD_IconGadget:
		     Icon=!Icon;
		     break;
		  case GD_QuickFmtGadget:
		     QuickFmt=!QuickFmt;
		     break;
		  case GD_VerifyGadget:
		     Verify=!Verify;
		     break;

		  /*OK*/
		  case GD_OKGadget:
		     return(eOK);

		  /*Cancel*/
		  case GD_CancelGadget:
		     return(eCancel);
	       }
	       break;

	    /*Keypress (gadget equivalents)*/
	    case VANILLAKEY:
	       switch(code)
	       {
		  /*Disk name*/
		  case 'n':
		  case 'N':
		     ActivateGadget(PrepGadgets[GD_NameGadget],
				     PrepWnd,NULL);
		     break;

		  /*FFS*/
		  case 'f':
		  case 'F':
		     tags[0].ti_Data=(FFS=!FFS);

		     /*Toggle the checkmark state of the gadget*/
		     GT_SetGadgetAttrsA(PrepGadgets[GD_FFSGadget],
					PrepWnd,NULL, tags);
		     break;

		  /*Verify*/
		  case 'v':
		  case 'V':
		     tags[0].ti_Data=(Verify=!Verify);

		     GT_SetGadgetAttrsA(PrepGadgets[GD_VerifyGadget],
					PrepWnd,NULL, tags);
		     break;

		  /*Quick Format*/
		  case 'q':
		  case 'Q':
		     tags[0].ti_Data=(QuickFmt=!QuickFmt);

		     GT_SetGadgetAttrsA(PrepGadgets[GD_QuickFmtGadget],
					PrepWnd,NULL, tags);
		     break;

		  /*Create icons*/
		  case 'r':
		  case 'R':
		     tags[0].ti_Data=(Icon=!Icon);

		     GT_SetGadgetAttrsA(PrepGadgets[GD_IconGadget],
					PrepWnd,NULL, tags);
		     break;

		  /*Cancel*/
		  case 'c':
		  case 'C':
		     return(eCancel);

		  /*OK*/
		  case 'o':
		  case 'O':
		     return(eOK);
	       }
	    break;
	 }
	 /*Get the next message*/
	 mesg=GT_GetIMsg(PrepWnd->UserPort);
      }
   }
   return(FALSE);
}

/*Convert a volume name to a device name, and get information*/
/*on the layout of the disk*/
BOOL volumeToDevName(BPTR volumeLock,char *dev,DriveLayout *layout)
{
   BOOL stat;
   struct DosList *dosList;
   struct DeviceNode *devNode;
   char name[36];
   char *temp;
   int c;
   BPTR tempLock=NULL;
   struct DosEnvec *driveEnv;
   struct Process *process;
   APTR oldWdw;

   /*Disable requestors during the execution of this function*/
   process=(struct Process *)FindTask(0L);
   oldWdw=process->pr_WindowPtr;
   process->pr_WindowPtr=(APTR)(-1);

   /*Get the DOS device list*/
   dosList=LockDosList(LDF_DEVICES|LDF_READ);

   /*Go through each entry*/
   while(dosList = NextDosEntry(dosList,LDF_DEVICES|LDF_READ))
   {
      devNode=(struct DeviceNode *)dosList;

      /*If the node in the list is a volume*/
      if(devNode->dn_Startup > 1000 &&
	  (devNode->dn_Task || devNode->dn_Handler || devNode->dn_SegList))
      {
	 /*Get the name of the device*/
	 temp=(char *)BADDR(devNode->dn_Name);

	 for(c=0;c<temp[0];c++)
	    name[c]=temp[c+1];

	 name[c]=':';
	 name[c+1]=0;

	 /*Get the information on the device*/
/*	   driveEnv=(struct DosEnvec *)
	    BADDR(((struct FileSysStartupMsg *)
		  BADDR(devNode->dn_Startup))->fssm_Environ);*/

	 /*If the volume lock is NULL, the 'dev' is assumed to be the*/
	 /*name of a device holding an unformatted disk, so compare the*/
	 /*name to the name of the current node*/
	 if(volumeLock==NULL)
	    stat=(stricmp(dev,name)==0);
	 else
	 {
	    /*Otherwise, since 'dev' could hold a volume rather than*/
	    /*device name, get a lock on it and compare it to the lock*/
	    /*on the device that was given;  if they're the same, we've*/
	    /*found the drive*/

	    tempLock=Lock(name,ACCESS_READ);
	    stat=(SameLock(tempLock,volumeLock)==LOCK_SAME);
	 }

	 /*If we've found the drive, get the information on it*/
	 if(stat)
	 {
	    struct FileSysStartupMsg *startup;
	    char *devName;

	    /*Get a pointer to the structure that holds the needed info*/
	    startup=(struct FileSysStartupMsg *)BADDR(devNode->dn_Startup);
	    driveEnv=(struct DosEnvec *)BADDR(startup->fssm_Environ);

	    /*Get the information*/
	    layout->unit=startup->fssm_Unit;

	    devName=(char *)BADDR(startup->fssm_Device);

	    /*Copy the device name to layout->devName*/
	    for(c=0;c<devName[0];c++)
	       layout->devName[c]=devName[c+1];
	    layout->devName[c+1]=0;

	    layout->flags=startup->fssm_Flags;

	    layout->memType=driveEnv->de_BufMemType;

	    layout->lowCyl=driveEnv->de_LowCyl;
	    layout->highCyl=driveEnv->de_HighCyl;
	    layout->surfaces=driveEnv->de_Surfaces;
	    layout->BPT=driveEnv->de_BlocksPerTrack;
	    layout->blockSize=driveEnv->de_SizeBlock;

	    /*Copy the device name back to 'dev'*/
	    strcpy(dev,name);

	    /*UnLock the drive lock, if it exists*/
	    if(tempLock!=NULL)
	       UnLock(tempLock);

	    /*Unlock the DOS list*/
	    UnLockDosList(LDF_DEVICES|LDF_READ);

	    /*Restore the requester pointer*/
	    process->pr_WindowPtr=oldWdw;

	    /*And return TRUE*/
	    return(TRUE);
	 }

	 /*We didn't find the drive, so unlock the lock and try again*/
	 if(tempLock!=NULL)
	    UnLock(tempLock);
      }
   }

   /*We didn't find the drive in the list, so unlock the list*/
   UnLockDosList(LDF_DEVICES|LDF_READ);

   /*Restore the requester pointer*/
   process->pr_WindowPtr=oldWdw;

   /*And return*/
   return(FALSE);
}

/*Create a communications port linking this process to the drive*/
struct IOExtTD *OpenDrive(char *driveDevName,ULONG unit,ULONG flags)
{
   struct MsgPort *diskPort;
   struct IOExtTD *diskRequest;

   /*Create the message port*/
   if((diskPort = CreateMsgPort())!=NULL)
   {
      /*Create the IORequest*/
      diskRequest=
	(struct IOExtTD *)CreateIORequest(diskPort,sizeof(struct IOExtTD));
      if(diskRequest!=NULL)
      {
	 /*Open the device, and return the IORequest if the device*/
	 /*opened successfully*/
	 if(!OpenDevice(driveDevName,unit,(struct IORequest *)diskRequest,flags))
	    return(diskRequest);

	 /*The device didn't open, so clean up*/
	 DeleteIORequest(diskRequest);
      }

      DeleteMsgPort(diskPort);
   }

   /*Return NULL to indicate that an error occurred*/
   return(NULL);
}

/*Close an open device and delete the accompanying port, etc.*/
void CloseDrive(struct IOExtTD *diskRequest)
{
   CloseDevice((struct IORequest *)diskRequest);
   DeleteMsgPort(diskRequest->iotd_Req.io_Message.mn_ReplyPort);
   DeleteIORequest(diskRequest);
}

/*Convert a CSTR to a BSTR*/
BSTR makeBSTR(char *in,char *out)
{
   int c;

   out[0]=strlen(in);
   for(c=0;c<out[0];c++)
      out[c+1]=in[c];

   return(MKBADDR(out));
}

LONG args[6]=
{
   NULL,NULL,0,0,0,0
};

/*Get the command-line arguments given by the user, by using ReadArgs()*/
void parseArgs(char *drive,char *newName,BOOL *ffs,BOOL *icons,BOOL *quick,
	       BOOL *verify)
{
   APTR r;

   /*Get the arguments*/
   r=ReadArgs("DRIVE/K/A,NAME/K/A,FFS/S,NOICONS/S,QUICK/S,NOVERIFY/S",args,
	       NULL);

   /*If the user didn't specify a drive name, print an error*/
   if(args[0]==NULL)
   {
      printError("You need to specify a drive to format",NULL,NULL);
      if(r!=NULL)
	 FreeArgs(r);
      cleanup(200);
   }
   else
      strcpy(drive,(char *)args[0]);

   /*Likewise for a name for the newly formatted volume*/
   if(args[1]==NULL)
   {
      printError("You need to specify a name for the volume",NULL,NULL);
      cleanup(200);
   }
   else
      strcpy(newName,(char *)args[1]);

   /*Get the four togglable settings*/
   *ffs=(args[2]!=0);
   *icons=(args[3]==0);
   *quick=(args[4]!=0);
   *verify=(args[5]==0);

   /*We're done, so free the ReadArgs result*/
   FreeArgs(r);

   /*And return*/
   return;
}

/*Update the status window (from Workbench), or CLI output (from the CLI)*/
/*If running from the CLI, this also checks to see if the user has pressed*/
/*control-C*/
BOOL updateStatWindow(char *string,UWORD percent)
{
   UWORD width;
   ULONG class;
   UWORD code;
   struct TagItem tags[3];
   static char Message[80];

   struct IntuiMessage *mesg;

   /*If this is NULL, we're running from the CLI*/
   if(WBenchMsg==NULL)
   {
      /*Write the string to the CLI, followed by a carriage return (but*/
      /*not a line feed)*/
      char CR=0x0d;
      Write(Output(),string,strlen(string));
      Write(Output(),&CR,1);

      /*Check to see if the user pressed Control-C*/
      if( (SetSignal(0,0) & SIGBREAKF_CTRL_C) == SIGBREAKF_CTRL_C)
      {
	 /*If he did, print "***Break" and return TRUE, to signal that*/
	 /*the user aborted the formatting process*/
	 Write(Output(),"\n***Break\n",10);
	 return(TRUE);
      }
      /*Otherwise, continue*/
      return(FALSE);
   }

   /*This code is used to update the status window that is displayed when*/
   /*the user runs NewFormat from the Workbench*/

   /*This puts the message into the text-display gadget*/
   /*This copy is so that the caller can change the contents of 'string'*/
   /*upon return (which can't be done without the copy, since GadTools*/
   /*won't copy the contents of 'string' to an internal buffer*/
   strcpy(Message,string);
   tags[0].ti_Tag=GTTX_Text;
   tags[0].ti_Data=(ULONG)Message;

   tags[1].ti_Tag=TAG_DONE;
   tags[1].ti_Data=NULL;

   GT_SetGadgetAttrsA(StatusGadgets[GD_StatusGadget],
       StatusWnd,NULL, tags);

   /*Fill the status box with the current percentage of completion*/
   SetAPen(StatusWnd->RPort,3);
   width=box.left+((box.width-3)*percent)/1000;
   RectFill(StatusWnd->RPort,box.left+2,box.top+1,width,box.top+box.height-2);

   /*Check user input*/
   mesg=GT_GetIMsg(StatusWnd->UserPort);

   /*Loop while there are messages*/
   while(mesg!=NULL)
   {
      class=mesg->Class;
      code=mesg->Code;
      GT_ReplyIMsg(mesg);

      switch(class)
      {
	 /*Return TRUE (user abort) if the user pressed 's', 'S'*/
	 case IDCMP_VANILLAKEY:
	    if(code=='s' || code=='S')
	       return(TRUE);
	    break;

	 /*Or if the user pressed the 'Stop' gadget*/
	 case IDCMP_GADGETUP:
	    return(TRUE);
      }
      /*Get the next message*/
      mesg=GT_GetIMsg(StatusWnd->UserPort);
   }
   return(FALSE);
}

/*End of Format.c*/

