Merge topic 'update-kwsys'

3ef5dab010 cmSystemTools: Simplify using KWSys Status
ec1b6157cb Update CMake code using KWSys to account for Status return values
eef585efaa Merge branch 'upstream-KWSys' into update-kwsys
a790167101 KWSys 2021-04-14 (7ab1c601)

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Ben Boeckel <ben.boeckel@kitware.com>
Merge-request: !6004
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx
index 3e36e8c..ad0a3e2 100644
--- a/Source/CPack/cmCPackSTGZGenerator.cxx
+++ b/Source/CPack/cmCPackSTGZGenerator.cxx
@@ -51,15 +51,16 @@
    * so we must iterate over generated packages.
    */
   for (std::string const& pfn : this->packageFileNames) {
-    retval &= cmSystemTools::SetPermissions(pfn.c_str(),
+    retval &= static_cast<bool>(
+      cmSystemTools::SetPermissions(pfn.c_str(),
 #if defined(_MSC_VER) || defined(__MINGW32__)
-                                            S_IREAD | S_IWRITE | S_IEXEC
+                                    S_IREAD | S_IWRITE | S_IEXEC
 #else
-                                            S_IRUSR | S_IWUSR | S_IXUSR |
-                                              S_IRGRP | S_IWGRP | S_IXGRP |
-                                              S_IROTH | S_IWOTH | S_IXOTH
+                                    S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
+                                      S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH |
+                                      S_IXOTH
 #endif
-    );
+                                    ));
   }
   return retval;
 }
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index ff0b179..d2cad39 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -921,7 +921,7 @@
     }
   }
 
-  return cmSystemTools::RemoveADirectory(directoryPath);
+  return static_cast<bool>(cmSystemTools::RemoveADirectory(directoryPath));
 }
 
 cmDuration cmCTestScriptHandler::GetRemainingTimeAllowed()
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 742e78a..db5cb9c 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -1881,7 +1881,7 @@
   std::string dirName = this->CTest->GetBinaryDir() + "/Testing/Temporary";
 
   cmsys::Directory directory;
-  if (directory.Load(dirName) == 0) {
+  if (!directory.Load(dirName)) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Unable to read the contents of " << dirName << std::endl);
     return;
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 0dfd1bd..643b43f 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -4,7 +4,6 @@
 
 #include <algorithm>
 #include <cctype>
-#include <cerrno>
 #include <chrono>
 #include <cstdio>
 #include <cstdlib>
@@ -2842,9 +2841,10 @@
       cmCTestLog(this, OUTPUT,
                  "Internal ctest changing into directory: " << workDir
                                                             << std::endl);
-      if (cmSystemTools::ChangeDirectory(workDir) != 0) {
+      cmsys::Status status = cmSystemTools::ChangeDirectory(workDir);
+      if (!status) {
         auto msg = "Failed to change working directory to \"" + workDir +
-          "\" : " + std::strerror(errno) + "\n";
+          "\" : " + status.GetString() + "\n";
         cmCTestLog(this, ERROR_MESSAGE, msg);
         return 1;
       }
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index 62bc526..f99592c 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -654,10 +654,10 @@
       if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
           this->IsKeyword(keyIS_NEWER_THAN, *argP1)) {
         int fileIsNewer = 0;
-        bool success = cmSystemTools::FileTimeCompare(
+        cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
           arg->GetValue(), (argP2)->GetValue(), &fileIsNewer);
         this->HandleBinaryOp(
-          (!success || fileIsNewer == 1 || fileIsNewer == 0), reducible, arg,
+          (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), reducible, arg,
           newArgs, argP1, argP2);
       }
 
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index a06ed48..26bdedf 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -2956,9 +2956,12 @@
 
   // Check if copy-on-error is enabled in the arguments.
   if (!completed && arguments.CopyOnError) {
-    completed = cmsys::SystemTools::CopyFileAlways(fileName, newFileName);
-    if (!completed) {
-      result = "Copy failed: " + cmSystemTools::GetLastSystemError();
+    cmsys::Status copied =
+      cmsys::SystemTools::CopyFileAlways(fileName, newFileName);
+    if (copied) {
+      completed = true;
+    } else {
+      result = "Copy failed: " + copied.GetString();
     }
   }
 
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index 6e88e26..568926e 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -116,7 +116,7 @@
   bool success = true;
   std::string const dirName = cmSystemTools::GetFilenamePath(filename);
   if (!dirName.empty()) {
-    success = cmSystemTools::MakeDirectory(dirName);
+    success = static_cast<bool>(cmSystemTools::MakeDirectory(dirName));
   }
   return success;
 }
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 3a438fd..2b3266d 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -153,27 +153,6 @@
 #  define environ (*_NSGetEnviron())
 #endif
 
-namespace {
-void ReportError(std::string* err)
-{
-  if (!err) {
-    return;
-  }
-#ifdef _WIN32
-  LPSTR message = NULL;
-  DWORD size = FormatMessageA(
-    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
-      FORMAT_MESSAGE_IGNORE_INSERTS,
-    NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-    (LPSTR)&message, 0, NULL);
-  *err = std::string(message, size);
-  LocalFree(message);
-#else
-  *err = strerror(errno);
-#endif
-}
-}
-
 bool cmSystemTools::s_RunCommandHideConsole = false;
 bool cmSystemTools::s_DisableRunCommandOutput = false;
 bool cmSystemTools::s_ErrorOccured = false;
@@ -1016,23 +995,31 @@
   }
 
   mode_t perm = 0;
-  bool perms = SystemTools::GetPermissions(oldname, perm);
+  cmsys::Status perms = SystemTools::GetPermissions(oldname, perm);
 
   // If files are the same do not copy
   if (SystemTools::SameFile(oldname, newname)) {
     return CopyResult::Success;
   }
 
