From b913e6d6db2732fa05b35ab91e33175e0132079b Mon Sep 17 00:00:00 2001
From: Andy Cedilnik <andy.cedilnik@kitware.com>
Date: Tue, 7 Feb 2006 10:23:17 -0500
Subject: [PATCH] ENH: Move relative path to kwsys

---
 SystemTools.cxx    | 102 +++++++++++++++++++++++++++++++++++++++++++++
 SystemTools.hxx.in |  18 ++++++++
 2 files changed, 120 insertions(+)

diff --git a/SystemTools.cxx b/SystemTools.cxx
index 4d0d076..a8e4a59 100644
--- a/SystemTools.cxx
+++ b/SystemTools.cxx
@@ -1130,6 +1130,29 @@ kwsys_stl::string SystemTools::CropString(const kwsys_stl::string& s,
   return n;
 }
 
+//----------------------------------------------------------------------------
+std::vector<kwsys::String> SystemTools::SplitString(const char* p, char sep, bool isPath)
+{
+  std::string path = p;
+  std::vector<kwsys::String> paths;
+  if(isPath && path[0] == '/')
+    {
+    path.erase(path.begin());
+    paths.push_back("/"); 
+    }
+  std::string::size_type pos1 = 0;
+  std::string::size_type pos2 = path.find(sep, pos1+1);
+  while(pos2 != std::string::npos)
+    {
+    paths.push_back(path.substr(pos1, pos2-pos1));
+    pos1 = pos2+1;
+    pos2 = path.find(sep, pos1+1);
+    } 
+  paths.push_back(path.substr(pos1, pos2-pos1));
+  
+  return paths;
+}
+
 //----------------------------------------------------------------------------
 int SystemTools::EstimateFormatLength(const char *format, va_list ap)
 {
@@ -2395,6 +2418,85 @@ kwsys_stl::string SystemTools::CollapseFullPath(const char* in_path,
   return newPath;
 }
 
+// compute the relative path from here to there
+std::string SystemTools::RelativePath(const char* local, const char* remote)
+{
+  if(!SystemTools::FileIsFullPath(local))
+    {
+    return "";
+    }
+  if(!SystemTools::FileIsFullPath(remote))
+    {
+    return "";
+    }
+  
+  // split up both paths into arrays of strings using / as a separator
+  std::vector<kwsys::String> localSplit = SystemTools::SplitString(local, '/', true); 
+  std::vector<kwsys::String> remoteSplit = SystemTools::SplitString(remote, '/', true);
+  std::vector<kwsys::String> commonPath; // store shared parts of path in this array
+  std::vector<kwsys::String> finalPath;  // store the final relative path here
+  // count up how many matching directory names there are from the start
+  unsigned int sameCount = 0;
+  while(
+    ((sameCount <= (localSplit.size()-1)) && (sameCount <= (remoteSplit.size()-1)))
+    && 
+// for windows and apple do a case insensitive string compare    
+#if defined(_WIN32) || defined(__APPLE__)
+    cmSystemTools::Strucmp(localSplit[sameCount].c_str(),
+                           remoteSplit[sameCount].c_str()) == 0
+#else
+    localSplit[sameCount] == remoteSplit[sameCount]
+#endif
+    )
+    {
+    // put the common parts of the path into the commonPath array
+    commonPath.push_back(localSplit[sameCount]);
+    // erase the common parts of the path from the original path arrays
+    localSplit[sameCount] = "";
+    remoteSplit[sameCount] = "";
+    sameCount++;
+    }
+  // If there is nothing in common but the root directory, then just
+  // return the full path.
+  if(sameCount <= 1)
+    {
+    return remote;
+    }
+  
+  // for each entry that is not common in the local path
+  // add a ../ to the finalpath array, this gets us out of the local
+  // path into the remote dir
+  for(unsigned int i = 0; i < localSplit.size(); ++i)
+    {
+    if(localSplit[i].size())
+      {
+      finalPath.push_back("../");
+      }
+    }
+  // for each entry that is not common in the remote path add it
+  // to the final path.
+  for(std::vector<kwsys_stl::string>::iterator i = remoteSplit.begin();
+      i != remoteSplit.end(); ++i)
+    {
+    if(i->size())
+      {
+      finalPath.push_back(*i);
+      }
+    }
+  std::string relativePath;     // result string
+  // now turn the array of directories into a unix path by puttint / 
+  // between each entry that does not already have one
+  for(std::vector<kwsys_stl::string>::iterator i = finalPath.begin();
+      i != finalPath.end(); ++i)
+    {
+    if(relativePath.size() && relativePath[relativePath.size()-1] != '/')
+      {
+      relativePath += "/";
+      }
+    relativePath += *i;
+    }
+  return relativePath;
+}
 
 // OK, some fun stuff to get the actual case of a given path.
 // Basically, you just need to call ShortPath, then GetLongPathName,
diff --git a/SystemTools.hxx.in b/SystemTools.hxx.in
index 7fa88f5..9303d71 100644
--- a/SystemTools.hxx.in
+++ b/SystemTools.hxx.in
@@ -20,6 +20,7 @@
 #include <@KWSYS_NAMESPACE@/stl/map>
 
 #include <@KWSYS_NAMESPACE@/Configure.h>
+#include <@KWSYS_NAMESPACE@/String.hxx>
 
 #include <sys/types.h>
 
@@ -176,6 +177,13 @@ public:
    */
   static kwsys_stl::string CropString(const kwsys_stl::string&,size_t max_len);
   
+  /** split a path by separator into an array of strings, default is /.
+      If isPath is true then the string is treated like a path and if
+      s starts with a / then the first element of the returned array will
+      be /, so /foo/bar will be [/, foo, bar]
+  */  
+  static std::vector<String> SplitString(const char* s, char separator = '/', 
+                                         bool isPath = false);
   /**
    * Perform a case-independent string comparison
    */
@@ -574,6 +582,16 @@ public:
   static kwsys_stl::string FileExistsInParentDirectories(const char* fname,
     const char* directory, const char* toplevel);
 
+  /** compute the relative path from local to remote.  local must 
+      be a directory.  remote can be a file or a directory.  
+      Both remote and local must be full paths.  Basically, if
+      you are in directory local and you want to access the file in remote
+      what is the relative path to do that.  For example:
+      /a/b/c/d to /a/b/c1/d1 -> ../../c1/d1
+      from /usr/src to /usr/src/test/blah/foo.cpp -> test/blah/foo.cpp
+  */
+  static std::string RelativePath(const char* local, const char* remote);
+  
   /**
    * Return file's modified time
    */
-- 
GitLab