Imported Upstream version 17.23.5
[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   void row( PoolQuery::const_iterator it_r )
90   {
91     //smax( _maxSID,  );
92     smax( _maxNAME, it_r->ident().size() + it_r->edition().size() + it_r->arch().size() );
93     smax( _maxREPO, it_r->repository().name().size(), it_r->repository().name() );
94     //smax( _maxTIME, );
95     //smax( _maxVEND, it_r->vendor().size() );
96
97     std::vector<std::string> & details { _map[*it_r] };
98     for_( match, it_r.matchesBegin(), it_r.matchesEnd() ) {
99       details.push_back( match->inSolvAttr().asString().substr( 9, 3 )+": " +match->asString() );
100     }
101   }
102
103   std::ostream & dumpOn( std::ostream & str ) const
104   {
105     #define S "  "
106
107     #define fmtSID  "%*d"
108     #define argSID  _maxSID, slv.id()
109
110     #define fmtNAME COL_B "%s" COL_OFF "-" COL_G "%s" COL_OFF ".%-*s"
111     #define argNAME slv.ident().c_str(), slv.edition().c_str(), _maxNAME-slv.ident().size()-slv.edition().size(), slv.arch().c_str()
112
113     #define fmtREPO "(%2d)%-*s"
114     #define argREPO slv.repository().info().priority(), _maxREPO, slv.repository().name().c_str()
115
116     #define fmtTIME "%10ld"
117     #define argTIME time_t( slv.isSystem() ? slv.installtime() : slv.buildtime() )
118
119     #define fmtVEND "%s"
120     #define argVEND slv.vendor().c_str()
121
122     std::string dind( _maxSID + _maxNAME+2/*-.*/ + 2*strlen(S), ' ' );
123
124     for ( const auto & el : _map ) {
125       sat::Solvable slv { el.first };
126       const char * tagCol = slv.isSystem() ? COL_M : ui::Selectable::get(slv)->identicalInstalled(PoolItem(slv)) ? COL_C : "";
127
128       str << str::form( "%s"    fmtSID S fmtNAME S "%s"    fmtREPO S fmtTIME S fmtVEND COL_OFF "\n",
129                         tagCol, argSID,  argNAME,  tagCol, argREPO,  argTIME,  argVEND );
130
131       for ( const auto & d : el.second )
132         str << dind << d << endl;
133     }
134     return str;
135   }
136
137 private:
138   static void smax( unsigned & var_r, unsigned val_r, std::string_view n = {} )
139   { if ( val_r > var_r ) var_r = val_r; }
140
141 private:
142   unsigned _maxSID  = 7;        // size + indent
143   unsigned _maxNAME = 0;
144   unsigned _maxREPO = 0;
145   //unsigned _maxTIME = 10;
146   //unsigned _maxVEND = 0;
147   std::map<sat::Solvable, std::vector<std::string>, PQSort> _map;
148 };
149
150 inline std::ostream & operator<<( std::ostream & str, const Table & table_r )
151 { return table_r.dumpOn( str ); }
152
153 ///////////////////////////////////////////////////////////////////
154
155 void dDump( const std::string & spec_r )
156 {
157   message << "DUMP " << spec_r << " {";
158
159   sat::WhatProvides q( Capability::guessPackageSpec( spec_r ) );
160   if ( q.empty() )
161   {
162     message << "}" << endl;
163     return;
164   }
165
166   for ( const auto & el : q )
167   {
168     message << endl << "==============================" << endl << dump(el);
169     if ( isKind<Product>(el) )
170     {
171       message << endl << "REPLACES: " << make<Product>(el)->replacedProducts();
172     }
173     else if ( isKind<Pattern>(el) )
174     {
175       message << endl << "CONTENT: " << make<Pattern>(el)->contents();
176     }
177   }
178   message << endl << "}" << endl;
179 }
180
181 /******************************************************************
182 **
183 **      FUNCTION NAME : main
184 **      FUNCTION TYPE : int
185 */
186 int main( int argc, char * argv[] )
187 {
188   INT << "===[START]==========================================" << endl;
189   appname = Pathname::basename( argv[0] );
190   --argc,++argv;
191
192   if ( ! argc )
193   {
194     return usage();
195   }
196
197   ///////////////////////////////////////////////////////////////////
198
199   ZConfig::instance();
200   Pathname sysRoot("/");
201   sat::Pool satpool( sat::Pool::instance() );
202
203   if ( argc && (*argv) == std::string("--root") )
204   {
205     --argc,++argv;
206     if ( ! argc )
207       return errexit("--root requires an argument.");
208
209     if ( ! PathInfo( *argv ).isDir() )
210       return errexit("--root requires a directory.");
211
212     sysRoot = *argv;
213     --argc,++argv;
214   }
215
216   bool onlyInstalled( false );
217   if ( argc && (*argv) == std::string("--installed") )
218   {
219     --argc,++argv;
220     onlyInstalled = true;
221   }
222
223   if ( TestSetup::isTestcase( sysRoot ) )
224   {
225     message << str::form( "*** Load Testcase from '%s'", sysRoot.c_str() ) << endl;
226     TestSetup test;
227     test.loadTestcaseRepos( sysRoot );
228     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
229   }
230   else if ( TestSetup::isTestSetup( sysRoot ) )
231   {
232     message << str::form( "*** Load TestSetup from '%s'", sysRoot.c_str() ) << endl;
233     const char * astr = getenv( "ZYPP_TESTSUITE_FAKE_ARCH" );
234     if ( !astr || !*astr )
235       astr = getenv( "ZYPP_ARCH" );
236     if ( !astr || !*astr )
237       astr = "x86_64";
238     TestSetup test( sysRoot, Arch( astr ) );
239     test.loadRepos();
240     dumpRange( message, satpool.reposBegin(), satpool.reposEnd() ) << endl;
241   }
242   else
243   {
244     // a system
245     message << str::form( "*** Load system at '%s'", sysRoot.c_str() ) << endl;
246     if ( true )
247     {
248       message << "*** load target '" << Repository::systemRepoAlias() << "'\t" << endl;
249       getZYpp()->initializeTarget( sysRoot );
250       getZYpp()->target()->load();
251       message << satpool.systemRepo() << endl;
252     }
253
254     if ( !onlyInstalled )
255     {
256       RepoManager repoManager( sysRoot );
257       RepoInfoList repos = repoManager.knownRepositories();
258       for_( it, repos.begin(), repos.end() )
259       {
260         RepoInfo & nrepo( *it );
261
262         if ( ! nrepo.enabled() )
263           continue;
264
265         if ( ! repoManager.isCached( nrepo ) )
266         {
267           message << str::form( "*** omit uncached repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
268           continue;
269         }
270
271         message << str::form( "*** load repo '%s'\t", nrepo.name().c_str() ) << flush;
272         try
273         {
274           repoManager.loadFromCache( nrepo );
275           message << satpool.reposFind( nrepo.alias() ) << endl;
276         }
277         catch ( const Exception & exp )
278         {
279           message << exp.asString() + "\n" + exp.historyAsString() << endl;
280           message << str::form( "*** omit broken repo '%s' (do 'zypper refresh')", nrepo.name().c_str() ) << endl;
281           continue;
282         }
283       }
284     }
285   }
286   ///////////////////////////////////////////////////////////////////
287
288   bool ignorecase       ( true );
289   bool matechexact      ( false );
290   bool withSrcPackages  ( false );
291   bool names            ( true );
292   bool provides         ( false );
293   bool requires         ( false );
294   bool conflicts        ( false );
295   bool obsoletes        ( false );
296   bool recommends       ( false );
297   bool supplements      ( false );
298   bool enhacements      ( false );
299
300
301   for ( ; argc; --argc,++argv )
302   {
303     if ( (*argv)[0] == '-' )
304     {
305       for ( const char * arg = (*argv)+1; *arg != '\0'; ++arg ) // -pr for -p -r
306       {
307         switch ( *arg )
308         {
309           case 'a': names =             true,   requires = provides =   true;   break;
310           case 'A': names =             true,   requires = provides =   false;  break;
311           case 'D':
312             if ( argc > 1 )
313             {
314               --argc,++argv;
315               dDump( *argv );
316             }
317             else
318               return errexit("-D <pkgspec> requires an argument.");
319             break;
320           case 'i': ignorecase =        true;   break;
321           case 'I': ignorecase =        false;  break;
322           case 'x': matechexact =       true;   break;
323           case 'n': names =             true;   break;
324           case 'N': names =             false;  break;
325           case 'r': requires =          true;   break;
326           case 'R': requires =          false;  break;
327           case 'p': provides =          true;   break;
328           case 'P': provides =          false;  break;
329           case 'c': conflicts =         true;   break;
330           case 'C': conflicts =         false;  break;
331           case 'o': obsoletes =         true;   break;
332           case 'O': obsoletes =         false;  break;
333           case 'm': recommends =        true;   break;
334           case 'M': recommends =        false;  break;
335           case 's': supplements =       true;   break;
336           case 'S': supplements =       false;  break;
337           case 'e': enhacements =       true;   break;
338           case 'E': enhacements =       false;  break;
339         }
340       }
341       continue;
342     }
343
344     PoolQuery q;
345     if ( onlyInstalled )
346       q.setInstalledOnly();
347     std::string qstr( *argv );
348
349     if ( *argv == ResKind::product )
350     {
351       q.addKind( ResKind::product );
352     }
353     else if ( *argv == ResKind::patch )
354     {
355       q.addKind( ResKind::patch );
356     }
357     else if ( *argv == ResKind::pattern )
358     {
359       q.addKind( ResKind::pattern );
360     }
361     else
362     {
363       sat::Solvable::SplitIdent ident( qstr );
364       if ( ident.kind() != ResKind::package )
365       {
366         q.addKind( ident.kind() );
367         q.addString( ident.name().asString() );
368       }
369       else
370         q.addString( qstr );
371
372       if ( matechexact )
373         q.setMatchGlob();
374       else
375         q.setMatchRegex();
376       q.setCaseSensitive( ! ignorecase );
377
378       if ( names )
379         q.addAttribute( sat::SolvAttr::name );
380       if ( provides )
381       {
382         q.addDependency( sat::SolvAttr::provides );
383         q.addDependency( sat::SolvAttr::provides, Capability(qstr) );
384       }
385       if ( requires )
386       {
387         q.addDependency( sat::SolvAttr::requires );
388         q.addDependency( sat::SolvAttr::requires, Capability(qstr) );
389       }
390       if ( conflicts )
391       {
392         q.addDependency( sat::SolvAttr::conflicts );
393         q.addDependency( sat::SolvAttr::conflicts, Capability(qstr) );
394       }
395       if ( obsoletes )
396       {
397         q.addDependency( sat::SolvAttr::obsoletes );
398         q.addDependency( sat::SolvAttr::obsoletes, Capability(qstr) );
399       }
400       if ( recommends )
401       {
402         q.addDependency( sat::SolvAttr::recommends );
403         q.addDependency( sat::SolvAttr::recommends, Capability(qstr) );
404       }
405       if ( supplements )
406       {
407         q.addDependency( sat::SolvAttr::supplements );
408         q.addDependency( sat::SolvAttr::supplements, Capability(qstr) );
409       }
410       if ( enhacements )
411       {
412         q.addDependency( sat::SolvAttr::enhances );
413         q.addDependency( sat::SolvAttr::enhances, Capability(qstr) );
414         q.addDependency( sat::SolvAttr::suggests );
415         q.addDependency( sat::SolvAttr::suggests, Capability(qstr) );
416       }
417     }
418
419     message << *argv << " [" << (ignorecase?'i':'_') << (names?'n':'_') << (requires?'r':'_') << (provides?'p':'_')
420     << (conflicts?'c':'_') << (obsoletes?'o':'_') << (recommends?'m':'_') << (supplements?'s':'_') << (enhacements?'e':'_')
421     << "] {" << endl;
422
423     Table t;
424     for_( it, q.begin(), q.end() )
425     {
426       if ( it->isKind( ResKind::srcpackage ) && !withSrcPackages )
427         continue;
428       t.row( it );
429     }
430     message << t << endl;
431
432     message << "}" << endl;
433   }
434
435   INT << "===[END]============================================" << endl << endl;
436   return 0;
437 }