about summary refs log tree commit diff stats
path: root/dirent
diff options
context:
space:
mode:
authorRory Bradford <roryrjb@gmail.com>2022-11-19 11:11:26 +0000
committerRory Bradford <roryrjb@gmail.com>2022-11-19 11:18:40 +0000
commit0f92f7352d1964a9859868439e8ded2c4de2111e (patch)
tree14c690402fa28c3b465ec00d2fe767054a1a3331 /dirent
parentfbb7c479de91b197c6c501c2023bf564a6a7610f (diff)
downloadrf-0f92f7352d1964a9859868439e8ded2c4de2111e.tar.gz
Full native win32 support
This will now compile with MSVC using the make.bat batch file. It does
however bring in some additional third party dependencies: ports of
dirent and getopt (something I'd ideally like to work on in the future).

Signed-off-by: Rory Bradford <roryrjb@gmail.com>
Diffstat (limited to 'dirent')
-rw-r--r--dirent/.gitignore27
-rw-r--r--dirent/CMakeLists.txt64
-rw-r--r--dirent/CONTRIBUTING.md41
-rw-r--r--dirent/ChangeLog129
-rw-r--r--dirent/LICENSE21
-rw-r--r--dirent/README.md176
-rw-r--r--dirent/distclean.cmake62
-rw-r--r--dirent/examples/cat.c127
-rw-r--r--dirent/examples/dir.c234
-rw-r--r--dirent/examples/du.c199
-rw-r--r--dirent/examples/find.c184
-rw-r--r--dirent/examples/locate.c262
-rw-r--r--dirent/examples/ls.c148
-rw-r--r--dirent/examples/scandir.c152
-rw-r--r--dirent/examples/updatedb.c206
-rw-r--r--dirent/include/dirent.h1212
-rw-r--r--dirent/tests/1/dir/readme.txt3
-rw-r--r--dirent/tests/1/file0
-rw-r--r--dirent/tests/2/Testfile-1.2.3.dat0
-rw-r--r--dirent/tests/2/file.txt1
-rw-r--r--dirent/tests/3/3zero.dat0
-rw-r--r--dirent/tests/3/666.dat0
-rw-r--r--dirent/tests/3/Qwerty-my-aunt.dat0
-rw-r--r--dirent/tests/3/README.txt2
-rw-r--r--dirent/tests/3/aaa.dat0
-rw-r--r--dirent/tests/3/dirent.dat0
-rw-r--r--dirent/tests/3/empty.dat0
-rw-r--r--dirent/tests/3/sane-1.12.0.dat0
-rw-r--r--dirent/tests/3/sane-1.2.30.dat0
-rw-r--r--dirent/tests/3/sane-1.2.4.dat0
-rw-r--r--dirent/tests/3/zebra.dat0
-rw-r--r--dirent/tests/4/asidofilusphosphorus0
-rw-r--r--dirent/tests/4/bubblebob0
-rw-r--r--dirent/tests/4/celsiusfortissimo0
-rw-r--r--dirent/tests/t-compile.c46
-rw-r--r--dirent/tests/t-cplusplus.cpp155
-rw-r--r--dirent/tests/t-dirent.c608
-rw-r--r--dirent/tests/t-scandir.c276
-rw-r--r--dirent/tests/t-strverscmp.c206
-rw-r--r--dirent/tests/t-telldir.c165
-rw-r--r--dirent/tests/t-unicode.c381
-rw-r--r--dirent/tests/t-utf8.c238
42 files changed, 5325 insertions, 0 deletions
diff --git a/dirent/.gitignore b/dirent/.gitignore
new file mode 100644
index 0000000..7def436
--- /dev/null
+++ b/dirent/.gitignore
@@ -0,0 +1,27 @@
+/CMakeCache.txt
+/CMakeFiles
+/CTestTestfile.cmake
+/DartConfiguration.tcl
+/Makefile
+/Testing
+/Win32
+/cmake_install.cmake
+/find
+/locate
+/ls
+/scandir
+/cat
+/dir
+/du
+/t-compile
+/t-dirent
+/t-scandir
+/t-cplusplus
+/t-unicode
+/t-strverscmp
+/t-telldir
+/t-utf8
+/updatedb
+/*.filters
+/*.vcxproj
+/*.dir
diff --git a/dirent/CMakeLists.txt b/dirent/CMakeLists.txt
new file mode 100644
index 0000000..0e002c8
--- /dev/null
+++ b/dirent/CMakeLists.txt
@@ -0,0 +1,64 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(dirent LANGUAGES C CXX)
+
+# User options
+option(DIRENT_BUILD_TESTS "Build bundled tests" ON)
+message(STATUS "Build test and example programs: ${DIRENT_BUILD_TESTS}")
+
+# Initialize C and C++ compilers
+enable_language(C CXX)
+
+# Compile in debug mode by default
+if(NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE Debug
+        CACHE STRING
+        "Type of build: None Debug Release RelWithDebInfo MinSizeRel."
+        FORCE
+    )
+endif(NOT CMAKE_BUILD_TYPE)
+
+# Use the include directory only on Windows
+if(WIN32)
+    include_directories(${CMAKE_SOURCE_DIR}/include)
+endif(WIN32)
+
+# Install dirent.h
+if(WIN32)
+    install(FILES include/dirent.h DESTINATION include)
+endif(WIN32)
+
+# Add distclean target
+add_custom_target(distclean
+    COMMAND ${CMAKE_BUILD_TOOL} clean
+    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/distclean.cmake
+)
+
+# Build example programs
+if(DIRENT_BUILD_TESTS)
+    add_executable(find examples/find.c)
+    add_executable(ls examples/ls.c)
+    add_executable(locate examples/locate.c)
+    add_executable(updatedb examples/updatedb.c)
+    add_executable(scandir examples/scandir.c)
+    add_executable(cat examples/cat.c)
+    add_executable(dir examples/dir.c)
+    add_executable(du examples/du.c)
+
+    # Build test programs
+    include(CTest)
+    add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C ${CMAKE_CFG_INTDIR})
+    function(add_test_executable TEST_NAME)
+        add_executable(${TEST_NAME} EXCLUDE_FROM_ALL ${ARGN})
+        add_test(NAME ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND $<TARGET_FILE:${TEST_NAME}>)
+        add_dependencies(check ${TEST_NAME})
+    endfunction(add_test_executable)
+
+    add_test_executable(t-compile tests/t-compile.c)
+    add_test_executable(t-dirent tests/t-dirent.c)
+    add_test_executable(t-scandir tests/t-scandir.c)
+    add_test_executable(t-unicode tests/t-unicode.c)
+    add_test_executable(t-cplusplus tests/t-cplusplus.cpp)
+    add_test_executable(t-telldir tests/t-telldir.c)
+    add_test_executable(t-strverscmp tests/t-strverscmp.c)
+    add_test_executable(t-utf8 tests/t-utf8.c)
+endif(DIRENT_BUILD_TESTS)
diff --git a/dirent/CONTRIBUTING.md b/dirent/CONTRIBUTING.md
new file mode 100644
index 0000000..bff76c3
--- /dev/null
+++ b/dirent/CONTRIBUTING.md
@@ -0,0 +1,41 @@
+# Contributing to Dirent
+
+Dirent is an open source project and we love to receive contributions from our
+community -- you!  There are many ways to contribute, from writing tutorials
+or blog posts, improving the documentation, submitting bug reports and
+feature requests, or writing code which can be incorporated into Dirent.
+
+For example, we are looking for contributions which
+
+1. Improve portability of code developed for Unix/Linux to Microsoft Windows.
+2. Make Dirent easier to use and/or more useful for programmers working on
+   Microsoft Windows platform.
+3. Remove compiler warnings or fix bugs.
+
+## How to Suggest a Feature
+
+We use Github to host code, to track issues and to discuss about Dirent.  If
+you would like to suggest a feature, then open an issue at Github.
+
+## How to Report a Bug
+
+If you have trouble using Dirent, then try to repeat the problem with the
+latest version found at Github.  If the problem remains, then open an issue
+at Github.
+
+## How to Submit a Fix or an Enhancement
+
+If you wish to contribute code, then fork the repo and test the change in your
+private repo first.  If the change works for you and you would like to donate
+the change back to the community, then make a pull request at Github.
+
+Before making a pull request, please consider that:
+
+1. We can only accept contributions which are compatible with [MIT Software
+   License](LICENSE).
+2. By making a pull request, you agree to transfer all copyrights to us.
+3. Our code will be reviewed and edited by us.  In particular, we may
+   reformat your code according to the [Linux Kernel Coding
+   Style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html)
+
+
diff --git a/dirent/ChangeLog b/dirent/ChangeLog
new file mode 100644
index 0000000..d79cea2
--- /dev/null
+++ b/dirent/ChangeLog
@@ -0,0 +1,129 @@
+2018-05-08  Toni Rönkkö
+
+	* Version 1.23.2: fixes bad scandir prototype.
+
+2017-08-27  Toni Rönkkö
+
+	* Version 1.23: support readdir_r and scandir functions.
+
+2017-07-18  Toni Rönkkö
+
+	* Created release branches v1.22 and v1.21 to Git.  Published version
+	1.22 at softagalleria.net.
+
+2016-09-11  Toni Rönkkö
+
+	* Version 1.22: added support for CMake.  Thanks to Paul Fultz II.
+
+2014-09-25  Toni Rönkkö
+
+	* Version 1.21: compiles correctly under Open Watcom.  Thanks to
+	Virgil Banowetz for a patch!
+
+2014-04-07  Toni Rönkkö
+
+	* Version 1.20.1: the zip file from the previous version did not open
+	correctly with Microsoft's compressed folders.  Thanks to Alexandre
+	for info!
+
+2014-03-17  Toni Ronkko
+
+	* Version 1.20: dirent.h compiles correctly in 64-bit architecture.
+	Thanks to Aaron Simmons!
+
+2014-03-03  Toni Ronkko
+
+	* Version 1.13.2: define DT_LNK for compatibility with Unix
+	programs.  Thanks to Joel Bruick for suggestion!
+
+2013-01-27  Toni Ronkko
+
+	* Version 1.13.1: patch from Edward Berner fixes set_errno() on
+	Windows NT 4.0.
+
+	* Revised wcstombs() and mbstowcs() wrappers to make sure that they do
+	not write past their target string.
+
+	* PATH_MAX from windows.h includes zero terminator so there is no
+	need to add one extra byte to variables and structures.
+
+2012-12-12  Toni Ronkko
+
+	* Version 1.13: use the traditional 8+3 file naming scheme if a file
+	name cannot be represented in the default ANSI code page.  Now
+	compiles again with MSVC 6.0.  Thanks to Konstantin Khomoutov for
+	testing.
+
+2012-10-01  Toni Ronkko
+
+	* Version 1.12.1: renamed wide-character DIR structure _wDIR to
+	_WDIR (with capital W) in order to maintain compatibility with MingW.
+
+2012-09-30  Toni Ronkko
+
+	* Version 1.12: define PATH_MAX and NAME_MAX.  Added wide-character
+	variants _wDIR, _wdirent, _wopendir(), _wreaddir(), _wclosedir() and
+	_wrewinddir().  Thanks to Edgar Buerkle and Jan Nijtmans for ideas
+	and code.
+
+	* Now avoiding windows.h.  This allows dirent.h to be integrated
+	more easily into programs using winsock.  Thanks to Fernando
+	Azaldegui.
+
+2011-03-15  Toni Ronkko
+
+	* Version 1.11: defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0.
+
+2010-08-11  Toni Ronkko
+
+	* Version 1.10: added d_type and d_namlen fields to dirent structure.
+	The former is especially useful for determining whether directory
+	entry represents a file or a directory.  For more information, see
+	http://www.delorie.com/gnu/docs/glibc/libc_270.html
+
+	* Improved conformance to the standards.  For example, errno is now
+	set properly on failure and assert() is never used.  Thanks to Peter
+	Brockam for suggestions.
+
+	* Fixed a bug in rewinddir(): when using relative directory names,
+	change of working directory no longer causes rewinddir() to fail.
+
+2009-12-15  John Cunningham
+
+	* Version 1.9: added rewinddir member function
+
+2008-01-18  Toni Ronkko
+
+	* Version 1.8: Using FindFirstFileA and WIN32_FIND_DATAA to avoid
+	converting string between multi-byte and unicode representations.
+	This makes the code simpler and also allows the code to be compiled
+	under MingW.  Thanks to Azriel Fasten for the suggestion.
+
+2007-03-04  Toni Ronkko
+
+	* Bug fix: due to the strncpy_s() function this file only compiled in
+	Visual Studio 2005.  Using the new string functions only when the
+	compiler version allows.
+
+2006-11-02  Toni Ronkko
+
+	* Major update: removed support for Watcom C, MS-DOS and Turbo C to
+	simplify the file, updated the code to compile cleanly on Visual
+	Studio 2005 with both unicode and multi-byte character strings,
+	removed rewinddir() as it had a bug.
+
+2006-08-20  Toni Ronkko
+
+	* Removed all remarks about MSVC 1.0, which is antiqued now.
+	Simplified comments by removing SGML tags.
+
+2002-05-14  Toni Ronkko
+
+	* Embedded the function definitions directly to the header so that no
+	source modules need to be included in the Visual Studio project.
+	Removed all the dependencies to other projects so that this header
+	file can be used independently.
+
+1998-05-28  Toni Ronkko
+
+	* First version.
diff --git a/dirent/LICENSE b/dirent/LICENSE
new file mode 100644
index 0000000..af04360
--- /dev/null
+++ b/dirent/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 1998-2019 Toni Ronkko
+
+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.
diff --git a/dirent/README.md b/dirent/README.md
new file mode 100644
index 0000000..93e21d6
--- /dev/null
+++ b/dirent/README.md
@@ -0,0 +1,176 @@
+# Dirent
+
+Dirent is a programming interface for retrieving information about files and
+directories in C and C++ languages.  This project provides a Dirent interface
+for Microsoft Visual Studio.
+
+
+# Installation
+
+Download the latest Dirent installation package from
+[GitHub](https://github.com/tronkko/dirent/releases) and
+unpack the installation file with 7-zip, for example.  The installation
+package contains ``include/dirent.h`` file as well as a few example and test
+programs.
+
+To make Dirent available to all C/C++ projects in your machine, simply copy
+``include/dirent.h`` file to the system include directory, e.g.
+``C:\Program Files\Microsoft Visual Studio 9.0\VC\include``.  Everything you
+need is included in the single ``dirent.h`` file, and you can start using
+Dirent immediately -- there is no need to add files to your Visual Studio
+project.
+
+Alternatively, if you wish to distribute ``dirent.h`` alongside with your own
+project, then copy ``include/dirent.h`` file to a new sub-directory within
+your project and add that directory to include path on Windows while omitting
+the directory under Linux/UNIX.  This allows your project to be compiled
+against native ``dirent.h`` on Linux/UNIX while substituting the functionality
+on Microsoft Windows.
+
+
+# Example Programs
+
+The installation package contains example programs:
+
+Program  | Purpose
+-------- | -----------------------------------------------------------------
+ls       | List files in a directory, e.g. ls "c:\Program Files"
+find     | Find files in subdirectories, e.g. find "c:\Program Files\CMake"
+updatedb | Build database of files in a drive, e.g. updatedb c:\
+locate   | Locate a file from database, e.g. locate notepad
+scandir  | Printed sorted list of file names in a directory, e.g. scandir .
+du       | Compute disk usage, e.g. du "C:\Program Files"
+cat      | Print a text file to screen, e.g. cat include/dirent.h
+
+In order to build the example programs, first install
+[CMake](https://cmake.org/) to your machine.  Then, open command prompt and
+create a temporary directory ``c:\temp\dirent`` for the build files as
+
+```
+c:\
+mkdir temp
+mkdir temp\dirent
+cd temp\dirent
+```
+
+Generate build files as
+
+```
+cmake d:\dirent
+```
+
+where ``d:\dirent`` is the root directory of the Dirent package containing
+this README.md file.
+
+Once CMake is finished, open Visual Studio, load the generated ``dirent.sln``
+file from the build directory and build the whole solution.
+
+Once the build completes, open command prompt and cd to the Debug directory to
+run the example programs.  For example:
+
+```
+cd c:\temp\dirent\Debug
+.\ls .
+```
+
+Visual Studio project also contains a solution named ``check`` which can be
+used to verify that Dirent API works as expected.  Just build the solution
+from Visual Studio to run the test programs.
+
+
+# UTF-8 Support
+
+By default, file and directory names in the Dirent API are expressed in the
+currently selected windows codepage.  If you wish to use UTF-8 character
+encoding, then replace the main function with \_main function and convert
+wide-character arguments to UTF-8 strings as demonstrated in the snippet
+below.
+
+```
+/* This is your true main function */
+static int
+_main(int argc, wchar_t *argv[])
+{
+	/* ... */
+}
+
+/* Convert arguments to UTF-8 */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+```
+
+For more information on UTF-8 support, please see setlocale in Visual Studio
+[C runtime library reference](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-160#utf-8-support).
+
+
+# Contributing
+
+We love to receive contributions from you.  See the
+[CONTRIBUTING](CONTRIBUTING.md) file for details.
+
+
+# Copying
+
+Dirent may be freely distributed under the MIT license.  See the
+[LICENSE](LICENSE) file for details.
+
+
+# Alternatives to Dirent
+
+I ported Dirent to Microsoft Windows in 1998 when only a few alternatives
+were available.  However, the situation has changed since then and nowadays
+both [Cygwin](http://www.cygwin.com) and [MingW](http://www.mingw.org)
+allow you to compile a great number of UNIX programs in Microsoft Windows.
+They both provide a full Dirent API as well as many other UNIX APIs.  MingW
+can even be used for commercial applications!
+
diff --git a/dirent/distclean.cmake b/dirent/distclean.cmake
new file mode 100644
index 0000000..60e69ad
--- /dev/null
+++ b/dirent/distclean.cmake
@@ -0,0 +1,62 @@
+# Remove CMake generated temporary files
+set (cmake_generated
+    ${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj
+    ${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/CMakeCache.txt
+    ${CMAKE_BINARY_DIR}/CMakeFiles
+    ${CMAKE_BINARY_DIR}/CTestTestfile.cmake
+    ${CMAKE_BINARY_DIR}/Continuous.vcxproj
+    ${CMAKE_BINARY_DIR}/Continuous.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/DartConfiguration.tcl
+    ${CMAKE_BINARY_DIR}/Debug
+    ${CMAKE_BINARY_DIR}/Experimental.vcxproj
+    ${CMAKE_BINARY_DIR}/Experimental.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/INSTALL.vcxproj
+    ${CMAKE_BINARY_DIR}/INSTALL.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/Makefile
+    ${CMAKE_BINARY_DIR}/Nightly.vcxproj
+    ${CMAKE_BINARY_DIR}/Nightly.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/NightlyMemoryCheck.vcxproj
+    ${CMAKE_BINARY_DIR}/NightlyMemoryCheck.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/RUN_TESTS.vcxproj
+    ${CMAKE_BINARY_DIR}/RUN_TESTS.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/Testing
+    ${CMAKE_BINARY_DIR}/Win32
+    ${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj
+    ${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/check.vcxproj
+    ${CMAKE_BINARY_DIR}/check.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/cmake_install.cmake
+    ${CMAKE_BINARY_DIR}/dirent.sln
+    ${CMAKE_BINARY_DIR}/distclean.vcxproj
+    ${CMAKE_BINARY_DIR}/distclean.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/find
+    ${CMAKE_BINARY_DIR}/find.dir
+    ${CMAKE_BINARY_DIR}/find.vcxproj
+    ${CMAKE_BINARY_DIR}/find.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/locate
+    ${CMAKE_BINARY_DIR}/locate.dir
+    ${CMAKE_BINARY_DIR}/locate.vcxproj
+    ${CMAKE_BINARY_DIR}/locate.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/ls
+    ${CMAKE_BINARY_DIR}/ls.dir
+    ${CMAKE_BINARY_DIR}/ls.vcxproj
+    ${CMAKE_BINARY_DIR}/ls.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/t-compile
+    ${CMAKE_BINARY_DIR}/t-compile.dir
+    ${CMAKE_BINARY_DIR}/t-compile.vcxproj
+    ${CMAKE_BINARY_DIR}/t-compile.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/t-dirent
+    ${CMAKE_BINARY_DIR}/t-dirent.dir
+    ${CMAKE_BINARY_DIR}/t-dirent.vcxproj
+    ${CMAKE_BINARY_DIR}/t-dirent.vcxproj.filters
+    ${CMAKE_BINARY_DIR}/updatedb
+    ${CMAKE_BINARY_DIR}/updatedb.dir
+    ${CMAKE_BINARY_DIR}/updatedb.vcxproj
+    ${CMAKE_BINARY_DIR}/updatedb.vcxproj.filters
+)
+foreach (file ${cmake_generated})
+    if (EXISTS ${file})
+        file (REMOVE_RECURSE ${file})
+    endif()
+endforeach (file)
diff --git a/dirent/examples/cat.c b/dirent/examples/cat.c
new file mode 100644
index 0000000..5346922
--- /dev/null
+++ b/dirent/examples/cat.c
@@ -0,0 +1,127 @@
+/*
+ * Output contents of a file.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a file name argument.  For example, command
+ *
+ *     cat include\dirent.h
+ *
+ * will output the dirent.h to screen.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <locale.h>
+
+static void output_file(const char *fn);
+static int _main(int argc, char *argv[]);
+
+static int
+_main(int argc, char *argv[])
+{
+	/* Require at least one file */
+	if (argc == 1) {
+		fprintf(stderr, "Usage: cat filename\n");
+		return EXIT_FAILURE;
+	}
+
+	/* For each file name argument in command line */
+	int i = 1;
+	while (i < argc) {
+		output_file(argv[i]);
+		i++;
+	}
+	return EXIT_SUCCESS;
+}
+
+/*
+ * Output file to screen
+ */
+static void
+output_file(const char *fn)
+{
+	/* Open file */
+	FILE *fp = fopen(fn, "r");
+	if (!fp) {
+		/* Could not open directory */
+		fprintf(stderr, "Cannot open %s (%s)\n", fn, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	/* Output file to screen */
+	size_t n;
+	do {
+		/* Read some bytes from file */
+		char buffer[4096];
+		n = fread(buffer, 1, 4096, fp);
+
+		/* Output bytes to screen */
+		fwrite(buffer, 1, n, stdout);
+	} while (n != 0);
+
+	/* Close file */
+	fclose(fp);
+}
+
+/* Stub for converting arguments to UTF-8 on Windows */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n + 1);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n + 1, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+
diff --git a/dirent/examples/dir.c b/dirent/examples/dir.c
new file mode 100644
index 0000000..46e38de
--- /dev/null
+++ b/dirent/examples/dir.c
@@ -0,0 +1,234 @@
+/*
+ * List files with date, type and size.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a directory name argument.  For example, command
+ *
+ *     dir "C:\Users\User 1\Documents"
+ *
+ * might output something like
+ *
+ *     Directory of c:\Users\User 1\Documents
+ *
+ *     2021-06-06  20:04       <DIR>               .
+ *     2021-07-20  13:42       <DIR>               ..
+ *     2020-06-21  15:00                       402 desktop.ini
+ *     2020-06-21  15:00       <DIR>               Omat kuvatiedostot
+ *     2020-06-21  15:00       <DIR>               Omat musiikkitiedostot
+ *     2020-06-21  15:00       <DIR>               Omat videotiedostot
+ *     2018-12-21  18:34       <DIR>               Visual Studio 2017
+ *                        3 File(s)       402 bytes
+ *                        7 Dir(s)
+ *
+ * The dir command provided by this file is only an example: the command
+ * does not have any fancy options.
+ *
+ * Copyright (C) 2006-2012 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <locale.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#define ERR_MSG_LEN 256
+
+static void list_directory(const char* dirname);
+static void fail(const char* dirname);
+static int _main(int argc, char *argv[]);
+
+static int
+_main(int argc, char* argv[])
+{
+	/* For each directory in command line */
+	int i = 1;
+	while (i < argc) {
+		list_directory(argv[i]);
+		i++;
+	}
+
+	/* List current working directory if no arguments on command line */
+	if (argc == 1)
+		list_directory(".");
+
+	return EXIT_SUCCESS;
+}
+
+/* List files and file sizes in directory */
+static void
+list_directory(const char* dirname)
+{
+	char path[PATH_MAX + 2];
+	char *p = path;
+	char *end = &path[PATH_MAX];
+
+	/* Copy directory name to path */
+	const char *src = dirname;
+	while (p < end && *src != '\0') {
+		*p++ = *src++;
+	}
+
+	/* Get final character of directory name */
+	char c = (path < p ? p[-1] : ':');
+
+	/* Append directory separator if not already there */
+	if (c != ':' && c != '/' && c != '\\')
+		*p++ = '/';
+
+	/* Print directory name to screen */
+	printf("Directory of %s\n\n", dirname);
+
+	/* Open directory stream */
+	DIR *dir = opendir(dirname);
+	if (!dir) {
+		/* Could not open directory */
+		fprintf(stderr,
+			"Cannot open %s (%s)\n", dirname, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	/* Loop through file names */
+	int filecount = 0;
+	int dircount = 0;
+	long long bytecount = 0;
+	struct dirent *ent;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Append file name to path */
+		char *q = p;
+		src = ent->d_name;
+		while (q < end && *src != '\0') {
+			*q++ = *src++;
+		}
+		*q = '\0';
+
+		/* Get file properties such as size and modification time */
+		struct stat stbuf;
+		if (stat(path, &stbuf) == /*error*/-1) {
+			fail(path);
+		}
+
+		/* Get file type from stat buffer */
+		const char *type;
+		if (S_ISDIR(stbuf.st_mode)) {
+			/* Directory */
+			type = "<DIR>";
+		} else if (S_ISREG(stbuf.st_mode)) {
+			/* Regular file */
+			type = "";
+		} else if (S_ISLNK(stbuf.st_mode)) {
+			/* Link */
+			type = "<LNK>";
+		} else {
+			/* Named pipe, socket, character device or else */
+			type = "<UNK>";
+		}
+
+		/* Get last modification date as a string */
+		struct tm *tp = localtime(&stbuf.st_mtime);
+		char mtime[40];
+		sprintf(mtime, "%04d-%02d-%02d  %02d:%02d",
+			tp->tm_year + 1900,
+			tp->tm_mon + 1,
+			tp->tm_mday,
+			tp->tm_hour,
+			tp->tm_min);
+
+		/* Get file size as a string */
+		char size[40];
+		if (S_ISREG(stbuf.st_mode)) {
+			sprintf(size, "%lld", (long long) stbuf.st_size);
+		} else {
+			size[0] = '\0';
+		}
+
+		/* Output file info */
+		printf("%-20s    %-5s  %12s %s\n",
+			mtime, type, size, ent->d_name);
+
+		/* Compute totals */
+		if (S_ISREG(stbuf.st_mode)) {
+			filecount++;
+			bytecount += (long long) stbuf.st_size;
+		}
+		if (S_ISDIR(stbuf.st_mode)) {
+			dircount++;
+		}
+	}
+
+	/* Output sums */
+	printf("%20d File(s) %12lld bytes\n", filecount, bytecount);
+	printf("%20d Dir(s)\n", dircount);
+}
+
+/* Print error message and exit with error condition */
+static void
+fail(const char* msg)
+{
+	/* Output error message */
+	perror(msg);
+
+	/* Exit the program immediately */
+	exit(EXIT_FAILURE);
+}
+
+/* Stub for converting arguments to UTF-8 on Windows */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n + 1);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n + 1, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+
diff --git a/dirent/examples/du.c b/dirent/examples/du.c
new file mode 100644
index 0000000..a5832c4
--- /dev/null
+++ b/dirent/examples/du.c
@@ -0,0 +1,199 @@
+/*
+ * Compute disk usage of files and sub-directories in bytes.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a directory name argument.  For example, command
+ *
+ *     du "c:\Program Files"
+ *
+ * might produce listing such as
+ *
+ *     5204927     7-Zip
+ *     140046882   CCleaner
+ *     83140342    CMake
+ *     2685264     Internet Explorer
+ *     686314712   LibreOffice
+ *     214025459   Mozilla Firefox
+ *     174753900   VideoLAN
+ *
+ * If you compare this program to a genuine du command in Linux, then be ware
+ * directories themselves consume some space in Linux.  This program, however,
+ * only counts the files and hence the size will always be smaller than that
+ * reported by Linux du.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <locale.h>
+
+static long long list_directory(const char *dirname, int level);
+static int _main(int argc, char *argv[]);
+
+static int
+_main(int argc, char *argv[])
+{
+	/* For each directory in command line */
+	int i = 1;
+	while (i < argc) {
+		list_directory(argv[i], 0);
+		i++;
+	}
+
+	/* List current working directory if no arguments on command line */
+	if (argc == 1)
+		list_directory(".", 0);
+
+	return EXIT_SUCCESS;
+}
+
+/* Find files and subdirectories recursively; list their sizes */
+static long long
+list_directory(const char *dirname, int level)
+{
+	char buffer[PATH_MAX + 2];
+	char *p = buffer;
+	char *end = &buffer[PATH_MAX];
+	
+	/* Copy directory name to buffer */
+	const char *src = dirname;
+	while (p < end && *src != '\0') {
+		*p++ = *src++;
+	}
+	*p = '\0';
+
+	/* Get final character of directory name */
+	char c;
+	if (buffer < p)
+		c = p[-1];
+	else
+		c = ':';
+
+	/* Append directory separator if not already there */
+	if (c != ':' && c != '/' && c != '\\')
+		*p++ = '/';
+
+	/* Open directory stream */
+	DIR *dir = opendir(dirname);
+	if (!dir) {
+		fprintf(stderr,
+			"Cannot open %s (%s)\n", dirname, strerror(errno));
+		return 0LL;
+	}
+
+	/* Compute total disk usage of all files and directories */
+	struct stat stbuf;
+	struct dirent *ent;
+	long long total = 0;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Skip pseudo directories . and .. */
+		if (strcmp(ent->d_name, ".") == 0
+			|| strcmp(ent->d_name, "..") == 0)
+			continue;
+
+		/* Skip links as they consume no space */
+		if (ent->d_type == DT_LNK)
+			continue;
+
+		/* Skip device entries */
+		if (ent->d_type != DT_REG && ent->d_type != DT_DIR)
+			continue;
+
+		/* Append file name to buffer */
+		src = ent->d_name;
+		char *q = p;
+		while (q < end && *src != '\0') {
+			*q++ = *src++;
+		}
+		*q = '\0';
+
+		/* Add file size */
+		long long size = 0;
+		if (ent->d_type == DT_REG) {
+			if (stat(buffer, &stbuf) == /*Error*/-1) {
+				fprintf(stderr, "Cannot access %s\n", buffer);
+				continue;
+			}
+			size += (long long) stbuf.st_size;
+		}
+
+		/* Compute size of subdirectories recursively */
+		if (ent->d_type == DT_DIR)
+			size += list_directory(buffer, level + 1);
+
+		/* Update total size of directory */
+		total += size;
+
+		/* Output file/directory size in bytes */
+		if (level == 0)
+			printf("%-10lld  %s\n", size, ent->d_name);
+	}
+
+	closedir(dir);
+
+	/* Return total size of directory */
+	return total;
+}
+
+/* Stub for converting arguments to UTF-8 on Windows */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n + 1);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n + 1, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+
diff --git a/dirent/examples/find.c b/dirent/examples/find.c
new file mode 100644
index 0000000..3e53d49
--- /dev/null
+++ b/dirent/examples/find.c
@@ -0,0 +1,184 @@
+/*
+ * List files and directories recursively.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a directory name argument.  For example, command
+ *
+ *     find "C:\Program Files"
+ *
+ * will output thousands of file names such as
+ *
+ *     c:\Program Files/7-Zip/7-zip.chm
+ *     c:\Program Files/7-Zip/7-zip.dll
+ *     c:\Program Files/7-Zip/7z.dll
+ *     c:\Program Files/Adobe/Reader 10.0/Reader/logsession.dll
+ *     c:\Program Files/Adobe/Reader 10.0/Reader/LogTransport2.exe
+ *     c:\Program Files/Windows NT/Accessories/wordpad.exe
+ *     c:\Program Files/Windows NT/Accessories/write.wpc
+ *
+ * The find command provided by this file is only an example: the command does
+ * not provide options to restrict the output to certain files as the Linux
+ * version does.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <locale.h>
+
+static int find_directory(const char *dirname);
+static int _main(int argc, char *argv[]);
+
+int
+_main(int argc, char *argv[])
+{
+	/* For each directory in command line */
+	int i = 1;
+	while (i < argc) {
+		if (!find_directory(argv[i]))
+			exit(EXIT_FAILURE);
+		i++;
+	}
+
+	/* List current working directory if no arguments on command line */
+	if (argc == 1)
+		find_directory(".");
+
+	return EXIT_SUCCESS;
+}
+
+/* Find files and subdirectories recursively */
+static int
+find_directory(const char *dirname)
+{
+	char buffer[PATH_MAX + 2];
+	char *p = buffer;
+	char *end = &buffer[PATH_MAX];
+
+	/* Copy directory name to buffer */
+	const char *src = dirname;
+	while (p < end && *src != '\0') {
+		*p++ = *src++;
+	}
+	*p = '\0';
+
+	/* Open directory stream */
+	DIR *dir = opendir(dirname);
+	if (!dir) {
+		/* Could not open directory */
+		fprintf(stderr,
+			"Cannot open %s (%s)\n", dirname, strerror(errno));
+		return /*failure*/ 0;
+	}
+
+	/* Print all files and directories within the directory */
+	struct dirent *ent;
+	while ((ent = readdir(dir)) != NULL) {
+		char *q = p;
+		char c;
+
+		/* Get final character of directory name */
+		if (buffer < q)
+			c = q[-1];
+		else
+			c = ':';
+
+		/* Append directory separator if not already there */
+		if (c != ':' && c != '/' && c != '\\')
+			*q++ = '/';
+
+		/* Append file name */
+		src = ent->d_name;
+		while (q < end && *src != '\0') {
+			*q++ = *src++;
+		}
+		*q = '\0';
+
+		/* Decide what to do with the directory entry */
+		switch (ent->d_type) {
+		case DT_LNK:
+		case DT_REG:
+			/* Output file name with directory */
+			printf("%s\n", buffer);
+			break;
+
+		case DT_DIR:
+			/* Scan sub-directory recursively */
+			if (strcmp(ent->d_name, ".") != 0
+				&&  strcmp(ent->d_name, "..") != 0) {
+				find_directory(buffer);
+			}
+			break;
+
+		default:
+			/* Ignore device entries */
+			/*NOP*/;
+		}
+
+	}
+
+	closedir(dir);
+	return /*success*/ 1;
+}
+
+/* Stub for converting arguments to UTF-8 on Windows */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n + 1);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n + 1, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+
diff --git a/dirent/examples/locate.c b/dirent/examples/locate.c
new file mode 100644
index 0000000..60bea4d
--- /dev/null
+++ b/dirent/examples/locate.c
@@ -0,0 +1,262 @@
+/*
+ * Find file name from locatedb database.
+ *
+ * Compile and run updatedb command first to create locate.db file.  Then,
+ * compile this program with Visual Studio and run the program in console with
+ * a file name argument.  For example, the command
+ *
+ *     locate autoexec
+ *
+ * might output something like
+ *
+ *     c:/AUTOEXEC.BAT
+ *     c:/WINDOWS/repair/autoexec.nt
+ *     c:/WINDOWS/system32/AUTOEXEC.NT
+ *
+ * Be ware that this file uses wide-character API which is not compatible
+ * with Linux or other major Unixes.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#ifdef WIN32
+#	include <io.h>
+#	include <fcntl.h>
+#endif
+#include <dirent.h>
+
+/* File name and location of database file */
+#define DB_LOCATION L"locate.db"
+
+/* Forward-decl */
+static int db_locate(const wchar_t *pattern);
+static int db_match(const wchar_t *fn, const wchar_t *pattern);
+static void db_open(void);
+static void db_close(void);
+static int db_read(wchar_t *buffer, size_t max);
+
+/* Module local variables */
+static FILE *db = NULL;
+
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Prepare for unicode output */
+	_setmode(_fileno(stdout), _O_U16TEXT);
+
+	/* For each pattern in command line */
+	int i = 1;
+	while (i < argc) {
+		int count = 0;
+
+		/* Find files matching pattern */
+		count = db_locate(argv[i]);
+
+		/* Output warning if string is not found */
+		if (count == 0) {
+			wprintf(L"%s not found\n", argv[i]);
+		}
+
+		i++;
+	}
+
+	if (argc < 2) {
+		wprintf(L"Usage: locate pattern\n");
+		exit(EXIT_FAILURE);
+	}
+	return EXIT_SUCCESS;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	printf("locate only works on Microsoft Windows\n");
+	return EXIT_SUCCESS;
+}
+#endif
+
+/* Match pattern against files in locate.db file */
+static int
+db_locate(const wchar_t *pattern)
+{
+	int count = 0;
+
+#ifdef WIN32
+	wchar_t buffer[PATH_MAX + 1];
+
+	/* Open locate.db for read */
+	db_open();
+
+	/* Read one directory and file name at a time from database file */
+	while (db_read(buffer, PATH_MAX + 1)) {
+		/* See if file name in buffer matches the search pattern */
+		if (db_match(buffer, pattern)) {
+			/* Match found => output file name and path */
+			wprintf(L"%s\n", buffer);
+			count++;
+		}
+	}
+
+	db_close();
+#endif
+
+	return count;
+}
+
+/* Match pattern against file name */
+static int
+db_match(const wchar_t *fn, const wchar_t *pattern)
+{
+	int found = 0;
+
+#ifdef WIN32
+	/* Locate zero-terminator from fn */
+	wchar_t *p = wcschr(fn, '\0');
+
+	/* Find base name from buffer */
+	int done = 0;
+	while (fn < p && !done) {
+		switch (p[-1]) {
+		case ':':
+		case '/':
+		case '\\':
+			/* Final path separator found */
+			done = 1;
+			break;
+
+		default:
+			/* No path separator yet */
+			p--;
+		}
+	}
+
+	/* Convert base name to lower case */
+	int i = 0;
+	wchar_t base[PATH_MAX + 1];
+	while (i < PATH_MAX && p[i] != '\0') {
+		base[i] = towlower(p[i]);
+		i++;
+	}
+	base[i] = '\0';
+
+	/* Convert search pattern to lower case */
+	i = 0;
+	wchar_t patt[PATH_MAX + 1];
+	while (i < PATH_MAX && pattern[i] != '\0') {
+		patt[i] = towlower(pattern[i]);
+		i++;
+	}
+	patt[i] = '\0';
+
+	/* See if file name matches pattern */
+	if (wcsstr(base, patt) != NULL) {
+		found = 1;
+	} else {
+		found = 0;
+	}
+#endif
+
+	return found;
+}
+
+/*
+ * Read line from locate.db.  This function is same as fgetws() except
+ * that new-line at the end of line is not included.
+ */
+static int
+db_read(wchar_t *buffer, size_t max)
+{
+	int ok = 0;
+
+#ifdef WIN32
+	size_t i = 0;
+	wchar_t c;
+	int done = 0;
+
+	if (!db) {
+		wprintf(L"Database not open\n");
+		exit(EXIT_SUCCESS);
+	}
+
+	do {
+		/* Read wide-character from stream */
+		c = fgetwc(db);
+
+		/* Determine how to process character */
+		switch (c) {
+		case '\r':
+			/* Ignore, should be handled by run-time libraries */
+			/*NOP*/;
+			break;
+
+		case '\n':
+			/* End of string => return file name */
+			done = 1;
+			ok = 1;
+			break;
+
+		case /*EOF*/WEOF:
+			/* End of file */
+			done = 1;
+			if (i == 0) {
+				/* No data in buffer => return false */
+				ok = 0;
+			} else {
+				/* Data in buffer => return file name */
+				ok = 1;
+			}
+			break;
+
+		default:
+			/* Store character */
+			if (i < max - 1) {
+				buffer[i++] = c;
+			} else {
+				buffer[max - 1] = '\0';
+				wprintf(L"Buffer too small: %s", buffer);
+				exit(EXIT_FAILURE);
+			}
+		}
+	} while (!done);
+
+	/* Zero-terminate buffer */
+	buffer[i] = '\0';
+#endif
+
+	return ok;
+}
+
+/* Open database file locate.db */
+static void
+db_open(void)
+{
+#ifdef WIN32
+	if (db)
+		return;
+
+	/* Open file for writing */
+	errno_t error = _wfopen_s(&db, DB_LOCATION, L"rt, ccs=UNICODE");
+	if (error) {
+		wprintf(L"Cannot open %s\n", DB_LOCATION);
+		exit(EXIT_FAILURE);
+	}
+#endif
+}
+
+/* Close database file */
+static void
+db_close(void)
+{
+	if (!db)
+		return;
+
+	fclose(db);
+	db = NULL;
+}
diff --git a/dirent/examples/ls.c b/dirent/examples/ls.c
new file mode 100644
index 0000000..3fb627e
--- /dev/null
+++ b/dirent/examples/ls.c
@@ -0,0 +1,148 @@
+/*
+ * List contents of a directory.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a directory name argument.  For example, command
+ *
+ *     ls "c:\Program Files"
+ *
+ * might output something like
+ *
+ *     ./
+ *     ../
+ *     7-Zip/
+ *     Internet Explorer/
+ *     Microsoft Visual Studio 9.0/
+ *     Microsoft.NET/
+ *     Mozilla Firefox/
+ *
+ * The ls command provided by this file is only an example: the command does
+ * not have any fancy options like "ls -al" in Linux and the command does not
+ * support file name matching like "ls *.c".
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <locale.h>
+
+static void list_directory(const char *dirname);
+static int _main(int argc, char *argv[]);
+
+static int
+_main(int argc, char *argv[])
+{
+	/* For each directory in command line */
+	int i = 1;
+	while (i < argc) {
+		list_directory(argv[i]);
+		i++;
+	}
+
+	/* List current working directory if no arguments on command line */
+	if (argc == 1)
+		list_directory(".");
+
+	return EXIT_SUCCESS;
+}
+
+/*
+ * List files and directories within a directory.
+ */
+static void
+list_directory(const char *dirname)
+{
+	/* Open directory stream */
+	DIR *dir = opendir(dirname);
+	if (!dir) {
+		/* Could not open directory */
+		fprintf(stderr,
+			"Cannot open %s (%s)\n", dirname, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	/* Print all files and directories within the directory */
+	struct dirent *ent;
+	while ((ent = readdir(dir)) != NULL) {
+		switch (ent->d_type) {
+		case DT_REG:
+			printf("%s\n", ent->d_name);
+			break;
+
+		case DT_DIR:
+			printf("%s/\n", ent->d_name);
+			break;
+
+		case DT_LNK:
+			printf("%s@\n", ent->d_name);
+			break;
+
+		default:
+			printf("%s*\n", ent->d_name);
+		}
+	}
+
+	closedir(dir);
+}
+
+/* Stub for converting arguments to UTF-8 on Windows */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n + 1);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n + 1, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+
diff --git a/dirent/examples/scandir.c b/dirent/examples/scandir.c
new file mode 100644
index 0000000..65dc11f
--- /dev/null
+++ b/dirent/examples/scandir.c
@@ -0,0 +1,152 @@
+/*
+ * List contents of a directory in an alphabetical order.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a directory name argument.  For example, command
+ *
+ *     scandir "c:\Program Files"
+ *
+ * might output something like
+ *
+ *     ./
+ *     ../
+ *     7-Zip/
+ *     Internet Explorer/
+ *     Microsoft Visual Studio 9.0/
+ *     Microsoft.NET/
+ *     Mozilla Firefox/
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <locale.h>
+
+static void list_directory(const char *dirname);
+static int _main(int argc, char *argv[]);
+
+static int
+_main(int argc, char *argv[])
+{
+	/* For each directory in command line */
+	int i = 1;
+	while (i < argc) {
+		list_directory(argv[i]);
+		i++;
+	}
+
+	/* List current working directory if no arguments on command line */
+	if (argc == 1)
+		list_directory(".");
+
+	return EXIT_SUCCESS;
+}
+
+/*
+ * List files and directories within a directory.
+ */
+static void
+list_directory(
+	const char *dirname)
+{
+	/* Scan files in directory */
+	struct dirent **files;
+	int n = scandir(dirname, &files, NULL, alphasort);
+	if (n < 0) {
+		fprintf(stderr,
+			"Cannot open %s (%s)\n", dirname, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	/* Loop through file names */
+	for (int i = 0; i < n; i++) {
+		/* Get pointer to file entry */
+		struct dirent *ent = files[i];
+
+		/* Output file name */
+		switch (ent->d_type) {
+		case DT_REG:
+			printf("%s\n", ent->d_name);
+			break;
+
+		case DT_DIR:
+			printf("%s/\n", ent->d_name);
+			break;
+
+		case DT_LNK:
+			printf("%s@\n", ent->d_name);
+			break;
+
+		default:
+			printf("%s*\n", ent->d_name);
+		}
+	}
+
+	/* Release file names */
+	for (int i = 0; i < n; i++) {
+		free(files[i]);
+	}
+	free(files);
+}
+
+/* Stub for converting arguments to UTF-8 on Windows */
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Select UTF-8 locale */
+	setlocale(LC_ALL, ".utf8");
+	SetConsoleCP(CP_UTF8);
+	SetConsoleOutputCP(CP_UTF8);
+
+	/* Allocate memory for multi-byte argv table */
+	char **mbargv;
+	mbargv = (char**) malloc(argc * sizeof(char*));
+	if (!mbargv) {
+		puts("Out of memory");
+		exit(3);
+	}
+
+	/* Convert each argument in argv to UTF-8 */
+	for (int i = 0; i < argc; i++) {
+		size_t n;
+		wcstombs_s(&n, NULL, 0, argv[i], 0);
+
+		/* Allocate room for ith argument */
+		mbargv[i] = (char*) malloc(n + 1);
+		if (!mbargv[i]) {
+			puts("Out of memory");
+			exit(3);
+		}
+
+		/* Convert ith argument to utf-8 */
+		wcstombs_s(NULL, mbargv[i], n + 1, argv[i], n);
+	}
+
+	/* Pass UTF-8 converted arguments to the main program */
+	int errorcode = _main(argc, mbargv);
+
+	/* Release UTF-8 arguments */
+	for (int i = 0; i < argc; i++) {
+		free(mbargv[i]);
+	}
+
+	/* Release the argument table */
+	free(mbargv);
+	return errorcode;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	return _main(argc, argv);
+}
+#endif
+
diff --git a/dirent/examples/updatedb.c b/dirent/examples/updatedb.c
new file mode 100644
index 0000000..c7ec652
--- /dev/null
+++ b/dirent/examples/updatedb.c
@@ -0,0 +1,206 @@
+/*
+ * Build database of file and directory names.
+ *
+ * Compile this file with Visual Studio and run the produced command in
+ * console with a directory name argument.  For example, command
+ *
+ *     updatedb C:\
+ *
+ * will produce the file locate.db with one file name per line such as
+ *
+ *     c:\Program Files/7-Zip/7-zip.chm
+ *     c:\Program Files/7-Zip/7-zip.dll
+ *     c:\Program Files/7-Zip/7z.dll
+ *     c:\Program Files/Adobe/Reader 10.0/Reader/logsession.dll
+ *     c:\Program Files/Adobe/Reader 10.0/Reader/LogTransport2.exe
+ *     c:\Program Files/Windows NT/Accessories/wordpad.exe
+ *     c:\Program Files/Windows NT/Accessories/write.wpc
+ *
+ * Be ware that this file uses wide-character API which is not compatible
+ * with Linux or other major Unixes.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#ifdef WIN32
+#	include <io.h>
+#	include <fcntl.h>
+#endif
+#include <dirent.h>
+
+/* File name and location of database file */
+#define DB_LOCATION L"locate.db"
+
+/* Forward-decl */
+static int update_directory(const wchar_t *dirname);
+static void db_open(void);
+static void db_close(void);
+static void db_store(const wchar_t *dirname);
+
+/* Module local variables */
+static FILE *db = NULL;
+
+#ifdef _MSC_VER
+int
+wmain(int argc, wchar_t *argv[])
+{
+	/* Prepare for unicode output */
+	_setmode(_fileno(stdout), _O_U16TEXT);
+
+	/* Open locate.db */
+	db_open();
+
+	/* For each directory in command line */
+	int i = 1;
+	while (i < argc) {
+		/* Scan directory for files */
+		int ok = update_directory(argv[i]);
+		if (!ok) {
+			wprintf(L"Cannot open directory %s\n", argv[i]);
+			exit(EXIT_FAILURE);
+		}
+
+		i++;
+	}
+
+	/* Use current working directory if no arguments on command line */
+	if (argc == 1)
+		update_directory(L".");
+
+	db_close();
+	return EXIT_SUCCESS;
+}
+#else
+int
+main(int argc, char *argv[])
+{
+	printf("updatedb only works on Microsoft Windows\n");
+	return EXIT_SUCCESS;
+}
+#endif
+
+/* Find files recursively */
+static int
+update_directory(const wchar_t *dirname)
+{
+#ifdef WIN32
+	wchar_t buffer[PATH_MAX + 2];
+	wchar_t *p = buffer;
+	wchar_t *end = &buffer[PATH_MAX];
+
+	/* Copy directory name to buffer */
+	const wchar_t *src = dirname;
+	while (p < end  &&  *src != '\0') {
+		*p++ = *src++;
+	}
+	*p = '\0';
+
+	/* Open directory stream */
+	_WDIR *dir = _wopendir(dirname);
+	if (!dir) {
+		/* Cannot open directory */
+		return /*failure*/ 0;
+	}
+
+	/* Print all files and directories within the directory */
+	struct _wdirent *ent;
+	while ((ent = _wreaddir (dir)) != NULL) {
+		wchar_t *q = p;
+		wchar_t c;
+
+		/* Get final character of directory name */
+		if (buffer < q)
+			c = q[-1];
+		else
+			c = ':';
+
+		/* Append directory separator if not already there */
+		if (c != ':'  &&  c != '/'  &&  c != '\\')
+			*q++ = '/';
+
+		/* Append file name */
+		src = ent->d_name;
+		while (q < end  &&  *src != '\0') {
+			*q++ = *src++;
+		}
+		*q = '\0';
+
+		/* Decide what to do with the directory entry */
+		switch (ent->d_type) {
+		case DT_REG:
+			/* Store file name */
+			db_store(buffer);
+			break;
+
+		case DT_DIR:
+			/* Scan sub-directory recursively */
+			if (wcscmp(ent->d_name, L".") != 0
+				&&  wcscmp(ent->d_name, L"..") != 0) {
+				update_directory(buffer);
+			}
+			break;
+
+		default:
+			/* Do not device entries */
+			/*NOP*/;
+		}
+
+	}
+
+	wclosedir(dir);
+	return /*success*/ 1;
+#else
+	return /*failure*/ 0;
+#endif
+}
+
+/* Store file name to locate.db */
+static void
+db_store(const wchar_t *dirname)
+{
+#ifdef WIN32
+	if (!db) {
+		wprintf(L"Database not open\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Output line to file */
+	fwprintf(db, L"%s\n", dirname);
+#endif
+}
+
+/* Open database file locate.db */
+static void
+db_open(void)
+{
+#ifdef WIN32
+	if (db)
+		return;
+
+	/* Open file for writing */
+	errno_t error = _wfopen_s(&db, DB_LOCATION, L"wt, ccs=UNICODE");
+	if (error) {
+		wprintf(L"Cannot open %s\n", DB_LOCATION);
+		exit(EXIT_FAILURE);
+	}
+#endif
+}
+
+/* Close database file */
+static void
+db_close(
+	void)
+{
+	if (!db)
+		return;
+
+	/* Close file */
+	fclose(db);
+	db = NULL;
+}
diff --git a/dirent/include/dirent.h b/dirent/include/dirent.h
new file mode 100644
index 0000000..557485a
--- /dev/null
+++ b/dirent/include/dirent.h
@@ -0,0 +1,1212 @@
+/*
+ * Dirent interface for Microsoft Visual Studio
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#ifndef DIRENT_H
+#define DIRENT_H
+
+/* Hide warnings about unreferenced local functions */
+#if defined(__clang__)
+#	pragma clang diagnostic ignored "-Wunused-function"
+#elif defined(_MSC_VER)
+#	pragma warning(disable:4505)
+#elif defined(__GNUC__)
+#	pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+/*
+ * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
+ * Windows Sockets 2.0.
+ */
+#ifndef WIN32_LEAN_AND_MEAN
+#	define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Indicates that d_type field is available in dirent structure */
+#define _DIRENT_HAVE_D_TYPE
+
+/* Indicates that d_namlen field is available in dirent structure */
+#define _DIRENT_HAVE_D_NAMLEN
+
+/* Entries missing from MSVC 6.0 */
+#if !defined(FILE_ATTRIBUTE_DEVICE)
+#	define FILE_ATTRIBUTE_DEVICE 0x40
+#endif
+
+/* File type and permission flags for stat(), general mask */
+#if !defined(S_IFMT)
+#	define S_IFMT _S_IFMT
+#endif
+
+/* Directory bit */
+#if !defined(S_IFDIR)
+#	define S_IFDIR _S_IFDIR
+#endif
+
+/* Character device bit */
+#if !defined(S_IFCHR)
+#	define S_IFCHR _S_IFCHR
+#endif
+
+/* Pipe bit */
+#if !defined(S_IFFIFO)
+#	define S_IFFIFO _S_IFFIFO
+#endif
+
+/* Regular file bit */
+#if !defined(S_IFREG)
+#	define S_IFREG _S_IFREG
+#endif
+
+/* Read permission */
+#if !defined(S_IREAD)
+#	define S_IREAD _S_IREAD
+#endif
+
+/* Write permission */
+#if !defined(S_IWRITE)
+#	define S_IWRITE _S_IWRITE
+#endif
+
+/* Execute permission */
+#if !defined(S_IEXEC)
+#	define S_IEXEC _S_IEXEC
+#endif
+
+/* Pipe */
+#if !defined(S_IFIFO)
+#	define S_IFIFO _S_IFIFO
+#endif
+
+/* Block device */
+#if !defined(S_IFBLK)
+#	define S_IFBLK 0
+#endif
+
+/* Link */
+#if !defined(S_IFLNK)
+#	define S_IFLNK 0
+#endif
+
+/* Socket */
+#if !defined(S_IFSOCK)
+#	define S_IFSOCK 0
+#endif
+
+/* Read user permission */
+#if !defined(S_IRUSR)
+#	define S_IRUSR S_IREAD
+#endif
+
+/* Write user permission */
+#if !defined(S_IWUSR)
+#	define S_IWUSR S_IWRITE
+#endif
+
+/* Execute user permission */
+#if !defined(S_IXUSR)
+#	define S_IXUSR 0
+#endif
+
+/* Read group permission */
+#if !defined(S_IRGRP)
+#	define S_IRGRP 0
+#endif
+
+/* Write group permission */
+#if !defined(S_IWGRP)
+#	define S_IWGRP 0
+#endif
+
+/* Execute group permission */
+#if !defined(S_IXGRP)
+#	define S_IXGRP 0
+#endif
+
+/* Read others permission */
+#if !defined(S_IROTH)
+#	define S_IROTH 0
+#endif
+
+/* Write others permission */
+#if !defined(S_IWOTH)
+#	define S_IWOTH 0
+#endif
+
+/* Execute others permission */
+#if !defined(S_IXOTH)
+#	define S_IXOTH 0
+#endif
+
+/* Maximum length of file name */
+#if !defined(PATH_MAX)
+#	define PATH_MAX MAX_PATH
+#endif
+#if !defined(FILENAME_MAX)
+#	define FILENAME_MAX MAX_PATH
+#endif
+#if !defined(NAME_MAX)
+#	define NAME_MAX FILENAME_MAX
+#endif
+
+/* File type flags for d_type */
+#define DT_UNKNOWN 0
+#define DT_REG S_IFREG
+#define DT_DIR S_IFDIR
+#define DT_FIFO S_IFIFO
+#define DT_SOCK S_IFSOCK
+#define DT_CHR S_IFCHR
+#define DT_BLK S_IFBLK
+#define DT_LNK S_IFLNK
+
+/* Macros for converting between st_mode and d_type */
+#define IFTODT(mode) ((mode) & S_IFMT)
+#define DTTOIF(type) (type)
+
+/*
+ * File type macros.  Note that block devices, sockets and links cannot be
+ * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
+ * only defined for compatibility.  These macros should always return false
+ * on Windows.
+ */
+#if !defined(S_ISFIFO)
+#	define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISDIR)
+#	define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG)
+#	define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK)
+#	define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK)
+#	define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISCHR)
+#	define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISBLK)
+#	define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+
+/* Return the exact length of the file name without zero terminator */
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
+
+/* Return the maximum size of a file name */
+#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Wide-character version */
+struct _wdirent {
+	/* Always zero */
+	long d_ino;
+
+	/* Position of next file in a directory stream */
+	long d_off;
+
+	/* Structure size */
+	unsigned short d_reclen;
+
+	/* Length of name without \0 */
+	size_t d_namlen;
+
+	/* File type */
+	int d_type;
+
+	/* File name */
+	wchar_t d_name[PATH_MAX+1];
+};
+typedef struct _wdirent _wdirent;
+
+struct _WDIR {
+	/* Current directory entry */
+	struct _wdirent ent;
+
+	/* Private file data */
+	WIN32_FIND_DATAW data;
+
+	/* True if data is valid */
+	int cached;
+
+	/* True if next entry is invalid */
+	int invalid;
+
+	/* Win32 search handle */
+	HANDLE handle;
+
+	/* Initial directory name */
+	wchar_t *patt;
+};
+typedef struct _WDIR _WDIR;
+
+/* Multi-byte character version */
+struct dirent {
+	/* Always zero */
+	long d_ino;
+
+	/* Position of next file in a directory stream */
+	long d_off;
+
+	/* Structure size */
+	unsigned short d_reclen;
+
+	/* Length of name without \0 */
+	size_t d_namlen;
+
+	/* File type */
+	int d_type;
+
+	/* File name */
+	char d_name[PATH_MAX+1];
+};
+typedef struct dirent dirent;
+
+struct DIR {
+	struct dirent ent;
+	struct _WDIR *wdirp;
+};
+typedef struct DIR DIR;
+
+
+/* Dirent functions */
+static DIR *opendir(const char *dirname);
+static _WDIR *_wopendir(const wchar_t *dirname);
+
+static struct dirent *readdir(DIR *dirp);
+static struct _wdirent *_wreaddir(_WDIR *dirp);
+
+static int readdir_r(
+	DIR *dirp, struct dirent *entry, struct dirent **result);
+static int _wreaddir_r(
+	_WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
+
+static int closedir(DIR *dirp);
+static int _wclosedir(_WDIR *dirp);
+
+static void rewinddir(DIR *dirp);
+static void _wrewinddir(_WDIR *dirp);
+
+static long telldir(DIR *dirp);
+static long _wtelldir(_WDIR *dirp);
+
+static void seekdir(DIR *dirp, long loc);
+static void _wseekdir(_WDIR *dirp, long loc);
+
+static int scandir(const char *dirname, struct dirent ***namelist,
+	int (*filter)(const struct dirent*),
+	int (*compare)(const struct dirent**, const struct dirent**));
+
+static int alphasort(const struct dirent **a, const struct dirent **b);
+
+static int versionsort(const struct dirent **a, const struct dirent **b);
+
+static int strverscmp(const char *a, const char *b);
+
+/* For compatibility with Symbian */
+#define wdirent _wdirent
+#define WDIR _WDIR
+#define wopendir _wopendir
+#define wreaddir _wreaddir
+#define wclosedir _wclosedir
+#define wrewinddir _wrewinddir
+#define wtelldir _wtelldir
+#define wseekdir _wseekdir
+
+/* Compatibility with older Microsoft compilers and non-Microsoft compilers */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+#	define wcstombs_s dirent_wcstombs_s
+#	define mbstowcs_s dirent_mbstowcs_s
+#endif
+
+/* Optimize dirent_set_errno() away on modern Microsoft compilers */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#	define dirent_set_errno _set_errno
+#endif
+
+
+/* Internal utility functions */
+static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp);
+static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp);
+static long dirent_hash(WIN32_FIND_DATAW *datap);
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_mbstowcs_s(
+	size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords,
+	const char *mbstr, size_t count);
+#endif
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_wcstombs_s(
+	size_t *pReturnValue, char *mbstr, size_t sizeInBytes,
+	const wchar_t *wcstr, size_t count);
+#endif
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static void dirent_set_errno(int error);
+#endif
+
+
+/*
+ * Open directory stream DIRNAME for read and return a pointer to the
+ * internal working area that is used to retrieve individual directory
+ * entries.
+ */
+static _WDIR *
+_wopendir(const wchar_t *dirname)
+{
+	wchar_t *p;
+
+	/* Must have directory name */
+	if (dirname == NULL || dirname[0] == '\0') {
+		dirent_set_errno(ENOENT);
+		return NULL;
+	}
+
+	/* Allocate new _WDIR structure */
+	_WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR));
+	if (!dirp)
+		return NULL;
+
+	/* Reset _WDIR structure */
+	dirp->handle = INVALID_HANDLE_VALUE;
+	dirp->patt = NULL;
+	dirp->cached = 0;
+	dirp->invalid = 0;
+
+	/*
+	 * Compute the length of full path plus zero terminator
+	 *
+	 * Note that on WinRT there's no way to convert relative paths
+	 * into absolute paths, so just assume it is an absolute path.
+	 */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+	/* Desktop */
+	DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL);
+#else
+	/* WinRT */
+	size_t n = wcslen(dirname);
+#endif
+
+	/* Allocate room for absolute directory name and search pattern */
+	dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16);
+	if (dirp->patt == NULL)
+		goto exit_closedir;
+
+	/*
+	 * Convert relative directory name to an absolute one.  This
+	 * allows rewinddir() to function correctly even when current
+	 * working directory is changed between opendir() and rewinddir().
+	 *
+	 * Note that on WinRT there's no way to convert relative paths
+	 * into absolute paths, so just assume it is an absolute path.
+	 */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+	/* Desktop */
+	n = GetFullPathNameW(dirname, n, dirp->patt, NULL);
+	if (n <= 0)
+		goto exit_closedir;
+#else
+	/* WinRT */
+	wcsncpy_s(dirp->patt, n+1, dirname, n);
+#endif
+
+	/* Append search pattern \* to the directory name */
+	p = dirp->patt + n;
+	switch (p[-1]) {
+	case '\\':
+	case '/':
+	case ':':
+		/* Directory ends in path separator, e.g. c:\temp\ */
+		/*NOP*/;
+		break;
+
+	default:
+		/* Directory name doesn't end in path separator */
+		*p++ = '\\';
+	}
+	*p++ = '*';
+	*p = '\0';
+
+	/* Open directory stream and retrieve the first entry */
+	if (!dirent_first(dirp))
+		goto exit_closedir;
+
+	/* Success */
+	return dirp;
+
+	/* Failure */
+exit_closedir:
+	_wclosedir(dirp);
+	return NULL;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns pointer to static directory entry which may be overwritten by
+ * subsequent calls to _wreaddir().
+ */
+static struct _wdirent *
+_wreaddir(_WDIR *dirp)
+{
+	/*
+	 * Read directory entry to buffer.  We can safely ignore the return
+	 * value as entry will be set to NULL in case of error.
+	 */
+	struct _wdirent *entry;
+	(void) _wreaddir_r(dirp, &dirp->ent, &entry);
+
+	/* Return pointer to statically allocated directory entry */
+	return entry;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns zero on success.  If end of directory stream is reached, then sets
+ * result to NULL and returns zero.
+ */
+static int
+_wreaddir_r(
+	_WDIR *dirp, struct _wdirent *entry, struct _wdirent **result)
+{
+	/* Validate directory handle */
+	if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt) {
+		dirent_set_errno(EBADF);
+		*result = NULL;
+		return -1;
+	}
+
+	/* Read next directory entry */
+	WIN32_FIND_DATAW *datap = dirent_next(dirp);
+	if (!datap) {
+		/* Return NULL to indicate end of directory */
+		*result = NULL;
+		return /*OK*/0;
+	}
+
+	/*
+	 * Copy file name as wide-character string.  If the file name is too
+	 * long to fit in to the destination buffer, then truncate file name
+	 * to PATH_MAX characters and zero-terminate the buffer.
+	 */
+	size_t i = 0;
+	while (i < PATH_MAX && datap->cFileName[i] != 0) {
+		entry->d_name[i] = datap->cFileName[i];
+		i++;
+	}
+	entry->d_name[i] = 0;
+
+	/* Length of file name excluding zero terminator */
+	entry->d_namlen = i;
+
+	/* Determine file type */
+	DWORD attr = datap->dwFileAttributes;
+	if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
+		entry->d_type = DT_CHR;
+	else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+		entry->d_type = DT_DIR;
+	else
+		entry->d_type = DT_REG;
+
+	/* Read the next directory entry to cache */
+	datap = dirent_next(dirp);
+	if (datap) {
+		/* Compute 31-bit hash of the next directory entry */
+		entry->d_off = dirent_hash(datap);
+
+		/* Push the next directory entry back to cache */
+		dirp->cached = 1;
+	} else {
+		/* End of directory stream */
+		entry->d_off = (long) ((~0UL) >> 1);
+	}
+
+	/* Reset other fields */
+	entry->d_ino = 0;
+	entry->d_reclen = sizeof(struct _wdirent);
+
+	/* Set result address */
+	*result = entry;
+	return /*OK*/0;
+}
+
+/*
+ * Close directory stream opened by opendir() function.  This invalidates the
+ * DIR structure as well as any directory entry read previously by
+ * _wreaddir().
+ */
+static int
+_wclosedir(_WDIR *dirp)
+{
+	if (!dirp) {
+		dirent_set_errno(EBADF);
+		return /*failure*/-1;
+	}
+
+	/*
+	 * Release search handle if we have one.  Being able to handle
+	 * partially initialized _WDIR structure allows us to use this
+	 * function to handle errors occuring within _wopendir.
+	 */
+	if (dirp->handle != INVALID_HANDLE_VALUE) {
+		FindClose(dirp->handle);
+	}
+
+	/*
+	 * Release search pattern.  Note that we don't need to care if
+	 * dirp->patt is NULL or not: function free is guaranteed to act
+	 * appropriately.
+	 */
+	free(dirp->patt);
+
+	/* Release directory structure */
+	free(dirp);
+	return /*success*/0;
+}
+
+/*
+ * Rewind directory stream such that _wreaddir() returns the very first
+ * file name again.
+ */
+static void _wrewinddir(_WDIR* dirp)
+{
+	/* Check directory pointer */
+	if (!dirp || dirp->handle == INVALID_HANDLE_VALUE || !dirp->patt)
+		return;
+
+	/* Release existing search handle */
+	FindClose(dirp->handle);
+
+	/* Open new search handle */
+	dirent_first(dirp);
+}
+
+/* Get first directory entry */
+static WIN32_FIND_DATAW *
+dirent_first(_WDIR *dirp)
+{
+	/* Open directory and retrieve the first entry */
+	dirp->handle = FindFirstFileExW(
+		dirp->patt, FindExInfoStandard, &dirp->data,
+		FindExSearchNameMatch, NULL, 0);
+	if (dirp->handle == INVALID_HANDLE_VALUE)
+		goto error;
+
+	/* A directory entry is now waiting in memory */
+	dirp->cached = 1;
+	return &dirp->data;
+
+error:
+	/* Failed to open directory: no directory entry in memory */
+	dirp->cached = 0;
+	dirp->invalid = 1;
+
+	/* Set error code */
+	DWORD errorcode = GetLastError();
+	switch (errorcode) {
+	case ERROR_ACCESS_DENIED:
+		/* No read access to directory */
+		dirent_set_errno(EACCES);
+		break;
+
+	case ERROR_DIRECTORY:
+		/* Directory name is invalid */
+		dirent_set_errno(ENOTDIR);
+		break;
+
+	case ERROR_PATH_NOT_FOUND:
+	default:
+		/* Cannot find the file */
+		dirent_set_errno(ENOENT);
+	}
+	return NULL;
+}
+
+/* Get next directory entry */
+static WIN32_FIND_DATAW *
+dirent_next(_WDIR *dirp)
+{
+	/* Return NULL if seek position was invalid */
+	if (dirp->invalid)
+		return NULL;
+
+	/* Is the next directory entry already in cache? */
+	if (dirp->cached) {
+		/* Yes, a valid directory entry found in memory */
+		dirp->cached = 0;
+		return &dirp->data;
+	}
+
+	/* Read the next directory entry from stream */
+	if (FindNextFileW(dirp->handle, &dirp->data) == FALSE) {
+		/* End of directory stream */
+		return NULL;
+	}
+
+	/* Success */
+	return &dirp->data;
+}
+
+/*
+ * Compute 31-bit hash of file name.
+ *
+ * See djb2 at http://www.cse.yorku.ca/~oz/hash.html
+ */
+static long
+dirent_hash(WIN32_FIND_DATAW *datap)
+{
+	unsigned long hash = 5381;
+	unsigned long c;
+	const wchar_t *p = datap->cFileName;
+	const wchar_t *e = p + MAX_PATH;
+	while (p != e && (c = *p++) != 0) {
+		hash = (hash << 5) + hash + c;
+	}
+
+	return (long) (hash & ((~0UL) >> 1));
+}
+
+/* Open directory stream using plain old C-string */
+static DIR *opendir(const char *dirname)
+{
+	/* Must have directory name */
+	if (dirname == NULL || dirname[0] == '\0') {
+		dirent_set_errno(ENOENT);
+		return NULL;
+	}
+
+	/* Allocate memory for DIR structure */
+	struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR));
+	if (!dirp)
+		return NULL;
+
+	/* Convert directory name to wide-character string */
+	wchar_t wname[PATH_MAX + 1];
+	size_t n;
+	int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1);
+	if (error)
+		goto exit_failure;
+
+	/* Open directory stream using wide-character name */
+	dirp->wdirp = _wopendir(wname);
+	if (!dirp->wdirp)
+		goto exit_failure;
+
+	/* Success */
+	return dirp;
+
+	/* Failure */
+exit_failure:
+	free(dirp);
+	return NULL;
+}
+
+/* Read next directory entry */
+static struct dirent *
+readdir(DIR *dirp)
+{
+	/*
+	 * Read directory entry to buffer.  We can safely ignore the return
+	 * value as entry will be set to NULL in case of error.
+	 */
+	struct dirent *entry;
+	(void) readdir_r(dirp, &dirp->ent, &entry);
+
+	/* Return pointer to statically allocated directory entry */
+	return entry;
+}
+
+/*
+ * Read next directory entry into called-allocated buffer.
+ *
+ * Returns zero on success.  If the end of directory stream is reached, then
+ * sets result to NULL and returns zero.
+ */
+static int
+readdir_r(
+	DIR *dirp, struct dirent *entry, struct dirent **result)
+{
+	/* Read next directory entry */
+	WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp);
+	if (!datap) {
+		/* No more directory entries */
+		*result = NULL;
+		return /*OK*/0;
+	}
+
+	/* Attempt to convert file name to multi-byte string */
+	size_t n;
+	int error = wcstombs_s(
+		&n, entry->d_name, PATH_MAX + 1,
+		datap->cFileName, PATH_MAX + 1);
+
+	/*
+	 * If the file name cannot be represented by a multi-byte string, then
+	 * attempt to use old 8+3 file name.  This allows the program to
+	 * access files although file names may seem unfamiliar to the user.
+	 *
+	 * Be ware that the code below cannot come up with a short file name
+	 * unless the file system provides one.  At least VirtualBox shared
+	 * folders fail to do this.
+	 */
+	if (error && datap->cAlternateFileName[0] != '\0') {
+		error = wcstombs_s(
+			&n, entry->d_name, PATH_MAX + 1,
+			datap->cAlternateFileName, PATH_MAX + 1);
+	}
+
+	if (!error) {
+		/* Length of file name excluding zero terminator */
+		entry->d_namlen = n - 1;
+
+		/* Determine file type */
+		DWORD attr = datap->dwFileAttributes;
+		if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
+			entry->d_type = DT_CHR;
+		else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+			entry->d_type = DT_DIR;
+		else
+			entry->d_type = DT_REG;
+
+		/* Get offset of next file */
+		datap = dirent_next(dirp->wdirp);
+		if (datap) {
+			/* Compute 31-bit hash of the next directory entry */
+			entry->d_off = dirent_hash(datap);
+
+			/* Push the next directory entry back to cache */
+			dirp->wdirp->cached = 1;
+		} else {
+			/* End of directory stream */
+			entry->d_off = (long) ((~0UL) >> 1);
+		}
+
+		/* Reset fields */
+		entry->d_ino = 0;
+		entry->d_reclen = sizeof(struct dirent);
+	} else {
+		/*
+		 * Cannot convert file name to multi-byte string so construct
+		 * an erroneous directory entry and return that.  Note that
+		 * we cannot return NULL as that would stop the processing
+		 * of directory entries completely.
+		 */
+		entry->d_name[0] = '?';
+		entry->d_name[1] = '\0';
+		entry->d_namlen = 1;
+		entry->d_type = DT_UNKNOWN;
+		entry->d_ino = 0;
+		entry->d_off = -1;
+		entry->d_reclen = 0;
+	}
+
+	/* Return pointer to directory entry */
+	*result = entry;
+	return /*OK*/0;
+}
+
+/* Close directory stream */
+static int
+closedir(DIR *dirp)
+{
+	int ok;
+
+	if (!dirp)
+		goto exit_failure;
+
+	/* Close wide-character directory stream */
+	ok = _wclosedir(dirp->wdirp);
+	dirp->wdirp = NULL;
+
+	/* Release multi-byte character version */
+	free(dirp);
+	return ok;
+
+exit_failure:
+	/* Invalid directory stream */
+	dirent_set_errno(EBADF);
+	return /*failure*/-1;
+}
+
+/* Rewind directory stream to beginning */
+static void
+rewinddir(DIR *dirp)
+{
+	if (!dirp)
+		return;
+
+	/* Rewind wide-character string directory stream */
+	_wrewinddir(dirp->wdirp);
+}
+
+/* Get position of directory stream */
+static long
+_wtelldir(_WDIR *dirp)
+{
+	if (!dirp || dirp->handle == INVALID_HANDLE_VALUE) {
+		dirent_set_errno(EBADF);
+		return /*failure*/-1;
+	}
+
+	/* Read next file entry */
+	WIN32_FIND_DATAW *datap = dirent_next(dirp);
+	if (!datap) {
+		/* End of directory stream */
+		return (long) ((~0UL) >> 1);
+	}
+
+	/* Store file entry to cache for readdir() */
+	dirp->cached = 1;
+
+	/* Return the 31-bit hash code to be used as stream position */
+	return dirent_hash(datap);
+}
+
+/* Get position of directory stream */
+static long
+telldir(DIR *dirp)
+{
+	if (!dirp) {
+		dirent_set_errno(EBADF);
+		return -1;
+	}
+
+	return _wtelldir(dirp->wdirp);
+}
+
+/* Seek directory stream to offset */
+static void
+_wseekdir(_WDIR *dirp, long loc)
+{
+	/* Directory must be open */
+	if (!dirp || dirp->handle == INVALID_HANDLE_VALUE)
+		goto exit_failure;
+
+	/* Ensure that seek position is valid */
+	if (loc < 0)
+		goto exit_failure;
+
+	/* Restart directory stream from the beginning */
+	FindClose(dirp->handle);
+	if (!dirent_first(dirp))
+		goto exit_failure;
+
+	/* Reset invalid flag so that we can read from the stream again */
+	dirp->invalid = 0;
+
+	/*
+	 * Read directory entries from the beginning until the hash matches a
+	 * file name.  Be ware that hash code is only 31 bits longs and
+	 * duplicates are possible: the hash code cannot return the position
+	 * with 100.00% accuracy! Moreover, the method is slow for large
+	 * directories.
+	 */
+	long hash;
+	do {
+		/* Read next directory entry */
+		WIN32_FIND_DATAW *datap = dirent_next(dirp);
+		if (!datap) {
+			/*
+			 * End of directory stream was reached before finding
+			 * the requested location.  Perhaps the file in
+			 * question was deleted or moved out of the directory.
+			 */
+			goto exit_failure;
+		}
+
+		/* Does the file name match the hash? */
+		hash = dirent_hash(datap);
+	} while (hash != loc);
+
+	/*
+	 * File name matches the hash!  Push the directory entry back to cache
+	 * from where next readdir() will return it.
+	 */
+	dirp->cached = 1;
+	dirp->invalid = 0;
+	return;
+
+exit_failure:
+	/* Ensure that readdir will return NULL */
+	dirp->invalid = 1;
+}
+
+/* Seek directory stream to offset */
+static void
+seekdir(DIR *dirp, long loc)
+{
+	if (!dirp)
+		return;
+
+	_wseekdir(dirp->wdirp, loc);
+}
+
+/* Scan directory for entries */
+static int
+scandir(
+	const char *dirname, struct dirent ***namelist,
+	int (*filter)(const struct dirent*),
+	int (*compare)(const struct dirent**, const struct dirent**))
+{
+	int result;
+
+	/* Open directory stream */
+	DIR *dir = opendir(dirname);
+	if (!dir) {
+		/* Cannot open directory */
+		return /*Error*/ -1;
+	}
+
+	/* Read directory entries to memory */
+	struct dirent *tmp = NULL;
+	struct dirent **files = NULL;
+	size_t size = 0;
+	size_t allocated = 0;
+	while (1) {
+		/* Allocate room for a temporary directory entry */
+		if (!tmp) {
+			tmp = (struct dirent*) malloc(sizeof(struct dirent));
+			if (!tmp)
+				goto exit_failure;
+		}
+
+		/* Read directory entry to temporary area */
+		struct dirent *entry;
+		if (readdir_r(dir, tmp, &entry) != /*OK*/0)
+			goto exit_failure;
+
+		/* Stop if we already read the last directory entry */
+		if (entry == NULL)
+			goto exit_success;
+
+		/* Determine whether to include the entry in results */
+		if (filter && !filter(tmp))
+			continue;
+
+		/* Enlarge pointer table to make room for another pointer */
+		if (size >= allocated) {
+			/* Compute number of entries in the new table */
+			size_t num_entries = size * 2 + 16;
+
+			/* Allocate new pointer table or enlarge existing */
+			void *p = realloc(files, sizeof(void*) * num_entries);
+			if (!p)
+				goto exit_failure;
+
+			/* Got the memory */
+			files = (dirent**) p;
+			allocated = num_entries;
+		}
+
+		/* Store the temporary entry to ptr table */
+		files[size++] = tmp;
+		tmp = NULL;
+	}
+
+exit_failure:
+	/* Release allocated entries */
+	for (size_t i = 0; i < size; i++) {
+		free(files[i]);
+	}
+
+	/* Release the pointer table */
+	free(files);
+	files = NULL;
+
+	/* Exit with error code */
+	result = /*error*/ -1;
+	goto exit_status;
+
+exit_success:
+	/* Sort directory entries */
+	qsort(files, size, sizeof(void*),
+		(int (*) (const void*, const void*)) compare);
+
+	/* Pass pointer table to caller */
+	if (namelist)
+		*namelist = files;
+
+	/* Return the number of directory entries read */
+	result = (int) size;
+
+exit_status:
+	/* Release temporary directory entry, if we had one */
+	free(tmp);
+
+	/* Close directory stream */
+	closedir(dir);
+	return result;
+}
+
+/* Alphabetical sorting */
+static int
+alphasort(const struct dirent **a, const struct dirent **b)
+{
+	return strcoll((*a)->d_name, (*b)->d_name);
+}
+
+/* Sort versions */
+static int
+versionsort(const struct dirent **a, const struct dirent **b)
+{
+	return strverscmp((*a)->d_name, (*b)->d_name);
+}
+
+/* Compare strings */
+static int
+strverscmp(const char *a, const char *b)
+{
+	size_t i = 0;
+	size_t j;
+
+	/* Find first difference */
+	while (a[i] == b[i]) {
+		if (a[i] == '\0') {
+			/* No difference */
+			return 0;
+		}
+		++i;
+	}
+
+	/* Count backwards and find the leftmost digit */
+	j = i;
+	while (j > 0 && isdigit(a[j-1])) {
+		--j;
+	}
+
+	/* Determine mode of comparison */
+	if (a[j] == '0' || b[j] == '0') {
+		/* Find the next non-zero digit */
+		while (a[j] == '0' && a[j] == b[j]) {
+			j++;
+		}
+
+		/* String with more digits is smaller, e.g 002 < 01 */
+		if (isdigit(a[j])) {
+			if (!isdigit(b[j])) {
+				return -1;
+			}
+		} else if (isdigit(b[j])) {
+			return 1;
+		}
+	} else if (isdigit(a[j]) && isdigit(b[j])) {
+		/* Numeric comparison */
+		size_t k1 = j;
+		size_t k2 = j;
+
+		/* Compute number of digits in each string */
+		while (isdigit(a[k1])) {
+			k1++;
+		}
+		while (isdigit(b[k2])) {
+			k2++;
+		}
+
+		/* Number with more digits is bigger, e.g 999 < 1000 */
+		if (k1 < k2)
+			return -1;
+		else if (k1 > k2)
+			return 1;
+	}
+
+	/* Alphabetical comparison */
+	return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]);
+}
+
+/* Convert multi-byte string to wide character string */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int
+dirent_mbstowcs_s(
+	size_t *pReturnValue, wchar_t *wcstr,
+	size_t sizeInWords, const char *mbstr, size_t count)
+{
+	/* Older Visual Studio or non-Microsoft compiler */
+	size_t n = mbstowcs(wcstr, mbstr, sizeInWords);
+	if (wcstr && n >= count)
+		return /*error*/ 1;
+
+	/* Zero-terminate output buffer */
+	if (wcstr && sizeInWords) {
+		if (n >= sizeInWords)
+			n = sizeInWords - 1;
+		wcstr[n] = 0;
+	}
+
+	/* Length of multi-byte string with zero terminator */
+	if (pReturnValue) {
+		*pReturnValue = n + 1;
+	}
+
+	/* Success */
+	return 0;
+}
+#endif
+
+/* Convert wide-character string to multi-byte string */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int
+dirent_wcstombs_s(
+	size_t *pReturnValue, char *mbstr,
+	size_t sizeInBytes, const wchar_t *wcstr, size_t count)
+{
+	/* Older Visual Studio or non-Microsoft compiler */
+	size_t n = wcstombs(mbstr, wcstr, sizeInBytes);
+	if (mbstr && n >= count)
+		return /*error*/1;
+
+	/* Zero-terminate output buffer */
+	if (mbstr && sizeInBytes) {
+		if (n >= sizeInBytes) {
+			n = sizeInBytes - 1;
+		}
+		mbstr[n] = '\0';
+	}
+
+	/* Length of resulting multi-bytes string WITH zero-terminator */
+	if (pReturnValue) {
+		*pReturnValue = n + 1;
+	}
+
+	/* Success */
+	return 0;
+}
+#endif
+
+/* Set errno variable */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static void
+dirent_set_errno(int error)
+{
+	/* Non-Microsoft compiler or older Microsoft compiler */
+	errno = error;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*DIRENT_H*/
diff --git a/dirent/tests/1/dir/readme.txt b/dirent/tests/1/dir/readme.txt
new file mode 100644
index 0000000..e59af2f
--- /dev/null
+++ b/dirent/tests/1/dir/readme.txt
@@ -0,0 +1,3 @@
+This file ensures that the directory dir will be created accordingly when
+you unzip dirent to your computer.  The directory itself is needed by the
+test program t-dirent.
diff --git a/dirent/tests/1/file b/dirent/tests/1/file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/1/file
diff --git a/dirent/tests/2/Testfile-1.2.3.dat b/dirent/tests/2/Testfile-1.2.3.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/2/Testfile-1.2.3.dat
diff --git a/dirent/tests/2/file.txt b/dirent/tests/2/file.txt
new file mode 100644
index 0000000..d32e004
--- /dev/null
+++ b/dirent/tests/2/file.txt
@@ -0,0 +1 @@
+This dummy file is needed by the test program t-dirent.
diff --git a/dirent/tests/3/3zero.dat b/dirent/tests/3/3zero.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/3zero.dat
diff --git a/dirent/tests/3/666.dat b/dirent/tests/3/666.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/666.dat
diff --git a/dirent/tests/3/Qwerty-my-aunt.dat b/dirent/tests/3/Qwerty-my-aunt.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/Qwerty-my-aunt.dat
diff --git a/dirent/tests/3/README.txt b/dirent/tests/3/README.txt
new file mode 100644
index 0000000..a07591c
--- /dev/null
+++ b/dirent/tests/3/README.txt
@@ -0,0 +1,2 @@
+This directory contains some random files for the t-scandir test program.  The
+files are empty and only the file names matter.
diff --git a/dirent/tests/3/aaa.dat b/dirent/tests/3/aaa.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/aaa.dat
diff --git a/dirent/tests/3/dirent.dat b/dirent/tests/3/dirent.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/dirent.dat
diff --git a/dirent/tests/3/empty.dat b/dirent/tests/3/empty.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/empty.dat
diff --git a/dirent/tests/3/sane-1.12.0.dat b/dirent/tests/3/sane-1.12.0.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/sane-1.12.0.dat
diff --git a/dirent/tests/3/sane-1.2.30.dat b/dirent/tests/3/sane-1.2.30.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/sane-1.2.30.dat
diff --git a/dirent/tests/3/sane-1.2.4.dat b/dirent/tests/3/sane-1.2.4.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/sane-1.2.4.dat
diff --git a/dirent/tests/3/zebra.dat b/dirent/tests/3/zebra.dat
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/3/zebra.dat
diff --git a/dirent/tests/4/asidofilusphosphorus b/dirent/tests/4/asidofilusphosphorus
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/4/asidofilusphosphorus
diff --git a/dirent/tests/4/bubblebob b/dirent/tests/4/bubblebob
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/4/bubblebob
diff --git a/dirent/tests/4/celsiusfortissimo b/dirent/tests/4/celsiusfortissimo
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dirent/tests/4/celsiusfortissimo
diff --git a/dirent/tests/t-compile.c b/dirent/tests/t-compile.c
new file mode 100644
index 0000000..90d8e50
--- /dev/null
+++ b/dirent/tests/t-compile.c
@@ -0,0 +1,46 @@
+/*
+ * Test program to make sure that dirent compiles cleanly with winsock.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#include <dirent.h>
+#ifdef WIN32
+#	include <winsock2.h>
+#	include <ws2tcpip.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main(int argc, char *argv[])
+{
+	struct dirent *dirp = NULL;
+
+	(void) argc;
+	(void) argv;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+	printf("Has d_type\n");
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+	printf("Has d_namlen\n");
+#endif
+#ifdef _D_EXACT_NAMLEN
+	printf("Has _D_EXACT_NAMLEN\n");
+#endif
+#ifdef _D_ALLOC_NAMLEN
+	printf("Has _D_ALLOC_NAMLEN\n");
+#endif
+#ifdef _D_ALLOC_NAMLEN
+	printf("Has _D_ALLOC_NAMLEN\n");
+#endif
+	printf("Length of d_name with terminator: %d\n",
+		(int) sizeof(dirp->d_name));
+
+	printf("OK\n");
+	return EXIT_SUCCESS;
+}
diff --git a/dirent/tests/t-cplusplus.cpp b/dirent/tests/t-cplusplus.cpp
new file mode 100644
index 0000000..8a8bad4
--- /dev/null
+++ b/dirent/tests/t-cplusplus.cpp
@@ -0,0 +1,155 @@
+/*
+ * Test program to make sure that dirent compiles cleanly with C++
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#include <iostream>
+#include <string.h>
+#include <dirent.h>
+#include <assert.h>
+using namespace std;
+
+/* Filter and sort functions */
+static int only_readme(const struct dirent *entry);
+static void test_retrieval(void);
+static void test_scan(void);
+
+int
+main(int argc, char *argv[])
+{
+	(void) argc;
+	(void) argv;
+
+	test_retrieval();
+	test_scan();
+
+	cout << "OK" << endl;
+	return EXIT_SUCCESS;
+}
+
+/* Test basic directory retrieval */
+static void
+test_retrieval(void)
+{
+	/* Open directory */
+	DIR *dir = opendir("tests/1");
+	if (dir == NULL) {
+		cerr << "Directory tests/1 not found" << endl;
+		abort();
+	}
+
+	/* Read directory entries */
+	struct dirent *ent;
+	int found = 0;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 1);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 1);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 1);
+#endif
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 2);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 2);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 2);
+#endif
+			found += 2;
+		} else if (strcmp(ent->d_name, "file") == 0) {
+			/* Regular file */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_REG);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 4);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 4);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 4);
+#endif
+			found += 4;
+		} else if (strcmp(ent->d_name, "dir") == 0) {
+			/* Just a directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 3);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 3);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 3);
+#endif
+			found += 8;
+		} else {
+			/* Other file */
+			cerr << "Unexpected file " << ent->d_name << endl;
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	closedir(dir);
+}
+
+/* Text basic scan with simple filter function */
+static void
+test_scan(void)
+{
+	struct dirent **files;
+
+	/* Read directory entries */
+	int n = scandir("tests/3", &files, only_readme, alphasort);
+	assert(n == 1);
+
+	/* Make sure that the filter works */
+	assert(strcmp(files[0]->d_name, "README.txt") == 0);
+
+	/* Release file names */
+	for (int i = 0; i < n; i++) {
+		free(files[i]);
+	}
+	free(files);
+}
+
+/* Only pass README.txt file */
+static int
+only_readme(const struct dirent *entry)
+{
+	int pass;
+
+	if (strcmp (entry->d_name, "README.txt") == 0) {
+		pass = 1;
+	} else {
+		pass = 0;
+	}
+
+	return pass;
+}
diff --git a/dirent/tests/t-dirent.c b/dirent/tests/t-dirent.c
new file mode 100644
index 0000000..95f151a
--- /dev/null
+++ b/dirent/tests/t-dirent.c
@@ -0,0 +1,608 @@
+/*
+ * A test program to make sure that dirent works correctly.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+#	include <direct.h>
+#	define chdir(x) _chdir(x)
+#else
+#	include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+static void test_macros(void);
+static void test_retrieval(void);
+static void test_nonexistent(void);
+static void test_isfile(void);
+static void test_zero(void);
+static void test_rewind(void);
+static void test_chdir(void);
+static void test_filename(void);
+static void test_readdir(void);
+static void test_wreaddir(void);
+
+int
+main(int argc, char *argv[])
+{
+	(void) argc;
+	(void) argv;
+
+	/* Execute tests */
+	test_macros();
+	test_retrieval();
+	test_nonexistent();
+	test_isfile();
+	test_zero();
+	test_rewind();
+	test_chdir();
+	test_filename();
+	test_readdir();
+	test_wreaddir();
+
+	printf("OK\n");
+	return EXIT_SUCCESS;
+}
+
+/* Test file type macros */
+static void
+test_macros(void)
+{
+	assert(DTTOIF(DT_REG) == S_IFREG);
+	assert(DTTOIF(DT_DIR) == S_IFDIR);
+	assert(DTTOIF(DT_FIFO) == S_IFIFO);
+	assert(DTTOIF(DT_SOCK) == S_IFSOCK);
+	assert(DTTOIF(DT_CHR) == S_IFCHR);
+	assert(DTTOIF(DT_BLK) == S_IFBLK);
+
+	assert(IFTODT(S_IFREG) == DT_REG);
+	assert(IFTODT(S_IFDIR) == DT_DIR);
+	assert(IFTODT(S_IFIFO) == DT_FIFO);
+	assert(IFTODT(S_IFSOCK) == DT_SOCK);
+	assert(IFTODT(S_IFCHR) == DT_CHR);
+	assert(IFTODT(S_IFBLK) == DT_BLK);
+}
+
+/* Test basic directory retrieval */
+static void
+test_retrieval(void)
+{
+	/* Open directory */
+	DIR *dir = opendir("tests/1");
+	if (dir == NULL) {
+		fprintf(stderr, "Directory tests/1 not found\n");
+		abort();
+	}
+
+	/* Read entries */
+	struct dirent *ent;
+	int found = 0;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 1);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 1);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 1);
+#endif
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 2);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 2);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 2);
+#endif
+			found += 2;
+		} else if (strcmp(ent->d_name, "file") == 0) {
+			/* Regular file */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_REG);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 4);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 4);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 4);
+#endif
+			found += 4;
+		} else if (strcmp(ent->d_name, "dir") == 0) {
+			/* Just a directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 3);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 3);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 3);
+#endif
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", ent->d_name);
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	closedir(dir);
+}
+
+/* Function opendir() fails if directory doesn't exist */
+static void
+test_nonexistent(void)
+{
+	DIR *dir = opendir("tests/invalid");
+	assert(dir == NULL);
+	assert(errno == ENOENT);
+}
+
+/* Function opendir() fails if pathname is really a file */
+static void
+test_isfile(void)
+{
+	DIR *dir = opendir("tests/1/file");
+	assert(dir == NULL);
+	assert(errno == ENOTDIR);
+}
+
+/* Function opendir() fails if pathname is a zero-length string */
+static void
+test_zero(void)
+{
+	DIR *dir = opendir("");
+	assert(dir == NULL);
+	assert(errno == ENOENT);
+}
+
+/* Test rewind of directory stream */
+static void
+test_rewind(void)
+{
+	/* Open directory */
+	DIR *dir = opendir("tests/1");
+	assert(dir != NULL);
+
+	/* Read entries */
+	int found = 0;
+	struct dirent *ent;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+			found += 2;
+		} else if (strcmp(ent->d_name, "file") == 0) {
+			/* Regular file */
+			found += 4;
+		} else if (strcmp(ent->d_name, "dir") == 0) {
+			/* Just a directory */
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", ent->d_name);
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	/* Rewind stream and read entries again */
+	rewinddir(dir);
+	found = 0;
+
+	/* Read entries */
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+			found += 2;
+		} else if (strcmp(ent->d_name, "file") == 0) {
+			/* Regular file */
+			found += 4;
+		} else if (strcmp(ent->d_name, "dir") == 0) {
+			/* Just a directory */
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", ent->d_name);
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	closedir(dir);
+}
+
+/* Test rewind with intervening change of working directory */
+static void
+test_chdir(void)
+{
+	/* Open directory */
+	DIR *dir = opendir("tests/1");
+	assert(dir != NULL);
+
+	/* Read entries */
+	struct dirent *ent;
+	int found = 0;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+			found += 2;
+		} else if (strcmp(ent->d_name, "file") == 0) {
+			/* Regular file */
+			found += 4;
+		} else if (strcmp(ent->d_name, "dir") == 0) {
+			/* Just a directory */
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", ent->d_name);
+			abort();
+		}
+
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	/* Change working directory */
+	int errorcode = chdir("tests");
+	assert(errorcode == 0);
+
+	/* Rewind stream and read entries again */
+	rewinddir(dir);
+	found = 0;
+
+	/* Read entries */
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+			found += 2;
+		} else if (strcmp(ent->d_name, "file") == 0) {
+			/* Regular file */
+			found += 4;
+		} else if (strcmp(ent->d_name, "dir") == 0) {
+			/* Just a directory */
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", ent->d_name);
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	/* Restore working directory */
+	errorcode = chdir("..");
+	assert(errorcode == 0);
+
+	closedir(dir);
+}
+
+/* Test long file name */
+static void
+test_filename(void)
+{
+	/* Open directory */
+	DIR *dir = opendir("tests/2");
+	if (dir == NULL) {
+		fprintf(stderr, "Directory tests/2 not found\n");
+		abort();
+	}
+
+	/* Read entries */
+	struct dirent *ent;
+	int found = 0;
+	while ((ent = readdir(dir)) != NULL) {
+		/* Check each file */
+		if (strcmp(ent->d_name, ".") == 0) {
+			/* Directory itself */
+			found += 1;
+		} else if (strcmp(ent->d_name, "..") == 0) {
+			/* Parent directory */
+			found += 2;
+		} else if (strcmp(ent->d_name, "file.txt") == 0) {
+			/* Regular 8+3 filename */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_REG);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 8);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 8);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 8);
+#endif
+			found += 4;
+		} else if (strcmp(ent->d_name, "Testfile-1.2.3.dat") == 0) {
+			/* Long file name with multiple dots */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(ent->d_type == DT_REG);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(ent->d_namlen == 18);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(ent) == 18);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(ent) > 18);
+#endif
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", ent->d_name);
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	closedir(dir);
+}
+
+/* Test basic directory retrieval with readdir_r */
+static void
+test_readdir(void)
+{
+	/* Open directory */
+	DIR *dir = opendir("tests/1");
+	if (dir == NULL) {
+		fprintf(stderr, "Directory tests/1 not found\n");
+		abort();
+	}
+
+	/* Read entries to table */
+	struct dirent ent[10];
+	struct dirent *entry;
+	size_t i = 0;
+	size_t n = 0;
+	while (readdir_r(dir, &ent[n], &entry) == /*OK*/0 && entry != 0) {
+		n++;
+		assert(n <= 4);
+	}
+
+	/* Make sure that we got all the files from directory */
+	assert(n == 4);
+
+	/* Check entries in memory */
+	int found = 0;
+	for (i = 0; i < 4; i++) {
+		entry = &ent[i];
+
+		/* Check each file */
+		if (strcmp(entry->d_name, ".") == 0) {
+			/* Directory itself */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 1);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 1);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 1);
+#endif
+			found += 1;
+		} else if (strcmp(entry->d_name, "..") == 0) {
+			/* Parent directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 2);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 2);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 2);
+#endif
+			found += 2;
+		} else if (strcmp(entry->d_name, "file") == 0) {
+			/* Regular file */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_REG);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 4);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 4);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 4);
+#endif
+			found += 4;
+		} else if (strcmp(entry->d_name, "dir") == 0) {
+			/* Just a directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 3);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 3);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 3);
+#endif
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file %s\n", entry->d_name);
+			abort();
+		}
+
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	closedir(dir);
+}
+
+/* Basic directory retrieval with _wreaddir_r */
+static void
+test_wreaddir(void)
+{
+#ifdef WIN32
+	/* Open directory */
+	_WDIR *dir = _wopendir(L"tests/1");
+	if (dir == NULL) {
+		fprintf(stderr, "Directory tests/1 not found\n");
+		abort();
+	}
+
+	/* Read entries to table */
+	struct _wdirent ent[10];
+	struct _wdirent *entry;
+	size_t i = 0;
+	size_t n = 0;
+	while (_wreaddir_r(dir, &ent[n], &entry) == /*OK*/0 && entry != 0) {
+		n++;
+		assert(n <= 4);
+	}
+
+	/* Make sure that we got all the files from directory */
+	assert(n == 4);
+
+	/* Check entries in memory */
+	int found = 0;
+	for (i = 0; i < 4; i++) {
+		entry = &ent[i];
+
+		/* Check each file */
+		if (wcscmp(entry->d_name, L".") == 0) {
+			/* Directory itself */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 1);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 1);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 1);
+#endif
+			found += 1;
+		} else if (wcscmp(entry->d_name, L"..") == 0) {
+			/* Parent directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 2);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 2);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 2);
+#endif
+			found += 2;
+		} else if (wcscmp(entry->d_name, L"file") == 0) {
+			/* Regular file */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_REG);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 4);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 4);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 4);
+#endif
+			found += 4;
+		} else if (wcscmp(entry->d_name, L"dir") == 0) {
+			/* Just a directory */
+#ifdef _DIRENT_HAVE_D_TYPE
+			assert(entry->d_type == DT_DIR);
+#endif
+#ifdef _DIRENT_HAVE_D_NAMLEN
+			assert(entry->d_namlen == 3);
+#endif
+#ifdef _D_EXACT_NAMLEN
+			assert(_D_EXACT_NAMLEN(entry) == 3);
+#endif
+#ifdef _D_ALLOC_NAMLEN
+			assert(_D_ALLOC_NAMLEN(entry) > 3);
+#endif
+			found += 8;
+		} else {
+			/* Other file */
+			fprintf(stderr, "Unexpected file\n");
+			abort();
+		}
+	}
+
+	/* Make sure that all files were found */
+	assert(found == 0xf);
+
+	_wclosedir(dir);
+#endif
+}
diff --git a/dirent/tests/t-scandir.c b/dirent/tests/t-scandir.c
new file mode 100644
index 0000000..c463f9b
--- /dev/null
+++ b/dirent/tests/t-scandir.c
@@ -0,0 +1,276 @@
+/*
+ * Make sure that scandir function works OK.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+
+/* Silence warning about fopen being insecure (MS Visual Studio) */
+#define _CRT_SECURE_NO_WARNINGS
+
+/* Include prototype for versionsort (Linux) */
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+/* Filter and sort functions */
+static int only_readme(const struct dirent *entry);
+static int no_directories(const struct dirent *entry);
+static int reverse_alpha(const struct dirent **a, const struct dirent **b);
+
+int
+main(int argc, char *argv[])
+{
+	struct dirent **files;
+	int i;
+	int n;
+
+	(void) argc;
+	(void) argv;
+
+	/* Initialize random number generator */
+	srand((unsigned) time(NULL));
+
+	/* Basic scan with simple filter function */
+	{
+		/* Read directory entries */
+		n = scandir("tests/3", &files, only_readme, alphasort);
+		assert(n == 1);
+
+		/* Make sure that the filter works */
+		assert(strcmp(files[0]->d_name, "README.txt") == 0);
+
+		/* Release file names */
+		for (i = 0; i < n; i++) {
+			free(files[i]);
+		}
+		free(files);
+	}
+
+	/* Basic scan with default sorting function */
+	{
+		/* Read directory entries in alphabetic order */
+		n = scandir("tests/3", &files, NULL, alphasort);
+		assert(n == 13);
+
+		/* Make sure that we got all the names in the proper order */
+		assert(strcmp(files[0]->d_name, ".") == 0);
+		assert(strcmp(files[1]->d_name, "..") == 0);
+		assert(strcmp(files[2]->d_name, "3zero.dat") == 0);
+		assert(strcmp(files[3]->d_name, "666.dat") == 0);
+		assert(strcmp(files[4]->d_name, "Qwerty-my-aunt.dat") == 0);
+		assert(strcmp(files[5]->d_name, "README.txt") == 0);
+		assert(strcmp(files[6]->d_name, "aaa.dat") == 0);
+		assert(strcmp(files[7]->d_name, "dirent.dat") == 0);
+		assert(strcmp(files[8]->d_name, "empty.dat") == 0);
+		assert(strcmp(files[9]->d_name, "sane-1.12.0.dat") == 0);
+		assert(strcmp(files[10]->d_name, "sane-1.2.30.dat") == 0);
+		assert(strcmp(files[11]->d_name, "sane-1.2.4.dat") == 0);
+		assert(strcmp(files[12]->d_name, "zebra.dat") == 0);
+
+		/* Release file names */
+		for (i = 0; i < n; i++) {
+			free(files[i]);
+		}
+		free(files);
+	}
+
+	/* Custom filter AND sort function */
+	{
+		/* Read directory entries in alphabetic order */
+		n = scandir("tests/3", &files, no_directories, reverse_alpha);
+		assert(n == 11);
+
+		/* Make sure that we got file names in the reverse order */
+		assert(strcmp(files[0]->d_name, "zebra.dat") == 0);
+		assert(strcmp(files[1]->d_name, "sane-1.2.4.dat") == 0);
+		assert(strcmp(files[2]->d_name, "sane-1.2.30.dat") == 0);
+		assert(strcmp(files[3]->d_name, "sane-1.12.0.dat") == 0);
+		assert(strcmp(files[4]->d_name, "empty.dat") == 0);
+		assert(strcmp(files[5]->d_name, "dirent.dat") == 0);
+		assert(strcmp(files[6]->d_name, "aaa.dat") == 0);
+		assert(strcmp(files[7]->d_name, "README.txt") == 0);
+		assert(strcmp(files[8]->d_name, "Qwerty-my-aunt.dat") == 0);
+		assert(strcmp(files[9]->d_name, "666.dat") == 0);
+		assert(strcmp(files[10]->d_name, "3zero.dat") == 0);
+
+		/* Release file names */
+		for (i = 0; i < n; i++) {
+			free(files[i]);
+		}
+		free(files);
+	}
+
+	/* Trying to read from non-existent directory leads to an error */
+	{
+		files = NULL;
+		n = scandir("tests/invalid", &files, NULL, alphasort);
+		assert(n == -1);
+		assert(files == NULL);
+		assert(errno == ENOENT);
+	}
+
+	/* Trying to open file as a directory produces ENOTDIR error */
+	{
+		files = NULL;
+		n = scandir("tests/3/666.dat", &files, NULL, alphasort);
+		assert(n == -1);
+		assert(files == NULL);
+		assert(errno == ENOTDIR);
+	}
+
+	/* Sort files using versionsort() */
+	{
+		files = NULL;
+		n = scandir("tests/3", &files, no_directories, versionsort);
+		assert(n == 11);
+
+		/*
+		 * Make sure that we got all the file names in the proper order:
+		 * 1.2.4 < 1.2.30 < 1.12.0
+		 */
+		assert(strcmp(files[0]->d_name, "3zero.dat") == 0);
+		assert(strcmp(files[1]->d_name, "666.dat") == 0);
+		assert(strcmp(files[2]->d_name, "Qwerty-my-aunt.dat") == 0);
+		assert(strcmp(files[3]->d_name, "README.txt") == 0);
+		assert(strcmp(files[4]->d_name, "aaa.dat") == 0);
+		assert(strcmp(files[5]->d_name, "dirent.dat") == 0);
+		assert(strcmp(files[6]->d_name, "empty.dat") == 0);
+		assert(strcmp(files[7]->d_name, "sane-1.2.4.dat") == 0);
+		assert(strcmp(files[8]->d_name, "sane-1.2.30.dat") == 0);
+		assert(strcmp(files[9]->d_name, "sane-1.12.0.dat") == 0);
+		assert(strcmp(files[10]->d_name, "zebra.dat") == 0);
+
+		/* Release file names */
+		for (i = 0; i < n; i++) {
+			free(files[i]);
+		}
+		free(files);
+	}
+
+	/* Scan large directory */
+	{
+		char dirname[PATH_MAX+1];
+		int i;
+		int ok;
+
+		/* Copy name of temporary directory to variable dirname */
+#ifdef WIN32
+		i = GetTempPathA(PATH_MAX, dirname);
+		assert(i > 0);
+#else
+		strcpy(dirname, "/tmp/");
+		i = strlen(dirname);
+#endif
+
+		/* Append random characters to dirname */
+		for (int j = 0; j < 10; j++) {
+			char c;
+
+			/* Generate random character */
+			c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26];
+
+			/* Append character to dirname */
+			assert(i < PATH_MAX);
+			dirname[i++] = c;
+		}
+
+		/* Terminate directory name */
+		assert(i < PATH_MAX);
+		dirname[i] = '\0';
+
+		/* Create directory */
+#ifdef WIN32
+		ok = CreateDirectoryA(dirname, NULL);
+		assert(ok);
+#else
+		ok = mkdir(dirname, 0700);
+		assert(ok == /*success*/0);
+#endif
+
+		/* Create one thousand files */
+		assert(i + 5 < PATH_MAX);
+		for (int j = 0; j < 1000; j++) {
+			FILE *fp;
+
+			/* Construct file name */
+			dirname[i] = '/';
+			dirname[i+1] = 'z';
+			dirname[i+2] = '0' + ((j / 100) % 10);
+			dirname[i+3] = '0' + ((j / 10) % 10);
+			dirname[i+4] = '0' + (j % 10);
+			dirname[i+5] = '\0';
+
+			/* Create file */
+			fp = fopen(dirname, "w");
+			assert(fp != NULL);
+			fclose(fp);
+
+		}
+
+		/* Cut out the file name part */
+		dirname[i] = '\0';
+
+		/* Scan directory */
+		n = scandir(dirname, &files, no_directories, alphasort);
+		assert(n == 1000);
+
+		/* Make sure that all 1000 files are read back */
+		for (int j = 0; j < n; j++) {
+			char match[100];
+
+			/* Construct file name */
+			match[0] = 'z';
+			match[1] = '0' + ((j / 100) % 10);
+			match[2] = '0' + ((j / 10) % 10);
+			match[3] = '0' + (j % 10);
+			match[4] = '\0';
+
+			/* Make sure that file name matches that on the disk */
+			assert(strcmp(files[j]->d_name, match) == 0);
+
+		}
+
+		/* Release file names */
+		for (int j = 0; j < n; j++) {
+			free(files[j]);
+		}
+		free(files);
+	}
+
+	printf("OK\n");
+	return EXIT_SUCCESS;
+}
+
+/* Only pass README.txt file */
+static int
+only_readme(const struct dirent *entry)
+{
+	return strcmp(entry->d_name, "README.txt") == 0;
+}
+
+/* Filter out directories */
+static int
+no_directories(const struct dirent *entry)
+{
+	return entry->d_type != DT_DIR;
+}
+
+/* Sort in reverse direction */
+static int
+reverse_alpha(const struct dirent **a, const struct dirent **b)
+{
+	return strcoll((*b)->d_name, (*a)->d_name);
+}
diff --git a/dirent/tests/t-strverscmp.c b/dirent/tests/t-strverscmp.c
new file mode 100644
index 0000000..fcb044f
--- /dev/null
+++ b/dirent/tests/t-strverscmp.c
@@ -0,0 +1,206 @@
+/*
+ * Test program to make sure that strverscmp works correctly
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+
+/* Include prototype for strverscmp */
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dirent.h>
+#include <ctype.h>
+
+int
+main(
+	int argc, char *argv[])
+{
+	(void) argc;
+	(void) argv;
+
+	/* Strings without digits are compared as in strcmp() */
+	assert(strverscmp("", "") == 0);
+	assert(strverscmp("abc", "abc") == 0);
+	assert(strverscmp("a", "b") < 0);
+	assert(strverscmp("b", "a") > 0);
+
+	/* Shorter string is smaller, other things being equal */
+	assert(strverscmp("a", "aa") < 0);
+	assert(strverscmp("aa", "a") > 0);
+	assert(strverscmp("abcdef", "abcdefg") < 0);
+	assert(strverscmp("abcdefg", "abcdef") > 0);
+
+	/* Integers with equal length are compared as in strcmp() */
+	assert(strverscmp("0", "0") == 0);
+	assert(strverscmp("000", "000") == 0);
+	assert(strverscmp("1", "2") < 0);
+	assert(strverscmp("2", "1") > 0);
+	assert(strverscmp("001", "100") < 0);
+	assert(strverscmp("100", "001") > 0);
+	assert(strverscmp("2020-07-01", "2020-07-02") < 0);
+	assert(strverscmp("2020-07-02", "2020-07-01") > 0);
+	assert(strverscmp("jan999", "jan999") == 0);
+
+	/* Integers of different length are compared as numbers */
+	assert(strverscmp("jan9", "jan10") < 0);
+	assert(strverscmp("jan10", "jan9") > 0);
+	assert(strverscmp("999", "1000") < 0);
+	assert(strverscmp("1000", "999") > 0);
+	assert(strverscmp("t12-1000", "t12-9999") < 0);
+	assert(strverscmp("t12-9999", "t12-1000") > 0);
+	assert(strverscmp("1000", "10001") < 0);
+	assert(strverscmp("10001", "1000") > 0);
+	assert(strverscmp("1000!", "10001") < 0);
+	assert(strverscmp("10001", "1000!") > 0);
+	assert(strverscmp("1000Z", "10001") < 0);
+	assert(strverscmp("10001", "1000Z") > 0);
+
+	/* If numbers starts with zero, then longer number is smaller */
+	assert(strverscmp("00", "0") < 0);
+	assert(strverscmp("0", "00") > 0);
+	assert(strverscmp("a000", "a00") < 0);
+	assert(strverscmp("a00", "a000") > 0);
+	assert(strverscmp("0000", "000") < 0);
+	assert(strverscmp("000", "0000") > 0);
+	assert(strverscmp("0000", "000!") < 0);
+	assert(strverscmp("000!", "0000") > 0);
+	assert(strverscmp("0000", "000Z") < 0);
+	assert(strverscmp("000Z", "0000") > 0);
+	assert(strverscmp("0000", "000Z") < 0);
+	assert(strverscmp("000Z", "0000") > 0);
+	assert(strverscmp("1.01", "1.0") < 0);
+	assert(strverscmp("1.0", "1.01") > 0);
+	assert(strverscmp("1.01", "1.0!") < 0);
+	assert(strverscmp("1.0!", "1.01") > 0);
+	assert(strverscmp("1.01", "1.0~") < 0);
+	assert(strverscmp("1.0~", "1.01") > 0);
+
+	/* Number having more leading zeros is considered smaller */
+	assert(strverscmp("item-0001", "item-001") < 0);
+	assert(strverscmp("item-001", "item-0001") > 0);
+	assert(strverscmp("item-001", "item-01") < 0);
+	assert(strverscmp("item-01", "item-001") > 0);
+	assert(strverscmp(".0001000", ".001") < 0);
+	assert(strverscmp(".001", ".0001000") > 0);
+	assert(strverscmp(".0001000", ".01") < 0);
+	assert(strverscmp(".01", ".0001000") > 0);
+	assert(strverscmp(".0001000", ".1") < 0);
+	assert(strverscmp(".1", ".0001000") > 0);
+	assert(strverscmp("1.0002", "1.0010000") < 0);
+	assert(strverscmp("1.0010000", "1.0002") > 0);
+
+	/* Number starting with zero is smaller than any number */
+	assert(strverscmp("item-009", "item-1") < 0);
+	assert(strverscmp("item-1", "item-009") > 0);
+	assert(strverscmp("item-099", "item-2") < 0);
+	assert(strverscmp("item-2", "item-099") > 0);
+
+	/* Number vs alphabetical comparison */
+	assert(strverscmp("1.001", "1.00!") < 0);
+	assert(strverscmp("1.00!", "1.001") > 0);
+	assert(strverscmp("1.001", "1.00x") < 0);
+	assert(strverscmp("1.00x", "1.001") > 0);
+	assert(strverscmp("1", "x") < 0);
+	assert(strverscmp("x", "1") > 0);
+	assert(strverscmp("1", "!") > 0);
+	assert(strverscmp("!", "1") < 0);
+
+	/* Handling the end of string */
+	assert(strverscmp("01", "011") < 0);
+	assert(strverscmp("011", "01") > 0);
+	assert(strverscmp("0100", "01000") < 0);
+	assert(strverscmp("01000", "0100") > 0);
+	assert(strverscmp("1", "1!") < 0);
+	assert(strverscmp("1!", "1") > 0);
+	assert(strverscmp("1", "1z") < 0);
+	assert(strverscmp("1z", "1") > 0);
+
+	/* Ordering 000 < 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 */
+	assert(strverscmp("000", "00") < 0);
+	assert(strverscmp("000", "01") < 0);
+	assert(strverscmp("000", "010") < 0);
+	assert(strverscmp("000", "09") < 0);
+	assert(strverscmp("000", "0") < 0);
+	assert(strverscmp("000", "1") < 0);
+	assert(strverscmp("000", "9") < 0);
+	assert(strverscmp("000", "10") < 0);
+
+	assert(strverscmp("00", "01") < 0);
+	assert(strverscmp("00", "010") < 0);
+	assert(strverscmp("00", "09") < 0);
+	assert(strverscmp("00", "0") < 0);
+	assert(strverscmp("00", "1") < 0);
+	assert(strverscmp("00", "9") < 0);
+	assert(strverscmp("00", "10") < 0);
+
+	assert(strverscmp("01", "010") < 0);
+	assert(strverscmp("01", "09") < 0);
+	assert(strverscmp("01", "0") < 0);
+	assert(strverscmp("01", "1") < 0);
+	assert(strverscmp("01", "9") < 0);
+	assert(strverscmp("01", "10") < 0);
+
+	assert(strverscmp("010", "09") < 0);
+	assert(strverscmp("010", "0") < 0);
+	assert(strverscmp("010", "1") < 0);
+	assert(strverscmp("010", "9") < 0);
+	assert(strverscmp("010", "10") < 0);
+
+	assert(strverscmp("09", "0") < 0);
+	assert(strverscmp("09", "1") < 0);
+	assert(strverscmp("09", "9") < 0);
+	assert(strverscmp("09", "10") < 0);
+
+	assert(strverscmp("0", "1") < 0);
+	assert(strverscmp("0", "9") < 0);
+	assert(strverscmp("0", "10") < 0);
+
+	assert(strverscmp("1", "9") < 0);
+	assert(strverscmp("1", "10") < 0);
+
+	assert(strverscmp("9", "10") < 0);
+
+	/* Compare speed */
+	{
+#define LENGTH 100
+#define REPEAT 1000000
+		char a[LENGTH+1];
+		char b[LENGTH+1];
+		size_t i;
+		size_t j;
+		char letters[] = "01234567890123456789abdefghjkpqrtwxyz-/.";
+		size_t n = strlen(letters);
+
+		/* Repeat test */
+		for(i = 0; i < REPEAT; i++) {
+			int diff1;
+			int diff2;
+
+			/* Generate two random strings of LENGTH characters */
+			for(j = 0; j < LENGTH; j++) {
+				a[j] = letters[rand() % n];
+				b[j] = letters[rand() % n];
+			}
+			a[j] = '\0';
+			b[j] = '\0';
+
+			/* Compare strings in both directions */
+			diff1 = strverscmp(a, b);
+			diff2 = strverscmp(b, a);
+
+			/* Must give identical result in both directions */
+			assert((diff1 < 0 && diff2 > 0)
+				|| (diff1 == 0 && diff2 == 0)
+				|| (diff1 > 0 && diff2 < 0));
+		}
+	}
+
+	printf("OK\n");
+	return EXIT_SUCCESS;
+}
diff --git a/dirent/tests/t-telldir.c b/dirent/tests/t-telldir.c
new file mode 100644
index 0000000..e2a1763
--- /dev/null
+++ b/dirent/tests/t-telldir.c
@@ -0,0 +1,165 @@
+/*
+ * A test program to make sure that dirent works correctly.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+
+/* Silence warning about strcmp being insecure (MS Visual Studio) */
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+static void test_telldir(void);
+
+int
+main(int argc, char *argv[])
+{
+	(void) argc;
+	(void) argv;
+
+	test_telldir();
+
+	return EXIT_SUCCESS;
+}
+
+static void
+test_telldir(void)
+{
+	DIR *dir = opendir("tests/4");
+	if (dir == NULL) {
+		fprintf(stderr, "Directory tests/4 not found\n");
+		abort();
+	}
+
+	/* Get position of first file */
+	long pos1 = telldir(dir);
+	printf("pos1: %lx\n", (long) pos1);
+	assert(pos1 >= 0);
+
+	/* Read first file */
+	struct dirent *ent = readdir(dir);
+	assert(ent != NULL);
+	struct dirent ent1 = *ent;
+	printf("ent1: %s %lx\n", ent->d_name, (long) ent->d_off);
+
+	/* Seek back to the first position */
+	seekdir(dir, pos1);
+
+	/* Re-read the first entry */
+	ent = readdir(dir);
+	assert(ent != NULL);
+	assert(strcmp(ent->d_name, ent1.d_name) == 0);
+
+	/* Get position to second file */
+	long pos2 = telldir(dir);
+	printf("pos2: %lx\n", (long) pos2);
+	assert(pos2 >= 0);
+
+	/* Read second file */
+	ent = readdir(dir);
+	assert(ent != NULL);
+	struct dirent ent2 = *ent;
+	printf("ent2: %s %lx\n", ent->d_name, (long) ent->d_off);
+
+	/* Get position to third file */
+	long pos3 = telldir(dir);
+	printf("pos3: %lx\n", (long) pos3);
+	assert(pos3 >= 0);
+
+	/* Read third file */
+	ent = readdir(dir);
+	assert(ent != NULL);
+	struct dirent ent3 = *ent;
+	printf("ent3: %s %lx\n", ent->d_name, (long) ent->d_off);
+
+	/* Get position to fourth file */
+	long pos4 = telldir(dir);
+	printf("pos4: %lx\n", (long) pos4);
+	assert(pos4 >= 0);
+
+	/* Read fourth file */
+	ent = readdir(dir);
+	assert(ent != NULL);
+	struct dirent ent4 = *ent;
+	printf("ent4: %s %lx\n", ent->d_name, (long) ent->d_off);
+
+	/* Get position to fifth file */
+	long pos5 = telldir(dir);
+	printf("pos5: %lx\n", (long) pos5);
+	assert(pos5 >= 0);
+
+	/* Read fifth file */
+	ent = readdir(dir);
+	assert(ent != NULL);
+	struct dirent ent5 = *ent;
+	printf("ent5: %s %lx\n", ent->d_name, (long) ent->d_off);
+
+	/* Read position at the end of directory stream */
+	long posx = telldir(dir);
+	assert(posx >= 0);
+	printf("posx: %lx\n", (long) posx);
+
+	/* End of directory stream has been reached */
+	ent = readdir(dir);
+	assert(ent == NULL);
+
+	/* Seek back to position just before the end of stream */
+	seekdir(dir, posx);
+
+	/* Function telldir returns the same position when asked again */
+	assert(telldir(dir) == posx);
+	assert(telldir(dir) == posx);
+	assert(telldir(dir) == posx);
+
+	/* Read end of stream again */
+	ent = readdir(dir);
+	assert(ent == NULL);
+
+	/* Seek back to fifth file and read it again */
+	seekdir(dir, pos5);
+	assert(telldir(dir) == pos5);
+	ent = readdir(dir);
+	assert(ent != NULL);
+	assert(strcmp(ent->d_name, ent5.d_name) == 0);
+
+	/* Seek back to second file and read it again */
+	seekdir(dir, pos2);
+	assert(telldir(dir) == pos2);
+	ent = readdir(dir);
+	assert(ent != NULL);
+	assert(strcmp(ent->d_name, ent2.d_name) == 0);
+
+	/* Continue reading from the third file without a seek in between */
+	assert(telldir(dir) == pos3);
+	ent = readdir(dir);
+	assert(ent != NULL);
+	assert(strcmp(ent->d_name, ent3.d_name) == 0);
+
+	/* Read fourth position again */
+	assert(telldir(dir) == pos4);
+	ent = readdir(dir);
+	assert(ent != NULL);
+	assert(strcmp(ent->d_name, ent4.d_name) == 0);
+
+	/* Read fifth position again */
+	assert(telldir(dir) == pos5);
+	ent = readdir(dir);
+	assert(ent != NULL);
+	assert(strcmp(ent->d_name, ent5.d_name) == 0);
+
+	/* Read end of stream again */
+	assert(telldir(dir) == posx);
+	ent = readdir(dir);
+	assert(ent == NULL);
+}
+
diff --git a/dirent/tests/t-unicode.c b/dirent/tests/t-unicode.c
new file mode 100644
index 0000000..8239120
--- /dev/null
+++ b/dirent/tests/t-unicode.c
@@ -0,0 +1,381 @@
+/*
+ * Test program to try unicode file names.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+
+/* Silence warning about fopen being insecure */
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <time.h>
+#include <locale.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+int
+main(int argc, char *argv[])
+{
+#ifdef WIN32
+	wchar_t wpath[MAX_PATH+1];
+	char path[MAX_PATH+1];
+	DWORD i, j, k, x;
+	BOOL ok;
+	HANDLE fh;
+	_WDIR *wdir;
+	struct _wdirent *wentry;
+	DIR *dir;
+	struct dirent *entry;
+	char buffer[100];
+	FILE *fp;
+	int counter = 0;
+
+	/* Initialize random number generator */
+	srand(((int) time(NULL)) * 257 + ((int) GetCurrentProcessId()));
+
+	/* Set current locale */
+	if (argc > 1) {
+		printf("Locale %s\n", argv[1]);
+		setlocale(LC_ALL, argv[1]);
+	} else {
+		setlocale(LC_ALL, "");
+	}
+
+	/****** CREATE FILE WITH UNICODE FILE NAME ******/
+
+	/* Get path to temporary directory (wide-character and ascii) */
+	i = GetTempPathW(MAX_PATH, wpath);
+	assert(i > 0);
+	j = GetTempPathA(MAX_PATH, path);
+	assert(j > 0);
+
+	/* Append random directory name */
+	for (k = 0; k < 10; k++) {
+		/* Generate random character */
+		char c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26];
+
+		/* Append character to paths */
+		assert(i < MAX_PATH  &&  j < MAX_PATH);
+		wpath[i++] = c;
+		path[j++] = c;
+	}
+
+	/* Terminate paths */
+	assert(i < MAX_PATH  &&  j < MAX_PATH);
+	wpath[i] = '\0';
+	path[j] = '\0';
+
+	/* Remember the end of directory name */
+	k = i;
+
+	/* Create directory using unicode */
+	ok = CreateDirectoryW(wpath, NULL);
+	if (!ok) {
+		DWORD e = GetLastError();
+		wprintf(L"Cannot create directory %ls (code %u)\n", wpath, e);
+		abort();
+	}
+
+	/* Overwrite zero terminator with path separator */
+	assert(i < MAX_PATH  &&  j < MAX_PATH);
+	wpath[i++] = '\\';
+
+	/* Append a few unicode characters */
+	assert(i < MAX_PATH);
+	wpath[i++] = 0x6d4b;
+	assert(i < MAX_PATH);
+	wpath[i++] = 0x8bd5;
+
+	/* Terminate string */
+	assert(i < MAX_PATH);
+	wpath[i] = '\0';
+
+	/* Create file with unicode */
+	fh = CreateFileW(
+		wpath,
+		/* Access */ GENERIC_READ | GENERIC_WRITE,
+		/* Share mode */ 0,
+		/* Security attributes */ NULL,
+		/* Creation disposition */ CREATE_NEW,
+		/* Attributes */ FILE_ATTRIBUTE_NORMAL,
+		/* Template files */ NULL
+		);
+	assert(fh != INVALID_HANDLE_VALUE);
+
+	/* Write some data to file */
+	ok = WriteFile(
+		/* File handle */ fh,
+		/* Pointer to data */ "hep\n",
+		/* Number of bytes to write */ 4,
+		/* Number of bytes written */ NULL,
+		/* Overlapped */ NULL
+		);
+	assert(ok);
+
+	/* Close file */
+	ok = CloseHandle(fh);
+	assert(ok);
+
+	/****** MAKE SURE THAT UNICODE FILE CAN BE READ BY _WREADDIR ******/
+
+	/* Zero terminate wide-character path and open directory stream */
+	wpath[k] = '\0';
+	wdir = _wopendir(wpath);
+	if (wdir == NULL) {
+		wprintf(L"Cannot open directory %ls\n", wpath);
+		abort();
+	}
+
+	/* Read through entries */
+	counter = 0;
+	while ((wentry = _wreaddir(wdir)) != NULL) {
+		/* Skip pseudo directories */
+		if (wcscmp(wentry->d_name, L".") == 0) {
+			continue;
+		}
+		if (wcscmp(wentry->d_name, L"..") == 0) {
+			continue;
+		}
+
+		/* Found a file */
+		counter++;
+		assert(wentry->d_type == DT_REG);
+
+		/* Append file name to path */
+		i = k;
+		assert(i < MAX_PATH);
+		wpath[i++] = '\\';
+		x = 0;
+		while (wentry->d_name[x] != '\0') {
+			assert(i < MAX_PATH);
+			wpath[i++] = wentry->d_name[x++];
+		}
+		assert(i < MAX_PATH);
+		wpath[i] = '\0';
+
+		/* Open file for read */
+		fh = CreateFileW(
+			wpath,
+			/* Access */ GENERIC_READ,
+			/* Share mode */ 0,
+			/* Security attributes */ NULL,
+			/* Creation disposition */ OPEN_EXISTING,
+			/* Attributes */ FILE_ATTRIBUTE_NORMAL,
+			/* Template files */ NULL
+			);
+		assert(fh != INVALID_HANDLE_VALUE);
+
+		/* Read data from file */
+		ok = ReadFile(
+			/* File handle */ fh,
+			/* Output buffer */ buffer,
+			/* Max number of bytes to read */ sizeof(buffer) - 1,
+			/* Number of bytes actually read */ &x,
+			/* Overlapped */ NULL
+		);
+		assert(ok);
+
+		/* Make sure that we got the file contents right */
+		assert(x == 4);
+		assert(buffer[0] == 'h');
+		assert(buffer[1] == 'e');
+		assert(buffer[2] == 'p');
+		assert(buffer[3] == '\n');
+
+		/* Close file */
+		ok = CloseHandle(fh);
+		assert(ok);
+	}
+	assert(counter == 1);
+
+	/* Close directory */
+	_wclosedir(wdir);
+
+	/****** MAKE SURE THAT UNICODE FILE NAME CAN BE READ BY READDIR *****/
+
+	/* Zero terminate ascii path and open directory stream */
+	k = j;
+	path[k] = '\0';
+	dir = opendir(path);
+	if (dir == NULL) {
+		fprintf(stderr, "Cannot open directory %s\n", path);
+		abort();
+	}
+
+	/* Read through entries */
+	counter = 0;
+	while ((entry = readdir(dir)) != NULL) {
+		/* Skip pseudo directories */
+		if (strcmp(entry->d_name, ".") == 0) {
+			continue;
+		}
+		if (strcmp(entry->d_name, "..") == 0) {
+			continue;
+		}
+
+		/* Found a file */
+		counter++;
+		assert(entry->d_type == DT_REG);
+
+		/* Append file name to path */
+		j = k;
+		assert(j < MAX_PATH);
+		path[j++] = '\\';
+		x = 0;
+		while (entry->d_name[x] != '\0') {
+			assert(j < MAX_PATH);
+			path[j++] = entry->d_name[x++];
+		}
+		assert(j < MAX_PATH);
+		path[j] = '\0';
+
+		/* Open file for read */
+		fp = fopen(path, "r");
+		if (!fp) {
+			fprintf(stderr, "Cannot open file %s\n", path);
+			abort();
+		}
+
+		/* Read data from file */
+		if (fgets(buffer, sizeof(buffer), fp) == NULL) {
+			fprintf(stderr, "Cannot read file %s\n", path);
+			abort();
+		}
+
+		/* Make sure that we got the file contents right */
+		assert(buffer[0] == 'h');
+		assert(buffer[1] == 'e');
+		assert(buffer[2] == 'p');
+		assert(buffer[3] == '\n');
+		assert(buffer[4] == '\0');
+
+		/* Close file */
+		fclose(fp);
+	}
+	assert(counter == 1);
+
+	/* Close directory */
+	closedir(dir);
+
+	/****** CREATE FILE WITH UTF-8 ******/
+
+	/* Append UTF-8 file name (åäö.txt) to path */
+	j = k;
+	path[j++] = '\\';
+	path[j++] = 0xc3;
+	path[j++] = 0xa5;
+	path[j++] = 0xc3;
+	path[j++] = 0xa4;
+	path[j++] = 0xc3;
+	path[j++] = 0xb6;
+	path[j++] = 0x2e;
+	path[j++] = 0x74;
+	path[j++] = 0x78;
+	path[j++] = 0x74;
+	assert(j < MAX_PATH);
+	path[j] = '\0';
+
+	/*
+	 * Create file.
+	 *
+	 * Be ware that the code below creates a different file depending on
+	 * the current locale!  For example, if the current locale is
+	 * english_us.65001, then the file name will be "åäö.txt" (7
+	 * characters).  However, if the current locale is english_us.1252,
+	 * then the file name will be "ÃċÃĊö.txt" (10 characters).
+	 */
+	printf("Creating %s\n", path);
+	fp = fopen(path, "w");
+	if (!fp) {
+		fprintf(stderr, "Cannot open file %s\n", path);
+		abort();
+	}
+	fputs("hep\n", fp);
+	fclose(fp);
+
+	/* Open directory again */
+	path[k] = '\0';
+	dir = opendir(path);
+	if (dir == NULL) {
+		fprintf(stderr, "Cannot open directory %s\n", path);
+		abort();
+	}
+
+	/* Read through entries */
+	counter = 0;
+	while ((entry = readdir(dir)) != NULL) {
+		/* Skip pseudo directories */
+		if (strcmp(entry->d_name, ".") == 0) {
+			continue;
+		}
+		if (strcmp(entry->d_name, "..") == 0) {
+			continue;
+		}
+
+		/* Found a file */
+		counter++;
+		assert(entry->d_type == DT_REG);
+
+		/* Append file name to path */
+		j = k;
+		assert(j < MAX_PATH);
+		path[j++] = '\\';
+		x = 0;
+		while (entry->d_name[x] != '\0') {
+			assert(j < MAX_PATH);
+			path[j++] = entry->d_name[x++];
+		}
+		assert(j < MAX_PATH);
+		path[j] = '\0';
+
+		/* Print file name for debugging */
+		printf("Opening \"%s\" hex ", path + k + 1);
+		x = 0;
+		while (entry->d_name[x] != '\0') {
+			printf("0x%02x ",
+				(unsigned) (entry->d_name[x++] & 0xff));
+		}
+		printf("\n");
+
+		/* Open file for read */
+		fp = fopen(path, "r");
+		if (!fp) {
+			fprintf(stderr, "Cannot open file %s\n", path);
+			abort();
+		}
+
+		/* Read data from file */
+		if (fgets(buffer, sizeof(buffer), fp) == NULL) {
+			fprintf(stderr, "Cannot read file %s\n", path);
+			abort();
+		}
+
+		/* Make sure that we got the file contents right */
+		assert(buffer[0] == 'h');
+		assert(buffer[1] == 'e');
+		assert(buffer[2] == 'p');
+		assert(buffer[3] == '\n');
+		assert(buffer[4] == '\0');
+
+		/* Close file */
+		fclose(fp);
+	}
+	assert(counter == 2);
+
+	/* Close directory */
+	closedir(dir);
+#else
+	/* Linux */
+	(void) argc;
+	(void) argv;
+#endif
+	return EXIT_SUCCESS;
+}
diff --git a/dirent/tests/t-utf8.c b/dirent/tests/t-utf8.c
new file mode 100644
index 0000000..fdc31fa
--- /dev/null
+++ b/dirent/tests/t-utf8.c
@@ -0,0 +1,238 @@
+/*
+ * Test program to try UTF-8 file names.
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+
+/* Silence warning about fopen being insecure */
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <time.h>
+#include <locale.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+int
+main(int argc, char *argv[])
+{
+#ifdef WIN32
+	/*
+	 * Select UTF-8 locale.  This will change the way how C runtime
+	 * functions such as fopen() and mkdir() handle character strings.
+	 * For more information, please see:
+	 * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-160#utf-8-support
+	 */
+	setlocale(LC_ALL, "LC_CTYPE=.utf8");
+
+	/* Initialize random number generator */
+	srand(((int) time(NULL)) * 257 + ((int) GetCurrentProcessId()));
+
+	/* Get path to temporary directory */
+	wchar_t wpath[MAX_PATH+1];
+	DWORD i = GetTempPathW(MAX_PATH, wpath);
+	assert(i > 0);
+
+	/* Ensure that path name ends in directory separator */
+	assert(wpath[i - 1] == '\\');
+
+	/* Append random prefix */
+	DWORD k;
+	for (k = 0; k < 8; k++) {
+		/* Generate random character */
+		char c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26];
+
+		/* Append character to path */
+		assert(i < MAX_PATH);
+		wpath[i++] = c;
+	}
+
+	/* Append a wide character to the path name */
+	wpath[i++] = 0x00c4;
+
+	/* Terminate the path name */
+	assert(i < MAX_PATH);
+	wpath[i] = '\0';
+
+	/* Create directory with unicode name */
+	BOOL ok = CreateDirectoryW(wpath, NULL);
+	if (!ok) {
+		DWORD e = GetLastError();
+		wprintf(L"Cannot create directory %ls (code %u)\n", wpath, e);
+		abort();
+	}
+
+	/* Overwrite zero terminator with path separator */
+	assert(i < MAX_PATH);
+	wpath[i++] = '\\';
+
+	/* Append a few unicode characters */
+	assert(i < MAX_PATH);
+	wpath[i++] = 0x00f6;
+	assert(i < MAX_PATH);
+	wpath[i++] = 0x00e4;
+
+	/* Terminate string */
+	assert(i < MAX_PATH);
+	wpath[i] = '\0';
+
+	/* Create file with unicode name */
+	HANDLE fh = CreateFileW(
+		wpath,
+		/* Access */ GENERIC_READ | GENERIC_WRITE,
+		/* Share mode */ 0,
+		/* Security attributes */ NULL,
+		/* Creation disposition */ CREATE_NEW,
+		/* Attributes */ FILE_ATTRIBUTE_NORMAL,
+		/* Template files */ NULL
+		);
+	assert(fh != INVALID_HANDLE_VALUE);
+
+	/* Write some data to file */
+	ok = WriteFile(
+		/* File handle */ fh,
+		/* Pointer to data */ "hep\n",
+		/* Number of bytes to write */ 4,
+		/* Number of bytes written */ NULL,
+		/* Overlapped */ NULL
+		);
+	assert(ok);
+
+	/* Close file */
+	ok = CloseHandle(fh);
+	assert(ok);
+
+	/* Convert file name to UTF-8 */
+	char path[MAX_PATH+1];
+	int n = WideCharToMultiByte(
+		/* Code page to use in conversion */ CP_UTF8,
+		/* Flags */ 0,
+		/* Pointer to unicode string */ wpath,
+		/* Length of unicode string in characters */ i,
+		/* Pointer to output buffer */ path,
+		/* Size of output buffer in bytes */ MAX_PATH,
+		/* Pointer to default character */ NULL,
+		/* Pointer to boolean variable */ NULL
+	);
+	assert(n > 0);
+
+	/* Zero-terminate path */
+	path[(size_t) n] = '\0';
+
+	/* Make sure that fopen() can open the file with UTF-8 file name */
+	FILE *fp = fopen(path, "r");
+	if (!fp) {
+		fprintf(stderr, "Cannot open file %s\n", path);
+		abort();
+	}
+
+	/* Read data from file */
+	char buffer[100];
+	if (fgets(buffer, sizeof(buffer), fp) == NULL) {
+		fprintf(stderr, "Cannot read file %s\n", path);
+		abort();
+	}
+
+	/* Make sure that we got the file contents right */
+	assert(buffer[0] == 'h');
+	assert(buffer[1] == 'e');
+	assert(buffer[2] == 'p');
+	assert(buffer[3] == '\n');
+	assert(buffer[4] == '\0');
+
+	/* Close file */
+	fclose(fp);
+
+	/* Truncate path name to the last directory separator */
+	i = 0;
+	k = 0;
+	while (path[k] != '\0') {
+		if (path[k] == '\\' || path[k] == '/') {
+			i = k;
+		}
+		k++;
+	}
+	path[i] = '\0';
+
+	/* Ensure that opendir() can open the directory with UTF-8 name */
+	DIR *dir = opendir(path);
+	if (dir == NULL) {
+		fprintf(stderr, "Cannot open directory %s\n", path);
+		abort();
+	}
+
+	/* Read entries */
+	int counter = 0;
+	struct dirent *entry;
+	while ((entry = readdir(dir)) != NULL) {
+		/* Skip pseudo directories */
+		if (strcmp(entry->d_name, ".") == 0) {
+			continue;
+		}
+		if (strcmp(entry->d_name, "..") == 0) {
+			continue;
+		}
+
+		/* Found a file */
+		counter++;
+		assert(entry->d_type == DT_REG);
+
+		/* Append file name to path */
+		k = i;
+		assert(i < MAX_PATH);
+		path[i++] = '\\';
+		DWORD x = 0;
+		while (entry->d_name[x] != '\0') {
+			assert(i < MAX_PATH);
+			path[i++] = entry->d_name[x++];
+		}
+		assert(i < MAX_PATH);
+		path[i] = '\0';
+
+		/* Reset buffer */
+		for (x = 0; x < sizeof(buffer); x++) {
+			buffer[x] = '\0';
+		}
+
+		/* Open file for read */
+		fp = fopen(path, "r");
+		if (!fp) {
+			fprintf(stderr, "Cannot open file %s\n", path);
+			abort();
+		}
+
+		/* Read data from file */
+		if (fgets(buffer, sizeof(buffer), fp) == NULL) {
+			fprintf(stderr, "Cannot read file %s\n", path);
+			abort();
+		}
+
+		/* Make sure that we got the file contents right */
+		assert(buffer[0] == 'h');
+		assert(buffer[1] == 'e');
+		assert(buffer[2] == 'p');
+		assert(buffer[3] == '\n');
+		assert(buffer[4] == '\0');
+
+		/* Close file */
+		fclose(fp);
+	}
+	assert(counter == 1);
+
+	/* Close directory */
+	closedir(dir);
+#else
+	/* Linux */
+	(void) argc;
+	(void) argv;
+#endif
+	return EXIT_SUCCESS;
+}