// dirs written by Harald Pehl
// Last change 28-Aug-93, 18:35:16
// Version 1.00
// compiled with MaxonC++


// Includes
#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#include <dos/dosextens.h>
#include <dos/datetime.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <stream.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>


// Functions
void close_all (const char *, const int);
char *itoa (int);
void strrfil (char *, char *, const char, int);
void strlfil (char *, char *, const char, int);
BOOL readdir (char *);
void usage ();



// Options for ReadArgs
char *template = "ARGS/M,C=CLI/S,S=SINCE/K,U=UPTO/K,AS=ALPHASORT/S,"\
                 "-AS=-ALPHASORT/S,SS=SIZESORT/S,-SS=-SIZESORT/S,"\
                 "DS=DATESORT/S,-DS=-DATESORT/S,Q=QUICK/S,DO=DIRSONLY/S,"\
                 "FO=FILESONLY/S,FF=FILESFIRST/S,SH=SHOWHIDDEN/S,NH=NOHEAD/S,"\
                 "NI=NOINFO/S,NS=NOSIZE/S,NP=NOPROTECT/S,ND=NODATE/S,"\
                 "NC=NOCOMMENT/S,V=VAR/K,HEADFORM/K,?=HELP/S";

struct Def
{
   char **argv;
   long cli;
   char *since;
   char *upto;
   long alphasort;
   long alphasortdown;
   long sizesort;
   long sizesortdown;
   long datesort;
   long datesortdown;
   long quick;
   long dirsonly;
   long filesonly;
   long filesfirst;
   long showhidden;
   long nohead;
   long noinfo;
   long nosize;
   long noprotect;
   long nodate;
   long nocomment;
   char *var;
   char *headform;
   long help;
} args = {NULL,  FALSE, FALSE, FALSE, NULL,  NULL,  FALSE, FALSE,
          FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
          FALSE, FALSE, FALSE, FALSE, FALSE, NULL,  "  <»--«>  ", FALSE};



struct RDArgs *rda = NULL;
struct FileInfoBlock *fib = NULL;
BOOL sincedate = TRUE, uptodate = TRUE;
struct DateTime *dat = NULL, *since = NULL, *upto = NULL;
char strdate[LEN_DATSTRING], strtime[LEN_DATSTRING], *pat = NULL;


// Class, in which one file or directory is stored
class Entry
{
   Entry *prev, *next;
   BOOL dir;
   long size, days, mins;
   char name[108], outstr[116];
   
   Entry ();
   int compare (const Entry *) const;
   friend class Lst;
};


Entry::Entry ()
{
   char *s = outstr;
   
   prev = next = NULL;
   dir  = fib->fib_DirEntryType > 0 ? TRUE : FALSE;
   size = fib->fib_Size;
   days = fib->fib_Date.ds_Days;
   mins = fib->fib_Date.ds_Minute;
   strcpy (name, fib->fib_FileName);
   memset (s, 0, 116);
   if (args.quick)      // Now fill outstr with datas according to parameters
   {
      strcpy (outstr, name);
      return;
   }
   strlfil (s, name, ' ', 25);
   s += 24;
   if (!args.nosize)
   {
      if (dir)
      {
         strncpy (s, "  (dir)   ", 11);
         s += 10;
      }
      else
      {
         strrfil (s, itoa (size), ' ', 7);
         strcat (s, "    "); 
         s += 10;
      }
   }
   if (!args.noprotect)
   {
      int i, j;
      char p[] = "hsparwed";
      
      for (i = 128, j = 0 ; i >= 16 ; i >>= 1, j++, s++)
         if (fib->fib_Protection & i)
            *s = p[j];
         else
            *s = '-';
      for (i = 8, j = 4 ; i >= 1 ; i >>= 1, j++, s++)
         if (fib->fib_Protection & i)
            *s = '-';
         else
            *s = p[j];
      strcat (s, "    ");
      s += 3;
   }
   if (!args.nodate)
   {
      dat->dat_Stamp = fib->fib_Date;
      if (DateToStr (dat))
      {
         strlfil (s, dat->dat_StrDate, ' ', 11);
         s += 10;
         strlfil (s, dat->dat_StrTime, ' ', 12);
         s += 11;
      }
   }
   if (!args.nocomment)
   {
      if (*fib->fib_Comment != '\0')
      {
         strcat (s, "\n    ");
         s += 4;
         strncat (s, fib->fib_Comment, 40);
      }
   }
}


