Imported Upstream version 17.25.3
[platform/upstream/libzypp.git] / zypp / VendorAttr.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/VendorAttr.cc
10 */
11 #include <iostream>
12 #include <fstream>
13 #include <set>
14 #include <map>
15 #include <vector>
16
17 #include <zypp/base/LogTools.h>
18 #include <zypp/base/IOStream.h>
19 #include <zypp/base/StringV.h>
20
21 #include <zypp/PathInfo.h>
22 #include <zypp/VendorAttr.h>
23 #include <zypp/ZYppFactory.h>
24
25 #include <zypp/ZConfig.h>
26 #include <zypp/PathInfo.h>
27 #include <zypp/parser/IniDict.h>
28
29 #undef  ZYPP_BASE_LOGGER_LOGGROUP
30 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::VendorAttr"
31
32 ///////////////////////////////////////////////////////////////////
33 namespace zypp
34 { /////////////////////////////////////////////////////////////////
35
36   ///////////////////////////////////////////////////////////////////
37   /// \class VendorAttr::Impl
38   /// \brief VendorAttr implementation.
39   ///////////////////////////////////////////////////////////////////
40   class VendorAttr::Impl // : private base::NonCopyable
41   {
42     friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
43   public:
44     /** Ctor.
45      * bsc#1030686: The legacy default equivalence of 'suse' and 'opensuse'
46      * has been removed.
47      * bnc#812608: No prefix compare in opensuse namespace, so just create
48      * a class for 'suse*'
49      */
50     Impl()
51     { _vendorGroupMap["suse"] = ++_vendorGroupId; }
52
53   public:
54     /** Add a new equivalent vendor set. */
55     void addVendorList( VendorList && vendorList_r );
56
57     /** Return whether two vendor strings should be treated as equivalent.*/
58     bool equivalent( IdString lVendor, IdString rVendor ) const
59     { return lVendor == rVendor || vendorMatchId( lVendor ) == vendorMatchId( rVendor ); }
60
61     unsigned foreachVendorList( std::function<bool(VendorList)> fnc_r ) const
62     {
63       std::map<unsigned,VendorList> lists;
64       for( const auto & el : _vendorGroupMap )
65         lists[el.second].push_back( el.first );
66
67       unsigned ret = 0;
68       for ( auto el : lists ) {
69         VendorList & vlist { el.second };
70         if ( vlist.empty() )
71           continue;
72         ++ret;
73         if ( fnc_r && !fnc_r( std::move(vlist) ) )
74           break;
75       }
76       return ret;
77     }
78
79   private:
80     using VendorGroupMap = std::map<std::string,unsigned>;
81     VendorGroupMap _vendorGroupMap;     ///< Vendor group definition. Equivalent groups share the same ID.
82     unsigned _vendorGroupId = 0;        ///< Highest group ID in use (incremented).
83
84   private:
85     typedef DefaultIntegral<unsigned,0>                         VendorMatchEntry;
86     typedef std::unordered_map<IdString, VendorMatchEntry>      VendorMatch;
87     mutable VendorMatch _vendorMatch;   ///< Cache mapping vendor strings to equivalence class ID
88     mutable unsigned _nextId = 0;       ///< Least equivalence class ID in use (decremented).
89
90     /** Reset vendor match cache if _vendorGroupMap was changed. */
91     void vendorMatchIdReset()
92     {
93       _nextId = 0;
94       _vendorMatch.clear();
95     }
96
97     /** Helper mapping a vendor string to it's eqivalence class ID.
98      *
99      * \li Return the vendor strings eqivalence class ID stored in _vendorMatch.
100      * \li If not found, assign and return the eqivalence class ID of the lowercased string.
101      * \li If not found, assign and return a new ID (look into the predefined VendorGroupMap (id>0),
102      *     otherwise create a new ID (<0)).
103      */
104     unsigned vendorMatchId( IdString vendor ) const;
105
106   private:
107     friend Impl * rwcowClone<Impl>( const Impl * rhs );
108     /** clone for RWCOW_pointer */
109     Impl * clone() const
110     { return new Impl( *this ); }
111   };
112
113   unsigned VendorAttr::Impl::vendorMatchId( IdString vendor ) const
114   {
115     VendorMatchEntry & ent { _vendorMatch[vendor] };
116     if ( ! ent )
117     {
118       IdString lcvendor { str::toLower( vendor.asString() ) };
119       VendorMatchEntry & lcent( _vendorMatch[lcvendor] );
120       if ( ! lcent )
121       {
122         // Cache miss - check whether it belongs to a vendor group.
123         // Otherwise assign a new class ID to it.
124         unsigned myid = 0;
125
126         // bnc#812608: no prefix compare in opensuse namespace
127         if ( str::hasPrefix( lcvendor.c_str(), "opensuse" ) )
128         {
129           if ( auto it = _vendorGroupMap.find( lcvendor.c_str() ); it != _vendorGroupMap.end() )
130             myid = it->second;
131         }
132         else
133         {
134           // Compare this entry with the global vendor map.
135           // Reversed to get the longest prefix.
136           for ( VendorGroupMap::const_reverse_iterator it = _vendorGroupMap.rbegin(); it != _vendorGroupMap.rend(); ++it )
137           {
138             if ( str::hasPrefix( lcvendor.c_str(), it->first ) ) {
139               myid = it->second;
140               break; // found
141             }
142           }
143         }
144
145         if ( ! myid )
146           myid = --_nextId; // get a new class ID
147
148         ent = lcent = myid; // remember the new DI
149       }
150       else
151         ent = lcent; // take the ID from the lowercased vendor string
152     }
153     return ent;
154   }
155
156   void VendorAttr::Impl::addVendorList( VendorList && vendorList_r )
157   {
158     // Will add a new equivalence group unless we merge with existing groups.
159     unsigned targetId = _vendorGroupId + 1;
160
161     // (!) Convert the group strings in place to lowercase before adding/checking.
162     // Extend/join already existing groups if they are referenced.
163     for ( std::string & vendor : vendorList_r )
164     {
165       vendor = str::toLower( std::move(vendor) );
166
167       if ( _vendorGroupMap.count( vendor ) )
168       {
169         unsigned joinId = _vendorGroupMap[vendor];
170         if ( targetId == _vendorGroupId + 1 ) {
171           targetId = joinId;    // will extend the existing group
172         }
173         else if ( targetId != joinId ) {
174           // yet another existing group -> join it into the target group
175           for ( auto & el : _vendorGroupMap ) {
176             if ( el.second == joinId )
177               el.second = targetId;
178           }
179         }
180         vendor.clear(); // no need to add it later
181       }
182     }
183
184     // Now add the new entries
185     for ( std::string & vendor : vendorList_r ) {
186       if ( ! vendor.empty() )
187         _vendorGroupMap[vendor] = targetId;
188     }
189
190     if ( targetId == _vendorGroupId + 1 )
191       ++_vendorGroupId;
192
193     // invalidate any match cache
194     vendorMatchIdReset();
195   }
196
197   /** \relates VendorAttr::Impl Stream output */
198   inline std::ostream & operator<<( std::ostream & str, const VendorAttr::Impl & obj )
199   {
200     str << "Equivalent vendors:";
201     for( const auto & p : obj._vendorGroupMap ) {
202       str << endl << "   [" << p.second << "] " << p.first;
203     }
204     return str;
205   }
206
207   ///////////////////////////////////////////////////////////////////
208   //
209   //    CLASS NAME : VendorAttr
210   //
211   ///////////////////////////////////////////////////////////////////
212
213   const VendorAttr & VendorAttr::instance()
214   {
215     Target_Ptr trg { getZYpp()->getTarget() };
216     return trg ? trg->vendorAttr() : noTargetInstance();
217   }
218
219   VendorAttr & VendorAttr::noTargetInstance()
220   {
221     static VendorAttr _val { ZConfig::instance().vendorPath() };
222     return _val;
223   }
224
225   VendorAttr::VendorAttr()
226   : _pimpl( new Impl )
227   {
228     MIL << "Initial: " << *this << endl;
229   }
230
231   VendorAttr::VendorAttr( const Pathname & initial_r )
232   : _pimpl( new Impl )
233   {
234     addVendorDirectory( initial_r );
235     MIL << "Initial " << initial_r << ": " << *this << endl;
236   }
237
238   VendorAttr::~VendorAttr()
239   {}
240
241   bool VendorAttr::addVendorDirectory( const Pathname & dirname_r )
242   {
243     if ( PathInfo pi { dirname_r }; ! pi.isDir() ) {
244       MIL << "Not a directory " << pi << endl;
245       return false;
246     }
247
248     filesystem::dirForEach( dirname_r, filesystem::matchNoDots(),
249                             [this]( const Pathname & dir_r, const std::string & str_r )->bool
250                             {
251                               this->addVendorFile( dir_r/str_r );
252                               return true;
253                             }
254     );
255     return true;
256   }
257
258   bool VendorAttr::addVendorFile( const Pathname & filename_r )
259   {
260     if ( PathInfo pi { filename_r }; ! pi.isFile() ) {
261       MIL << "Not a file " << pi << endl;
262       return false;
263     }
264
265     parser::IniDict dict { InputStream(filename_r) };
266     for ( const auto & el : dict.entries("main") )
267     {
268       if ( el.first == "vendors" )
269       {
270         VendorList tmp;
271         strv::split( el.second, ",", strv::Trim::trim,
272                      [&tmp]( std::string_view word ) {
273                        if ( ! word.empty() )
274                          tmp.push_back( std::string(word) );
275                      } );
276         _addVendorList( std::move(tmp) );
277         break;
278       }
279     }
280     return true;
281   }
282
283   void VendorAttr::_addVendorList( VendorList && vendorList_r )
284   { _pimpl->addVendorList( std::move(vendorList_r) ); }
285
286   unsigned VendorAttr::foreachVendorList( std::function<bool(VendorList)> fnc_r ) const
287   { return _pimpl->foreachVendorList( std::move(fnc_r) ); }
288
289 #if LEGACY(1722)
290   bool VendorAttr::addVendorDirectory( const Pathname & dirname ) const
291   { return const_cast<VendorAttr*>(this)->addVendorDirectory( dirname ); }
292
293   bool VendorAttr::addVendorFile( const Pathname & filename ) const
294   { return const_cast<VendorAttr*>(this)->addVendorFile( filename ); }
295
296   void VendorAttr::_addVendorList( std::vector<std::string> & vendorList_r ) const
297   { return const_cast<VendorAttr*>(this)->_addVendorList( VendorList( vendorList_r.begin(), vendorList_r.end() ) ); }
298
299   void VendorAttr::_addVendorList( std::vector<IdString> && list_r )
300   {
301     VendorList tmp;
302     for ( const auto & el : list_r )
303       tmp.push_back( std::string(el) );
304     _addVendorList( std::move(tmp) );
305   }
306 #endif
307   //////////////////////////////////////////////////////////////////
308   // vendor equivalence:
309   //////////////////////////////////////////////////////////////////
310
311   bool VendorAttr::equivalent( IdString lVendor, IdString rVendor ) const
312   { return _pimpl->equivalent( lVendor, rVendor );}
313
314   bool VendorAttr::equivalent( const Vendor & lVendor, const Vendor & rVendor ) const
315   { return _pimpl->equivalent( IdString( lVendor ), IdString( rVendor ) ); }
316
317   bool VendorAttr::equivalent( sat::Solvable lVendor, sat::Solvable rVendor ) const
318   { return _pimpl->equivalent( lVendor.vendor(), rVendor.vendor() ); }
319
320   bool VendorAttr::equivalent( const PoolItem & lVendor, const PoolItem & rVendor ) const
321   { return _pimpl->equivalent( lVendor.satSolvable().vendor(), rVendor.satSolvable().vendor() ); }
322
323   //////////////////////////////////////////////////////////////////
324
325   std::ostream & operator<<( std::ostream & str, const VendorAttr & obj )
326   { return str << *obj._pimpl; }
327
328   /////////////////////////////////////////////////////////////////
329 } // namespace zypp
330 ///////////////////////////////////////////////////////////////////
331