Add relative() to utils
authorMikko Rasa <tdb@tdb.fi>
Sun, 27 Aug 2006 17:23:03 +0000 (17:23 +0000)
committerMikko Rasa <tdb@tdb.fi>
Sun, 27 Aug 2006 17:23:03 +0000 (17:23 +0000)
Unify init and operator/= with add_component
Allow a Path to start with ..

source/path.cpp
source/path.h
source/utils.cpp
source/utils.h

index 1a9460ac9c15f4496fd320adacf89d9aed36401a..6127f7a84c06955dc4963589a8406515d3bd4207 100644 (file)
@@ -34,6 +34,9 @@ bool Path::is_absolute() const
        return false;
 }
 
+/**
+Extracts the given component range from the path.
+*/
 Path Path::subpath(unsigned start, unsigned count) const
 {
        Path result;
@@ -60,26 +63,7 @@ Path &Path::operator/=(const Path &p)
        else
        {
                for(iterator i=p.begin(); i!=p.end(); ++i)
-               {
-                       if(*i=="..")
-                       {
-                               unsigned slash=path.rfind(DIRCHAR);
-#ifdef WIN32
-                               if(is_windows_drive(path.substr(0,slash))) ++slash;
-#endif
-                               if(slash==0) ++slash;
-                               if(slash==string::npos)
-                                       path="";
-                               else
-                                       path.erase(slash);
-                       }
-                       else if(*i!=".")
-                       {
-                               if(path.size()>1 || (path.size()==1 && path[0]!=DIRCHAR))
-                                       path+=DIRCHAR;
-                               path+=*i;
-                       }
-               }
+                       add_component(*i);
        }
        return *this;
 }
@@ -116,20 +100,17 @@ bool Path::operator==(const Path &p) const
 #endif
 }
 
-void Path::init(const std::string &p)
+void Path::init(const string &p)
 {
        unsigned start=0;
-       bool absolute=false;
-       if(p[0]==DIRCHAR)
-               absolute=true;
+       if(p[0]=='/' || p[0]=='\\')
+               add_component(string(1, DIRCHAR));
        while(1)
        {
                unsigned slash=p.find_first_of("/\\",start);
                if(slash>start)
                {
-                       if(path.size() || absolute)
-                               path+=DIRCHAR;
-                       path+=p.substr(start,slash-start);
+                       add_component(p.substr(start, slash-start));
                }
                if(slash==string::npos)
                        break;
@@ -137,6 +118,62 @@ void Path::init(const std::string &p)
        }
 }
 
+/**
+Adds a single component to the path, emulating the cd command.
+*/
+void Path::add_component(const string &comp)
+{
+       if(comp.empty())
+               ;
+       else if(comp.size()==1 && comp[0]==DIRCHAR)
+       {
+               // Replace the path with the root directory
+#ifdef WIN32
+               unsigned slash=path.find(DIRCHAR);
+               if(is_windows_drive(path.substr(0, slash)))
+                       path=path.substr(0,2);
+               else
+#endif
+               path=comp;
+       }
+#ifdef WIN32
+       else if(is_windows_drive(comp))
+               path=comp;
+#endif
+       else if(comp=="..")
+       {
+               if(path.empty())
+                       path=comp;
+               // .. in root directory is a no-op
+               else if(path.size()==1 && path[0]==DIRCHAR)
+                       ;
+#ifdef WIN32
+               else if(is_windows_drive(path))
+                       ;
+#endif
+               else
+               {
+                       unsigned slash=path.rfind(DIRCHAR);
+                       unsigned start=(slash==string::npos)?0:slash+1;
+                       if(path.compare(start, string::npos, ".."))
+                       {
+                               // If the last component already is a .., add another
+                               path+=DIRCHAR;
+                               path+=comp;
+                       }
+                       else
+                               // Otherwise, erase the last component
+                               path.erase(slash, string::npos);
+               }
+       }
+       else if(comp!=".")
+       {
+               if(path.size()>1 || (path.size()==1 && path[0]!=DIRCHAR))
+                       path+=DIRCHAR;
+               path+=comp;
+       }
+}
+
 Path::iterator::iterator(const Path &p):
        path(p),
        start(0)
index 9e1e3747fed37c0b6687ee48c0925071bbccd6ea..0f333018fc2bdbb1de6d7f89182f6ded2ea8a606 100644 (file)
@@ -59,6 +59,7 @@ private:
        std::string path;
 
        void init(const std::string &);
+       void add_component(const std::string &);
 };
 
 inline std::ostream &operator<<(std::ostream &o, const Path &p) { o<<p.str(); return o; }
index 08ab9586a31700ed9a1399b35187caba5472f37c..204f33bfdedcf841e6b4b4e0e090d9caa1824d67 100644 (file)
@@ -158,5 +158,20 @@ int fnmatch(const string &pat, const Path &fn)
 #endif
 }
 
+Path relative(const Path &path, const Path &base)
+{
+       Path::iterator i=path.begin();
+       Path::iterator j=base.begin();
+       for(; (i!=path.end() && j!=base.end() && *i==*j); ++i,++j);
+
+       Path result;
+       for(; j!=base.end(); ++j)
+               result/="..";
+       for(; i!=path.end(); ++i)
+               result/=*i;
+
+       return result;
+}
+
 } // namespace Path
 } // namespace Msp
index 961ad499674666629ecc7b635d04032e2b5d63ac..faf5ef7f261e038e2e1d688ec013d150b93369cd 100644 (file)
@@ -28,6 +28,7 @@ extern std::list<std::string> list_files(const Path &);
 extern bool exists(const Path &);
 extern Filename splitext(const std::string &);
 extern int fnmatch(const std::string &, const Path &);
+extern Path relative(const Path &, const Path &);
 
 inline int stat(const Path &fn, struct stat &st)
 { return ::stat(fn.str().c_str(), &st); }