/**
 * g_open:
 * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
 * @flags: as in open()
 * @mode: as in open()
 *
 * A wrapper for the POSIX open() function. The open() function is
 * used to convert a pathname into a file descriptor.
 *
 * On POSIX systems file descriptors are implemented by the operating
 * system. On Windows, it's the C library that implements open() and
 * file descriptors. The actual Win32 API for opening files is quite
 * different, see MSDN documentation for CreateFile(). The Win32 API
 * uses file handles, which are more randomish integers, not small
 * integers like file descriptors.
 *
 * Because file descriptors are specific to the C library on Windows,
 * the file descriptor returned by this function makes sense only to
 * functions in the same C library. Thus if the GLib-using code uses a
 * different C library than GLib does, the file descriptor returned by
 * this function cannot be passed to C library functions like write()
 * or read().
 *
 * See your C library manual for more details about open().
 *
 * Returns: a new file descriptor, or -1 if an error occurred. The
 * return value can be used exactly like the return value from open().
 * 
 * Since: 2.6
 */
int
g_open (const gchar *filename,
	int          flags,
	int          mode)
{
#ifdef G_OS_WIN32
  HANDLE hFile;
  DWORD  dwDesiredAccess       = 0;
  DWORD  dwFlagsAndAttributes  = 0;
  DWORD  dwDisposition         = OPEN_EXISTING;
  DWORD  dwSharedAccess        = FILE_SHARE_READ | FILE_SHARE_DELETE;
  wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
  int retval;
  int save_errno;
    
  if (wfilename == NULL)
    {
      errno = EINVAL;
      return -1;
    }

  /* This function changed by JE - 27-03-2012 to use CreateFile() instead of _wopen() */
  /* Set up the access modes and other attributes */
  if ((flags & _O_CREAT) && (mode & _S_IREAD))
  {
    if (! (mode & _S_IWRITE))
      dwFlagsAndAttributes  = FILE_ATTRIBUTE_READONLY; /* Sets file to 'read only' after the file gets closed */
  }
  if ( !(flags & _O_ACCMODE))
  {
    /* Equates to _O_RDONLY */
    if (flags & _O_TRUNC)
	{
	  errno = EINVAL;
	  return -1;
	}

	dwDesiredAccess |= GENERIC_READ;
	dwSharedAccess  |= FILE_SHARE_WRITE;
  }
  if (flags & _O_WRONLY)
  {
    if (flags & _O_RDWR)
	{
	  errno = EINVAL;
	  return -1;
	}

	dwDesiredAccess |= GENERIC_WRITE;
  }
  if (flags & _O_RDWR)
  {
	dwDesiredAccess |= GENERIC_READ;
	dwDesiredAccess |= GENERIC_WRITE;
  }
  if (flags & _O_TRUNC)
  {
    if (flags & _O_CREAT)
	  dwDisposition = CREATE_ALWAYS;
	else
	  dwDisposition = TRUNCATE_EXISTING;
  }
  if ((flags & _O_CREAT) && !(flags & _O_TRUNC))
  {
    if (flags & _O_EXCL)
	  dwDisposition = CREATE_NEW;
	else
	  dwDisposition = OPEN_ALWAYS;
  }
  if (flags & _O_CREAT)
  {
    /* Handle the other flags that can be attached to _O_CREAT */
    if ((flags & _O_TEMPORARY) || (flags & _O_SHORT_LIVED))
      dwFlagsAndAttributes |= FILE_ATTRIBUTE_TEMPORARY;

    if (flags & _O_TEMPORARY)
      dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
  }
  if ((flags & _O_SEQUENTIAL) || (flags & _O_APPEND))
  {
    dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
  }
  else if (flags & _O_RANDOM)
  {
    dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
  }

  if (0 == dwFlagsAndAttributes)
    dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
  hFile = CreateFileW(wfilename, dwDesiredAccess, dwSharedAccess, NULL, dwDisposition, dwFlagsAndAttributes, NULL);

  if (INVALID_HANDLE_VALUE == hFile)
  {
    retval = (-1);

    switch (GetLastError ())
    {
#define CASE(a,b) case ERROR_##a: errno = b; break
      CASE (FILE_NOT_FOUND, ENOENT);
      CASE (PATH_NOT_FOUND, ENOENT);
      CASE (ACCESS_DENIED, EACCES);
      CASE (NOT_SAME_DEVICE, EXDEV);
      CASE (LOCK_VIOLATION, EACCES);
      CASE (SHARING_VIOLATION, EACCES);
      CASE (FILE_EXISTS, EEXIST);
      CASE (ALREADY_EXISTS, EEXIST);
#undef CASE
      default: errno = EIO;
    }
  }
  else
    retval = _open_osfhandle((long)hFile, flags);

  if ((-1) != retval)
  {
    /* We have a valid file handle. Set its translation mode to text or binary, as appropriate */
    if ((!(flags & _O_TEXT)) && (_fmode == _O_BINARY))
      _setmode(retval, _O_BINARY);
    else if ((flags & _O_TEXT) || (_fmode == _O_TEXT))
      _setmode(retval, _O_TEXT);
    else
      _setmode(retval, _O_BINARY);
  }

  save_errno = errno;
  g_free (wfilename);
  errno = save_errno;

  return retval;
#else
  return open (filename, flags, mode);
#endif
}


