
#include "du_h.h"

struct ExecBase *SysBase;
struct DosLibrary *DOSBase = NULL;
struct Library *UtilityBase = NULL;

STRPTR verstag = "$VER: du 1.1 (96.3.17)\r\n";

struct entry
   {
   struct Node node;
   struct InfoData info;
   BOOL showit;
   UBYTE name[32];
   };

#define ENTRY_VOLUME NT_USER-1
#define ENTRY_DEVICE NT_USER-2
#define ENTRY_ASSIGN NT_USER-3
#define ENTRY_DEVDISK NT_USER-8

VOID btocstr(BSTR, STRPTR, UWORD);
VOID killlist(struct List *);
STRPTR itok(ULONG num, STRPTR buf);
LONG __asm cmpfunc(register __a1 struct entry *, register __a2 struct entry *);

struct Hook cmphook =
   {
   {NULL, NULL},
   (ULONG (*)())cmpfunc,
   NULL, NULL
   };

LONG __saveds start(VOID)
   {
   LONG rc = 0;

   SysBase = *(struct ExecBase **)4L;
   if ((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)) &&
    (UtilityBase = OpenLibrary("utility.library", 37)))
      {
      struct RDArgs *rdargs;
      STRPTR *devs = NULL, *devpt;
      struct Process *proc;
      APTR win;
      struct List dlist;
      struct DosList *dl, *vn;
      struct entry *de, **array;
      struct InfoData *info;
      UWORD i, count;

      ULONG size, used, free;
      UBYTE sizebuf[6], usedbuf[6], freebuf[6], buf[64];
      BOOL moreinfo;
      STRPTR disktype, status;
      UBYTE matchbuf[256];

      if (rdargs = ReadArgs("Devices/M", (LONG *)&devs, NULL))
         {
         proc = (struct Process *)FindTask(NULL);
         win = proc->pr_WindowPtr;
         proc->pr_WindowPtr = (APTR)-1; // suppress system requesters

         dlist.lh_Head = (struct Node *)&dlist.lh_Tail;
         dlist.lh_Tail = NULL;
         dlist.lh_TailPred = (struct Node *)&dlist;

         if (info = AllocMem(sizeof(struct InfoData), MEMF_PUBLIC))
            {
            dl = LockDosList(LDF_DEVICES|LDF_READ);
            i = 0;
            while (dl = NextDosEntry(dl, LDF_DEVICES|LDF_READ))
               {
               if (dl->dol_Task && DoPkt(dl->dol_Task,
                ACTION_DISK_INFO, MKBADDR(info), NULL, NULL, NULL, NULL))
                  {
                  if (de = AllocMem(sizeof(struct entry), MEMF_CLEAR))
                     {
                     de->node.ln_Pri = 1;
                     de->node.ln_Type = ENTRY_DEVICE;
                     de->node.ln_Name = de->name;
                     de->info = *info;
                     btocstr(dl->dol_Name, de->name, 28);
                     strcat(de->name, ":");
                     Enqueue(&dlist, (struct Node *)de);
                     i++;
                     }
                  }
               }
            UnLockDosList(LDF_DEVICES|LDF_READ);
            FreeMem(info, sizeof(struct InfoData));
            }

         count = i;
         if (array = AllocMem(sizeof(APTR)*count, MEMF_ANY))
            {
            for (i = 0, de = (struct entry *)dlist.lh_Head;
             de->node.ln_Succ;
             i++, de = (struct entry *)de->node.ln_Succ)
               array[i] = de;

            QSort((APTR)array, count, &cmphook);

            for (devpt = devs; *devpt; devpt++)
               {
               ParsePatternNoCase(*devpt, matchbuf, 254);
               for (i = 0; i < count; i++)
                  {
                  de = array[i];
                  de->showit |= MatchPatternNoCase(matchbuf, de->node.ln_Name);
                  }
               }

            PutStr("Unit     Size Block DiskType  Used Free Full  Errs   Status    Volume\n");
   /***
    ***             Unit     Size Block DiskType  Used Free Full  Errs   Status    Volume
    ***             ŻŻŻŻŻŻŻŻ ŻŻŻŻ ŻŻŻŻŻ ŻŻŻŻŻŻŻŻ  ŻŻŻŻ ŻŻŻŻ ŻŻŻŻ  ŻŻŻŻ ŻŻŻŻŻŻŻŻŻŻ  ŻŻŻŻŻŻŻŻ
    ***/
            for (i = 0; i < count; i++)
               {
               de = array[i];
               if (devs && !de->showit)
                  continue;

               moreinfo = TRUE;
               switch(de->info.id_DiskType)
                  {
                  case ID_NO_DISK_PRESENT:
                     disktype = "No disk present";
                     moreinfo = FALSE;
                     break;

                  case ID_UNREADABLE_DISK:
                     disktype = "Unreadable disk";
                     moreinfo = FALSE;
                     break;

                  case ID_NOT_REALLY_DOS:
                     disktype = "Not a DOS disk";
                     moreinfo = FALSE;
                     break;

                  case ID_KICKSTART_DISK:
                     disktype = "Kickstart disk";
                     moreinfo = FALSE;
                     break;

                  case ID_DOS_DISK:
                     disktype = "   OFS  ";
                     break;

                  case ID_FFS_DISK:
                     disktype = "   FFS  ";
                     break;

                  case ID_INTER_DOS_DISK:
                     disktype = "INTL/OFS";
                     break;

                  case ID_INTER_FFS_DISK:
                     disktype = "INTL/FFS";
                     break;

                  case ID_FASTDIR_DOS_DISK:
                     disktype = " DC/OFS ";
                     break;

                  case ID_FASTDIR_FFS_DISK:
                     disktype = " DC/FFS ";
                     break;

                  case ID_MSDOS_DISK:
                     disktype = " MS-DOS ";
                     break;

                  default:
                     {
                     UWORD j;
                     UBYTE c;
                     STRPTR s;

                     strcpy(buf, "Unreadable disk (\'");
                     s = buf+strlen("Unreadable disk (\'");
                     for (j = 0; j < 4; j++)
                        {
                        c = (de->info.id_DiskType >> ((3-i)*8)) & 0xFF;
                        if (c & 0x60)
                           *s++ = c;
                        else if (c<10)
                           {
                           *s++ = '\\';
                           *s++ = c+'0';
                           }
                        else
                           *s++ = '?';
                        }
                     *s = '\0';
                     strcat(buf, "\')");
                     disktype = buf;
                     moreinfo = FALSE;
                     }
                  }

               if (moreinfo)
                  {
                  size = de->info.id_NumBlocks*de->info.id_BytesPerBlock;
                  used = de->info.id_NumBlocksUsed*de->info.id_BytesPerBlock;
                  free = size-used;

                  if (vn = (struct DosList *)BADDR(de->info.id_VolumeNode))
                     btocstr(vn->dol_Name, buf, 60);
                  else
                     buf[0] = '\0';

                  switch(de->info.id_DiskState)
                     {
                     case ID_WRITE_PROTECTED:
                        status = "Read Only ";
                        break;

                     case ID_VALIDATED:
                        status = "Read/Write";
                        break;

                     case ID_VALIDATING:
                        status = "Validating";
                        break;

                     default:
                        status = " Unknown  ";
                     }

                  Printf("%-8s %4s %5ld %s  %4s %4s %3ld%%  %3ld  %s  %s\n",
                   de->node.ln_Name,
                   itok(size, sizebuf),
                   de->info.id_BytesPerBlock,
                   disktype,
                   itok(used, usedbuf),
                   itok(free, freebuf),
                   (de->info.id_NumBlocksUsed*200+1)/de->info.id_NumBlocks/2,
                   de->info.id_NumSoftErrors,
                   status, buf);
                  }
               else
                  Printf("%-8s    %s\n", de->node.ln_Name, disktype);
               if (SetSignal(0L, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D) &
                (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D))
                  {
                  PrintFault(ERROR_BREAK, NULL);
                  rc = 20;
                  break;
                  }
               }
            FreeMem(array, sizeof(APTR)*count);
            }
         FreeArgs(rdargs);
         }
      else
         PrintFault(IoErr(), NULL);

      killlist(&dlist);

      proc->pr_WindowPtr = win;
      }
   CloseLibrary((struct Library *)DOSBase);
   CloseLibrary(UtilityBase);

   return rc;
   }

