/*

 * dirent.c

 *

 * Derived from DIRLIB.C by Matt J. Weinstein 

 * This note appears in the DIRLIB.H

 * DIRLIB.H by M. J. Weinstein   Released to public domain 1-Jan-89

 *

 * Updated by Jeremy Bettis <jeremy@hksys.com>

 * Significantly revised and rewinddir, seekdir and telldir added by Colin

 * Peters <colin@fu.is.saga-u.ac.jp>

 *

 * Resource leaks fixed by <steve.lhomme@free.fr>

 *

 *      

 * $Revision$

 * $Author$

 * $Date$

 *

 */



#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <io.h>

#include <direct.h>

#include <dirent.h>

#include <gtchar.h>

#define SUFFIX  _T("*")

#define SLASH   _T("\\")



#include <stdio.h>



#define WIN32_LEAN_AND_MEAN

#include <windows.h>            /* for GetFileAttributes */



/*

 * opendir

 *

 * Returns a pointer to a DIR structure appropriately filled in to begin

 * searching a directory.

 */

_TDIR *
_topendir (const _TCHAR * szPath)
{

  _TDIR *nd;

  unsigned int rc;


  _TCHAR szFullPath[MAX_PATH];


  errno = 0;


  if (!szPath) {

    errno = EFAULT;

    return (_TDIR *) 0;

  }


  if (szPath[0] == _T ('\0')) {

    errno = ENOTDIR;

    return (_TDIR *) 0;

  }



  /* Attempt to determine if the given path really is a directory. */

  rc = GetFileAttributes (szPath);

  if (rc == (unsigned int) -1) {


    /* call GetLastError for more error info */

    errno = ENOENT;

    return (_TDIR *) 0;

  }

  if (!(rc & FILE_ATTRIBUTE_DIRECTORY)) {


    /* Error, entry exists but not a directory. */

    errno = ENOTDIR;

    return (_TDIR *) 0;

  }



  /* Make an absolute pathname.  */

  _tfullpath (szFullPath, szPath, MAX_PATH);



  /* Allocate enough space to store DIR structure and the complete

   * directory path given. */

  nd = (_TDIR *) malloc (sizeof (_TDIR) + (_tcslen (szFullPath) +
          _tcslen (SLASH) + _tcslen (SUFFIX) + 1) * sizeof (_TCHAR));


  if (!nd) {


    /* Error, out of memory. */

    errno = ENOMEM;

    return (_TDIR *) 0;

  }



  /* Create the search expression. */

  _tcscpy (nd->dd_name, szFullPath);



  /* Add on a slash if the path does not end with one. */

  if (nd->dd_name[0] != _T ('\0')
      && nd->dd_name[_tcslen (nd->dd_name) - 1] != _T ('/')
      && nd->dd_name[_tcslen (nd->dd_name) - 1] != _T ('\\')) {

    _tcscat (nd->dd_name, SLASH);

  }



  /* Add on the search pattern */

  _tcscat (nd->dd_name, SUFFIX);



  /* Initialize handle to -1 so that a premature closedir doesn't try

   * to call _findclose on it. */

  nd->dd_handle = -1;



  /* Initialize the status. */

  nd->dd_stat = 0;



  /* Initialize the dirent structure. ino and reclen are invalid under

   * Win32, and name simply points at the appropriate part of the

   * findfirst_t structure. */

  nd->dd_dir.d_ino = 0;

  nd->dd_dir.d_reclen = 0;

  nd->dd_dir.d_namlen = 0;


  // Added by jcsston 02/04/2004, memset was writing to a bad pointer

  nd->dd_dir.d_name = malloc (FILENAME_MAX);


  // End add

  memset (nd->dd_dir.d_name, 0, FILENAME_MAX);


  return nd;

}





/*

 * readdir

 *

 * Return a pointer to a dirent structure filled with the information on the

 * next entry in the directory.

 */

