/*
 * CRTDLL drive/directory functions
 *
 * Copyright 1996,1998 Marcus Meissner
 * Copyright 1996 Jukka Iivonen
 * Copyright 1997,2000 Uwe Bonnes
 * Copyright 2000 Jon Griffiths
 *
 *
 * Implementation Notes:
 * MT Safe.
 */

#include "crtdll.h"
#include <errno.h>

#include "ntddk.h"
#include <time.h>

#include <string.h>
#include <ctype.h>                      /* toupper proto */

DEFAULT_DEBUG_CHANNEL(crtdll);

/* INTERNAL: Translate find_t to PWIN32_FIND_DATAA */
static void __CRTDLL__fttofd(LPWIN32_FIND_DATAA fd, find_t* ft);
static void __CRTDLL__fttofd(LPWIN32_FIND_DATAA fd, find_t* ft)
{
  DWORD dw;

  /* Tested with crtdll.dll Version 2.50.4170 (NT) from win98 SE:
   * attrib 0x80 (FILE_ATTRIBUTE_NORMAL)is returned as 0.
   */
  if (fd->dwFileAttributes  == FILE_ATTRIBUTE_NORMAL)
    ft->attrib = 0;
  else
    ft->attrib = fd->dwFileAttributes;

  RtlTimeToSecondsSince1970( &fd->ftCreationTime, &dw );
  ft->time_create = dw;
  RtlTimeToSecondsSince1970( &fd->ftLastAccessTime, &dw );
  ft->time_access = dw;
  RtlTimeToSecondsSince1970( &fd->ftLastWriteTime, &dw );
  ft->time_write = dw;
  ft->size = fd->nFileSizeLow;
  strcpy(ft->name, fd->cFileName);
}


/*********************************************************************
 *                  _chdir           (CRTDLL.51)
 *
 * Change the current directory.
 *
 * PARAMS
 *   newdir [in] Directory to change to
 *
 * RETURNS
 * Sucess:  0
 *
 * Failure: -1
 */
INT CDECL CRTDLL__chdir(LPCSTR newdir)
{
  dprintf(("CRTDLL: _chdir(%s)\n",
          newdir));

  if (!SetCurrentDirectoryA(newdir))
  {
    __CRTDLL__set_errno(newdir?GetLastError():0);
    return -1;
  }
  return 0;
}


/*********************************************************************
 *                  _chdrive           (CRTDLL.52)
 *
 * Change the current drive.
 *
 * PARAMS
 *   newdrive [in] new drive to change to, A: =1, B: =2, etc
 *
 * RETURNS
 * Sucess:  0
 *
 * Failure: 1
 */
BOOL CDECL CRTDLL__chdrive(INT newdrive)
{
  char buffer[3] = "A:";
  buffer[0] += newdrive - 1;

  dprintf(("CRTDLL: _chdrive(%s)\n",
          buffer));

  if (!SetCurrentDirectoryA( buffer ))
  {
    __CRTDLL__set_errno(GetLastError());
    if (newdrive <= 0)
      CRTDLL_errno = EACCES;
    return -1;
  }
  return 0;
}


/*********************************************************************
 *                  _findclose     (CRTDLL.098)
 *
 * Free the resources from a search handle created from _findfirst.
 *
 * PARAMS
 *   hand [in]: Search handle to close
 *
 * RETURNS
 * Success:  0
 *
 * Failure:  -1
 */
INT CDECL CRTDLL__findclose(DWORD hand)
{
  dprintf(("CRTDLL: _findclose(%08xh)\n",
          hand));

  if (!FindClose((HANDLE)hand))
  {
    __CRTDLL__set_errno(GetLastError());
    return -1;
  }
  return 0;
}


 /*********************************************************************
 *                  _findfirst    (CRTDLL.099)
 *
 * Create and return a search handle for iterating through a file and
 * directory list.
 *
 * PARAMS
 *   fspec [in]  File specification string for search, e.g "C:\*.BAT"
 *
 *   ft [out]    A pointer to a find_t structure to populate.
 *
 * RETURNS
 * Success:  A handle for the search, suitable for passing to _findnext
 *           or _findclose. Populates the members of ft with the details
 *           of the first matching file.
 *
 * Failure:  -1.
 */
DWORD CDECL CRTDLL__findfirst(LPCSTR fspec, find_t* ft)
{
  WIN32_FIND_DATAA find_data;
  HANDLE hfind;

  dprintf(("CRTDLL: _findfirst(%s)\n",
          fspec));

  hfind  = FindFirstFileA(fspec, &find_data);
  if (hfind == INVALID_HANDLE_VALUE)
  {
    __CRTDLL__set_errno(GetLastError());
    return -1;
  }
  __CRTDLL__fttofd(&find_data,ft);
  TRACE(":got handle %d\n",hfind);
  return hfind;
}


/*********************************************************************
 *                  _findnext     (CRTDLL.100)
 *
 * Return the next matching file/directory from a search hadle.
 *
 * PARAMS
 *   hand [in] Search handle from a pervious call to _findfirst
 *
 *   ft [out]  A pointer to a find_t structure to populate.
 *
 * RETURNS
 * Success:  0. Populates the members of ft with the details
 *           of the first matching file
 *
 * Failure:  -1
 */
