]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
Added EXT_WRATH
authorReki <spiper212@gmail.com>
Sat, 22 May 2021 20:36:35 +0000 (16:36 -0400)
committerReki <spiper212@gmail.com>
Sat, 22 May 2021 20:36:35 +0000 (16:36 -0400)
clvm_cmds.c
fs.c
fs.h
mvm_cmds.c
prvm_cmds.c
prvm_cmds.h
svvm_cmds.c
vsdirent.h [new file with mode: 0644]

index 8cf1791aa17ed96d431304f178bf73a7cbe4b3ef..218a63149e673a0ce216d1932c80c1c87134e839 100644 (file)
@@ -5589,12 +5589,44 @@ NULL,                                                   // #634
 NULL,                                                  // #635
 NULL,                                                  // #636
 NULL,                                                  // #637
-VM_CL_RotateMoves,                                     // #638
-VM_digest_hex,                                         // #639
-VM_CL_V_CalcRefdef,                                    // #640 void(entity e) V_CalcRefdef (DP_CSQC_V_CALCREFDEF)
+VM_CL_RotateMoves,                             // #638
+VM_digest_hex,                                 // #639
+VM_CL_V_CalcRefdef,                            // #640 void(entity e) V_CalcRefdef (DP_CSQC_V_CALCREFDEF)
 NULL,                                                  // #641
-VM_coverage,                                           // #642
-NULL
+VM_coverage,                                   // #642
+NULL,                                                  // #643
+NULL,                                                  // #644
+NULL,                                                  // #645
+NULL,                                                  // #646
+NULL,                                                  // #647
+NULL,                                                  // #648
+NULL,                                                  // #649
+// WRATH range (#650-#???)
+VM_fcopy,                                              // #650 float(string fnfrom, string fnto) fcopy (EXT_WRATH)
+VM_frename,                                            // #651 float (string fnold, string fnnew) frename (EXT_WRATH)
+VM_fremove,                                            // #652 float (string fname) fremove (EXT_WRATH)
+VM_fexists,                                            // #653 float (string fname) fexists (EXT_WRATH)
+VM_rmtree,                                             // #654 float (string path) rmtree (EXT_WRATH)
+NULL,                                                  // #656
+NULL,                                                  // #657
+NULL,                                                  // #658
+NULL,                                                  // #659
+NULL,                                                  // #660
+NULL,                                                  // #661
+NULL,                                                  // #662
+NULL,                                                  // #663
+NULL,                                                  // #664
+NULL,                                                  // #665
+NULL,                                                  // #666
+NULL,                                                  // #667
+NULL,                                                  // #668
+NULL,                                                  // #669
+NULL,                                                  // #670
+NULL,                                                  // #671
+NULL,                                                  // #672
+NULL,                                                  // #673
+NULL,                                                  // #674
+NULL                                                   // #675
 };
 
 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
diff --git a/fs.c b/fs.c
index 87c5df4accacaa479f96ab0e2ebf5e4ca565fa84..cac01bedc65f2419f3152e46f2e087d4550b8a41 100644 (file)
--- a/fs.c
+++ b/fs.c
 #include <limits.h>
 #include <fcntl.h>
 
+#if defined(_MSC_VER)
+// visual studio does not have a dirent.h, so we use
+// https://github.com/tronkko/dirent
+# include "vsdirent.h"
+#else
+# include <dirent.h>
+#endif
+
 #ifdef WIN32
 # include <direct.h>
 # include <io.h>
@@ -944,6 +952,59 @@ static void FS_mkdir (const char *path)
        }
 }
 
