fixup Fix to build with libxml 2.12.x (fixes #505)
[platform/upstream/libzypp.git] / tools / zypp-NameReqPrv.cc
index 7312b3e..ec2c8b0 100644 (file)
@@ -1,10 +1,11 @@
 #define INCLUDE_TESTSETUP_WITHOUT_BOOST
-#include "zypp/../tests/lib/TestSetup.h"
+#include "../tests/lib/TestSetup.h"
 #undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
 
 #include <algorithm>
 #include <zypp/PoolQuery.h>
 #include <zypp/ResObjects.h>
+#include <zypp/ui/SelectableTraits.h>
 
 static std::string appname( "NameReqPrv" );
 
@@ -28,7 +29,7 @@ int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
   }
   cerr << "Usage: " << appname << " [--root ROOTDIR] [OPTIONS] NAME... [[OPTIONS] NAME...]..." << endl;
   cerr << "  Load all enabled repositories (no refresh) and search for" << endl;
-  cerr << "  occurrences of NAME (regex) in package names or dependencies" << endl;
+  cerr << "  occurrences of NAME (regex or -x) in package names or dependencies" << endl;
   cerr << "  --root   Load repos from the system located below ROOTDIR. If ROOTDIR" << endl;
   cerr << "           denotes a sover testcase, the testcase is loaded." << endl;
   cerr << "  --installed Process installed packages only." << endl;
@@ -43,25 +44,115 @@ int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
   cerr << "  -e/-E    turn on/off looking for enhan./sugg.(default off)" << endl;
   cerr << "  -a       short for -n -p -r" << endl;
   cerr << "  -A       short for -n -P -R" << endl;
+  cerr << "  -x       do exact matching (glob) rather than regex (substring)" << endl;
   cerr << "  -D <pkg> dump dependencies of <pkg>" << endl;
   cerr << "" << endl;
   return exit_r;
 }
 
