#include <Functions.h>
#include <Stdio.h>
#include <CType.h>
#include <Exec/Exec.h>
#include <Intuition/Intuition.h>
#include <Libraries/Dos.h>
#include <Libraries/DosExtens.h>
#include <Libraries/FileHandler.h>

#define PHYSICAL_DEVICE      0L

#define SHUT_IT_ALL_DOWN     0L
#define INFO_ALLOC_ERROR     1L
#define BLOCK_ALLOC_ERROR    2L
#define INTUITION_OPEN_ERROR 3L
#define GRAPHICS_OPEN_ERROR  4L
#define WINDOW_OPEN_ERROR    5L

#define WINDOW_WIDTH      640L
#define WINDOW_HEIGHT     100L

#define PEN_0             0L
#define PEN_1             1L
#define PEN_2             2L
#define PEN_3             3L

extern struct DosLibrary *DOSBase;

struct Library      *IntuitionBase;
struct Library      *GfxBase;
struct IntuiMessage *Message;
struct RastPort     *raster;
struct Window       *Info_Window;
struct NewWindow     Info_Window_Def =
   {
      (SHORT)0,             (SHORT)0,
      (SHORT)WINDOW_WIDTH,  (SHORT)WINDOW_HEIGHT,
      (UBYTE)PEN_0,         (UBYTE)PEN_1,
      (ULONG)(CLOSEWINDOW),
      (ULONG)(WINDOWDEPTH | WINDOWDRAG | WINDOWCLOSE | RMBTRAP),
      NULL,                 NULL,
      (UBYTE *)"Super Info",
      NULL,                 NULL,
      (SHORT)WINDOW_WIDTH,  (SHORT)WINDOW_HEIGHT,
      (SHORT)WINDOW_WIDTH,  (SHORT)WINDOW_HEIGHT,
      (USHORT)WBENCHSCREEN
   } ;

struct DevList
   {
      unsigned char  *Name;
      struct DevList *Next;
   } ;

struct Lock            *DeviceLock;
struct InfoData        *DeviceInfo;
struct FileInfoBlock   *DeviceBlock;

main(arc, arv)
int  arc;
char *arv[];
   {
      void FreeDeviceList(),
           DrawHeading(),
           ProcessDevice(),
           DetailPrint(),
           ShutDown();

      struct DevList *FindAllDeviceNames();
      struct DevList *BuildFromArgs();
      struct DevList *FindLastDevice();

      struct Process    *ThisTask;
      struct Window     *OldWindowPtr;
      struct DevList    *FirstDevice;
      struct DevList    *Temp;
      unsigned char      DeviceData[72];
      unsigned long int  class,
                         success,
                         StopProgram = FALSE;
      unsigned short int run_program = TRUE,
                         info_available,
                         loop,
                         code,
                         TotalDevices = 0,
                         counter;

      DeviceInfo  = (struct InfoData *)AllocMem( (sizeof(struct InfoData) ),
                                                MEMF_CLEAR);
      if (DeviceInfo == NULL)
         ShutDown(INFO_ALLOC_ERROR);

      DeviceBlock = (struct FileInfoBlock *)AllocMem( (sizeof(struct FileInfoBlock) ),
                                                MEMF_CLEAR);
      if (DeviceBlock == NULL)
         ShutDown(BLOCK_ALLOC_ERROR);

      IntuitionBase = (struct Library *)OpenLibrary("intuition.library", LIBRARY_VERSION);
      if (IntuitionBase == NULL)
         ShutDown(INTUITION_OPEN_ERROR);

      GfxBase = (struct Library *)OpenLibrary("graphics.library", LIBRARY_VERSION);
      if (GfxBase == NULL)
         ShutDown(GRAPHICS_OPEN_ERROR);

      if(arc < 2)
         FirstDevice = FindAllDeviceNames();
      else
         FirstDevice = BuildFromArgs(arv, arc);

      Temp = FirstDevice;
      do
         {
            TotalDevices++;
            Temp = Temp->Next;
         }
      while (Temp != NULL);

      Info_Window_Def.Height = ( (TotalDevices + 2L) * 8L) + 4L;

      Info_Window = (struct Window *)OpenWindow(&Info_Window_Def);
      if(Info_Window == NULL)
         ShutDown(WINDOW_OPEN_ERROR);

      ThisTask               = (struct Process *)FindTask(0L);
      OldWindowPtr           = (struct Window *)ThisTask->pr_WindowPtr;
      ThisTask->pr_WindowPtr = (APTR)-1L;

      raster = (struct RastPort *)Info_Window->RPort;
      DrawHeading(FirstDevice);

      while (StopProgram == FALSE)
         {
            Message = (struct IntuiMessage *)GetMsg(Info_Window->UserPort);
            class      = Message->Class;
            code       = Message->Code;

            if(Message != 0)
               ReplyMsg(Message);

            switch (class)
               {
                  case CLOSEWINDOW: StopProgram = TRUE;
                                    break;
               }

            counter = 1;
            Temp = FirstDevice;
            do
               {
                  Forbid();
                  ProcessDevice(Temp->Name, DeviceData);
                  Permit();
                  DetailPrint(DeviceData, counter);
                  counter++;
                  Temp = Temp->Next;
               }
            while (Temp != NULL);
            Delay(10L);
         }

      ShutDown(SHUT_IT_ALL_DOWN);
      FreeDeviceList(FirstDevice);
      ThisTask->pr_WindowPtr = (APTR)OldWindowPtr;

   }

