diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2570e5be312b2b3da57602d79804d0b28140cda1..c0154c8c202480f38fda2d6c2ef0b9b8b5cf4cb3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -496,6 +496,16 @@ IF(KWSYS_USE_SystemTools)
     KWSYS_CXX_STAT_HAS_ST_MTIM=${KWSYS_CXX_STAT_HAS_ST_MTIM}
     KWSYS_CXX_STAT_HAS_ST_MTIMESPEC=${KWSYS_CXX_STAT_HAS_ST_MTIMESPEC}
     )
+  IF(NOT WIN32)
+    IF(KWSYS_STANDALONE)
+      OPTION(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES "If true, Windows paths will be supported on Unix as well" ON)
+    ENDIF()
+    IF(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
+      SET_PROPERTY(SOURCE SystemTools.cxx testSystemTools.cxx APPEND PROPERTY COMPILE_DEFINITIONS
+        KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES
+        )
+    ENDIF()
+  ENDIF()
 
   # Disable getpwnam for static linux builds since it depends on shared glibc
   GET_PROPERTY(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS)
diff --git a/SystemTools.cxx b/SystemTools.cxx
index 999beb3abef539c49a405d932e429ce3736188ee..38910c8d1eaf716615a78dfd43eef7ebecda2eed 100644
--- a/SystemTools.cxx
+++ b/SystemTools.cxx
@@ -1884,21 +1884,23 @@ static void ConvertVMSToUnix(std::string& path)
 // convert windows slashes to unix slashes
 void SystemTools::ConvertToUnixSlashes(std::string& path)
 {
+  if (path.empty()) {
+    return;
+  }
+
   const char* pathCString = path.c_str();
   bool hasDoubleSlash = false;
 #ifdef __VMS
   ConvertVMSToUnix(path);
 #else
   const char* pos0 = pathCString;
-  const char* pos1 = pathCString + 1;
   for (std::string::size_type pos = 0; *pos0; ++pos) {
-    // make sure we don't convert an escaped space to a unix slash
-    if (*pos0 == '\\' && *pos1 != ' ') {
+    if (*pos0 == '\\') {
       path[pos] = '/';
     }
 
     // Also, reuse the loop to check for slash followed by another slash
-    if (*pos1 == '/' && *(pos1 + 1) == '/' && !hasDoubleSlash) {
+    if (!hasDoubleSlash && *(pos0 + 1) == '/' && *(pos0 + 2) == '/') {
 #ifdef _WIN32
       // However, on windows if the first characters are both slashes,
       // then keep them that way, so that network paths can be handled.
@@ -1911,43 +1913,41 @@ void SystemTools::ConvertToUnixSlashes(std::string& path)
     }
 
     pos0++;
-    pos1++;
   }
 
   if (hasDoubleSlash) {
     SystemTools::ReplaceString(path, "//", "/");
   }
 #endif
+
   // remove any trailing slash
-  if (!path.empty()) {
-    // if there is a tilda ~ then replace it with HOME
-    pathCString = path.c_str();
-    if (pathCString[0] == '~' &&
-        (pathCString[1] == '/' || pathCString[1] == '\0')) {
-      std::string homeEnv;
-      if (SystemTools::GetEnv("HOME", homeEnv)) {
-        path.replace(0, 1, homeEnv);
-      }
+  // if there is a tilda ~ then replace it with HOME
+  pathCString = path.c_str();
+  if (pathCString[0] == '~' &&
+      (pathCString[1] == '/' || pathCString[1] == '\0')) {
+    std::string homeEnv;
+    if (SystemTools::GetEnv("HOME", homeEnv)) {
+      path.replace(0, 1, homeEnv);
     }
+  }
 #ifdef HAVE_GETPWNAM
-    else if (pathCString[0] == '~') {
-      std::string::size_type idx = path.find_first_of("/\0");
-      std::string user = path.substr(1, idx - 1);
-      passwd* pw = getpwnam(user.c_str());
-      if (pw) {
-        path.replace(0, idx, pw->pw_dir);
-      }
+  else if (pathCString[0] == '~') {
+    std::string::size_type idx = path.find_first_of("/\0");
+    std::string user = path.substr(1, idx - 1);
+    passwd* pw = getpwnam(user.c_str());
+    if (pw) {
+      path.replace(0, idx, pw->pw_dir);
     }
+  }
 #endif
-    // remove trailing slash if the path is more than
-    // a single /
-    pathCString = path.c_str();
-    size_t size = path.size();
-    if (size > 1 && *path.rbegin() == '/') {
-      // if it is c:/ then do not remove the trailing slash
-      if (!((size == 3 && pathCString[1] == ':'))) {
-        path.resize(size - 1);
-      }
+  // remove trailing slash if the path is more than
+  // a single /
+  pathCString = path.c_str();
+  size_t size = path.size();
+  if (size > 1 && *path.rbegin() == '/') {
+    // if it is c:/ then do not remove the trailing slash
+    if (!((size == 3 && pathCString[1] == ':'))) {
+      path.resize(size - 1);
     }
   }
 }
@@ -3171,8 +3171,8 @@ void SystemTools::CheckTranslationPath(std::string& path)
 
 static void SystemToolsAppendComponents(
   std::vector<std::string>& out_components,
-  std::vector<std::string>::const_iterator first,
-  std::vector<std::string>::const_iterator last)
+  std::vector<std::string>::iterator first,
+  std::vector<std::string>::iterator last)
 {
   static const std::string up = "..";
   static const std::string cur = ".";
@@ -3182,7 +3182,11 @@ static void SystemToolsAppendComponents(
         out_components.resize(out_components.size() - 1);
       }
     } else if (!i->empty() && *i != cur) {
+#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
+      out_components.push_back(std::move(*i));
+#else
       out_components.push_back(*i);
+#endif
     }
   }
 }
@@ -3190,63 +3194,18 @@ static void SystemToolsAppendComponents(
 std::string SystemTools::CollapseFullPath(const std::string& in_path,
                                           const char* in_base)
 {
-  // Collect the output path components.
-  std::vector<std::string> out_components;
-
-  // Split the input path components.
-  std::vector<std::string> path_components;
-  SystemTools::SplitPath(in_path, path_components);
-
-  // If the input path is relative, start with a base path.
-  if (path_components[0].empty()) {
-    std::vector<std::string> base_components;
-    if (in_base) {
-      // Use the given base path.
-      SystemTools::SplitPath(in_base, base_components);
+  // Use the current working directory as a base path.
+  char buf[2048];
+  const char* res_in_base = in_base;
+  if (!res_in_base) {
+    if (const char* cwd = Getcwd(buf, 2048)) {
+      res_in_base = cwd;
     } else {
-      // Use the current working directory as a base path.
-      char buf[2048];
-      if (const char* cwd = Getcwd(buf, 2048)) {
-        SystemTools::SplitPath(cwd, base_components);
-      } else {
-        base_components.push_back("");
-      }
+      res_in_base = "";
     }
-
-    // Append base path components to the output path.
-    out_components.push_back(base_components[0]);
-    SystemToolsAppendComponents(out_components, base_components.begin() + 1,
-                                base_components.end());
   }
 
-  // Append input path components to the output path.
-  SystemToolsAppendComponents(out_components, path_components.begin(),
-                              path_components.end());
-
-  // Transform the path back to a string.
-  std::string newPath = SystemTools::JoinPath(out_components);
-
-  // Update the translation table with this potentially new path.  I am not
-  // sure why this line is here, it seems really questionable, but yet I
-  // would put good money that if I remove it something will break, basically
-  // from what I can see it created a mapping from the collapsed path, to be
-  // replaced by the input path, which almost completely does the opposite of
-  // this function, the only thing preventing this from happening a lot is
-  // that if the in_path has a .. in it, then it is not added to the
-  // translation table. So for most calls this either does nothing due to the
-  // ..  or it adds a translation between identical paths as nothing was
-  // collapsed, so I am going to try to comment it out, and see what hits the
-  // fan, hopefully quickly.
-  // Commented out line below:
-  // SystemTools::AddTranslationPath(newPath, in_path);
-
-  SystemTools::CheckTranslationPath(newPath);
-#ifdef _WIN32
-  newPath = SystemTools::GetActualCaseForPath(newPath);
-  SystemTools::ConvertToUnixSlashes(newPath);
-#endif
-  // Return the reconstructed path.
-  return newPath;
+  return SystemTools::CollapseFullPath(in_path, std::string(res_in_base));
 }
 
 std::string SystemTools::CollapseFullPath(const std::string& in_path,
@@ -3258,9 +3217,10 @@ std::string SystemTools::CollapseFullPath(const std::string& in_path,
   // Split the input path components.
   std::vector<std::string> path_components;
   SystemTools::SplitPath(in_path, path_components);
+  out_components.reserve(path_components.size());
 
   // If the input path is relative, start with a base path.
-  if (path_components[0].length() == 0) {
+  if (path_components[0].empty()) {
     std::vector<std::string> base_components;
     // Use the given base path.
     SystemTools::SplitPath(in_base, base_components);
@@ -3619,7 +3579,7 @@ std::string SystemTools::JoinPath(
 
   // All remaining components are always separated with a slash.
   while (first != last) {
-    result.append("/");
+    result.push_back('/');
     result.append((*first++));
   }
 
@@ -3715,7 +3675,12 @@ std::string SystemTools::GetFilenamePath(const std::string& filename)
  */
 std::string SystemTools::GetFilenameName(const std::string& filename)
 {
-  std::string::size_type slash_pos = filename.find_last_of("/\\");
+#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
+  const char* separators = "/\\";
+#else
+  char separators = '/';
+#endif
+  std::string::size_type slash_pos = filename.find_last_of(separators);
   if (slash_pos != std::string::npos) {
     return filename.substr(slash_pos + 1);
   } else {
diff --git a/testSystemTools.cxx b/testSystemTools.cxx
index f1321e5da26b69831c1b11eec3ab2037c6d9e104..e436a2b262dc61e8d6d95f3f260291ec0eea3740 100644
--- a/testSystemTools.cxx
+++ b/testSystemTools.cxx
@@ -39,19 +39,19 @@ typedef unsigned short mode_t;
 static const char* toUnixPaths[][2] = {
   { "/usr/local/bin/passwd", "/usr/local/bin/passwd" },
   { "/usr/lo cal/bin/pa sswd", "/usr/lo cal/bin/pa sswd" },
-  { "/usr/lo\\ cal/bin/pa\\ sswd", "/usr/lo\\ cal/bin/pa\\ sswd" },
+  { "/usr/lo\\ cal/bin/pa\\ sswd", "/usr/lo/ cal/bin/pa/ sswd" },
   { "c:/usr/local/bin/passwd", "c:/usr/local/bin/passwd" },
   { "c:/usr/lo cal/bin/pa sswd", "c:/usr/lo cal/bin/pa sswd" },
-  { "c:/usr/lo\\ cal/bin/pa\\ sswd", "c:/usr/lo\\ cal/bin/pa\\ sswd" },
+  { "c:/usr/lo\\ cal/bin/pa\\ sswd", "c:/usr/lo/ cal/bin/pa/ sswd" },
   { "\\usr\\local\\bin\\passwd", "/usr/local/bin/passwd" },
   { "\\usr\\lo cal\\bin\\pa sswd", "/usr/lo cal/bin/pa sswd" },
-  { "\\usr\\lo\\ cal\\bin\\pa\\ sswd", "/usr/lo\\ cal/bin/pa\\ sswd" },
+  { "\\usr\\lo\\ cal\\bin\\pa\\ sswd", "/usr/lo/ cal/bin/pa/ sswd" },
   { "c:\\usr\\local\\bin\\passwd", "c:/usr/local/bin/passwd" },
   { "c:\\usr\\lo cal\\bin\\pa sswd", "c:/usr/lo cal/bin/pa sswd" },
-  { "c:\\usr\\lo\\ cal\\bin\\pa\\ sswd", "c:/usr/lo\\ cal/bin/pa\\ sswd" },
+  { "c:\\usr\\lo\\ cal\\bin\\pa\\ sswd", "c:/usr/lo/ cal/bin/pa/ sswd" },
   { "\\\\usr\\local\\bin\\passwd", "//usr/local/bin/passwd" },
   { "\\\\usr\\lo cal\\bin\\pa sswd", "//usr/lo cal/bin/pa sswd" },
-  { "\\\\usr\\lo\\ cal\\bin\\pa\\ sswd", "//usr/lo\\ cal/bin/pa\\ sswd" },
+  { "\\\\usr\\lo\\ cal\\bin\\pa\\ sswd", "//usr/lo/ cal/bin/pa/ sswd" },
   { KWSYS_NULLPTR, KWSYS_NULLPTR }
 };
 
@@ -700,6 +700,16 @@ static bool CheckCollapsePath()
   bool res = true;
   res &= CheckCollapsePath("/usr/share/*", "/usr/share/*");
   res &= CheckCollapsePath("C:/Windows/*", "C:/Windows/*");
+  res &= CheckCollapsePath("/usr/share/../lib", "/usr/lib");
+  res &= CheckCollapsePath("/usr/share/./lib", "/usr/share/lib");
+  res &= CheckCollapsePath("/usr/share/../../lib", "/lib");
+  res &= CheckCollapsePath("/usr/share/.././../lib", "/lib");
+  res &= CheckCollapsePath("/../lib", "/lib");
+  res &= CheckCollapsePath("/../lib/", "/lib");
+  res &= CheckCollapsePath("/", "/");
+  res &= CheckCollapsePath("C:/", "C:/");
+  res &= CheckCollapsePath("C:/../", "C:/");
+  res &= CheckCollapsePath("C:/../../", "C:/");
   return res;
 }
 
@@ -764,20 +774,26 @@ static bool CheckGetFilenameName()
   const char* windowsFilepath = "C:\\somewhere\\something";
   const char* unixFilepath = "/somewhere/something";
 
-  std::string expectedFilename = "something";
+#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
+  std::string expectedWindowsFilename = "something";
+#else
+  std::string expectedWindowsFilename = "C:\\somewhere\\something";
+#endif
+  std::string expectedUnixFilename = "something";
 
   bool res = true;
   std::string filename = kwsys::SystemTools::GetFilenameName(windowsFilepath);
-  if (filename != expectedFilename) {
+  if (filename != expectedWindowsFilename) {
     std::cerr << "GetFilenameName(" << windowsFilepath << ") yielded "
-              << filename << " instead of " << expectedFilename << std::endl;
+              << filename << " instead of " << expectedWindowsFilename
+              << std::endl;
     res = false;
   }
 
   filename = kwsys::SystemTools::GetFilenameName(unixFilepath);
-  if (filename != expectedFilename) {
+  if (filename != expectedUnixFilename) {
     std::cerr << "GetFilenameName(" << unixFilepath << ") yielded " << filename
-              << " instead of " << expectedFilename << std::endl;
+              << " instead of " << expectedUnixFilename << std::endl;
     res = false;
   }
   return res;