/**
 * g_creat:
 * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
 * @mode: as in creat()
 *
 * A wrapper for the POSIX creat() function. The creat() function is
 * used to convert a pathname into a file descriptor, creating a file
 * if necessary.

 * On POSIX systems file descriptors are implemented by the operating
 * system. On Windows, it's the C library that implements creat() and
 * file descriptors. The actual Windows API for opening files is
 * different, see MSDN documentation for CreateFile(). The Win32 API
 * uses file handles, which are more randomish integers, not small
 * integers like file descriptors.
 *
 * Because file descriptors are specific to the C library on Windows,
 * the file descriptor returned by this function makes sense only to
 * functions in the same C library. Thus if the GLib-using code uses a
 * different C library than GLib does, the file descriptor returned by
 * this function cannot be passed to C library functions like write()
 * or read().
 *
 * See your C library manual for more details about creat().
 *
 * Returns: a new file descriptor, or -1 if an error occurred. The
 * return value can be used exactly like the return value from creat().
 * 
 * Since: 2.8
 */
int
g_creat (const gchar *filename,
	 int          mode)
{
#ifdef G_OS_WIN32
  HANDLE hFile;
  DWORD  dwFlagsAndAttributes  = FILE_ATTRIBUTE_NORMAL;
  wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
  int retval;
  int save_errno;
    
  if (wfilename == NULL)
    {
      errno = EINVAL;
      return -1;
    }

  /* This function changed by JE - 27-03-2012 to use CreateFile() instead of _wcreat() */
  if (mode & _S_IREAD)
  {
    if (! (mode & _S_IWRITE))
      dwFlagsAndAttributes  = FILE_ATTRIBUTE_READONLY; /* Sets file to 'read only' after the file gets closed */
  }

  hFile = CreateFileW(wfilename, (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_DELETE),
                                  NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL);

  if (INVALID_HANDLE_VALUE == hFile)
  {
    retval = (-1);

    switch (GetLastError ())
    {
#define CASE(a,b) case ERROR_##a: errno = b; break
      CASE (FILE_NOT_FOUND, ENOENT);
      CASE (PATH_NOT_FOUND, ENOENT);
      CASE (ACCESS_DENIED, EACCES);
      CASE (NOT_SAME_DEVICE, EXDEV);
      CASE (LOCK_VIOLATION, EACCES);
      CASE (SHARING_VIOLATION, EACCES);
      CASE (FILE_EXISTS, EEXIST);
      CASE (ALREADY_EXISTS, EEXIST);
#undef CASE
      default: errno = EIO;
    }
  }
  else
    retval = _open_osfhandle((long)hFile, _O_RDWR);

  save_errno = errno;
  g_free (wfilename);
  errno = save_errno;

  return retval;
#else
  return creat (filename, mode);
#endif
}

/**
 * g_fopen:
 * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
 * @mode: a string describing the mode in which the file should be 
 *   opened
 *
 * A wrapper for the stdio fopen() function. The fopen() function
 * opens a file and associates a new stream with it.
 * 
 * Because file descriptors are specific to the C library on Windows,
 * and a file descriptor is partof the <type>FILE</type> struct, the
 * <type>FILE</type> pointer returned by this function makes sense
 * only to functions in the same C library. Thus if the GLib-using
 * code uses a different C library than GLib does, the
 * <type>FILE</type> pointer returned by this function cannot be
 * passed to C library functions like fprintf() or fread().
 *
 * See your C library manual for more details about fopen().
 *
 * Returns: A <type>FILE</type> pointer if the file was successfully
 *    opened, or %NULL if an error occurred
 * 
 * Since: 2.6
 */
