#define INCL_DOSFILEMGR
#define INCL_DOSERRORS
#include <os2.h>

#include <string.h>
#include <time.h>
 
unsigned long dosToUnix( unsigned short d, unsigned short t )
{
  struct tm tt;

  tt.tm_sec   = (((FTIME *)&t)->twosecs)*2;
  tt.tm_min   = ((FTIME *)&t)->minutes;
  tt.tm_hour  = ((FTIME *)&t)->hours;
  tt.tm_mday  = ((FDATE *)&d)->day;
  tt.tm_mon   = ((FDATE *)&d)->month-1;
  tt.tm_year  = (((FDATE *)&d)->year)+80;
  tt.tm_wday  = 0;
  tt.tm_yday  = 0;
  tt.tm_isdst = 0;

  return mktime( &tt );
}

#include "direct.h"

Directory::Directory( char *searchMask, SearchAttributes attr,
                      SearchEA iniEa ) :
  searchAttrib( attr ),
  done( 0 ),
  handle( (HDIR)HDIR_CREATE ),
  ea( iniEa )
{
  bufSize = ea==NeedEASize ? sizeof( FILEFINDBUF4 ) : sizeof( FILEFINDBUF3 );
  buf = ea==NeedEASize ? (void *) new FILEFINDBUF4 : (void *) new FILEFINDBUF3;
  mask = new char [ strlen( searchMask )+1 ];
  strcpy( mask, searchMask );

  char *ptr = mask,
       *cur = mask;

  for( ; *ptr; ptr++ )
    if( *ptr==':' || *ptr=='\\' || *ptr=='/' )
      cur = ptr;

  if( cur==mask )
  {
    static char no[] = "./";

    path = new char [ sizeof(no) ];
    strcpy( path, no );
  }
  else
  {
    path = new char [ cur+2-mask ];
    strncpy( path, mask, cur+1-mask );
    path[ cur+1-mask ] = 0;
  }

  startSearch();
}

void Directory::closeHandle()
{
  if( handle!=(HDIR)HDIR_CREATE )
    DosFindClose( handle );

  handle = (HDIR)HDIR_CREATE;
}

void Directory::processEntry( int rc )
{
  if( rc )
  {
    done = 1;
    *name = 0;
    cdate = 0;
    ctime = 0;
    mdate = 0;
    mtime = 0;
    adate = 0;
    atime = 0;
    aunix = 0;
    munix = 0;
    cunix = 0;
    size = 0;
    EAsize = 0;
    attributes = 0;
    return;
  }

  done = 0;

  if( ea==NeedEASize )
  {
    strncpy( name, ((PFILEFINDBUF4)buf)->achName, sizeof(name)-1 );
    cdate  = *(USHORT *)&((PFILEFINDBUF4)buf)->fdateCreation;
    ctime  = *(USHORT *)&((PFILEFINDBUF4)buf)->ftimeCreation;
    mdate  = *(USHORT *)&((PFILEFINDBUF4)buf)->fdateLastWrite;
    mtime  = *(USHORT *)&((PFILEFINDBUF4)buf)->ftimeLastWrite;
    adate  = *(USHORT *)&((PFILEFINDBUF4)buf)->fdateLastAccess;
    atime  = *(USHORT *)&((PFILEFINDBUF4)buf)->ftimeLastAccess;
    size   = (ULONG)((PFILEFINDBUF4)buf)->cbFile;
    EAsize = (ULONG)((PFILEFINDBUF4)buf)->cbList;
  }
  else
  {
    strncpy( name, ((PFILEFINDBUF3)buf)->achName, sizeof(name)-1 );
    cdate = *(USHORT *)&((PFILEFINDBUF3)buf)->fdateCreation;
    ctime = *(USHORT *)&((PFILEFINDBUF3)buf)->ftimeCreation;
    mdate = *(USHORT *)&((PFILEFINDBUF3)buf)->fdateLastWrite;
    mtime = *(USHORT *)&((PFILEFINDBUF3)buf)->ftimeLastWrite;
    adate = *(USHORT *)&((PFILEFINDBUF3)buf)->fdateLastAccess;
    atime = *(USHORT *)&((PFILEFINDBUF3)buf)->ftimeLastAccess;
    size  = (ULONG)((PFILEFINDBUF3)buf)->cbFile;
    EAsize = 0;
  }

  name[ sizeof(name)-1 ] = 0;
  attributes = (USHORT)((PFILEFINDBUF3)buf)->attrFile;
  munix = dosToUnix( mdate, mtime );
  aunix = (adate && atime) ? dosToUnix( adate, atime ) : munix; //Fuckin FAT
  cunix = (cdate && ctime) ? dosToUnix( cdate, ctime ) : munix; //Fuckin FAT
  totalFiles++;
  totalSize += size;
  totalEASize += EAsize;
}

void Directory::startSearch()
{
  totalFiles = 0;
  totalSize = 0;
  totalEASize = 0;

  ULONG need( 1 );
  APIRET rc( DosFindFirst( (PSZ)mask, (PHDIR)&handle, (ULONG)searchAttrib,
                           buf, bufSize, &need, 
                           ea==NeedEASize ? FIL_QUERYEASIZE : FIL_STANDARD ) );

  if( rc )
    if( rc==ERROR_EAS_DIDNT_FIT )
      rc = NO_ERROR;
    else
      handle = (HDIR)HDIR_CREATE;

  processEntry( need!=1 || rc );
}

void Directory::nextEntry()
{
  if( done || handle==(HDIR)HDIR_CREATE )
    processEntry( 1 );
  else
  {
    ULONG need( 1 );
    APIRET rc( DosFindNext( handle, buf, bufSize, &need ) );

    if( rc==ERROR_EAS_DIDNT_FIT )
      rc = NO_ERROR;

    processEntry( need!=1 || rc );
  }
}

char *Directory::getFSPoint() const
{
  if( path==NULL )
    return NULL;

  char *ret( new char [ strlen(path)+strlen(name)+1 ] );

  strcpy( ret, path );
  strcat( ret, name );
  return ret;
}


#ifdef TestDirectoryClass

#include <stdio.h>

void main()
{
  Directory i( "./*",
               SearchAttributes( MayBeArchived|MayBeHidden|MayBeReadOnly|MayBeSystem ),
               NeedEASize );

  printf( "\nDirectory of %s\n\n", i.getPath() );

  for( ; i; i++ )
  {
    time_t tt( i.getUNIXModify() );
    char *t = ctime( &tt );
    t[ strlen(t)-1 ] = 0;

    printf( "%8lu %04x %5lu %s %s\n",
            i.getSize(), i.getAttrib(), i.getEASize(), 
            t,
            i.getName() );
  }

  printf( "\nTotal %lu bytes and %lu EA bytes in %d files\n\n",
          i.getTotalSize(), i.getTotalEASize(), i.getTotalFiles() );
}

#endif