+int FS_rmtree(const char *dir)
+{
+       struct dirent *ep;
+       struct stat st;
+       DIR *dp;
+       char vabuf[1024] = { 0 };
+       int ret = 0;
+
+       dp = opendir(dir);
+       if (!dp)
+       {
+               Con_DPrintf("FS_rmtree(): can't open dir `%s`: %s\n", dir, strerror(errno));
+               return -1;
+       }
+
+       while ((ep = readdir(dp)))
+       {
+               if (!strcmp(".", ep->d_name) || !strcmp("..", ep->d_name))
+                       continue; // NB: do these even appear in readdir() output?
+
+               if (dpsnprintf(vabuf, sizeof(vabuf), "%s/%s", dir, ep->d_name) >= (int)sizeof(vabuf))
+               {
+                       // this is some wacky shit, better abort lest we fuck some poor sod's files up
+                       Con_DPrintf("FS_rmtree(): path `%s` is longer than the path buffer, aborting\n", vabuf);
+                       ret = -2;
+                       break;
+               }
+
+               if (stat(vabuf, &st) < 0)
+               {
+                       Con_DPrintf("FS_rmtree(): can't stat `%s`: %s\n", vabuf, strerror(errno));
+                       ret = -3;
+                       break;
+               }
+
+               ret = (S_ISDIR(st.st_mode)) ? FS_rmtree(vabuf) : remove(vabuf);
+               if (ret)
+               {
+                       Con_DPrintf("FS_rmtree(): can't remove `%s`: %s\n", vabuf, strerror(errno));
+                       break;
+               }
+       }
+
+       closedir(dp);
+
+       if (!ret)
+       {
+               ret = rmdir(dir);
+               if (ret) Con_DPrintf("FS_rmtree(): can't remove `%s`: %s\n", dir, strerror(errno));
+       }
+
+       return ret;
+}
 
 /*
 ============
diff --git a/fs.h b/fs.h
index 5977f8c6087a2e968bf6c5db4ef9c2f8db01079e..a10ca1178eb84b49d8c4ab52e2fa0c5f560740fa 100644 (file)
--- a/fs.h
+++ b/fs.h
@@ -85,6 +85,7 @@ void FS_Purge (qfile_t* file);
 const char *FS_FileWithoutPath (const char *in);
 const char *FS_FileExtension (const char *in);
 int FS_CheckNastyPath (const char *path, qbool isgamedir);
+int FS_rmtree (const char *path);
 
 extern const char *const fs_checkgamedir_missing; // "(missing)"
 const char *FS_CheckGameDir(const char *gamedir); // returns NULL if nasty, fs_checkgamedir_missing (exact pointer) if missing
index 3168bbf445ac3de706a2a149b9d7d4a0c9287bd9..f227587be4010a7b2efbbd280801a1641bb455d0 100644 (file)
@@ -50,6 +50,7 @@ const char *vm_m_extensions[] = {
 "DP_QC_WHICHPACK",
 "FTE_STRINGS",
 "DP_QC_FS_SEARCH_PACKFILE",
+"EXT_WRATH",
 NULL
 };
 
@@ -1721,7 +1722,38 @@ NULL,                                                    // #640
 VM_M_crypto_getmyidstatus,                             // #641 float(float i) crypto_getmyidstatus
 VM_coverage,                                           // #642
 VM_M_crypto_getidstatus,                               // #643 float(string addr) crypto_getidstatus
-NULL
+NULL,                                                  // #644
+NULL,                                                  // #645
+NULL,                                                  // #646
+NULL,                                                  // #647
+NULL,                                                  // #648
+NULL,                                                  // #649
+// WRATH range (#650-#???)
+VM_fcopy,                                              // #650 float(string fnfrom, string fnto) fcopy (EXT_WRATH)
+VM_frename,                                            // #651 float (string fnold, string fnnew) frename (EXT_WRATH)
+VM_fremove,                                            // #652 float (string fname) fremove (EXT_WRATH)
+VM_fexists,                                            // #653 float (string fname) fexists (EXT_WRATH)
+VM_rmtree,                                             // #654 float (string path) rmtree (EXT_WRATH)
+NULL,                                                  // #656
+NULL,                                                  // #657
+NULL,                                                  // #658
+NULL,                                                  // #659
+NULL,                                                  // #660
+NULL,                                                  // #661
+NULL,                                                  // #662
+NULL,                                                  // #663
+NULL,                                                  // #664
+NULL,                                                  // #665
+NULL,                                                  // #666
+NULL,                                                  // #667
+NULL,                                                  // #668
+NULL,                                                  // #669
+NULL,                                                  // #670
+NULL,                                                  // #671
+NULL,                                                  // #672
+NULL,                                                  // #673
+NULL,                                                  // #674
+NULL                                                   // #675
 };
 
 const int vm_m_numbuiltins = sizeof(vm_m_builtins) / sizeof(prvm_builtin_t);
index 67ccb3f31151dfc810137fcd1c4f71253da8d83d..6a3e9ba1e23665845a18fb8d102ff858a1a6524f 100644 (file)
@@ -1988,6 +1988,239 @@ void VM_fputs(prvm_prog_t *prog)
                Con_DPrintf("fputs: %s: %s\n", prog->name, string);
 }
 
+// EXT_WRATH file stuff
+
+/*
+=========
+VM_fcopy
+float  fcopy(string fnfrom, string fnto)
+=========
+*/
+// float(string fnfrom, string fnto) fcopy = #650;
+// copies quake/gamedir/data/$fnfrom to quake/gamedir/data/$fnto
+// returns 0 on success, <0 on failure
+void VM_fcopy(prvm_prog_t *prog)
+{
+       char vabuf[1024];
+       char fbuf[VM_STRINGTEMP_LENGTH];
+       const char *fname1, *fname2;
+       qfile_t *f1, *f2;
+       fs_offset_t rx = 0, wx = 0;
+
+       VM_SAFEPARMCOUNT(2, VM_fcopy);
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+
+       fname1 = PRVM_G_STRING(OFS_PARM0);
+       fname2 = PRVM_G_STRING(OFS_PARM1);
+
+       f1 = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", fname1), false);
+       if (f1 == NULL)
+               f1 = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", fname1), false);
+       if (f1 == NULL)
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -1;
+               VM_Warning(prog, "VM_fcopy: %s could not open file %s for reading\n", prog->name, fname1);
+               return;
+       }
+
+       f2 = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "data/%s", fname2), "wb", false);
+       if (f2 == NULL)
+       {
+               FS_Close(f1);
+               PRVM_G_FLOAT(OFS_RETURN) = -2;
+               VM_Warning(prog, "VM_fcopy: %s could not open file %s for writing\n", prog->name, fname2);
+               return;
+       }
+
+       while ((rx = FS_Read(f1, fbuf, sizeof(fbuf)-1)) > 0) {
+               wx = FS_Write(f2, fbuf, rx);
+               if (wx != rx)
+               {
+                       FS_Close(f1);
+                       FS_Close(f2);
+                       PRVM_G_FLOAT(OFS_RETURN) = -3;
+                       VM_Warning(prog, "VM_fcopy: %s read %I64d from %s but wrote %I64d to %s\n", prog->name, rx, fname1, wx, fname2);
+                       return;
+               }
+       }
+
+       if (wx == 0)
+               VM_Warning(prog, "VM_fcopy: %s wrote 0 bytes to %s\n", prog->name, fname2);
+       FS_Close(f1);
+       FS_Close(f2);
+}
+
+/*
+=========
+VM_frename
+float  frename(string fnold, string fnnew)
+=========
+*/
+// float(string fnold, string fnnew) frename = #651;
+// renames quake/gamedir/data/$fnold to quake/gamedir/data/$fnnew
+// returns 0 on success, <0 on failure
+void VM_frename(prvm_prog_t *prog)
+{
+       char vabuf1[1024], vabuf2[1024];
+       const char *fname1, *fname2;
+       int err = 0;
+
+       VM_SAFEPARMCOUNT(2, VM_frename);
+       fname1 = PRVM_G_STRING(OFS_PARM0);
+       fname2 = PRVM_G_STRING(OFS_PARM1);
+
+       if (FS_CheckNastyPath(fname1, true))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -1;
+               VM_Warning(prog, "VM_frename: %s rejects nasty path %s\n", prog->name, vabuf1);
+               return;
+       }
+
+       if (FS_CheckNastyPath(fname2, true))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -2;
+               VM_Warning(prog, "VM_frename: %s rejects nasty path %s\n", prog->name, vabuf2);
+               return;
+       }
+
+       dpsnprintf(vabuf1, sizeof(vabuf1), "%s/data/%s", fs_gamedir, fname1);
+       dpsnprintf(vabuf2, sizeof(vabuf2), "%s/data/%s", fs_gamedir, fname2);
+
+       if (!FS_SysFileExists(vabuf1))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -3;
+               VM_Warning(prog, "VM_frename: %s can't find %s\n", prog->name, vabuf1);
+               return;
+       }
+
+       if (FS_SysFileExists(vabuf2))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -4;
+               VM_Warning(prog, "VM_frename: %s file %s already exists\n", prog->name, vabuf2);
+               return;
+       }
+
+       if ((err = rename(vabuf1, vabuf2)) != 0)
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -5;
+               VM_Warning(prog, "VM_frename: %s could not rename %s to %s\n", prog->name, vabuf1, vabuf2);
+               return;
+       }
+
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+}
+
+/*
+=========
+VM_fremove
+float  fremove(string fname)
+=========
+*/
+// float(string fname) fremove = #652;
+// removes quake/gamedir/data/$fname
+// returns 0 on success, <0 on failure
+void VM_fremove(prvm_prog_t *prog)
+{
+       const char *fname;
+       char vabuf[1024] = { 0 };
+       int err = 0;
+
+       VM_SAFEPARMCOUNT(1, VM_fremove);
+       fname = PRVM_G_STRING(OFS_PARM0);
+
+       if (FS_CheckNastyPath(fname, true))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -1;
+               VM_Warning(prog, "VM_fremove: %s rejects nasty path %s\n", prog->name, vabuf);
+               return;
+       }
+
+       if (dpsnprintf(vabuf, sizeof(vabuf), "%s/data/%s", fs_gamedir, fname) >= (int)sizeof(vabuf))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -2;
+               VM_Warning(prog, "VM_fremove: %s rejects path %s as too long\n", prog->name, vabuf);
+               return;
+       }
+
+       if (!FS_SysFileExists(vabuf))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -3;
+               VM_Warning(prog, "VM_fremove: %s can't find %s\n", prog->name, vabuf);
+               return;
+       }
+
+       if ((err = remove(vabuf)) != 0)
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -4;
+               VM_Warning(prog, "VM_fremove: %s could not remove %s\n", prog->name, vabuf);
+               return;
+       }
+
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+}
+
+/*
+=========
+VM_fexists
+float  fexists(string filename)
+=========
+*/
+// float(string filename) fexists = #653;
+// checks if quake/gamedir/$fname exists without opening it
+// returns not 0 if it exists, 0 if it does not
+void VM_fexists(prvm_prog_t *prog)
+{
+       const char *fname;
+       char vabuf[1024] = { 0 };
+
+       VM_SAFEPARMCOUNT(1, VM_fexists);
+       fname = PRVM_G_STRING(OFS_PARM0);
+
+       if (FS_CheckNastyPath(fname, true))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = 0;
+               VM_Warning(prog, "VM_fexists: %s rejects nasty path %s\n", prog->name, vabuf);
+               return;
+       }
+
+       dpsnprintf(vabuf, sizeof(vabuf), "%s/%s", fs_gamedir, fname);
+       PRVM_G_FLOAT(OFS_RETURN) = FS_SysFileExists(vabuf);
+}
+
+/*
+=========
+VM_rmtree
+float  rmtree(string path)
+=========
+*/
+// float(string path) rmtree = #654;
+// removes the directory "data/$path" and all of its contents
+// returns 0 on success, !=0 on failure
+void VM_rmtree(prvm_prog_t *prog)
+{
+       const char *fname;
+       char vabuf[1024] = { 0 };
+
+       VM_SAFEPARMCOUNT(1, VM_rmtree);
+       fname = PRVM_G_STRING(OFS_PARM0);
+
+       if (FS_CheckNastyPath(fname, true))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -1;
+               VM_Warning(prog, "VM_rmtree: %s rejects nasty path %s\n", prog->name, vabuf);
+               return;
+       }
+
+       if (dpsnprintf(vabuf, sizeof(vabuf), "%s/data/%s", fs_gamedir, fname) >= (int)sizeof(vabuf))
+       {
+               PRVM_G_FLOAT(OFS_RETURN) = -2;
+               VM_Warning(prog, "VM_fremove: %s rejects path %s as too long\n", prog->name, vabuf);
+               return;
+       }
+
+       PRVM_G_FLOAT(OFS_RETURN) = FS_rmtree(vabuf);
+}
+
 /*
 =========
 VM_writetofile
index c3f182c2bb5c870cfc7395c61ac807d13d174e05..476780015db8b57cfc756802c3084cda3d4a9999 100644 (file)
@@ -486,4 +486,11 @@ void VM_physics_addtorque(prvm_prog_t *prog);
 
 void VM_coverage(prvm_prog_t *prog);
 
+// EXT_WRATH
+void VM_fcopy(prvm_prog_t *prog);
+void VM_frename(prvm_prog_t *prog);
+void VM_fremove(prvm_prog_t *prog);
+void VM_fexists(prvm_prog_t *prog);
+void VM_rmtree(prvm_prog_t *prog);
+
 #endif
index 5fabf63a8e350a7db5bcdccdc5af5611b9cc0c76..5bf7175fd53b2603f1c5e42272fd1d0694045015 100644 (file)
@@ -229,6 +229,7 @@ const char *vm_sv_extensions[] = {
 "ZQ_PAUSE",
 "DP_RM_CLIPGROUP",
 "DP_QC_FS_SEARCH_PACKFILE",
+"EXT_WRATH",
 NULL
 //"EXT_CSQC" // not ready yet
 };
@@ -3864,6 +3865,38 @@ NULL,                                                    // #640
 NULL,                                                  // #641
 VM_coverage,                                           // #642
 NULL,                                                  // #643
+NULL,                                                  // #644
+NULL,                                                  // #645
+NULL,                                                  // #646
+NULL,                                                  // #647
+NULL,                                                  // #648
+NULL,                                                  // #649
+// WRATH range (#650-#???)
+VM_fcopy,                                              // #650 float(string fnfrom, string fnto) fcopy (EXT_WRATH)
+VM_frename,                                            // #651 float (string fnold, string fnnew) frename (EXT_WRATH)
+VM_fremove,                                            // #652 float (string fname) fremove (EXT_WRATH)
+VM_fexists,                                            // #653 float (string fname) fexists (EXT_WRATH)
+VM_rmtree,                                             // #654 float (string path) rmtree (EXT_WRATH)
+NULL,                                                  // #656
+NULL,                                                  // #657
+NULL,                                                  // #658
+NULL,                                                  // #659
+NULL,                                                  // #660
+NULL,                                                  // #661
+NULL,                                                  // #662
+NULL,                                                  // #663
+NULL,                                                  // #664
+NULL,                                                  // #665
+NULL,                                                  // #666
+NULL,                                                  // #667
+NULL,                                                  // #668
+NULL,                                                  // #669
+NULL,                                                  // #670
+NULL,                                                  // #671
+NULL,                                                  // #672
+NULL,                                                  // #673
+NULL,                                                  // #674
+NULL                                                   // #675
 };
 
 const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);
diff --git a/vsdirent.h b/vsdirent.h
new file mode 100644 (file)
index 0000000..5f87481
--- /dev/null
@@ -0,0 +1,1159 @@
+/*
+ * Dirent interface for Microsoft Visual Studio
+ * Version 1.23.1
+ *
+ * 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
+ */
+#ifndef DIRENT_H
+#define DIRENT_H
+
+/*
+ * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
+ * Windows Sockets 2.0.
+ */
+#ifndef WIN32_LEAN_AND_MEAN
+#   define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+/* Indicates that d_type field is available in dirent structure */
+#define _DIRENT_HAVE_D_TYPE
+
+/* Indicates that d_namlen field is available in dirent structure */
+#define _DIRENT_HAVE_D_NAMLEN
+
+/* Entries missing from MSVC 6.0 */
+#if !defined(FILE_ATTRIBUTE_DEVICE)
+#   define FILE_ATTRIBUTE_DEVICE 0x40
+#endif
+
+/* File type and permission flags for stat(), general mask */
+#if !defined(S_IFMT)
+#   define S_IFMT _S_IFMT
+#endif
+
+/* Directory bit */
+#if !defined(S_IFDIR)
+#   define S_IFDIR _S_IFDIR
+#endif
+
+/* Character device bit */
+#if !defined(S_IFCHR)
+#   define S_IFCHR _S_IFCHR
+#endif
+
+/* Pipe bit */
+#if !defined(S_IFFIFO)
+#   define S_IFFIFO _S_IFFIFO
+#endif
+
+/* Regular file bit */
+#if !defined(S_IFREG)
+#   define S_IFREG _S_IFREG
+#endif
+
+/* Read permission */
+#if !defined(S_IREAD)
+#   define S_IREAD _S_IREAD
+#endif
+
+/* Write permission */
+#if !defined(S_IWRITE)
+#   define S_IWRITE _S_IWRITE
+#endif
+
+/* Execute permission */
+#if !defined(S_IEXEC)
+#   define S_IEXEC _S_IEXEC
+#endif
+
+/* Pipe */
+#if !defined(S_IFIFO)
+#   define S_IFIFO _S_IFIFO
+#endif
+
+/* Block device */
+#if !defined(S_IFBLK)
+#   define S_IFBLK 0
+#endif
+
+/* Link */
+#if !defined(S_IFLNK)
+#   define S_IFLNK 0
+#endif
+
+/* Socket */
+#if !defined(S_IFSOCK)
+#   define S_IFSOCK 0
+#endif
+
+/* Read user permission */
+#if !defined(S_IRUSR)
+#   define S_IRUSR S_IREAD
+#endif
+
+/* Write user permission */
+#if !defined(S_IWUSR)
+#   define S_IWUSR S_IWRITE
+#endif
+
+/* Execute user permission */
+#if !defined(S_IXUSR)
+#   define S_IXUSR 0
+#endif
+
+/* Read group permission */
+#if !defined(S_IRGRP)
+#   define S_IRGRP 0
+#endif
+
+/* Write group permission */
+#if !defined(S_IWGRP)
+#   define S_IWGRP 0
+#endif
+
+/* Execute group permission */
+#if !defined(S_IXGRP)
+#   define S_IXGRP 0
+#endif
+
+/* Read others permission */
+#if !defined(S_IROTH)
+#   define S_IROTH 0
+#endif
+
+/* Write others permission */
+#if !defined(S_IWOTH)
+#   define S_IWOTH 0
+#endif
+
+/* Execute others permission */
+#if !defined(S_IXOTH)
+#   define S_IXOTH 0
+#endif
+
+/* Maximum length of file name */
+#if !defined(PATH_MAX)
+#   define PATH_MAX MAX_PATH
+#endif
+#if !defined(FILENAME_MAX)
+#   define FILENAME_MAX MAX_PATH
+#endif
+#if !defined(NAME_MAX)
+#   define NAME_MAX FILENAME_MAX
+#endif
+
+/* File type flags for d_type */
+#define DT_UNKNOWN 0
+#define DT_REG S_IFREG
+#define DT_DIR S_IFDIR
+#define DT_FIFO S_IFIFO
+#define DT_SOCK S_IFSOCK
+#define DT_CHR S_IFCHR
+#define DT_BLK S_IFBLK
+#define DT_LNK S_IFLNK
+
+/* Macros for converting between st_mode and d_type */
+#define IFTODT(mode) ((mode) & S_IFMT)
+#define DTTOIF(type) (type)
+
+/*
+ * File type macros.  Note that block devices, sockets and links cannot be
+ * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
+ * only defined for compatibility.  These macros should always return false
+ * on Windows.
+ */
+#if !defined(S_ISFIFO)
+#   define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISDIR)
+#   define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG)
+#   define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK)
+#   define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK)
+#   define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISCHR)
+#   define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISBLK)
+#   define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+
+/* Return the exact length of the file name without zero terminator */
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
+
+/* Return the maximum size of a file name */
+#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Wide-character version */
+struct _wdirent {
+    /* Always zero */
+    long d_ino;
+
+    /* File position within stream */
+    long d_off;
+
+    /* Structure size */
+    unsigned short d_reclen;
+
+    /* Length of name without \0 */
+    size_t d_namlen;
+
+    /* File type */
+    int d_type;
+
+    /* File name */
+    wchar_t d_name[PATH_MAX+1];
+};
+typedef struct _wdirent _wdirent;
+
+struct _WDIR {
+    /* Current directory entry */
+    struct _wdirent ent;
+
+    /* Private file data */
+    WIN32_FIND_DATAW data;
+
+    /* True if data is valid */
+    int cached;
+
+    /* Win32 search handle */
+    HANDLE handle;
+
+    /* Initial directory name */
+    wchar_t *patt;
+};
+typedef struct _WDIR _WDIR;
+
+/* Multi-byte character version */
+struct dirent {
+    /* Always zero */
+    long d_ino;
+
+    /* File position within stream */
+    long d_off;
+
+    /* Structure size */
+    unsigned short d_reclen;
+
+    /* Length of name without \0 */
+    size_t d_namlen;
+
+    /* File type */
+    int d_type;
+
+    /* File name */
+    char d_name[PATH_MAX+1];
+};
+typedef struct dirent dirent;
+
+struct DIR {
+    struct dirent ent;
+    struct _WDIR *wdirp;
+};
+typedef struct DIR DIR;
+
+
+/* Dirent functions */
+static DIR *opendir (const char *dirname);
+static _WDIR *_wopendir (const wchar_t *dirname);
+
+static struct dirent *readdir (DIR *dirp);
+static struct _wdirent *_wreaddir (_WDIR *dirp);
+
+static int readdir_r(
+    DIR *dirp, struct dirent *entry, struct dirent **result);
+static int _wreaddir_r(
+    _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
+
+static int closedir (DIR *dirp);
+static int _wclosedir (_WDIR *dirp);
+
+static void rewinddir (DIR* dirp);
+static void _wrewinddir (_WDIR* dirp);
+
+static int scandir (const char *dirname, struct dirent ***namelist,
+    int (*filter)(const struct dirent*),
+    int (*compare)(const struct dirent**, const struct dirent**));
+
+static int alphasort (const struct dirent **a, const struct dirent **b);
+
+static int versionsort (const struct dirent **a, const struct dirent **b);
+
+
+/* For compatibility with Symbian */
+#define wdirent _wdirent
+#define WDIR _WDIR
+#define wopendir _wopendir
+#define wreaddir _wreaddir
+#define wclosedir _wclosedir
+#define wrewinddir _wrewinddir
+
+
+/* Internal utility functions */
+static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
+static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
+
+static int dirent_mbstowcs_s(
+    size_t *pReturnValue,
+    wchar_t *wcstr,
+    size_t sizeInWords,
+    const char *mbstr,
+    size_t count);
+
+static int dirent_wcstombs_s(
+    size_t *pReturnValue,
+    char *mbstr,
+    size_t sizeInBytes,
+    const wchar_t *wcstr,
+    size_t count);
+
+static void dirent_set_errno (int error);
+
+
+/*
+ * Open directory stream DIRNAME for read and return a pointer to the
+ * internal working area that is used to retrieve individual directory
+ * entries.
+ */
+static _WDIR*
+_wopendir(
+    const wchar_t *dirname)
+{
+    _WDIR *dirp = NULL;
+    int error;
+
+    /* Must have directory name */
+    if (dirname == NULL  ||  dirname[0] == '\0') {
+        dirent_set_errno (ENOENT);
+        return NULL;
+    }
+
+    /* Allocate new _WDIR structure */
+    dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
+    if (dirp != NULL) {
+        DWORD n;
+
+        /* Reset _WDIR structure */
+        dirp->handle = INVALID_HANDLE_VALUE;
+        dirp->patt = NULL;
+        dirp->cached = 0;
+
+        /* Compute the length of full path plus zero terminator
+         *
+         * Note that on WinRT there's no way to convert relative paths
+         * into absolute paths, so just assume it is an absolute path.
+         */
+#       if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
+            n = wcslen(dirname);
+#       else
+            n = GetFullPathNameW (dirname, 0, NULL, NULL);
+#       endif
+
+        /* Allocate room for absolute directory name and search pattern */
+        dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
+        if (dirp->patt) {
+
+            /*
+             * Convert relative directory name to an absolute one.  This
+             * allows rewinddir() to function correctly even when current
+             * working directory is changed between opendir() and rewinddir().
+             *
+             * Note that on WinRT there's no way to convert relative paths
+             * into absolute paths, so just assume it is an absolute path.
+             */
+#           if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
+                wcsncpy_s(dirp->patt, n+1, dirname, n);
+#           else
+                n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
+#           endif
+            if (n > 0) {
+                wchar_t *p;
+
+                /* Append search pattern \* to the directory name */
+                p = dirp->patt + n;
+                if (dirp->patt < p) {
+                    switch (p[-1]) {
+                    case '\\':
+                    case '/':
+                    case ':':
+                        /* Directory ends in path separator, e.g. c:\temp\ */
+                        /*NOP*/;
+                        break;
+
+                    default:
+                        /* Directory name doesn't end in path separator */
+                        *p++ = '\\';
+                    }
+                }
+                *p++ = '*';
+                *p = '\0';
+
+                /* Open directory stream and retrieve the first entry */
+                if (dirent_first (dirp)) {
+                    /* Directory stream opened successfully */
+                    error = 0;
+                } else {
+                    /* Cannot retrieve first entry */
+                    error = 1;
+                    dirent_set_errno (ENOENT);
+                }
+
+            } else {
+                /* Cannot retrieve full path name */
+                dirent_set_errno (ENOENT);
+                error = 1;
+            }
+
+        } else {
+            /* Cannot allocate memory for search pattern */
+            error = 1;
+        }
+
+    } else {
+        /* Cannot allocate _WDIR structure */
+        error = 1;
+    }
+
+    /* Clean up in case of error */
+    if (error  &&  dirp) {
+        _wclosedir (dirp);
+        dirp = NULL;
+    }
+
+    return dirp;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns pointer to static directory entry which may be overwritten by
+ * subsequent calls to _wreaddir().
+ */
+static struct _wdirent*
+_wreaddir(
+    _WDIR *dirp)
+{
+    struct _wdirent *entry;
+
+    /*
+     * Read directory entry to buffer.  We can safely ignore the return value
+     * as entry will be set to NULL in case of error.
+     */
+    (void) _wreaddir_r (dirp, &dirp->ent, &entry);
+
+    /* Return pointer to statically allocated directory entry */
+    return entry;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns zero on success.  If end of directory stream is reached, then sets
+ * result to NULL and returns zero.
+ */
+static int
+_wreaddir_r(
+    _WDIR *dirp,
+    struct _wdirent *entry,
+    struct _wdirent **result)
+{
+    WIN32_FIND_DATAW *datap;
+
+    /* Read next directory entry */
+    datap = dirent_next (dirp);
+    if (datap) {
+        size_t n;
+        DWORD attr;
+
+        /*
+         * Copy file name as wide-character string.  If the file name is too
+         * long to fit in to the destination buffer, then truncate file name
+         * to PATH_MAX characters and zero-terminate the buffer.
+         */
+        n = 0;
+        while (n < PATH_MAX  &&  datap->cFileName[n] != 0) {
+            entry->d_name[n] = datap->cFileName[n];
+            n++;
+        }
+        entry->d_name[n] = 0;
+
+        /* Length of file name excluding zero terminator */
+        entry->d_namlen = n;
+
+        /* File type */
+        attr = datap->dwFileAttributes;
+        if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
+            entry->d_type = DT_CHR;
+        } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+            entry->d_type = DT_DIR;
+        } else {
+            entry->d_type = DT_REG;
+        }
+
+        /* Reset dummy fields */
+        entry->d_ino = 0;
+        entry->d_off = 0;
+        entry->d_reclen = sizeof (struct _wdirent);
+
+        /* Set result address */
+        *result = entry;
+
+    } else {
+
+        /* Return NULL to indicate end of directory */
+        *result = NULL;
+
+    }
+
+    return /*OK*/0;
+}
+
+/*
+ * Close directory stream opened by opendir() function.  This invalidates the
+ * DIR structure as well as any directory entry read previously by
+ * _wreaddir().
+ */
+static int
+_wclosedir(
+    _WDIR *dirp)
+{
+    int ok;
+    if (dirp) {
+
+        /* Release search handle */
+        if (dirp->handle != INVALID_HANDLE_VALUE) {
+            FindClose (dirp->handle);
+            dirp->handle = INVALID_HANDLE_VALUE;
+        }
+
+        /* Release search pattern */
+        if (dirp->patt) {
+            free (dirp->patt);
+            dirp->patt = NULL;
+        }
+
+        /* Release directory structure */
+        free (dirp);
+        ok = /*success*/0;
+
+    } else {
+
+        /* Invalid directory stream */
+        dirent_set_errno (EBADF);
+        ok = /*failure*/-1;
+
+    }
+    return ok;
+}
+
+/*
+ * Rewind directory stream such that _wreaddir() returns the very first
+ * file name again.
+ */
+static void
+_wrewinddir(
+    _WDIR* dirp)
+{
+    if (dirp) {
+        /* Release existing search handle */
+        if (dirp->handle != INVALID_HANDLE_VALUE) {
+            FindClose (dirp->handle);
+        }
+
+        /* Open new search handle */
+        dirent_first (dirp);
+    }
+}
+
+/* Get first directory entry (internal) */
+static WIN32_FIND_DATAW*
+dirent_first(
+    _WDIR *dirp)
+{
+    WIN32_FIND_DATAW *datap;
+
+    /* Open directory and retrieve the first entry */
+    dirp->handle = FindFirstFileExW(
+        dirp->patt, FindExInfoStandard, &dirp->data,
+        FindExSearchNameMatch, NULL, 0);
+    if (dirp->handle != INVALID_HANDLE_VALUE) {
+
+        /* a directory entry is now waiting in memory */
+        datap = &dirp->data;
+        dirp->cached = 1;
+
+    } else {
+
+        /* Failed to re-open directory: no directory entry in memory */
+        dirp->cached = 0;
+        datap = NULL;
+
+    }
+    return datap;
+}
+
+/*
+ * Get next directory entry (internal).
+ *
+ * Returns 
+ */
+static WIN32_FIND_DATAW*
+dirent_next(
+    _WDIR *dirp)
+{
+    WIN32_FIND_DATAW *p;
+
+    /* Get next directory entry */
+    if (dirp->cached != 0) {
+
+        /* A valid directory entry already in memory */
+        p = &dirp->data;
+        dirp->cached = 0;
+
+    } else if (dirp->handle != INVALID_HANDLE_VALUE) {
+
+        /* Get the next directory entry from stream */
+        if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
+            /* Got a file */
+            p = &dirp->data;
+        } else {
+            /* The very last entry has been processed or an error occurred */
+            FindClose (dirp->handle);
+            dirp->handle = INVALID_HANDLE_VALUE;
+            p = NULL;
+        }
+
+    } else {
+
+        /* End of directory stream reached */
+        p = NULL;
+
+    }
+
+    return p;
+}
+
+/*
+ * Open directory stream using plain old C-string.
+ */
+static DIR*
+opendir(
+    const char *dirname) 
+{
+    struct DIR *dirp;
+    int error;
+
+    /* Must have directory name */
+    if (dirname == NULL  ||  dirname[0] == '\0') {
+        dirent_set_errno (ENOENT);
+        return NULL;
+    }
+
+    /* Allocate memory for DIR structure */
+    dirp = (DIR*) malloc (sizeof (struct DIR));
+    if (dirp) {
+        wchar_t wname[PATH_MAX + 1];
+        size_t n;
+
+        /* Convert directory name to wide-character string */
+        error = dirent_mbstowcs_s(
+            &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1);
+        if (!error) {
+
+            /* Open directory stream using wide-character name */
+            dirp->wdirp = _wopendir (wname);
+            if (dirp->wdirp) {
+                /* Directory stream opened */
+                error = 0;
+            } else {
+                /* Failed to open directory stream */
+                error = 1;
+            }
+
+        } else {
+            /*
+             * Cannot convert file name to wide-character string.  This
+             * occurs if the string contains invalid multi-byte sequences or
+             * the output buffer is too small to contain the resulting
+             * string.
+             */
+            error = 1;
+        }
+
+    } else {
+        /* Cannot allocate DIR structure */
+        error = 1;
+    }
+
+    /* Clean up in case of error */
+    if (error  &&  dirp) {
+        free (dirp);
+        dirp = NULL;
+    }
+
+    return dirp;
+}
+
+/*
+ * Read next directory entry.
+ */
+static struct dirent*
+readdir(
+    DIR *dirp)
+{
+    struct dirent *entry;
+
+    /*
+     * Read directory entry to buffer.  We can safely ignore the return value
+     * as entry will be set to NULL in case of error.
+     */
+    (void) readdir_r (dirp, &dirp->ent, &entry);
+
+    /* Return pointer to statically allocated directory entry */
+    return entry;
+}
+
+/*
+ * Read next directory entry into called-allocated buffer.
+ *
+ * Returns zero on success.  If the end of directory stream is reached, then
+ * sets result to NULL and returns zero.
+ */
+static int
+readdir_r(
+    DIR *dirp,
+    struct dirent *entry,
+    struct dirent **result)
+{
+    WIN32_FIND_DATAW *datap;
+
+    /* Read next directory entry */
+    datap = dirent_next (dirp->wdirp);
+    if (datap) {
+        size_t n;
+        int error;
+
+        /* Attempt to convert file name to multi-byte string */
+        error = dirent_wcstombs_s(
+            &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1);
+
+        /*
+         * If the file name cannot be represented by a multi-byte string,
+         * then attempt to use old 8+3 file name.  This allows traditional
+         * Unix-code to access some file names despite of unicode
+         * characters, although file names may seem unfamiliar to the user.
+         *
+         * Be ware that the code below cannot come up with a short file
+         * name unless the file system provides one.  At least
+         * VirtualBox shared folders fail to do this.
+         */
+        if (error  &&  datap->cAlternateFileName[0] != '\0') {
+            error = dirent_wcstombs_s(
+                &n, entry->d_name, PATH_MAX + 1,
+                datap->cAlternateFileName, PATH_MAX + 1);
+        }
+
+        if (!error) {
+            DWORD attr;
+
+            /* Length of file name excluding zero terminator */
+            entry->d_namlen = n - 1;
+
+            /* File attributes */
+            attr = datap->dwFileAttributes;
+            if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
+                entry->d_type = DT_CHR;
+            } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+                entry->d_type = DT_DIR;
+            } else {
+                entry->d_type = DT_REG;
+            }
+
+            /* Reset dummy fields */
+            entry->d_ino = 0;
+            entry->d_off = 0;
+            entry->d_reclen = sizeof (struct dirent);
+
+        } else {
+
+            /*
+             * Cannot convert file name to multi-byte string so construct
+             * an erroneous directory entry and return that.  Note that
+             * we cannot return NULL as that would stop the processing
+             * of directory entries completely.
+             */
+            entry->d_name[0] = '?';
+            entry->d_name[1] = '\0';
+            entry->d_namlen = 1;
+            entry->d_type = DT_UNKNOWN;
+            entry->d_ino = 0;
+            entry->d_off = -1;
+            entry->d_reclen = 0;
+
+        }
+
+        /* Return pointer to directory entry */
+        *result = entry;
+
+    } else {
+
+        /* No more directory entries */
+        *result = NULL;
+
+    }
+
+    return /*OK*/0;
+}
+
+/*
+ * Close directory stream.
+ */
+static int
+closedir(
+    DIR *dirp)
+{
+    int ok;
+    if (dirp) {
+
+        /* Close wide-character directory stream */
+        ok = _wclosedir (dirp->wdirp);
+        dirp->wdirp = NULL;
+
+        /* Release multi-byte character version */
+        free (dirp);
+
+    } else {
+
+        /* Invalid directory stream */
+        dirent_set_errno (EBADF);
+        ok = /*failure*/-1;
+
+    }
+    return ok;
+}
+
+/*
+ * Rewind directory stream to beginning.
+ */
+static void
+rewinddir(
+    DIR* dirp)
+{
+    /* Rewind wide-character string directory stream */
+    _wrewinddir (dirp->wdirp);
+}
+
+/*
+ * Scan directory for entries.
+ */
+static int
+scandir(
+    const char *dirname,
+    struct dirent ***namelist,
+    int (*filter)(const struct dirent*),
+    int (*compare)(const struct dirent**, const struct dirent**))
+{
+    struct dirent **files = NULL;
+    size_t size = 0;
+    size_t allocated = 0;
+    const size_t init_size = 1;
+    DIR *dir = NULL;
+    struct dirent *entry;
+    struct dirent *tmp = NULL;
+    size_t i;
+    int result = 0;
+
+    /* Open directory stream */
+    dir = opendir (dirname);
+    if (dir) {
+
+        /* Read directory entries to memory */
+        while (1) {
+
+            /* Enlarge pointer table to make room for another pointer */
+            if (size >= allocated) {
+                void *p;
+                size_t num_entries;
+
+                /* Compute number of entries in the enlarged pointer table */
+                if (size < init_size) {
+                    /* Allocate initial pointer table */
+                    num_entries = init_size;
+                } else {
+                    /* Double the size */
+                    num_entries = size * 2;
+                }
+
+                /* Allocate first pointer table or enlarge existing table */
+                p = realloc (files, sizeof (void*) * num_entries);
+                if (p != NULL) {
+                    /* Got the memory */
+                    files = (dirent**) p;
+                    allocated = num_entries;
+                } else {
+                    /* Out of memory */
+                    result = -1;
+                    break;
+                }
+
+            }
+
+            /* Allocate room for temporary directory entry */
+            if (tmp == NULL) {
+                tmp = (struct dirent*) malloc (sizeof (struct dirent));
+                if (tmp == NULL) {
+                    /* Cannot allocate temporary directory entry */
+                    result = -1;
+                    break;
+                }
+            }
+
+            /* Read directory entry to temporary area */
+            if (readdir_r (dir, tmp, &entry) == /*OK*/0) {
+
+                /* Did we get an entry? */
+                if (entry != NULL) {
+                    int pass;
+
+                    /* Determine whether to include the entry in result */
+                    if (filter) {
+                        /* Let the filter function decide */
+                        pass = filter (tmp);
+                    } else {
+                        /* No filter function, include everything */
+                        pass = 1;
+                    }
+
+                    if (pass) {
+                        /* Store the temporary entry to pointer table */
+                        files[size++] = tmp;
+                        tmp = NULL;
+
+                        /* Keep up with the number of files */
+                        result++;
+                    }
+
+                } else {
+
+                    /*
+                     * End of directory stream reached => sort entries and
+                     * exit.
+                     */
+                    qsort (files, size, sizeof (void*),
+                        (int (*) (const void*, const void*)) compare);
+                    break;
+
+                }
+
+            } else {
+                /* Error reading directory entry */
+                result = /*Error*/ -1;
+                break;
+            }
+
+        }
+
+    } else {
+        /* Cannot open directory */
+        result = /*Error*/ -1;
+    }
+
+    /* Release temporary directory entry */
+    if (tmp) {
+        free (tmp);
+    }
+
+    /* Release allocated memory on error */
+    if (result < 0) {
+        for (i = 0; i < size; i++) {
+            free (files[i]);
+        }
+        free (files);
+        files = NULL;
+    }
+
+    /* Close directory stream */
+    if (dir) {
+        closedir (dir);
+    }
+
+    /* Pass pointer table to caller */
+    if (namelist) {
+        *namelist = files;
+    }
+    return result;
+}
+
+/* Alphabetical sorting */
+static int
+alphasort(
+    const struct dirent **a, const struct dirent **b)
+{
+    return strcoll ((*a)->d_name, (*b)->d_name);
+}
+
+/* Sort versions */
+static int
+versionsort(
+    const struct dirent **a, const struct dirent **b)
+{
+    /* FIXME: implement strverscmp and use that */
+    return alphasort (a, b);
+}
+
+
+/* Convert multi-byte string to wide character string */
+static int
+dirent_mbstowcs_s(
+    size_t *pReturnValue,
+    wchar_t *wcstr,
+    size_t sizeInWords,
+    const char *mbstr,
+    size_t count)
+{
+    int error;
+
+#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
+
+    /* Microsoft Visual Studio 2005 or later */
+    error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
+
+#else
+
+    /* Older Visual Studio or non-Microsoft compiler */
+    size_t n;
+
+    /* Convert to wide-character string (or count characters) */
+    n = mbstowcs (wcstr, mbstr, sizeInWords);
+    if (!wcstr  ||  n < count) {
+
+        /* Zero-terminate output buffer */
+        if (wcstr  &&  sizeInWords) {
+            if (n >= sizeInWords) {
+                n = sizeInWords - 1;
+            }
+            wcstr[n] = 0;
+        }
+
+        /* Length of resulting multi-byte string WITH zero terminator */
+        if (pReturnValue) {
+            *pReturnValue = n + 1;
+        }
+
+        /* Success */
+        error = 0;
+
+    } else {
+
+        /* Could not convert string */
+        error = 1;
+
+    }
+
+#endif
+
+    return error;
+}
+
+/* Convert wide-character string to multi-byte string */
+static int
+dirent_wcstombs_s(
+    size_t *pReturnValue,
+    char *mbstr,
+    size_t sizeInBytes, /* max size of mbstr */
+    const wchar_t *wcstr,
+    size_t count)
+{
+    int error;
+
+#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
+
+    /* Microsoft Visual Studio 2005 or later */
+    error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
+
+#else
+
+    /* Older Visual Studio or non-Microsoft compiler */
+    size_t n;
+
+    /* Convert to multi-byte string (or count the number of bytes needed) */
+    n = wcstombs (mbstr, wcstr, sizeInBytes);
+    if (!mbstr  ||  n < count) {
+
+        /* Zero-terminate output buffer */
+        if (mbstr  &&  sizeInBytes) {
+            if (n >= sizeInBytes) {
+                n = sizeInBytes - 1;
+            }
+            mbstr[n] = '\0';
+        }
+
+        /* Length of resulting multi-bytes string WITH zero-terminator */
+        if (pReturnValue) {
+            *pReturnValue = n + 1;
+        }
+
+        /* Success */
+        error = 0;
+
+    } else {
+
+        /* Cannot convert string */
+        error = 1;
+
+    }
+
+#endif
+
+    return error;
+}
+
+/* Set errno variable */
+static void
+dirent_set_errno(
+    int error)
+{
+#if defined(_MSC_VER)  &&  _MSC_VER >= 1400
+
+    /* Microsoft Visual Studio 2005 and later */
+    _set_errno (error);
+
+#else
+
+    /* Non-Microsoft compiler or older Microsoft compiler */
+    errno = error;
+
+#endif
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*DIRENT_H*/