-void tableOut( const std::string & s1 = std::string(),
-               const std::string & s2 = std::string(),
-               const std::string & s3 = std::string(),
-               const std::string & s4 = std::string(),
-               const std::string & s5 = std::string() )
+#define COL_R   "\033[0;31m"
+#define COL_G   "\033[0;32m"
+#define COL_B   "\033[0;34m"
+#define COL_C   "\033[0;36m"
+#define COL_M   "\033[0;35m"
+#define COL_Y   "\033[0;33m"
+#define COL_BL  "\033[0;30m"
+#define COL_WH  "\033[1;37m"
+#define COL_OFF "\033[0m"
+
+struct PQSort
 {
-  message << "  ";
-#define TABEL(N) static unsigned w##N = 0; if ( ! s##N.empty() ) w##N = std::max( w##N, unsigned(s##N.size()) ); message << str::form( " %-*s ", w##N, s##N.c_str() )
-#define TABER(N) static unsigned w##N = 0; if ( ! s##N.empty() ) w##N = std::max( w##N, unsigned(s##N.size()) ); message << str::form( " %*s ", w##N, s##N.c_str() )
-  TABER( 1 ); TABEL( 2 ); TABEL( 3 ); TABEL( 4 ); TABEL( 5 );
-#undef TABEL
-  message << endl;
-}
+  // std::less semantic
+  bool operator()( const sat::Solvable & lhs, const sat::Solvable & rhs ) const
+  {
+    {
+      bool l = lhs.isSystem();
+      bool r = rhs.isSystem();
+      if ( l != r )
+       return r;
+    }
+    {
+      IdString l { lhs.ident() };
+      IdString r { rhs.ident() };
+      if ( l != r )
+       return l < r;
+    }
+    return avo( PoolItem(lhs), PoolItem(rhs) );
+    return lhs.id() > rhs.id();
+  }
+
+  ui::SelectableTraits::AVOrder avo;
+};
+
+struct Table
+{
+  std::vector<std::string> & row( const sat::Solvable & solv_r )
+  {
+    //smax( _maxSID,  );
+    smax( _maxNAME, solv_r.ident().size() + solv_r.edition().size() + solv_r.arch().size() );
+    smax( _maxREPO, solv_r.repository().name().size(), solv_r.repository().name() );
+    //smax( _maxTIME, );
+    //smax( _maxVEND, solv_r.vendor().size() );
+    return _map[solv_r];
+  }
+
+  void row( PoolQuery::const_iterator it_r )
+  {
+    std::vector<std::string> & details { row( *it_r ) };
+    for_( match, it_r.matchesBegin(), it_r.matchesEnd() ) {
+      details.push_back( match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString() );
+    }
+  }
+
+  std::ostream & dumpOn( std::ostream & str ) const
+  {
+    #define S "  "
+
+    #define fmtSID  "%*d"
+    #define argSID  _maxSID, slv.id()
+
+    #define fmtNAME COL_B "%s" COL_OFF "-" COL_G "%s" COL_OFF ".%-*s"
+    #define argNAME slv.ident().c_str(), slv.edition().c_str(), _maxNAME-slv.ident().size()-slv.edition().size(), slv.arch().c_str()
+
+    #define fmtREPO "(%2d)%-*s"
+    #define argREPO slv.repository().info().priority(), _maxREPO, slv.repository().name().c_str()
+
+    #define fmtTIME "%10ld"
+    #define argTIME time_t( slv.isSystem() ? slv.installtime() : slv.buildtime() )
 
+    #define fmtVEND "%s"
+    #define argVEND slv.vendor().c_str()
+
+    std::string dind( _maxSID + _maxNAME+2/*-.*/ + 2*strlen(S), ' ' );
+
+    for ( const auto & el : _map ) {
+      sat::Solvable slv { el.first };
+      const char * tagCol = slv.isSystem() ? COL_M : ui::Selectable::get(slv)->identicalInstalled(PoolItem(slv)) ? COL_C : "";
+
+      str << str::form( "%s"    fmtSID S fmtNAME S "%s"    fmtREPO S fmtTIME S fmtVEND COL_OFF "\n",
+                       tagCol, argSID,  argNAME,  tagCol, argREPO,  argTIME,  argVEND );
+
+      for ( const auto & d : el.second )
+       str << dind << d << endl;
+    }
+    return str;
+  }
+
+private:
+  static void smax( unsigned & var_r, unsigned val_r, std::string_view n = {} )
+  { if ( val_r > var_r ) var_r = val_r; }
+
+private:
+  unsigned _maxSID  = 7;       // size + indent
+  unsigned _maxNAME = 0;
+  unsigned _maxREPO = 0;
+  //unsigned _maxTIME = 10;
+  //unsigned _maxVEND = 0;
+  std::map<sat::Solvable, std::vector<std::string>, PQSort> _map;
+};
+
+inline std::ostream & operator<<( std::ostream & str, const Table & table_r )
+{ return table_r.dumpOn( str ); }
 
 ///////////////////////////////////////////////////////////////////
 
@@ -87,10 +178,83 @@ void dDump( const std::string & spec_r )
     {
       message << endl << "CONTENT: " << make<Pattern>(el)->contents();
     }
+    else if ( isKind<Patch>(el) )
+    {
+      message << endl << "STATUS: " << PoolItem(el);
+    }
   }
   message << endl << "}" << endl;
 }
 