-  if (!cmsys::SystemTools::CloneFileContent(oldname, newname)) {
+  cmsys::Status status;
+  status = cmsys::SystemTools::CloneFileContent(oldname, newname);
+  if (!status) {
     // if cloning did not succeed, fall back to blockwise copy
-    if (!cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname)) {
-      ReportError(err);
-      return CopyResult::Failure;
+    status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
+  }
+  if (!status) {
+    if (err) {
+      *err = status.GetString();
     }
+    return CopyResult::Failure;
   }
   if (perms) {
-    if (!SystemTools::SetPermissions(newname, perm)) {
-      ReportError(err);
+    status = SystemTools::SetPermissions(newname, perm);
+    if (!status) {
+      if (err) {
+        *err = status.GetString();
+      }
       return CopyResult::Failure;
     }
   }
@@ -1090,7 +1077,9 @@
       if (replace == Replace::No && move_last_error == ERROR_ALREADY_EXISTS) {
         return RenameResult::NoReplace;
       }
-      ReportError(err);
+      if (err) {
+        *err = cmsys::Status::Windows(move_last_error).GetString();
+      }
       return RenameResult::Failure;
     }
 
@@ -1121,7 +1110,9 @@
   if (replace == Replace::No && GetLastError() == ERROR_ALREADY_EXISTS) {
     return RenameResult::NoReplace;
   }
-  ReportError(err);
+  if (err) {
+    *err = cmsys::Status::Windows_GetLastError().GetString();
+  }
   return RenameResult::Failure;
 #else
   // On UNIX we have OS-provided calls to create 'newname' atomically.
@@ -1132,13 +1123,17 @@
     if (errno == EEXIST) {
       return RenameResult::NoReplace;
     }
-    ReportError(err);
+    if (err) {
+      *err = cmsys::Status::POSIX_errno().GetString();
+    }
     return RenameResult::Failure;
   }
   if (rename(oldname.c_str(), newname.c_str()) == 0) {
     return RenameResult::Success;
   }
-  ReportError(err);
+  if (err) {
+    *err = cmsys::Status::POSIX_errno().GetString();
+  }
   return RenameResult::Failure;
 #endif
 }
@@ -3130,7 +3125,7 @@
   }
   return false;
 #else
-  return cmSystemTools::RemoveADirectory(dir);
+  return static_cast<bool>(cmSystemTools::RemoveADirectory(dir));
 #endif
 }
 
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 5620899..99f20e0 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -12,6 +12,7 @@
 #include <cm/string_view>
 
 #include "cmsys/Process.h"
+#include "cmsys/Status.hxx"      // IWYU pragma: export
 #include "cmsys/SystemTools.hxx" // IWYU pragma: export
 
 #include "cmCryptoHash.h"
diff --git a/Source/cmWorkingDirectory.cxx b/Source/cmWorkingDirectory.cxx
index 5700b1c..12fae12 100644
--- a/Source/cmWorkingDirectory.cxx
+++ b/Source/cmWorkingDirectory.cxx
@@ -19,7 +19,7 @@
 
 bool cmWorkingDirectory::SetDirectory(std::string const& newdir)
 {
-  if (cmSystemTools::ChangeDirectory(newdir) == 0) {
+  if (cmSystemTools::ChangeDirectory(newdir)) {
     this->ResultCode = 0;
     return true;
   }
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 9ba4b93..a47eccd 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -1635,7 +1635,7 @@
     cmSystemTools::RemoveFile(link);
   }
 #if defined(_WIN32) && !defined(__CYGWIN__)
-  return cmSystemTools::CopyFileAlways(file, link);
+  return static_cast<bool>(cmSystemTools::CopyFileAlways(file, link));
 #else
   std::string linktext = cmSystemTools::GetFilenameName(file);
   return cmSystemTools::CreateSymlink(linktext, link);
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index b0a8542..bf8543e 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -142,6 +142,7 @@
   set(KWSYS_USE_MD5 1)
   set(KWSYS_USE_Process 1)
   set(KWSYS_USE_RegularExpression 1)
+  set(KWSYS_USE_Status 1)
   set(KWSYS_USE_System 1)
   set(KWSYS_USE_SystemTools 1)
   set(KWSYS_USE_CommandLineArguments 1)
@@ -157,6 +158,7 @@
   set(KWSYS_USE_Directory 1)
   set(KWSYS_USE_FStream 1)
   set(KWSYS_USE_Encoding 1)
+  set(KWSYS_USE_Status 1)
 endif()
 if(KWSYS_USE_Glob)
   set(KWSYS_USE_Directory 1)
@@ -177,6 +179,7 @@
 endif()
 if(KWSYS_USE_Directory)
   set(KWSYS_USE_Encoding 1)
+  set(KWSYS_USE_Status 1)
 endif()
 if(KWSYS_USE_DynamicLoader)
   set(KWSYS_USE_Encoding 1)
@@ -630,7 +633,7 @@
 # Add selected C++ classes.
 set(cppclasses
   Directory DynamicLoader Encoding Glob RegularExpression SystemTools
-  CommandLineArguments FStream SystemInformation ConsoleBuf
+  CommandLineArguments FStream SystemInformation ConsoleBuf Status
   )
 foreach(cpp ${cppclasses})
   if(KWSYS_USE_${cpp})
@@ -963,6 +966,7 @@
     # C++ tests
     set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
       testConfigure.cxx
+      testStatus.cxx
       testSystemTools.cxx
       testCommandLineArguments.cxx
       testCommandLineArguments1.cxx
diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx
index e70d4e8..2e8aa83 100644
--- a/Source/kwsys/Directory.cxx
+++ b/Source/kwsys/Directory.cxx
@@ -94,7 +94,7 @@
 
 namespace KWSYS_NAMESPACE {
 
-bool Directory::Load(const std::string& name, std::string* errorMessage)
+Status Directory::Load(std::string const& name, std::string* errorMessage)
 {
   this->Clear();
   intptr_t srchHandle;
@@ -121,21 +121,11 @@
   delete[] buf;
 
   if (srchHandle == -1) {
+    Status status = Status::POSIX_errno();
     if (errorMessage) {
-      if (unsigned int errorId = GetLastError()) {
-        LPSTR message = nullptr;
-        DWORD size = FormatMessageA(
-          FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
-            FORMAT_MESSAGE_IGNORE_INSERTS,
-          nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-          (LPSTR)&message, 0, nullptr);
-        *errorMessage = std::string(message, size);
-        LocalFree(message);
-      } else {
-        *errorMessage = "Unknown error.";
-      }
+      *errorMessage = status.GetString();
     }
-    return false;
+    return status;
   }
 
   // Loop through names
@@ -143,7 +133,14 @@
     this->Internal->Files.push_back(Encoding::ToNarrow(data.name));
   } while (_wfindnext(srchHandle, &data) != -1);
   this->Internal->Path = name;
-  return _findclose(srchHandle) != -1;
+  if (_findclose(srchHandle) == -1) {
+    Status status = Status::POSIX_errno();
+    if (errorMessage) {
+      *errorMessage = status.GetString();
+    }
+    return status;
+  }
+  return Status::Success();
 }
 
 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
@@ -220,7 +217,7 @@
 
 namespace KWSYS_NAMESPACE {
 
-bool Directory::Load(const std::string& name, std::string* errorMessage)
+Status Directory::Load(std::string const& name, std::string* errorMessage)
 {
   this->Clear();
 
@@ -231,7 +228,7 @@
     if (errorMessage != nullptr) {
       *errorMessage = std::string(strerror(errno));
     }
-    return false;
+    return Status::POSIX_errno();
   }
 
   errno = 0;
@@ -242,12 +239,12 @@
     if (errorMessage != nullptr) {
       *errorMessage = std::string(strerror(errno));
     }
-    return false;
+    return Status::POSIX_errno();
   }
 
   this->Internal->Path = name;
   closedir(dir);
-  return true;
+  return Status::Success();
 }
 
 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
