/** \file zypp/CpeId.cc
*/
#include <iostream>
+#include <array>
#include "zypp/base/String.h"
#include "zypp/base/LogTools.h"
break;
// else: fallthrough
default:
- throw std::invalid_argument( "CpeId:Wfn:part: illegal value" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn:part: '" << wfn << "' illegal value; expected: 'h' | 'o' | 'a'" );
break;
}
}
}
}
if ( wfn.size() != len )
- throw std::invalid_argument( "CpeId:Wfn:language: illegal value" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn:language: '" << wfn << "' illegal value; expected RFC5646 conform: language ['-' region]" );
}
break;
ret = unbindFs( cpe_r );
}
else
- throw std::invalid_argument( "CpeId: bad magic" );
+ throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
}
else if ( cpe_r[0] != '\0' )
- throw std::invalid_argument( "CpeId: bad magic" );
+ throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
return ret;
}
{
Wfn ret;
+ static constexpr unsigned numUriAttr = 7u; // basic URI attibutes
std::vector<std::string> field;
- field.reserve( Attribute::numAttributes );
- if ( str::splitFields( cpe_r.c_str()+5/* skip magic 'cpe:/' */, std::back_inserter(field), ":" ) > Attribute::numAttributes )
- throw std::invalid_argument( "CpeId:Uri: too many fields" );
- field.resize( Attribute::numAttributes ); // fillup with ANY("")
+ field.reserve( Attribute::numAttributes ); // reserve 7 + 4 for packed extened attrs in edition
+ if ( str::splitFields( cpe_r.c_str()+5/* skip magic 'cpe:/' */, std::back_inserter(field), ":" ) > numUriAttr )
+ throw std::invalid_argument( str::Str() << "CpeId:Uri: too many fields (" << field.size() << "); expected " << numUriAttr );
+ field.resize( Attribute::numAttributes ); // fillup with ANY(""),
for ( auto ai : WFN_ATTRIBUTES )
{
std::vector<std::string> pack;
pack.reserve( numPacks );
if ( str::splitFields( field[ai], std::back_inserter(pack), "~" ) > numPacks )
- throw std::invalid_argument( "CpeId:Uri: too many packs" );
+ throw std::invalid_argument( str::Str() << "CpeId:Uri:edition: too many packs (" << pack.size() << "); expected " << numPacks );
pack.resize( numPacks ); // fillup with ANY(""), should be noOP
pack[1].swap( field[Attribute::edition] );
std::vector<std::string> field;
field.reserve( Attribute::numAttributes );
if ( str::splitFields( cpe_r.c_str()+8/* skip magic 'cpe:2.3:' */, std::back_inserter(field), ":" ) > Attribute::numAttributes )
- throw std::invalid_argument( "CpeId:Fs: too many fields" );
+ throw std::invalid_argument( str::Str() << "CpeId:Fs: too many fields (" << field.size() << "); expected 11" /*<< Attribute::numAttributes but g++ currently can't resoolve this as constexpr*/ );
+ if ( !field.empty() && field.back().empty() ) // A trailing ':' leads to an empty (illegal) field, but we fillup missing fields with ANY|"*"
+ field.back() = "*";
field.resize( Attribute::numAttributes, "*" ); // fillup with ANY|"*"
for ( auto ai : WFN_ATTRIBUTES )
// class CpeId
///////////////////////////////////////////////////////////////////
+ std::string CpeId::NoThrowType::lastMalformed;
+
CpeId::CpeId()
: _pimpl( new Impl )
{}
CpeId::CpeId( const std::string & cpe_r, NoThrowType )
{
try
- { _pimpl.reset( new Impl( cpe_r ) ); }
+ {
+ _pimpl.reset( new Impl( cpe_r ) );
+ NoThrowType::lastMalformed.clear();
+ }
catch(...)
- { _pimpl.reset( new Impl ); }
+ {
+ _pimpl.reset( new Impl );
+ NoThrowType::lastMalformed = cpe_r;
+ }
}
CpeId::~CpeId()
if ( ! chIsValidRange( *chp ) )
{
if ( *chp )
- throw std::invalid_argument( "CpeId:Wfn: illegal quoted character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal quoted character '\\" << reinterpret_cast<void*>(*chp) << "'" );
else
throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
}
else if ( chIsWfnUnescaped( *chp ) )
- throw std::invalid_argument( "CpeId:Wfn: unnecessarily quoted character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn: unnecessarily quoted character '\\" << *chp << "'" );
else if ( starting && *chp == '-' && chp+1 == value_r.end() )
- throw std::invalid_argument( "CpeId:Wfn: '\\-' is illegal value" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn: '\\-' is illegal value" );
break;
case '?': // sequence at beginning or end of string
if ( ! chIsWfnUnescaped( *chp ) )
{
if ( chIsValidRange( *chp ) )
- throw std::invalid_argument( "CpeId:Wfn: missing quote" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn: missing quote before '" << *chp << "'" );
else
- throw std::invalid_argument( "CpeId:Wfn: illegal character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
}
break;
}
else if ( chIsValidRange( *chp ) )
result << '\\' << *chp;
else if ( *chp )
- throw std::invalid_argument( "CpeId:Fs: illegal quoted character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal quoted character '\\" << *chp << "'" );
else
throw std::invalid_argument( "CpeId:Fs: Backslash escapes nothing" );
break;
else if ( chIsValidRange( *chp ) )
result << '\\' << *chp;
else
- throw std::invalid_argument( "CpeId:Fs: illegal character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
break;
}
if ( starting )
starting = false;
}
if ( starting )
- throw std::invalid_argument( "CpeId:Fs: '' is illegal" );
+ throw std::invalid_argument( "CpeId:Fs: '' value is illegal" );
_value.reset( new std::string( result ) );
}
}
}
ch = (d1<<4)|d2;
if ( ! chIsValidRange( ch ) )
- throw std::invalid_argument( "CpeId:Uri: illegal % encoded character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal % encoded character '" << reinterpret_cast<void*>(ch) << "'" );
}
}
}
else if ( ! chIsValidRange( ch ) )
- throw std::invalid_argument( "CpeId:Uri: illegal character" );
+ throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal character '" << reinterpret_cast<void*>(ch) << "'" );
if ( chIsWfnUnescaped( ch ) )
result << ch;
|| ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
}
- SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
+ ///////////////////////////////////////////////////////////////////
+ /// Symmetric attribute compare if wildcards are involved!
+ /// The specs define any comarison with a wildcarded attribute as
+ /// target to return \c uncomparable:
+ /// \code
+ /// wildcardfree <=> wildcarded ==> uncomparable,
+ /// wildcarded <=> wildcardfree ==> superset or disjoint
+ /// \endcode
+ /// But a symmetric result is much more intuitive:
+ /// \code
+ /// wildcardfree <=> wildcarded ==> subset or disjoint
+ /// wildcarded <=> wildcardfree ==> superset or disjoint
+ /// \endcode
+ ///////////////////////////////////////////////////////////////////
+#define WFN_STRICT_SPEC 0
+#if WFN_STRICT_SPEC
+ //SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
{
static const SetCompare _NeedsCloserLook( SetCompare::Enum(-1) ); // artificial Compare value
static const SetCompare matchTabel[4][4] = {{
}
return ret;
}
+#else
+ SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
+ {
+ ///////////////////////////////////////////////////////////////////
+ // ANY, ANY => equal
+ // ANY, NA => properSuperset
+ // ANY, wildcardfree => properSuperset
+ // ANY, wildcarded => properSuperset
+ //
+ // NA, ANY => properSubset
+ // NA, NA => equal
+ // NA, wildcardfree => disjoint
+ // NA, wildcarded => disjoint
+ //
+ // wildcardfree, ANY => properSubset
+ // wildcardfree, NA => disjoint
+ // wildcardfree, wildcardfree => NeedsCloserLook: equal or disjoint
+ // wildcardfree, wildcarded => NeedsCloserLook: subset or disjoint
+ //
+ // wildcarded, ANY => properSubset
+ // wildcarded, NA => disjoint
+ // wildcarded, wildcardfree => NeedsCloserLook: superset or disjoint
+ // wildcarded, wildcarded => NeedsCloserLook" equal or uncomparable
+ ///////////////////////////////////////////////////////////////////
+
+ SetCompare ret = SetCompare::disjoint;
+
+ if ( isANY() )
+ {
+ ret = trg.isANY() ? SetCompare::equal : SetCompare::properSuperset;
+ }
+ else if ( trg.isANY() )
+ {
+ ret = SetCompare::properSubset;
+ }
+ else if ( isNA() )
+ {
+ if ( trg.isNA() ) ret = SetCompare::equal; // else: SetCompare::disjoint;
+ }
+ else if ( ! trg.isNA() ) // else: SetCompare::disjoint;
+ {
+ // NeedsCloserLook:
+ if ( isWildcarded() )
+ {
+ if ( trg.isWildcarded() )
+ {
+ // simple string compare just to detect 'equal'
+ ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::uncomparable;
+ }
+ else
+ {
+ // Needs wildcard compare (src,trg)
+ if ( matchWildcardedString( *_value, *trg._value ) ) ret = SetCompare::properSuperset; // else: SetCompare::disjoint;
+ }
+ }
+ else
+ {
+ if ( trg.isWildcarded() )
+ {
+ // Needs wildcard compare (trg,src)
+ if ( matchWildcardedString( *trg._value, *_value ) ) ret = SetCompare::properSubset; // else: SetCompare::disjoint;
+ }
+ else
+ {
+ // simple string compare
+ if ( matchWildcardfreeString( *_value, *trg._value ) ) ret = SetCompare::equal; // else: SetCompare::disjoint;
+ }
+ }
+ }
+ return ret;
+ }
+#endif // WFN_STRICT_SPEC
std::ostream & operator<<( std::ostream & str, const CpeId::Value & obj )
{ return str << obj.asString(); }