//////////////////////////////////////////////////////////
//                                                      //
//           ******************************             //
//           ****  Browning's Classes  ****             //
//           ******************************             //
//                                                      //
//                  Class DL_3.Cpp                      //
//                                                      //
//       Public Domain           Roy G. Browning        //
//       December 1989           The Fulcrum's Edge     //
//                                                      //
//       Zortech C/C++           version 2.01           //
//                                                      //
//       Ztc -o -mti DL_3                               //
//                                                      //
//       Initiated               05-01-1989             //
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
//                                                      //
//                   Charity Request                    //
//                                                      //
//       If benefit is derived in any fashion from      //
//       this public code it is requested that a        //
//       donation be made to a children's charity       //
//       if sufficient funds are available.             //
//                                                      //
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
//                                                      //
//                  C++ing How Series                   //
//                                                      //
//////////////////////////////////////////////////////////

#include "DL_3.Hpp"

//////////////////////////////////////////////////////////
//                    Cmp_FileName()                    //
//                                                      //
//       Compares two filenames together for the qsort  //
//       If a numerical filename is passed it is con-   //
//       verted to an integer value for processing.     //
//////////////////////////////////////////////////////////

static int Cmp_FileName(const FIND **p1, const FIND **p2)
{
       int  k1 = atoi((*p1)->name),
            k2 = atoi((*p2)->name);

       if (k1 && k2)
              return (k1 - k2);

       return strcmp((*p1)->name, (*p2)->name);
}

//////////////////////////////////////////////////////////
//                     Cmp_FileExt()                    //
//                                                      //
//      Compares two file extensions together for       //
//      the qsort.  If they match then the filename     //
//      is used instead.                                //
//////////////////////////////////////////////////////////

inline int Cmp_FileExt(const FIND **p1, const FIND **p2)
{

       int i = strcmp(strchr((*p1)->name,'.')+one,
                      strchr((*p2)->name,'.')+one);

       if (i)
              return (i);

       Cmp_FileName(p1,p2);
}

//////////////////////////////////////////////////////////
//                    Cmp_FileDate()                    //
//                                                      //
//      Compares two file dates together for the        //
//      qsort.  If they match then the file times       //
//      are used instead.                               //
//////////////////////////////////////////////////////////

inline int Cmp_FileDate(const FIND **p1, const FIND **p2)
{
       int i = (*p1)->date - (*p2)->date;

       if (i)
              return (i);

       if ((*p1)->time > (*p2)->time)
              return one;

       if ((*p1)->time < (*p2)->time)
              return minusone;

       Cmp_FileName(p1,p2);
}

//////////////////////////////////////////////////////////
//                    Cmp_FileSize()                    //
//                                                      //
//      Compares two file sizes together for the        //
//      qsort.  If they match the file names are        //
//      used instead.                                   //
//////////////////////////////////////////////////////////

inline int Cmp_FileSize(const FIND **p1, const FIND **p2)
{
       if ((*p1)->size > (*p2)->size)
              return one;

       if ((*p1)->size < (*p2)->size)
              return minusone;

       Cmp_FileName(p1,p2);
}

//////////////////////////////////////////////////////////
//                                                      //
//            Class FileList::FileList()                //
//                                                      //
//      Constructor                                     //
//                                                      //
//      Stores the specified disk directory struct-     //
//      ures in dynamic memory accessed via an ar-      //
//      ray of pointers also allocated.  Assigns a      //
//      pointer to the structure pointer array and      //
//      reallocates to the number of returned files.    //
//                                                      //
//      char *name - Drive\Path\Pattern                 //
//                                                      //
//      int attrib - File attributes to search for      //
//                                                      //
//////////////////////////////////////////////////////////