diff --git a/Source/kwsys/Directory.hxx.in b/Source/kwsys/Directory.hxx.in
index 7bc9db0..d501116 100644
--- a/Source/kwsys/Directory.hxx.in
+++ b/Source/kwsys/Directory.hxx.in
@@ -4,6 +4,7 @@
 #define @KWSYS_NAMESPACE@_Directory_hxx
 
 #include <@KWSYS_NAMESPACE@/Configure.h>
+#include <@KWSYS_NAMESPACE@/Status.hxx>
 
 #include <string>
 
@@ -32,10 +33,9 @@
 
   /**
    * Load the specified directory and load the names of the files
-   * in that directory. 0 is returned if the directory can not be
-   * opened, 1 if it is opened.
+   * in that directory.
    */
-  bool Load(const std::string&, std::string* errorMessage = nullptr);
+  Status Load(std::string const&, std::string* errorMessage = nullptr);
 
   /**
    * Return the number of files in the current directory.
diff --git a/Source/kwsys/Status.cxx b/Source/kwsys/Status.cxx
new file mode 100644
index 0000000..503d1e1
--- /dev/null
+++ b/Source/kwsys/Status.cxx
@@ -0,0 +1,60 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
+#include "kwsysPrivate.h"
+#include KWSYS_HEADER(Status.hxx)
+
+// Work-around CMake dependency scanning limitation.  This must
+// duplicate the above list of headers.
+#if 0
+#  include "Status.hxx.in"
+#endif
+
+#include <cerrno>
+#include <cstring>
+#include <string>
+
+#if defined(_WIN32)
+#  include <windows.h>
+#endif
+
+namespace KWSYS_NAMESPACE {
+
+Status Status::POSIX_errno()
+{
+  return Status::POSIX(errno);
+}
+
+#ifdef _WIN32
+Status Status::Windows_GetLastError()
+{
+  return Status::Windows(GetLastError());
+}
+#endif
+
+std::string Status::GetString() const
+{
+  std::string err;
+  switch (this->Kind_) {
+    case Kind::Success:
+      err = "Success";
+      break;
+    case Kind::POSIX:
+      err = strerror(this->POSIX_);
+      break;
+#ifdef _WIN32
+    case Kind::Windows: {
+      LPSTR message = NULL;
+      DWORD size = FormatMessageA(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+          FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL, this->Windows_, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPSTR)&message, 0, NULL);
+      err = std::string(message, size);
+      LocalFree(message);
+    } break;
+#endif
+  };
+  return err;
+}
+
+} // namespace KWSYS_NAMESPACE
diff --git a/Source/kwsys/Status.hxx.in b/Source/kwsys/Status.hxx.in
new file mode 100644
index 0000000..feb5b84
--- /dev/null
+++ b/Source/kwsys/Status.hxx.in
@@ -0,0 +1,101 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
+#ifndef @KWSYS_NAMESPACE@_Status_hxx
+#define @KWSYS_NAMESPACE@_Status_hxx
+
+#include <@KWSYS_NAMESPACE@/Configure.hxx>
+
+#include <string>
+
+namespace @KWSYS_NAMESPACE@ {
+
+/** \class Status
+ * \brief OS-specific status of a system operation.
+ */
+class @KWSYS_NAMESPACE@_EXPORT Status
+{
+public:
+  enum class Kind
+  {
+    Success,
+    POSIX,
+#ifdef _WIN32
+    Windows,
+#endif
+  };
+
+  /** Construct with kind "Success".  */
+  Status() = default;
+
+  /** Construct with kind "Success".  */
+  static Status Success() { return Status(); }
+
+  /** Construct with kind "POSIX" using given errno-style value.  */
+  static Status POSIX(int e)
+  {
+    Status s(Kind::POSIX);
+    s.POSIX_ = e;
+    return s;
+  }
+
+  /** Construct with kind "POSIX" using errno.  */
+  static Status POSIX_errno();
+
+#ifdef _WIN32
+  /** Construct with kind "Windows" using given GetLastError()-style value.  */
+  static Status Windows(unsigned int e)
+  {
+    Status s(Kind::Windows);
+    s.Windows_ = e;
+    return s;
+  }
+
+  /** Construct with kind "Windows" using GetLastError().  */
+  static Status Windows_GetLastError();
+#endif
+
+  /** Return true on "Success", false otherwise.  */
+  explicit operator bool() const { return this->Kind_ == Kind::Success; }
+
+  /** Return the kind of status.  */
+  Kind GetKind() const { return this->Kind_; }
+
+  /** If the kind is "POSIX", returns the errno-style value.
+      Otherwise, returns 0.  */
+  int GetPOSIX() const
+  {
+    return this->Kind_ == Kind::POSIX ? this->POSIX_ : 0;
+  }
+
+#ifdef _WIN32
+  /** If the kind is "Windows", returns the GetLastError()-style value.
+      Otherwise, returns 0.  */
+  int GetWindows() const
+  {
+    return this->Kind_ == Kind::Windows ? this->Windows_ : 0;
+  }
+#endif
+
+  /** Return a human-readable description of the status.  */
+  std::string GetString() const;
+
+private:
+  Status(Kind kind)
+    : Kind_(kind)
+  {
+  }
+
+  Kind Kind_ = Kind::Success;
+
+  union
+  {
+    int POSIX_;
+#ifdef _WIN32
+    unsigned int Windows_;
+#endif
+  };
+};
+
+} // namespace @KWSYS_NAMESPACE@
+
+#endif
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 4d974a8..2518845 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -882,21 +882,24 @@
 #endif
 }
 
