commit 134c3506933ccf8ed5d8c00e2711851fbddc9252
Author: Roman Fomin <rfomin@gmail.com>
AuthorDate: Wed Aug 28 11:35:16 2024 +0700
Commit: Roman Fomin <rfomin@gmail.com>
CommitDate: Wed Aug 28 13:18:15 2024 +0700
Rewrite win_opendir.c
Adapted from https://github.com/win32ports/dirent_h
* Fixes issues with postfix "." for non-relative paths.
* Support UTF8 paths as input, so we don't need `M_ConvertUtf8ToSysNativeMB`
and `M_ConvertSysNativeMBToUtf8` functions.
* Support long NTFS paths (32768 characters).
---
configure.ac | 2 +-
src/CMakeLists.txt | 4 +-
src/Makefile.am | 5 +-
src/i_glob.c | 20 +-
src/m_misc.c | 70 ------
src/m_misc.h | 2 -
win32/win_opendir.c | 707 +++++++++++++++++++++++++++++-----------------------
win32/win_opendir.h | 175 ++++++++-----
8 files changed, 525 insertions(+), 460 deletions(-)
diff --git a/configure.ac b/configure.ac
index 8ccb9f30..b4d76ac4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -179,7 +179,7 @@ CFLAGS="$CFLAGS $SDL_CFLAGS ${SAMPLERATE_CFLAGS:-} ${PNG_CFLAGS:-} ${FLUIDSYNTH_
LDFLAGS="$LDFLAGS $SDL_LIBS ${SAMPLERATE_LIBS:-} ${PNG_LIBS:-} ${FLUIDSYNTH_LIBS:-}"
case "$host" in
*-*-mingw* | *-*-cygwin* | *-*-msvc* )
- LDFLAGS="$LDFLAGS -lwinmm"
+ LDFLAGS="$LDFLAGS -lwinmm -lshlwapi"
;;
*)
esac
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8b02fe66..6fbf0790 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -106,7 +106,7 @@ set(GAME_SOURCE_FILES
set(GAME_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/../")
-if(MSVC)
+if(WIN32)
list(APPEND GAME_SOURCE_FILES
"../win32/win_opendir.c" "../win32/win_opendir.h")
@@ -143,7 +143,7 @@ if(FluidSynth_FOUND)
list(APPEND EXTRA_LIBS FluidSynth::libfluidsynth)
endif()
if(WIN32)
- list(APPEND EXTRA_LIBS winmm)
+ list(APPEND EXTRA_LIBS winmm shlwapi)
endif()
if(WIN32)
diff --git a/src/Makefile.am b/src/Makefile.am
index 3d964645..68ec563a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,6 +21,7 @@ execgames_SCRIPTS = $(SETUP_BINARIES)
AM_CFLAGS = -I$(top_srcdir)/textscreen \
-I$(top_srcdir)/opl \
-I$(top_srcdir)/pcsound \
+ -I$(top_srcdir)/win32 \
@SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@
# Common source files used by absolutely everything:
@@ -117,7 +118,9 @@ w_file.c w_file.h \
w_file_stdc.c \
w_file_posix.c \
w_file_win32.c \
-w_merge.c w_merge.h
+w_merge.c w_merge.h \
+$(top_builddir)/win32/win_opendir.c \
+$(top_builddir)/win32/win_opendir.h
MEMORY_NATIVE_SOURCE_FILES=\
diff --git a/src/i_glob.c b/src/i_glob.c
index 90c53107..8b761283 100644
--- a/src/i_glob.c
+++ b/src/i_glob.c
@@ -24,8 +24,7 @@
#include "m_misc.h"
#include "config.h"
-#if defined(_MSC_VER)
-// For Visual C++, we need to include the win_opendir module.
+#if defined(_WIN32)
#include <win_opendir.h>
#define S_ISDIR(m) (((m)& S_IFMT) == S_IFDIR)
#elif defined(HAVE_DIRENT_H)
@@ -102,7 +101,6 @@ glob_t *I_StartMultiGlob(const char *directory, int flags,
int num_globs;
glob_t *result;
va_list args;
- char *directory_native;
globs = malloc(sizeof(char *));
if (globs == NULL)
@@ -141,18 +139,15 @@ glob_t *I_StartMultiGlob(const char *directory, int flags,
return NULL;
}
- directory_native = M_ConvertUtf8ToSysNativeMB(directory);
-
- result->dir = opendir(directory_native);
+ result->dir = opendir(directory);
if (result->dir == NULL)
{
FreeStringList(globs, num_globs);
free(result);
- free(directory_native);
return NULL;
}
- result->directory = directory_native;
+ result->directory = M_StringDuplicate(directory);
result->globs = globs;
result->num_globs = num_globs;
result->flags = flags;
@@ -246,7 +241,6 @@ static boolean MatchesAnyGlob(const char *name, glob_t *glob)
static char *NextGlob(glob_t *glob)
{
struct dirent *de;
- char *temp, *ret;
do
{
@@ -259,13 +253,7 @@ static char *NextGlob(glob_t *glob)
|| !MatchesAnyGlob(de->d_name, glob));
// Return the fully-qualified path, not just the bare filename.
- temp = M_StringJoin(glob->directory, DIR_SEPARATOR_S, de->d_name, NULL);
-
- ret = M_ConvertSysNativeMBToUtf8(temp);
-
- free(temp);
-
- return ret;
+ return M_StringJoin(glob->directory, DIR_SEPARATOR_S, de->d_name, NULL);
}
static void ReadAllFilenames(glob_t *glob)
diff --git a/src/m_misc.c b/src/m_misc.c
index ace206d4..ba1e1889 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -28,23 +28,15 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
-#ifdef _MSC_VER
#include <direct.h>
-#endif
#else
#include <sys/types.h>
#endif
#include "doomtype.h"
-#include "deh_str.h"
-
-#include "i_swap.h"
#include "i_system.h"
-#include "i_video.h"
#include "m_misc.h"
-#include "v_video.h"
-#include "w_wad.h"
#include "z_zone.h"
#ifdef _WIN32
@@ -122,16 +114,6 @@ char *M_ConvertWideToUtf8(const wchar_t *wstr)
return ConvertWideToMultiByte(wstr, CP_UTF8);
}
-static wchar_t *ConvertSysNativeMBToWide(const char *str)
-{
- return ConvertMultiByteToWide(str, CP_ACP);
-}
-
-static char *ConvertWideToSysNativeMB(const wchar_t *wstr)
-{
- return ConvertWideToMultiByte(wstr, CP_ACP);
-}
-
// Convert UTF8 string to a wide string. The result is newly allocated and must
// be freed by the caller after use.
@@ -141,58 +123,6 @@ wchar_t *M_ConvertUtf8ToWide(const char *str)
}
#endif
-// Convert multibyte string in system encoding to UTF8. The result is newly
-// allocated and must be freed by the caller after use.
-
-char *M_ConvertSysNativeMBToUtf8(const char *str)
-{
-#ifdef _WIN32
- char *ret = NULL;
- wchar_t *wstr = NULL;
-
- wstr = ConvertSysNativeMBToWide(str);
-
- if (!wstr)
- {
- return NULL;
- }
-
- ret = M_ConvertWideToUtf8(wstr);
-
- free(wstr);
-
- return ret;
-#else
- return M_StringDuplicate(str);
-#endif
-}
-
-// Convert UTF8 string to multibyte string in system encoding. The result is
-// newly allocated and must be freed by the caller after use.
-
-char *M_ConvertUtf8ToSysNativeMB(const char *str)
-{
-#ifdef _WIN32
- char *ret = NULL;
- wchar_t *wstr = NULL;
-
- wstr = M_ConvertUtf8ToWide(str);
-
- if (!wstr)
- {
- return NULL;
- }
-
- ret = ConvertWideToSysNativeMB(wstr);
-
- free(wstr);
-
- return ret;
-#else
- return M_StringDuplicate(str);
-#endif
-}
-
FILE *M_fopen(const char *filename, const char *mode)
{
#ifdef _WIN32
diff --git a/src/m_misc.h b/src/m_misc.h
index 7b9e72cc..6dc18ccf 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -30,8 +30,6 @@
wchar_t *M_ConvertUtf8ToWide(const char *str);
char *M_ConvertWideToUtf8(const wchar_t *wstr);
#endif
-char *M_ConvertUtf8ToSysNativeMB(const char *str);
-char *M_ConvertSysNativeMBToUtf8(const char *str);
FILE *M_fopen(const char *filename, const char *mode);
int M_remove(const char *path);
diff --git a/win32/win_opendir.c b/win32/win_opendir.c
index b2f67805..a03489c9 100644
--- a/win32/win_opendir.c
+++ b/win32/win_opendir.c
@@ -1,336 +1,425 @@
-//
-// 03/10/2006 James Haley
-//
-// For this module only:
-// This code is public domain. No change sufficient enough to constitute a
-// significant or original work has been made, and thus it remains as such.
-//
-//
-// DESCRIPTION:
-//
-// Implementation of POSIX opendir for Visual C++.
-// Derived from the MinGW C Library Extensions Source (released to the
-// public domain). As with other Win32 modules, don't include most DOOM
-// headers into this or conflicts will occur.
-//
-// Original Header:
-//
-// * dirent.c
-// * This file has no copyright assigned and is placed in the Public Domain.
-// * This file is a part of the mingw-runtime package.
-// * No warranty is given; refer to the file DISCLAIMER within the package.
-// *
-// * 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>
-//
-
-#ifndef _MSC_VER
-#error i_opndir.c is for Microsoft Visual C++ only
-#endif
+/*
+MIT License
+Copyright (c) 2019 win32ports
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifdef _WIN32
+
+#include "win_opendir.h"
-#include <stdlib.h>
+#include <stdint.h>
#include <errno.h>
-#include <string.h>
#define WIN32_LEAN_AND_MEAN
-#include <windows.h> /* for GetFileAttributes */
+#include <windows.h>
+#include <shlwapi.h>
-#include <tchar.h>
-#define SUFFIX _T("*")
-#define SLASH _T("\\")
+#ifdef _MSC_VER
+#pragma comment(lib, "Shlwapi.lib")
+#endif
-#include "win_opendir.h"
+#ifndef NTFS_MAX_PATH
+#define NTFS_MAX_PATH 32768
+#endif /* NTFS_MAX_PATH */
+
+#ifndef FSCTL_GET_REPARSE_POINT
+#define FSCTL_GET_REPARSE_POINT 0x900a8
+#endif /* FSCTL_GET_REPARSE_POINT */
+
+#ifndef FILE_NAME_NORMALIZED
+#define FILE_NAME_NORMALIZED 0
+#endif /* FILE_NAME_NORMALIZED */
-//
-// opendir
-//
-// Returns a pointer to a DIR structure appropriately filled in to begin
-// searching a directory.
-//
-DIR *opendir(const _TCHAR *szPath)
+int closedir(DIR *dirp)
{
- DIR *nd;
- unsigned int rc;
- _TCHAR szFullPath[MAX_PATH];
-
- errno = 0;
-
- if(!szPath)
- {
- errno = EFAULT;
- return (DIR *)0;
- }
-
- if(szPath[0] == _T('\0'))
- {
- errno = ENOTDIR;
- return (DIR *)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 (DIR *)0;
- }
- if(!(rc & FILE_ATTRIBUTE_DIRECTORY))
- {
- /* Error, entry exists but not a directory. */
- errno = ENOTDIR;
- return (DIR *)0;
- }
-
- /* Make an absolute pathname. */
- _tfullpath(szFullPath, szPath, MAX_PATH);
-
- /* Allocate enough space to store DIR structure and the complete
- * directory path given. */
- nd = (DIR *)(malloc(sizeof(DIR) + (_tcslen(szFullPath)
- + _tcslen(SLASH)
- + _tcslen(SUFFIX) + 1)
- * sizeof(_TCHAR)));
-
- if(!nd)
- {
- /* Error, out of memory. */
- errno = ENOMEM;
- return (DIR *)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')
- && _tcsrchr(nd->dd_name, _T('/')) != nd->dd_name
- + _tcslen(nd->dd_name) - 1
- && _tcsrchr(nd->dd_name, _T('\\')) != nd->dd_name
- + _tcslen(nd->dd_name) - 1)
- {
- _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;
- memset(nd->dd_dir.d_name, 0, FILENAME_MAX);
-
- return nd;
+ struct __dir *data = NULL;
+ if (!dirp)
+ {
+ errno = EBADF;
+ return -1;
+ }
+ data = (struct __dir *) dirp;
+ CloseHandle((HANDLE) data->fd);
+ free(data->entries);
+ free(data);
+ return 0;
}
-//
-// readdir
-//
-// Return a pointer to a dirent structure filled with the information on the
-// next entry in the directory.
-//
-struct dirent *readdir(DIR *dirp)
+static void __seterrno(int value)
{
- errno = 0;
-
- /* Check for valid DIR struct. */
- if(!dirp)
- {
- errno = EFAULT;
- return (struct dirent *)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 dirent *)0;
- }
- else if (dirp->dd_stat == 0)
- {
- /* We haven't started the search yet. */
- /* Start the search */
- dirp->dd_handle = _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 = _tcslen(dirp->dd_dta.name);
- _tcscpy(dirp->dd_dir.d_name, dirp->dd_dta.name);
- return &dirp->dd_dir;
- }
-
- return (struct dirent *)0;
+#ifdef _MSC_VER
+ _set_errno(value);
+#else /* _MSC_VER */
+ errno = value;
+#endif /* _MSC_VER */
}
+static int __islink(const wchar_t *name, char *buffer)
+{
+ DWORD io_result = 0;
+ DWORD bytes_returned = 0;
+ HANDLE hFile = CreateFileW(
+ name, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return 0;
+ }
-//
-// closedir
-//
-// Frees up resources allocated by opendir.
-//
-int closedir(DIR *dirp)
+ io_result = DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer,
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
+ &bytes_returned, NULL);
+
+ CloseHandle(hFile);
+
+ if (io_result == 0)
+ {
+ return 0;
+ }
+
+ return ((REPARSE_GUID_DATA_BUFFER *) buffer)->ReparseTag ==
+ IO_REPARSE_TAG_SYMLINK;
+}
+
+#pragma pack(push, 1)
+
+typedef struct dirent_FILE_ID_128
+{
+ BYTE Identifier[16];
+} dirent_FILE_ID_128;
+
+typedef struct _dirent_FILE_ID_INFO
+{
+ ULONGLONG VolumeSerialNumber;
+ dirent_FILE_ID_128 FileId;
+} dirent_FILE_ID_INFO;
+
+#pragma pack(pop)
+
+typedef enum dirent_FILE_INFO_BY_HANDLE_CLASS
+{
+ dirent_FileIdInfo = 18
+} dirent_FILE_INFO_BY_HANDLE_CLASS;
+
+static __ino_t __inode(const wchar_t *name)
{
- int rc;
-
- errno = 0;
- rc = 0;
-
- if(!dirp)
- {
- errno = EFAULT;
- return -1;
- }
-
- if(dirp->dd_handle != -1)
- {
- rc = _findclose(dirp->dd_handle);
- }
-
- /* Delete the dir structure. */
- free(dirp);
-
- return rc;
+ __ino_t value = {0};
+ BOOL result;
+ dirent_FILE_ID_INFO fileid;
+ BY_HANDLE_FILE_INFORMATION info;
+ typedef BOOL(__stdcall * pfnGetFileInformationByHandleEx)(
+ HANDLE hFile, dirent_FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
+ LPVOID lpFileInformation, DWORD dwBufferSize);
+
+ HANDLE hKernel32 = GetModuleHandleW(L"kernel32.dll");
+ if (!hKernel32)
+ {
+ return value;
+ }
+
+ pfnGetFileInformationByHandleEx fnGetFileInformationByHandleEx =
+ (pfnGetFileInformationByHandleEx) GetProcAddress(
+ hKernel32, "GetFileInformationByHandleEx");
+ if (!fnGetFileInformationByHandleEx)
+ {
+ return value;
+ }
+
+ HANDLE hFile = CreateFileW(name, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, 0, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return value;
+ }
+
+ result = fnGetFileInformationByHandleEx(hFile, dirent_FileIdInfo, &fileid,
+ sizeof(fileid));
+ if (result)
+ {
+ value.serial = fileid.VolumeSerialNumber;
+ memcpy(value.fileid, fileid.FileId.Identifier, 16);
+ }
+ else
+ {
+ result = GetFileInformationByHandle(hFile, &info);
+ if (result)
+ {
+ value.serial = info.dwVolumeSerialNumber;
+ memcpy(value.fileid + 8, &info.nFileIndexHigh, 4);
+ memcpy(value.fileid + 12, &info.nFileIndexLow, 4);
+ }
+ }
+ CloseHandle(hFile);
+ return value;
}
-//
-// rewinddir
-//
-// Return to the beginning of the directory "stream". We simply call findclose
-// and then reset things like an opendir.
-//
-void rewinddir(DIR * dirp)
+static DIR *__internal_opendir(wchar_t *wname, int size)
{
- errno = 0;
-
- if(!dirp)
- {
- errno = EFAULT;
- return;
- }
-
- if(dirp->dd_handle != -1)
- {
- _findclose(dirp->dd_handle);
- }
-
- dirp->dd_handle = -1;
- dirp->dd_stat = 0;
+ struct __dir *data = NULL;
+ struct dirent *tmp_entries = NULL;
+ static char default_char = '?';
+ static wchar_t *prefix = L"\\\\?\\";
+ static wchar_t *suffix = L"\\*.*";
+ static int extra_prefix = 4; /* use prefix "\\?\" to handle long file names */
+ static int extra_suffix = 4; /* use suffix "\*.*" to find everything */
+ WIN32_FIND_DATAW w32fd = {0};
+ HANDLE hFindFile = INVALID_HANDLE_VALUE;
+ static int grow_factor = 2;
+ char *buffer = NULL;
+
+ BOOL relative = PathIsRelativeW(wname + extra_prefix);
+
+ memcpy(wname + size - 1, suffix, sizeof(wchar_t) * extra_suffix);
+ wname[size + extra_suffix - 1] = 0;
+
+ if (relative)
+ {
+ wname += extra_prefix;
+ size -= extra_prefix;
+ }
+ hFindFile = FindFirstFileW(wname, &w32fd);
+ if (INVALID_HANDLE_VALUE == hFindFile)
+ {
+ __seterrno(ENOENT);
+ return NULL;
+ }
+
+ data = (struct __dir *) malloc(sizeof(struct __dir));
+ if (!data)
+ {
+ goto out_of_memory;
+ }
+ wname[size - 1] = 0;
+ data->fd = (intptr_t) CreateFileW(wname, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, 0);
+ wname[size - 1] = L'\\';
+ data->count = 16;
+ data->index = 0;
+ data->entries = (struct dirent *) malloc(sizeof(struct dirent) * data->count);
+ if (!data->entries)
+ {
+ goto out_of_memory;
+ }
+ buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ if (!buffer)
+ {
+ goto out_of_memory;
+ }
+
+ do
+ {
+ WideCharToMultiByte(CP_UTF8, 0, w32fd.cFileName, -1,
+ data->entries[data->index].d_name, NAME_MAX,
+ &default_char, NULL);
+
+ memcpy(wname + size, w32fd.cFileName, sizeof(wchar_t) * NAME_MAX);
+
+ if (((w32fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ==
+ FILE_ATTRIBUTE_REPARSE_POINT)
+ && __islink(wname, buffer))
+ {
+ data->entries[data->index].d_type = DT_LNK;
+ }
+ else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) ==
+ FILE_ATTRIBUTE_DEVICE)
+ {
+ data->entries[data->index].d_type = DT_CHR;
+ }
+ else if ((w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
+ FILE_ATTRIBUTE_DIRECTORY)
+ {
+ data->entries[data->index].d_type = DT_DIR;
+ }
+ else
+ {
+ data->entries[data->index].d_type = DT_REG;
+ }
+
+ data->entries[data->index].d_ino = __inode(wname);
+ data->entries[data->index].d_reclen = sizeof(struct dirent);
+ data->entries[data->index].d_namelen =
+ (unsigned char) wcslen(w32fd.cFileName);
+ data->entries[data->index].d_off = 0;
+
+ if (++data->index == data->count)
+ {
+ tmp_entries = (struct dirent *) realloc(
+ data->entries,
+ sizeof(struct dirent) * data->count * grow_factor);
+ if (!tmp_entries)
+ {
+ goto out_of_memory;
+ }
+ data->entries = tmp_entries;
+ data->count *= grow_factor;
+ }
+ } while (FindNextFileW(hFindFile, &w32fd) != 0);
+
+ free(buffer);
+ FindClose(hFindFile);
+
+ data->count = data->index;
+ data->index = 0;
+ return (DIR *) data;
+
+out_of_memory:
+ if (data)
+ {
+ if (INVALID_HANDLE_VALUE != (HANDLE) data->fd)
+ {
+ CloseHandle((HANDLE) data->fd);
+ }
+ free(data->entries);
+ }
+ free(buffer);
+ free(data);
+ if (INVALID_HANDLE_VALUE != hFindFile)
+ {
+ FindClose(hFindFile);
+ }
+ __seterrno(ENOMEM);
+ return NULL;
+}
+
+static wchar_t *__get_buffer()
+{
+ wchar_t *name = malloc(sizeof(wchar_t) * (NTFS_MAX_PATH + NAME_MAX + 8));
+ if (name)
+ {
+ memcpy(name, L"\\\\?\\", sizeof(wchar_t) * 4);
+ }
+ return name;
}
-//
-// 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 telldir(DIR *dirp)
+DIR *opendir(const char *name)
{
- errno = 0;
-
- if(!dirp)
- {
- errno = EFAULT;
- return -1;
- }
- return dirp->dd_stat;
+ DIR *dirp = NULL;
+ wchar_t *wname = __get_buffer();
+ int size = 0;
+ if (!wname)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ size = MultiByteToWideChar(CP_UTF8, 0, name, -1, wname + 4, NTFS_MAX_PATH);
+ if (0 == size)
+ {
+ free(wname);
+ return NULL;
+ }
+ dirp = __internal_opendir(wname, size + 4);
+ free(wname);
+ return dirp;
}
-//
-// 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 seekdir(DIR *dirp, long lPos)
+DIR *_wopendir(const wchar_t *name)
{
- 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. */
- rewinddir(dirp);
-
- while((dirp->dd_stat < lPos) && readdir(dirp))
- ; /* do-nothing loop */
- }
+ DIR *dirp = NULL;
+ wchar_t *wname = __get_buffer();
+ int size = 0;
+ if (!wname)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ size = (int) wcslen(name);
+ if (size > NTFS_MAX_PATH)
+ {
+ free(wname);
+ return NULL;
+ }
+ memcpy(wname + 4, name, sizeof(wchar_t) * (size + 1));
+ dirp = __internal_opendir(wname, size + 5);
+ free(wname);
+ return dirp;
+}
+
+DIR *fdopendir(intptr_t fd)
+{
+ DIR *dirp = NULL;
+ wchar_t *wname = __get_buffer();
+ int size = 0;
+
+ if (!wname)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ size = GetFinalPathNameByHandleW((HANDLE) fd, wname + 4, NTFS_MAX_PATH,
+ FILE_NAME_NORMALIZED);
+ if (0 == size)
+ {
+ free(wname);
+ errno = ENOTDIR;
+ return NULL;
+ }
+ dirp = __internal_opendir(wname, size + 5);
+ free(wname);
+ return dirp;
}
-// EOF
+struct dirent *readdir(DIR *dirp)
+{
+ struct __dir *data = (struct __dir *) dirp;
+ if (!data)
+ {
+ errno = EBADF;
+ return NULL;
+ }
+ if (data->index < data->count)
+ {
+ return &data->entries[data->index++];
+ }
+ return NULL;
+}
+
+void seekdir(DIR *dirp, long int offset)
+{
+ if (dirp)
+ {
+ struct __dir *data = (struct __dir *) dirp;
+ data->index = (offset < data->count) ? offset : data->index;
+ }
+}
+
+void rewinddir(DIR *dirp)
+{
+ seekdir(dirp, 0);
+}
+
+long int telldir(DIR *dirp)
+{
+ if (!dirp)
+ {
+ errno = EBADF;
+ return -1;
+ }
+ return ((struct __dir *) dirp)->count;
+}
+
+intptr_t dirfd(DIR *dirp)
+{
+ if (!dirp)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ return ((struct __dir *) dirp)->fd;
+}
+#endif /* _WIN32 */
diff --git a/win32/win_opendir.h b/win32/win_opendir.h
index c1844e11..94c9d2c8 100644
--- a/win32/win_opendir.h
+++ b/win32/win_opendir.h
@@ -1,73 +1,130 @@
-//
-// 03/10/2006 James Haley
-//
-// For this module only:
-// This code is public domain. No change sufficient enough to constitute a
-// significant or original work has been made, and thus it remains as such.
-//
-//
-// DESCRIPTION:
-//
-// Implementation of POSIX opendir for Visual C++.
-// Derived from the MinGW C Library Extensions Source (released to the
-// public domain).
-//
-
-#ifndef I_OPNDIR_H__
-#define I_OPNDIR_H__
-
-#include <io.h>
-
-#ifndef FILENAME_MAX
-#define FILENAME_MAX 260
-#endif
+/*
+MIT License
+Copyright (c) 2019 win32ports
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifdef _WIN32
+
+#ifndef WIN_OPENDIR_H
+#define WIN_OPENDIR_H
+
+#include <sys/types.h>
+
+#ifndef NAME_MAX
+#define NAME_MAX 260
+#endif /* NAME_MAX */
+
+#ifndef DT_UNKNOWN
+#define DT_UNKNOWN 0
+#endif /* DT_UNKNOWN */
+
+#ifndef DT_FIFO
+#define DT_FIFO 1
+#endif /* DT_FIFO */
+
+#ifndef DT_CHR
+#define DT_CHR 2
+#endif /* DT_CHR */
+
+#ifndef DT_DIR
+#define DT_DIR 4
+#endif /* DT_DIR */
+
+#ifndef DT_BLK
+#define DT_BLK 6
+#endif /* DT_BLK */
+
+#ifndef DT_REG
+#define DT_REG 8
+#endif /* DT_REF */
+
+#ifndef DT_LNK
+#define DT_LNK 10
+#endif /* DT_LNK */
+
+#ifndef DT_SOCK
+#define DT_SOCK 12
+#endif /* DT_SOCK */
+
+#ifndef DT_WHT
+#define DT_WHT 14
+#endif /* DT_WHT */
+
+#ifndef _DIRENT_HAVE_D_NAMLEN
+#define _DIRENT_HAVE_D_NAMLEN 1
+#endif /* _DIRENT_HAVE_D_NAMLEN */
+
+#ifndef _DIRENT_HAVE_D_RECLEN
+#define _DIRENT_HAVE_D_RECLEN 1
+#endif /* _DIRENT_HAVE_D_RECLEN */
+
+#ifndef _DIRENT_HAVE_D_OFF
+#define _DIRENT_HAVE_D_OFF 1
+#endif /* _DIRENT_HAVE_D_OFF */
+
+#ifndef _DIRENT_HAVE_D_TYPE
+#define _DIRENT_HAVE_D_TYPE 1
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+typedef void *DIR;
+
+typedef struct ino_t
+{
+ unsigned long long serial;
+ unsigned char fileid[16];
+} __ino_t;
struct dirent
{
- long d_ino; /* Always zero. */
- unsigned short d_reclen; /* Always zero. */
- unsigned short d_namlen; /* Length of name in d_name. */
- char d_name[FILENAME_MAX]; /* File name. */
+ __ino_t d_ino;
+ off_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_namelen;
+ unsigned char d_type;
+ char d_name[NAME_MAX];
};
-/*
- * This is an internal data structure. Good programmers will not use it
- * except as an argument to one of the functions below.
- * dd_stat field is now int (was short in older versions).
- */
-typedef struct
+struct __dir
{
- /* disk transfer area for this dir */
- struct _finddata_t dd_dta;
+ struct dirent *entries;
+ intptr_t fd;
+ long int count;
+ long int index;
+};
+
+int closedir(DIR *dirp);
+
+DIR *opendir(const char *name);
+
+DIR *_wopendir(const wchar_t *name);
- /* dirent struct to return from dir (NOTE: this makes this thread
- * safe as long as only one thread uses a particular DIR struct at
- * a time) */
- struct dirent dd_dir;
+DIR *fdopendir(intptr_t fd);
- /* _findnext handle */
- intptr_t dd_handle;
+struct dirent *readdir(DIR *dirp);
- /*
- * Status of search:
- * 0 = not started yet (next entry to read is first entry)
- * -1 = off the end
- * positive = 0 based index of next entry
- */
- int dd_stat;
+void seekdir(DIR *dirp, long int offset);
- /* given path for dir with search pattern (struct is extended) */
- char dd_name[1];
-} DIR;
+void rewinddir(DIR *dirp);
-DIR *opendir(const char *);
-struct dirent *readdir(DIR *);
-int closedir(DIR *);
-void rewinddir(DIR *);
-long telldir(DIR *);
-void seekdir(DIR *, long);
+long int telldir(DIR *dirp);
-#endif
+intptr_t dirfd(DIR *dirp);
-// EOF
+#endif /* WIN_OPENDIR_H */
+#endif /* _WIN32 */