int Entry::compare (const Entry *that) const
{
   if (args.alphasort || args.alphasortdown)
      return stricmp (name, that->name);
   else if (args.sizesort || args.sizesortdown)
      return size - that->size;
   else if (args.datesort || args.datesortdown)
      return (days - that->days) ? (days - that->days) : (mins - that->mins);
      // If the difference of the two dates is 0, then give back the difference
      // of the two times
   else return -1;
}


// Class, in which the entries are stored
class Lst
{
   Entry *first, *last;
   
   public:
   int count, bytes;
   
   Lst ();
   ~Lst ();
   void insert ();
   void ausgabe () const;
   BOOL empty () const;
};


Lst::Lst ()
{
   count = bytes = 0;
   first = last = NULL;
}


Lst::~Lst ()
{
   Entry *e = first;
   
   while (e)
   {
      Entry *hilf = e;
      e = e->next;
      delete hilf;
   }
}


void Lst::insert ()
{
   BOOL sort = FALSE;
   Entry *pos = first, *neu = NULL;
   
   if (pat)             // Is there a pattern?
      if (MatchPatternNoCase (pat, fib->fib_FileName) == NULL)
         return;
   if (args.since)      // Is there a since-option
   {
      if (sincedate)
      {
         if (fib->fib_Date.ds_Days < since->dat_Stamp.ds_Days)
            return;
      }
      else
      {
         if (fib->fib_Date.ds_Minute < since->dat_Stamp.ds_Minute)
            return;
      }
   }
   if (args.upto)       // Is ther a upto-option
   {
      if (uptodate)
      {
         if (fib->fib_Date.ds_Days > upto->dat_Stamp.ds_Days)
            return;
      }
      else
      {
         if (fib->fib_Date.ds_Minute > upto->dat_Stamp.ds_Minute)
            return;
      }
   }
   if (fib->fib_DirEntryType < 0)   // Is the entry a file
   {                                // and defined the user
      if (args.dirsonly)            // dirsonly...
         return;
      if (args.noinfo && strstr (fib->fib_FileName, ".info"))
         return;
   }
   else
   {
      if (args.filesonly)           // ...or visa versa ?
         return;
   }
   if (!args.showhidden)            // Is the 'h'-flag set
      if (fib->fib_Protection & FIBF_HIDDEN)
         return;
   neu = new Entry ();
   if (neu == NULL)
   {
      cout << "\nNo memory!!\n\n";
      return;
   }
   
   // if neccesary, sort the entries 
   if (args.alphasort || args.sizesort || args.datesort)
   {
      while (pos && neu->compare (pos) > 0)
         pos = pos->next;
      sort = TRUE;
   }
   else if (args.alphasortdown || args.sizesortdown || args.datesortdown)
   {
      while (pos && neu->compare (pos) < 0)
         pos = pos->next;
      sort = TRUE;
   }
   if (sort)
   {
      if (pos == NULL)
      {
         Entry *alt = last;

         if (alt != NULL)
         {
            alt->next = neu;
            neu->prev = alt;
            last = neu;
         }
         else
         {
            first = neu;
            last = neu;
         }
      }
      else
      {
         if (pos->prev == NULL)
         {
            pos->prev = neu;
            neu->next = pos;
            first = neu;
         }
         else
         {
            Entry *vor = pos->prev;
            
            vor->next = neu;
            neu->prev = vor;
            neu->next = pos;
            pos->prev = neu;
         }
      }
   }
   else
   {
      Entry *alt = last;
   
      if (alt)
      {
         alt->next = neu;
         neu->prev = alt;
         last = neu;
      }
      else
      {
         first = neu;
         last = neu;
      }
   }
   count++;
   if (!neu->dir)
      bytes += neu->size;
   return;
}


void Lst::ausgabe () const
{
   for (Entry *e = first ; e ; e = e->next)
      cout << "   " << e->outstr << "\n";
}


BOOL Lst::empty () const
{
   return first || last ? FALSE : TRUE;
}



void close_all (const char *why, const int how)
{
   if (why)    cout << why << "\n\n";
   if (pat)    delete [] pat;
   if (fib)    FreeDosObject (DOS_FIB, (APTR)fib);
   if (upto)   FreeVec ((APTR)upto);
   if (since)  FreeVec ((APTR)since);
   if (dat)    FreeVec ((APTR)dat);
   if (rda)    FreeArgs (rda);
   exit (how);
}


char *itoa (int zahl)
{
   static char res[10];
   char c, *dest = res;
   int maxpot = 1000000, flag = 0;
   
   while (maxpot >= 1)
   {
      c = zahl / maxpot + '0';
      if ((c != '0') || (maxpot == 1) || flag)
      {
         flag = 1;
         *dest++ = c;
      }
      zahl %= maxpot;
      maxpot /= 10;
   }
   *dest = '\0';
   return res;
}

