fixup Fix to build with libxml 2.12.x (fixes #505)
[platform/upstream/libzypp.git] / zypp / Pathname.cc
index 9ad4ebb..d81a472 100644 (file)
@@ -11,8 +11,9 @@
 */
 #include <iostream>
 
-#include "zypp/Pathname.h"
-#include "zypp/Url.h"
+#include <zypp/base/String.h>
+#include <zypp/Pathname.h>
+#include <zypp/Url.h>
 
 using std::string;
 
@@ -24,162 +25,99 @@ namespace zypp
   { /////////////////////////////////////////////////////////////////
 
     ///////////////////////////////////////////////////////////////////
-    namespace
-    { /////////////////////////////////////////////////////////////////
-
-      ///////////////////////////////////////////////////////////////////
-      //
-      //       CLASS NAME : DirStack
-      //
-      /** silly helper to build Pathnames.
-      */
-      class DirStack {
-
-        struct Dir {
-
-          Dir *  up;
-          Dir *  dn;
-          string name;
-
-          Dir( const string & n = "" ) {
-            name = n;
-            up = dn = 0;
-          }
-
-          ~Dir() {
-            if ( up )
-              up->dn = dn;
-            if ( dn )
-              dn->up = up;
-          }
-        };
-
-        Dir *  top;
-        Dir *  bot;
-
-        void Pop() {
-          if ( !top )
-            return;
-          top = top->dn;
-          if ( top )
-            delete top->up;
-          else {
-            delete bot;
-            bot = 0;
-          }
-        }
-
-      public:
-
-        DirStack() { top = bot = 0; }
-        ~DirStack() {
-          while ( bot )
-            Pop();
-        }
-
-        void Push( const string & n ) {
-          if ( n.empty() || n == "." ) { // '.' or '/' only for bot
-            if ( bot )
-              return;
-          } else if ( n == ".." && top ) {
-            if ( top->name == "" )          // "/.."        ==> "/"
-              return;
-
-            if ( top->name != "." && top->name != ".." ) {      // "somedir/.." ==> ""
-              Pop();
-              return;
-            }
-            // "../.." "./.." stays
-          }
-
-          Dir * d = new Dir( n );
-          if ( !top )
-            top = bot = d;
-          else {
-            top->up = d;
-            d->dn = top;
-            d->up = 0;
-            top = d;
-          }
-        }
-
-        string str() {
-          if ( !bot )
-            return "";
-          string ret;
-          for ( Dir * d = bot; d; d = d->up ) {
-            if ( d != bot )
-              ret += "/";
-            ret += d->name;
-          }
-          if ( ret.empty() )
-            return "/";
-          return ret;
-        }
-      };
-
-      /////////////////////////////////////////////////////////////////
-    } // namespace
-    ///////////////////////////////////////////////////////////////////
-
-    ///////////////////////////////////////////////////////////////////
     //
     // METHOD NAME : Pathname::_assign
     // METHOD TYPE : void
     //
-    void Pathname::_assign( const string & name_tv )
+    void Pathname::_assign( const string & name_r )
     {
-      prfx_i = 0;
-      name_t = name_tv;
-
-      if ( name_t.empty() )
+      _name.clear();
+      if ( name_r.empty() )
         return;
-
-      string   Tprfx;
-      DirStack Stack_Ci;
-
-      char *       Buf_aci    = new char[name_tv.length() + 1];
-      char *       W_pci      = Buf_aci;
-      const char * R_pci      = name_tv.c_str();
-
-      // check for prefix
-      if (    name_t.length() >= 2
-           && name_t[1] == ':'
-           && (    'a' <= name_t[0] && name_t[0] <= 'z'
-                || 'A' <= name_t[0] && name_t[0] <= 'Z' ) ) {
-        Tprfx  = name_t.substr( 0, 2 );
-        prfx_i = 2;
-        R_pci += 2;
+      _name.reserve( name_r.size() );
+
+      // Collect up to "/.."
+      enum Pending {
+       P_none  = 0,    // ""
+       P_slash = 1,    // "/"
+       P_dot1  = 2,    // "/."
+       P_dot2  = 3     // "/.."
+      } pending = P_none;
+
+      // Assert relative path starting with "./"
+      // We rely on this below!
+      if ( name_r[0] != '/' )
+      {
+       _name += '.';
+       pending = P_slash;
       }
 
-      // rel or abs path
-      if ( *R_pci == '/' || *R_pci == '\\' ) {
-        Stack_Ci.Push( "" );
-        ++R_pci;
-      } else {
-        Stack_Ci.Push( "." );
+      // Lambda handling the "/.." case:
+      // []      + "/.."  ==> []
+      // [.]     + "/.."  ==> [./..]
+      // [foo]   is always [./foo] due to init above
+      // [*/..]  + "/.."  ==> [*/../..]
+      // [*/foo] + "/.."  ==> [*]
+      auto goParent_f =  [&](){
+       if ( _name.empty() )
+         /*NOOP*/;
+       else if ( _name.size() == 1 ) // content is '.'
+         _name += "/..";
+       else
+       {
+         std::string::size_type pos = _name.rfind( "/" );
+         if ( pos == _name.size() - 3 && _name[pos+1] == '.' && _name[pos+2] == '.' )
+           _name += "/..";
+         else
+           _name.erase( pos );
+       }
+      };
+
+      for ( char ch : name_r )
+      {
+       switch ( ch )
+       {
+         case '/':
+           switch ( pending )
+           {
+             case P_none:      pending = P_slash; break;
+             case P_slash:     break;
+             case P_dot1:      pending = P_slash; break;
+             case P_dot2:      goParent_f(); pending = P_slash; break;
+           }
+           break;
+
+         case '.':
+           switch ( pending )
+           {
+             case P_none:      _name += '.'; break;
+             case P_slash:     pending = P_dot1; break;
+             case P_dot1:      pending = P_dot2; break;
+             case P_dot2:      _name += "/..."; pending = P_none; break;
+           }
+           break;
+
+         default:
+           switch ( pending )
+           {
+             case P_none:      break;
+             case P_slash:     _name += '/';    pending = P_none; break;
+             case P_dot1:      _name += "/.";   pending = P_none; break;
+             case P_dot2:      _name += "/.."; pending = P_none; break;
+           }
+           _name += ch;
+           break;
+       }
       }
 
-      do {
-        switch ( *R_pci ) {
-        case '/':
-        case '\\':
-        case '\0':
-          if ( W_pci != Buf_aci ) {
-            *W_pci = '\0';
-            W_pci = Buf_aci;
-            Stack_Ci.Push( Buf_aci );
-          }
-          break;
-
-        default:
-          *W_pci++ = *R_pci;
-          break;
-        }
-      } while( *R_pci++ );
-
-      delete[] Buf_aci;
-      name_t = Tprfx + Stack_Ci.str();
+      switch ( pending )
+      {
+       case P_none:    break;
+       case P_slash:   if ( _name.empty() ) _name = "/"; break;
+       case P_dot1:    if ( _name.empty() ) _name = "/"; break;
+       case P_dot2:    goParent_f(); if ( _name.empty() ) _name = "/"; break;
+      }
+      return;
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -187,22 +125,20 @@ namespace zypp
     // METHOD NAME : Pathname::dirname
     // METHOD TYPE : Pathname
     //
-    Pathname Pathname::dirname( const Pathname & name_tv )
+    Pathname Pathname::dirname( const Pathname & name_r )
     {
-      if ( name_tv.empty() )
-        return "";
-
-      Pathname ret_t( name_tv );
-      string::size_type idx = ret_t.name_t.find_last_of( '/' );
-
-      if ( idx == string::npos ) {
-        ret_t.name_t.erase( ret_t.prfx_i );
-        ret_t.name_t += ".";
-      } else if ( idx == ret_t.prfx_i ) {
-        ret_t.name_t.erase( ret_t.prfx_i );
-        ret_t.name_t += "/";
+      if ( name_r.empty() )
+        return Pathname();
+
+      Pathname ret_t( name_r );
+      std::string::size_type idx = ret_t._name.find_last_of( '/' );
+
+      if ( idx == std::string::npos ) {
+        ret_t._name = ".";
+      } else if ( idx == 0 ) {
+        ret_t._name = "/";
       } else {
-        ret_t.name_t.erase( idx );
+        ret_t._name.erase( idx );
       }
 
       return ret_t;
@@ -213,15 +149,14 @@ namespace zypp
     // METHOD NAME : Pathname::basename
     // METHOD TYPE : string
     //
-    string Pathname::basename( const Pathname & name_tv )
+    string Pathname::basename( const Pathname & name_r )
     {
-      if ( name_tv.empty() )
+      if ( name_r.empty() )
         return string();
 
-      string ret_t( name_tv.asString() );
-      ret_t.erase( 0, name_tv.prfx_i );
-      string::size_type idx = ret_t.find_last_of( '/' );
-      if ( idx != string::npos ) {
+      string ret_t( name_r.asString() );
+      std::string::size_type idx = ret_t.find_last_of( '/' );
+      if ( idx != std::string::npos && ( idx != 0 || ret_t.size() != 1 ) ) {
         ret_t.erase( 0, idx+1 );
       }
 
@@ -233,46 +168,107 @@ namespace zypp
     // METHOD NAME : Pathname::asUrl
     // METHOD TYPE : Url
     //
-    Url Pathname::asUrl() const
+    Url Pathname::asUrl( const std::string & scheme_r ) const
     {
-      Url ret( "dir:///" );
+      Url ret;
       ret.setPathName( asString() );
+      ret.setScheme( scheme_r );
       return ret;
     }
 
+    Url Pathname::asUrl() const
+    { return asUrl( "dir" ); }
+
+    Url Pathname::asDirUrl() const
+    { return asUrl( "dir" ); }
+
+    Url Pathname::asFileUrl() const
+    { return asUrl( "file" ); }
+
+
+    std::string Pathname::showRoot( const Pathname & root_r, const Pathname & path_r )
+    {
+      return str::Str() << "(" << root_r << ")" << path_r;
+    }
+
+    std::string Pathname::showRootIf( const Pathname & root_r, const Pathname & path_r )
+    {
+      if ( root_r.empty() || root_r == "/" )
+        return path_r.asString();
+      return showRoot( root_r, path_r );
+    }
+
     ///////////////////////////////////////////////////////////////////
     //
     // METHOD NAME : Pathname::extension
     // METHOD TYPE : string
     //
-    string Pathname::extension( const Pathname & name_tv )
+    string Pathname::extension( const Pathname & name_r )
     {
-      if ( name_tv.empty() )
+      if ( name_r.empty() )
         return string();
 
-      string base( basename( name_tv ) );
-      string::size_type pos = base.rfind( '.' );
-      if ( pos == string::npos )
-        return string();
+      string base( basename( name_r ) );
+      std::string::size_type pos = base.rfind( '.' );
+      switch ( pos )
+      {
+       case 0:
+         if ( base.size() == 1 )                       // .
+           return string();
+         break;
+       case 1:
+         if ( base.size() == 2 && base[0] == '.' )     // ..
+           return string();
+         break;
+       case std::string::npos:
+         return string();
+         break;
+      }
       return base.substr( pos );
     }
 
     ///////////////////////////////////////////////////////////////////
     //
+    // METHOD NAME : Pathname::assertprefix
+    // METHOD TYPE : Pathname
+    //
+    Pathname Pathname::assertprefix( const Pathname & root_r, const Pathname & path_r )
+    {
+      if ( root_r.empty()
+           || path_r == root_r
+           || str::hasPrefix( path_r.asString(), root_r.asString() ) )
+        return path_r;
+      return root_r / path_r;
+    }
+
+    Pathname Pathname::stripprefix( const Pathname & root_r, const Pathname & path_r )
+    {
+      if ( root_r.emptyOrRoot() )
+       return path_r;
+      if ( root_r == path_r )
+       return "/";
+      std::string rest( str::stripPrefix( path_r.asString(), root_r.asString() ) );
+      if ( rest[0] == '/' )    // needs to be a dir prefix!
+       return rest;
+      return path_r;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
     // METHOD NAME : Pathname::cat
     // METHOD TYPE : Pathname
     //
-    Pathname Pathname::cat( const Pathname & name_tv, const Pathname & add_tv )
+    Pathname Pathname::cat( const Pathname & name_r, const Pathname & add_tv )
     {
       if ( add_tv.empty() )
-        return name_tv;
-      if ( name_tv.empty() )
+        return name_r;
+      if ( name_r.empty() )
         return add_tv;
 
-      string ret_ti( add_tv.asString() );
-      ret_ti.replace( 0, add_tv.prfx_i, "/" );
-
-      return name_tv.asString() + ret_ti;
+      string ret_ti( name_r._name );
+      if( add_tv._name[0] != '/' )
+       ret_ti += '/';
+      return ret_ti + add_tv._name;
     }
 
     ///////////////////////////////////////////////////////////////////