static
FileList::FileList(const char* name,const int attrib)
{
     FIND dta;
     union REGS inregs, outregs;


     num_files = zero;
     match_numbers_allocated = zero;

     bdos(0x1A,&dta);

     inregs.x.ax = 0x4E00;
     inregs.x.dx = (int) name;
     inregs.x.cx = attrib;
     intdos(&inregs,&outregs);

     if (outregs.x.cflag) {
          num_files = error;
          return;
     }

     F = (FIND **)new FIND *[MAXFILES];

     if (!F) {
          cerr << "Error: F array not allocated!\n";
          exit(error);
     }

     for (int i = zero; !outregs.x.cflag && (i < MAXFILES); i++) {
          num_files++;
          F[i] = (FIND *)new FIND;

          if (F[i])
               *F[i] = dta;
          else {
               cerr << "Error: F[i] structure not",
                    "allocated!\n";
               exit(error);
          }

          inregs.x.ax= 0x4F00;
          intdos(&inregs,&outregs);
     }

     F = realloc(F,num_files*sizeof(FIND *));
}

//////////////////////////////////////////////////////////
//                                                      //
//             Class FileList::~FileList()              //
//                                                      //
//       Destructor for Class FileList.                 //
//                                                      //
//////////////////////////////////////////////////////////

FileList::~FileList()
{
     for (; zero < num_files--;)
          delete F[num_files];

     delete F;

     if (match_numbers_allocated)
          delete match_numbers;
}

//////////////////////////////////////////////////////////
//                                                      //
//          Class FileList::FileName_Match()            //
//                                                      //
//       Returns an array of integers that indicate     //
//       the relative offset of each matching file-     //
//       name.ext within the directory structures.      //
//                                                      //
//////////////////////////////////////////////////////////

static int *
FileList::FileName_Match(char *string) const
{
     int *num;

     match_numbers = num = new int[num_files];

     if (!match_numbers) {
          cerr << "Error: `num' array not allocated!\n";
          return NULL;
     } else {
          match_numbers_allocated = true;
     }

     int i = num_files;
     strupr(string);
     do {
          if(strstr(F[i]->name,string))
               *(num++) = i;
     } while (i--);
     *num++ = zero;

     match_numbers = realloc(match_numbers,
                            (num - match_numbers) << one);

     return match_numbers;
}
//////////////////////////////////////////////////////////
//                                                      //
//                Class FileList::Print()               //
//                                                      //
//       Prints a formated display of the directory     //
//       structures.                                    //
//                                                      //
//////////////////////////////////////////////////////////

static void
FileList::Print() const
{
       int    m,
              h;

       for (int i = zero; i < num_files; i++) {
              cout << F[i]->name << "\t";

              if (strlen(F[i]->name) < eight)
                     cout << "\t";

              cout << form("%9lu",F[i]->size) << " ";

              cout << form("%02d",
                          ((F[i]->date >> five) & 0x0F))
                   << "/"
                   << form("%02d",(F[i]->date & 0x001F))
                   << "/"
                   << form("%02d",(((F[i]->date >> nine)
                                  & 0x7F) | 80));

              m = (F[i]->time >> five) & 0x3F;
              h = (F[i]->time >> eleven) & 0x1F;
              cout << " " << form("%2d",h)
                   << ":"
                   << form("%02d",m);

              if (h > 12)
                     cout << " pm";
              else
                     cout << " am";

              cout << "\n";
       }
}

//////////////////////////////////////////////////////////
//                                                      //
//            Class FileName::FileName()                //
//                           :FileList()                //
//                                                      //
//      Constructor                                     //
//                                                      //
//      Creates an instance of FileList via its con-    //
//      structor then sorts the directory structures    //
//      in ascending order according to File Names.     //
//                                                      //
//////////////////////////////////////////////////////////

inline FileName::FileName(const char *name,const int attrib)
                :FileList(name,attrib)
{
     qsort(F, num_files, sizeof(FIND *), Cmp_FileName);
}

//////////////////////////////////////////////////////////
//                                                      //
//            Class FileSize::FileSize()                //
//                           :FileList()                //
//                                                      //
//      Constructor                                     //
//                                                      //
//      Creates an instance of FileList via its con-    //
//      structor then sorts the directory structures    //
//      in ascending order according to File Sizes.     //
//                                                      //
//////////////////////////////////////////////////////////

inline FileSize::FileSize(const char *name,const int attrib)
                :FileList(name,attrib)
{
     qsort(F, num_files, sizeof(FIND *), Cmp_FileSize);
}

