| /_____||_| |_| |_| |
| |
\---------------------------------------------------------------------*/
-/*
- 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>
#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>
{ /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
- 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