KWSys 2021-04-14 (7ab1c601)

Code extracted from:

    https://gitlab.kitware.com/utils/kwsys.git

at commit 7ab1c60177e76707fbdd462fdbf7304ad2532edc (master).

Upstream Shortlog
-----------------

Brad King (11):
      85e76a10 Status: Add class to hold an OS-specific status of a system operation
      eec0add2 Directory: Return Status from Load
      955e7c1e SystemTools: Return Status from RemoveFile and RemoveADirectory
      c5a99e59 SystemTools: Return Status from GetPermissions and SetPermissions
      fadfd7a0 SystemTools: Return Status from Touch
      06216f0b SystemTools: Return Status from FileTimeCompare
      ab198a22 SystemTools: Return Status from GetShortPath
      2446b205 SystemTools: Return Status from MakeDirectory
      6aec6af9 SystemTools: Return Status from ChangeDirectory
      0664553b SystemTools: Return Status from copy operations
      a95f61cf SystemTools: Return Status from symlink operations
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b0a8542..bf8543e 100644
--- a/CMakeLists.txt
+++ b/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/Directory.cxx b/Directory.cxx
index e70d4e8..2e8aa83 100644
--- a/Directory.cxx
+++ b/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/Directory.hxx.in b/Directory.hxx.in
index 7bc9db0..d501116 100644
--- a/Directory.hxx.in
+++ b/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/Status.cxx b/Status.cxx
new file mode 100644
index 0000000..503d1e1
--- /dev/null
+++ b/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/Status.hxx.in b/Status.hxx.in
new file mode 100644
index 0000000..feb5b84
--- /dev/null
+++ b/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/SystemTools.cxx b/SystemTools.cxx
index 4d974a8..2518845 100644
--- a/SystemTools.cxx
+++ b/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/SystemTools.hxx.in b/SystemTools.hxx.in
index a7b1288..e5d115e 100644
--- a/SystemTools.hxx.in
+++ b/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/testDirectory.cxx b/testDirectory.cxx
index eb3ca32..06a22dc 100644
--- a/testDirectory.cxx
+++ b/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/testStatus.cxx b/testStatus.cxx
new file mode 100644
index 0000000..f85ef42
--- /dev/null
+++ b/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;
+}