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