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 using Wfn = std::array<Value, Attribute::numAttributes>;
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();
117 std::string val =
_wfn[ai].asUri();
119 if ( ai == Attribute::edition )
121 if ( ! (
_wfn[Attribute::sw_edition].isANY()
122 &&
_wfn[Attribute::target_sw].isANY()
123 &&
_wfn[Attribute::target_hw].isANY()
124 &&
_wfn[Attribute::other].isANY() ) )
129 <<
'~' <<
_wfn[Attribute::sw_edition].asUri()
130 <<
'~' <<
_wfn[Attribute::target_sw].asUri()
131 <<
'~' <<
_wfn[Attribute::target_hw].asUri()
132 <<
'~' <<
_wfn[Attribute::other].asUri();
139 ret << std::string( colon,
':' );
146 if ( ai == Attribute::language )
161 if ( ai ) ret <<
',';
162 ret << Attribute::asString( ai ) <<
'=';
164 ret <<
'"' << val <<
'"';
175 SetCompare ret = SetCompare::equal;
180 case SetCompare::uncomparable:
181 ret = SetCompare::uncomparable;
184 case SetCompare::equal:
187 case SetCompare::properSubset:
188 if ( ret == SetCompare::equal )
189 ret = SetCompare::properSubset;
190 else if ( ret != SetCompare::properSubset )
191 ret = SetCompare::uncomparable;
194 case SetCompare::properSuperset:
195 if ( ret == SetCompare::equal )
196 ret = SetCompare::properSuperset;
197 else if ( ret != SetCompare::properSuperset )
198 ret = SetCompare::uncomparable;
201 case SetCompare::disjoint:
202 ret = SetCompare::disjoint;
205 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
219 switch ( attr_r.asEnum() )
221 case Attribute::part:
223 const std::string & wfn( val_r.
asWfn() );
229 if ( wfn[1] ==
'\0' )
233 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:part: '" << wfn <<
"' illegal value; expected: 'h' | 'o' | 'a'" );
239 case Attribute::language:
241 const std::string & wfn( val_r.
asWfn() );
242 std::string::size_type len = 0;
244 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
246 len = chIsAlpha( wfn[2] ) ? 3 : 2;
247 if ( wfn[len] ==
'-' )
249 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
251 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
255 if ( wfn.size() != len )
256 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn:language: '" << wfn <<
"' illegal value; expected RFC5646 conform: language ['-' region]" );
265 wfn_r[attr_r.asIntegral()] = val_r;
272 static Wfn unbind(
const std::string & cpe_r );
296 if ( cpe_r[4] ==
'/' )
300 else if ( cpe_r[4] ==
'2'
308 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
310 else if ( cpe_r[0] !=
'\0' )
311 throw std::invalid_argument(
"CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
319 static constexpr unsigned numUriAttr = 7u;
320 std::vector<std::string> field;
321 field.reserve( Attribute::numAttributes );
322 if (
str::splitFields( cpe_r.c_str()+5, std::back_inserter(field),
":" ) > numUriAttr )
323 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: too many fields (" << field.size() <<
"); expected " << numUriAttr );
324 field.resize( Attribute::numAttributes );
328 if ( ai == Attribute::edition && field[ai][0] ==
'~' )
331 static constexpr unsigned numPacks = 6u;
332 std::vector<std::string> pack;
333 pack.reserve( numPacks );
334 if (
str::splitFields( field[ai], std::back_inserter(pack),
"~" ) > numPacks )
335 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri:edition: too many packs (" << pack.size() <<
"); expected " << numPacks );
336 pack.resize( numPacks );
338 pack[1].swap( field[Attribute::edition] );
339 pack[2].swap( field[Attribute::sw_edition] );
340 pack[3].swap( field[Attribute::target_sw] );
341 pack[4].swap( field[Attribute::target_hw] );
342 pack[5].swap( field[Attribute::other] );
353 std::vector<std::string> field;
354 field.reserve( Attribute::numAttributes );
355 if (
str::splitFields( cpe_r.c_str()+8, std::back_inserter(field),
":" ) > Attribute::numAttributes )
356 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: too many fields (" << field.size() <<
"); expected 11" );
357 if ( !field.empty() && field.back().empty() )
359 field.resize( Attribute::numAttributes,
"*" );
404 {
return _pimpl->asFs(); }
407 {
return _pimpl->asUri(); }
410 {
return _pimpl->asWfn(); }
413 {
return _pimpl->setRelationMixinCompare( *trg.
_pimpl ); }
421 static std::map<Enum,std::string> _table = {
422#define OUTS(N) { N, #N }
436 return _table[val_r];
451 if ( value_r.empty() )
454 _value.reset(
new std::string );
458 else if ( value_r !=
"*" )
460 bool starting =
true;
461 for_( chp, value_r.begin(), value_r.end() )
467 if ( ! chIsValidRange( *chp ) )
470 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal quoted character '\\" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
472 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
474 else if ( chIsWfnUnescaped( *chp ) )
475 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: unnecessarily quoted character '\\" << *chp <<
"'" );
476 else if ( starting && *chp ==
'-' && chp+1 == value_r.end() )
477 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: '\\-' is illegal value" );
481 while ( *(chp+1) ==
'?' )
483 if ( ! ( starting || chp+1 == value_r.end() ) )
484 throw std::invalid_argument(
"CpeId:Wfn: embedded ?" );
488 if ( ! ( starting || chp+1 == value_r.end() ) )
489 throw std::invalid_argument(
"CpeId:Wfn: embedded *" );
493 if ( ! chIsWfnUnescaped( *chp ) )
495 if ( chIsValidRange( *chp ) )
496 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: missing quote before '" << *chp <<
"'" );
498 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal character '" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
505 _value.reset(
new std::string( value_r ) );
511 if ( encoded_r !=
"*" )
513 if ( encoded_r ==
"-" )
520 bool starting =
true;
521 for_( chp, encoded_r.begin(), encoded_r.end() )
527 if ( chIsWfnUnescaped( *chp ) )
529 else if ( chIsValidRange( *chp ) )
530 result <<
'\\' << *chp;
532 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal quoted character '\\" << *chp <<
"'" );
534 throw std::invalid_argument(
"CpeId:Fs: Backslash escapes nothing" );
539 while ( *(chp+1) ==
'?' )
544 if ( ! ( starting || chp+1 == encoded_r.end() ) )
545 throw std::invalid_argument(
"CpeId:Fs: embedded ?" );
549 if ( starting || chp+1 == encoded_r.end() )
552 throw std::invalid_argument(
"CpeId:Fs: embedded *" );
556 if ( chIsWfnUnescaped( *chp ) )
558 else if ( chIsValidRange( *chp ) )
559 result <<
'\\' << *chp;
561 throw std::invalid_argument(
str::Str() <<
"CpeId:Fs: illegal character '" <<
reinterpret_cast<void*
>(*chp) <<
"'" );
568 throw std::invalid_argument(
"CpeId:Fs: '' value is illegal" );
569 _value.reset(
new std::string( result ) );
576 if ( ! encoded_r.empty() )
578 if ( encoded_r ==
"-" )
585 bool starting =
true;
586 for_( chp, encoded_r.begin(), encoded_r.end() )
592 int d1 = heDecodeCh( *(chp+1) );
595 int d2 = heDecodeCh( *(chp+2) );
604 while ( *(chp+1) ==
'%' && *(chp+2) ==
'0' && *(chp+3) ==
'1' )
609 if ( starting || chp+1 == encoded_r.end() )
615 throw std::invalid_argument(
"CpeId:Uri: embedded %01" );
619 if ( starting || chp+1 == encoded_r.end() )
626 throw std::invalid_argument(
"CpeId:Uri: embedded %02" );
630 if ( ! chIsValidRange( ch ) )
631 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal % encoded character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
635 else if ( ! chIsValidRange( ch ) )
636 throw std::invalid_argument(
str::Str() <<
"CpeId:Uri: illegal character '" <<
reinterpret_cast<void*
>(ch) <<
"'" );
638 if ( chIsWfnUnescaped( ch ) )
641 result <<
'\\' << ch;
646 _value.reset(
new std::string( result ) );
656 static const std::string any(
"*" );
669 static const std::string asterisk(
"*" );
674 static const std::string dash(
"-" );
696 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
700 result <<
'\\' << *chp;
717 static const std::string dash(
"-" );
725 if ( chIsWfnUnescaped( *chp ) )
731 static const char *
const hdig =
"0123456789abcdef";
744 throw std::invalid_argument(
"CpeId:Wfn: Backslash escapes nothing" );
748 result <<
'%' << hdig[(
unsigned char)(*chp)/16] << hdig[(
unsigned char)(*chp)%16];
762 throw std::invalid_argument(
str::Str() <<
"CpeId:Wfn: illegal char '" << *chp <<
"' in WFN" );
777 inline bool isWildchar(
char ch_r )
778 {
return( ch_r ==
'*' || ch_r ==
'?' ); }
783 inline bool evenNumberOfBackslashes(
const std::string::const_reverse_iterator& rbegin_r,
const std::string::const_reverse_iterator& rend_r )
785 unsigned backslashes = 0;
786 for_( it, rbegin_r, rend_r )
793 return !(backslashes & 1U);
797 inline unsigned trueCharsIn(
const std::string & str_r, std::string::size_type begin_r, std::string::size_type end_r )
800 for_( it, begin_r, end_r )
803 if ( str_r[it] ==
'\\' )
813 inline bool matchWildcardfreeString(
const std::string & lhs,
const std::string & rhs )
842 inline bool matchWildcardedString( std::string src, std::string trg )
846 std::string::size_type prefx = 0;
847 switch ( *src.begin() )
850 if ( src.size() == 1 )
853 prefx = std::string::npos;
858 for_( it, ++src.begin(), src.end() )
859 {
if ( *it ==
'?' ) ++prefx;
else break; }
860 if ( src.size() == prefx )
861 return( trg.size() <= prefx );
863 src.erase( 0, prefx );
869 std::string::size_type suffx = 0;
872 switch ( *src.rbegin() )
875 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
877 suffx = std::string::npos;
878 src.erase( src.size()-1 );
883 for_( it, ++src.rbegin(), src.rend() )
884 {
if ( *it ==
'?' ) ++suffx;
else break; }
885 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
887 src.erase( src.size()-suffx );
896 for ( std::string::size_type match = trg.find( src, 0 );
897 match != std::string::npos;
898 match = trg.find( src, match+1 ) )
900 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
902 std::string::size_type frontSize = match + src.size();
903 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
914 const std::string & value( *
_value );
915 return ( isWildchar( *value.begin() )
916 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
933#define WFN_STRICT_SPEC 0
937 static const SetCompare kNeedsCloserLook( SetCompare::Enum(-1) );
938 static const SetCompare matchTabel[4][4] = {{
940 SetCompare::properSuperset,
941 SetCompare::properSuperset,
942 SetCompare::uncomparable,
944 SetCompare::properSubset,
946 SetCompare::disjoint,
947 SetCompare::uncomparable,
949 SetCompare::properSubset,
950 SetCompare::disjoint,
952 SetCompare::uncomparable,
954 SetCompare::properSubset,
955 SetCompare::disjoint,
957 SetCompare::uncomparable,
960 Type srcType = type();
961 Type trgType = trg.type();
962 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
963 if ( ret == kNeedsCloserLook )
965 if ( srcType == Type::wildcardfree )
968 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
970 else if ( srcType == Type::wildcarded )
973 ret = matchWildcardedString( *_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
1003 SetCompare ret = SetCompare::disjoint;
1007 ret = trg.
isANY() ? SetCompare::equal : SetCompare::properSuperset;
1009 else if ( trg.
isANY() )
1011 ret = SetCompare::properSubset;
1015 if ( trg.
isNA() ) ret = SetCompare::equal;
1017 else if ( ! trg.
isNA() )
1025 ret = matchWildcardfreeString( *
_value, *trg.
_value ) ? SetCompare::equal : SetCompare::uncomparable;
1030 if ( matchWildcardedString( *
_value, *trg.
_value ) ) ret = SetCompare::properSuperset;
1038 if ( matchWildcardedString( *trg.
_value, *
_value ) ) ret = SetCompare::properSubset;
1043 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.
std::array< Value, Attribute::numAttributes > Wfn
static Wfn unbindFs(const std::string &cpe_r)
Parse Fs and unbind.
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 ([*?
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 [*?
std::string asUri() const
String representation as in URI (ANY:"", NA:"-")
std::string asWfn() const
String representation as in Well-Formed-Name (ANY:"*", NA:"").
std::string asUri() const
String representation as URI (in/out).
std::ostream & operator<<(std::ostream &str, const CpeId &obj)
Stream output.
static constexpr NoThrowType noThrow
Indicator argument for non-trowing ctor.
base::EnumClass< EAttributeDef > Attribute
'enum class Attribute'
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 SetRelationMixin< CpeId > &src, const SetRelationMixin< CpeId > &trg)
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.