// dirs geschrieben von Harald Pehl
// Letze Änderung am 23.August.1993, 18:30:24
// Version 1.00
// compiliert mit 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>


// Funktionen
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 ();



// Optionen für 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;


// Klasse, in der ein File bzw. Directory festgehalten wird
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)      // Jetzt outstr je nach Parameter mit Daten auffüllen
   {
      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);
      // Wenn die Differenz der zwei Daten 0 ist, dann wird die Differenz
      // der zwei Zeitangaben zurückgegeben
   else return -1;
}


// Klasse, die die Einträge enthält
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)             // Ist ein Pattern definiert?
      if (MatchPatternNoCase (pat, fib->fib_FileName) == NULL)
         return;
   if (args.since)      // Gibt es eine since-Vorgabe
   {
      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)       // Gibt es eine upto-Vorgabe
   {
      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)   // Ist der Eintrag ein File
   {                                // und sind nur Dirs
      if (args.dirsonly)            // zugelassen...
         return;
      if (args.noinfo && strstr (fib->fib_FileName, ".info"))
         return;
   }
   else
   {
      if (args.filesonly)           // ...oder umgekehrt?
         return;
   }
   if (!args.showhidden)            // Ist das 'h'-Bit gesetzt
      if (fib->fib_Protection & FIBF_HIDDEN)
         return;
   neu = new Entry ();
   if (neu == NULL)
   {
      cout << "\nKein Speicherplatz mehr!!\n\n";
      return;
   }
   
   // Sortieren falls nötig
   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)
   {
      // und endlich einfügen
      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;
}

// Auffüllen eines Stringes mit einem Zeichen von rechts
void strrfil (char *s, char *t, const char c, int n)
{
   int len = strlen (t);
   
   while (n-- > len)
      *s++ = c;
   while (*s++ = *t++)
      ;
}

// Auffüllen eines Stringes mit einem Zeichen von links
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;   // Zwei Listen anlegen, eine für
                                       // Dirs eine für 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 ()))   // steht was in den Listen?
      {
         if (!args.nohead)
         {
            cout << '\n';
            if (!file)
            {
               if (*name)
                  cout << "Verzeichnis: \x9b""1m" << name << "\x9b""0m\n\n";
               else
                  cout << "Aktuelles Verzeichnis:\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 << " Verzeichnis" << (dl->count==1 ? "" : "se");
            if (fl->count)
            {
               if (dl->count)
                  cout << args.headform;
               cout << fl->count << " Datei" << (fl->count==1 ? "" : "en")
                    << args.headform << fl->bytes << " Bytes benutzt";
            }
            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;40mOptionen:\x9b""0m " << template << "\n\n";
   cout << "In den Klammern ist die jeweilige Grund-Einstellung angegeben\n";
   cout << "ARGS         = Verzeichnis(se) die aufgelistet werden\n"
           "CLI          = Parameter NUR übers CLI einlesen  (aus)\n"
           "SINCE        = Dateien seit einem Datum          (leer)\n"
           "UPTO         = Dateien bis zu einem Datum        (leer)\n"
           "(-)ALPHASORT = Nach Namen sortieren A-Z          (unsortiert)\n"
           "(-)SIZESORT  = Nach Größe sortieren 10,20,30...  (unsortiert)\n"
           "(-)DATESORT  = Nach Datum sortieren 1.1.78-heute (unsortiert)\n"
           "QUICK        = Nur den Dateinamen ausgeben       (aus)\n"
           "DIRSONLY     = Nur Directories ausgeben          (aus)\n"
           "FILESONLY    = Nur Files ausgeben                (aus)\n"
           "FILESFIRST   = Files vor Directories ausgeben    (aus)\n"
           "SHOWHIDDEN   = Versteckte Dateien mitanzeigen    (aus)\n"
           "NOHEAD       = Keine Kopf- und Fußzeile          (aus)\n"
           "NOINFO       = Keine .info - Dateien ausgeben    (aus)\n"
           "NOSIZE       = Keine Größe ausgeben              (aus)\n"
           "NOPROTECT    = Keine Schutzbits ausgeben         (aus)\n"
           "NODATE       = Kein Datum ausgeben               (aus)\n"
           "NOCOMMENT    = Kein Kommentar  ausgeben          (aus)\n"
           "VAR          = Variable, die ausgelesen wird     (aus)\n"
           "HEADFORM     = Zeichenkette in der Fußzeile      (<»--«>)\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)       // Eine ENV-Variable wird eingelesen
   {
      if (args.var)
      {                                      // var in richtige Form bringen...
         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)   // ...und einlesen
      {
         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;
   // Strukturen anlegen und Speicher allokieren, falls erforderlich
   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;    // Dirs und Files in argv kopieren
   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 << "\nUngültiger Dateiname: \'" << name << "\' !!\n\n";
         argv++;
         delete [] pat;
         pat = NULL;
      } while (*argv);
   }  
   close_all (NULL, 0);
}

