about summary refs log tree commit diff stats
path: root/dirent/tests
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/tests
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/tests')
-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
26 files changed, 2081 insertions, 0 deletions
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;
+}