// Fill a string with characters. Start from right
void strrfil (char *s, char *t, const char c, int n)
{
   int len = strlen (t);
   
   while (n-- > len)
      *s++ = c;
   while (*s++ = *t++)
      ;
}

// Fill a string with characters. Start from left
void strlfil (char *s, char *t, const char c, int n)
{
   while ((n-- > 0) && (*s++ = *t++))
      ;
   s--;
   while (n-- > 0)
      *s++ = c;
}


BOOL readdir (char *name)
{
   BOOL file = FALSE;
   BPTR lock = NULL, l = NULL;
   Lst *dl = new Lst, *fl = new Lst;   // Create two lists, one for
                                       // Directoriess and one for files
   if (lock = Lock (name, ACCESS_READ))
   {
      Examine (lock, fib);
      if (fib->fib_DirEntryType < 0)
      {
         file = TRUE;
         fl->insert ();
      }
      else
      {
         while (ExNext (lock, fib) == DOSTRUE)
         {
            if (fib->fib_DirEntryType < 0)
               fl->insert ();
            else
               dl->insert ();
         }
      }
      if ((!fl->empty ()) || (!dl->empty ()))   // is there something in the lists?
      {
         if (!args.nohead)
         {
            cout << '\n';
            if (!file)
            {
               if (*name)
                  cout << "Directory: \x9b""1m" << name << "\x9b""0m\n\n";
               else
                  cout << "Current Directory:\n\n";
            }
         }
         if (args.filesfirst)
         {
            fl->ausgabe ();
            dl->ausgabe ();
         }
         else
         {
            dl->ausgabe ();
            fl->ausgabe ();
         }
         if (!args.nohead)
         {
            cout << "\n";
            if (dl->count)
               cout << dl->count << " Director" << (dl->count==1 ? "y" : "ies");
            if (fl->count)
            {
               if (dl->count)
                  cout << args.headform;
               cout << fl->count << " File" << (fl->count==1 ? "" : "s")
                    << args.headform << fl->bytes << " bytes used";
            }
            cout << "\n\n";
         }
      }
      delete dl;
      delete fl;
      UnLock (lock);
      return TRUE;
   }
   return FALSE;
}


void usage ()
{
   cout << "\x9b""32;40m\x9b""1mDirs © by  Harald Pehl in 1993\n";
   cout << "\x9b""31;40mOptions:\x9b""0m " << template << "\n\n";
   cout << "The value in brackets is the default-value for the argument\n";
   cout << "ARGS         = Directory(ies) which are displayed\n"
           "CLI          = Read arguments ONLY via CLI        (FALSE)\n"
           "SINCE        = Files since a date                 (empty)\n"
           "UPTO         = Files upto a date                  (empty)\n"
           "(-)ALPHASORT = Sort by name  A-Z                  (unsorted)\n"
           "(-)SIZESORT  = Sort by size 10,20,30...           (unsorted)\n"
           "(-)DATESORT  = Sort by date 1.1.78-today          (unsorted)\n"
           "QUICK        = Display only the name              (FALSE)\n"
           "DIRSONLY     = Display only directories           (FALSE)\n"
           "FILESONLY    = Display only files                 (FALSE)\n"
           "FILESFIRST   = Display files before directories   (FALSE)\n"
           "SHOWHIDDEN   = Display hidden files               (FALSE)\n"
           "NOHEAD       = No statusbar                       (FALSE)\n"
           "NOINFO       = Don\'t display .info - Files       (FALSE)\n"
           "NOSIZE       = Don\'t display the size            (FALSE)\n"
           "NOPROTECT    = Don\'t display the protection-bits (FALSE)\n"
           "NODATE       = Don\'t display the date            (FALSE)\n"
           "NOCOMMENT    = Don\'t display the comment         (FALSE)\n"
           "VAR          = Variable which will be read in     (FALSE)\n"
           "HEADFORM     = String in the statusbar            (<»--«>)\n\n";
   close_all (NULL, 0);
}


