[Pkg-virtualbox-commits] [kbuild] 01/10: Imported Upstream version 0.1.9998svn3025+dfsg

Gianfranco Costamagna locutusofborg at moszumanska.debian.org
Thu Oct 19 15:44:39 UTC 2017


This is an automated email from the git hooks/post-receive script.

locutusofborg pushed a commit to branch experimental
in repository kbuild.

commit 10af2d8272974667288cd5e1ca802bf1df5e4c1e
Author: Gianfranco Costamagna <locutusofborg at debian.org>
Date:   Fri Jan 13 17:46:59 2017 +0100

    Imported Upstream version 0.1.9998svn3025+dfsg
---
 SlickEdit/kdev.e              |  56 ++-
 SlickEdit/kkeys.e             |   8 +-
 kBuild/tools/VAC308.kmk       |  12 +-
 kBuild/units/qt5.kmk          |  17 +-
 src/Makefile.kmk              |   3 +-
 src/{ => kDeDup}/Makefile.kmk |  23 +-
 src/kDeDup/kDeDup.c           | 838 ++++++++++++++++++++++++++++++++++++++++++
 src/kmk/dir-nt-bird.c         |  53 ++-
 src/lib/nt/fts-nt.c           | 786 +++++++++++++++++++++++++++++----------
 src/lib/nt/fts-nt.h           |  18 +-
 src/lib/nt/kFsCache.c         |  21 +-
 src/lib/nt/ntdir.c            | 290 ++++++++++++++-
 src/lib/nt/ntdir.h            |  27 +-
 src/lib/nt/nthlp.h            |   4 +-
 src/lib/nt/nthlpcore.c        |   5 +-
 src/lib/nt/ntstat.c           | 237 +++++++-----
 src/lib/nt/ntstat.h           |  11 +-
 src/lib/nt/ntstuff.h          |   7 +-
 src/lib/nt/ntunlink.c         |  66 +++-
 src/lib/nt/ntunlink.h         |   8 +-
 src/lib/nt/tstNtFts.c         |  19 +-
 src/lib/nt/tstNtStat.c        |   5 +-
 22 files changed, 2139 insertions(+), 375 deletions(-)

diff --git a/SlickEdit/kdev.e b/SlickEdit/kdev.e
index 5e883ae..46d7940 100644
--- a/SlickEdit/kdev.e
+++ b/SlickEdit/kdev.e
@@ -1,4 +1,4 @@
-/* $Id: kdev.e 2826 2016-08-14 13:58:02Z bird $  -*- tab-width: 4 c-indent-level: 4 -*- */
+/* $Id: kdev.e 3018 2017-01-07 00:06:39Z bird $  -*- tab-width: 4 c-indent-level: 4 -*- */
 /** @file
  * Visual SlickEdit Documentation Macros.
  */
@@ -190,45 +190,71 @@ static boolean k_commentconfig(_str &sLeft, _str &sRight, int &iColumn, _str sEx
     if (sLexer)
     {
         /* multiline */
-#if __VERSION__ >= 14.0
+#if __VERSION__ >= 21.0
+        COMMENT_TYPE aComments[];
+        GetComments(aComments, "M", sLexer);
+        for (i = 0; i < aComments._length(); i++)
+            if (!aComments[i].isDocumentation)
+            {
+                sLeft   = aComments[i].delim1;
+                sRight  = aComments[i].delim2;
+                iColumn = aComments[i].startcol;
+                if (sLeft != '' && sRight != '')
+                    return true;
+            }
+#else
+# if __VERSION__ >= 14.0
         _str aComments[] = null;
         GetComments(aComments, "mlcomment", sLexer);
         for (i = 0; i < aComments._length(); i++)
-            if (!pos("documentation", aComments[i]) > 0)
+            if (pos("documentation", aComments[i]) <= 0)
             {
                 sLine = aComments[i];
                 break;
             }
         if (sLine != '')
-#else
+# else
         rc = _ini_get_value(slick_path_search("user.vlx"), sLexer, 'mlcomment', sLine);
         if (rc)
             rc = _ini_get_value(slick_path_search("vslick.vlx"), sLexer, 'mlcomment', sLine);
         if (!rc)
-#endif
+# endif
         {
             sLeft  = strip(word(sLine, 1));
             sRight = strip(word(sLine, 2));
             if (sLeft != '' && sRight != '')
                 return true;
         }
+#endif
 
         /* failed, try single line. */
-#if __VERSION__ >= 14.0
+#if __VERSION__ >= 21.0
+        GetComments(aComments, "L", sLexer);
+        for (i = 0; i < aComments._length(); i++)
+            if (!aComments[i].isDocumentation)
+            {
+                sLeft   = aComments[i].delim1;
+                sRight  = '';
+                iColumn = aComments[i].startcol;
+                if (sLeft != '')
+                    return true;
+            }
+#else
+# if __VERSION__ >= 14.0
         GetComments(aComments, "linecomment", sLexer)
         for (i = 0; i < aComments._length(); i++)
-            if (!pos("documentation", aComments[i]) > 0)
+            if (pos("documentation", aComments[i]) <= 0)
             {
                 sLine = aComments[i];
                 break;
             }
         if (sLine != '')
-#else
+# else
         rc = _ini_get_value(slick_path_search("user.vlx"), sLexer, 'linecomment', sLine);
         if (rc)
             rc = _ini_get_value(slick_path_search("vslick.vlx"), sLexer, 'linecomment', sLine);
         if (!rc)
-#endif
+# endif
         {
             sLeft = strip(word(sLine, 1));
             sRight = '';
@@ -239,6 +265,7 @@ static boolean k_commentconfig(_str &sLeft, _str &sRight, int &iColumn, _str sEx
             if (sLeft != '')
                 return true;
         }
+#endif
     }
 
     /*
@@ -2447,6 +2474,11 @@ static int k_style_emacs_var(_str sVar, _str sVal)
         return -1;
     //say 'k_style_emacs_var: 'sVar'='sVal;
 
+#if __VERSION__ >= 21.0
+    /** @todo figure out p_index. */
+    return 0;
+#else
+
     /*
      * Unpack the mode style parameters.
      */
@@ -2804,6 +2836,7 @@ static int k_style_emacs_var(_str sVar, _str sVal)
     }
 
     return 0;
+#endif
 }
 
 
@@ -3570,6 +3603,10 @@ _command void kdev_load_settings()
         LanguageSettings.setSyntaxIndent(sLangId, 4);
 
         /* C/C++ setup, wrap at column 80 not 64. */
+# if __VERSION__ >= 21.0
+        if (_LangGetPropertyInt32(sLangId, VSLANGPROPNAME_CW_FIXED_RIGHT_COLUMN) < 80)
+            _LangSetPropertyInt32(sLangId, VSLANGPROPNAME_CW_FIXED_RIGHT_COLUMN, 80);
+# else
         sTmp = LanguageSettings.getCommentWrapOptions(sLangId);
         if (length(sTmp) > 10)
         {
@@ -3581,6 +3618,7 @@ _command void kdev_load_settings()
             //replace_def_data("def-comment-wrap-c",'0 1 0 1 1 80 0 0 80 0 80 0 80 0 0 0 '); - disabled
             //replace_def_data("def-comment-wrap-c",'1 1 0 1 1 80 0 0 80 0 80 0 80 0 0 1 '); - enable block comment wrap.
         }
+# endif
 
         /* set the encoding to UTF-8 without any friggin useless signatures. */
         idxExt = name_match('def-lang-for-ext-', 1, MISC_TYPE);
diff --git a/SlickEdit/kkeys.e b/SlickEdit/kkeys.e
index d447ef3..2614376 100644
--- a/SlickEdit/kkeys.e
+++ b/SlickEdit/kkeys.e
@@ -1,4 +1,4 @@
-/* $Id: kkeys.e 2558 2012-02-13 12:36:03Z bird $ */
+/* $Id: kkeys.e 3015 2016-11-29 10:14:56Z bird $ */
 /** @file
  * Bird's key additions to Visual Slickedit.
  */
@@ -299,6 +299,7 @@ _command kkeys_push_ref()
       if (sProjTagFile != '')
       {
 
+# if __VERSION__ < 21.0 /** @todo fix me? */
          /* HACK ALERT: Make sure gtag_filelist_last_ext has the right value. */
          _update_tag_filelist_ext(sLangId);
 
@@ -309,14 +310,17 @@ _command kkeys_push_ref()
          /* HACK ALERT: Replace the tag file list for this language. */
          gtag_filelist_ext._makeempty();
          gtag_filelist_ext[0] = sProjTagFile;
-         saved_gtag_filelist_cache_updated = true;
+         gtag_filelist_cache_updated = true;
+# endif
 
          /* Do the reference searching. */
          push_ref('-e ' :+ sLangId);
 
+# if __VERSION__ < 21.0
          /* restore*/
          gtag_filelist_cache_updated = saved_gtag_filelist_cache_updated;
          gtag_filelist_ext           = saved_gtag_filelist_ext;
+# endif
       }
       else
          push_ref();
diff --git a/kBuild/tools/VAC308.kmk b/kBuild/tools/VAC308.kmk
index 8a12242..8899f19 100644
--- a/kBuild/tools/VAC308.kmk
+++ b/kBuild/tools/VAC308.kmk
@@ -1,4 +1,4 @@
-# $Id: VAC308.kmk 2750 2015-01-23 12:24:02Z bird $
+# $Id: VAC308.kmk 3023 2017-01-07 17:24:57Z bird $
 ## @file
 # kBuild Tool Config - VisualAge for C++ v3.08.
 #
@@ -66,7 +66,7 @@ ifneq ($(PATH_TOOL_VAC308),)
  TOOL_VAC308_ENV_SETUP ?= $(REDIRECT) \
 	-E 'BEGINLIBPATH=$(PATH_TOOL_VAC308_DLL);$(BEGINLIBPATH)' \
 	-E 'DPATH=$(PATH_TOOL_VAC308_LOCALE);$(PATH_TOOL_VAC308_HELP);$(DPATH)' \
-	-E 'LIB=' \
+	-E 'LIB=$1' \
 	-E 'INCLUDE=' \
 	--
  TOOL_VAC308_CC  ?= $(PATH_TOOL_VAC308_BIN)/icc$(HOSTSUFF_EXE)
@@ -79,7 +79,7 @@ else
  # Pathless, relies on the environment.
  TOOL_VAC308_PATHLESS := yes
  TOOL_VAC308_ENV_SETUP ?= $(REDIRECT) \
-	-E 'LIB=' \
+	-E 'LIB=$1' \
 	-E 'INCLUDE=' \
 	--
  TOOL_VAC308_CC  ?= icc$(HOSTSUFF_EXE)
@@ -176,7 +176,7 @@ TOOL_VAC308_LINK_PROGRAM_OUTPUT = $(outbase).map
 TOOL_VAC308_LINK_PROGRAM_DEPEND = $(foreach lib,$(libs),$(if $(findstring $(lib),$(subst /,x,$(lib))),, $(lib))) $(othersrc)
 TOOL_VAC308_LINK_PROGRAM_DEPORD =
 define TOOL_VAC308_LINK_PROGRAM_CMDS
-	$(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst $(SP),;,$(libpath))) \
+	$(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst ;$(SP),;,$(foreach one,$(libpath),$(one);))) \
 		$(TOOL_VAC308_LD) $(flags) -Fe$(out) -Fm$(outbase).map $(filter-out %.res,$(objs)) $(libs) $(othersrc)
 	$(if $(filter %.res,$(objs)), $(QUIET)$(call TOOL_VAC308_ENV_SETUP) \
 		$(TOOL_VAC308_RC) $(filter %.res,$(objs)) $(out))
@@ -186,7 +186,7 @@ TOOL_VAC308_LINK_DLL_OUTPUT = $(outbase).map
 TOOL_VAC308_LINK_DLL_DEPEND = $(foreach lib,$(libs),$(if $(findstring $(lib),$(subst /,x,$(lib))),, $(lib))) $(othersrc)
 TOOL_VAC308_LINK_DLL_DEPORD =
 define TOOL_VAC308_LINK_DLL_CMDS
-	$(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst $(SP),;,$(libpath))) \
+	$(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst ;$(SP),;,$(foreach one,$(libpath),$(one);))) \
 		$(TOOL_VAC308_LD) /B"/DLL" $(flags) -Fe$(out) -Fm$(outbase).map $(filter-out %.res,$(objs)) $(libs) $(othersrc)
 	$(if $(filter %.res,$(objs)), $(QUIET)$(call TOOL_VAC308_ENV_SETUP) \
 		$(TOOL_VAC308_RC) $(filter %.res,$(objs)) $(out))
@@ -196,7 +196,7 @@ TOOL_VAC308_LINK_SYSMOD_OUTPUT = $(outbase).map
 TOOL_VAC308_LINK_SYSMOD_DEPEND = $(foreach lib,$(libs),$(if $(findstring $(lib),$(subst /,x,$(lib))),, $(lib))) $(othersrc)
 TOOL_VAC308_LINK_SYSMOD_DEPORD =
 define TOOL_VAC308_LINK_SYSMOD_CMDS
-	$(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst $(SP),;,$(libpath))) \
+	$(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst ;$(SP),;,$(foreach one,$(libpath),$(one);))) \
 		$(TOOL_VAC308_LD) $(flags) -Fe$(out) -Fm$(outbase).map $(filter-out %.res,$(objs)) $(libs) $(othersrc)
 	$(if $(filter %.res,$(objs)), $(QUIET)$(call TOOL_VAC308_ENV_SETUP) \
 		$(TOOL_VAC308_RC) $(filter %.res,$(objs)) $(out))
diff --git a/kBuild/units/qt5.kmk b/kBuild/units/qt5.kmk
index 67e5baf..e7e7af9 100644
--- a/kBuild/units/qt5.kmk
+++ b/kBuild/units/qt5.kmk
@@ -1,4 +1,4 @@
-# $Id: qt5.kmk 2980 2016-09-27 14:40:53Z bird $
+# $Id: qt5.kmk 3016 2017-01-04 12:51:53Z bird $
 ## @file
 # Qt 5 unit.
 #
@@ -113,15 +113,26 @@ ifndef PATH_SDK_QT5
     ifneq ($(PATH_SDK_QT5),)
      export PATH_SDK_QT5
 
-     # Locate the include files.
+     # Locate the include files. Check for qglobalstatic.h (since 5.1) first.
      ifeq ($(PATH_SDK_QT5_INC),)
-      PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobal.h,%,$(firstword $(wildcard \
+      PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobalstatic.h,%,$(firstword $(wildcard \
+	$(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/include/$(type)/qt5/QtCore/qglobalstatic.h) \
+	$(PATH_SDK_QT5)/include/qt5/QtCore/qglobalstatic.h \
+	/usr/include/qt5/QtCore/qtglobalstatic.h \
+	/usr/local/include/qt5/QtCore/qtglobalstatic.h \
+	$(PATH_SDK_QT5)/include/QtCore/qglobalstatic.h \
+        /usr/include/qt/QtCore/qglobalstatic.h \
+        /usr/local/include/qt/QtCore/qglobalstatic.h \
+	)))
+      ifeq ($(PATH_SDK_QT5_INC),)
+       PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobal.h,%,$(firstword $(wildcard \
 	$(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/include/$(type)/qt5/QtCore/qglobal.h) \
 	$(PATH_SDK_QT5)/include/qt5/QtCore/qglobal.h \
 	/usr/include/qt5/QtCore/qtglobal.h \
 	/usr/local/include/qt5/QtCore/qtglobal.h \
 	$(PATH_SDK_QT5)/include/QtCore/qglobal.h \
 	)))
+      endif
       ifneq ($(PATH_SDK_QT5_INC),)
        export PATH_SDK_QT5_INC
       endif
diff --git a/src/Makefile.kmk b/src/Makefile.kmk
index a951bb3..d9aca68 100644
--- a/src/Makefile.kmk
+++ b/src/Makefile.kmk
@@ -1,4 +1,4 @@
-# $Id: Makefile.kmk 2846 2016-08-30 12:48:33Z bird $
+# $Id: Makefile.kmk 3013 2016-11-07 11:54:02Z bird $
 ## @file
 # Sub-makefile for the source directory.
 #
@@ -35,6 +35,7 @@ include $(PATH_SUB_CURRENT)/kObjCache/Makefile.kmk
 include $(PATH_SUB_CURRENT)/misc/Makefile.kmk
 ifeq ($(KBUILD_TARGET),win)
  include $(PATH_SUB_CURRENT)/kLibTweaker/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/kDeDup/Makefile.kmk
  include $(PATH_SUB_CURRENT)/kWorker/Makefile.kmk
 endif
 
diff --git a/src/Makefile.kmk b/src/kDeDup/Makefile.kmk
similarity index 53%
copy from src/Makefile.kmk
copy to src/kDeDup/Makefile.kmk
index a951bb3..5fe5213 100644
--- a/src/Makefile.kmk
+++ b/src/kDeDup/Makefile.kmk
@@ -1,10 +1,10 @@
-# $Id: Makefile.kmk 2846 2016-08-30 12:48:33Z bird $
+# $Id: Makefile.kmk 3012 2016-11-07 11:53:11Z bird $
 ## @file
-# Sub-makefile for the source directory.
+# Sub-makefile for kDeDup.
 #
 
 #
-# Copyright (c) 2004-2010 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
+# Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
 #
 # This file is part of kBuild.
 #
@@ -23,20 +23,13 @@
 #
 #
 
-SUB_DEPTH = ..
+SUB_DEPTH = ../..
 include $(KBUILD_PATH)/subheader.kmk
 
-include $(PATH_SUB_CURRENT)/lib/Makefile.kmk
-include $(PATH_SUB_CURRENT)/sed/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kmk/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kash/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kDepPre/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kObjCache/Makefile.kmk
-include $(PATH_SUB_CURRENT)/misc/Makefile.kmk
-ifeq ($(KBUILD_TARGET),win)
- include $(PATH_SUB_CURRENT)/kLibTweaker/Makefile.kmk
- include $(PATH_SUB_CURRENT)/kWorker/Makefile.kmk
-endif
+PROGRAMS += kDeDup
+kDeDup_TEMPLATE        = BIN
+kDeDup_LIBS            = $(LIB_KUTIL)
+kDeDup_SOURCES         = kDeDup.c
 
 include $(FILE_KBUILD_SUB_FOOTER)
 