-bool SystemTools::MakeDirectory(const char* path, const mode_t* mode)
+Status SystemTools::MakeDirectory(const char* path, const mode_t* mode)
 {
   if (!path) {
-    return false;
+    return Status::POSIX(EINVAL);
   }
   return SystemTools::MakeDirectory(std::string(path), mode);
 }
 
-bool SystemTools::MakeDirectory(const std::string& path, const mode_t* mode)
+Status SystemTools::MakeDirectory(std::string const& path, const mode_t* mode)
 {
-  if (SystemTools::PathExists(path)) {
-    return SystemTools::FileIsDirectory(path);
-  }
   if (path.empty()) {
-    return false;
+    return Status::POSIX(EINVAL);
+  }
+  if (SystemTools::PathExists(path)) {
+    if (SystemTools::FileIsDirectory(path)) {
+      return Status::Success();
+    }
+    return Status::POSIX(EEXIST);
   }
   std::string dir = path;
   SystemTools::ConvertToUnixSlashes(dir);
@@ -914,15 +917,11 @@
     ++pos;
   }
   topdir = dir;
-  if (Mkdir(topdir, mode) != 0) {
-    // if it is some other error besides directory exists
-    // then return false
-    if (errno != EEXIST) {
-      return false;
-    }
+  if (Mkdir(topdir, mode) != 0 && errno != EEXIST) {
+    return Status::POSIX_errno();
   }
 
-  return true;
+  return Status::Success();
 }
 
 // replace replace with with as many times as it shows up in source.
@@ -1411,18 +1410,18 @@
 #endif
 }
 
-bool SystemTools::Touch(const std::string& filename, bool create)
+Status SystemTools::Touch(std::string const& filename, bool create)
 {
   if (!SystemTools::FileExists(filename)) {
     if (create) {
       FILE* file = Fopen(filename, "a+b");
       if (file) {
         fclose(file);
-        return true;
+        return Status::Success();
       }
-      return false;
+      return Status::POSIX_errno();
     } else {
-      return true;
+      return Status::Success();
     }
   }
 #if defined(_WIN32) && !defined(__CYGWIN__)
@@ -1430,31 +1429,32 @@
                          FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, 0,
                          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
   if (!h) {
-    return false;
+    return Status::Windows_GetLastError();
   }
   FILETIME mtime;
   GetSystemTimeAsFileTime(&mtime);
   if (!SetFileTime(h, 0, 0, &mtime)) {
+    Status status = Status::Windows_GetLastError();
     CloseHandle(h);
-    return false;
+    return status;
   }
   CloseHandle(h);
 #elif KWSYS_CXX_HAS_UTIMENSAT
   // utimensat is only available on newer Unixes and macOS 10.13+
   if (utimensat(AT_FDCWD, filename.c_str(), nullptr, 0) < 0) {
-    return false;
+    return Status::POSIX_errno();
   }
 #else
   // fall back to utimes
   if (utimes(filename.c_str(), nullptr) < 0) {
-    return false;
+    return Status::POSIX_errno();
   }
 #endif
-  return true;
+  return Status::Success();
 }
 
-bool SystemTools::FileTimeCompare(const std::string& f1, const std::string& f2,
-                                  int* result)
+Status SystemTools::FileTimeCompare(std::string const& f1,
+                                    std::string const& f2, int* result)
 {
   // Default to same time.
   *result = 0;
@@ -1462,11 +1462,11 @@
   // POSIX version.  Use stat function to get file modification time.
   struct stat s1;
   if (stat(f1.c_str(), &s1) != 0) {
-    return false;
+    return Status::POSIX_errno();
   }
   struct stat s2;
   if (stat(f2.c_str(), &s2) != 0) {
-    return false;
+    return Status::POSIX_errno();
   }
 #  if KWSYS_CXX_STAT_HAS_ST_MTIM
   // Compare using nanosecond resolution.
@@ -1504,17 +1504,17 @@
   WIN32_FILE_ATTRIBUTE_DATA f2d;
   if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f1).c_str(),
                             GetFileExInfoStandard, &f1d)) {
-    return false;
+    return Status::Windows_GetLastError();
   }
   if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f2).c_str(),
                             GetFileExInfoStandard, &f2d)) {
-    return false;
+    return Status::Windows_GetLastError();
   }
 
   // Compare the file times using resolution provided by system call.
   *result = (int)CompareFileTime(&f1d.ftLastWriteTime, &f2d.ftLastWriteTime);
 #endif
-  return true;
+  return Status::Success();
 }
 
 // Return a capitalized string (i.e the first letter is uppercased, all other
@@ -2129,8 +2129,8 @@
   return new_destination + '/' + SystemTools::GetFilenameName(source);
 }
 
-bool SystemTools::CopyFileIfDifferent(const std::string& source,
-                                      const std::string& destination)
+Status SystemTools::CopyFileIfDifferent(std::string const& source,
+                                        std::string const& destination)
 {
   // special check for a destination that is a directory
   // FilesDiffer does not handle file to directory compare
@@ -2147,7 +2147,7 @@
     }
   }
   // at this point the files must be the same so return true
-  return true;
+  return Status::Success();
 }
 
 #define KWSYS_ST_BUFFER 4096
@@ -2273,13 +2273,13 @@
   return false;
 }
 
-bool SystemTools::CopyFileContentBlockwise(const std::string& source,
-                                           const std::string& destination)
+Status SystemTools::CopyFileContentBlockwise(std::string const& source,
+                                             std::string const& destination)
 {
   // Open files
   kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
   if (!fin) {
-    return false;
+    return Status::POSIX_errno();
   }
 
   // try and remove the destination file so that read only destination files
@@ -2291,7 +2291,7 @@
   kwsys::ofstream fout(destination.c_str(),
                        std::ios::out | std::ios::trunc | std::ios::binary);
   if (!fout) {
-    return false;
+    return Status::POSIX_errno();
   }
 
   // This copy loop is very sensitive on certain platforms with
@@ -2320,10 +2320,10 @@
   fout.close();
 
   if (!fout) {
-    return false;
+    return Status::POSIX_errno();
   }
 
-  return true;
+  return Status::Success();
 }
 
 /**
@@ -2338,13 +2338,13 @@
  * - The underlying filesystem does not support file cloning
  * - An unspecified error occurred
  */
