opendir windows

This code is a port of opendir. readdir and closedir for windows. (include dirent.h on linux)


#include <Windows.h>

#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WINDOWS_


/*
 opendir,  readdir closedir for windows.
 include dirent.h on linux
*/

enum
{
  DT_UNKNOWN = 0,
  DT_FIFO = 1,
  DT_CHR = 2,
  DT_DIR = 4,
  DT_BLK = 6,
  DT_REG = 8,
  DT_LNK = 10,
  DT_SOCK = 12,
  DT_WHT = 14
};

struct dirent
{
  ino_t d_ino;             /* Inode number */
  off_t d_off;             /* Not an offset; see below */
  unsigned short d_reclen; /* Length of this record */
  unsigned char d_type;    /* Type of file; not supported
                                   by all filesystem types*/
  char d_name[256];        /* Null-terminated filename */
};

typedef struct
{
  void *handle;
  struct dirent dirent;
} DIR;

DIR *opendir(const char *name);
int closedir(DIR *dirp);
struct dirent *readdir(DIR *dirp);

static int convert_windows_getlasterror_to_posix(int i)
{
  switch (i)
  {
  case ERROR_ACCESS_DENIED:
    return EACCES;
  case ERROR_ALREADY_EXISTS:
    return EEXIST;
  case ERROR_BAD_UNIT:
    return ENODEV;
  case ERROR_BUFFER_OVERFLOW:
    return ENAMETOOLONG;
  case ERROR_BUSY:
    return EBUSY;
  case ERROR_BUSY_DRIVE:
    return EBUSY;
  case ERROR_CANNOT_MAKE:
    return EACCES;
  case ERROR_CANTOPEN:
    return EIO;
  case ERROR_CANTREAD:
    return EIO;
  case ERROR_CANTWRITE:
    return EIO;
  case ERROR_CURRENT_DIRECTORY:
    return EACCES;
  case ERROR_DEV_NOT_EXIST:
    return ENODEV;
  case ERROR_DEVICE_IN_USE:
    return EBUSY;
  case ERROR_DIR_NOT_EMPTY:
    return ENOTEMPTY;
  case ERROR_DIRECTORY:
    return EINVAL;
  case ERROR_DISK_FULL:
    return ENOSPC;
  case ERROR_FILE_EXISTS:
    return EEXIST;
  case ERROR_FILE_NOT_FOUND:
    return ENOENT;
  case ERROR_HANDLE_DISK_FULL:
    return ENOSPC;
  case ERROR_INVALID_ACCESS:
    return EACCES;
  case ERROR_INVALID_DRIVE:
    return ENODEV;
  case ERROR_INVALID_FUNCTION:
    return ENOSYS;
  case ERROR_INVALID_HANDLE:
    return EINVAL;
  case ERROR_INVALID_NAME:
    return EINVAL;
  case ERROR_LOCK_VIOLATION:
    return ENOLCK;
  case ERROR_LOCKED:
    return ENOLCK;
  case ERROR_NEGATIVE_SEEK:
    return EINVAL;
  case ERROR_NOACCESS:
    return EACCES;
  case ERROR_NOT_ENOUGH_MEMORY:
    return ENOMEM;
  case ERROR_NOT_READY:
    return EAGAIN;
  case ERROR_NOT_SAME_DEVICE:
    return EXDEV;
  case ERROR_OPEN_FAILED:
    return EIO;
  case ERROR_OPEN_FILES:
    return EBUSY;
  case ERROR_OPERATION_ABORTED:
    return ECANCELED;
  case ERROR_OUTOFMEMORY:
    return ENOMEM;
  case ERROR_PATH_NOT_FOUND:
    return ENOENT;
  case ERROR_READ_FAULT:
    return EIO;
  case ERROR_RETRY:
    return EAGAIN;
  case ERROR_SEEK:
    return EIO;
  case ERROR_SHARING_VIOLATION:
    return EACCES;
  case ERROR_TOO_MANY_OPEN_FILES:
    return EMFILE;
  case ERROR_WRITE_FAULT:
    return EIO;
  case ERROR_WRITE_PROTECT:
    return EACCES;
  case WSAEACCES:
    return EACCES;
  case WSAEADDRINUSE:
    return EADDRINUSE;
  case WSAEADDRNOTAVAIL:
    return EADDRNOTAVAIL;
  case WSAEAFNOSUPPORT:
    return EAFNOSUPPORT;
  case WSAEALREADY:
    return EALREADY;
  case WSAEBADF:
    return EBADF;
  case WSAECONNABORTED:
    return ECONNABORTED;
  case WSAECONNREFUSED:
    return ECONNREFUSED;
  case WSAECONNRESET:
    return ECONNRESET;
  case WSAEDESTADDRREQ:
    return EDESTADDRREQ;
  case WSAEFAULT:
    return EFAULT;
  case WSAEHOSTUNREACH:
    return EHOSTUNREACH;
  case WSAEINPROGRESS:
    return EINPROGRESS;
  case WSAEINTR:
    return EINTR;
  case WSAEINVAL:
    return EINVAL;
  case WSAEISCONN:
    return EISCONN;
  case WSAEMFILE:
    return EMFILE;
  case WSAEMSGSIZE:
    return EMSGSIZE;
  case WSAENETDOWN:
    return ENETDOWN;
  case WSAENETRESET:
    return ENETRESET;
  case WSAENETUNREACH:
    return ENETUNREACH;
  case WSAENOBUFS:
    return ENOBUFS;
  case WSAENOPROTOOPT:
    return ENOPROTOOPT;
  case WSAENOTCONN:
    return ENOTCONN;
  case WSAENOTSOCK:
    return ENOTSOCK;
  case WSAEOPNOTSUPP:
    return EOPNOTSUPP;
  case WSAEPROTONOSUPPORT:
    return EPROTONOSUPPORT;
  case WSAEPROTOTYPE:
    return EPROTOTYPE;
  case WSAETIMEDOUT:
    return ETIMEDOUT;
  case WSAEWOULDBLOCK:
    return EWOULDBLOCK;
  }
  return EPERM;
}