void main (int argc, char **argv)
{
   BPTR lock = NULL;
   char name[108], str[256], var[108];
   
   rda = ReadArgs (template, (LONG *)&args, NULL);
   if (args.help)
      usage ();
   if (!args.cli)       // Read in an ENV-variable
   {
      if (args.var)
      {
         if (strstr (args.var, "ENV:"))
            strcpy (var, args.var);
         else
         {
            strcpy (var, "ENV:\0");
            strcat (var, args.var);
         }
      }
      else
         strcpy (var, "ENV:DIRCMD\0");
      if (GetVar (var, str, 256, 0) != -1)
      {
         struct RDArgs *env = NULL;
   
         if (env = (struct RDArgs *)AllocDosObject (DOS_RDARGS, NULL))
         {
            env->RDA_Source.CS_Buffer = str;
            env->RDA_Source.CS_Length = strlen (str);
            env->RDA_Source.CS_CurChr = 0;
            env->RDA_DAList  = NULL;
            env->RDA_Buffer  = NULL;
            env->RDA_BufSiz  = 0;
            env->RDA_ExtHelp = NULL;
            env->RDA_Flags   = 0;
            rda = ReadArgs (template, (LONG *)&args, env);
            FreeDosObject (DOS_RDARGS, (APTR)env);
         }
      }
   }
   if (args.quick)
      args.nohead = args.nosize = args.noprotect = args.nodate = args.nocomment = TRUE;
   // Allocate memory and define structs, if neccesary
   if (!args.nodate)
   {
      if (! (dat = (struct DateTime *)AllocVec (sizeof (struct DateTime), MEMF_CLEAR)))
         close_all ("Kein Speicherplatz mehr", 10);
      dat->dat_Stamp.ds_Days   = 0;
      dat->dat_Stamp.ds_Minute = 0;
      dat->dat_Stamp.ds_Tick   = 0;
      dat->dat_Format  = FORMAT_DOS;
      dat->dat_Flags   = DTF_SUBST;
      dat->dat_StrDay  = NULL;
      dat->dat_StrDate = strdate;
      dat->dat_StrTime = strtime;
   }
   if (args.since)
   {
      if (! (since = (struct DateTime *)AllocVec (sizeof (struct DateTime), MEMF_CLEAR)))
         close_all ("Kein Speicherplatz mehr", 10);
      since->dat_Stamp.ds_Days   = 0;
      since->dat_Stamp.ds_Minute = 0;
      since->dat_Stamp.ds_Tick   = 0;
      since->dat_Format = FORMAT_DOS;
      since->dat_Flags  = DTF_SUBST;
      since->dat_StrDay = NULL;
      if (strchr (args.since, ':'))
      {
         sincedate = FALSE;
         since->dat_StrDate = NULL;
         since->dat_StrTime = args.since;
      }
      else
      {
         since->dat_StrDate = args.since;
         since->dat_StrTime = NULL;
      }
      StrToDate (since);
   }
   if (args.upto)
   {
      if (! (upto = (struct DateTime *)AllocVec (sizeof (struct DateTime), MEMF_CLEAR)))
         close_all ("Kein Speicherplatz mehr", 10);
      upto->dat_Stamp.ds_Days   = 0;
      upto->dat_Stamp.ds_Minute = 0;
      upto->dat_Stamp.ds_Tick   = 0;
      upto->dat_Format = FORMAT_DOS;
      upto->dat_Flags  = DTF_SUBST;
      upto->dat_StrDay = NULL;
      if (strchr (args.since, ':'))
      {
         uptodate = FALSE;
         upto->dat_StrDate = NULL;
         upto->dat_StrTime = args.upto;
      }
      else
      {
         upto->dat_StrDate = args.upto;
         upto->dat_StrTime = NULL;
      }
      StrToDate (upto);
   }
   if (! (fib = (struct FileInfoBlock *)AllocDosObject (DOS_FIB, NULL)))
      close_all ("Kein Speicherplatz mehr", 10);
   argv = args.argv;    // Copy dirs and files to argv
   if (*argv == NULL)
   {
      GetCurrentDirName (name, 108);
      readdir (name);
   }
   else
   {
      do
      {
         if (strstr (*argv, "#?") || strchr (*argv, '?'))
         {
            char *s = *argv;
            int len = strlen (*argv) * 3;
            
            pat = new char [len];
            *argv = FilePart (*argv);
            if (s != *argv)
            {
               strncpy (name, s, *argv-s);
               name[*argv-s] = '\0';
            }
            else
               GetCurrentDirName (name, 108);
            ParsePatternNoCase (*argv, pat, len);
            lock = Lock (name, ACCESS_READ);
         }
         else
            strcpy (name, *argv);
         if (!readdir (name))
            cout << "\nFile/Directory not found: \'" << name << "\' !!\n\n";
         argv++;
         delete [] pat;
         pat = NULL;
      } while (*argv);
   }  
   close_all (NULL, 0);
}

