Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / tools / zypp-NameReqPrv.cc
1 #define INCLUDE_TESTSETUP_WITHOUT_BOOST
2 #include "../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 struct PQSort
64 {
65   // std::less semantic
66   bool operator()( const sat::Solvable & lhs, const sat::Solvable & rhs ) const
67   {
68     {
69       bool l = lhs.isSystem();
70       bool r = rhs.isSystem();
71       if ( l != r )
72         return r;
73     }
74     {
75       IdString l { lhs.ident() };
76       IdString r { rhs.ident() };
77       if ( l != r )
78         return l < r;
79     }
80     return avo( PoolItem(lhs), PoolItem(rhs) );
81     return lhs.id() > rhs.id();
82   }
83
84   ui::SelectableTraits::AVOrder avo;
85 };
86
87 struct Table
88 {
89   using ResultLInes = std::set<std::string>;
90
91   ResultLInes & row( const sat::Solvable & solv_r )
92   {
93     //smax( _maxSID,  );
94     smax( _maxNAME, solv_r.ident().size() + solv_r.edition().size() + solv_r.arch().size() );
95     smax( _maxREPO, solv_r.repository().name().size(), solv_r.repository().name() );
96     //smax( _maxTIME, );
97     //smax( _maxVEND, solv_r.vendor().size() );
98     return _map[solv_r];
99   }
100
101   void row( PoolQuery::const_iterator it_r )
102   {
103     ResultLInes & details { row( *it_r ) };
104     for_( match, it_r.matchesBegin(), it_r.matchesEnd() ) {
105       std::string ent { match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString() };
106       if ( match->inSolvAttr() == sat::SolvAttr::requires
107         && match->inSolvable().prerequires().contains( Capability(match->id()) ) ) {
108         static const char * pre = "   " COL_C "[PRE]" COL_OFF;
109         ent[3] = '+';
110         ent += pre;
111       }
112       details.insert( std::move(ent) );
113     }
114   }
115
116   std::ostream & dumpOn( std::ostream & str ) const
117   {
118     #define S "  "
119
120     #define fmtSID  "%*d"
121     #define argSID  _maxSID, slv.id()
122
123     #define fmtNAME COL_B "%s" COL_OFF "-" COL_G "%s" COL_OFF ".%-*s"
124     #define argNAME slv.ident().c_str(), slv.edition().c_str(), _maxNAME-slv.ident().size()-slv.edition().size(), slv.arch().c_str()
125
126     #define fmtREPO "(%2d)%-*s"
127     #define argREPO slv.repository().info().priority(), _maxREPO, slv.repository().name().c_str()
128
129     #define fmtTIME "%10ld"
130     #define argTIME time_t( slv.isSystem() ? slv.installtime() : slv.buildtime() )
131
132     #define fmtVEND "%s"
133     #define argVEND slv.vendor().c_str()
134
135     std::string dind( _maxSID + _maxNAME+2/*-.*/ + 2*strlen(S), ' ' );
136
137     for ( const auto & el : _map ) {
138       sat::Solvable slv { el.first };
139       const char * tagCol = slv.isSystem() ? COL_M : ui::Selectable::get(slv)->identicalInstalled(PoolItem(slv)) ? COL_C : "";
140
141       str << str::form( "%s"    fmtSID S fmtNAME S "%s"    fmtREPO S fmtTIME S fmtVEND COL_OFF "\n",
142                         tagCol, argSID,  argNAME,  tagCol, argREPO,  argTIME,  argVEND );
143
144       for ( const auto & d : el.second )
145         str << dind << d << endl;
146     }
147     return str;
148   }
149
150 private:
151   static void smax( unsigned & var_r, unsigned val_r, std::string_view n = {} )
152   { if ( val_r > var_r ) var_r = val_r; }
153
154 private:
155   unsigned _maxSID  = 7;        // size + indent
156   unsigned _maxNAME = 0;
157   unsigned _maxREPO = 0;
158   //unsigned _maxTIME = 10;
159   //unsigned _maxVEND = 0;
160   std::map<sat::Solvable, ResultLInes, PQSort> _map;
161 };
162
163 inline std::ostream & operator<<( std::ostream & str, const Table & table_r )
164 { return table_r.dumpOn( str ); }
165
166 ///////////////////////////////////////////////////////////////////
167
168 void dDump( const std::string & spec_r )
169 {
170   message << "DUMP " << spec_r << " {";
171
172   sat::WhatProvides q( Capability::guessPackageSpec( spec_r ) );
173   if ( q.empty() )
174   {
175     message << "}" << endl;
176     return;
177   }
178
179   for ( const auto & el : q )
180   {
181     message << endl << "==============================" << endl << dump(el);
182     if ( isKind<Product>(el) )
183     {
184       message << endl << "REPLACES: " << make<Product>(el)->replacedProducts();
185     }
186     else if ( isKind<Pattern>(el) )
187     {
188       message << endl << "CONTENT: " << make<Pattern>(el)->contents();
189     }
190     else if ( isKind<Patch>(el) )
191     {
192       message << endl << "STATUS: " << PoolItem(el);
193     }
194   }
195   message << endl << "}" << endl;
196 }
197
198 ///////////////////////////////////////////////////////////////////
199
200 void dTree( const std::string & cmd_r, const std::string & spec_r )
201 {
202   message << "tree " << spec_r << " {";
203
204   sat::WhatProvides spec( Capability::guessPackageSpec( spec_r ) );
205   if ( spec.empty() )
206   {
207     message << "}" << endl;
208     return;
209   }
210
211   static const std::list<sat::SolvAttr> attrs {
212     sat::SolvAttr::requires,
213     sat::SolvAttr::recommends,
214     sat::SolvAttr::obsoletes,
215     sat::SolvAttr::conflicts,
216     sat::SolvAttr::supplements,
217   };
218
219   std::map<sat::SolvAttr,std::map<sat::Solvable,std::set<std::string>>> result; // solvables recommending provided capability
220   {
221     Table t;
222     for ( const auto & el : spec )
223     {
224       auto & details { t.row( el ) };
225       for ( const auto & cap : el.provides() )
226       {
227         //details.push_back( "prv: "+cap.asString() );  // list only matching ones
228
229         // get attrs matching cap
230         for ( const auto & attr : attrs )
231         {
232           PoolQuery q;
233           q.addDependency( attr, cap );
234           if ( q.empty() )
235             continue;
236           for_( it, q.begin(), q.end() ) {
237             for_( match, it.matchesBegin(), it.matchesEnd() ) {
238               result[attr][*it].insert( match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString()
239               + " (" + cap.asString() + ")"
240               );
241             }
242           }
243         }
244       }
245     }
246     message << endl << t;
247   }
248
249   for ( const auto & attr : attrs )
250   {
251     if ( result[attr].empty() )
252       continue;
253
254     message << endl << "======== " << attr << " by:" << endl;
255     Table t;
256     for ( const auto & el : result[attr] )
257     {
258       auto & details { t.row( el.first ) };
259       for ( const auto & cap : el.second )
260         details.insert( cap );
261     }
262     message << endl << t << endl;
263   }
264   message << "}" << endl;
265 }
266
267 /******************************************************************
268 **
269 **      FUNCTION NAME : main
270 **      FUNCTION TYPE : int
271 */
272 int main( int argc, char * argv[] )
273 {
274   INT << "===[START]==========================================" << endl;
275   appname = Pathname::basename( argv[0] );
276   --argc,++argv;
277
278   if ( ! argc )
279   {
280     return usage();
281   }
282
283   ///////////////////////////////////////////////////////////////////
284
285   ZConfig::instance();
286   Pathname sysRoot("/");
287   sat::Pool satpool( sat::Pool::instance() );
288
289   if ( argc && (*argv) == std::string("--root") )
290   {
291     --argc,++argv;
292     if ( ! argc )
293       return errexit("--root requires an argument.");
294
295     if ( ! PathInfo( *argv ).isDir() )
296       return errexit("--root requires a directory.");
297
298     sysRoot = *argv;
299     --argc,++argv;
300   }
301
302   bool onlyInstalled( false );
303   if ( argc && (*argv) == std::string("--installed") )
304   {
305     --argc,++argv;
306     onlyInstalled = true;
307   }
308
309   if ( TestSetup::isTestcase( sysRoot ) )
310   {
311     message << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
312     TestSetup test;
313     test.loadTestcaseRepos( sysRoot );
314     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
315   }
316   else if ( TestSetup::isTestSetup( sysRoot ) )
317   {
318     message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
319     const char * astr = getenv( "ZYPP_TESTSUITE_FAKE_ARCH" );
320     if ( !astr || !*astr )
321       astr = getenv( "ZYPP_ARCH" );
322     if ( !astr || !*astr )
323       astr = "x86_64";
324     TestSetup test( sysRoot, Arch( astr ) );
325     test.loadRepos();
326     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
327   }
328   else
329   {
330     // a system
331     message << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
332     if ( true )
333     {
334       message << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
335       getZYpp()->initializeTarget( sysRoot );
336       getZYpp()->target()->load();
337       message << satpool.systemRepo() << endl;
338     }
339
340     if ( !onlyInstalled )
341     {
342       RepoManager repoManager( sysRoot );
343       RepoInfoList repos = repoManager.knownRepositories();
344       for_( it, repos.begin(), repos.end() )
345       {
346         RepoInfo & nrepo( *it );
347
348         if ( ! nrepo.enabled() )
349           continue;
350
351         if ( ! repoManager.isCached( nrepo ) )
352         {
353           message << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
354           continue;
355         }
356
357         message << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
358         try
359         {
360           repoManager.loadFromCache( nrepo );
361           message << satpool.reposFind( nrepo.alias() ) << endl;
362         }
363         catch ( const Exception & exp )
364         {
365           message << exp.asString() + "\n" + exp.historyAsString() << endl;
366           message << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
367           continue;
368         }
369       }
370     }
371   }
372   ///////////////////////////////////////////////////////////////////
373
374   bool ignorecase       ( true );
375   bool matechexact      ( false );
376   bool withSrcPackages  ( false );
377   bool names            ( true );
378   bool provides         ( false );
379   bool requires         ( false );
380   bool conflicts        ( false );
381   bool obsoletes        ( false );
382   bool recommends       ( false );
383   bool supplements      ( false );
384   bool enhacements      ( false );
385
386
387   for ( ; argc; --argc,++argv )
388   {
389     if ( (*argv)[0] == '-' )
390     {
391       for ( const char * arg = (*argv)+1; *arg != '\0'; ++arg ) // -pr for -p -r
392       {
393         switch ( *arg )
394         {
395           case 'a': names =             true,   requires = provides =   true;   break;
396           case 'A': names =             true,   requires = provides =   false;  break;
397           case 'D':
398             if ( argc > 1 )
399             {
400               --argc,++argv;
401               dDump( *argv );
402             }
403             else
404               return errexit("-D <pkgspec> requires an argument.");
405             break;
406           case 'T':
407             if ( argc > 1 )
408             {
409               std::string cmd { *argv };
410               --argc,++argv;
411               dTree( cmd, *argv );
412             }
413             else
414               return errexit("-T <pkgspec> requires an argument.");
415             break;
416           case 'i': ignorecase =        true;   break;
417           case 'I': ignorecase =        false;  break;
418           case 'x': matechexact =       true;   break;
419           case 'n': names =             true;   break;
420           case 'N': names =             false;  break;
421           case 'r': requires =          true;   break;
422           case 'R': requires =          false;  break;
423           case 'p': provides =          true;   break;
424           case 'P': provides =          false;  break;
425           case 'c': conflicts =         true;   break;
426           case 'C': conflicts =         false;  break;
427           case 'o': obsoletes =         true;   break;
428           case 'O': obsoletes =         false;  break;
429           case 'm': recommends =        true;   break;
430           case 'M': recommends =        false;  break;
431           case 's': supplements =       true;   break;
432           case 'S': supplements =       false;  break;
433           case 'e': enhacements =       true;   break;
434           case 'E': enhacements =       false;  break;
435         }
436       }
437       continue;
438     }
439
440     PoolQuery q;
441     if ( onlyInstalled )
442       q.setInstalledOnly();
443     std::string qstr( *argv );
444
445     if ( *argv == ResKind::product )
446     {
447       q.addKind( ResKind::product );
448     }
449     else if ( *argv == ResKind::patch )
450     {
451       q.addKind( ResKind::patch );
452     }
453     else if ( *argv == ResKind::pattern )
454     {
455       q.addKind( ResKind::pattern );
456     }
457     else
458     {
459       sat::Solvable::SplitIdent ident( qstr );
460       if ( ident.kind() != ResKind::package )
461       {
462         q.addKind( ident.kind() );
463         q.addString( ident.name().asString() );
464       }
465       else
466         q.addString( qstr );
467
468       if ( matechexact )
469         q.setMatchGlob();
470       else
471         q.setMatchRegex();
472       q.setCaseSensitive( ! ignorecase );
473
474       if ( names )
475         q.addAttribute( sat::SolvAttr::name );
476       if ( provides )
477       {
478         q.addDependency( sat::SolvAttr::provides );
479         q.addDependency( sat::SolvAttr::provides, Capability(qstr) );
480       }
481       if ( requires )
482       {
483         q.addDependency( sat::SolvAttr::requires );
484         q.addDependency( sat::SolvAttr::requires, Capability(qstr) );
485       }
486       if ( conflicts )
487       {
488         q.addDependency( sat::SolvAttr::conflicts );
489         q.addDependency( sat::SolvAttr::conflicts, Capability(qstr) );
490       }
491       if ( obsoletes )
492       {
493         q.addDependency( sat::SolvAttr::obsoletes );
494         q.addDependency( sat::SolvAttr::obsoletes, Capability(qstr) );
495       }
496       if ( recommends )
497       {
498         q.addDependency( sat::SolvAttr::recommends );
499         q.addDependency( sat::SolvAttr::recommends, Capability(qstr) );
500       }
501       if ( supplements )
502       {
503         q.addDependency( sat::SolvAttr::supplements );
504         q.addDependency( sat::SolvAttr::supplements, Capability(qstr) );
505       }
506       if ( enhacements )
507       {
508         q.addDependency( sat::SolvAttr::enhances );
509         q.addDependency( sat::SolvAttr::enhances, Capability(qstr) );
510         q.addDependency( sat::SolvAttr::suggests );
511         q.addDependency( sat::SolvAttr::suggests, Capability(qstr) );
512       }
513     }
514
515     message << *argv << " [" << (ignorecase?'i':'_') << (names?'n':'_') << (requires?'r':'_') << (provides?'p':'_')
516     << (conflicts?'c':'_') << (obsoletes?'o':'_') << (recommends?'m':'_') << (supplements?'s':'_') << (enhacements?'e':'_')
517     << "] {" << endl;
518
519     Table t;
520     for_( it, q.begin(), q.end() )
521     {
522       if ( it->isKind( ResKind::srcpackage ) && !withSrcPackages )
523         continue;
524       t.row( it );
525     }
526     message << t << "}" << endl;
527   }
528
529   INT << "===[END]============================================" << endl << endl;
530   return 0;
531 }