VOID btocstr(BSTR src, STRPTR dest, UWORD maxlen)
   {
   UWORD i;
   STRPTR s;

   s = BADDR(src);
   i = *s++;
   if (i>maxlen)
      i = maxlen;
   while (i--)
      *dest++ = *s++;
   *dest = '\0';
   }


VOID killlist(struct List *list)
   {
   struct entry *de;

   while (de = (struct entry *)RemTail(list))
      {
      FreeMem(de, sizeof(struct entry));
      }
   }

STRPTR itok(ULONG num, STRPTR buf)
   {
   UBYTE *exp = " KMG";
   ULONG m = 1, m2, res, mod;

   if (num<1024)
      {
      sprintf(buf, "%ld", num);
      return buf;
      }
   while ((res = (num+m/2)/m) >= 1000)
      {
      m2 = m;
      m *= 1024;
      exp++;
      }
   if (res < 11)
      {
      res = num/m;
      if (res < 1)
         {
         sprintf(buf, "1.0%lc", *exp);
         return buf;
         }
      mod = ((num/m2)%1024*10+512)/1024;
      if (mod > 9)
         {
         res++;
         mod = 0;
         }
      if (res < 10)
         {
         sprintf(buf, "%ld.%ld%lc", res, mod, *exp);
         return buf;
         }
      }
   sprintf(buf, "%ld%lc", res, *exp);

   return buf;
   }

LONG __asm cmpfunc(register __a1 struct entry *e1, register __a2 struct entry *e2)
   {
   return Stricmp(e1->node.ln_Name, e2->node.ln_Name);
   }
