Imported Upstream version 17.17.0
[platform/upstream/libzypp.git] / tools / zypp-NameReqPrv.cc
1 #define INCLUDE_TESTSETUP_WITHOUT_BOOST
2 #include "zypp/../tests/lib/TestSetup.h"
3 #undef  INCLUDE_TESTSETUP_WITHOUT_BOOST
4
5 #include <algorithm>
6 #include <zypp/PoolQuery.h>
7 #include <zypp/ResObjects.h>
8 #include <zypp/ui/SelectableTraits.h>
9
10 static std::string appname( "NameReqPrv" );
11
12 #define message cout
13 using std::flush;
14
15 int errexit( const std::string & msg_r = std::string(), int exit_r = 100 )
16 {
17   if ( ! msg_r.empty() )
18   {
19     cerr << endl << msg_r << endl << endl;
20   }
21   return exit_r;
22 }
23
24 int usage( const std::string & msg_r = std::string(), int exit_r = 100 )
25 {
26   if ( ! msg_r.empty() )
27   {
28     cerr << endl << msg_r << endl << endl;
29   }
30   cerr << "Usage: " << appname << " [--root ROOTDIR] [OPTIONS] NAME... [[OPTIONS] NAME...]..." << endl;
31   cerr << "  Load all enabled repositories (no refresh) and search for" << endl;
32   cerr << "  occurrences of NAME (regex or -x) in package names or dependencies" << endl;
33   cerr << "  --root   Load repos from the system located below ROOTDIR. If ROOTDIR" << endl;
34   cerr << "           denotes a sover testcase, the testcase is loaded." << endl;
35   cerr << "  --installed Process installed packages only." << endl;
36   cerr << "  -i/-I    turn on/off case insensitive search (default on)" << endl;
37   cerr << "  -n/-N    turn on/off looking for names       (default on)" << endl;
38   cerr << "  -p/-P    turn on/off looking for provides    (default off)" << endl;
39   cerr << "  -r/-R    turn on/off looking for requires    (default off)" << endl;
40   cerr << "  -c/-C    turn on/off looking for conflicts   (default off)" << endl;
41   cerr << "  -o/-O    turn on/off looking for obsoletes   (default off)" << endl;
42   cerr << "  -m/-M    turn on/off looking for recommends  (default off)" << endl;
43   cerr << "  -s/-S    turn on/off looking for supplements (default off)" << endl;
44   cerr << "  -e/-E    turn on/off looking for enhan./sugg.(default off)" << endl;
45   cerr << "  -a       short for -n -p -r" << endl;
46   cerr << "  -A       short for -n -P -R" << endl;
47   cerr << "  -x       do exact matching (glob) rather than regex (substring)" << endl;
48   cerr << "  -D <pkg> dump dependencies of <pkg>" << endl;
49   cerr << "" << endl;
50   return exit_r;
51 }
52
53 #define COL_R   "\033[0;31m"
54 #define COL_G   "\033[0;32m"
55 #define COL_B   "\033[0;34m"
56 #define COL_C   "\033[0;36m"
57 #define COL_M   "\033[0;35m"
58 #define COL_Y   "\033[0;33m"
59 #define COL_BL  "\033[0;30m"
60 #define COL_WH  "\033[1;37m"
61 #define COL_OFF "\033[0m"
62
63 std::string colorId( sat::Solvable solv_r )
64 {
65   // return solv_r.asString();
66   std::string ret;
67   if ( solv_r )
68   {
69     static str::Format fmt { COL_B "%s" COL_OFF "-" COL_G "%s" COL_OFF ".%s" };
70     ret = fmt % solv_r.name() % solv_r.edition() % solv_r.arch();
71   }
72   else
73   {
74     ret = ( solv_r.id() == sat::detail::systemSolvableId ?  "systemSolvable" : "noSolvable" );
75   }
76   return ret;
77 }
78
79 void tableOut( const std::string & s1 = std::string(),
80                const std::string & s2 = std::string(),
81                const std::string & s3 = std::string(),
82                const std::string & s4 = std::string(),
83                const std::string & s5 = std::string() )
84 {
85   message << "  ";
86 #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() )
87 #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() )
88   TABER( 1 ); TABEL( 2 ); TABEL( 3 ); TABEL( 4 ); TABEL( 5 );
89 #undef TABEL
90   message << endl;
91 }
92
93 struct PQSort
94 {
95   // std::less semantic
96   bool operator()( const PoolQuery::const_iterator & lhs, const PoolQuery::const_iterator & rhs ) const
97   {
98     {
99       bool l = lhs->isSystem();
100       bool r = rhs->isSystem();
101       if ( l != r )
102         return r;
103     }
104     {
105       std::string l( lhs->ident().asString() );
106       std::string r( rhs->ident().asString() );
107       if ( l != r )
108         return l < r;
109     }
110     return avo( PoolItem(*lhs), PoolItem(*rhs) );
111     return lhs->id() > rhs->id();
112   }
113
114   ui::SelectableTraits::AVOrder avo;
115 };
116
117 ///////////////////////////////////////////////////////////////////
118
119 void dDump( const std::string & spec_r )
120 {
121   message << "DUMP " << spec_r << " {";
122
123   sat::WhatProvides q( Capability::guessPackageSpec( spec_r ) );
124   if ( q.empty() )
125   {
126     message << "}" << endl;
127     return;
128   }
129
130   for ( const auto & el : q )
131   {
132     message << endl << "==============================" << endl << dump(el);
133     if ( isKind<Product>(el) )
134     {
135       message << endl << "REPLACES: " << make<Product>(el)->replacedProducts();
136     }
137     else if ( isKind<Pattern>(el) )
138     {
139       message << endl << "CONTENT: " << make<Pattern>(el)->contents();
140     }
141   }
142   message << endl << "}" << endl;
143 }
144
145 /******************************************************************
146 **
147 **      FUNCTION NAME : main
148 **      FUNCTION TYPE : int
149 */
150 int main( int argc, char * argv[] )
151 {
152   INT << "===[START]==========================================" << endl;
153   appname = Pathname::basename( argv[0] );
154   --argc,++argv;
155
156   if ( ! argc )
157   {
158     return usage();
159   }
160
161   ///////////////////////////////////////////////////////////////////
162
163   ZConfig::instance();
164   Pathname sysRoot("/");
165   sat::Pool satpool( sat::Pool::instance() );
166
167   if ( argc && (*argv) == std::string("--root") )
168   {
169     --argc,++argv;
170     if ( ! argc )
171       return errexit("--root requires an argument.");
172
173     if ( ! PathInfo( *argv ).isDir() )
174       return errexit("--root requires a directory.");
175
176     sysRoot = *argv;
177     --argc,++argv;
178   }
179
180   bool onlyInstalled( false );
181   if ( argc && (*argv) == std::string("--installed") )
182   {
183     --argc,++argv;
184     onlyInstalled = true;
185   }
186
187   if ( TestSetup::isTestcase( sysRoot ) )
188   {
189     message << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
190     TestSetup test;
191     test.loadTestcaseRepos( sysRoot );
192     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
193   }
194   else if ( TestSetup::isTestSetup( sysRoot ) )
195   {
196     message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
197     const char * astr = getenv( "ZYPP_TESTSUITE_FAKE_ARCH" );
198     if ( !astr || !*astr )
199       astr = getenv( "ZYPP_ARCH" );
200     if ( !astr || !*astr )
201       astr = "x86_64";
202     TestSetup test( sysRoot, Arch( astr ) );
203     test.loadRepos();
204     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
205   }
206   else
207   {
208     // a system
209     message << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
210     if ( true )
211     {
212       message << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
213       getZYpp()->initializeTarget( sysRoot );
214       getZYpp()->target()->load();
215       message << satpool.systemRepo() << endl;
216     }
217
218     if ( !onlyInstalled )
219     {
220       RepoManager repoManager( sysRoot );
221       RepoInfoList repos = repoManager.knownRepositories();
222       for_( it, repos.begin(), repos.end() )
223       {
224         RepoInfo & nrepo( *it );
225
226         if ( ! nrepo.enabled() )
227           continue;
228
229         if ( ! repoManager.isCached( nrepo ) )
230         {
231           message << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
232           continue;
233         }
234
235         message << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
236         try
237         {
238           repoManager.loadFromCache( nrepo );
239           message << satpool.reposFind( nrepo.alias() ) << endl;
240         }
241         catch ( const Exception & exp )
242         {
243           message << exp.asString() + "\n" + exp.historyAsString() << endl;
244           message << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
245           continue;
246         }
247       }
248     }
249   }
250
251   ///////////////////////////////////////////////////////////////////
252
253   bool ignorecase       ( true );
254   bool matechexact      ( false );
255   bool withSrcPackages  ( false );
256   bool names            ( true );
257   bool provides         ( false );
258   bool requires         ( false );
259   bool conflicts        ( false );
260   bool obsoletes        ( false );
261   bool recommends       ( false );
262   bool supplements      ( false );
263   bool enhacements      ( false );
264
265
266   for ( ; argc; --argc,++argv )
267   {
268     if ( (*argv)[0] == '-' )
269     {
270       for ( const char * arg = (*argv)+1; *arg != '\0'; ++arg ) // -pr for -p -r
271       {
272         switch ( *arg )
273         {
274           case 'a': names =             true,   requires = provides =   true;   break;
275           case 'A': names =             true,   requires = provides =   false;  break;
276           case 'D':
277             if ( argc > 1 )
278             {
279               --argc,++argv;
280               dDump( *argv );
281             }
282             else
283               return errexit("-D <pkgspec> requires an argument.");
284             break;
285           case 'i': ignorecase =        true;   break;
286           case 'I': ignorecase =        false;  break;
287           case 'x': matechexact =       true;   break;
288           case 'n': names =             true;   break;
289           case 'N': names =             false;  break;
290           case 'r': requires =          true;   break;
291           case 'R': requires =          false;  break;
292           case 'p': provides =          true;   break;
293           case 'P': provides =          false;  break;
294           case 'c': conflicts =         true;   break;
295           case 'C': conflicts =         false;  break;
296           case 'o': obsoletes =         true;   break;
297           case 'O': obsoletes =         false;  break;
298           case 'm': recommends =        true;   break;
299           case 'M': recommends =        false;  break;
300           case 's': supplements =       true;   break;
301           case 'S': supplements =       false;  break;
302           case 'e': enhacements =       true;   break;
303           case 'E': enhacements =       false;  break;
304         }
305       }
306       continue;
307     }
308
309     PoolQuery q;
310     if ( onlyInstalled )
311       q.setInstalledOnly();
312     std::string qstr( *argv );
313
314     if ( *argv == ResKind::product )
315     {
316       q.addKind( ResKind::product );
317     }
318     else if ( *argv == ResKind::patch )
319     {
320       q.addKind( ResKind::patch );
321     }
322     else if ( *argv == ResKind::pattern )
323     {
324       q.addKind( ResKind::pattern );
325     }
326     else
327     {
328       sat::Solvable::SplitIdent ident( qstr );
329       if ( ident.kind() != ResKind::package )
330       {
331         q.addKind( ident.kind() );
332         q.addString( ident.name().asString() );
333       }
334       else
335         q.addString( qstr );
336
337       if ( matechexact )
338         q.setMatchGlob();
339       else
340         q.setMatchRegex();
341       q.setCaseSensitive( ! ignorecase );
342
343       if ( names )
344         q.addAttribute( sat::SolvAttr::name );
345       if ( provides )
346       {
347         q.addDependency( sat::SolvAttr::provides );
348         q.addDependency( sat::SolvAttr::provides, Capability(qstr) );
349       }
350       if ( requires )
351       {
352         q.addDependency( sat::SolvAttr::requires );
353         q.addDependency( sat::SolvAttr::requires, Capability(qstr) );
354       }
355       if ( conflicts )
356       {
357         q.addDependency( sat::SolvAttr::conflicts );
358         q.addDependency( sat::SolvAttr::conflicts, Capability(qstr) );
359       }
360       if ( obsoletes )
361       {
362         q.addDependency( sat::SolvAttr::obsoletes );
363         q.addDependency( sat::SolvAttr::obsoletes, Capability(qstr) );
364       }
365       if ( recommends )
366       {
367         q.addDependency( sat::SolvAttr::recommends );
368         q.addDependency( sat::SolvAttr::recommends, Capability(qstr) );
369       }
370       if ( supplements )
371       {
372         q.addDependency( sat::SolvAttr::supplements );
373         q.addDependency( sat::SolvAttr::supplements, Capability(qstr) );
374       }
375       if ( enhacements )
376       {
377         q.addDependency( sat::SolvAttr::enhances );
378         q.addDependency( sat::SolvAttr::enhances, Capability(qstr) );
379         q.addDependency( sat::SolvAttr::suggests );
380         q.addDependency( sat::SolvAttr::suggests, Capability(qstr) );
381       }
382     }
383
384     message << *argv << " [" << (ignorecase?'i':'_') << (names?'n':'_') << (requires?'r':'_') << (provides?'p':'_')
385     << (conflicts?'c':'_') << (obsoletes?'o':'_') << (recommends?'m':'_') << (supplements?'s':'_') << (enhacements?'e':'_')
386     << "] {" << endl;
387
388     std::set<PoolQuery::const_iterator,PQSort> qsorted;
389     for_( it, q.begin(), q.end() )
390       qsorted.insert( it );
391
392     for ( auto && it : qsorted )
393     {
394       if ( it->isKind( ResKind::srcpackage ) && !withSrcPackages )
395         continue;
396
397       tableOut( str::numstring( it->id() ), colorId(*it),
398                 str::form( "(%d)%s", it->repository().info().priority(), it->repository().name().c_str() ),
399                 str::numstring( PoolItem(*it)->buildtime() ) );
400       tableOut( "", "",
401                 it->vendor().asString() );
402       if ( ! it.matchesEmpty() )
403       {
404         for_( match, it.matchesBegin(), it.matchesEnd() )
405         {
406           tableOut( "", "", match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString() );
407         }
408       }
409     }
410
411     message << "}" << endl;
412   }
413
414   INT << "===[END]============================================" << endl << endl;
415   return 0;
416 }