From dc4e4a55ab3b924b0fa6394019ef24df23b9d483 Mon Sep 17 00:00:00 2001
From: Domen Vrankar <domen.vrankar@gmail.com>
Date: Tue, 22 Sep 2015 02:32:56 +0200
Subject: [PATCH] SystemTools: Handle directories in
 CopyFile{Always,IfDifferent}

Teach these functions to copy directories as directories instead
of files.  Create the destination directory and set permissions,
but do not recurse into the directory structure because these
functions are meant for individual filesystem entries.

Change-Id: I20fae2e159f86285c02c3fd894609e2e51cafdac
---
 SystemTools.cxx | 135 +++++++++++++++++++++++++-----------------------
 1 file changed, 71 insertions(+), 64 deletions(-)

diff --git a/SystemTools.cxx b/SystemTools.cxx
index 3857e41..80289b8 100644
--- a/SystemTools.cxx
+++ b/SystemTools.cxx
@@ -2365,95 +2365,102 @@ bool SystemTools::CopyFileAlways(const std::string& source, const std::string& d
     }
   mode_t perm = 0;
   bool perms = SystemTools::GetPermissions(source, perm);
-
-  const int bufferSize = 4096;
-  char buffer[bufferSize];
-
-  // If destination is a directory, try to create a file with the same
-  // name as the source in that directory.
-
   std::string real_destination = destination;
-  std::string destination_dir;
-  if(SystemTools::FileExists(destination) &&
-     SystemTools::FileIsDirectory(destination))
+
+  if(SystemTools::FileIsDirectory(source))
     {
-    destination_dir = real_destination;
-    SystemTools::ConvertToUnixSlashes(real_destination);
-    real_destination += '/';
-    std::string source_name = source;
-    real_destination += SystemTools::GetFilenameName(source_name);
+    SystemTools::MakeDirectory(destination);
     }
   else
     {
-    destination_dir = SystemTools::GetFilenamePath(destination);
-    }
+    const int bufferSize = 4096;
+    char buffer[bufferSize];
+
+    // If destination is a directory, try to create a file with the same
+    // name as the source in that directory.
+
+    std::string destination_dir;
+    if(SystemTools::FileExists(destination) &&
+       SystemTools::FileIsDirectory(destination))
+      {
+      destination_dir = real_destination;
+      SystemTools::ConvertToUnixSlashes(real_destination);
+      real_destination += '/';
+      std::string source_name = source;
+      real_destination += SystemTools::GetFilenameName(source_name);
+      }
+    else
+      {
+      destination_dir = SystemTools::GetFilenamePath(destination);
+      }
 
-  // Create destination directory
+    // Create destination directory
 
-  SystemTools::MakeDirectory(destination_dir);
+    SystemTools::MakeDirectory(destination_dir);
 
-  // Open files
+    // Open files
 #if defined(_WIN32)
-  kwsys::ifstream fin(Encoding::ToNarrow(
-    SystemTools::ConvertToWindowsExtendedPath(source)).c_str(),
-                std::ios::in | std::ios::binary);
+    kwsys::ifstream fin(Encoding::ToNarrow(
+      SystemTools::ConvertToWindowsExtendedPath(source)).c_str(),
+                  std::ios::in | std::ios::binary);
 #else
-  kwsys::ifstream fin(source.c_str(),
-                std::ios::in | std::ios::binary);
+    kwsys::ifstream fin(source.c_str(),
+                  std::ios::in | std::ios::binary);
 #endif
-  if(!fin)
-    {
-    return false;
-    }
+    if(!fin)
+      {
+      return false;
+      }
 
-  // try and remove the destination file so that read only destination files
-  // can be written to.
-  // If the remove fails continue so that files in read only directories
-  // that do not allow file removal can be modified.
-  SystemTools::RemoveFile(real_destination);
+    // try and remove the destination file so that read only destination files
+    // can be written to.
+    // If the remove fails continue so that files in read only directories
+    // that do not allow file removal can be modified.
+    SystemTools::RemoveFile(real_destination);
 
 #if defined(_WIN32)
-  kwsys::ofstream fout(Encoding::ToNarrow(
-    SystemTools::ConvertToWindowsExtendedPath(real_destination)).c_str(),
+    kwsys::ofstream fout(Encoding::ToNarrow(
+      SystemTools::ConvertToWindowsExtendedPath(real_destination)).c_str(),
                      std::ios::out | std::ios::trunc | std::ios::binary);
 #else
-  kwsys::ofstream fout(real_destination.c_str(),
+    kwsys::ofstream fout(real_destination.c_str(),
                      std::ios::out | std::ios::trunc | std::ios::binary);
 #endif
-  if(!fout)
-    {
-    return false;
-    }
-
-  // This copy loop is very sensitive on certain platforms with
-  // slightly broken stream libraries (like HPUX).  Normally, it is
-  // incorrect to not check the error condition on the fin.read()
-  // before using the data, but the fin.gcount() will be zero if an
-  // error occurred.  Therefore, the loop should be safe everywhere.
-  while(fin)
-    {
-    fin.read(buffer, bufferSize);
-    if(fin.gcount())
+    if(!fout)
       {
-      fout.write(buffer, fin.gcount());
+      return false;
       }
-    else
+
+    // This copy loop is very sensitive on certain platforms with
+    // slightly broken stream libraries (like HPUX).  Normally, it is
+    // incorrect to not check the error condition on the fin.read()
+    // before using the data, but the fin.gcount() will be zero if an
+    // error occurred.  Therefore, the loop should be safe everywhere.
+    while(fin)
       {
-      break;
+      fin.read(buffer, bufferSize);
+      if(fin.gcount())
+        {
+        fout.write(buffer, fin.gcount());
+        }
+      else
+        {
+        break;
+        }
       }
-    }
 
-  // Make sure the operating system has finished writing the file
-  // before closing it.  This will ensure the file is finished before
-  // the check below.
-  fout.flush();
+    // Make sure the operating system has finished writing the file
+    // before closing it.  This will ensure the file is finished before
+    // the check below.
+    fout.flush();
 
-  fin.close();
-  fout.close();
+    fin.close();
+    fout.close();
 
-  if(!fout)
-    {
-    return false;
+    if(!fout)
+      {
+      return false;
+      }
     }
   if ( perms )
     {
-- 
GitLab