struct _tdirent *
_treaddir (_TDIR * dirp)
{

  errno = 0;



  /* Check for valid DIR struct. */

  if (!dirp) {

    errno = EFAULT;

    return (struct _tdirent *) 0;

  }


  if (dirp->dd_stat < 0) {


    /* We have already returned all files in the directory

     * (or the structure has an invalid dd_stat). */

    return (struct _tdirent *) 0;

  } else if (dirp->dd_stat == 0) {


    /* We haven't started the search yet. */

    /* Start the search */

    dirp->dd_handle = (long) _tfindfirst (dirp->dd_name, &(dirp->dd_dta));


    if (dirp->dd_handle == -1) {


      /* Whoops! Seems there are no files in that

       * directory. */

      dirp->dd_stat = -1;

    } else {

      dirp->dd_stat = 1;

    }

  } else {


    /* Get the next search entry. */

    if (_tfindnext (dirp->dd_handle, &(dirp->dd_dta))) {


      /* We are off the end or otherwise error.     

         _findnext sets errno to ENOENT if no more file

         Undo this. */

      DWORD winerr = GetLastError ();


      if (winerr == ERROR_NO_MORE_FILES)

        errno = 0;

      _findclose (dirp->dd_handle);

      dirp->dd_handle = -1;

      dirp->dd_stat = -1;

    } else {


      /* Update the status to indicate the correct

       * number. */

      dirp->dd_stat++;

    }

  }


  if (dirp->dd_stat > 0) {


    /* Successfully got an entry. Everything about the file is

     * already appropriately filled in except the length of the

     * file name. */

    dirp->dd_dir.d_namlen = (unsigned short) _tcslen (dirp->dd_dta.name);

    _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);

    return &dirp->dd_dir;

  }


  return (struct _tdirent *) 0;

}




/*

 * closedir

 *

 * Frees up resources allocated by opendir.

 */

int
_tclosedir (_TDIR * dirp)
{

  int rc;



  errno = 0;

  rc = 0;


  if (!dirp) {

    errno = EFAULT;

    return -1;

  }


  if (dirp->dd_handle != -1) {

    rc = _findclose (dirp->dd_handle);

  }


  if (dirp->dd_dir.d_name)

    free (dirp->dd_dir.d_name);



  /* Delete the dir structure. */

  free (dirp);


  return rc;

}




/*

 * rewinddir

 *

 * Return to the beginning of the directory "stream". We simply call findclose

 * and then reset things like an opendir.

 */

void
_trewinddir (_TDIR * dirp)
{

  errno = 0;


  if (!dirp) {

    errno = EFAULT;

    return;

  }


  if (dirp->dd_handle != -1) {

    _findclose (dirp->dd_handle);

  }


  dirp->dd_handle = -1;

  dirp->dd_stat = 0;

}




/*

 * telldir

 *

 * Returns the "position" in the "directory stream" which can be used with

 * seekdir to go back to an old entry. We simply return the value in stat.

 */

long
_ttelldir (_TDIR * dirp)
{

  errno = 0;


  if (!dirp) {

    errno = EFAULT;

    return -1;

  }

  return dirp->dd_stat;

}




/*

 * seekdir

 *

 * Seek to an entry previously returned by telldir. We rewind the directory

 * and call readdir repeatedly until either dd_stat is the position number

 * or -1 (off the end). This is not perfect, in that the directory may

 * have changed while we weren't looking. But that is probably the case with

 * any such system.

 */

void
_tseekdir (_TDIR * dirp, long lPos)
{

  errno = 0;


  if (!dirp) {

    errno = EFAULT;

    return;

  }


  if (lPos < -1) {


    /* Seeking to an invalid position. */

    errno = EINVAL;

    return;

  } else if (lPos == -1) {


    /* Seek past end. */

    if (dirp->dd_handle != -1) {

      _findclose (dirp->dd_handle);

    }

    dirp->dd_handle = -1;

    dirp->dd_stat = -1;

  } else {


    /* Rewind and read forward to the appropriate index. */

    _trewinddir (dirp);


    while ((dirp->dd_stat < lPos) && _treaddir (dirp));

  }

}