-bool SystemTools::CloneFileContent(const std::string& source,
-                                   const std::string& destination)
+Status SystemTools::CloneFileContent(std::string const& source,
+                                     std::string const& destination)
 {
 #if defined(__linux) && defined(FICLONE)
   int in = open(source.c_str(), O_RDONLY);
   if (in < 0) {
-    return false;
+    return Status::POSIX_errno();
   }
 
   SystemTools::RemoveFile(destination);
@@ -2352,38 +2352,42 @@
   int out =
     open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
   if (out < 0) {
+    Status status = Status::POSIX_errno();
     close(in);
-    return false;
+    return status;
   }
 
-  int result = ioctl(out, FICLONE, in);
+  Status status = Status::Success();
+  if (ioctl(out, FICLONE, in) < 0) {
+    status = Status::POSIX_errno();
+  }
   close(in);
   close(out);
 
-  if (result < 0) {
-    return false;
-  }
-
-  return true;
+  return status;
 #else
   (void)source;
   (void)destination;
-  return false;
+  return Status::POSIX(ENOSYS);
 #endif
 }
 
 /**
  * Copy a file named by "source" to the file named by "destination".
  */
-bool SystemTools::CopyFileAlways(const std::string& source,
-                                 const std::string& destination)
+Status SystemTools::CopyFileAlways(std::string const& source,
+                                   std::string const& destination)
 {
+  Status status;
   mode_t perm = 0;
-  bool perms = SystemTools::GetPermissions(source, perm);
+  Status perms = SystemTools::GetPermissions(source, perm);
   std::string real_destination = destination;
 
   if (SystemTools::FileIsDirectory(source)) {
-    SystemTools::MakeDirectory(destination);
+    status = SystemTools::MakeDirectory(destination);
+    if (!status) {
+      return status;
+    }
   } else {
     // If destination is a directory, try to create a file with the same
     // name as the source in that directory.
@@ -2400,30 +2404,34 @@
     }
     // If files are the same do not copy
     if (SystemTools::SameFile(source, real_destination)) {
-      return true;
+      return status;
     }
 
     // Create destination directory
-
-    SystemTools::MakeDirectory(destination_dir);
-
-    if (!SystemTools::CloneFileContent(source, real_destination)) {
-      // if cloning did not succeed, fall back to blockwise copy
-      if (!SystemTools::CopyFileContentBlockwise(source, real_destination)) {
-        return false;
+    if (!destination_dir.empty()) {
+      status = SystemTools::MakeDirectory(destination_dir);
+      if (!status) {
+        return status;
       }
     }
+
+    status = SystemTools::CloneFileContent(source, real_destination);
+    // if cloning did not succeed, fall back to blockwise copy
+    if (!status) {
+      status = SystemTools::CopyFileContentBlockwise(source, real_destination);
+    }
+    if (!status) {
+      return status;
+    }
   }
   if (perms) {
-    if (!SystemTools::SetPermissions(real_destination, perm)) {
-      return false;
-    }
+    status = SystemTools::SetPermissions(real_destination, perm);
   }
-  return true;
+  return status;
 }
 
-bool SystemTools::CopyAFile(const std::string& source,
-                            const std::string& destination, bool always)
+Status SystemTools::CopyAFile(std::string const& source,
+                              std::string const& destination, bool always)
 {
   if (always) {
     return SystemTools::CopyFileAlways(source, destination);
@@ -2436,18 +2444,21 @@
  * Copy a directory content from "source" directory to the directory named by
  * "destination".
  */
-bool SystemTools::CopyADirectory(const std::string& source,
-                                 const std::string& destination, bool always)
+Status SystemTools::CopyADirectory(std::string const& source,
+                                   std::string const& destination, bool always)
 {
+  Status status;
   Directory dir;
-  if (dir.Load(source) == 0) {
-    return false;
+  status = dir.Load(source);
+  if (!status) {
+    return status;
   }
-  size_t fileNum;
-  if (!SystemTools::MakeDirectory(destination)) {
-    return false;
+  status = SystemTools::MakeDirectory(destination);
+  if (!status) {
+    return status;
   }
-  for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
+
+  for (size_t fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
     if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 &&
         strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) {
       std::string fullPath = source;
@@ -2457,18 +2468,20 @@
         std::string fullDestPath = destination;
         fullDestPath += "/";
         fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum));
-        if (!SystemTools::CopyADirectory(fullPath, fullDestPath, always)) {
-          return false;
+        status = SystemTools::CopyADirectory(fullPath, fullDestPath, always);
+        if (!status) {
+          return status;
         }
       } else {
-        if (!SystemTools::CopyAFile(fullPath, destination, always)) {
-          return false;
+        status = SystemTools::CopyAFile(fullPath, destination, always);
+        if (!status) {
+          return status;
         }
       }
     }
   }
 
-  return true;
+  return status;
 }
 
 // return size of file; also returns zero if no file exists
@@ -2550,26 +2563,26 @@
   return strerror(e);
 }
 
-bool SystemTools::RemoveFile(const std::string& source)
+Status SystemTools::RemoveFile(std::string const& source)
 {
 #ifdef _WIN32
   std::wstring const& ws = Encoding::ToWindowsExtendedPath(source);
   if (DeleteFileW(ws.c_str())) {
-    return true;
+    return Status::Success();
   }
   DWORD err = GetLastError();
   if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
-    return true;
+    return Status::Success();
   }
   if (err != ERROR_ACCESS_DENIED) {
-    return false;
+    return Status::Windows(err);
   }
   /* The file may be read-only.  Try adding write permission.  */
   mode_t mode;
   if (!SystemTools::GetPermissions(source, mode) ||
       !SystemTools::SetPermissions(source, S_IWRITE)) {
     SetLastError(err);
-    return false;
+    return Status::Windows(err);
   }
 
   const DWORD DIRECTORY_SOFT_LINK_ATTRS =
@@ -2578,26 +2591,29 @@
   if (attrs != INVALID_FILE_ATTRIBUTES &&
       (attrs & DIRECTORY_SOFT_LINK_ATTRS) == DIRECTORY_SOFT_LINK_ATTRS &&
       RemoveDirectoryW(ws.c_str())) {
-    return true;
+    return Status::Success();
   }
   if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND ||
       GetLastError() == ERROR_PATH_NOT_FOUND) {
-    return true;
+    return Status::Success();
   }
   /* Try to restore the original permissions.  */
   SystemTools::SetPermissions(source, mode);
   SetLastError(err);
-  return false;
+  return Status::Windows(err);
 #else
-  return unlink(source.c_str()) == 0 || errno == ENOENT;
+  if (unlink(source.c_str()) != 0 && errno != ENOENT) {
+    return Status::POSIX_errno();
+  }
+  return Status::Success();
 #endif
 }
 