INT CDECL CRTDLL__findnext(DWORD hand, find_t * ft)
{
  WIN32_FIND_DATAA find_data;

  dprintf(("CRTDLL: _findnext(%08xh)\n",
          hand));

  if (!FindNextFileA(hand, &find_data))
  {
    SetLastError(ERROR_INVALID_DRIVE);
    __CRTDLL__set_errno(GetLastError());
    return -1;
  }

  __CRTDLL__fttofd(&find_data,ft);
  return 0;
}


/*********************************************************************
 *                  _getcwd           (CRTDLL.120)
 *
 * Get the current directory.
 *
 * PARAMS
 * buf [out]  A buffer to place the current directory name in
 *
 * size [in]  The size of buf.
 *
 * RETURNS
 * Success: buf, or if buf is NULL, an allocated buffer
 *
 * Failure: NULL
 */
CHAR* CDECL CRTDLL__getcwd(LPSTR buf, INT size)
{
  // return (_getcwd(buf, size));

  char dir[_MAX_PATH];
  int dir_len = GetCurrentDirectoryA(_MAX_PATH,dir);

  dprintf(("CRTDLL: _getcwd()\n"));

  if (dir_len < 1)
    return NULL; /* FIXME: Real return value untested */

  if (!buf)
  {
    if (size < 0)
      return CRTDLL__strdup(dir);
        return __CRTDLL__strndup(dir,size);
  }
  if (dir_len >= size)
  {
    CRTDLL_errno = ERANGE;
    return NULL; /* buf too small */
  }
  strcpy(buf,dir);
  return buf;
}


/*********************************************************************
 *                  _getdcwd           (CRTDLL.121)
 *
 * Get the current directory on a drive. A: =1, B: =2, etc.
 * Passing drive 0 means the current drive.
 */
CHAR* CDECL CRTDLL__getdcwd(INT drive,LPSTR buf, INT size)
{
  // return (_getdcwd(drive, buffer, maxlen));

  static CHAR* dummy;

  dprintf(("CRTDLL: _getdcwd()\n"));

  if (!drive || drive == CRTDLL__getdrive())
    return CRTDLL__getcwd(buf,size); /* current */
  else
  {
    char dir[_MAX_PATH];
    char drivespec[4] = {'A', ':', '\\', 0};
    int dir_len;

    drivespec[0] += drive - 1;
    if (GetDriveTypeA(drivespec) < DRIVE_REMOVABLE)
    {
      CRTDLL_errno = EACCES;
      return NULL;
    }

    dir_len = GetFullPathNameA(drivespec,_MAX_PATH,dir,&dummy);
    if (dir_len >= size || dir_len < 1)
    {
      CRTDLL_errno = ERANGE;
      return NULL; /* buf too small */
    }

    if (!buf)
      return CRTDLL__strdup(dir); /* allocate */

    strcpy(buf,dir);
  }
  return buf;
}


/*********************************************************************
 *                  _getdiskfree     (CRTDLL.122)
 *
 * Get free disk space on given drive or the current drive.
 *
 */
UINT CDECL CRTDLL__getdiskfree(UINT disk, diskfree_t* d)
{
  char drivespec[4] = {'@', ':', '\\', 0};
  DWORD ret[4];
  UINT err;

  dprintf(("CRTDLL: _getdiskfree(%08xh)\n", disk));

  if (disk > 26)
    return ERROR_INVALID_PARAMETER; /* CRTDLL doesn't set errno here */

  drivespec[0] += disk; /* make a drive letter */

  if (GetDiskFreeSpaceA(disk==0?NULL:drivespec,ret,ret+1,ret+2,ret+3))
  {
    d->cluster_sectors = (unsigned)ret[0];
    d->sector_bytes = (unsigned)ret[1];
    d->available = (unsigned)ret[2];
    d->num_clusters = (unsigned)ret[3];
    return 0;
  }
  err = GetLastError();
  __CRTDLL__set_errno(err);
  return err;
}


/*********************************************************************
 *                  _getdrive           (CRTDLL.124)
 *
 *  Return current drive, A: =1, B: =2, etc
 */
INT CDECL CRTDLL__getdrive(VOID)
{
  // return DRIVE_GetCurrentDrive() + 1;

  char buffer[MAX_PATH];

  dprintf(("CRTDLL: _getdrive()\n"));

  if (!GetCurrentDirectoryA( sizeof(buffer), buffer ))
    return 0;
  if (buffer[1] != ':')
    return 0;
  return toupper(buffer[0]) - 'A' + 1;
}


/*********************************************************************
 *                  _mkdir           (CRTDLL.234)
 *
 * Create a directory.
 */
INT CDECL CRTDLL__mkdir(LPCSTR newdir)
{
  dprintf(("CRTDLL: _mkdir(%s)\n",
          newdir));

  if (CreateDirectoryA(newdir,NULL))
    return 0;

  __CRTDLL__set_errno(GetLastError());
  return -1;
}

/*********************************************************************
 *                  _rmdir           (CRTDLL.255)
 *
 * Delete a directory
 *
 */
INT CDECL CRTDLL__rmdir(LPSTR dir)
{
  dprintf(("CRTDLL: _rmdir(%s)\n",
          dir));

  if (RemoveDirectoryA(dir))
    return 0;

  __CRTDLL__set_errno(GetLastError());
  return -1;
}