FILE *
g_fopen (const gchar *filename,
	 const gchar *mode)
{
#ifdef G_OS_WIN32
  int   hFile;
  int   flags  = 0;
  gchar priv_mode[4];
  FILE *retval = NULL;

  /* This function changed by JE - 27-03-2012 to use CreateFile() instead of _wfopen() */
  if ((NULL == filename) || (NULL == mode))
  {
    errno = EINVAL;
    goto out;
  }
  if ((strlen(mode) < 1) || (strlen(mode) > 3))
  {
    errno - EINVAL;
    goto out;
  }

  strncpy(priv_mode, mode, 3);
  priv_mode[3] = '\0';

  /* Set up any flags to pass to 'g_open()' */
  if (3 == strlen(priv_mode))
  {
    if (('c' == priv_mode[2]) || ('n' == priv_mode[2]))
      priv_mode[2] = '\0';
    else
    {
      if (0 == strcmp(priv_mode, "a+b"))
        flags = _O_RDWR | _O_CREAT | _O_APPEND | _O_BINARY;
      else if (0 == strcmp(priv_mode, "a+t"))
        flags = _O_RDWR | _O_CREAT | _O_APPEND | _O_TEXT;
      else if (0 == strcmp(priv_mode, "r+b"))
        flags = _O_RDWR | _O_BINARY;
      else if (0 == strcmp(priv_mode, "r+t"))
        flags = _O_RDWR | _O_TEXT;
      else if (0 == strcmp(priv_mode, "w+b"))
        flags = _O_RDWR | _O_CREAT |_O_TRUNC | _O_BINARY;
      else if (0 == strcmp(priv_mode, "w+t"))
        flags = _O_RDWR | _O_CREAT |_O_TRUNC | _O_TEXT;
      else
	  {
	    errno = EINVAL;
        goto out;
	  }
    }
  }
  if (2 == strlen(priv_mode))
  {
    if (('c' == priv_mode[1]) || ('n' == priv_mode[1]))
      priv_mode[1] = '\0';
    else
    {
      if (0 == strcmp(priv_mode, "a+"))
        flags = _O_RDWR | _O_CREAT | _O_APPEND;
      else if (0 == strcmp(priv_mode, "ab"))
        flags = _O_WRONLY | _O_CREAT | _O_APPEND | _O_BINARY;
      else if (0 == strcmp(priv_mode, "at"))
        flags = _O_WRONLY | _O_CREAT | _O_APPEND | _O_TEXT;
      else if (0 == strcmp(priv_mode, "rb"))
        flags = _O_RDONLY | _O_BINARY;
      else if (0 == strcmp(priv_mode, "rt"))
        flags = _O_RDONLY | _O_TEXT;
      else if (0 == strcmp(priv_mode, "wb"))
        flags = _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY;
      else if (0 == strcmp(priv_mode, "wt"))
        flags = _O_WRONLY | _O_CREAT | _O_TRUNC | _O_TEXT;
      else
	  {
	    errno = EINVAL;
        goto out;
	  }
    }
  }
  if (1 == strlen(priv_mode))
  {
    if (0 == strcmp(priv_mode, "a"))
      flags = _O_WRONLY | _O_CREAT | _O_APPEND;
    else if (0 == strcmp(priv_mode, "r"))
      flags = _O_RDONLY;
    else if (0 == strcmp(priv_mode, "w"))
      flags = _O_WRONLY | _O_CREAT | _O_TRUNC;
    else if ( !((0 == strcmp(priv_mode, "c")) || (0 == strcmp(priv_mode, "n"))))
	{
	  errno = EINVAL;
      goto out;
	}
  }

  hFile = g_open (filename, flags, (_S_IREAD | _S_IWRITE));

  if (INVALID_HANDLE_VALUE == (HANDLE)hFile)
    /* 'errno' will have already been set by 'g_open()' */
    retval = NULL;
  else
    retval = _fdopen(hFile, mode);

out:
  return retval;
#else
  return fopen (filename, mode);
#endif
}