diff --git a/src/kDeDup/kDeDup.c b/src/kDeDup/kDeDup.c
new file mode 100644
index 0000000..3faf47d
--- /dev/null
+++ b/src/kDeDup/kDeDup.c
@@ -0,0 +1,838 @@
+/* $Id: kDeDup.c 3012 2016-11-07 11:53:11Z bird $ */
+/** @file
+ * kDeDup - Utility that finds duplicate files, optionally hardlinking them.
+ */
+
+/*
+ * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <k/kTypes.h>
+//#include <stdlib.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "md5.h"
+//#include "sha2.h"
+
+#include "nt/ntstuff.h"
+#include "nt/ntstat.h"
+#include "nt/fts-nt.h"
+#include "nt/nthlp.h"
+#include "nt/ntunlink.h"
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/**
+ * The key is made up of two cryptographic hashes, collisions are
+ * highly unlikely (once SHA2 is implemented).
+ */
+typedef struct KDUPFILENODEKEY
+{
+    /** The MD5 digest of the file. */
+    KU8             abMd5[16];
+    /** The 256-bit SHA-2 digest of the file. */
+    KU8             abSha2[32];
+} KDUPFILENODEKEY;
+/** Pointer to a file node.*/
+typedef struct KDUPFILENODE *PKDUPFILENODE;
+/**
+ * Hash tree node.
+ */
+typedef struct KDUPFILENODE
+{
+    /** The is made up of two hashes. */
+    KDUPFILENODEKEY mKey;
+    /** Left branch. */
+    PKDUPFILENODE   mpLeft;
+    /** Right branch. */
+    PKDUPFILENODE   mpRight;
+    /** Tree height (hmm). */
+    KU8             mHeight;
+
+    /** The inode number. */
+    KU64            uInode;
+    /** The device number. */
+    KU64            uDev;
+
+    /** Pointer to next hard linked node (same inode and udev values). */
+    PKDUPFILENODE   pNextHardLink;
+    /** Pointer to next duplicate node. */
+    PKDUPFILENODE   pNextDup;
+    /** Pointer to next duplicate node on the global list. */
+    PKDUPFILENODE   pNextGlobalDup;
+
+    /** The path to this file (variable size). */
+    wchar_t         wszPath[1];
+} KDUPFILENODE;
+
+/*#define KAVL_EQUAL_ALLOWED*/
+#define KAVL_CHECK_FOR_EQUAL_INSERT
+#define KAVL_MAX_STACK          32
+/*#define KAVL_RANGE */
+/*#define KAVL_OFFSET */
+/*#define KAVL_STD_KEY_COMP*/
+#define KAVLKEY                 KDUPFILENODEKEY
+#define KAVLNODE                KDUPFILENODE
+#define KAVL_FN(name)           kDupFileTree_ ## name
+#define KAVL_TYPE(prefix,name)  prefix ## KDUPFILENODE ## name
+#define KAVL_INT(name)          KDUPFILENODEINT ## name
+#define KAVL_DECL(rettype)      static rettype
+#define KAVL_G(key1, key2)      ( memcmp(&(key1), &(key2), sizeof(KDUPFILENODEKEY)) >  0 )
+#define KAVL_E(key1, key2)      ( memcmp(&(key1), &(key2), sizeof(KDUPFILENODEKEY)) == 0 )
+#define KAVL_NE(key1, key2)     ( memcmp(&(key1), &(key2), sizeof(KDUPFILENODEKEY)) != 0 )
+
+#define register
+#include <k/kAvlTmpl/kAvlBase.h>
+#include <k/kAvlTmpl/kAvlDoWithAll.h>
+//#include <k/kAvlTmpl/kAvlEnum.h> - busted
+#include <k/kAvlTmpl/kAvlGet.h>
+#include <k/kAvlTmpl/kAvlGetBestFit.h>
+#include <k/kAvlTmpl/kAvlGetWithParent.h>
+#include <k/kAvlTmpl/kAvlRemove2.h>
+#include <k/kAvlTmpl/kAvlRemoveBestFit.h>
+#include <k/kAvlTmpl/kAvlUndef.h>
+#undef register
+
+
+/** Pointer to a size tree node. */
+typedef struct KDUPSIZENODE *PKDUPSIZENODE;
+/**
+ * Size tree node.
+ */
+typedef struct KDUPSIZENODE
+{
+    /** The file size. */
+    KU64            mKey;
+    /** Left branch. */
+    PKDUPSIZENODE   mpLeft;
+    /** Right branch. */
+    PKDUPSIZENODE   mpRight;
+    /** Tree height (hmm). */
+    KU8             mHeight;
+    /** Number of files. */
+    KU32            cFiles;
+    /** Tree with same sized files.
+     * When cFiles is 1 the root node does not have hashes calculated yet.  */
+    KDUPFILENODEROOT FileRoot;
+} KDUPSIZENODE;
+
+/*#define KAVL_EQUAL_ALLOWED*/
+#define KAVL_CHECK_FOR_EQUAL_INSERT
+#define KAVL_MAX_STACK          32
+/*#define KAVL_RANGE */
+/*#define KAVL_OFFSET */
+#define KAVL_STD_KEY_COMP
+#define KAVLKEY                 KU64
+#define KAVLNODE                KDUPSIZENODE
+#define KAVL_FN(name)           kDupSizeTree_ ## name
+#define KAVL_TYPE(prefix,name)  prefix ## KDUPSIZENODE ## name
+#define KAVL_INT(name)          KDUPSIZENODEINT ## name
+#define KAVL_DECL(rettype)      static rettype
+
+#include <k/kAvlTmpl/kAvlBase.h>
+#include <k/kAvlTmpl/kAvlDoWithAll.h>
+//#include <k/kAvlTmpl/kAvlEnum.h> - busted
+#include <k/kAvlTmpl/kAvlGet.h>
+#include <k/kAvlTmpl/kAvlGetBestFit.h>
+#include <k/kAvlTmpl/kAvlGetWithParent.h>
+#include <k/kAvlTmpl/kAvlRemove2.h>
+#include <k/kAvlTmpl/kAvlRemoveBestFit.h>
+#include <k/kAvlTmpl/kAvlUndef.h>
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+/** The verbosity level. */
+static unsigned g_cVerbosity                    = 2;
+
+/** Whether to recurse into subdirectories. */
+static KBOOL    g_fRecursive                    = K_FALSE;
+/** Whether to recurse into symlinked subdirectories. */
+static KBOOL    g_fRecursiveViaSymlinks         = K_FALSE;
+/** Whether to follow symbolicly linked files. */
+static KBOOL    g_fFollowSymlinkedFiles         = K_TRUE;
+
+/** Minimum file size to care about.   */
+static KU64     g_cbMinFileSize                 = 1;
+/** Maximum file size to care about.   */
+static KU64     g_cbMaxFileSize                 = KU64_MAX;
+
+/** The root of the size tree.   */
+static KDUPSIZENODEROOT g_SizeRoot;
+
+/** Global list of duplicate file with duplicates.
+ * @remarks This only contains the files in the hash tree, not the ones on
+ *          the KDUPFILENODE::pNextDup list. */
+static PKDUPFILENODE    g_pDuplicateHead        = NULL;
+/** Where to insert the next file with duplicates. */
+static PKDUPFILENODE   *g_ppNextDuplicate       = &g_pDuplicateHead;
+
+/** Number of files we're tracking. */
+static KU64             g_cFiles                = 0;
+/** Number of hardlinked files or files entered more than once. */
+static KU64             g_cHardlinked           = 0;
+/** Number of duplicates files (not hardlinked). */
+static KU64             g_cDuplicates           = 0;
+/** Number of duplicates files that can be hardlinked. */
+static KU64             g_cDuplicatesSaved      = 0;
+/** Size that could be saved if the duplicates were hardlinked. */
+static KU64             g_cbDuplicatesSaved     = 0;
+
+
+
+/**
+ * Wrapper around malloc() that complains when out of memory.
+ *
+ * @returns Pointer to allocated memory
+ * @param   cb      The size of the memory to allocate.
+ */
+static void *kDupAlloc(KSIZE cb)
+{
+    void *pvRet = malloc(cb);
+    if (pvRet)
+        return pvRet;
+    fprintf(stderr, "kDeDup: error: out of memory! (cb=%#z)\n", cb);
+    return NULL;
+}
+
+/** Wrapper around free() for symmetry. */
+#define kDupFree(ptr) free(ptr)
+
+
+static void kDupHashFile(PKDUPFILENODE pFileNode, FTSENT *pFtsEnt)
+{
+    KSIZE           i;
+    PKDUPFILENODE  *ppHash;
+
+    /*
+     * Open the file.
+     */
+    HANDLE hFile;
+    if (pFtsEnt && pFtsEnt->fts_parent && pFtsEnt->fts_parent->fts_dirfd != INVALID_HANDLE_VALUE)
+        hFile = birdOpenFileExW(pFtsEnt->fts_parent->fts_dirfd, pFtsEnt->fts_wcsname,
+                                FILE_READ_DATA | SYNCHRONIZE,
+                                FILE_ATTRIBUTE_NORMAL,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                FILE_OPEN,
+                                FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+                                OBJ_CASE_INSENSITIVE);
+    else
+        hFile = birdOpenFileExW(NULL, pFileNode->wszPath,
+                                FILE_READ_DATA | SYNCHRONIZE,
+                                FILE_ATTRIBUTE_NORMAL,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                FILE_OPEN,
+                                FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+                                OBJ_CASE_INSENSITIVE);
+    if (hFile != INVALID_HANDLE_VALUE)
+    {
+        /*
+         * Init the hash calculation contexts.
+         */
+        struct MD5Context Md5Ctx;
+        //SHA256CONTEXT Sha256Ctx;
+        MD5Init(&Md5Ctx);
+        //Sha256Init(&Sha256Ctx);
+
+        /*
+         * Process the file chunk by chunk.
+         *
+         * We could complicate this by memory mapping medium sized files, but
+         * those kind of complications can wait.
+         */
+        for (;;)
+        {
+            static KU8          s_abBuffer[2*1024*1024];
+            MY_NTSTATUS         rcNt;
+            MY_IO_STATUS_BLOCK  Ios;
+            Ios.Information = -1;
+            Ios.u.Status    = -1;
+            rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/,
+                                   &Ios, s_abBuffer, sizeof(s_abBuffer), NULL /*poffFile*/, NULL /*puKey*/);
+            if (MY_NT_SUCCESS(rcNt))
+            {
+                MD5Update(&Md5Ctx, s_abBuffer, (unsigned)Ios.Information);
+                //SHA256Update(&Sha256Ctx, s_abBuffer, Ios.Information);
+            }
+            else if (rcNt != STATUS_END_OF_FILE)
+            {
+                fprintf(stderr, "kDeDup: warning: Error reading '%ls': %#x\n", pFileNode->wszPath, rcNt);
+                break;
+            }
+
+            /* Check for end of file. */
+            if (   rcNt == STATUS_END_OF_FILE
+                || Ios.Information < sizeof(s_abBuffer))
+            {
+                MD5Final(pFileNode->mKey.abMd5, &Md5Ctx);
+                //Sha256Final(pFileNode->mKey.abSha2, &Sha256Ctx);
+
+                birdCloseFile(hFile);
+                return;
+            }
+        }
+
+        birdCloseFile(hFile);
+    }
+    else
+        fprintf(stderr, "kDeDup: warning: Failed to open '%ls': %s (%d)\n", pFileNode->wszPath, strerror(errno), errno);
+
+    /*
+     * Hashing failed.  We fake the digests by repeating the node pointer value
+     * again and again, holding a collision with both SHA2 and MD5 with similar
+     * digest pattern for highly unlikely.
+     */
+    ppHash = (PKDUPFILENODE *)&pFileNode->mKey;
+    i = sizeof(pFileNode->mKey) / sizeof(*ppHash);
+    while (i-- > 0)
+        *ppHash++ = pFileNode;
+}
+
+
+/**
+ * Deal with one file, adding it to the tree if it matches the criteria.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param   pFtsEnt         The FTS entry for the file.
+ */
+static int kDupDoFile(FTSENT *pFtsEnt)
+{
+    KU64 cbFile;
+
+    if (g_cVerbosity >= 2)
+        printf("debug: kDupDoFile(%ls)\n", pFtsEnt->fts_wcsaccpath);
+
+    /*
+     * Check that it's within the size range.
+     */
+    cbFile = pFtsEnt->fts_stat.st_size;
+    if (   cbFile >= g_cbMinFileSize
+        && cbFile <= g_cbMaxFileSize)
+    {
+        /*
+         * Start out treating this like a unique file with a unique size, i.e.
+         * allocate all the structures we might possibly need.
+         */
+        size_t        cbAccessPath = (wcslen(pFtsEnt->fts_wcsaccpath) + 1) * sizeof(wchar_t);
+        PKDUPFILENODE pFileNode = (PKDUPFILENODE)kDupAlloc(sizeof(*pFileNode) + cbAccessPath);
+        PKDUPSIZENODE pSizeNode = (PKDUPSIZENODE)kDupAlloc(sizeof(*pSizeNode));
+        if (!pFileNode || !pSizeNode)
+            return 3;
+        g_cFiles++;
+
+        memset(&pFileNode->mKey, 0, sizeof(pFileNode->mKey));
+        pFileNode->pNextHardLink    = NULL;
+        pFileNode->pNextDup         = NULL;
+        pFileNode->pNextGlobalDup   = NULL;
+        pFileNode->uDev             = pFtsEnt->fts_stat.st_dev;
+        pFileNode->uInode           = pFtsEnt->fts_stat.st_ino;
+        memcpy(pFileNode->wszPath, pFtsEnt->fts_wcsaccpath, cbAccessPath);
+
+        pSizeNode->mKey = cbFile;
+        pSizeNode->cFiles = 1;
+        kDupFileTree_Init(&pSizeNode->FileRoot);
+        kDupFileTree_Insert(&pSizeNode->FileRoot, pFileNode);
+
+        /*
+         * Try insert it.
+         */
+        if (kDupSizeTree_Insert(&g_SizeRoot, pSizeNode))
+        { /* unique size, nothing more to do for now. */ }
+        else
+        {
+            /*
+             * More than one file with this size.  We may need to hash the
+             * hash the file we encountered with this size, if this is the
+             * second one.  In that case we should check for hardlinked or
+             * double entering of the file first as well.
+             */
+            kDupFree(pSizeNode);
+            pSizeNode = kDupSizeTree_Get(&g_SizeRoot, cbFile);
+            if (pSizeNode->cFiles == 1)
+            {
+                PKDUPFILENODE pFirstFileNode = pSizeNode->FileRoot.mpRoot;
+                if (   pFirstFileNode->uInode == pFileNode->uInode
+                    && pFileNode->uInode != 0
+                    && pFirstFileNode->uDev   == pFileNode->uDev)
+                {
+                    pFileNode->pNextHardLink      = pFirstFileNode->pNextHardLink;
+                    pFirstFileNode->pNextHardLink = pFileNode;
+                    if (g_cVerbosity >= 1)
+                        printf("Found hardlinked: '%ls' -> '%ls' (ino:%#" KX64_PRI " dev:%#" KX64_PRI ")\n",
+                               pFileNode->wszPath, pFirstFileNode->wszPath, pFileNode->uInode, pFileNode->uDev);
+                    g_cHardlinked += 1;
+                    return 0;
+                }
+
+                kDupHashFile(pFirstFileNode, NULL);
+            }
+            kDupHashFile(pFileNode, pFtsEnt);
+
+            if (kDupFileTree_Insert(&pSizeNode->FileRoot, pFileNode))
+            {  /* great, unique content */ }
+            else
+            {
+                /*
+                 * Duplicate content.  Could be hardlinked or a duplicate entry.
+                 */
+                PKDUPFILENODE pDupFileNode = kDupFileTree_Get(&pSizeNode->FileRoot, pFileNode->mKey);
+                if (   pDupFileNode->uInode == pFileNode->uInode
+                    && pFileNode->uInode != 0
+                    && pDupFileNode->uDev   == pFileNode->uDev)
+                {
+                    pFileNode->pNextHardLink = pDupFileNode->pNextHardLink;
+                    pDupFileNode->pNextHardLink = pFileNode;
+                    if (g_cVerbosity >= 1)
+                        printf("Found hardlinked: '%ls' -> '%ls' (ino:%#" KX64_PRI " dev:%#" KX64_PRI ")\n",
+                               pFileNode->wszPath, pDupFileNode->wszPath, pFileNode->uInode, pFileNode->uDev);
+                    g_cHardlinked += 1;
+                }
+                else
+                {
+                    KBOOL fDifferentDev;
+
+                    /* Genuinly duplicate (or inode numbers are busted). */
+                    if (!pDupFileNode->pNextDup)
+                    {
+                        *g_ppNextDuplicate = pDupFileNode;
+                        g_ppNextDuplicate = &pDupFileNode->pNextGlobalDup;
+                    }
+
+                    /* The list is sorted by device to better facility hardlinking later. */
+                    while (   (fDifferentDev = pDupFileNode->uDev != pFileNode->uDev)
+                           && pDupFileNode->pNextDup)
+                        pDupFileNode = pDupFileNode->pNextDup;
+
+                    pFileNode->pNextDup = pDupFileNode->pNextDup;
+                    pDupFileNode->pNextDup = pFileNode;
+
+                    g_cDuplicates += 1;
+                    if (!fDifferentDev)
+                    {
+                        g_cDuplicatesSaved += 1;
+                        g_cbDuplicatesSaved += pFtsEnt->fts_stat.st_blocks * BIRD_STAT_BLOCK_SIZE;
+                        if (g_cVerbosity >= 1)
+                            printf("Found duplicate: '%ls' <-> '%ls'\n", pFileNode->wszPath, pDupFileNode->wszPath);
+                    }
+                    else if (g_cVerbosity >= 1)
+                        printf("Found duplicate: '%ls' <-> '%ls' (devices differ).\n", pFileNode->wszPath, pDupFileNode->wszPath);
+                }
+            }
+        }
+    }
+    else if (g_cVerbosity >= 1)
+        printf("Skipping '%ls' because %" KU64_PRI " bytes is outside the size range.\n",
+               pFtsEnt->fts_wcsaccpath, cbFile);
+    return 0;
+}
+
+
+/**
+ * Process the non-option arguments, creating the file tree.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param   papwszFtsArgs   The input in argv style.
+ * @param   fFtsOptions     The FTS options.
+ */
+static int kDupReadAll(wchar_t **papwszFtsArgs, unsigned fFtsOptions)
+{
+    int  rcExit = 0;
+    FTS *pFts = nt_fts_openw(papwszFtsArgs, fFtsOptions, NULL /*pfnCompare*/);
+    if (pFts != NULL)
+    {
+        for (;;)
+        {
+            FTSENT *pFtsEnt = nt_fts_read(pFts);
+            if (pFtsEnt)
+            {
+                switch (pFtsEnt->fts_info)
+                {
+                    case FTS_F:
+                        rcExit = kDupDoFile(pFtsEnt);
+                        if (rcExit == 0)
+                            continue;
+                        break;
+
+                    case FTS_D:
+                        if (   g_fRecursive
+                            || pFtsEnt->fts_level == FTS_ROOTLEVEL) /* enumerate dirs on the command line */
+                            continue;
+                        rcExit = nt_fts_set(pFts, pFtsEnt, FTS_SKIP);
+                        if (rcExit == 0)
+                            continue;
+                        fprintf(stderr, "kDeDup: internal error: nt_fts_set failed!\n");
+                        rcExit = 1;
+                        break;
+
+                    case FTS_DP:
+                        /* nothing to do here. */
+                        break;
+
+                    case FTS_SL:
+                        /* The nice thing on windows is that we already know whether it's a
+                           directory or file when encountering the symbolic link. */
+                        if (   (pFtsEnt->fts_stat.st_isdirsymlink ? g_fRecursiveViaSymlinks : g_fFollowSymlinkedFiles)
+                            &&  pFtsEnt->fts_number == 0)
+                        {
+                            pFtsEnt->fts_number++;
+                            rcExit = nt_fts_set(pFts, pFtsEnt, FTS_FOLLOW);
+                            if (rcExit == 0)
+                                continue;
+                            fprintf(stderr, "kDeDup: internal error: nt_fts_set failed!\n");
+                            rcExit = 1;
+                        }
+                        break;
+
+                    case FTS_DC:
+                        fprintf(stderr, "kDeDup: warning: Ignoring cycle '%ls'!\n", pFtsEnt->fts_wcsaccpath);
+                        continue;
+
+                    case FTS_NS:
+                        fprintf(stderr, "kDeDup: warning: Failed to stat '%ls': %s (%d)\n",
+                                pFtsEnt->fts_wcsaccpath, strerror(pFtsEnt->fts_errno), pFtsEnt->fts_errno);
+                        continue;
+
+                    case FTS_DNR:
+                        fprintf(stderr, "kDeDup: error: Error reading directory '%ls': %s (%d)\n",
+                                pFtsEnt->fts_wcsaccpath, strerror(pFtsEnt->fts_errno), pFtsEnt->fts_errno);
+                        rcExit = 1;
+                        break;
+
+                    case FTS_ERR:
+                        fprintf(stderr, "kDeDup: error: Error on '%ls': %s (%d)\n",
+                                pFtsEnt->fts_wcsaccpath, strerror(pFtsEnt->fts_errno), pFtsEnt->fts_errno);
+                        rcExit = 1;
+                        break;
+
+
+                    /* ignore */
+                    case FTS_SLNONE:
+                    case FTS_DEFAULT:
+                        break;
+
+                    /* Not supposed to get here. */
+                    default:
+                        fprintf(stderr, "kDeDup: internal error: fts_info=%d - '%ls'\n",
+                                pFtsEnt->fts_info, pFtsEnt->fts_wcsaccpath);
+                        rcExit = 1;
+                        break;
+                }
+            }
+            else if (errno == 0)
+                break;
+            else
+            {
+                fprintf(stderr, "kDeDup: error: nt_fts_read failed: %s (%d)\n", strerror(errno), errno);
+                rcExit = 1;
+                break;
+            }
+        }
+
+        if (nt_fts_close(pFts) != 0)
+        {
+            fprintf(stderr, "kDeDup: error: nt_fts_close failed: %s (%d)\n", strerror(errno), errno);
+            rcExit = 1;
+        }
+    }
+    else
+    {
+        fprintf(stderr, "kDeDup: error: nt_fts_openw failed: %s (%d)\n", strerror(errno), errno);
+        rcExit = 1;
+    }
+
+    return rcExit;
+}
+
+
+/**
+ * Hardlink duplicates.
+ */
+static int kDupHardlinkDuplicates(void)
+{
+    int           rcExit = 0;
+    PKDUPFILENODE pFileNode;
+    for (pFileNode = g_pDuplicateHead; pFileNode != NULL; pFileNode = pFileNode->pNextGlobalDup)
+    {
+        PKDUPFILENODE pTargetFile = pFileNode;
+        PKDUPFILENODE pDupFile;
+        for (pDupFile = pFileNode->pNextDup; pDupFile != NULL; pDupFile = pDupFile->pNextDup)
+        {
+            /*
+             * Can only hard link if the files are on the same device.
+             */
+            if (pDupFile->uDev == pTargetFile->uDev)
+            {
+                /** @todo compare the files?   */
+                if (1)
+                {
+                    /*
+                     * Start by renaming the orinal file before we try create the hard link.
+                     */
+                    static const wchar_t s_wszBackupSuffix[] = L".kDepBackup";
+                    wchar_t wszBackup[0x4000];
+                    size_t  cwcPath = wcslen(pDupFile->wszPath);
+                    if (cwcPath + sizeof(s_wszBackupSuffix) / sizeof(wchar_t) < K_ELEMENTS(wszBackup))
+                    {
+                        memcpy(wszBackup, pDupFile->wszPath, cwcPath * sizeof(wchar_t));
+                        memcpy(&wszBackup[cwcPath], s_wszBackupSuffix, sizeof(s_wszBackupSuffix));
+                        if (MoveFileW(pDupFile->wszPath, wszBackup))
+                        {
+                            if (CreateHardLinkW(pDupFile->wszPath, pTargetFile->wszPath, NULL))
+                            {
+                                if (birdUnlinkForcedW(wszBackup) == 0)
+                                {
+                                    if (g_cVerbosity >= 1)
+                                        printf("Hardlinked '%ls' to '%ls'.\n", pDupFile->wszPath, pTargetFile->wszPath);
+                                }
+                                else
+                                {
+                                    fprintf(stderr, "kDeDup: fatal: failed to delete '%ls' after hardlinking: %s (%d)\n",
+                                            wszBackup, strerror(errno), errno);
+                                    return 8;
+                                }
+                            }
+                            else
+                            {
+                                fprintf(stderr, "kDeDup: error: failed to hard link '%ls' to '%ls': %u\n",
+                                        pDupFile->wszPath, wszBackup, GetLastError());
+                                if (!MoveFileW(wszBackup, pDupFile->wszPath))
+                                {
+                                    fprintf(stderr, "kDeDup: fatal: Restore back '%ls' to '%ls' after hardlinking faild: %u\n",
+                                            wszBackup, pDupFile->wszPath, GetLastError());
+                                    return 8;
+                                }
+                                rcExit = 1;
+                            }
+                        }
+                        else
+                        {
+                            fprintf(stderr, "kDeDup: error: failed to rename '%ls' to '%ls': %u\n",
+                                    pDupFile->wszPath, wszBackup, GetLastError());
+                            rcExit = 1;
+                        }
+                    }
+                    else
+                    {
+                        fprintf(stderr, "kDeDup: error: too long backup path: '%ls'\n", pDupFile->wszPath);
+                        rcExit = 1;
+                    }
+                }
+            }
+            /*
+             * Since the list is sorted by uDev, we now change the target file.
+             */
+            else
+                pTargetFile = pDupFile;
+        }
+    }
+    return rcExit;
+}
+
+
+static int usage(const char *pszName, FILE *pOut)
+{
+    fprintf(pOut,
+            "usage: %s [options] <path1> [path2 [..]]\n"
+            "usage: %s <-V|--version>\n"
+            "usage: %s <-h|--help>\n"
+            , pszName, pszName, pszName);
+    fprintf(pOut,
+            "\n"
+            "Options:\n"
+            "  -H, --dereference-command-line, --no-dereference-command-line\n"
+            "    Follow symbolic links on the command line.\n"
+            "  -L, --dereference\n"
+            "    Follow symbolic links while scanning directories.\n"
+            "  -P, --no-dereference\n"
+            "    Do not follow symbolic links while scanning directories.\n"
+            "  -r, --recursive\n"
+            "    Recurse into subdirectories, but do not follow links to them.\n"
+            "  -R, --recursive-dereference\n"
+            "    Same as -r, but also follow into symlinked subdirectories.\n"
+            "  -x, --one-file-system\n"
+            "    Do not consider other file system (volumes), either down thru a\n"
+            "    mount point or via a symbolic link to a directory.\n"
+            "  --no-one-file-system, --cross-file-systems\n"
+            "    Reverses the effect of --one-file-system.\n"
+            );
+    return 0;
+}
+
+
+int wmain(int argc, wchar_t **argv)
+{
+    int             rcExit;
+
+    /*
+     * Process parameters.  Position.
+     */
+    wchar_t   **papwszFtsArgs = (wchar_t **)calloc(argc + 1, sizeof(wchar_t *));
+    unsigned    cFtsArgs      = 0;
+    unsigned    fFtsOptions   = FTS_NOCHDIR | FTS_NO_ANSI;
+    KBOOL       fEndOfOptions = K_FALSE;
+    KBOOL       fHardlinkDups = K_FALSE;
+    int         i;
+    for (i = 1; i < argc; i++)
+    {
+        wchar_t *pwszArg = argv[i];
+        if (   *pwszArg == '-'
+            && !fEndOfOptions)
+        {
+            wchar_t wcOpt = *++pwszArg;
+            pwszArg++;
+            if (wcOpt == '-')
+            {
+                /* Translate long options. */
+                if (wcscmp(pwszArg, L"help") == 0)
+                    wcOpt = 'h';
+                else if (wcscmp(pwszArg, L"version") == 0)
+                    wcOpt = 'V';
+                else if (wcscmp(pwszArg, L"recursive") == 0)
+                    wcOpt = 'r';
+                else if (wcscmp(pwszArg, L"dereference-recursive") == 0)
+                    wcOpt = 'R';
+                else if (wcscmp(pwszArg, L"dereference") == 0)
+                    wcOpt = 'L';
+                else if (wcscmp(pwszArg, L"dereference-command-line") == 0)
+                    wcOpt = 'H';
+                else if (wcscmp(pwszArg, L"one-file-system") == 0)
+                    wcOpt = 'x';
+                /* Process long options. */
+                else if (*pwszArg == '\0')
+                {
+                    fEndOfOptions = K_TRUE;
+                    continue;
+                }
+                else if (wcscmp(pwszArg, L"no-recursive") == 0)
+                {
+                    g_fRecursive = g_fRecursiveViaSymlinks = K_FALSE;
+                    continue;
+                }
+                else if (wcscmp(pwszArg, L"no-dereference-command-line") == 0)
+                {
+                    fFtsOptions &= ~FTS_COMFOLLOW;
+                    continue;
+                }
+                else if (   wcscmp(pwszArg, L"no-one-file-system") == 0
+                         || wcscmp(pwszArg, L"cross-file-systems") == 0)
+                {
+                    fFtsOptions &= ~FTS_XDEV;
+                    continue;
+                }
+                else if (wcscmp(pwszArg, L"hardlink-duplicates") == 0)
+                {
+                    fHardlinkDups = K_TRUE;
+                    continue;
+                }
+                else
+                {
+                    fprintf(stderr, "kDeDup: syntax error: Unknown option '--%ls'\n", pwszArg);
+                    return 2;
+                }
+            }
+
+            /* Process one or more short options. */
+            do
+            {
+                switch (wcOpt)
+                {
+                    case 'r': /* --recursive */
+                        g_fRecursive = K_TRUE;
+                        break;
+
+                    case 'R': /* --dereference-recursive */
+                        g_fRecursive = g_fRecursiveViaSymlinks = K_TRUE;
+                        break;
+
+                    case 'H': /* --dereference-command-line */
+                        fFtsOptions |= FTS_COMFOLLOW;
+                        break;
+
+                    case 'L': /* --dereference*/
+                        g_fFollowSymlinkedFiles = K_TRUE;
+                        break;
+
+                    case 'x': /* --one-file-system*/
+                        fFtsOptions |= FTS_XDEV;
+                        break;
+
+                    case 'q':
+                        g_cVerbosity = 0;
+                        break;
+
+                    case 'v':
+                        g_cVerbosity++;
+                        break;
+
+
+                    case 'h':
+                    case '?':
+                        return usage("kDeDup", stdout);
+
+                    case 'V':
+                        printf("0.0.1\n");
+                        return 0;
+
+                    default:
+                        fprintf(stderr, "kDeDup: syntax error: Unknown option '-%lc'\n", wcOpt);
+                        return 2;
+                }
+
+                wcOpt = *pwszArg++;
+            } while (wcOpt != '\0');
+        }
+        else
+        {
+            /*
+             * Append non-option arguments to the FTS argument vector.
+             */
+            papwszFtsArgs[cFtsArgs] = pwszArg;
+            cFtsArgs++;
+        }
+    }
+
+    /*
+     * Do the FTS processing.
+     */
+    kDupSizeTree_Init(&g_SizeRoot);
+    rcExit = kDupReadAll(papwszFtsArgs, fFtsOptions);
+    if (rcExit == 0)
+    {
+        /*
+         * Display the result.
+         */
+        printf("Found %" KU64_PRI " duplicate files, out which %" KU64_PRI " can be hardlinked saving %" KU64_PRI " bytes\n",
+               g_cDuplicates, g_cDuplicatesSaved, g_cbDuplicatesSaved);
+
+        if (fHardlinkDups)
+            rcExit = kDupHardlinkDuplicates();
+    }
+
+    return rcExit;
+}
+
diff --git a/src/kmk/dir-nt-bird.c b/src/kmk/dir-nt-bird.c
index 3724833..69d5369 100644
--- a/src/kmk/dir-nt-bird.c
+++ b/src/kmk/dir-nt-bird.c
@@ -1,4 +1,4 @@
-/* $Id: dir-nt-bird.c 2948 2016-09-20 15:36:07Z bird $ */
+/* $Id: dir-nt-bird.c 3024 2017-01-07 17:46:13Z bird $ */
 /** @file
  * Reimplementation of dir.c for NT using kFsCache.
  *
@@ -250,8 +250,11 @@ static struct dirent *dir_glob_readdir(__ptr_t pvDir)
         /* Don't return missing objects. */
         if (pEntry->bObjType != KFSOBJ_TYPE_MISSING)
         {
-            /* Copy the name that fits.  If neither fits, skip the name. */
-            if (pEntry->cchName < sizeof(pDir->DirEnt.d_name))
+            /* Copy the name that fits, trying to avoid names with spaces.
+               If neither fits, skip the name. */
+            if (   pEntry->cchName < sizeof(pDir->DirEnt.d_name)
+                && (   pEntry->pszShortName == pEntry->pszName
+                    || memchr(pEntry->pszName, ' ', pEntry->cchName) == NULL))
             {
                 pDir->DirEnt.d_namlen = pEntry->cchName;
                 memcpy(pDir->DirEnt.d_name, pEntry->pszName, pEntry->cchName + 1);
@@ -455,7 +458,8 @@ void print_dir_data_base(void)
 }
 
 
-/* duplicated in kWorker.c */
+/* duplicated in kWorker.c
+ * Note! Tries avoid to produce a result with spaces since they aren't supported by makefiles.  */
 void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
 {
     KFSLOOKUPERROR  enmError;
@@ -470,7 +474,7 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
             {
                 PKFSDIR pAncestor;
 
-                pszFull[off + pPathObj->cchName] = '\0';
+                pszFull[offEnd] = '\0';
                 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
 
                 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
@@ -479,9 +483,38 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
                     kHlpAssert(pAncestor != NULL);
                     kHlpAssert(pAncestor->Obj.cchName > 0);
                     pszFull[--off] = '/';
-                    off -= pAncestor->Obj.cchName;
-                    kHlpAssert(pAncestor->Obj.cchParent == off);
-                    memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+                    if (   pAncestor->Obj.pszName == pAncestor->Obj.pszShortName
+                        || memchr(pAncestor->Obj.pszName, ' ', pAncestor->Obj.cchName) == NULL)
+#endif
+                    {
+                        off -= pAncestor->Obj.cchName;
+                        kHlpAssert(pAncestor->Obj.cchParent == off);
+                        memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
+                    }
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+                    else
+                    {
+                        /*
+                         * The long name constains a space, so use the alternative name instead.
+                         * Most likely the alternative name differs in length, usually it's shorter,
+                         * so we have to shift the part of the path we've already assembled
+                         * accordingly.
+                         */
+                        KSSIZE cchDelta = (KSSIZE)pAncestor->Obj.cchShortName - (KSSIZE)pAncestor->Obj.cchName;
+                        if (cchDelta != 0)
+                        {
+                            if ((KSIZE)(offEnd + cchDelta) >= cbFull)
+                                goto l_fallback;
+                            memmove(&pszFull[off + cchDelta], &pszFull[off], offEnd + 1 - off);
+                            off    += cchDelta;
+                            offEnd += cchDelta;
+                        }
+                        off -= pAncestor->Obj.cchShortName;
+                        kHlpAssert(pAncestor->Obj.cchParent == off);
+                        memcpy(&pszFull[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
+                    }
+#endif
                 }
                 kFsCacheObjRelease(g_pFsCache, pPathObj);
                 return;
@@ -491,6 +524,7 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
         {
             if ((size_t)pPathObj->cchName + 1 < cbFull)
             {
+                /* Assume no spaces here. */
                 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
                 pszFull[pPathObj->cchName] = '/';
                 pszFull[pPathObj->cchName + 1] = '\0';
@@ -501,6 +535,9 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
         }
 
         /* do fallback. */
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+l_fallback:
+#endif
         kHlpAssertFailed();
         kFsCacheObjRelease(g_pFsCache, pPathObj);
     }
diff --git a/src/lib/nt/fts-nt.c b/src/lib/nt/fts-nt.c
index 1b815a1..d86b6e2 100644
--- a/src/lib/nt/fts-nt.c
+++ b/src/lib/nt/fts-nt.c
@@ -1,4 +1,4 @@
-/* $Id: fts-nt.c 2992 2016-11-01 22:06:08Z bird $ */
+/* $Id: fts-nt.c 3009 2016-11-07 02:21:59Z bird $ */
 /** @file
  * Source for the NT port of BSD fts.c.
  *
@@ -84,14 +84,20 @@ static char sccsid[] = "@(#)fts.c	8.6 (Berkeley) 8/14/94";
 #include <assert.h>
 #include "nthlp.h"
 #include "ntdir.h"
+#include <stdio.h>//debug
 
-static FTSENT	*fts_alloc(FTS *, char *, size_t);
+static FTSENT	*fts_alloc(FTS *sp, char const *name, size_t namelen, wchar_t const *wcsname, size_t cwcname);
+static FTSENT	*fts_alloc_ansi(FTS *sp, char const *name, size_t namelen);
+static FTSENT	*fts_alloc_utf16(FTS *sp, wchar_t const *wcsname, size_t cwcname);
+static void 	 nt_fts_free_alloc_cache(FTS *sp);
 static FTSENT	*fts_build(FTS *, int);
 static void	 fts_lfree(FTSENT *);
 static void	 fts_load(FTS *, FTSENT *);
 static size_t	 fts_maxarglen(char * const *);
+static size_t	 fts_maxarglenw(wchar_t * const *);
 static void	 fts_padjust(FTS *, FTSENT *);
-static int	 fts_palloc(FTS *, size_t);
+static void	 fts_padjustw(FTS *, FTSENT *);
+static int	 fts_palloc(FTS *, size_t, size_t);
 static FTSENT	*fts_sort(FTS *, FTSENT *, size_t);
 static int	 fts_stat(FTS *, FTSENT *, int, HANDLE);
 static int	 fts_process_stats(FTSENT *, BirdStat_T const *);
@@ -111,9 +117,18 @@ static int	 fts_process_stats(FTSENT *, BirdStat_T const *);
 #define MAXPATHLEN 260
 #define MAX(a, b)  ( (a) >= (b) ? (a) : (b) )
 
-#define AT_SYMLINK_NOFOLLOW 1
-#define fstatat(hDir, pszPath, pStat, fFlags) birdStatAt((hDir), (pszPath), (pStat), (fFlags) != AT_SYMLINK_NOFOLLOW)
-#define FTS_NT_DUMMY_SYMFD_VALUE 	((HANDLE)~(intptr_t)(2)) /* current process */
+/** Enables BirdDir_T reuse. (Saves malloc and free calls.) */
+#define FTS_WITH_DIRHANDLE_REUSE
+/** Enables allocation statistics. */
+//#define FTS_WITH_STATISTICS
+/** Enables FTSENT allocation cache. */
+#define FTS_WITH_ALLOC_CACHE
+/** Number of size buckets for the FTSENT allocation cache. */
+#define FTS_NUM_FREE_BUCKETS    64
+/** Shift for converting size to free bucket index. */
+#define FTS_FREE_BUCKET_SHIFT   4
+/** The FTSENT allocation alignment. */
+#define FTS_ALIGN_FTSENT        (1U << FTS_FREE_BUCKET_SHIFT)
 
 /*
  * Internal representation of an FTS, including extra implementation
@@ -122,11 +137,30 @@ static int	 fts_process_stats(FTSENT *, BirdStat_T const *);
  */
 struct _fts_private {
 	FTS		ftsp_fts;
+#ifdef FTS_WITH_DIRHANDLE_REUSE
+	/** Statically allocate directory handle. */
+	BirdDir_T	dirhandle;
+#endif
+#ifdef FTS_WITH_ALLOC_CACHE
+	/** Number of free entries in the above buckets. */
+	size_t		numfree;
+# ifdef FTS_WITH_STATISTICS
+	size_t		allocs;
+	size_t		hits;
+	size_t		misses;
+# endif
+	/** Free FTSENT buckets (by size).
+	 * This is to avoid hitting the heap, which is a little sluggish on windows. */
+	struct
+	{
+		FTSENT		*head;
+	} freebuckets[FTS_NUM_FREE_BUCKETS];
+#endif
 };
 
 
-FTS * FTSCALL
-nt_fts_open(char * const *argv, int options,
+static FTS * FTSCALL
+nt_fts_open_common(char * const *argv, wchar_t * const *wcsargv, int options,
     int (*compar)(const FTSENT * const *, const FTSENT * const *))
 {
 	struct _fts_private *priv;
@@ -135,6 +169,8 @@ nt_fts_open(char * const *argv, int options,
 	FTSENT *parent, *tmp;
 	size_t len, nitems;
 
+	birdResolveImports();
+
 	/* Options check. */
 	if (options & ~FTS_OPTIONMASK) {
 		errno = EINVAL;
@@ -142,7 +178,7 @@ nt_fts_open(char * const *argv, int options,
 	}
 
 	/* fts_open() requires at least one path */
-	if (*argv == NULL) {
+	if (wcsargv ? *wcsargv == NULL : *argv == NULL) {
 		errno = EINVAL;
 		return (NULL);
 	}
@@ -162,43 +198,70 @@ nt_fts_open(char * const *argv, int options,
 	 * Start out with 1K of path space, and enough, in any case,
 	 * to hold the user's paths.
 	 */
-	if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
+	if (fts_palloc(sp, MAX(argv ? fts_maxarglen(argv) : 1, MAXPATHLEN),
+				   MAX(wcsargv ? fts_maxarglenw(wcsargv) : 1, MAXPATHLEN)) )
 		goto mem1;
 
 	/* Allocate/initialize root's parent. */
-	if ((parent = fts_alloc(sp, "", 0)) == NULL)
+	if ((parent = fts_alloc(sp, NULL, 0, NULL, 0)) == NULL)
 		goto mem2;
 	parent->fts_level = FTS_ROOTPARENTLEVEL;
 
 	/* Allocate/initialize root(s). */
-	for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
+	for (root = NULL, nitems = 0; wcsargv ? *wcsargv != NULL : *argv != NULL; ++nitems) {
 		/* NT: We need to do some small input transformations to make this and
 		       the API user code happy.  1. Lone drive letters get a dot
 		       appended so it won't matter if a slash is appended afterwards.
 		       2. DOS slashes are converted to UNIX ones. */
-		char *slash;
-		len = strlen(*argv);
-		if (len == 2 && argv[0][1] == ':') {
-			char tmp[4];
-			tmp[0] = argv[0][0];
-			tmp[1] = ':';
-			tmp[2] = '.';
-			tmp[3] = '\0';
-			p = fts_alloc(sp, tmp, 3);
+		wchar_t *wcslash;
+
+		if (wcsargv) {
+			len = wcslen(*wcsargv);
+			if (len == 2 && wcsargv[0][1] == ':') {
+				wchar_t wcsdrive[4];
+				wcsdrive[0] = wcsargv[0][0];
+				wcsdrive[1] = ':';
+				wcsdrive[2] = '.';
+				wcsdrive[3] = '\0';
+				p = fts_alloc_utf16(sp, wcsdrive, 3);
+			} else {
+				p = fts_alloc_utf16(sp, *wcsargv, len);
+			}
+			wcsargv++;
 		} else {
-			p = fts_alloc(sp, *argv, len);
+			len = strlen(*argv);
+			if (len == 2 && argv[0][1] == ':') {
+				char szdrive[4];
+				szdrive[0] = argv[0][0];
+				szdrive[1] = ':';
+				szdrive[2] = '.';
+				szdrive[3] = '\0';
+				p = fts_alloc_ansi(sp, szdrive, 3);
+			} else {
+				p = fts_alloc_ansi(sp, *argv, len);
+			}
+			argv++;
 		}
-#if 1 /* bird */
 		if (p != NULL) { /* likely */ } else { goto mem3; }
-#endif
-		slash = strchr(p->fts_name, '\\');
-		while (slash != NULL) {
-			*slash++ = '/';
-			slash = strchr(p->fts_name, '\\');
+
+		wcslash = wcschr(p->fts_wcsname, '\\');
+		while (wcslash != NULL) {
+			*wcslash++ = '/';
+			wcslash = wcschr(p->fts_wcsname, '\\');
+		}
+
+		if (p->fts_name) {
+			char *slash = strchr(p->fts_name, '\\');
+			while (slash != NULL) {
+				*slash++ = '/';
+				slash = strchr(p->fts_name, '\\');
+			}
 		}
+
 		p->fts_level = FTS_ROOTLEVEL;
 		p->fts_parent = parent;
 		p->fts_accpath = p->fts_name;
+		p->fts_wcsaccpath = p->fts_wcsname;
 		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), INVALID_HANDLE_VALUE);
 
 		/* Command-line "." and ".." are real directories. */
@@ -230,26 +293,49 @@ nt_fts_open(char * const *argv, int options,
 	 * finished the node before the root(s); set p->fts_info to FTS_INIT
 	 * so that everything about the "current" node is ignored.
 	 */
-	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
+	if ((sp->fts_cur = fts_alloc(sp, NULL, 0, NULL, 0)) == NULL)
 		goto mem3;
 	sp->fts_cur->fts_link = root;
 	sp->fts_cur->fts_info = FTS_INIT;
 
 	return (sp);
 
-mem3:	fts_lfree(root);
+mem3:
+	fts_lfree(root);
 	free(parent);
-mem2:	free(sp->fts_path);
-mem1:	free(sp);
+mem2:
+	free(sp->fts_path);
+	free(sp->fts_wcspath);
+mem1:
+	free(sp);
 	return (NULL);
 }
 
 
+FTS * FTSCALL
+nt_fts_open(char * const *argv, int options,
+    int (*compar)(const FTSENT * const *, const FTSENT * const *))
+{
+	return nt_fts_open_common(argv, NULL, options, compar);
+}
+
+
+FTS * FTSCALL
+nt_fts_openw(wchar_t * const *argv, int options,
+    int (*compar)(const FTSENT * const *, const FTSENT * const *))
+{
+	return nt_fts_open_common(NULL, argv, options, compar);
+}
+
+
+/**
+ * Called by fts_read for FTS_ROOTLEVEL entries only.
+ */
 static void
 fts_load(FTS *sp, FTSENT *p)
 {
 	size_t len;
-	char *cp;
+	wchar_t *pwc;
 
 	/*
 	 * Load the stream structure for the next traversal.  Since we don't
@@ -258,17 +344,33 @@ fts_load(FTS *sp, FTSENT *p)
 	 * place and the user can access the first node.  From fts_open it's
 	 * known that the path will fit.
 	 */
-	len = p->fts_pathlen = p->fts_namelen;
-	memmove(sp->fts_path, p->fts_name, len + 1);
-	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
-		len = strlen(++cp);
-		memmove(p->fts_name, cp, len + 1);
-		p->fts_namelen = len;
-	}
-	p->fts_accpath = p->fts_path = sp->fts_path;
+	if (!(sp->fts_options & FTS_NO_ANSI)) {
+		char *cp;
+		len = p->fts_pathlen = p->fts_namelen;
+		memmove(sp->fts_path, p->fts_name, len + 1);
+		cp = strrchr(p->fts_name, '/');
+		if (cp != NULL && (cp != p->fts_name || cp[1])) {
+			len = strlen(++cp);
+			memmove(p->fts_name, cp, len + 1);
+			p->fts_namelen = len;
+		}
+		p->fts_accpath = p->fts_path = sp->fts_path;
+	}
+
+	len = p->fts_cwcpath = p->fts_cwcname;
+	memmove(sp->fts_wcspath, p->fts_wcsname, (len + 1) * sizeof(wchar_t));
+	pwc = wcsrchr(p->fts_wcsname, '/');
+	if (pwc != NULL && (pwc != p->fts_wcsname || pwc[1])) {
+		len = wcslen(++pwc);
+		memmove(p->fts_wcsname, pwc, (len + 1) * sizeof(wchar_t));
+		p->fts_cwcname = len;
+	}
+	p->fts_wcsaccpath = p->fts_wcspath = sp->fts_wcspath;
+
 	sp->fts_dev = p->fts_dev;
 }
 
+
 int FTSCALL
 nt_fts_close(FTS *sp)
 {
@@ -295,52 +397,99 @@ nt_fts_close(FTS *sp)
 	if (sp->fts_array)
 		free(sp->fts_array);
 	free(sp->fts_path);
+	free(sp->fts_wcspath);
+#ifdef FTS_WITH_ALLOC_CACHE
+# ifdef FTS_WITH_STATISTICS
+	{
+		struct _fts_private *priv = (struct _fts_private *)sp;
+		fprintf(stderr, "numfree=%u allocs=%u  hits=%u (%uppt)  misses=%u (%uppt)  other=%u\n",
+			priv->numfree, priv->allocs,
+			priv->hits,   (unsigned)((double)priv->hits   * 1000.0 / priv->allocs),
+			priv->misses, (unsigned)((double)priv->misses * 1000.0 / priv->allocs),
+			priv->allocs - priv->misses - priv->hits);
+        }
+# endif
+#endif
+	nt_fts_free_alloc_cache(sp);
+#ifdef FTS_WITH_DIRHANDLE_REUSE
+	birdDirClose(&((struct _fts_private *)sp)->dirhandle);
+#endif
 
 	/* Free up the stream pointer. */
 	free(sp);
 	return (0);
 }
 
-/*
- * Special case of "/" at the end of the path so that slashes aren't
- * appended which would cause paths to be written as "....//foo".
- */
-#define	NAPPEND(p)							\
-	(p->fts_path[p->fts_pathlen - 1] == '/'				\
-	    ? p->fts_pathlen - 1 : p->fts_pathlen)
 
+/**
+ * Frees a FTSENT structure by way of the allocation cache.
+ */
 static void
-fts_free_entry(FTSENT *tmp)
+fts_free_entry(FTS *sp, FTSENT *tmp)
 {
-    if (tmp != NULL) {
-		if (tmp->fts_dirfd != INVALID_HANDLE_VALUE) {
+	if (tmp != NULL) {
+		struct _fts_private *priv = (struct _fts_private *)sp;
+#ifdef FTS_WITH_ALLOC_CACHE
+		size_t idx;
+#endif
+
+		if (tmp->fts_dirfd == INVALID_HANDLE_VALUE) {
+			/* There are probably more files than directories out there. */
+		} else {
 			birdCloseFile(tmp->fts_dirfd);
 			tmp->fts_dirfd = INVALID_HANDLE_VALUE;
 		}
+
+#ifdef FTS_WITH_ALLOC_CACHE
+		idx = (tmp->fts_alloc_size - sizeof(FTSENT)) >> FTS_FREE_BUCKET_SHIFT;
+		if (idx < FTS_NUM_FREE_BUCKETS) {
+			tmp->fts_link = priv->freebuckets[idx].head;
+			priv->freebuckets[idx].head = tmp;
+		} else {
+			tmp->fts_link = priv->freebuckets[FTS_NUM_FREE_BUCKETS - 1].head;
+			priv->freebuckets[FTS_NUM_FREE_BUCKETS - 1].head = tmp;
+		}
+
+		priv->numfree++;
+#else
 		free(tmp);
-    }
+#endif
+	}
 }
 
+
+/*
+ * Special case of "/" at the end of the path so that slashes aren't
+ * appended which would cause paths to be written as "....//foo".
+ */
+#define	NAPPEND(p)  ( p->fts_pathlen - (p->fts_path[p->fts_pathlen - 1]    ==  '/') )
+#define	NAPPENDW(p) ( p->fts_cwcpath - (p->fts_wcspath[p->fts_cwcpath - 1] == L'/') )
+
 FTSENT * FTSCALL
 nt_fts_read(FTS *sp)
 {
 	FTSENT *p, *tmp;
 	int instr;
-	char *t;
-
-	/* If finished or unrecoverable error, return NULL. */
-	if (sp->fts_cur == NULL || ISSET(FTS_STOP))
-		return (NULL);
+	wchar_t *pwc;
 
 	/* Set current node pointer. */
 	p = sp->fts_cur;
 
+	/* If finished or unrecoverable error, return NULL. */
+	if (p != NULL && !ISSET(FTS_STOP)) {
+		/* likely */
+	} else {
+		return (NULL);
+	}
+
 	/* Save and zero out user instructions. */
 	instr = p->fts_instr;
 	p->fts_instr = FTS_NOINSTR;
 
 	/* Any type of file may be re-visited; re-stat and re-turn. */
-	if (instr == FTS_AGAIN) {
+	if (instr != FTS_AGAIN) {
+		/* likely */
+	} else {
 		p->fts_info = fts_stat(sp, p, 0, INVALID_HANDLE_VALUE);
 		return (p);
 	}
@@ -351,15 +500,15 @@ nt_fts_read(FTS *sp)
 	 * keep a pointer to current location.  If unable to get that
 	 * pointer, follow fails.
 	 *
-	 * NT: Since we don't change directory, we just set fts_symfd to a
-	 *     placeholder value handle value here in case a API client
-	 *     checks it. Ditto FTS_SYMFOLLOW.
+	 * NT: Since we don't change directory, we just set FTS_SYMFOLLOW
+	 *     here in case a API client checks it.
 	 */
-	if (instr == FTS_FOLLOW &&
-	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+	if (   instr != FTS_FOLLOW
+	    || (p->fts_info != FTS_SL && p->fts_info != FTS_SLNONE)) {
+	    /* likely */
+	} else {
 		p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
 		if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
-			p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
 			p->fts_flags |= FTS_SYMFOLLOW;
 		}
 		return (p);
@@ -368,11 +517,8 @@ nt_fts_read(FTS *sp)
 	/* Directory in pre-order. */
 	if (p->fts_info == FTS_D) {
 		/* If skipped or crossed mount point, do post-order visit. */
-		if (instr == FTS_SKIP ||
-		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
-			if (p->fts_flags & FTS_SYMFOLLOW) {
-				p->fts_symfd = INVALID_HANDLE_VALUE;
-			}
+		if (  instr == FTS_SKIP
+		    || (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
 			if (sp->fts_child) {
 				fts_lfree(sp->fts_child);
 				sp->fts_child = NULL;
@@ -400,15 +546,20 @@ nt_fts_read(FTS *sp)
 		 * If haven't read do so.  If the read fails, fts_build sets
 		 * FTS_STOP or the fts_info field of the node.
 		 */
-		if (sp->fts_child != NULL) {
-			/* nothing to do */
-		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
-			if (ISSET(FTS_STOP))
-				return (NULL);
-			return (p);
+		if (sp->fts_child == NULL) {
+			p = fts_build(sp, BREAD);
+			if (p != NULL) {
+			    /* likely */
+			} else {
+			    if (ISSET(FTS_STOP))
+				    return (NULL);
+			    return sp->fts_cur;
+			}
+
+		} else {
+			p = sp->fts_child;
+			sp->fts_child = NULL;
 		}
-		p = sp->fts_child;
-		sp->fts_child = NULL;
 		goto name;
 	}
 
@@ -419,8 +570,10 @@ next:	tmp = p;
 		 * If reached the top, return to the original directory (or
 		 * the root of the tree), and load the paths for the next root.
 		 */
-		if (p->fts_level == FTS_ROOTLEVEL) {
-			fts_free_entry(tmp);
+		if (p->fts_level != FTS_ROOTLEVEL) {
+			/* likely */
+		} else {
+			fts_free_entry(sp, tmp);
 			fts_load(sp, p);
 			return (sp->fts_cur = p);
 		}
@@ -430,61 +583,70 @@ next:	tmp = p;
 		 * ignore.  If followed, get a file descriptor so we can
 		 * get back if necessary.
 		 */
-		if (p->fts_instr == FTS_SKIP) {
-			fts_free_entry(tmp);
+		if (p->fts_instr != FTS_SKIP) {
+			/* likely */
+		} else {
+			fts_free_entry(sp, tmp);
 			goto next;
 		}
-		if (p->fts_instr == FTS_FOLLOW) {
+		if (p->fts_instr != FTS_FOLLOW) {
+			/* likely */
+		} else {
 			p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
-			/* NT: See above regarding fts_symfd. */
-			if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
-				p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
+			/* NT: See above regarding fts_flags. */
+			if (p->fts_info == FTS_D) {
 				p->fts_flags |= FTS_SYMFOLLOW;
 			}
 			p->fts_instr = FTS_NOINSTR;
 		}
 
-		fts_free_entry(tmp);
+		fts_free_entry(sp, tmp);
 
-name:		t = sp->fts_path + NAPPEND(p->fts_parent);
-		*t++ = '/';
-		memmove(t, p->fts_name, p->fts_namelen + 1);
+name:
+		if (!(sp->fts_options & FTS_NO_ANSI)) {
+			char *t = sp->fts_path + NAPPEND(p->fts_parent);
+			*t++ = '/';
+			memmove(t, p->fts_name, p->fts_namelen + 1);
+		}
+		pwc = sp->fts_wcspath + NAPPENDW(p->fts_parent);
+		*pwc++ = '/';
+		memmove(pwc, p->fts_wcsname, (p->fts_cwcname + 1) * sizeof(wchar_t));
 		return (sp->fts_cur = p);
 	}
 
 	/* Move up to the parent node. */
 	p = tmp->fts_parent;
 
-	if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+	if (p->fts_level != FTS_ROOTPARENTLEVEL) {
+		/* likely */
+	} else {
 		/*
 		 * Done; free everything up and set errno to 0 so the user
 		 * can distinguish between error and EOF.
 		 */
-		fts_free_entry(tmp);
-		fts_free_entry(p);
+		fts_free_entry(sp, tmp);
+		fts_free_entry(sp, p);
 		errno = 0;
 		return (sp->fts_cur = NULL);
 	}
 
 	/* NUL terminate the pathname. */
-	sp->fts_path[p->fts_pathlen] = '\0';
+	if (!(sp->fts_options & FTS_NO_ANSI))
+		sp->fts_path[p->fts_pathlen] = '\0';
+	sp->fts_wcspath[ p->fts_cwcpath] = '\0';
 
 	/*
 	 * Return to the parent directory.  If at a root node or came through
 	 * a symlink, go back through the file descriptor.  Otherwise, cd up
 	 * one directory.
 	 *
-	 * NT: We're doing no fchdir, but we need to close the directory handle
-	 *     and clear fts_symfd now.
+	 * NT: We're doing no fchdir, but we need to close the directory handle.
 	 */
-	if (p->fts_flags & FTS_SYMFOLLOW) {
-		p->fts_symfd = INVALID_HANDLE_VALUE;
-	}
-    if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
+	if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
 		birdCloseFile(p->fts_dirfd);
 		p->fts_dirfd = INVALID_HANDLE_VALUE;
-    }
-    fts_free_entry(tmp);
+	}
+	fts_free_entry(sp, tmp);
 	p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
 	return (sp->fts_cur = p);
 }
@@ -609,15 +771,13 @@ nt_fts_set_clientptr(FTS *sp, void *clientptr)
 static FTSENT *
 fts_build(FTS *sp, int type)
 {
-	BirdDirEntry_T *dp;
-	FTSENT *p, *head;
-	FTSENT *cur, *tail;
+	BirdDirEntryW_T *dp;
+	FTSENT *p, *cur;
+	FTSENT * volatile head,* volatile *tailp; /* volatile is to prevent aliasing trouble */
 	DIR *dirp;
-	void *oldaddr;
-	char *cp;
-	int saved_errno, doadjust;
+	int saved_errno, doadjust, doadjust_utf16;
 	long level;
-	size_t dnamlen, len, maxlen, nitems;
+	size_t len, cwcdir, maxlen, cwcmax, nitems;
 	unsigned fDirOpenFlags;
 
 	/* Set current node pointer. */
@@ -633,31 +793,40 @@ fts_build(FTS *sp, int type)
 	 *     members of the directory.
 	 */
 	fDirOpenFlags = BIRDDIR_F_EXTRA_INFO | BIRDDIR_F_KEEP_HANDLE;
-    if (cur->fts_dirfd == INVALID_HANDLE_VALUE) {
+	if (cur->fts_dirfd == INVALID_HANDLE_VALUE) {
 		if (cur->fts_parent->fts_dirfd != INVALID_HANDLE_VALUE) {
 			/* (This works fine for symlinks too, since we follow them.) */
-			cur->fts_dirfd = birdOpenFileEx(cur->fts_parent->fts_dirfd,
-											cur->fts_name,
-											FILE_READ_DATA | SYNCHRONIZE,
-											FILE_ATTRIBUTE_NORMAL,
-											FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-											FILE_OPEN,
-											FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
-											OBJ_CASE_INSENSITIVE);
+			cur->fts_dirfd = birdOpenFileExW(cur->fts_parent->fts_dirfd,
+			                                 cur->fts_wcsname,
+			                                 FILE_READ_DATA | SYNCHRONIZE,
+			                                 FILE_ATTRIBUTE_NORMAL,
+			                                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			                                 FILE_OPEN,
+			                                 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+			                                 OBJ_CASE_INSENSITIVE);
 		} else {
-			cur->fts_dirfd = birdOpenFile(cur->fts_accpath,
-										  FILE_READ_DATA | SYNCHRONIZE,
-										  FILE_ATTRIBUTE_NORMAL,
-										  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-										  FILE_OPEN,
-										  FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
-										  OBJ_CASE_INSENSITIVE);
+			cur->fts_dirfd = birdOpenFileW(cur->fts_wcsaccpath,
+			                               FILE_READ_DATA | SYNCHRONIZE,
+			                               FILE_ATTRIBUTE_NORMAL,
+			                               FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			                               FILE_OPEN,
+			                               FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+			                               OBJ_CASE_INSENSITIVE);
 		}
-    } else {
+		if (cur->fts_dirfd != INVALID_HANDLE_VALUE) { /* likely */ }
+		else goto l_open_err;
+
+	} else {
 		fDirOpenFlags |= BIRDDIR_F_RESTART_SCAN;
 	}
+#ifdef FTS_WITH_DIRHANDLE_REUSE
+	dirp = birdDirOpenFromHandleWithReuse(&((struct _fts_private *)sp)->dirhandle, cur->fts_dirfd, NULL, 
+					      fDirOpenFlags | BIRDDIR_F_STATIC_ALLOC);
+#else
 	dirp = birdDirOpenFromHandle(cur->fts_dirfd, NULL, fDirOpenFlags);
+#endif
 	if (dirp == NULL) {
+l_open_err:
 		if (type == BREAD) {
 			cur->fts_info = FTS_DNR;
 			cur->fts_errno = errno;
@@ -671,40 +840,62 @@ fts_build(FTS *sp, int type)
 	 * We really wouldn't have to do the maxlen calculations here, we
 	 * could do them in fts_read before returning the path, but it's a
 	 * lot easier here since the length is part of the dirent structure.
-	 *
-	 * If not changing directories set a pointer so that can just append
-	 * each new name into the path.
 	 */
-	len = NAPPEND(cur);
-	cp = sp->fts_path + len;
-	*cp++ = '/';
-	len++;
-	maxlen = sp->fts_pathlen - len;
+	if (sp->fts_options & FTS_NO_ANSI) {
+		len = 0;
+		maxlen = 0x10000;
+	} else {
+		len = NAPPEND(cur);
+		len++;
+		maxlen = sp->fts_pathlen - len;
+	}
+
+	cwcdir = NAPPENDW(cur);
+	cwcdir++;
+	cwcmax = sp->fts_cwcpath - len;
 
 	level = cur->fts_level + 1;
 
 	/* Read the directory, attaching each entry to the `link' pointer. */
-	doadjust = 0;
-	for (head = tail = NULL, nitems = 0; dirp && (dp = birdDirRead(dirp));) {
-		dnamlen = dp->d_namlen;
-		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
+	doadjust = doadjust_utf16 = 0;
+	nitems = 0;
+	head = NULL;
+	tailp = &head;
+	while ((dp = birdDirReadW(dirp)) != NULL) {
+		if (ISSET(FTS_SEEDOT) || !ISDOT(dp->d_name))  {
+			/* assume dirs have two or more entries */
+		} else {
 			continue;
+		}
 
-		if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
+		if ((p = fts_alloc_utf16(sp, dp->d_name, dp->d_namlen)) != NULL) {
+			/* likely */
+		} else {
 			goto mem1;
-		if (dnamlen >= maxlen) {	/* include space for NUL */
-			oldaddr = sp->fts_path;
-			if (fts_palloc(sp, dnamlen + len + 1)) {
+		}
+
+		/* include space for NUL */
+		if (p->fts_namelen < maxlen && p->fts_cwcname < cwcmax) {
+		    /* likely */
+		} else {
+			void *oldaddr = sp->fts_path;
+			wchar_t *oldwcspath = sp->fts_wcspath;
+			if (fts_palloc(sp,
+			               p->fts_namelen >= maxlen ? len + p->fts_namelen + 1 : 0,
+			               p->fts_cwcname >= cwcmax ? cwcdir + p->fts_cwcname + 1 : 0)) {
+mem1:
 				/*
 				 * No more memory for path or structures.  Save
 				 * errno, free up the current structure and the
 				 * structures already allocated.
 				 */
-mem1:				saved_errno = errno;
+				saved_errno = errno;
 				if (p)
 					free(p);
 				fts_lfree(head);
+#ifndef FTS_WITH_DIRHANDLE_REUSE
 				birdDirClose(dirp);
+#endif
 				birdCloseFile(cur->fts_dirfd);
 				cur->fts_dirfd = INVALID_HANDLE_VALUE;
 				cur->fts_info = FTS_ERR;
@@ -713,33 +904,31 @@ mem1:				saved_errno = errno;
 				return (NULL);
 			}
 			/* Did realloc() change the pointer? */
-			if (oldaddr != sp->fts_path) {
-				doadjust = 1;
-				if (1 /*ISSET(FTS_NOCHDIR)*/)
-					cp = sp->fts_path + len;
-			}
+			doadjust       |= oldaddr != sp->fts_path;
+			doadjust_utf16 |= oldwcspath != sp->fts_wcspath;
 			maxlen = sp->fts_pathlen - len;
+			cwcmax = sp->fts_cwcpath - cwcdir;
 		}
 
 		p->fts_level = level;
 		p->fts_parent = sp->fts_cur;
-		p->fts_pathlen = len + dnamlen;
+		p->fts_pathlen = len + p->fts_namelen;
+		p->fts_cwcpath = cwcdir + p->fts_cwcname;
 		p->fts_accpath = p->fts_path;
+		p->fts_wcsaccpath = p->fts_wcspath;
 		p->fts_stat = dp->d_stat;
 		p->fts_info = fts_process_stats(p, &dp->d_stat);
 
 		/* We walk in directory order so "ls -f" doesn't get upset. */
 		p->fts_link = NULL;
-		if (head == NULL)
-			head = tail = p;
-		else {
-			tail->fts_link = p;
-			tail = p;
-		}
+		*tailp = p;
+		tailp = &p->fts_link;
 		++nitems;
 	}
 
+#ifndef FTS_WITH_DIRHANDLE_REUSE
 	birdDirClose(dirp);
+#endif
 
 	/*
 	 * If realloc() changed the address of the path, adjust the
@@ -747,11 +936,8 @@ mem1:				saved_errno = errno;
 	 */
 	if (doadjust)
 		fts_padjust(sp, head);
-
-	/*
-	 * Reset the path back to original state.
-	 */
-	sp->fts_path[cur->fts_pathlen] = '\0';
+	if (doadjust_utf16)
+		fts_padjustw(sp, head);
 
 	/* If didn't find anything, return NULL. */
 	if (!nitems) {
@@ -776,12 +962,12 @@ static int
 fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
 {
 	int saved_errno;
-	const char *path;
+	const wchar_t *wcspath;
 
 	if (dfd == INVALID_HANDLE_VALUE) {
-		path = p->fts_accpath;
+		wcspath = p->fts_wcsaccpath;
 	} else {
-		path = p->fts_name;
+		wcspath = p->fts_wcsname;
 	}
 
 	/*
@@ -790,9 +976,9 @@ fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
 	 * fail, set the errno from the stat call.
 	 */
 	if (ISSET(FTS_LOGICAL) || follow) {
-		if (fstatat(dfd, path, &p->fts_stat, 0)) {
+		if (birdStatAtW(dfd, wcspath, &p->fts_stat, 1 /*fFollowLink*/)) {
 			saved_errno = errno;
-			if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
+			if (birdStatAtW(dfd, wcspath, &p->fts_stat, 0 /*fFollowLink*/)) {
 				p->fts_errno = saved_errno;
 				goto err;
 			}
@@ -800,7 +986,7 @@ fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
 			if (S_ISLNK(p->fts_stat.st_mode))
 				return (FTS_SLNONE);
 		}
-	} else if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
+	} else if (birdStatAtW(dfd, wcspath, &p->fts_stat, 0 /*fFollowLink*/)) {
 		p->fts_errno = errno;
 err:		memset(&p->fts_stat, 0, sizeof(struct stat));
 		return (FTS_NS);
@@ -828,7 +1014,7 @@ fts_process_stats(FTSENT *p, BirdStat_T const *sbp)
 		ino = p->fts_ino = sbp->st_ino;
 		p->fts_nlink = sbp->st_nlink;
 
-		if (ISDOT(p->fts_name))
+		if (ISDOT(p->fts_wcsname))
 			return (FTS_DOT);
 
 		/*
@@ -902,43 +1088,197 @@ fts_sort(FTS *sp, FTSENT *head, size_t nitems)
 }
 
 static FTSENT *
-fts_alloc(FTS *sp, char *name, size_t namelen)
+fts_alloc(FTS *sp, char const *name, size_t namelen, wchar_t const *wcsname, size_t cwcname)
 {
+	struct _fts_private *priv = (struct _fts_private *)sp;
 	FTSENT *p;
 	size_t len;
+#ifdef FTS_WITH_ALLOC_CACHE
+	size_t aligned;
+	size_t idx;
+#endif
 
-	struct ftsent_withstat {
-		FTSENT	ent;
-		struct	stat statbuf;
-	};
-
+#if defined(FTS_WITH_STATISTICS) && defined(FTS_WITH_ALLOC_CACHE)
+	priv->allocs++;
+#endif
 	/*
 	 * The file name is a variable length array.  Allocate the FTSENT
 	 * structure and the file name.
 	 */
-	len = sizeof(FTSENT) + namelen + 1;
-	if ((p = malloc(len)) == NULL)
-		return (NULL);
+	len = sizeof(FTSENT) + (cwcname + 1) * sizeof(wchar_t);
+	if (!(sp->fts_options & FTS_NO_ANSI))
+		len += namelen + 1;
 
-	p->fts_name = (char *)(p + 1);
-	p->fts_statp = &p->fts_stat;
+	/*
+	 * To speed things up we cache entries.  This code is a little insane,
+	 * but that's preferable to slow code.
+	 */
+#ifdef FTS_WITH_ALLOC_CACHE
+	aligned = (len + FTS_ALIGN_FTSENT + 1) & ~(size_t)(FTS_ALIGN_FTSENT - 1);
+	idx     = ((aligned - sizeof(FTSENT)) >> FTS_FREE_BUCKET_SHIFT);
+	if (   idx < FTS_NUM_FREE_BUCKETS
+	    && (p = priv->freebuckets[idx].head)
+	    && p->fts_alloc_size >= len) {
+		priv->freebuckets[idx].head = p->fts_link;
+		priv->numfree--;
+# ifdef FTS_WITH_STATISTICS
+		priv->hits++;
+# endif
+
+	} else {
+# ifdef FTS_WITH_STATISTICS
+		priv->misses++;
+# endif
+		p = malloc(aligned);
+		if (p) {
+			p->fts_alloc_size = (unsigned)aligned;
+		} else {
+			nt_fts_free_alloc_cache(sp);
+			p = malloc(len);
+			if (!p)
+				return NULL;
+			p->fts_alloc_size = (unsigned)len;
+		}
+	}
+#else  /* !FTS_WITH_ALLOC_CACHE */
+	p = malloc(len);
+	if (p) {
+		p->fts_alloc_size = (unsigned)len;
+	} else {
+		return NULL;
+	}
+#endif /* !FTS_WITH_ALLOC_CACHE */
+
+	/* Copy the names and guarantee NUL termination. */
+	p->fts_wcsname = (wchar_t *)(p + 1);
+	memcpy(p->fts_wcsname, wcsname, cwcname * sizeof(wchar_t));
+	p->fts_wcsname[cwcname] = '\0';
+	p->fts_cwcname = cwcname;
+	if (!(sp->fts_options & FTS_NO_ANSI)) {
+		p->fts_name = (char *)(p->fts_wcsname + cwcname + 1);
+		memcpy(p->fts_name, name, namelen);
+		p->fts_name[namelen] = '\0';
+		p->fts_namelen = namelen;
+	} else {
+		p->fts_name = NULL;
+		p->fts_namelen = 0;
+	}
 
-	/* Copy the name and guarantee NUL termination. */
-	memcpy(p->fts_name, name, namelen);
-	p->fts_name[namelen] = '\0';
-	p->fts_namelen = namelen;
 	p->fts_path = sp->fts_path;
+	p->fts_wcspath = sp->fts_wcspath;
+	p->fts_statp = &p->fts_stat;
 	p->fts_errno = 0;
 	p->fts_flags = 0;
 	p->fts_instr = FTS_NOINSTR;
 	p->fts_number = 0;
 	p->fts_pointer = NULL;
 	p->fts_fts = sp;
-	p->fts_symfd = INVALID_HANDLE_VALUE;
 	p->fts_dirfd = INVALID_HANDLE_VALUE;
 	return (p);
 }
 
+
+/**
+ * Converts the ANSI name to UTF-16 and calls fts_alloc.
+ *
+ * @returns Pointer to allocated and mostly initialized FTSENT structure on
+ *          success.  NULL on failure, caller needs to record it.
+ * @param   sp                  Pointer to FTS instance.
+ * @param   name                The ANSI name.
+ * @param   namelen             The ANSI name length.
+ */
+static FTSENT *
+fts_alloc_ansi(FTS *sp, char const *name, size_t namelen)
+{
+	MY_UNICODE_STRING UniStr;
+	MY_ANSI_STRING AnsiStr;
+	MY_NTSTATUS rcNt;
+	FTSENT *pRet;
+
+	UniStr.Buffer = NULL;
+	UniStr.MaximumLength = UniStr.Length = 0;
+
+	AnsiStr.Buffer = (char *)name;
+	AnsiStr.Length = AnsiStr.MaximumLength = (USHORT)namelen;
+
+	rcNt = g_pfnRtlAnsiStringToUnicodeString(&UniStr, &AnsiStr, TRUE /*fAllocate*/);
+	if (NT_SUCCESS(rcNt)) {
+		pRet = fts_alloc(sp, name, namelen, UniStr.Buffer, UniStr.Length / sizeof(wchar_t));
+		HeapFree(GetProcessHeap(), 0, UniStr.Buffer);
+	} else {
+		pRet = NULL;
+	}
+	return pRet;
+}
+
+
+/**
+ * Converts the UTF-16 name to ANSI (if necessary) and calls fts_alloc.
+ *
+ * @returns Pointer to allocated and mostly initialized FTSENT structure on
+ *          success.  NULL on failure, caller needs to record it.
+ * @param   sp                  Pointer to the FTS instance.
+ * @param   wcsname             The UTF-16 name.
+ * @param   cwcname		The UTF-16 name length.
+ */
+static FTSENT *
+fts_alloc_utf16(FTS *sp, wchar_t const *wcsname, size_t cwcname)
+{
+	FTSENT *pRet;
+
+	if (sp->fts_options & FTS_NO_ANSI) {
+		pRet = fts_alloc(sp, NULL, 0, wcsname, cwcname);
+	} else {
+		MY_UNICODE_STRING UniStr;
+		MY_ANSI_STRING AnsiStr;
+		MY_NTSTATUS rcNt;
+
+		UniStr.Buffer = (wchar_t *)wcsname;
+		UniStr.MaximumLength = UniStr.Length = (USHORT)(cwcname * sizeof(wchar_t));
+
+		AnsiStr.Buffer = NULL;
+		AnsiStr.Length = AnsiStr.MaximumLength = 0;
+
+		rcNt = g_pfnRtlUnicodeStringToAnsiString(&AnsiStr, &UniStr, TRUE /*fAllocate*/);
+		if (NT_SUCCESS(rcNt)) {
+			pRet = fts_alloc(sp, AnsiStr.Buffer, AnsiStr.Length, wcsname, cwcname);
+			HeapFree(GetProcessHeap(), 0, AnsiStr.Buffer);
+		} else {
+			pRet = NULL;
+		}
+	}
+	return pRet;
+}
+
+
+/**
+ * Frees up the FTSENT allocation cache.
+ *
+ * Used by nt_fts_close, but also called by fts_alloc on alloc failure.
+ *
+ * @param   sp                  Pointer to the FTS instance.
+ */
+static void nt_fts_free_alloc_cache(FTS *sp)
+{
+#ifdef FTS_WITH_ALLOC_CACHE
+	struct _fts_private *priv = (struct _fts_private *)sp;
+	unsigned i = K_ELEMENTS(priv->freebuckets);
+	while (i-- > 0) {
+		FTSENT *cur = priv->freebuckets[i].head;
+		priv->freebuckets[i].head = NULL;
+		while (cur) {
+			FTSENT *freeit = cur;
+			cur = cur->fts_link;
+			free(freeit);
+		}
+	}
+	priv->numfree = 0;
+#else
+	(void)sp;
+#endif
+}
+
+
 static void
 fts_lfree(FTSENT *head)
 {
@@ -959,19 +1299,42 @@ fts_lfree(FTSENT *head)
  * plus 256 bytes so don't realloc the path 2 bytes at a time.
  */
 static int
-fts_palloc(FTS *sp, size_t more)
+fts_palloc(FTS *sp, size_t more, size_t cwcmore)
 {
 	void *ptr;
 
-	sp->fts_pathlen += more + 256;
-	ptr = realloc(sp->fts_path, sp->fts_pathlen);
-	if (ptr) {
-		/*likely */
-	} else {
-		free(sp->fts_path);
+	/** @todo Isn't more and cwcmore minimum buffer sizes rather than what needs
+	 *  	  to be added to the buffer??  This code makes no sense when looking at
+	 *  	  the way the caller checks things out! */
+
+	if (more) {
+		sp->fts_pathlen += more + 256;
+		ptr = realloc(sp->fts_path, sp->fts_pathlen);
+		if (ptr) {
+			sp->fts_path = ptr;
+		} else {
+			free(sp->fts_path);
+			sp->fts_path = NULL;
+			free(sp->fts_wcspath);
+			sp->fts_wcspath = NULL;
+			return 1;
+		}
 	}
-	sp->fts_path = ptr;
-	return (ptr == NULL);
+
+	if (cwcmore) {
+		sp->fts_cwcpath += cwcmore + 256;
+		ptr = realloc(sp->fts_wcspath, sp->fts_cwcpath);
+		if (ptr) {
+			sp->fts_wcspath = ptr;
+		} else {
+			free(sp->fts_path);
+			sp->fts_path = NULL;
+			free(sp->fts_wcspath);
+			sp->fts_wcspath = NULL;
+			return 1;
+		}
+	}
+	return 0;
 }
 
 /*
@@ -1002,6 +1365,34 @@ fts_padjust(FTS *sp, FTSENT *head)
 	}
 }
 
+/*
+ * When the UTF-16 path is realloc'd, have to fix all of the pointers in
+ * structures already returned.
+ */
+static void
+fts_padjustw(FTS *sp, FTSENT *head)
+{
+	FTSENT *p;
+	wchar_t *addr = sp->fts_wcspath;
+
+#define	ADJUSTW(p) \
+		do {	\
+			if ((p)->fts_wcsaccpath != (p)->fts_wcsname) \
+				(p)->fts_wcsaccpath = addr + ((p)->fts_wcsaccpath - (p)->fts_wcspath); \
+			(p)->fts_wcspath = addr;						\
+		} while (0)
+
+	/* Adjust the current set of children. */
+	for (p = sp->fts_child; p; p = p->fts_link)
+		ADJUSTW(p);
+
+	/* Adjust the rest of the tree, including the current level. */
+	for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
+		ADJUSTW(p);
+		p = p->fts_link ? p->fts_link : p->fts_parent;
+	}
+}
+
 static size_t
 fts_maxarglen(char * const *argv)
 {
@@ -1013,3 +1404,16 @@ fts_maxarglen(char * const *argv)
 	return (max + 1);
 }
 
+/** Returns the max string size (including term). */
+static size_t
+fts_maxarglenw(wchar_t * const *argv)
+{
+	size_t max = 0;
+	for (; *argv; ++argv) {
+		size_t len = wcslen(*argv);
+		if (len > max)
+			max = len;
+	}
+	return max + 1;
+}
+
diff --git a/src/lib/nt/fts-nt.h b/src/lib/nt/fts-nt.h
index dc990b0..d5fe38e 100644
--- a/src/lib/nt/fts-nt.h
+++ b/src/lib/nt/fts-nt.h
@@ -1,4 +1,4 @@
-/* $Id: fts-nt.h 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: fts-nt.h 3004 2016-11-05 23:18:51Z bird $ */
 /** @file
  * Header for the NT port of BSD fts.h.
  *
@@ -66,6 +66,8 @@ typedef struct {
 	fts_dev_t fts_dev;		/* starting device # */
 	char *fts_path;			/* path for this descent */
 	size_t fts_pathlen;		/* sizeof(path) */
+	wchar_t *fts_wcspath;		/* NT: UTF-16 path for this descent. */
+	size_t fts_cwcpath;		/* NT: size of fts_wcspath buffer */
 	size_t fts_nitems;		/* elements in the sort array */
 	int (FTSCALL *fts_compar)	/* compare function */
 	    (const struct _ftsent * const *, const struct _ftsent * const *);
@@ -80,7 +82,8 @@ typedef struct {
 #if 0 /* No whiteout on NT. */
 #define	FTS_WHITEOUT	0x080		/* return whiteout information */
 #endif
-#define	FTS_OPTIONMASK	0x0ff		/* valid user option mask */
+#define FTS_NO_ANSI     0x40000000      /* NT: No ansi name or access path. */
+#define	FTS_OPTIONMASK	0x400000ff	/* valid user option mask */
 
 #define	FTS_NAMEONLY	0x100		/* (private) child names only */
 #define	FTS_STOP	0x200		/* (private) unrecoverable error */
@@ -96,12 +99,16 @@ typedef struct _ftsent {
 #define	fts_bignum	fts_number	/* XXX non-std, should go away */
 	void *fts_pointer;		/* local address value */
 	char *fts_accpath;		/* access path */
+	wchar_t *fts_wcsaccpath;	/* NT: UTF-16 access path */
 	char *fts_path;			/* root path */
+	wchar_t *fts_wcspath;		/* NT: UTF-16 root path */
 	int fts_errno;			/* errno for this node */
-	fts_fd_t fts_symfd;		/* NT: Normally -1; -2 we followed this symlinked dir */
+	size_t fts_alloc_size;		/* internal - size of the allocation for this entry. */
 	fts_fd_t fts_dirfd;		/* NT: Handle to the directory (NT_FTS_)INVALID_HANDLE_VALUE if not valid */
 	size_t fts_pathlen;		/* strlen(fts_path) */
+	size_t fts_cwcpath;		/* NT: length of fts_wcspath. */
 	size_t fts_namelen;		/* strlen(fts_name) */
+	size_t fts_cwcname;		/* NT: length of fts_wcsname. */
 
 	fts_ino_t fts_ino;		/* inode */
 	fts_dev_t fts_dev;		/* device */
@@ -140,6 +147,7 @@ typedef struct _ftsent {
 
 	struct stat *fts_statp;		/* stat(2) information */
 	char *fts_name;			/* file name */
+	wchar_t *fts_wcsname;		/* NT: UTF-16 file name. */
 	FTS *fts_fts;			/* back pointer to main FTS */
 	BirdStat_T fts_stat;		/* NT: We always got stat info. */
 } FTSENT;
@@ -155,8 +163,8 @@ void	*FTSCALL nt_fts_get_clientptr(FTS *);
 #define	 fts_get_clientptr(fts)	((fts)->fts_clientptr)
 FTS	*FTSCALL nt_fts_get_stream(FTSENT *);
 #define	 fts_get_stream(ftsent)	((ftsent)->fts_fts)
-FTS	*FTSCALL nt_fts_open(char * const *, int,
-	    int (FTSCALL*)(const FTSENT * const *, const FTSENT * const *));
+FTS	*FTSCALL nt_fts_open(char * const *, int, int (FTSCALL*)(const FTSENT * const *, const FTSENT * const *));
+FTS	*FTSCALL nt_fts_openw(wchar_t * const *, int, int (FTSCALL*)(const FTSENT * const *, const FTSENT * const *));
 FTSENT	*FTSCALL nt_fts_read(FTS *);
 int	 FTSCALL nt_fts_set(FTS *, FTSENT *, int);
 void	 FTSCALL nt_fts_set_clientptr(FTS *, void *);
diff --git a/src/lib/nt/kFsCache.c b/src/lib/nt/kFsCache.c
index cb28198..59fa467 100644
--- a/src/lib/nt/kFsCache.c
+++ b/src/lib/nt/kFsCache.c
@@ -1,4 +1,4 @@
-/* $Id: kFsCache.c 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: kFsCache.c 3007 2016-11-06 16:46:43Z bird $ */
 /** @file
  * ntdircache.c - NT directory content cache.
  */
@@ -1328,7 +1328,8 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
         /* Include the structures for better alignment. */
         MY_FILE_ID_BOTH_DIR_INFORMATION     WithId;
         MY_FILE_ID_FULL_DIR_INFORMATION     NoId;
-        /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */
+        /** Buffer padding. We're using a 56KB buffer here to avoid size troubles
+         * with CIFS and such that starts at 64KB. */
         KU8                                 abBuf[56*1024];
     } uBuf;
 
@@ -1567,14 +1568,14 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
 
 #ifdef KFSCACHE_CFG_SHORT_NAMES
                 if (enmInfoClass == enmInfoClassWithId)
-                    birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
+                    birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId);
                 else
-                    birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
+                    birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId);
 #else
                 if (enmInfoClass == enmInfoClassWithId)
-                    birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
+                    birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId);
                 else
-                    birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
+                    birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId);
 #endif
                 pCur->Stats.st_dev = pDir->uDevNo;
                 pCur->fHaveStats   = K_TRUE;
@@ -1600,14 +1601,14 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
                 pDir->iLastWrite = uPtr.pNoId->LastWriteTime.QuadPart;
 #ifdef KFSCACHE_CFG_SHORT_NAMES
                 if (enmInfoClass == enmInfoClassWithId)
-                    birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
+                    birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
                 else
-                    birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
+                    birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
 #else
                 if (enmInfoClass == enmInfoClassWithId)
-                    birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
+                    birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
                 else
-                    birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
+                    birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
 #endif
             }
 
diff --git a/src/lib/nt/ntdir.c b/src/lib/nt/ntdir.c
index 3ea166e..61a58e3 100644
--- a/src/lib/nt/ntdir.c
+++ b/src/lib/nt/ntdir.c
@@ -1,4 +1,4 @@
-/* $Id: ntdir.c 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntdir.c 3007 2016-11-06 16:46:43Z bird $ */
 /** @file
  * MSC + NT opendir, readdir, telldir, seekdir, and closedir.
  */
@@ -114,7 +114,7 @@ BirdDir_T *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *p
  */
 BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigned fFlags)
 {
-    if (!pvReserved)
+    if (!pvReserved && !(fFlags & BIRDDIR_F_STATIC_ALLOC))
     {
         /*
          * Allocate and initialize the directory enum handle.
@@ -137,8 +137,56 @@ BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigne
         }
     }
     else
+    {
+        assert(!(fFlags & BIRDDIR_F_STATIC_ALLOC));
+        assert(pvReserved == NULL);
+    }
+    birdSetErrnoToInvalidArg();
+    return NULL;
+}
+
+
+/**
+ * Special API that takes a preallocated BirdDir_T and can be called again
+ * without involving birdDirClose.
+ *
+ *
+ */
+BirdDir_T *birdDirOpenFromHandleWithReuse(BirdDir_T *pDir, void *pvHandle, const void *pvReserved, unsigned fFlags)
+{
+    if (!pvReserved)
+    {
+        /*
+         * Allocate and initialize the directory enum handle.
+         */
+        if (pDir)
+        {
+            if (pDir->uMagic == BIRD_DIR_MAGIC)
+            {
+                if (   (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE)
+                    && pDir->pvHandle != INVALID_HANDLE_VALUE)
+                    birdCloseFile((HANDLE)pDir->pvHandle);
+            }
+            else
+            {
+                pDir->cbBuf     = 0;
+                pDir->pabBuf    = NULL;
+                pDir->uMagic    = BIRD_DIR_MAGIC;
+            }
+            pDir->pvHandle      = pvHandle;
+            pDir->fFlags        = fFlags;
+            pDir->uDev          = 0;
+            pDir->offPos        = 0;
+            pDir->fHaveData     = 0;
+            pDir->fFirst        = 1;
+            pDir->iInfoClass    = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation;
+            pDir->offBuf        = 0;
+            return pDir;
+        }
+    }
+    else
         assert(pvReserved == NULL);
-    birdSetErrnoToNoMem();
+    birdSetErrnoToInvalidArg();
     return NULL;
 }
 
@@ -173,13 +221,19 @@ static int birdDirReadMore(BirdDir_T *pDir)
         else
             pDir->uDev = 0;
 
-        /*
-         * Allocate a buffer.
-         */
-        pDir->cbBuf = 0x20000;
-        pDir->pabBuf = birdMemAlloc(pDir->cbBuf);
         if (!pDir->pabBuf)
-            return birdSetErrnoToNoMem();
+        {
+            /*
+             * Allocate a buffer.
+             *
+             * Better not exceed 64KB or CIFS may throw a fit.  Also, on win10/64
+             * here there is a noticable speedup when going one byte below 64KB.
+             */
+            pDir->cbBuf = 0xffe0;
+            pDir->pabBuf = birdMemAlloc(pDir->cbBuf);
+            if (!pDir->pabBuf)
+                return birdSetErrnoToNoMem();
+        }
 
         pDir->fFirst = 0;
     }
@@ -238,9 +292,55 @@ static int birdDirCopyNameToEntry(WCHAR const *pwcName, ULONG cbName, BirdDirEnt
 }
 
 
+/**
+ * Deals with mount points.
+ *
+ * @param   pDir        The directory handle.
+ * @param   pInfo       The NT entry information.
+ * @param   pEntryStat  The stats for the mount point directory that needs
+ *                      updating (a d_stat member).
+ */
+static void birdDirUpdateMountPointInfo(BirdDir_T *pDir, MY_FILE_ID_FULL_DIR_INFORMATION *pInfo,
+                                        BirdStat_T *pEntryStat)
+{
+    /*
+     * Try open the root directory of the mount.
+     * (Can't use birdStatAtW here because the name isn't terminated.)
+     */
+    HANDLE              hRoot = INVALID_HANDLE_VALUE;
+    MY_NTSTATUS         rcNt;
+    MY_UNICODE_STRING   Name;
+    Name.Buffer = pInfo->FileName;
+    Name.Length = Name.MaximumLength = (USHORT)pInfo->FileNameLength;
+
+    rcNt = birdOpenFileUniStr((HANDLE)pDir->pvHandle, &Name,
+                              FILE_READ_ATTRIBUTES,
+                              FILE_ATTRIBUTE_NORMAL,
+                              FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                              FILE_OPEN,
+                              FILE_OPEN_FOR_BACKUP_INTENT,
+                              OBJ_CASE_INSENSITIVE,
+                              &hRoot);
+    if (MY_NT_SUCCESS(rcNt))
+    {
+        int         iSavedErrno = errno;
+        BirdStat_T  RootStat;
+        if (birdStatHandle(hRoot, &RootStat, NULL) == 0)
+        {
+            RootStat.st_ismountpoint = 2;
+            *pEntryStat = RootStat;
+        }
+        birdCloseFile(hRoot);
+        errno = iSavedErrno;
+    }
+    /* else: don't mind failures, we've got some info. */
+}
+
 
 /**
  * Implements readdir_r().
+ *
+ * @remarks birdDirReadReentrantW is a copy of this.  Keep them in sync!
  */
 int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T **ppResult)
 {
@@ -315,7 +415,7 @@ int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T
                 pEntry->d_namlen        = 0;
                 if (birdDirCopyNameToEntry(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
                     fSkipEntry = 1;
-                birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo, pEntry->d_name);
+                birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo);
                 pEntry->d_stat.st_dev   = pDir->uDev;
                 switch (pEntry->d_stat.st_mode & S_IFMT)
                 {
@@ -332,6 +432,11 @@ int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T
                         break;
                 }
 
+                if (pEntry->d_stat.st_ismountpoint != 1)
+                { /* likely. */ }
+                else
+                    birdDirUpdateMountPointInfo(pDir, pInfo, &pEntry->d_stat);
+
                 cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength;
                 offNext  = pInfo->NextEntryOffset;
                 break;
@@ -370,7 +475,165 @@ int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T
 BirdDirEntry_T *birdDirRead(BirdDir_T *pDir)
 {
     BirdDirEntry_T *pRet = NULL;
-    birdDirReadReentrant(pDir, &pDir->DirEntry, &pRet);
+    birdDirReadReentrant(pDir, &pDir->u.DirEntry, &pRet);
+    return pRet;
+}
+
+
+static int birdDirCopyNameToEntryW(WCHAR const *pwcName, ULONG cbName, BirdDirEntryW_T *pEntry)
+{
+    ULONG cwcName = cbName / sizeof(wchar_t);
+    if (cwcName < sizeof(pEntry->d_name))
+    {
+        memcpy(pEntry->d_name, pwcName, cbName);
+        pEntry->d_name[cwcName] = '\0';
+        pEntry->d_namlen = (unsigned __int16)cwcName;
+        pEntry->d_reclen = (unsigned __int16)((size_t)&pEntry->d_name[cwcName + 1] - (size_t)pEntry);
+        return 0;
+    }
+    return -1;
+}
+
+
+/**
+ * Implements readdir_r(), UTF-16 version.
+ *
+ * @remarks This is a copy of birdDirReadReentrant where only the name handling
+ *          and entry type differs.  Remember to keep them in sync!
+ */
+int birdDirReadReentrantW(BirdDir_T *pDir, BirdDirEntryW_T *pEntry, BirdDirEntryW_T **ppResult)
+{
+    int fSkipEntry;
+
+    *ppResult = NULL;
+
+    if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
+        return birdSetErrnoToBadFileNo();
+
+    do
+    {
+        ULONG offNext;
+        ULONG cbMinCur;
+
+        /*
+         * Read more?
+         */
+        if (!pDir->fHaveData)
+        {
+            if (birdDirReadMore(pDir) != 0)
+                return -1;
+            if (!pDir->fHaveData)
+                return 0;
+        }
+
+        /*
+         * Convert the NT data to the unixy output structure.
+         */
+        fSkipEntry = 0;
+        switch (pDir->iInfoClass)
+        {
+            case MyFileNamesInformation:
+            {
+                MY_FILE_NAMES_INFORMATION *pInfo = (MY_FILE_NAMES_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
+                if (   pDir->offBuf          >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_NAMES_INFORMATION
+                    || pInfo->FileNameLength >= pDir->cbBuf
+                    || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_NAMES_INFORMATION > pDir->cbBuf)
+                {
+                    fSkipEntry = 1;
+                    pDir->fHaveData = 0;
+                    continue;
+                }
+
+                memset(&pEntry->d_stat, 0, sizeof(pEntry->d_stat));
+                pEntry->d_stat.st_mode  = S_IFMT;
+                pEntry->d_type          = DT_UNKNOWN;
+                pEntry->d_reclen        = 0;
+                pEntry->d_namlen        = 0;
+                if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
+                    fSkipEntry = 1;
+
+                cbMinCur = MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + pInfo->FileNameLength;
+                offNext  = pInfo->NextEntryOffset;
+                break;
+            }
+
+            case MyFileIdFullDirectoryInformation:
+            {
+                MY_FILE_ID_FULL_DIR_INFORMATION *pInfo = (MY_FILE_ID_FULL_DIR_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
+                if (   pDir->offBuf          >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION
+                    || pInfo->FileNameLength >= pDir->cbBuf
+                    || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION > pDir->cbBuf)
+                {
+                    fSkipEntry = 1;
+                    pDir->fHaveData = 0;
+                    continue;
+                }
+
+                pEntry->d_type          = DT_UNKNOWN;
+                pEntry->d_reclen        = 0;
+                pEntry->d_namlen        = 0;
+                if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
+                    fSkipEntry = 1;
+                birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo);
+                pEntry->d_stat.st_dev   = pDir->uDev;
+                switch (pEntry->d_stat.st_mode & S_IFMT)
+                {
+                    case S_IFREG:       pEntry->d_type = DT_REG; break;
+                    case S_IFDIR:       pEntry->d_type = DT_DIR; break;
+                    case S_IFLNK:       pEntry->d_type = DT_LNK; break;
+                    case S_IFIFO:       pEntry->d_type = DT_FIFO; break;
+                    case S_IFCHR:       pEntry->d_type = DT_CHR; break;
+                    default:
+#ifndef NDEBUG
+                        __debugbreak();
+#endif
+                        pEntry->d_type = DT_UNKNOWN;
+                        break;
+                }
+
+                if (pEntry->d_stat.st_ismountpoint != 1)
+                { /* likely. */ }
+                else
+                    birdDirUpdateMountPointInfo(pDir, pInfo, &pEntry->d_stat);
+
+                cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength;
+                offNext  = pInfo->NextEntryOffset;
+                break;
+            }
+
+            default:
+                return birdSetErrnoToBadFileNo();
+        }
+
+        /*
+         * Advance.
+         */
+        if (   offNext >= cbMinCur
+            && offNext < pDir->cbBuf)
+            pDir->offBuf += offNext;
+        else
+        {
+            pDir->fHaveData = 0;
+            pDir->offBuf    = pDir->cbBuf;
+        }
+        pDir->offPos++;
+    } while (fSkipEntry);
+
+
+    /*
+     * Successful return.
+     */
+    *ppResult = pEntry;
+    return 0;
+}
+
+/**
+ * Implements readdir().
+ */
+BirdDirEntryW_T *birdDirReadW(BirdDir_T *pDir)
+{
+    BirdDirEntryW_T *pRet = NULL;
+    birdDirReadReentrantW(pDir, &pDir->u.DirEntryW, &pRet);
     return pRet;
 }
 
@@ -392,7 +655,7 @@ void birdDirSeek(BirdDir_T *pDir, long offDir);
 /**
  * Implements closedir().
  */
-int             birdDirClose(BirdDir_T *pDir)
+int birdDirClose(BirdDir_T *pDir)
 {
     if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
         return birdSetErrnoToBadFileNo();
@@ -403,7 +666,8 @@ int             birdDirClose(BirdDir_T *pDir)
     pDir->pvHandle = (void *)INVALID_HANDLE_VALUE;
     birdMemFree(pDir->pabBuf);
     pDir->pabBuf = NULL;
-    birdMemFree(pDir);
+    if (!(pDir->fFlags & BIRDDIR_F_STATIC_ALLOC))
+        birdMemFree(pDir);
 
     return 0;
 }
diff --git a/src/lib/nt/ntdir.h b/src/lib/nt/ntdir.h
index dd8eee2..c6d6c3c 100644
--- a/src/lib/nt/ntdir.h
+++ b/src/lib/nt/ntdir.h
@@ -1,4 +1,4 @@
-/* $Id: ntdir.h 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntdir.h 3005 2016-11-06 00:07:37Z bird $ */
 /** @file
  * MSC + NT opendir, readdir, closedir and friends.
  */
@@ -49,6 +49,21 @@ typedef struct dirent
     char                d_name[512 - sizeof(BirdStat_T) - 2 - 2 - 1];
 } BirdDirEntry_T;
 
+typedef struct direntw
+{
+    /** Optional stat information.
+     * Only provided if using birdDirOpenExtraInfo(). */
+    BirdStat_T          d_stat;
+    /** The record length. */
+    unsigned __int16    d_reclen;
+    /** The name length (in wchar_t). */
+    unsigned __int16    d_namlen;
+    /** The name type. */
+    unsigned char       d_type;
+    /** The name. */
+    wchar_t             d_name[512 - sizeof(BirdStat_T) - 2 - 2 - 1];
+} BirdDirEntryW_T;
+
 #define d_ino           d_stat.st_ino;
 
 /** @name d_type values.
@@ -74,6 +89,8 @@ typedef struct dirent
 #define BIRDDIR_F_EXTRA_INFO    2U
 /** Whether to restart the scan. */
 #define BIRDDIR_F_RESTART_SCAN  4U
+/** Set if the BirdDir_T structure is statically allocated. */
+#define BIRDDIR_F_STATIC_ALLOC  8U
 /** @} */
 
 typedef struct BirdDir
@@ -103,7 +120,11 @@ typedef struct BirdDir
     unsigned char      *pabBuf;
 
     /** Static directory entry. */
-    BirdDirEntry_T      DirEntry;
+    union
+    {
+        BirdDirEntry_T  DirEntry;
+        BirdDirEntryW_T DirEntryW;
+    } u;
 } BirdDir_T;
 /** Magic value for BirdDir. */
 #define BIRD_DIR_MAGIC      0x19731120
@@ -113,7 +134,9 @@ BirdDir_T      *birdDirOpen(const char *pszPath);
 BirdDir_T      *birdDirOpenExtraInfo(const char *pszPath);
 BirdDir_T      *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *pwszFilter, unsigned fFlags);
 BirdDir_T      *birdDirOpenFromHandle(void *hDir, const void *pvReserved, unsigned fFlags);
+BirdDir_T      *birdDirOpenFromHandleWithReuse(BirdDir_T *pDir, void *pvHandle, const void *pvReserved, unsigned fFlags);
 BirdDirEntry_T *birdDirRead(BirdDir_T *pDir);
+BirdDirEntryW_T *birdDirReadW(BirdDir_T *pDir);
 long            birdDirTell(BirdDir_T *pDir);
 void            birdDirSeek(BirdDir_T *pDir, long offDir);
 int             birdDirClose(BirdDir_T *pDir);
diff --git a/src/lib/nt/nthlp.h b/src/lib/nt/nthlp.h
index 4199cc2..18125e4 100644
--- a/src/lib/nt/nthlp.h
+++ b/src/lib/nt/nthlp.h
@@ -1,4 +1,4 @@
-/* $Id: nthlp.h 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: nthlp.h 3009 2016-11-07 02:21:59Z bird $ */
 /** @file
  * MSC + NT helper functions.
  */
@@ -74,7 +74,9 @@ MY_NTSTATUS birdOpenFileUniStr(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_
 HANDLE      birdOpenCurrentDirectory(void);
 void        birdCloseFile(HANDLE hFile);
 int         birdDosToNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath);
+int         birdDosToNtPathW(const wchar_t *pwszPath, MY_UNICODE_STRING *pNtPath);
 int         birdDosToRelativeNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath);
+int         birdDosToRelativeNtPathW(const wchar_t *pszPath, MY_UNICODE_STRING *pNtPath);
 void        birdFreeNtPath(MY_UNICODE_STRING *pNtPath);
 
 
diff --git a/src/lib/nt/nthlpcore.c b/src/lib/nt/nthlpcore.c
index b4d77eb..82793fe 100644
--- a/src/lib/nt/nthlpcore.c
+++ b/src/lib/nt/nthlpcore.c
@@ -1,4 +1,4 @@
-/* $Id: nthlpcore.c 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: nthlpcore.c 2998 2016-11-05 19:37:35Z bird $ */
 /** @file
  * MSC + NT core helpers functions and globals.
  */
@@ -60,6 +60,7 @@ MY_NTSTATUS (WINAPI *g_pfnNtQueryFullAttributesFile)(MY_OBJECT_ATTRIBUTES *, MY_
 MY_NTSTATUS (WINAPI *g_pfnNtSetInformationFile)(HANDLE, MY_IO_STATUS_BLOCK *, PVOID, LONG, MY_FILE_INFORMATION_CLASS);
 BOOLEAN     (WINAPI *g_pfnRtlDosPathNameToNtPathName_U)(PCWSTR, MY_UNICODE_STRING *, PCWSTR *, MY_RTL_RELATIVE_NAME_U *);
 MY_NTSTATUS (WINAPI *g_pfnRtlAnsiStringToUnicodeString)(MY_UNICODE_STRING *, MY_ANSI_STRING const *, BOOLEAN);
+MY_NTSTATUS (WINAPI *g_pfnRtlUnicodeStringToAnsiString)(MY_ANSI_STRING *, MY_UNICODE_STRING *, BOOLEAN);
 BOOLEAN     (WINAPI *g_pfnRtlEqualUnicodeString)(MY_UNICODE_STRING const *, MY_UNICODE_STRING const *, BOOLEAN);
 BOOLEAN     (WINAPI *g_pfnRtlEqualString)(MY_ANSI_STRING const *, MY_ANSI_STRING const *, BOOLEAN);
 UCHAR       (WINAPI *g_pfnRtlUpperChar)(UCHAR uch);
@@ -67,7 +68,6 @@ ULONG       (WINAPI *g_pfnRtlNtStatusToDosError)(MY_NTSTATUS rcNt);
 VOID        (WINAPI *g_pfnRtlAcquirePebLock)(VOID);
 VOID        (WINAPI *g_pfnRtlReleasePebLock)(VOID);
 
-
 static struct
 {
     FARPROC    *ppfn;
@@ -87,6 +87,7 @@ static struct
     { (FARPROC *)&g_pfnNtSetInformationFile,            "NtSetInformationFile" },
     { (FARPROC *)&g_pfnRtlDosPathNameToNtPathName_U,    "RtlDosPathNameToNtPathName_U" },
     { (FARPROC *)&g_pfnRtlAnsiStringToUnicodeString,    "RtlAnsiStringToUnicodeString" },
+    { (FARPROC *)&g_pfnRtlUnicodeStringToAnsiString,    "RtlUnicodeStringToAnsiString" },
     { (FARPROC *)&g_pfnRtlEqualUnicodeString,           "RtlEqualUnicodeString" },
     { (FARPROC *)&g_pfnRtlEqualString,                  "RtlEqualString" },
     { (FARPROC *)&g_pfnRtlUpperChar,                    "RtlUpperChar" },
diff --git a/src/lib/nt/ntstat.c b/src/lib/nt/ntstat.c
index 8778cda..dff8e5d 100644
--- a/src/lib/nt/ntstat.c
+++ b/src/lib/nt/ntstat.c
@@ -1,4 +1,4 @@
-/* $Id: ntstat.c 2993 2016-11-01 22:41:26Z bird $ */
+/* $Id: ntstat.c 3019 2017-01-07 00:07:08Z bird $ */
 /** @file
  * MSC + NT stat, lstat and fstat.
  */
@@ -57,7 +57,7 @@ static int birdIsExecutableExtension(const char *pszExt)
             return pszExt[1] == 'a' && pszExt[2] == 't' && pszExt[3] == '\0';
 
         case 'v': /* vbs */
-            return pszExt[1] == 'v' && pszExt[2] == 's' && pszExt[3] == '\0';
+            return pszExt[1] == 'b' && pszExt[2] == 's' && pszExt[3] == '\0';
 
         case 'c': /* com and cmd */
             return (pszExt[1] == 'o' && pszExt[2] == 'm' && pszExt[3] == '\0')
@@ -68,43 +68,48 @@ static int birdIsExecutableExtension(const char *pszExt)
 
 static int birdIsFileExecutable(const char *pszName)
 {
-    const char     *pszExt = NULL;
-    char            szExt[8];
-    size_t          cchExt;
-    unsigned        i;
-    char            ch;
+    if (pszName)
+    {
+        const char     *pszExt = NULL;
+        char            szExt[8];
+        size_t          cchExt;
+        unsigned        i;
+        char            ch;
+
+        /* Look for a 3 char extension. */
+        ch = *pszName++;
+        if (!ch)
+            return 0;
 
-    /* Look for a 3 char extension. */
-    ch = *pszName++;
-    if (!ch)
-        return 0;
+        while ((ch = *pszName++) != '\0')
+            if (ch == '.')
+                pszExt = pszName;
 
-    while ((ch = *pszName++) != '\0')
-        if (ch == '.')
-            pszExt = pszName;
+        if (!pszExt)
+            return 0;
+        pszExt++;
+        cchExt = pszName - pszExt;
+        if (cchExt != 3)
+            return 0;
 
-    if (!pszExt)
-        return 0;
-    pszExt++;
-    cchExt = pszName - pszExt;
-    if (cchExt != 3)
-        return 0;
+        /* Copy the extension out and lower case it.  Fail immediately on non-alpha chars. */
+        for (i = 0; i < cchExt; i++, pszExt++)
+        {
+            ch = *pszExt;
+            if (ch >= 'a' && ch <= 'z')
+            { /* likely */ }
+            else if (ch >= 'A' && ch <= 'Z')
+                ch += 'a' - 'A';
+            else
+                return 0;
+            szExt[i] = ch;
+        }
+        szExt[i] = '\0';
 
-    /* Copy the extension out and lower case it.  Fail immediately on non-alpha chars. */
-    for (i = 0; i < cchExt; i++, pszExt++)
-    {
-        ch = *pszExt;
-        if (ch >= 'a' && ch <= 'z')
-        { /* likely */ }
-        else if (ch >= 'A' && ch <= 'Z')
-            ch += 'a' - 'A';
-        else
-            return 0;
-        szExt[i] = ch;
+        return birdIsExecutableExtension(szExt);
     }
-    szExt[i] = '\0';
 
-    return birdIsExecutableExtension(szExt);
+    return 0;
 }
 
 
@@ -147,40 +152,41 @@ static int birdIsFileExecutableW(WCHAR const *pwcName, size_t cwcName)
 }
 
 
-static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const char *pszName,
-                                         const wchar_t *pwszName, size_t cbNameW, __int16 *pfIsDirSymlink)
+static unsigned short birdFileInfoToMode(ULONG fAttribs, ULONG uReparseTag,
+                                         const char *pszName, const wchar_t *pwszName, size_t cbNameW,
+                                         unsigned __int8 *pfIsDirSymlink, unsigned __int8 *pfIsMountPoint)
 {
     unsigned short fMode;
 
     /* File type. */
-    if (  (fAttribs & FILE_ATTRIBUTE_REPARSE_POINT)
-        && hFile != INVALID_HANDLE_VALUE)
+    *pfIsDirSymlink = 0;
+    *pfIsMountPoint = 0;
+    if (!(fAttribs & FILE_ATTRIBUTE_REPARSE_POINT))
     {
-        MY_FILE_ATTRIBUTE_TAG_INFORMATION   TagInfo;
-        MY_IO_STATUS_BLOCK                  Ios;
-        MY_NTSTATUS                         rcNt;
-        Ios.Information = 0;
-        Ios.u.Status    = -1;
-        rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation);
-        if (   !MY_NT_SUCCESS(rcNt)
-            || !MY_NT_SUCCESS(Ios.u.Status)
-            || TagInfo.ReparseTag != IO_REPARSE_TAG_SYMLINK)
-            fAttribs &= ~FILE_ATTRIBUTE_REPARSE_POINT;
-    }
-
-    if (fAttribs & FILE_ATTRIBUTE_REPARSE_POINT)
-    {
-        *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY);
-        fMode = S_IFLNK;
-    }
-    else
-    {
-        *pfIsDirSymlink = 0;
         if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
             fMode = S_IFDIR;
         else
             fMode = S_IFREG;
     }
+    else
+    {
+        switch (uReparseTag)
+        {
+            case IO_REPARSE_TAG_SYMLINK:
+                *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY);
+                fMode = S_IFLNK;
+                break;
+
+            case IO_REPARSE_TAG_MOUNT_POINT:
+                *pfIsMountPoint = 1;
+            default:
+                if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
+                    fMode = S_IFDIR;
+                else
+                    fMode = S_IFREG;
+                break;
+        }
+    }
 
     /* Access mask. */
     fMode |= S_IROTH | S_IRGRP | S_IRUSR;
@@ -188,7 +194,7 @@ static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const cha
         fMode |= S_IWOTH | S_IWGRP | S_IWUSR;
     if (   (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
         || (pwszName
-            ? birdIsFileExecutableW(pwszName, cbNameW)
+            ? birdIsFileExecutableW(pwszName, cbNameW / sizeof(wchar_t))
             : birdIsFileExecutable(pszName)) )
         fMode |= S_IXOTH | S_IXGRP | S_IXUSR;
 
@@ -201,13 +207,12 @@ static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const cha
  *
  * @param   pStat               The stat structure.
  * @param   pBuf                The MY_FILE_ID_FULL_DIR_INFORMATION entry.
- * @param   pszPath             Optionally, the path for X bit checks.
  * @remarks Caller sets st_dev.
  */
-void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf, const char *pszPath)
+void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf)
 {
-    pStat->st_mode          = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
-                                                 pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
+    pStat->st_mode          = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
+                                                 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
     pStat->st_padding0[0]   = 0;
     pStat->st_padding0[1]   = 0;
     pStat->st_size          = pBuf->EndOfFile.QuadPart;
@@ -233,13 +238,12 @@ void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_IN
  *
  * @param   pStat               The stat structure.
  * @param   pBuf                The MY_FILE_ID_BOTH_DIR_INFORMATION entry.
- * @param   pszPath             Optionally, the path for X bit checks.
  * @remarks Caller sets st_dev.
  */
-void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath)
+void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf)
 {
-    pStat->st_mode          = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
-                                                 pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
+    pStat->st_mode          = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
+                                                 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
     pStat->st_padding0[0]   = 0;
     pStat->st_padding0[1]   = 0;
     pStat->st_size          = pBuf->EndOfFile.QuadPart;
@@ -265,13 +269,12 @@ void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_IN
  *
  * @param   pStat               The stat structure.
  * @param   pBuf                The MY_FILE_BOTH_DIR_INFORMATION entry.
- * @param   pszPath             Optionally, the path for X bit checks.
  * @remarks Caller sets st_dev.
  */
-void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath)
+void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf)
 {
-    pStat->st_mode          = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
-                                                 pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
+    pStat->st_mode          = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
+                                                 pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
     pStat->st_padding0[0]   = 0;
     pStat->st_padding0[1]   = 0;
     pStat->st_size          = pBuf->EndOfFile.QuadPart;
@@ -309,9 +312,9 @@ int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const
             rcNt = Ios.u.Status;
         if (MY_NT_SUCCESS(rcNt))
         {
-            pStat->st_mode          = birdFileInfoToMode(hFile, pAll->BasicInformation.FileAttributes, pszPath,
+            pStat->st_mode          = birdFileInfoToMode(pAll->BasicInformation.FileAttributes, pszPath,
                                                          pAll->NameInformation.FileNamepAll->NameInformation.FileNameLength,
-                                                         &pStat->st_dirsymlink);
+                                                         hFile, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
             pStat->st_padding0[0]   = 0;
             pStat->st_padding0[1]   = 0;
             pStat->st_size          = pAll->StandardInformation.EndOfFile.QuadPart;
@@ -353,27 +356,47 @@ int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const
     else
         rc = birdSetErrnoToNoMem();
 #else
-    ULONG                           cbNameInfo = 0;
-    MY_FILE_NAME_INFORMATION       *pNameInfo  = NULL;
-    MY_FILE_STANDARD_INFORMATION    StdInfo;
-    MY_FILE_BASIC_INFORMATION       BasicInfo;
-    MY_FILE_INTERNAL_INFORMATION    InternalInfo;
-    MY_IO_STATUS_BLOCK              Ios;
+    ULONG                               cbNameInfo = 0;
+    MY_FILE_NAME_INFORMATION           *pNameInfo  = NULL;
+    MY_FILE_STANDARD_INFORMATION        StdInfo;
+    MY_FILE_BASIC_INFORMATION           BasicInfo;
+    MY_FILE_INTERNAL_INFORMATION        InternalInfo;
+    MY_FILE_ATTRIBUTE_TAG_INFORMATION   TagInfo;
+    MY_IO_STATUS_BLOCK                  Ios;
 
     Ios.Information = 0;
     Ios.u.Status    = -1;
     rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), MyFileStandardInformation);
     if (MY_NT_SUCCESS(rcNt))
         rcNt = Ios.u.Status;
+
     if (MY_NT_SUCCESS(rcNt))
         rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
     if (MY_NT_SUCCESS(rcNt))
         rcNt = Ios.u.Status;
+
     if (MY_NT_SUCCESS(rcNt))
         rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &InternalInfo, sizeof(InternalInfo), MyFileInternalInformation);
     if (MY_NT_SUCCESS(rcNt))
         rcNt = Ios.u.Status;
-    if (MY_NT_SUCCESS(rcNt) && !pszPath && !pwszPath)
+
+    if (MY_NT_SUCCESS(rcNt))
+    {
+        if (!(BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+            TagInfo.ReparseTag = 0;
+        else
+        {
+            MY_NTSTATUS rcNt2 = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation);
+            if (   !MY_NT_SUCCESS(rcNt2)
+                || !MY_NT_SUCCESS(Ios.u.Status))
+                TagInfo.ReparseTag = 0;
+        }
+    }
+
+    if (   MY_NT_SUCCESS(rcNt)
+        && !pszPath
+        && !pwszPath
+        && !(BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
     {
         cbNameInfo = 0x10020;
         pNameInfo  = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
@@ -384,11 +407,11 @@ int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const
 
     if (MY_NT_SUCCESS(rcNt))
     {
-        pStat->st_mode          = birdFileInfoToMode(hFile, BasicInfo.FileAttributes, pszPath,
+        pStat->st_mode          = birdFileInfoToMode(BasicInfo.FileAttributes, TagInfo.ReparseTag, pszPath,
                                                      pNameInfo ? pNameInfo->FileName : pwszPath,
                                                      pNameInfo ? pNameInfo->FileNameLength
                                                      : pwszPath ? wcslen(pwszPath) * sizeof(wchar_t) : 0,
-                                                     &pStat->st_dirsymlink);
+                                                     &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
         pStat->st_padding0[0]   = 0;
         pStat->st_padding0[1]   = 0;
         pStat->st_size          = StdInfo.EndOfFile.QuadPart;
@@ -505,6 +528,30 @@ static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat
         rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
         birdCloseFile(hFile);
 
+        if (rc || !pStat->st_ismountpoint)
+        { /* very likely */ }
+        else
+        {
+            /*
+             * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
+             * we should return information about what's mounted there rather than the
+             * directory it is mounted at as this is what UNIX does.
+             */
+            hFile = birdOpenFileEx(hRoot, pszPath,
+                                   FILE_READ_ATTRIBUTES,
+                                   FILE_ATTRIBUTE_NORMAL,
+                                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                   FILE_OPEN,
+                                   FILE_OPEN_FOR_BACKUP_INTENT,
+                                   OBJ_CASE_INSENSITIVE);
+            if (hFile != INVALID_HANDLE_VALUE)
+            {
+                rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
+                pStat->st_ismountpoint = 2;
+                birdCloseFile(hFile);
+            }
+        }
+
 #if 0
         {
             static char s_szPrev[256];
@@ -557,7 +604,7 @@ static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat
                     /*
                      * Convert the data.
                      */
-                    birdStatFillFromFileIdFullDirInfo(pStat, pBuf, pszPath);
+                    birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
 
                     /* Get the serial number, reusing the buffer from above. */
                     rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
@@ -596,6 +643,30 @@ static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *
     {
         rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
         birdCloseFile(hFile);
+
+        if (rc || !pStat->st_ismountpoint)
+        { /* very likely */ }
+        else
+        {
+            /*
+             * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
+             * we should return information about what's mounted there rather than the
+             * directory it is mounted at as this is what UNIX does.
+             */
+            hFile = birdOpenFileExW(hRoot, pwszPath,
+                                    FILE_READ_ATTRIBUTES,
+                                    FILE_ATTRIBUTE_NORMAL,
+                                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                    FILE_OPEN,
+                                    FILE_OPEN_FOR_BACKUP_INTENT,
+                                    OBJ_CASE_INSENSITIVE);
+            if (hFile != INVALID_HANDLE_VALUE)
+            {
+                rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
+                pStat->st_ismountpoint = 2;
+                birdCloseFile(hFile);
+            }
+        }
     }
     else
     {
@@ -635,7 +706,7 @@ static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *
                     /*
                      * Convert the data.
                      */
-                    birdStatFillFromFileIdFullDirInfo(pStat, pBuf, NULL);
+                    birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
 
                     /* Get the serial number, reusing the buffer from above. */
                     rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
diff --git a/src/lib/nt/ntstat.h b/src/lib/nt/ntstat.h
index ca18c3b..e400797 100644
--- a/src/lib/nt/ntstat.h
+++ b/src/lib/nt/ntstat.h
@@ -1,4 +1,4 @@
-/* $Id: ntstat.h 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntstat.h 3007 2016-11-06 16:46:43Z bird $ */
 /** @file
  * MSC + NT stat, lstat and fstat implementation and wrappers.
  */
@@ -49,7 +49,8 @@
 typedef struct BirdStat
 {
     unsigned __int16    st_mode;
-    unsigned __int16    st_dirsymlink;
+    unsigned __int8     st_isdirsymlink; /**< Set if directory symlink. */
+    unsigned __int8     st_ismountpoint; /**< Set if mount point; 1 if not followed, 2 if followed (lstat & readdir only). */
     unsigned __int16    st_padding0[2];
     __int64             st_size;
     BirdTimeSpec_T      st_atim;
@@ -86,9 +87,9 @@ int birdStatOnFdJustSize(int fd, __int64 *pcbFile);
 int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink);
 #ifdef ___nt_ntstuff_h
 int  birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath);
-void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf, const char *pszPath);
-void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath);
-void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath);
+void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf);
+void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf);
+void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf);
 MY_NTSTATUS birdQueryVolumeDeviceNumber(HANDLE hFile, MY_FILE_FS_VOLUME_INFORMATION *pVolInfo, size_t cbVolInfo,
                                         unsigned __int64 *puDevNo);
 unsigned __int64 birdVolumeInfoToDeviceNumber(MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo);
diff --git a/src/lib/nt/ntstuff.h b/src/lib/nt/ntstuff.h
index c1a9be3..a70d349 100644
--- a/src/lib/nt/ntstuff.h
+++ b/src/lib/nt/ntstuff.h
@@ -1,4 +1,4 @@
-/* $Id: ntstuff.h 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntstuff.h 3021 2017-01-07 16:52:16Z bird $ */
 /** @file
  * Definitions, types, prototypes and globals for NT.
  */
@@ -518,10 +518,10 @@ typedef struct MY_PARTIAL_TEB
 /** Internal macro for reading uintptr_t sized TEB members. */
 #if K_ARCH == K_ARCH_AMD64
 # define MY_NT_READ_TEB_WORKER(a_offTebMember) ( __readgsqword(a_offTebMember) )
-#elif K_ARCH == K_ARCH_X86
+#elif K_ARCH == K_ARCH_X86_32
 # define MY_NT_READ_TEB_WORKER(a_offTebMember) ( __readfsdword(a_offTebMember) )
 #else
-# else "Port me!"
+# error "Port me!"
 #endif
 /** Get the PEB pointer.
  * @remark Needs stddef.h. */
@@ -555,6 +555,7 @@ extern MY_NTSTATUS (WINAPI * g_pfnNtQueryFullAttributesFile)(MY_OBJECT_ATTRIBUTE
 extern MY_NTSTATUS (WINAPI * g_pfnNtSetInformationFile)(HANDLE, MY_IO_STATUS_BLOCK *, PVOID, LONG, MY_FILE_INFORMATION_CLASS);
 extern BOOLEAN     (WINAPI * g_pfnRtlDosPathNameToNtPathName_U)(PCWSTR, MY_UNICODE_STRING *, PCWSTR *, MY_RTL_RELATIVE_NAME_U *);
 extern MY_NTSTATUS (WINAPI * g_pfnRtlAnsiStringToUnicodeString)(MY_UNICODE_STRING *, MY_ANSI_STRING const *, BOOLEAN);
+extern MY_NTSTATUS (WINAPI * g_pfnRtlUnicodeStringToAnsiString)(MY_ANSI_STRING *, MY_UNICODE_STRING *, BOOLEAN);
 extern BOOLEAN     (WINAPI * g_pfnRtlEqualUnicodeString)(MY_UNICODE_STRING const *pUniStr1, MY_UNICODE_STRING const *pUniStr2,
                                                          BOOLEAN fCaseInsensitive);
 extern BOOLEAN     (WINAPI * g_pfnRtlEqualString)(MY_ANSI_STRING const *pAnsiStr1, MY_ANSI_STRING const *pAnsiStr2,
diff --git a/src/lib/nt/ntunlink.c b/src/lib/nt/ntunlink.c
index 622f47e..50921bd 100644
--- a/src/lib/nt/ntunlink.c
+++ b/src/lib/nt/ntunlink.c
@@ -1,4 +1,4 @@
-/* $Id: ntunlink.c 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: ntunlink.c 3009 2016-11-07 02:21:59Z bird $ */
 /** @file
  * MSC + NT unlink and variations.
  */
@@ -82,7 +82,7 @@ static MY_NTSTATUS birdMakeWritable(MY_UNICODE_STRING *pNtPath)
 }
 
 
-static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyToo, int fFast)
+static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, const wchar_t *pwszFile, int fReadOnlyToo, int fFast)
 {
     MY_UNICODE_STRING   NtPath;
     int                 rc;
@@ -90,9 +90,19 @@ static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyTo
     if (hRoot == INVALID_HANDLE_VALUE)
         hRoot = NULL;
     if (hRoot == NULL)
-        rc = birdDosToNtPath(pszFile, &NtPath);
+    {
+        if (pwszFile)
+            rc = birdDosToNtPathW(pwszFile, &NtPath);
+        else
+            rc = birdDosToNtPath(pszFile, &NtPath);
+    }
     else
-        rc = birdDosToRelativeNtPath(pszFile, &NtPath);
+    {
+        if (pwszFile)
+            rc = birdDosToRelativeNtPathW(pwszFile, &NtPath);
+        else
+            rc = birdDosToRelativeNtPath(pszFile, &NtPath);
+    }
     if (rc == 0)
     {
         MY_NTSTATUS rcNt;
@@ -161,36 +171,72 @@ static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyTo
 
 int birdUnlink(const char *pszFile)
 {
-    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkW(const wchar_t *pwszFile)
+{
+    return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pwszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
 }
 
 
 int birdUnlinkEx(void *hRoot, const char *pszFile)
 {
-    return birdUnlinkInternal((HANDLE)hRoot, pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+    return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkExW(void *hRoot, const wchar_t *pwszFile)
+{
+    return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
 }
 
 
 int birdUnlinkForced(const char *pszFile)
 {
-    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkForcedW(const wchar_t *pwszFile)
+{
+    return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
 }
 
 
 int birdUnlinkForcedEx(void *hRoot, const char *pszFile)
 {
-    return birdUnlinkInternal((HANDLE)hRoot, pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+    return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkForcedExW(void *hRoot, const wchar_t *pwszFile)
+{
+    return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
 }
 
 
 int birdUnlinkForcedFast(const char *pszFile)
 {
-    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+}
+
+
+int birdUnlinkForcedFastW(const wchar_t *pwszFile)
+{
+    return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
 }
 
 
 int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile)
 {
-    return birdUnlinkInternal((HANDLE)hRoot, pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+    return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+}
+
+
+int birdUnlinkForcedFastExW(void *hRoot, const wchar_t *pwszFile)
+{
+    return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
 }
 
diff --git a/src/lib/nt/ntunlink.h b/src/lib/nt/ntunlink.h
index 195ef7f..035a028 100644
--- a/src/lib/nt/ntunlink.h
+++ b/src/lib/nt/ntunlink.h
@@ -1,4 +1,4 @@
-/* $Id: ntunlink.h 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: ntunlink.h 3009 2016-11-07 02:21:59Z bird $ */
 /** @file
  * MSC + NT unlink and variations.
  */
@@ -34,11 +34,17 @@
 #include "nttypes.h"
 
 int birdUnlink(const char *pszFile);
+int birdUnlinkW(const wchar_t *pwszFile);
 int birdUnlinkEx(void *hRoot, const char *pszFile);
+int birdUnlinkExW(void *hRoot, const wchar_t *pwszFile);
 int birdUnlinkForced(const char *pszFile);
+int birdUnlinkForcedW(const wchar_t *pwszFile);
 int birdUnlinkForcedEx(void *hRoot, const char *pszFile);
+int birdUnlinkForcedExW(void *hRoot, const wchar_t *pszFile);
 int birdUnlinkForcedFast(const char *pszFile);
+int birdUnlinkForcedFastW(const wchar_t *pwszFile);
 int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile);
+int birdUnlinkForcedFastExW(void *hRoot, const wchar_t *pwszFile);
 
 #undef  unlink
 #define unlink(a_pszPath)     birdUnlinkForced(a_pszPath)
diff --git a/src/lib/nt/tstNtFts.c b/src/lib/nt/tstNtFts.c
index d2fed4c..8d8136c 100644
--- a/src/lib/nt/tstNtFts.c
+++ b/src/lib/nt/tstNtFts.c
@@ -70,7 +70,6 @@ int main(int argc, char **argv)
             pszArg++;
             if (chOpt == '-')
             {
-                chOpt = *pszArg++;
                 if (!chOpt)
                 {
                     fDoneOptions = 1;
@@ -98,9 +97,11 @@ int main(int argc, char **argv)
                     chOpt = 'q';
                 else if (strcmp(pszArg, "verbose") == 0)
                     chOpt = 'v';
+                else if (strcmp(pszArg, "no-ansi") == 0)
+                    chOpt = 'w';
                 else
                 {
-                    fprintf(stderr, "syntax error: Unknown option: --%s\n", pszArg);
+                    fprintf(stderr, "syntax error: Unknown option: %s (%s)\n", argv[i], pszArg);
                     return 2;
                 }
                 pszArg = "";
@@ -137,6 +138,11 @@ int main(int argc, char **argv)
                     case 'x':
                         fFtsFlags |= FTS_XDEV;
                         break;
+#ifdef FTS_NO_ANSI
+                    case 'w':
+                        fFtsFlags |= FTS_NO_ANSI;
+                        break;
+#endif
                     case 'L':
                         fFollowLinks = 1;
                         break;
@@ -205,7 +211,14 @@ int main(int argc, char **argv)
                 }
 
                 if (cVerbosity > 0)
-                    printf("%8s %s\n", pszState, pFtsEnt->fts_accpath);
+                {
+#ifdef FTS_NO_ANSI
+                    if (fFtsFlags & FTS_NO_ANSI)
+                        printf("%8s %ls\n", pszState, pFtsEnt->fts_wcsaccpath);
+                    else
+#endif
+                        printf("%8s %s\n", pszState, pFtsEnt->fts_accpath);
+                }
                 if (   pFtsEnt->fts_info == FTS_SL
                     && pFtsEnt->fts_number == 0
                     && fFollowLinks
diff --git a/src/lib/nt/tstNtStat.c b/src/lib/nt/tstNtStat.c
index 2a830f1..3823ab7 100644
--- a/src/lib/nt/tstNtStat.c
+++ b/src/lib/nt/tstNtStat.c
@@ -1,4 +1,4 @@
-/* $Id: tstNtStat.c 2702 2013-11-21 00:11:08Z bird $ */
+/* $Id: tstNtStat.c 3007 2016-11-06 16:46:43Z bird $ */
 /** @file
  * Manual lstat/stat testcase.
  */
@@ -130,7 +130,8 @@ int main(int argc, char **argv)
             char szBuf[256];
             printf("%s:\n", argv[i]);
             printf("  st_mode:          %o\n", st.st_mode);
-            printf("  st_dirsymlink:    %d\n", st.st_dirsymlink);
+            printf("  st_isdirsymlink:  %d\n", st.st_isdirsymlink);
+            printf("  st_ismountpoint:  %d\n", st.st_ismountpoint);
             printf("  st_size:          %I64u (%#I64x)\n", st.st_size, st.st_size);
             printf("  st_atim:          %s\n", FormatTimeSpec(szBuf, &st.st_atim));
             printf("  st_mtim:          %s\n", FormatTimeSpec(szBuf, &st.st_mtim));

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-virtualbox/kbuild.git



More information about the Pkg-virtualbox-commits mailing list