+///////////////////////////////////////////////////////////////////
+
+void dTree( const std::string & cmd_r, const std::string & spec_r )
+{
+  message << "tree " << spec_r << " {";
+
+  sat::WhatProvides spec( Capability::guessPackageSpec( spec_r ) );
+  if ( spec.empty() )
+  {
+    message << "}" << endl;
+    return;
+  }
+
+  static const std::list<sat::SolvAttr> attrs {
+    sat::SolvAttr::requires,
+    sat::SolvAttr::recommends,
+    sat::SolvAttr::obsoletes,
+    sat::SolvAttr::conflicts,
+    sat::SolvAttr::supplements,
+  };
+
+  std::map<sat::SolvAttr,std::map<sat::Solvable,std::set<std::string>>> result;        // solvables recommending provided capability
+  {
+    Table t;
+    for ( const auto & el : spec )
+    {
+      auto & details { t.row( el ) };
+      for ( const auto & cap : el.provides() )
+      {
+       //details.push_back( "prv: "+cap.asString() );  // list only matching ones
+
+       // get attrs matching cap
+       for ( const auto & attr : attrs )
+       {
+         PoolQuery q;
+         q.addDependency( attr, cap );
+         if ( q.empty() )
+           continue;
+         for_( it, q.begin(), q.end() ) {
+           for_( match, it.matchesBegin(), it.matchesEnd() ) {
+             result[attr][*it].insert( match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString()
+             + " (" + cap.asString() + ")"
+             );
+           }
+         }
+       }
+      }
+    }
+    message << endl << t;
+  }
+
+  for ( const auto & attr : attrs )
+  {
+    if ( result[attr].empty() )
+      continue;
+
+    message << endl << "======== " << attr << " by:" << endl;
+    Table t;
+    for ( const auto & el : result[attr] )
+    {
+      auto & details { t.row( el.first ) };
+      for ( const auto & cap : el.second )
+       details.push_back( cap );
+    }
+    message << endl << t << endl;
+  }
+  message << "}" << endl;
+}
+
 /******************************************************************
 **
 **      FUNCTION NAME : main
@@ -143,7 +307,12 @@ int main( int argc, char * argv[] )
   else if ( TestSetup::isTestSetup( sysRoot ) )
   {
     message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
-    TestSetup test( sysRoot, Arch_x86_64 );
+    const char * astr = getenv( "ZYPP_TESTSUITE_FAKE_ARCH" );
+    if ( !astr || !*astr )
+      astr = getenv( "ZYPP_ARCH" );
+    if ( !astr || !*astr )
+      astr = "x86_64";
+    TestSetup test( sysRoot, Arch( astr ) );
     test.loadRepos();
     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
   }
@@ -191,10 +360,11 @@ int main( int argc, char * argv[] )
       }
     }
   }
-
   ///////////////////////////////////////////////////////////////////
 
   bool ignorecase      ( true );
+  bool matechexact     ( false );
+  bool withSrcPackages ( false );
   bool names           ( true );
   bool provides                ( false );
   bool requires                ( false );
@@ -204,41 +374,56 @@ int main( int argc, char * argv[] )
   bool supplements     ( false );
   bool enhacements     ( false );
 
+
   for ( ; argc; --argc,++argv )
   {
     if ( (*argv)[0] == '-' )
     {
-      switch ( (*argv)[1] )
+      for ( const char * arg = (*argv)+1; *arg != '\0'; ++arg )        // -pr for -p -r
       {
-        case 'a': names =      true,   requires = provides =   true;   break;
-        case 'A': names =      true,   requires = provides =   false;  break;
-       case 'D':
-         if ( argc > 1 )
-         {
-           --argc,++argv;
-           dDump( *argv );
-         }
-         else
-           return errexit("-D <pkgspec> requires an argument.");
-         break;
-        case 'i': ignorecase = true;   break;
-        case 'I': ignorecase = false;  break;
-        case 'n': names =      true;   break;
-        case 'N': names =      false;  break;
-        case 'r': requires =   true;   break;
-        case 'R': requires =   false;  break;
-        case 'p': provides =   true;   break;
-        case 'P': provides =   false;  break;
-        case 'c': conflicts =  true;   break;
-        case 'C': conflicts =  false;  break;
-        case 'o': obsoletes =  true;   break;
-        case 'O': obsoletes =  false;  break;
-        case 'm': recommends = true;   break;
-        case 'M': recommends = false;  break;
-        case 's': supplements =        true;   break;
-        case 'S': supplements =        false;  break;
-        case 'e': enhacements =        true;   break;
-        case 'E': enhacements =        false;  break;
+       switch ( *arg )
+       {
+         case 'a': names =             true,   requires = provides =   true;   break;
+         case 'A': names =             true,   requires = provides =   false;  break;
+         case 'D':
+           if ( argc > 1 )
+           {
+             --argc,++argv;
+             dDump( *argv );
+           }
+           else
+             return errexit("-D <pkgspec> requires an argument.");
+           break;
+         case 'T':
+           if ( argc > 1 )
+           {
+             std::string cmd { *argv };
+             --argc,++argv;
+             dTree( cmd, *argv );
+           }
+           else
+             return errexit("-T <pkgspec> requires an argument.");
+           break;
+         case 'i': ignorecase =        true;   break;
+         case 'I': ignorecase =        false;  break;
+         case 'x': matechexact =       true;   break;
+         case 'n': names =             true;   break;
+         case 'N': names =             false;  break;
+         case 'r': requires =          true;   break;
+         case 'R': requires =          false;  break;
+         case 'p': provides =          true;   break;
+         case 'P': provides =          false;  break;
+         case 'c': conflicts =         true;   break;
+         case 'C': conflicts =         false;  break;
+         case 'o': obsoletes =         true;   break;
+         case 'O': obsoletes =         false;  break;
+         case 'm': recommends =        true;   break;
+         case 'M': recommends =        false;  break;
+         case 's': supplements =       true;   break;
+         case 'S': supplements =       false;  break;
+         case 'e': enhacements =       true;   break;
+         case 'E': enhacements =       false;  break;
+       }
       }
       continue;
     }
@@ -271,27 +456,50 @@ int main( int argc, char * argv[] )
       else
        q.addString( qstr );
 
-      q.setMatchRegex();
+      if ( matechexact )
+       q.setMatchGlob();
+      else
+       q.setMatchRegex();
       q.setCaseSensitive( ! ignorecase );
 
       if ( names )
        q.addAttribute( sat::SolvAttr::name );
       if ( provides )
+      {
        q.addDependency( sat::SolvAttr::provides );
+       q.addDependency( sat::SolvAttr::provides, Capability(qstr) );
+      }
       if ( requires )
+      {
        q.addDependency( sat::SolvAttr::requires );
+       q.addDependency( sat::SolvAttr::requires, Capability(qstr) );
+      }
       if ( conflicts )
+      {
        q.addDependency( sat::SolvAttr::conflicts );
+       q.addDependency( sat::SolvAttr::conflicts, Capability(qstr) );
+      }
       if ( obsoletes )
+      {
        q.addDependency( sat::SolvAttr::obsoletes );
+       q.addDependency( sat::SolvAttr::obsoletes, Capability(qstr) );
+      }
       if ( recommends )
+      {
        q.addDependency( sat::SolvAttr::recommends );
+       q.addDependency( sat::SolvAttr::recommends, Capability(qstr) );
+      }
       if ( supplements )
+      {
        q.addDependency( sat::SolvAttr::supplements );
+       q.addDependency( sat::SolvAttr::supplements, Capability(qstr) );
+      }
       if ( enhacements )
       {
        q.addDependency( sat::SolvAttr::enhances );
+       q.addDependency( sat::SolvAttr::enhances, Capability(qstr) );
        q.addDependency( sat::SolvAttr::suggests );
+       q.addDependency( sat::SolvAttr::suggests, Capability(qstr) );
       }
     }
 
@@ -299,23 +507,14 @@ int main( int argc, char * argv[] )
     << (conflicts?'c':'_') << (obsoletes?'o':'_') << (recommends?'m':'_') << (supplements?'s':'_') << (enhacements?'e':'_')
     << "] {" << endl;
 
+    Table t;
     for_( it, q.begin(), q.end() )
     {
-      tableOut( str::numstring( it->id() ), it->asString(),
-               str::form( "(%d)%s", it->repository().info().priority(), it->repository().name().c_str() ),
-               str::numstring( PoolItem(*it)->buildtime() ) );
-      tableOut( "", "",
-               it->vendor().asString() );
-      if ( ! it.matchesEmpty() )
-      {
-       for_( match, it.matchesBegin(), it.matchesEnd() )
-       {
-         tableOut( "", "", match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString() );
-       }
-      }
+      if ( it->isKind( ResKind::srcpackage ) && !withSrcPackages )
+       continue;
+      t.row( it );
     }
-
-    message << "}" << endl;
+    message << t << "}" << endl;
   }
 
   INT << "===[END]============================================" << endl << endl;