void ProcessDevice(DeviceName, DataLine)
unsigned char DeviceName[];
unsigned char DataLine[];
   {
      void ProcessIOErr();

      long int Error;
      long int StorageSize;
      long int Percent;

      float PercentFloat;

      unsigned char StorageSizeString[5];
      unsigned char PercentString[5];
      unsigned char StateString[11];
      unsigned char VolumeString[29];

      DeviceLock = (struct Lock*)Lock( (char *)DeviceName, (long)ACCESS_READ);
      if (DeviceLock == NULL)
         {
            Error = IoErr();
            ProcessIOErr(Error, DataLine);
            return;
         }

      Info(DeviceLock, DeviceInfo);
      Examine(DeviceLock, DeviceBlock);

      PercentFloat = (float)DeviceInfo->id_NumBlocksUsed / (float)DeviceInfo->id_NumBlocks
                 * (float)100;
      Percent = PercentFloat;
      sprintf(PercentString, "%d%c", Percent, '%');

      strncpy(VolumeString, &DeviceBlock->fib_FileName[0], 28);

      if ( (strlen(&DeviceBlock->fib_FileName[0]) ) > 28);
         {
            VolumeString[27] = '~';
            VolumeString[28] = NULL;
         }

      StorageSize = DeviceInfo->id_NumBlocks / 2;
      if (StorageSize >= 1024)
         {
            StorageSize = StorageSize / 1024;
            sprintf(StorageSizeString, "%3dM", StorageSize);
         }
      else
         sprintf(StorageSizeString, "%3dK", StorageSize);

      switch (DeviceInfo->id_DiskState)
         {
            case ID_WRITE_PROTECTED: sprintf(StateString, "%s", "Read Only");
                                     break;
            case ID_VALIDATED:       sprintf(StateString, "%s", "Read/Write");
                                     break;
            case ID_VALIDATING:      sprintf(StateString, "%s", "Validating");
                                     break;
         }

      sprintf(DataLine, "%4s  %5d  %5d  %4s %5d %-11s %-28s",
                         StorageSizeString, DeviceInfo->id_NumBlocksUsed,
                         (DeviceInfo->id_NumBlocks - DeviceInfo->id_NumBlocksUsed),
                         PercentString, DeviceInfo->id_NumSoftErrors,
                         StateString, VolumeString);

      UnLock(DeviceLock);
   }

void ProcessIOErr(ErrNumber, ErrorReport)
long int ErrNumber;
unsigned char ErrorReport[];
   {
      switch (ErrNumber)
         {
            case ERROR_OBJECT_IN_USE:
               sprintf(ErrorReport, "%-71s", "Object in use");
               break;
            case ERROR_DEVICE_NOT_MOUNTED:
               sprintf(ErrorReport, "%-71s", "Device not mounted");
               break;
            case ERROR_NOT_A_DOS_DISK:
               sprintf(ErrorReport, "%-71s", "Not a DOS disk");
               break;
            case ERROR_NO_DISK:
               sprintf(ErrorReport, "%-71s", "No disk present");
               break;
            default:
               sprintf(ErrorReport, "%s #%ld", "Undocumented DOS error", ErrNumber);
               break;
         }
   }

struct DevList *FindAllDeviceNames()
   {

      void *malloc();
      unsigned char *calloc();

      struct RootNode   *DOSRoot;
      struct DosInfo    *InfoRoot;
      struct DeviceNode *DeviceRoot;
      struct DevList    *Temp  = NULL;
      struct DevList    *First = NULL;
      struct DevList    *Last  = NULL;
      struct DevList    DevListSize;

      unsigned char      *TempDeviceName;
      unsigned char       DeviceName[256];
      unsigned long int   DeviceNameLength;
      unsigned short int  counter;

      Forbid(); /* Turn off task switching */

      DOSRoot    = (struct RootNode *)DOSBase->dl_Root;
      InfoRoot   = (struct DosInfo *)BADDR(DOSRoot->rn_Info);
      DeviceRoot = (struct DeviceNode *)BADDR(InfoRoot->di_DevInfo);

      while (DeviceRoot != NULL)
         {
            TempDeviceName   = (unsigned char *)BADDR(DeviceRoot->dn_Name);
            DeviceNameLength = (unsigned long int)DeviceName[0];

            if ( (DeviceRoot->dn_Type == PHYSICAL_DEVICE) &&
                 (DeviceRoot->dn_Task != NULL)            &&
                 (DeviceRoot->dn_Name != 0) )
               {
                  counter = 0;
                  DeviceName[counter] = TempDeviceName[counter + 1L];
                  while(DeviceName[counter] != NULL)
                     {
                        counter++;
                        DeviceName[counter] = TempDeviceName[counter + 1];
                     }

                  DeviceName[counter++] = ':';
                  DeviceName[counter]   = NULL;

                  Temp = (struct DevList *)malloc( (sizeof(DevListSize) ) );
                  Temp->Next = NULL;
                  Temp->Name = (unsigned char *)calloc(strlen(DeviceName) + 1, sizeof(char) );
                  sprintf(Temp->Name, "%s", DeviceName);

                  if (First == NULL)
                     {
                        Temp->Next = NULL;
                        First      = Temp;
                        Last       = First;
                     }
                  else
                     {
                        Temp->Next = NULL;
                        Last->Next = Temp;
                        Last       = Temp;
                     }
               }
            DeviceRoot = (struct DeviceNode *)BADDR(DeviceRoot->dn_Next);
         }

      Permit();
      return(First);

   }

