Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / VendorAttr.cc
index cac387a..47680d1 100644 (file)
@@ -6,16 +6,8 @@
 |                         /_____||_| |_| |_|                           |
 |                                                                      |
 \---------------------------------------------------------------------*/
-/*
-  File:       VendorAttr.cc
-
-  Author:     Michael Andres <ma@suse.de>
-  Maintainer: Michael Andres <ma@suse.de>
-
-  Purpose: Manage vendor attributes
-
-/-*/
-
+/** \file zypp/VendorAttr.cc
+*/
 #include <iostream>
 #include <fstream>
 #include <set>
@@ -24,7 +16,7 @@
 
 #include <zypp/base/LogTools.h>
 #include <zypp/base/IOStream.h>
-#include <zypp/base/String.h>
+#include <zypp/base/StringV.h>
 
 #include <zypp/PathInfo.h>
 #include <zypp/VendorAttr.h>
@@ -42,246 +34,296 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
-  namespace
-  { /////////////////////////////////////////////////////////////////
+  /// \class VendorAttr::Impl
+  /// \brief VendorAttr implementation.
+  ///////////////////////////////////////////////////////////////////
+  class VendorAttr::Impl // : private base::NonCopyable
+  {
+    friend std::ostream & operator<<( std::ostream & str, const Impl & obj );
+  public:
+    /** Ctor.
+     * bsc#1030686: The legacy default equivalence of 'suse' and 'opensuse'
+     * has been removed.
+     * bnc#812608: No prefix compare in opensuse namespace, so just create
+     * a class for 'suse*'
+     */
+    Impl()
+    { _vendorGroupMap["suse"] = ++_vendorGroupId; }
 
-    typedef std::map<Vendor,unsigned> VendorMap;
-    VendorMap _vendorMap;
-    unsigned vendorGroupCounter;
+  public:
+    /** Add a new equivalent vendor set. */
+    void addVendorList( VendorList && vendorList_r );
 
-    /////////////////////////////////////////////////////////////////
-  } // namespace
-  ///////////////////////////////////////////////////////////////////
+    /** Return whether two vendor strings should be treated as equivalent.*/
+    bool equivalent( IdString lVendor, IdString rVendor ) const
+    { return lVendor == rVendor || vendorMatchId( lVendor ) == vendorMatchId( rVendor ); }
 
-  ///////////////////////////////////////////////////////////////////
-  namespace
-  { /////////////////////////////////////////////////////////////////
-    typedef DefaultIntegral<int,0>                             VendorMatchEntry;
+    unsigned foreachVendorList( std::function<bool(VendorList)> fnc_r ) const
+    {
+      std::map<unsigned,VendorList> lists;
+      for( const auto & el : _vendorGroupMap )
+       lists[el.second].push_back( el.first );
+
+      unsigned ret = 0;
+      for ( auto el : lists ) {
+       VendorList & vlist { el.second };
+       if ( vlist.empty() )
+         continue;
+       ++ret;
+       if ( fnc_r && !fnc_r( std::move(vlist) ) )
+         break;
+      }
+      return ret;
+    }
+
+  private:
+    using VendorGroupMap = std::map<std::string,unsigned>;
+    VendorGroupMap _vendorGroupMap;    ///< Vendor group definition. Equivalent groups share the same ID.
+    unsigned _vendorGroupId = 0;       ///< Highest group ID in use (incremented).
+
+  private:
+    typedef DefaultIntegral<unsigned,0>                                VendorMatchEntry;
     typedef std::unordered_map<IdString, VendorMatchEntry>     VendorMatch;
-    int         _nextId = -1;
-    VendorMatch _vendorMatch;
+    mutable VendorMatch _vendorMatch;  ///< Cache mapping vendor strings to equivalence class ID
+    mutable unsigned _nextId = 0;      ///< Least equivalence class ID in use (decremented).
 
-    /** Reset match cache if global VendorMap was changed. */
-    inline void vendorMatchIdReset()
+    /** Reset vendor match cache if _vendorGroupMap was changed. */
+    void vendorMatchIdReset()
     {
-      _nextId = -1;
+      _nextId = 0;
       _vendorMatch.clear();
     }
 
-    /**
-     * Helper mapping vendor string to eqivalence class ID.
+    /** Helper mapping a vendor string to it's eqivalence class ID.
      *
      * \li Return the vendor strings eqivalence class ID stored in _vendorMatch.
      * \li If not found, assign and return the eqivalence class ID of the lowercased string.
-     * \li If not found, assign and return a new ID (look into the predefined VendorMap (id>0),
+     * \li If not found, assign and return a new ID (look into the predefined VendorGroupMap (id>0),
      *     otherwise create a new ID (<0)).
      */
-    inline unsigned vendorMatchId( IdString vendor )
+    unsigned vendorMatchId( IdString vendor ) const;
+
+  private:
+    friend Impl * rwcowClone<Impl>( const Impl * rhs );
+    /** clone for RWCOW_pointer */
+    Impl * clone() const
+    { return new Impl( *this ); }
+  };
+
+  unsigned VendorAttr::Impl::vendorMatchId( IdString vendor ) const
+  {
+    VendorMatchEntry & ent { _vendorMatch[vendor] };
+    if ( ! ent )
     {
-      VendorMatchEntry & ent( _vendorMatch[vendor] );
-      if ( ! ent )
+      IdString lcvendor { str::toLower( vendor.asString() ) };
+      VendorMatchEntry & lcent( _vendorMatch[lcvendor] );
+      if ( ! lcent )
       {
-        IdString lcvendor( str::toLower( vendor.asString() ) );
-        VendorMatchEntry & lcent( _vendorMatch[lcvendor] );
-        if ( ! lcent )
-        {
-          unsigned myid = 0;
-         // bnc#812608: no pefix compare in opensuse namespace
-         static const IdString openSUSE( "opensuse" );
-         if ( lcvendor == openSUSE || ! str::hasPrefix( lcvendor.c_str(), openSUSE.c_str() ) )
+       // Cache miss - check whether it belongs to a vendor group.
+       // Otherwise assign a new class ID to it.
+       unsigned myid = 0;
+
+       // bnc#812608: no prefix compare in opensuse namespace
+       if ( str::hasPrefix( lcvendor.c_str(), "opensuse" ) )
+       {
+         if ( auto it = _vendorGroupMap.find( lcvendor.c_str() ); it != _vendorGroupMap.end() )
+           myid = it->second;
+       }
+       else
+       {
+         // Compare this entry with the global vendor map.
+         // Reversed to get the longest prefix.
+         for ( VendorGroupMap::const_reverse_iterator it = _vendorGroupMap.rbegin(); it != _vendorGroupMap.rend(); ++it )
          {
-           // Compare this entry with the global vendor map.
-           // Reversed to get the longest prefix.
-           for ( VendorMap::reverse_iterator it = _vendorMap.rbegin(); it != _vendorMap.rend(); ++it )
-           {
-             if ( str::hasPrefix( lcvendor.c_str(), it->first ) )
-             {
-               myid = it->second;
-               break; // found
-             }
+           if ( str::hasPrefix( lcvendor.c_str(), it->first ) ) {
+             myid = it->second;
+             break; // found
            }
          }
-         if ( ! myid )
-          {
-            myid = --_nextId; // get a new class ID
-          }
-          ent = lcent = myid; // remember the new DI
-        }
-        else
-        {
-          ent = lcent; // take the ID from the lowercased vendor string
        }
-      }
-      return ent;
-    }
-    /////////////////////////////////////////////////////////////////
-  } // namespace
-  ///////////////////////////////////////////////////////////////////
 
-  const VendorAttr & VendorAttr::instance()
-  {
-      static VendorAttr _val;
-      return _val;
-  }
+       if ( ! myid )
+         myid = --_nextId; // get a new class ID
 
-  VendorAttr::VendorAttr ()
-  {
-      vendorGroupCounter = 1;
-      Pathname vendorPath (ZConfig::instance().vendorPath());
-      {
-       Target_Ptr trg( getZYpp()->getTarget() );
-       if ( trg )
-         vendorPath = trg->root() / vendorPath;
+       ent = lcent = myid; // remember the new DI
       }
-      // creating entries
-      addVendorDirectory (vendorPath);
-
-      // bsc#1030686: The legacy default equivalence of 'suse' and 'opensuse'
-      // has been removed. Unless they are mentioned in a custom rule, create
-      // separate classes for them.
-      if ( _vendorMap.find("suse") == _vendorMap.end() )
-       _vendorMap["suse"] = ++vendorGroupCounter;
-
-      if ( _vendorMap.find("opensuse") == _vendorMap.end() )
-       _vendorMap["opensuse"] = ++vendorGroupCounter;
-
-      MIL << *this << endl;
+      else
+       ent = lcent; // take the ID from the lowercased vendor string
+    }
+    return ent;
   }
 
-  void VendorAttr::_addVendorList( VendorList & vendorList_r ) const
+  void VendorAttr::Impl::addVendorList( VendorList && vendorList_r )
   {
-    unsigned int nextId = vendorGroupCounter + 1;
-       // convert to lowercase and check if a vendor is already defined
-       // in an existing group.
+    // Will add a new equivalence group unless we merge with existing groups.
+    unsigned targetId = _vendorGroupId + 1;
 
-    for_( it, vendorList_r.begin(), vendorList_r.end() )
+    // (!) Convert the group strings in place to lowercase before adding/checking.
+    // Extend/join already existing groups if they are referenced.
+    for ( std::string & vendor : vendorList_r )
     {
-      *it = str::toLower( *it );
-      if (_vendorMap.find(*it) != _vendorMap.end())
+      vendor = str::toLower( std::move(vendor) );
+
+      if ( _vendorGroupMap.count( vendor ) )
       {
-        if (nextId != vendorGroupCounter + 1 &&
-            nextId != _vendorMap[*it])
-        {
-         // We have at least 3 groups which has to be mixed --> mix the third group to the first
-          unsigned int moveID = _vendorMap[*it];
-          for_( itMap, _vendorMap.begin(), _vendorMap.end() )
-          {
-            if (itMap->second == moveID)
-              itMap->second = nextId;
-          }
-        }
-        else
-        {
-          nextId = _vendorMap[*it];
-          WAR << "Vendor " << *it << " is already used in another vendor group. --> mixing these groups" << endl;
-        }
+       unsigned joinId = _vendorGroupMap[vendor];
+       if ( targetId == _vendorGroupId + 1 ) {
+         targetId = joinId;    // will extend the existing group
+       }
+       else if ( targetId != joinId ) {
+         // yet another existing group -> join it into the target group
+         for ( auto & el : _vendorGroupMap ) {
+           if ( el.second == joinId )
+             el.second = targetId;
+         }
+       }
+       vendor.clear(); // no need to add it later
       }
     }
-       // add new entries
-    for_( it, vendorList_r.begin(), vendorList_r.end() )
-    {
-      _vendorMap[*it] = nextId;
+
+    // Now add the new entries
+    for ( std::string & vendor : vendorList_r ) {
+      if ( ! vendor.empty() )
+       _vendorGroupMap[vendor] = targetId;
     }
 
-    if (nextId == vendorGroupCounter + 1)
-      ++vendorGroupCounter;
+    if ( targetId == _vendorGroupId + 1 )
+      ++_vendorGroupId;
 
     // invalidate any match cache
     vendorMatchIdReset();
   }
 
-  bool VendorAttr::addVendorFile( const Pathname & filename ) const
+  /** \relates VendorAttr::Impl Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const VendorAttr::Impl & obj )
   {
-      parser::IniDict dict;
+    str << "Equivalent vendors:";
+    for( const auto & p : obj._vendorGroupMap ) {
+      str << endl << "   [" << p.second << "] " << p.first;
+    }
+    return str;
+  }
 
-      if ( PathInfo(filename).isExist())
-      {
-          InputStream is(filename);
-          dict.read(is);
-      }
-      else
-      {
-          MIL << filename << " not found." << endl;
-          return false;
-      }
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : VendorAttr
+  //
+  ///////////////////////////////////////////////////////////////////
 
-      for ( parser::IniDict::section_const_iterator sit = dict.sectionsBegin();
-           sit != dict.sectionsEnd();
-           ++sit )
-      {
-          std::string section(*sit);
-          //MIL << section << endl;
-          for ( parser::IniDict::entry_const_iterator it = dict.entriesBegin(*sit);
-                it != dict.entriesEnd(*sit);
-                ++it )
-          {
-             std::string entry(it->first);
-             std::string value(it->second);
-             if ( section == "main" )
-             {
-                 if ( entry == "vendors" )
-                 {
-                     VendorList vendorlist;
-                     str::split( value, back_inserter(vendorlist), "," );
-                     _addVendorList (vendorlist);
-                     break;
-                 }
-             }
-         }
-      }
+  const VendorAttr & VendorAttr::instance()
+  {
+    Target_Ptr trg { getZYpp()->getTarget() };
+    return trg ? trg->vendorAttr() : noTargetInstance();
+  }
 
-      return true;
+  VendorAttr & VendorAttr::noTargetInstance()
+  {
+    static VendorAttr _val { ZConfig::instance().vendorPath() };
+    return _val;
   }
 
-  bool VendorAttr::addVendorDirectory( const Pathname & dirname ) const
+  VendorAttr::VendorAttr()
+  : _pimpl( new Impl )
   {
-      if ( ! PathInfo(dirname).isExist() )
-      {
-          MIL << dirname << " not found." << endl;
-          return false;
-      }
+    MIL << "Initial: " << *this << endl;
+  }
+
+  VendorAttr::VendorAttr( const Pathname & initial_r )
+  : _pimpl( new Impl )
+  {
+    addVendorDirectory( initial_r );
+    MIL << "Initial " << initial_r << ": " << *this << endl;
+  }
+
+  VendorAttr::~VendorAttr()
+  {}
+
+  bool VendorAttr::addVendorDirectory( const Pathname & dirname_r )
+  {
+    if ( PathInfo pi { dirname_r }; ! pi.isDir() ) {
+      MIL << "Not a directory " << pi << endl;
+      return false;
+    }
+
+    filesystem::dirForEach( dirname_r, filesystem::matchNoDots(),
+                           [this]( const Pathname & dir_r, const std::string & str_r )->bool
+                           {
+                             this->addVendorFile( dir_r/str_r );
+                             return true;
+                           }
+    );
+    return true;
+  }
+
+  bool VendorAttr::addVendorFile( const Pathname & filename_r )
+  {
+    if ( PathInfo pi { filename_r }; ! pi.isFile() ) {
+      MIL << "Not a file " << pi << endl;
+      return false;
+    }
 
-      std::list<Pathname> filenames;
-      filesystem::readdir( filenames, dirname, false );
-      for_( it, filenames.begin(), filenames.end() ) {
-         MIL << "Adding file " << *it << endl;
-         addVendorFile( *it );
+    parser::IniDict dict { InputStream(filename_r) };
+    for ( const auto & el : dict.entries("main") )
+    {
+      if ( el.first == "vendors" )
+      {
+       VendorList tmp;
+       strv::split( el.second, ",", strv::Trim::trim,
+                    [&tmp]( std::string_view word ) {
+                      if ( ! word.empty() )
+                        tmp.push_back( std::string(word) );
+                    } );
+       _addVendorList( std::move(tmp) );
+       break;
       }
-      return true;
+    }
+    return true;
   }
 
+  void VendorAttr::_addVendorList( VendorList && vendorList_r )
+  { _pimpl->addVendorList( std::move(vendorList_r) ); }
+
+  unsigned VendorAttr::foreachVendorList( std::function<bool(VendorList)> fnc_r ) const
+  { return _pimpl->foreachVendorList( std::move(fnc_r) ); }
+
+#if LEGACY(1722)
+  bool VendorAttr::addVendorDirectory( const Pathname & dirname ) const
+  { return const_cast<VendorAttr*>(this)->addVendorDirectory( dirname ); }
+
+  bool VendorAttr::addVendorFile( const Pathname & filename ) const
+  { return const_cast<VendorAttr*>(this)->addVendorFile( filename ); }
+
+  void VendorAttr::_addVendorList( std::vector<std::string> & vendorList_r ) const
+  { return const_cast<VendorAttr*>(this)->_addVendorList( VendorList( vendorList_r.begin(), vendorList_r.end() ) ); }
+
+  void VendorAttr::_addVendorList( std::vector<IdString> && list_r )
+  {
+    VendorList tmp;
+    for ( const auto & el : list_r )
+      tmp.push_back( std::string(el) );
+    _addVendorList( std::move(tmp) );
+  }
+#endif
   //////////////////////////////////////////////////////////////////
   // vendor equivalence:
   //////////////////////////////////////////////////////////////////
 
   bool VendorAttr::equivalent( IdString lVendor, IdString rVendor ) const
-  {
-    if ( lVendor == rVendor )
-      return true;
-    return vendorMatchId( lVendor ) == vendorMatchId( rVendor );
-  }
+  { return _pimpl->equivalent( lVendor, rVendor );}
 
   bool VendorAttr::equivalent( const Vendor & lVendor, const Vendor & rVendor ) const
-  { return equivalent( IdString( lVendor ), IdString( rVendor ) );
-  }
+  { return _pimpl->equivalent( IdString( lVendor ), IdString( rVendor ) ); }
 
   bool VendorAttr::equivalent( sat::Solvable lVendor, sat::Solvable rVendor ) const
-  { return equivalent( lVendor.vendor(), rVendor.vendor() ); }
+  { return _pimpl->equivalent( lVendor.vendor(), rVendor.vendor() ); }
 
   bool VendorAttr::equivalent( const PoolItem & lVendor, const PoolItem & rVendor ) const
-  { return equivalent( lVendor.satSolvable().vendor(), rVendor.satSolvable().vendor() ); }
+  { return _pimpl->equivalent( lVendor.satSolvable().vendor(), rVendor.satSolvable().vendor() ); }
 
   //////////////////////////////////////////////////////////////////
 
-  std::ostream & operator<<( std::ostream & str, const VendorAttr & /*obj*/ )
-  {
-    str << "Equivalent vendors:";
-    for_( it, _vendorMap.begin(), _vendorMap.end() )
-    {
-      str << endl << "   [" << it->second << "] " << it->first;
-    }
-    return str;
-  }
+  std::ostream & operator<<( std::ostream & str, const VendorAttr & obj )
+  { return str << *obj._pimpl; }
 
   /////////////////////////////////////////////////////////////////
 } // namespace zypp