DIR *opendir(const char *name)
{
  WIN32_FIND_DATAA fdFile;

  char sPath[MAX_PATH] = {0};
  strcat(sPath, name);
  strcat(sPath, "\\*.*");

  HANDLE handle = FindFirstFileA(sPath, &fdFile);

  if (handle != INVALID_HANDLE_VALUE)
  {
    DIR *p = calloc(1, sizeof *p);
    if (p)
    {
      p->handle = handle;
      return p;
    }
    else
    {
      /*calloc sets errno to ENOMEM if a memory allocation fails */
      FindClose(handle);      
    }
  }
  else
  {
    errno = convert_windows_getlasterror_to_posix(GetLastError());
  }

  return NULL;
}

int closedir(DIR *dirp)
{
  FindClose(dirp->handle);
  free(dirp);
  return 0;
}

struct dirent *readdir(DIR *dirp)
{
  WIN32_FIND_DATAA fdFile;
  BOOL b = FindNextFileA(dirp->handle, &fdFile);
  if (b)
  {
    /*clear*/
    memset(&dirp->dirent, 0, sizeof(dirp->dirent));

    if (fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      dirp->dirent.d_type |= DT_DIR;
    }

    /*worst case trunks the string*/
    strncpy(dirp->dirent.d_name,
            fdFile.cFileName,
            sizeof(dirp->dirent.d_name) - 1);

    return &dirp->dirent;
  }
  else
  {
    errno = convert_windows_getlasterror_to_posix(GetLastError());
  }
  return NULL;
}

#endif //_WINDOWS_

int copy_file(const char *pathfrom,
              const char *pathto)
{
  char buf[4096];
  size_t nread;
  int saved_errno;

  FILE *fd_from = fopen(pathfrom, "rb");
  if (fd_from < 0)
    return -1;

  FILE *fd_to = fopen(pathto, "wb");
  if (fd_to < 0)
    goto out_error;

  while (nread = fread(buf, sizeof(char), sizeof buf, fd_from), nread > 0)
  {
    char *out_ptr = buf;
    size_t nwritten;

    do
    {
      nwritten = fwrite(out_ptr, sizeof(char), nread, fd_to);

      if (nwritten >= 0)
      {
        nread -= nwritten;
        out_ptr += nwritten;
      }
      else
      {
        goto out_error;
      }
    } while (nread > 0);
  }

  if (nread == 0)
  {
    if (fclose(fd_to) < 0)
    {
      fd_to = NULL;
      goto out_error;
    }
    fclose(fd_from);

    /* Success! */
    return 0;
  }

out_error:
  saved_errno = errno;

  fclose(fd_from);

  if (fd_to != NULL)
    fclose(fd_to);

  return saved_errno;
}

int copy_folder(const char *from, const char *to)
{
  int errcode = _mkdir(to);
  if (errcode != 0)
  {
    return errcode;
  }
  
  DIR *dir = opendir(from);

  if (dir == NULL)
  {
    return errno;
  }

  struct dirent *dp;
  while ((dp = readdir(dir)) != NULL)
  {
    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
    {
      /* skip self and parent */
      continue; 
    }

    char fromlocal[MAX_PATH];
    snprintf(fromlocal, MAX_PATH, "%s/%s", from, dp->d_name);

    char tolocal[MAX_PATH];
    snprintf(tolocal, MAX_PATH, "%s/%s", to, dp->d_name);

    if (dp->d_type & DT_DIR)
    {
        errcode = copy_folder(fromlocal, tolocal);
    }
    else
    {
      errcode = copy_file(fromlocal, tolocal);
    }

    if (errcode != 0)
      break;
  }

  closedir(dir);
  return errcode;
}

int main(void)
{
  char buffer[200];
  _getcwd(buffer, 200);
  printf("%s\n", buffer);

  int errcode = copy_folder("from", "to");

  return EXIT_SUCCESS;
}