-bool SystemTools::RemoveADirectory(const std::string& source)
+Status SystemTools::RemoveADirectory(std::string const& source)
 {
   // Add write permission to the directory so we can modify its
   // content to remove files and directories from it.
-  mode_t mode;
+  mode_t mode = 0;
   if (SystemTools::GetPermissions(source, mode)) {
 #if defined(_WIN32) && !defined(__CYGWIN__)
     mode |= S_IWRITE;
@@ -2607,8 +2623,13 @@
     SystemTools::SetPermissions(source, mode);
   }
 
+  Status status;
   Directory dir;
-  dir.Load(source);
+  status = dir.Load(source);
+  if (!status) {
+    return status;
+  }
+
   size_t fileNum;
   for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
     if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 &&
@@ -2618,18 +2639,23 @@
       fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
       if (SystemTools::FileIsDirectory(fullPath) &&
           !SystemTools::FileIsSymlink(fullPath)) {
-        if (!SystemTools::RemoveADirectory(fullPath)) {
-          return false;
+        status = SystemTools::RemoveADirectory(fullPath);
+        if (!status) {
+          return status;
         }
       } else {
-        if (!SystemTools::RemoveFile(fullPath)) {
-          return false;
+        status = SystemTools::RemoveFile(fullPath);
+        if (!status) {
+          return status;
         }
       }
     }
   }
 
-  return (Rmdir(source) == 0);
+  if (Rmdir(source) != 0) {
+    status = Status::POSIX_errno();
+  }
+  return status;
 }
 
 /**
@@ -3023,44 +3049,49 @@
 }
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
-bool SystemTools::CreateSymlink(const std::string&, const std::string&)
+Status SystemTools::CreateSymlink(std::string const&, std::string const&)
 {
-  return false;
+  return Status::Windows(ERROR_NOT_SUPPORTED);
 }
 #else
-bool SystemTools::CreateSymlink(const std::string& origName,
-                                const std::string& newName)
+Status SystemTools::CreateSymlink(std::string const& origName,
+                                  std::string const& newName)
 {
-  return symlink(origName.c_str(), newName.c_str()) >= 0;
+  if (symlink(origName.c_str(), newName.c_str()) < 0) {
+    return Status::POSIX_errno();
+  }
+  return Status::Success();
 }
 #endif
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
-bool SystemTools::ReadSymlink(const std::string&, std::string&)
+Status SystemTools::ReadSymlink(std::string const&, std::string&)
 {
-  return false;
+  return Status::Windows(ERROR_NOT_SUPPORTED);
 }
 #else
-bool SystemTools::ReadSymlink(const std::string& newName,
-                              std::string& origName)
+Status SystemTools::ReadSymlink(std::string const& newName,
+                                std::string& origName)
 {
   char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1];
   int count = static_cast<int>(
     readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH));
-  if (count >= 0) {
-    // Add null-terminator.
-    buf[count] = 0;
-    origName = buf;
-    return true;
-  } else {
-    return false;
+  if (count < 0) {
+    return Status::POSIX_errno();
   }
+  // Add null-terminator.
+  buf[count] = 0;
+  origName = buf;
+  return Status::Success();
 }
 #endif
 
-int SystemTools::ChangeDirectory(const std::string& dir)
+Status SystemTools::ChangeDirectory(std::string const& dir)
 {
-  return Chdir(dir);
+  if (Chdir(dir) < 0) {
+    return Status::POSIX_errno();
+  }
+  return Status::Success();
 }
 
 std::string SystemTools::GetCurrentWorkingDirectory()
@@ -3957,7 +3988,8 @@
   return false;
 }
 
-bool SystemTools::GetShortPath(const std::string& path, std::string& shortPath)
+Status SystemTools::GetShortPath(std::string const& path,
+                                 std::string& shortPath)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__)
   std::string tempPath = path; // create a buffer
@@ -3977,14 +4009,14 @@
   }
 
   if (ret == 0) {
-    return false;
+    return Status::Windows_GetLastError();
   } else {
     shortPath = Encoding::ToNarrow(&buffer[0]);
-    return true;
+    return Status::Success();
   }
 #else
   shortPath = path;
-  return true;
+  return Status::Success();
 #endif
 }
 
@@ -4085,21 +4117,21 @@
   return width;
 }
 
-bool SystemTools::GetPermissions(const char* file, mode_t& mode)
+Status SystemTools::GetPermissions(const char* file, mode_t& mode)
 {
   if (!file) {
-    return false;
+    return Status::POSIX(EINVAL);
   }
   return SystemTools::GetPermissions(std::string(file), mode);
 }
 
-bool SystemTools::GetPermissions(const std::string& file, mode_t& mode)
+Status SystemTools::GetPermissions(std::string const& file, mode_t& mode)
 {
 #if defined(_WIN32)
   DWORD attr =
     GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str());
   if (attr == INVALID_FILE_ATTRIBUTES) {
-    return false;
+    return Status::Windows_GetLastError();
   }
   if ((attr & FILE_ATTRIBUTE_READONLY) != 0) {
     mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
@@ -4122,27 +4154,27 @@
 #else
   struct stat st;
   if (stat(file.c_str(), &st) < 0) {
-    return false;
+    return Status::POSIX_errno();
   }
   mode = st.st_mode;
 #endif
-  return true;
+  return Status::Success();
 }
 
-bool SystemTools::SetPermissions(const char* file, mode_t mode,
-                                 bool honor_umask)
+Status SystemTools::SetPermissions(const char* file, mode_t mode,
+                                   bool honor_umask)
 {
   if (!file) {
-    return false;
+    return Status::POSIX(EINVAL);
   }
   return SystemTools::SetPermissions(std::string(file), mode, honor_umask);
 }
 
-bool SystemTools::SetPermissions(const std::string& file, mode_t mode,
-                                 bool honor_umask)
+Status SystemTools::SetPermissions(std::string const& file, mode_t mode,
+                                   bool honor_umask)
 {
   if (!SystemTools::PathExists(file)) {
-    return false;
+    return Status::POSIX(ENOENT);
   }
   if (honor_umask) {
     mode_t currentMask = umask(0);
@@ -4155,10 +4187,10 @@
   if (chmod(file.c_str(), mode) < 0)
 #endif
   {
-    return false;
+    return Status::POSIX_errno();
   }
 
-  return true;
+  return Status::Success();
 }
 
 std::string SystemTools::GetParentDirectory(const std::string& fileOrDir)
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in
index a7b1288..e5d115e 100644
--- a/Source/kwsys/SystemTools.hxx.in
+++ b/Source/kwsys/SystemTools.hxx.in
@@ -4,6 +4,7 @@
 #define @KWSYS_NAMESPACE@_SystemTools_hxx
 
 #include <@KWSYS_NAMESPACE@/Configure.hxx>
+#include <@KWSYS_NAMESPACE@/Status.hxx>
 
 #include <iosfwd>
 #include <map>
@@ -339,7 +340,7 @@
   /**
      Change the modification time or create a file
   */