//////////////////////////////////////////////////////////
//                                                      //
//            Class FileDate::FileDate()                //
//                           :FileList()                //
//                                                      //
//      Constructor                                     //
//                                                      //
//      Creates an instance of FileList via its con-    //
//      structor then sorts the directory structures    //
//      in ascending order according to File Dates.     //
//                                                      //
//////////////////////////////////////////////////////////

inline FileDate::FileDate(const char *name,const int attrib)
                :FileList(name,attrib)
{
     qsort(F, num_files, sizeof(FIND *), Cmp_FileDate);
}

//////////////////////////////////////////////////////////
//                                                      //
//            Class FileExt::FileExt()                  //
//                          :FileList()                 //
//                                                      //
//      Constructor                                     //
//                                                      //
//      Creates an instance of FileList via its con-    //
//      structor then sorts the directory structures    //
//      in ascending order according to File Ext's.     //
//                                                      //
//////////////////////////////////////////////////////////

inline FileExt::FileExt(const char *name,const int attrib)
               :FileList(name,attrib)
{
     qsort(F, num_files, sizeof(FIND *), Cmp_FileExt);
}


//////////////////////////////////////////////////////////
//                                                      //
//      First_Object() creates a FileList object        //
//      that is not sorted, the default.  Implemented   //
//      as a seperate function allowing the destructor  //
//      to be called.                                   //
//                                                      //
//////////////////////////////////////////////////////////

inline void
First_Object()
{
       FileList test1;

       if (test1.Number() != error)
              test1.Print();
       else
              cerr << "\tNo files to print.\n";
       cout << "\n";
}

//////////////////////////////////////////////////////////
//                                                      //
//      Second_Object() creates a FileName object       //
//      that is sorted by the file name.  Implemented   //
//      as a seperate function allowing the destructor  //
//      to be called.                                   //
//                                                      //
//////////////////////////////////////////////////////////

inline void
Second_Object()
{
       FileName test2;

       if (test2.Number() != error)
              test2.Print();
       else
              cerr << "\tNo files to print\n";
       cout << "\n";
}

//////////////////////////////////////////////////////////
//                                                      //
//      Third_Object() creates a FileExt object         //
//      that is sorted by the file extension.           //
//      Implemented as a seperate function allowing     //
//      the destructor to be called.                    //
//                                                      //
//////////////////////////////////////////////////////////

inline void
Third_Object()
{
       FileExt test3;

       if (test3.Number() != error)
              test3.Print();
       else
              cerr << "\tNo files to print\n";
       cout << "\n";
}

//////////////////////////////////////////////////////////
//                                                      //
//      Fourth_Object() creates a FileDate object       //
//      that is sorted by the file date and time.       //
//      Implemented as a seperate function allowing     //
//      the destructor to be called.                    //
//                                                      //
//////////////////////////////////////////////////////////

inline void
Fourth_Object()
{
       FileDate test4;

       if (test4.Number() != error)
              test4.Print();
       else
              cerr << "\tNo files to print\n";
       cout << "\n";

}


//////////////////////////////////////////////////////////
//                                                      //
//      Main() tests FileList  FileName  FileExt        //
//                   FileDate  FileSize                 //
//                                                      //
//////////////////////////////////////////////////////////

main()
{
//
// Test the sorting methods
//

     First_Object();
     Second_Object();
     Third_Object();
     Fourth_Object();

     FileSize test5("*.*",FA_DIREC | FA_HIDDEN);

     if (test5.Number() != error)
          test5.Print();
     else
          cerr << "\tNo files to print\n";
     cout << "\n";

//
// Test the FileName_Match member
//

     cout << "Testing FileSize::FileName_Match()"
          << " for \"com\".\n\n";
     FIND test; 
     int *i = test5.FileName_Match("com");
     while (*i) {
          test = test5.Element(*i);
          int j = *i++;
          cout << "test5 file " << ++j << " is: "
               << test.name << "\n";
     };

//
// Test the Element and Number members
//

     test = test5.Element(two);
     cout << "\nThird element of test5 is: " << test.name
          << "\n\nTotal number of files = "
          << test5.Number() << "\n\n";

     cout << "\nFinished\n";

     exit(zero);
}

