#include <libraries/dosextens.h>
#include "reqdir.h"

#define ALIGN(X) ((X+1)&~1)
#define DaysInYear(year) ((year%4) ? 365 : 366)

/* Array of entry pointers */
static Entry **ent;
static ULONG num_entries, max_entries;
static char *buffer_max;

extern struct Lock *Lock();
extern short Examine();
extern short ExNext();
extern long IoErr();
extern char *strcpy();
extern void UnLock();
extern void *AllocMem();


static void put_date(e,fib)
register Entry *e;
struct FileInfoBlock *fib;
{
register UWORD year, month;
register long d;
static UWORD days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

/* figure out what year */
for( year = 1978, d = fib->fib_Date.ds_Days; d > DaysInYear(year);)
   { d -= DaysInYear(year); year++; }

/* there are 29 days in February in a leap year */
days[1] = (year % 4) ? 28 : 29;

/* figure out what month */
for( month = 0; d > days[month]; month++)
   d -= days[month];

e->month = month+1;
e->day = d+1;
e->year = year - 1978;
}


/* adds a directory entry to the buffer */

static char *add_entry(curr,fib)
register char *curr;
register struct FileInfoBlock *fib;
{
register Entry *e;
register char *next;
register UWORD len;

len = ALIGN(strlen(fib->fib_FileName) + 1);

/* check for space in the buffer */
if( (next = curr + (sizeof(Entry) + len*sizeof(char)) )  > buffer_max )
   return(0L);

e = (Entry *)curr;
e->size = len + sizeof(Entry);

strcpy(EntryName(e),fib->fib_FileName);

if( !(e->dir = ((fib->fib_EntryType > 0) ? 1 : 0)) )
   {
   e->archive = (fib->fib_Protection & FIBF_ARCHIVE) ? 0 : 1;
   e->read    = (fib->fib_Protection & FIBF_READ)    ? 0 : 1;
   e->write   = (fib->fib_Protection & FIBF_WRITE)   ? 0 : 1;
   e->execute = (fib->fib_Protection & FIBF_EXECUTE) ? 0 : 1;
   e->delete  = (fib->fib_Protection & FIBF_DELETE)  ? 0 : 1;
   }

put_date(e,fib);

if( num_entries < max_entries )
   ent[num_entries] = e;
else return(0L);

return(next);
}


static short comp(i,j)
register ULONG i, j;
{
extern short strcmpa();

if( ent[i]->dir != ent[j]->dir )
   return( (short)(ent[j]->dir - ent[i]->dir) );
return( strcmpa(EntryName(ent[i]),EntryName(ent[j])) );
}

static void swap(i,j)
register ULONG i, j;
{
register Entry *e;
e = ent[i];
ent[i] = ent[j];
ent[j] = e;
}

/* Returns the number of directory entries found or an error */

long reqdir(name,buffer,bufsize,entries,maxent)
char *name;        /* => The directory to lock first */
char *buffer;      /* => The buffer for the directory entries */
ULONG bufsize;      /* => The size of the buffer for dir entries */
Entry *entries[];  /* <= A sorted array of directory entry pointers */
ULONG maxent;       /* => The maximum number of directory entries */
{
register struct Lock *lock;
register char *max = buffer + (bufsize - 1);
register char *curr = buffer;
register struct FileInfoBlock *fib;
short error;

/* Assign some global variables so the sorting functions
 * can get at them */
ent = entries;
max_entries = maxent;
buffer_max = max;

if( !(lock = Lock(name,(long)ACCESS_READ)) )
   return(-1L);

if( !(fib = AllocMem((long)sizeof(struct FileInfoBlock),(long)MEMF_CLEAR)) )
   {
   UnLock(lock);
   return(-1L);
   }
if( !Examine(lock, fib) )
   {
   UnLock(lock);
   return(-1L);
   }
if( fib->fib_DirEntryType <= 0 )
   {
   FreeMem(fib,(long)sizeof(struct FileInfoBlock));
   UnLock(lock);
   return(-2L);
   }

/* always add the parent directory to the list of entries */
num_entries = 0L;
fib->fib_EntryType = 1;
strcpy(fib->fib_FileName,"/");
curr = add_entry(curr,fib);


for(num_entries = 1L;;)
   {
   if( !ExNext(lock,fib) )
      if( IoErr() == ERROR_NO_MORE_ENTRIES )
         break;
   if( !(curr = add_entry(curr,fib)) )
      break;
   num_entries++;
   }

UnLock(lock);

/* Sort the directory entries */
if( num_entries )
   quicksort(num_entries,comp,swap);

FreeMem(fib,(long)sizeof(struct FileInfoBlock));

return((long)num_entries);
}


/* This is need to convert the stupid BCPL strings */
UBYTE *bstrcpy(dest,src)
register UBYTE *dest;
BSTR *src;
{
register char *string;
register UWORD length, i;

string = (char *) BADDR(src);
length = *string++;

for( i = 0; i < length; i++)
   dest[i] = string[i];

dest[length] = '\0';

return(dest);
}


void init_dir(path,ac)
UBYTE *path;
short ac;
{
extern struct Task *FindTask();

register struct Process *me;

me = (struct Process *)FindTask(0L);

/* find the current directrory */
if( ac )  /* startup from the CLI */
   {
   register struct CommandLineInterface *cli =
      (struct CommandLineInterface *) BADDR(me->pr_CLI);
   bstrcpy(path,cli->cli_SetName);
   }
else path[0] = '\0';

}

