/* readdir.c: */

#include <dos/dos.h>
#include <dos/dostags.h>
#include <clib/dos_protos.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unixlib.h"

#define MAX_DIRS  16

#ifdef LATTICE
#include <proto/dos.h>
#endif

static short g_module_initialized = 0;
static DIR *g_dir_array[MAX_DIRS];

static void Cleanup (void)
{
  int i;

  for (i=0; i<MAX_DIRS; i++)
    if (g_dir_array[i]) {
      if (g_dir_array[i]->lock)
        UnLock ((BPTR) g_dir_array[i]->lock);
      if (g_dir_array[i]->fib)
        FreeDosObject (DOS_FIB, (struct FileInfoBlock *) g_dir_array[i]->fib);
    }
}

static int Register_Dir (DIR *p_dir)
{
  int i;
  
  for (i=0; i<MAX_DIRS; i++)
    if (!g_dir_array[i]) {
      g_dir_array[i] = p_dir;
      return i+1;
    }
  
  return 0;
}

static void Initialize_Module (void)
{
  int i;

  g_module_initialized = 1;
  
  for (i=0; i<MAX_DIRS; i++)
    g_dir_array[i] = NULL;

  atexit (Cleanup);
}

DIR *opendir (const char *p_pathname)
{
  DIR *new = malloc (sizeof (*new));

  if (!malloc) {
    errno = ENOMEM;
    return NULL;
  }
  
  new->lock = new->fib = NULL;

  if (!(new->dir_index = Register_Dir (new))) {
    free (new);
    errno = EMFILE;
    return NULL;
  }

  if (!g_module_initialized)
    Initialize_Module ();

  new->lock = (char *) Lock ((UBYTE *) p_pathname, ACCESS_READ);
  if (!new->lock) {
    free (new);
    errno = ENOENT;
    return NULL;
  }

  new->fib = (char *) AllocDosObject (DOS_FIB, TAG_DONE);
  if (!new->fib) {
    UnLock ((BPTR) new->lock);
    g_dir_array[new->dir_index] = NULL;
    free (new);
    errno = ENOMEM;
    return NULL;
  }

  if (!Examine ((BPTR) new->lock, (struct FileInfoBlock *) new->fib) ||
       ((struct FileInfoBlock *) new->fib)->fib_DirEntryType <= 0) {
    FreeDosObject (DOS_FIB, (struct FileInfoBlock *) new->fib);
    UnLock ((BPTR) new->lock);
    g_dir_array[new->dir_index] = NULL;
    free (new);
    errno = ENOTDIR;
    return NULL;
  }

  new->phase = 0;
  return new;
}

struct dirent *readdir (DIR *p_dir_ptr)
{
  struct FileInfoBlock *fib = (struct FileInfoBlock *) p_dir_ptr->fib;

  switch (p_dir_ptr->phase) {
  case 0:
    strcpy (p_dir_ptr->dirent.d_name, ".");
    p_dir_ptr->dirent.d_reclen = 1;
    p_dir_ptr->phase++;
    return &(p_dir_ptr->dirent);
  case 1:
    strcpy (p_dir_ptr->dirent.d_name, "..");
    p_dir_ptr->dirent.d_reclen = 2;
    p_dir_ptr->phase++;
    return &(p_dir_ptr->dirent);
  default:
    if (!ExNext ((BPTR) p_dir_ptr->lock, fib)) {
      errno = ENOENT;
      return NULL;
    }

    strcpy (p_dir_ptr->dirent.d_name, fib->fib_FileName);
    p_dir_ptr->dirent.d_reclen = strlen (fib->fib_FileName);

    return &(p_dir_ptr->dirent);
  }
}

int closedir (DIR *p_dir_ptr)
{
  g_dir_array[p_dir_ptr->dir_index - 1] = NULL;
  UnLock ((BPTR) p_dir_ptr->lock);
  FreeDosObject (DOS_FIB, (struct FileInfoBlock *) p_dir_ptr->fib);
  free (p_dir_ptr);
  return 0;
}