-  static bool Touch(const std::string& filename, bool create);
+  static Status Touch(std::string const& filename, bool create);
 
   /**
    *  Compare file modification times.
@@ -347,8 +348,8 @@
    *  When true is returned, result has -1, 0, +1 for
    *  f1 older, same, or newer than f2.
    */
-  static bool FileTimeCompare(const std::string& f1, const std::string& f2,
-                              int* result);
+  static Status FileTimeCompare(std::string const& f1, std::string const& f2,
+                                int* result);
 
   /**
    *  Get the file extension (including ".") needed for an executable
@@ -507,7 +508,7 @@
    * For windows return the short path for the given path,
    * Unix just a pass through
    */
-  static bool GetShortPath(const std::string& path, std::string& result);
+  static Status GetShortPath(std::string const& path, std::string& result);
 
   /**
    * Read line from file. Make sure to read a full line and truncates it if
@@ -553,16 +554,16 @@
    * can make a full path even if none of the directories existed
    * prior to calling this function.
    */
-  static bool MakeDirectory(const char* path, const mode_t* mode = nullptr);
-  static bool MakeDirectory(const std::string& path,
-                            const mode_t* mode = nullptr);
+  static Status MakeDirectory(const char* path, const mode_t* mode = nullptr);
+  static Status MakeDirectory(std::string const& path,
+                              const mode_t* mode = nullptr);
 
   /**
    * Copy the source file to the destination file only
    * if the two files differ.
    */
-  static bool CopyFileIfDifferent(const std::string& source,
-                                  const std::string& destination);
+  static Status CopyFileIfDifferent(std::string const& source,
+                                    std::string const& destination);
 
   /**
    * Compare the contents of two files.  Return true if different
@@ -580,13 +581,13 @@
   /**
    * Blockwise copy source to destination file
    */
-  static bool CopyFileContentBlockwise(const std::string& source,
-                                       const std::string& destination);
+  static Status CopyFileContentBlockwise(std::string const& source,
+                                         std::string const& destination);
   /**
    * Clone the source file to the destination file
    */
-  static bool CloneFileContent(const std::string& source,
-                               const std::string& destination);
+  static Status CloneFileContent(std::string const& source,
+                                 std::string const& destination);
 
   /**
    * Return true if the two files are the same file
@@ -596,16 +597,16 @@
   /**
    * Copy a file.
    */
-  static bool CopyFileAlways(const std::string& source,
-                             const std::string& destination);
+  static Status CopyFileAlways(std::string const& source,
+                               std::string const& destination);
 
   /**
    * Copy a file.  If the "always" argument is true the file is always
    * copied.  If it is false, the file is copied only if it is new or
    * has changed.
    */
-  static bool CopyAFile(const std::string& source,
-                        const std::string& destination, bool always = true);
+  static Status CopyAFile(std::string const& source,
+                          std::string const& destination, bool always = true);
 
   /**
    * Copy content directory to another directory with all files and
@@ -613,19 +614,19 @@
    * always copied.  If it is false, only files that have changed or
    * are new are copied.
    */
-  static bool CopyADirectory(const std::string& source,
-                             const std::string& destination,
-                             bool always = true);
+  static Status CopyADirectory(std::string const& source,
+                               std::string const& destination,
+                               bool always = true);
 
   /**
    * Remove a file
    */
-  static bool RemoveFile(const std::string& source);
+  static Status RemoveFile(std::string const& source);
 
   /**
    * Remove a directory
    */
-  static bool RemoveADirectory(const std::string& source);
+  static Status RemoveADirectory(std::string const& source);
 
   /**
    * Get the maximum full file path length
@@ -719,14 +720,14 @@
    * Create a symbolic link if the platform supports it.  Returns whether
    * creation succeeded.
    */
-  static bool CreateSymlink(const std::string& origName,
-                            const std::string& newName);
+  static Status CreateSymlink(std::string const& origName,
+                              std::string const& newName);
 
   /**
    * Read the contents of a symbolic link.  Returns whether reading
    * succeeded.
    */
-  static bool ReadSymlink(const std::string& newName, std::string& origName);
+  static Status ReadSymlink(std::string const& newName, std::string& origName);
 
   /**
    * Try to locate the file 'filename' in the directory 'dir'.
@@ -776,12 +777,12 @@
    * WARNING:  A non-thread-safe method is currently used to get the umask
    * if a honor_umask parameter is set to true.
    */