struct DevList *BuildFromArgs(Arg, ArgNum)
char *Arg[];
int   ArgNum;
   {
      void *malloc();
      unsigned char *calloc();

      struct DevList *First = NULL;
      struct DevList *Last  = NULL;
      struct DevList *Temp  = NULL;
      struct DevList DevListSize;

      unsigned short int counter = 1;
      unsigned short int letter;

      while (counter < ArgNum)
         {
            Temp       = (struct DevList *)malloc( (sizeof(DevListSize) ) );
            Temp->Next = NULL;

            letter = 0;
            while(Arg[counter][letter] != NULL)
               {
                  Arg[counter][letter] = toupper(Arg[counter][letter]);
                  letter++;
               }

            if (Arg[counter][ (strlen(Arg[counter] ) - 1) ] != ':')
               {
                  Temp->Name = (unsigned char *)calloc(strlen(Arg[counter] ) + 2, sizeof(char) );
                  sprintf(Temp->Name, "%s:", Arg[counter]);
               }
            else
               {
                  Temp->Name = (unsigned char *)calloc(strlen(Arg[counter] ) + 1, sizeof(char) );
                  sprintf(Temp->Name, "%s", Arg[counter]);
               }

            if (First == NULL)
               {
                  Temp->Next = NULL;
                  First      = Temp;
                  Last       = First;
               }
            else
               {
                  Temp->Next = NULL;
                  Last->Next = Temp;
                  Last       = Temp;
               }

            counter++;
         }

      return(First);

   }

void FreeDeviceList(Items)
struct DevList *Items;
   {
      struct DevList *Temp;
      struct DevList *Temp2;

      Temp = Items;
      while (Temp != NULL)
         {
            free(Temp->Name);
            Temp2 = Temp->Next;
            free(Temp);
            Temp = Temp2;
         }
   }

void ShutDown(Error_Level)
long int Error_Level;
   {
      switch (Error_Level)
         {
            case SHUT_IT_ALL_DOWN:     CloseWindow(Info_Window);
            case WINDOW_OPEN_ERROR:    CloseLibrary(GfxBase);
            case GRAPHICS_OPEN_ERROR:  CloseLibrary(IntuitionBase);
            case INTUITION_OPEN_ERROR: FreeMem(DeviceBlock, sizeof(struct FileInfoBlock) );
            case BLOCK_ALLOC_ERROR:    FreeMem(DeviceInfo, sizeof(struct InfoData) );
                                       break;
         }

      switch (Error_Level)
         {
            case WINDOW_OPEN_ERROR:    printf("Window open error.\n");
                                       exit(WINDOW_OPEN_ERROR);
            case GRAPHICS_OPEN_ERROR:  printf("Graphics open error.\n");
                                       exit(GRAPHICS_OPEN_ERROR);
            case INTUITION_OPEN_ERROR: printf("Intuition open error.\n");
                                       exit(INTUITION_OPEN_ERROR);
            case BLOCK_ALLOC_ERROR:    printf("Block allocation error.\n");
                                       exit(BLOCK_ALLOC_ERROR);
            case INFO_ALLOC_ERROR:     printf("Info allocation error.\n");
                                       exit(INFO_ALLOC_ERROR);
         }
   }

void DrawHeading(Dev)
struct DevList *Dev;
   {
      struct DevList *Temp;
      unsigned short int counter = 1;

      SetAPen(raster, PEN_2);
      Move(raster, 4L, 17L);
      Text(raster, "Unit  Size   Used   Free  Full  Errs   Status    Name",
          (long)(strlen("Unit  Size   Used   Free  Full  Errs   Status    Name") ) );

      SetAPen(raster, PEN_1);
      Temp = Dev;

      do
         {
            Move(raster, 4L, (long)(17 + (counter * 8) ) );
            Text(raster, Temp->Name, (long)(strlen(Temp->Name) ) );
            counter++;
            Temp = Temp->Next;
         }
      while (Temp != NULL);

   }

void DetailPrint(Information, Line)
unsigned char Information[];
unsigned short int Line;
   {
      Move (raster, 52L, (long)(17 + (Line * 8) ) );
      Text(raster, Information, (long)(strlen(Information) ) );
   }