14#include <zypp/base/String.h>
15#include <zypp/base/LogTools.h>
16#include <zypp/base/NonCopyable.h>
23#define WFN_ATTRIBUTES {\
30 Attribute::language, \
31 Attribute::sw_edition,\
32 Attribute::target_sw, \
33 Attribute::target_hw, \
44 inline int heDecodeCh(
char ch )
46 if (
'0' <= ch && ch <=
'9' )
48 if (
'A' <= ch && ch <=
'F' )
49 return( ch -
'A' + 10 );
50 if (
'a' <= ch && ch <=
'f' )
51 return( ch -
'a' + 10 );
56 inline bool chIsValidRange(
char ch )
57 {
return(
'!' <= ch && ch <=
'~' ); }
60 inline bool chIsAlpha(
char ch )
61 {
return( (
'a' <= ch && ch <=
'z' ) || (
'A' <= ch && ch <=
'Z' ) ); }
64 inline bool chIsNum(
char ch )
65 {
return(
'0' <= ch && ch <=
'9' ); }
68 inline bool chIsAlNum(
char ch )
69 {
return( chIsAlpha( ch ) || chIsNum( ch ) ); }
72 inline bool chIsWfnUnescaped(
char ch )
73 {
return( chIsAlNum( ch ) || ch ==
'_' ); }
86 typedef std::array<Value,Attribute::numAttributes>
Wfn;
91 Impl(
const std::string & cpe_r )
96 explicit operator bool()
const
97 {
for (
const auto & val :
_wfn )
if ( ! val.isANY() )
return true;
return false; }
105 ret <<
':' <<
_wfn[ai].asFs();
118 val =
_wfn[ai].asUri();
120 if ( ai == Attribute::edition )
122 if ( ! (
_wfn[Attribute::sw_edition].isANY()
123 &&
_wfn[Attribute::target_sw].isANY()
124 &&
_wfn[Attribute::target_hw].isANY()
125 &&
_wfn[Attribute::other].isANY() ) )
130 <<
'~' <<
_wfn[Attribute::sw_edition].asUri()
131 <<
'~' <<
_wfn[Attribute::target_sw].asUri()
132 <<
'~' <<
_wfn[Attribute::target_hw].asUri()
133 <<
'~' <<
_wfn[Attribute::other].asUri();
140 ret << std::string( colon,
':' );
147 if ( ai == Attribute::language )
162 if ( ai ) ret <<
',';
163 ret << Attribute::asString( ai ) <<
'=';
165 ret <<
'"' << val <<
'"';
176 SetCompare ret = SetCompare::equal;
181 case SetCompare::uncomparable:
182 ret = SetCompare::uncomparable;
185 case SetCompare::equal:
188 case SetCompare::properSubset:
189 if ( ret == SetCompare::equal )
190 ret = SetCompare::properSubset;
191 else if ( ret != SetCompare::properSubset )
192 ret = SetCompare::uncomparable;
195 case SetCompare::properSuperset:
196 if ( ret == SetCompare::equal )
197 ret = SetCompare::properSuperset;
198 else if ( ret != SetCompare::properSuperset )
199 ret = SetCompare::uncomparable;
202 case SetCompare::disjoint:
203 ret = SetCompare::disjoint;
206 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
220 switch ( attr_r.asEnum() )
222 case Attribute::part:
224 const std::string & wfn( val_r.
asWfn() );
230 if ( wfn[1] ==
'\0' )
234 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:part: '" << wfn <<
"' illegal value; expected: 'h' | 'o' | 'a'" );
240 case Attribute::language:
242 const std::string & wfn( val_r.
asWfn() );
243 std::string::size_type len = 0;
245 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
247 len = chIsAlpha( wfn[2] ) ? 3 : 2;
248 if ( wfn[len] ==
'-' )
250 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
252 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
256 if ( wfn.size() != len )
257 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:language: '" << wfn <<
"' illegal value; expected RFC5646 conform: language ['-' region]" );
266 wfn_r[attr_r.asIntegral()] = val_r;
273 static Wfn unbind(
const std::string & cpe_r );
297 if ( cpe_r[4] ==
'/' )
301 else if ( cpe_r[4] ==
'2'
309 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
311 else if ( cpe_r[0] !=
'\0' )
312 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
320 static constexpr unsigned numUriAttr = 7u;
321 std::vector<std::string> field;
322 field.reserve( Attribute::numAttributes );
323 if (
str::splitFields( cpe_r.c_str()+5, std::back_inserter(field),
":" ) > numUriAttr )
324 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: too many fields (" << field.size() <<
"); expected " << numUriAttr );
325 field.resize( Attribute::numAttributes );
329 if ( ai == Attribute::edition && field[ai][0] ==
'~' )
332 static constexpr unsigned numPacks = 6u;
333 std::vector<std::string> pack;
334 pack.reserve( numPacks );
335 if (
str::splitFields( field[ai], std::back_inserter(pack),
"~" ) > numPacks )
336 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri:edition: too many packs (" << pack.size() <<
"); expected " << numPacks );
337 pack.resize( numPacks );
339 pack[1].swap( field[Attribute::edition] );
340 pack[2].swap( field[Attribute::sw_edition] );
341 pack[3].swap( field[Attribute::target_sw] );
342 pack[4].swap( field[Attribute::target_hw] );
343 pack[5].swap( field[Attribute::other] );
354 std::vector<std::string> field;
355 field.reserve( Attribute::numAttributes );
356 if (
str::splitFields( cpe_r.c_str()+8, std::back_inserter(field),
":" ) > Attribute::numAttributes )
357 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: too many fields (" << field.size() <<
"); expected 11" );
358 if ( !field.empty() && field.back().empty() )
360 field.resize( Attribute::numAttributes,
"*" );
381 : _pimpl( new
Impl( cpe_r ) )
402 {
return bool(*_pimpl); }
405 {
return _pimpl->asFs(); }
408 {
return _pimpl->asUri(); }
411 {
return _pimpl->asWfn(); }
414 {
return _pimpl->setRelationMixinCompare( *trg.
_pimpl ); }
422 static std::map<Enum,std::string> _table = {
423#define OUTS(N) { N, #N }
437 return _table[val_r];
452 if ( value_r.empty() )
455 _value.reset(
new std::string );
459 else if ( value_r !=
"*" )
461 bool starting =
true;
462 for_( chp, value_r.begin(), value_r.end() )
468 if ( ! chIsValidRange( *chp ) )
471 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal quoted character '\\" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
473 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
475 else if ( chIsWfnUnescaped( *chp ) )
476 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: unnecessarily quoted character '\\" << *chp <<
"'" );
477 else if ( starting && *chp ==
'-' && chp+1 == value_r.end() )
478 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: '\\-' is illegal value" );
482 while ( *(chp+1) ==
'?' )
484 if ( ! ( starting || chp+1 == value_r.end() ) )
485 throw std::invalid_argument(
"CpeId:Wfn: embedded ?" );
489 if ( ! ( starting || chp+1 == value_r.end() ) )
490 throw std::invalid_argument(
"CpeId:Wfn: embedded *" );
494 if ( ! chIsWfnUnescaped( *chp ) )
496 if ( chIsValidRange( *chp ) )
497 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: missing quote before '" << *chp <<
"'" );
499 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal character '" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
506 _value.reset(
new std::string( value_r ) );
512 if ( encoded_r !=
"*" )
514 if ( encoded_r ==
"-" )
521 bool starting =
true;
522 for_( chp, encoded_r.begin(), encoded_r.end() )
528 if ( chIsWfnUnescaped( *chp ) )
530 else if ( chIsValidRange( *chp ) )
531 result <<
'\\' << *chp;
533 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal quoted character '\\" << *chp <<
"'" );
535 throw std::invalid_argument(
"CpeId:Fs: Backslash escapes nothing" );
540 while ( *(chp+1) ==
'?' )
545 if ( ! ( starting || chp+1 == encoded_r.end() ) )
546 throw std::invalid_argument(
"CpeId:Fs: embedded ?" );
550 if ( starting || chp+1 == encoded_r.end() )
553 throw std::invalid_argument(
"CpeId:Fs: embedded *" );
557 if ( chIsWfnUnescaped( *chp ) )
559 else if ( chIsValidRange( *chp ) )
560 result <<
'\\' << *chp;
562 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal character '" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
569 throw std::invalid_argument(
"CpeId:Fs: '' value is illegal" );
570 _value.reset(
new std::string( result ) );
577 if ( ! encoded_r.empty() )
579 if ( encoded_r ==
"-" )
586 bool starting =
true;
587 for_( chp, encoded_r.begin(), encoded_r.end() )
593 int d1 = heDecodeCh( *(chp+1) );
596 int d2 = heDecodeCh( *(chp+2) );
605 while ( *(chp+1) ==
'%' && *(chp+2) ==
'0' && *(chp+3) ==
'1' )
610 if ( starting || chp+1 == encoded_r.end() )
616 throw std::invalid_argument(
"CpeId:Uri: embedded %01" );
620 if ( starting || chp+1 == encoded_r.end() )
627 throw std::invalid_argument(
"CpeId:Uri: embedded %02" );
631 if ( ! chIsValidRange( ch ) )
632 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal % encoded character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
636 else if ( ! chIsValidRange( ch ) )
637 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
639 if ( chIsWfnUnescaped( ch ) )
642 result <<
'\\' << ch;
647 _value.reset(
new std::string( result ) );
657 static const std::string any(
"*" );
670 static const std::string asterisk(
"*" );
675 static const std::string dash(
"-" );
697 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
701 result <<
'\\' << *chp;
718 static const std::string dash(
"-" );
726 if ( chIsWfnUnescaped( *chp ) )
732 static const char *
const hdig =
"0123456789abcdef";
745 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
749 result <<
'%' << hdig[(
unsigned char)(*chp)/16] << hdig[(
unsigned char)(*chp)%16];
763 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal char '" << *chp <<
"' in WFN" );
778 inline bool isWildchar(
char ch_r )
779 {
return( ch_r ==
'*' || ch_r ==
'?' ); }
784 inline bool evenNumberOfBackslashes( std::string::const_reverse_iterator rbegin_r, std::string::const_reverse_iterator rend_r )
786 unsigned backslashes = 0;
787 for_( it, rbegin_r, rend_r )
794 return !(backslashes & 1U);
798 inline unsigned trueCharsIn(
const std::string & str_r, std::string::size_type begin_r, std::string::size_type end_r )
801 for_( it, begin_r, end_r )
804 if ( str_r[it] ==
'\\' )
814 inline bool matchWildcardfreeString(
const std::string & lhs,
const std::string & rhs )
843 inline bool matchWildcardedString( std::string src, std::string trg )
847 std::string::size_type prefx = 0;
848 switch ( *src.begin() )
851 if ( src.size() == 1 )
854 prefx = std::string::npos;
859 for_( it, ++src.begin(), src.end() )
860 {
if ( *it ==
'?' ) ++prefx;
else break; }
861 if ( src.size() == prefx )
862 return( trg.size() <= prefx );
864 src.erase( 0, prefx );
870 std::string::size_type suffx = 0;
873 switch ( *src.rbegin() )
876 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
878 suffx = std::string::npos;
879 src.erase( src.size()-1 );
884 for_( it, ++src.rbegin(), src.rend() )
885 {
if ( *it ==
'?' ) ++suffx;
else break; }
886 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
888 src.erase( src.size()-suffx );
897 for ( std::string::size_type match = trg.find( src, 0 );
898 match != std::string::npos;
899 match = trg.find( src, match+1 ) )
901 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
903 std::string::size_type frontSize = match + src.size();
904 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
915 const std::string & value( *
_value );
916 return ( isWildchar( *value.begin() )
917 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
934#define WFN_STRICT_SPEC 0
938 static const SetCompare kNeedsCloserLook( SetCompare::Enum(-1) );
939 static const SetCompare matchTabel[4][4] = {{
941 SetCompare::properSuperset,
942 SetCompare::properSuperset,
943 SetCompare::uncomparable,
945 SetCompare::properSubset,
947 SetCompare::disjoint,
948 SetCompare::uncomparable,
950 SetCompare::properSubset,
951 SetCompare::disjoint,
953 SetCompare::uncomparable,
955 SetCompare::properSubset,
956 SetCompare::disjoint,
958 SetCompare::uncomparable,
961 Type srcType = type();
962 Type trgType = trg.type();
963 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
964 if ( ret == kNeedsCloserLook )
966 if ( srcType == Type::wildcardfree )
969 ret = matchWildcardfreeString( *
_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
971 else if ( srcType == Type::wildcarded )
974 ret = matchWildcardedString( *
_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
1004 SetCompare ret = SetCompare::disjoint;
1008 ret = trg.
isANY() ? SetCompare::equal : SetCompare::properSuperset;
1010 else if ( trg.
isANY() )
1012 ret = SetCompare::properSubset;
1016 if ( trg.
isNA() ) ret = SetCompare::equal;
1018 else if ( ! trg.
isNA() )
1021 if ( isWildcarded() )
1026 ret = matchWildcardfreeString( *
_value, *trg.
_value ) ? SetCompare::equal : SetCompare::uncomparable;
1031 if ( matchWildcardedString( *
_value, *trg.
_value ) ) ret = SetCompare::properSuperset;
1039 if ( matchWildcardedString( *trg.
_value, *
_value ) ) ret = SetCompare::properSubset;
1044 if ( matchWildcardfreeString( *
_value, *trg.
_value ) ) ret = SetCompare::equal;
#define WFN_ATTRIBUTES
Initializer list with all wfn attributes.
std::string asUri() const
Impl(const std::string &cpe_r)
SetCompare setRelationMixinCompare(const Impl &trg) const
static void assignAttr(Wfn &wfn_r, Attribute attr_r, const Value &val_r)
Assign val_r if it meets attr_r specific contraints.
static Wfn unbindUri(const std::string &cpe_r)
Parse Uri and unbind.
static Wfn unbind(const std::string &cpe_r)
Parse magic and unbind accordingly.
static Wfn unbindFs(const std::string &cpe_r)
Parse Fs and unbind.
std::array< Value, Attribute::numAttributes > Wfn
std::string asWfn() const
static const Value ANY
Logical value matching ANY value.
bool isNA() const
Whether value is NA.
static const Value NA
Logical value indicating “not applicable/not used".
bool isWildcarded() const
An attribute value string with wildcards ([*?] at begin and/or end)
RWCOW_pointer< std::string > _value
bool isANY() const
Whether value is ANY.
static constexpr UriFormatType uriFormat
Indicator argument for ctor arg in URI format.
static constexpr FsFormatType fsFormat
Indicator argument for ctor arg in FS format.
bool isString() const
Whether it's an attribute value string (not logical value).
std::string asString() const
Default string representation [asWfn].
std::string asFs() const
String representation as in Formated-String (ANY:"*", NA:"-")
SetCompare setRelationMixinCompare(const Value &trg) const
CPE name matching hook for SetRelationMixin.
Value()
Default ctor: ANY.
bool containsWildcard() const
HAs unquoted [*?] at begin and/or end of value.
std::string asUri() const
String representation as in URI (ANY:"", NA:"-")
std::string asWfn() const
String representation as in Well-Formed-Name (ANY:"*", NA:"").
Common Platform Enumearation (2.3) See http://cpe.mitre.org/ for more information on the Common Platf...
std::string asUri() const
String representation as URI (in/out).
base::EnumClass< EAttributeDef > Attribute
'enum class Attribute'
std::ostream & operator<<(std::ostream &str, const CpeId &obj)
Stream output.
static constexpr NoThrowType noThrow
Indicator argument for non-trowing ctor.
std::string asWfn() const
String representation as Well-Formed-Name (internal format, out only).
std::string asFs() const
String representation as Formated-String (in/out).
SetCompare setRelationMixinCompare(const CpeId &trg) const
CPE name matching hook for SetRelationMixin.
CpeId()
Default ctor: ANY-Cpeid, all attribute values are ANY.
RWCOW_pointer< Impl > _pimpl
Implementation class.
SetCompare compare(const CpeId &trg) const
Compare sets.
String related utilities and Regular expression matching.
boost::noncopyable NonCopyable
Ensure derived classes cannot be copied.
unsigned splitFields(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=":")
Split line_r into fields.
std::string toLower(const std::string &s)
Return lowercase version of s.
int compareCI(const C_Str &lhs, const C_Str &rhs)
Easy-to use interface to the ZYPP dependency resolver.
static const std::string & asString(Enum val_r)
string representantion
Indicator type for non-trowing ctor.
static std::string lastMalformed
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
#define for_(IT, BEG, END)
Convenient for-loops using iterator.