-  static bool GetPermissions(const char* file, mode_t& mode);
-  static bool GetPermissions(const std::string& file, mode_t& mode);
-  static bool SetPermissions(const char* file, mode_t mode,
-                             bool honor_umask = false);
-  static bool SetPermissions(const std::string& file, mode_t mode,
-                             bool honor_umask = false);
+  static Status GetPermissions(const char* file, mode_t& mode);
+  static Status GetPermissions(std::string const& file, mode_t& mode);
+  static Status SetPermissions(const char* file, mode_t mode,
+                               bool honor_umask = false);
+  static Status SetPermissions(std::string const& file, mode_t mode,
+                               bool honor_umask = false);
 
   /** -----------------------------------------------------------------
    *               Time Manipulation Routines
@@ -878,7 +879,7 @@
   /**
    * Change directory to the directory specified
    */
-  static int ChangeDirectory(const std::string& dir);
+  static Status ChangeDirectory(std::string const& dir);
 
   /**
    * Get the result of strerror(errno)
diff --git a/Source/kwsys/testDirectory.cxx b/Source/kwsys/testDirectory.cxx
index eb3ca32..06a22dc 100644
--- a/Source/kwsys/testDirectory.cxx
+++ b/Source/kwsys/testDirectory.cxx
@@ -88,7 +88,7 @@
 
   errorMessage = "foo";
   // Increment res failure if directory lists
-  res += testdir.Load(testdirpath, &errorMessage);
+  res += testdir.Load(testdirpath, &errorMessage) ? 1 : 0;
 #if !defined(_WIN32) || defined(__CYGWIN__)
   // Increment res failure if errorMessage is unmodified
   res += (errorMessage == "foo");
@@ -120,7 +120,7 @@
     std::cerr << destination << " shouldn't exist before test" << std::endl;
     return 2;
   }
-  const bool copysuccess = SystemTools::CopyADirectory(source, destination);
+  const Status copysuccess = SystemTools::CopyADirectory(source, destination);
   const bool destinationexists = SystemTools::PathExists(destination);
   if (copysuccess) {
     std::cerr << "CopyADirectory should have returned false" << std::endl;
diff --git a/Source/kwsys/testStatus.cxx b/Source/kwsys/testStatus.cxx
new file mode 100644
index 0000000..f85ef42
--- /dev/null
+++ b/Source/kwsys/testStatus.cxx
@@ -0,0 +1,117 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
+#include "kwsysPrivate.h"
+#include KWSYS_HEADER(Status.hxx)
+
+// Work-around CMake dependency scanning limitation.  This must
+// duplicate the above list of headers.
+#if 0
+#  include "Status.hxx.in"
+#endif
+
+#include <cerrno>
+#include <iostream>
+
+#ifdef _WIN32
+#  include <windows.h>
+#endif
+
+int testStatus(int, char* [])
+{
+  bool res = true;
+  {
+    kwsys::Status status;
+    if (status.GetKind() != kwsys::Status::Kind::Success) {
+      std::cerr << "Status default constructor does not produce Success\n";
+      res = false;
+    }
+
+    status = kwsys::Status::Success();
+    if (status.GetKind() != kwsys::Status::Kind::Success) {
+      std::cerr << "Status Success constructor does not produce Success\n";
+      res = false;
+    }
+    if (!status) {
+      std::cerr << "Status Success kind is not true\n";
+      res = false;
+    }
+    if (status.GetPOSIX() != 0) {
+      std::cerr << "Status Success kind does not return POSIX 0\n";
+      res = false;
+    }
+#ifdef _WIN32
+    if (status.GetWindows() != 0) {
+      std::cerr << "Status Success kind does not return Windows 0\n";
+      res = false;
+    }
+#endif
+    if (status.GetString() != "Success") {
+      std::cerr << "Status Success kind does not return \"Success\" string\n";
+      res = false;
+    }
+
+    status = kwsys::Status::POSIX(EINVAL);
+    if (status.GetKind() != kwsys::Status::Kind::POSIX) {
+      std::cerr << "Status POSIX constructor does not produce POSIX\n";
+      res = false;
+    }
+    if (status) {
+      std::cerr << "Status POSIX kind is not false\n";
+      res = false;
+    }
+    if (status.GetPOSIX() != EINVAL) {
+      std::cerr << "Status POSIX kind does not preserve POSIX value\n";
+      res = false;
+    }
+#ifdef _WIN32
+    if (status.GetWindows() != 0) {
+      std::cerr << "Status POSIX kind does not return Windows 0\n";
+      res = false;
+    }
+#endif
+    if (status.GetString().empty()) {
+      std::cerr << "Status POSIX kind returns empty string\n";
+      res = false;
+    }
+    errno = ENOENT;
+    status = kwsys::Status::POSIX_errno();
+    if (status.GetPOSIX() != ENOENT) {
+      std::cerr << "Status POSIX_errno did not use errno\n";
+      res = false;
+    }
+    errno = 0;
+
+#ifdef _WIN32
+    status = kwsys::Status::Windows(ERROR_INVALID_PARAMETER);
+    if (status.GetKind() != kwsys::Status::Kind::Windows) {
+      std::cerr << "Status Windows constructor does not produce Windows\n";
+      res = false;
+    }
+    if (status) {
+      std::cerr << "Status Windows kind is not false\n";
+      res = false;
+    }
+    if (status.GetWindows() != ERROR_INVALID_PARAMETER) {
+      std::cerr << "Status Windows kind does not preserve Windows value\n";
+      res = false;
+    }
+    if (status.GetPOSIX() != 0) {
+      std::cerr << "Status Windows kind does not return POSIX 0\n";
+      res = false;
+    }
+    if (status.GetString().empty()) {
+      std::cerr << "Status Windows kind returns empty string\n";
+      res = false;
+    }
+
+    SetLastError(ERROR_FILE_NOT_FOUND);
+    status = kwsys::Status::Windows_GetLastError();
+    if (status.GetWindows() != ERROR_FILE_NOT_FOUND) {
+      std::cerr << "Status Windows_GetLastError did not use GetLastError()\n";
+      res = false;
+    }
+    SetLastError(ERROR_SUCCESS);
+#endif
+  }
+  return res ? 0 : 1;
+}
diff --git a/bootstrap b/bootstrap
index 768750d..2a81ef2 100755
--- a/bootstrap
+++ b/bootstrap
@@ -525,6 +525,7 @@
   FStream \
   Glob \
   RegularExpression \
+  Status \
   SystemTools"
 
 KWSYS_FILES="\
@@ -535,6 +536,7 @@
   Glob.hxx \
   Process.h \
   RegularExpression.hxx \
+  Status.hxx \
   String.h \
   String.hxx \
   System.h \