libzypp 17.37.17
CpeId.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
11#include <iostream>
12#include <array>
13
14#include <zypp/base/String.h>
15#include <zypp/base/LogTools.h>
17
18#include <zypp/CpeId.h>
19
20using std::endl;
21
23#define WFN_ATTRIBUTES {\
24 Attribute::part, \
25 Attribute::vendor, \
26 Attribute::product, \
27 Attribute::version, \
28 Attribute::update, \
29 Attribute::edition, \
30 Attribute::language, \
31 Attribute::sw_edition,\
32 Attribute::target_sw, \
33 Attribute::target_hw, \
34 Attribute::other, \
35}
36
38namespace zypp
39{
41 namespace
42 {
44 inline int heDecodeCh( char ch )
45 {
46 if ( '0' <= ch && ch <= '9' )
47 return( ch - '0' );
48 if ( 'A' <= ch && ch <= 'F' )
49 return( ch - 'A' + 10 );
50 if ( 'a' <= ch && ch <= 'f' )
51 return( ch - 'a' + 10 );
52 return -1;
53 }
54
56 inline bool chIsValidRange( char ch )
57 { return( '!' <= ch && ch <= '~' ); }
58
60 inline bool chIsAlpha( char ch )
61 { return( ( 'a' <= ch && ch <= 'z' ) || ( 'A' <= ch && ch <= 'Z' ) ); }
62
64 inline bool chIsNum( char ch )
65 { return( '0' <= ch && ch <= '9' ); }
66
68 inline bool chIsAlNum( char ch )
69 { return( chIsAlpha( ch ) || chIsNum( ch ) ); }
70
72 inline bool chIsWfnUnescaped( char ch )
73 { return( chIsAlNum( ch ) || ch == '_' ); }
74
75 } // namespace
77
78 constexpr CpeId::NoThrowType CpeId::noThrow;
79
85 {
86 using Wfn = std::array<Value, Attribute::numAttributes>;
87
88 public:
89 Impl() {}
90
91 Impl( const std::string & cpe_r )
92 : _wfn( unbind( cpe_r ) )
93 {}
94
95 public:
96 explicit operator bool() const
97 { for ( const auto & val : _wfn ) if ( ! val.isANY() ) return true; return false; }
98
99 std::string asFs() const
100 {
101 str::Str ret;
102 ret << "cpe:2.3";
103 for ( auto ai : WFN_ATTRIBUTES )
104 {
105 ret << ':' << _wfn[ai].asFs();
106 }
107 return ret;
108 }
109
110 std::string asUri() const
111 {
112 str::Str ret;
113 ret << "cpe:/";
114 unsigned colon = 0; // to remember trailing colons
115 for ( auto ai : WFN_ATTRIBUTES )
116 {
117 std::string val = _wfn[ai].asUri();
118
119 if ( ai == Attribute::edition )
120 {
121 if ( ! ( _wfn[Attribute::sw_edition].isANY()
122 && _wfn[Attribute::target_sw].isANY()
123 && _wfn[Attribute::target_hw].isANY()
124 && _wfn[Attribute::other].isANY() ) )
125 {
126 // packing is needed
127 val = str::Str()
128 << '~' << val//Attribute::edition
129 << '~' << _wfn[Attribute::sw_edition].asUri()
130 << '~' << _wfn[Attribute::target_sw].asUri()
131 << '~' << _wfn[Attribute::target_hw].asUri()
132 << '~' << _wfn[Attribute::other].asUri();
133 }
134 }
135
136 if ( ! val.empty() )
137 {
138 if ( colon )
139 ret << std::string( colon, ':' );
140 ret << val;
141 colon = 1;
142 }
143 else
144 ++colon;
145
146 if ( ai == Attribute::language )
147 break; // remaining attrs packaed in edition
148 }
149 return ret;
150 }
151
152 std::string asWfn() const
153 {
154 str::Str ret;
155 ret << "wfn:[";
156 for ( auto ai : WFN_ATTRIBUTES )
157 {
158 const Value & val( _wfn[ai] );
159 if ( ! val.isANY() )
160 {
161 if ( ai ) ret << ',';
162 ret << Attribute::asString( ai ) << '=';
163 if ( val.isString() )
164 ret << '"' << val << '"';
165 else
166 ret << "NA"; // as ANY is omitted, it must be NA
167 }
168 }
169 return ret << "]";
170 }
171
172 public:
173 SetCompare setRelationMixinCompare( const Impl & trg ) const
174 {
175 SetCompare ret = SetCompare::equal;
176 for ( auto ai : WFN_ATTRIBUTES )
177 {
178 switch ( _wfn[ai].compare( trg._wfn[ai] ).asEnum() )
179 {
180 case SetCompare::uncomparable:
181 ret = SetCompare::uncomparable;
182 break;
183
184 case SetCompare::equal:
185 break;
186
187 case SetCompare::properSubset:
188 if ( ret == SetCompare::equal )
189 ret = SetCompare::properSubset;
190 else if ( ret != SetCompare::properSubset )
191 ret = SetCompare::uncomparable;
192 break;
193
194 case SetCompare::properSuperset:
195 if ( ret == SetCompare::equal )
196 ret = SetCompare::properSuperset;
197 else if ( ret != SetCompare::properSuperset )
198 ret = SetCompare::uncomparable;
199 break;
200
201 case SetCompare::disjoint:
202 ret = SetCompare::disjoint;
203 break;
204 }
205 if ( ret == SetCompare::uncomparable || ret == SetCompare::disjoint )
206 break;
207 }
208 return ret;
209 }
210
211 private:
215 static void assignAttr( Wfn & wfn_r, Attribute attr_r, const Value & val_r )
216 {
217 if ( val_r.isString() )
218 {
219 switch ( attr_r.asEnum() )
220 {
221 case Attribute::part:
222 {
223 const std::string & wfn( val_r.asWfn() );
224 switch ( wfn[0] )
225 {
226 case 'h':
227 case 'o':
228 case 'a':
229 if ( wfn[1] == '\0' )
230 break;
231 // else: fallthrough
232 default:
233 throw std::invalid_argument( str::Str() << "CpeId:Wfn:part: '" << wfn << "' illegal value; expected: 'h' | 'o' | 'a'" );
234 break;
235 }
236 }
237 break;
238
239 case Attribute::language:
240 {
241 const std::string & wfn( val_r.asWfn() );
242 std::string::size_type len = 0;
243 // (2*3ALPHA) ["-" (2ALPHA / 3DIGIT)]
244 if ( chIsAlpha( wfn[0] ) && chIsAlpha( wfn[1] ) )
245 {
246 len = chIsAlpha( wfn[2] ) ? 3 : 2;
247 if ( wfn[len] == '-' )
248 {
249 if ( chIsAlpha( wfn[len+1] ) && chIsAlpha( wfn[len+2] ) )
250 len += 3;
251 else if ( chIsNum( wfn[len+1] ) && chIsNum( wfn[len+2] ) && chIsNum( wfn[len+3] ) )
252 len += 4;
253 }
254 }
255 if ( wfn.size() != len )
256 throw std::invalid_argument( str::Str() << "CpeId:Wfn:language: '" << wfn << "' illegal value; expected RFC5646 conform: language ['-' region]" );
257 }
258 break;
259
260 default:
261 // no contraints
262 break;
263 }
264 }
265 wfn_r[attr_r.asIntegral()] = val_r;
266 }
267
268 private:
272 static Wfn unbind( const std::string & cpe_r );
273
277 static Wfn unbindUri( const std::string & cpe_r );
278
282 static Wfn unbindFs( const std::string & cpe_r );
283
284 private:
286 };
287
288 CpeId::Impl::Wfn CpeId::Impl::unbind( const std::string & cpe_r )
289 {
290 Wfn ret;
291 if ( cpe_r[0] == 'c'
292 && cpe_r[1] == 'p'
293 && cpe_r[2] == 'e'
294 && cpe_r[3] == ':' )
295 {
296 if ( cpe_r[4] == '/' )
297 {
298 ret = unbindUri( cpe_r );
299 }
300 else if ( cpe_r[4] == '2'
301 && cpe_r[5] == '.'
302 && cpe_r[6] == '3'
303 && cpe_r[7] == ':' )
304 {
305 ret = unbindFs( cpe_r );
306 }
307 else
308 throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
309 }
310 else if ( cpe_r[0] != '\0' )
311 throw std::invalid_argument( "CpeId: bad magic; expected: 'cpe:2.3:' | 'cpe:/'" );
312 return ret;
313 }
314
315 CpeId::Impl::Wfn CpeId::Impl::unbindUri( const std::string & cpe_r )
316 {
317 Wfn ret;
318
319 static constexpr unsigned numUriAttr = 7u; // basic URI attibutes
320 std::vector<std::string> field;
321 field.reserve( Attribute::numAttributes ); // reserve 7 + 4 for packed extened attrs in edition
322 if ( str::splitFields( cpe_r.c_str()+5/* skip magic 'cpe:/' */, 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 ); // fillup with ANY(""),
325
326 for ( auto ai : WFN_ATTRIBUTES )
327 {
328 if ( ai == Attribute::edition && field[ai][0] == '~' )
329 {
330 // unpacking is needed
331 static constexpr unsigned numPacks = 6u; // dummy_before_~ + edition + 4 extended attributes
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 ); // fillup with ANY(""), should be noOP
337
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] );
343 }
344 assignAttr( ret, ai, Value( field[ai], Value::uriFormat ) );
345 }
346 return ret;
347 }
348
349 CpeId::Impl::Wfn CpeId::Impl::unbindFs( const std::string & cpe_r )
350 {
351 Wfn ret;
352
353 std::vector<std::string> field;
354 field.reserve( Attribute::numAttributes );
355 if ( str::splitFields( cpe_r.c_str()+8/* skip magic 'cpe:2.3:' */, std::back_inserter(field), ":" ) > Attribute::numAttributes )
356 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*/ );
357 if ( !field.empty() && field.back().empty() ) // A trailing ':' leads to an empty (illegal) field, but we fillup missing fields with ANY|"*"
358 field.back() = "*";
359 field.resize( Attribute::numAttributes, "*" ); // fillup with ANY|"*"
360
361 for ( auto ai : WFN_ATTRIBUTES )
362 {
363 assignAttr( ret, ai, Value( field[ai], Value::fsFormat ) );
364 }
365 return ret;
366 }
367
368
370 // class CpeId
372
374
376 : _pimpl( new Impl )
377 {}
378
379 CpeId::CpeId( const std::string & cpe_r )
380 : _pimpl( new Impl( cpe_r ) )
381 {}
382
383 CpeId::CpeId( const std::string & cpe_r, NoThrowType )
384 {
385 try
386 {
387 _pimpl.reset( new Impl( cpe_r ) );
389 }
390 catch(...)
391 {
392 _pimpl.reset( new Impl );
394 }
395 }
396
399
400 CpeId::operator bool() const
401 { return bool(*_pimpl); }
402
403 std::string CpeId::asFs() const
404 { return _pimpl->asFs(); }
405
406 std::string CpeId::asUri() const
407 { return _pimpl->asUri(); }
408
409 std::string CpeId::asWfn() const
410 { return _pimpl->asWfn(); }
411
412 SetCompare CpeId::setRelationMixinCompare( const CpeId & trg ) const
413 { return _pimpl->setRelationMixinCompare( *trg._pimpl ); }
414
416 // class CpeId::WfnAttribute
418
419 const std::string & CpeId::EAttributeDef::asString( Enum val_r )
420 {
421 static std::map<Enum,std::string> _table = {
422#define OUTS(N) { N, #N }
423 OUTS( part ),
424 OUTS( vendor ),
425 OUTS( product ),
426 OUTS( version ),
427 OUTS( update ),
428 OUTS( edition ),
429 OUTS( language ),
430 OUTS( sw_edition ),
431 OUTS( target_sw ),
432 OUTS( target_hw ),
433 OUTS( other ),
434#undef OUTS
435 };
436 return _table[val_r];
437 }
438
440 // class CpeId::Value
442
444 const CpeId::Value CpeId::Value::NA( "" );
445
448
449 CpeId::Value::Value( const std::string & value_r )
450 {
451 if ( value_r.empty() ) // NA
452 {
453 if ( ! CpeId::Value::NA._value ) // initialized by this ctor!
454 _value.reset( new std::string );
455 else
456 _value = CpeId::Value::NA._value;
457 }
458 else if ( value_r != "*" ) // ANY is default constructed
459 {
460 bool starting = true; // false after the 1st non-?
461 for_( chp, value_r.begin(), value_r.end() )
462 {
463 switch ( *chp )
464 {
465 case '\\': // quoted
466 ++chp;
467 if ( ! chIsValidRange( *chp ) )
468 {
469 if ( *chp )
470 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal quoted character '\\" << reinterpret_cast<void*>(*chp) << "'" );
471 else
472 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
473 }
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" );
478 break;
479
480 case '?': // sequence at beginning or end of string
481 while ( *(chp+1) == '?' )
482 ++chp;
483 if ( ! ( starting || chp+1 == value_r.end() ) )
484 throw std::invalid_argument( "CpeId:Wfn: embedded ?" );
485 break;
486
487 case '*': // single at beginning or end of string
488 if ( ! ( starting || chp+1 == value_r.end() ) )
489 throw std::invalid_argument( "CpeId:Wfn: embedded *" );
490 break;
491
492 default: // everything else unquoted
493 if ( ! chIsWfnUnescaped( *chp ) )
494 {
495 if ( chIsValidRange( *chp ) )
496 throw std::invalid_argument( str::Str() << "CpeId:Wfn: missing quote before '" << *chp << "'" );
497 else
498 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
499 }
500 break;
501 }
502 if ( starting )
503 starting = false;
504 }
505 _value.reset( new std::string( value_r ) );
506 }
507 }
508
509 CpeId::Value::Value( const std::string & encoded_r, FsFormatType )
510 {
511 if ( encoded_r != "*" ) // ANY is default constructed
512 {
513 if ( encoded_r == "-" ) // NA
514 {
515 _value = CpeId::Value::NA._value;
516 }
517 else
518 {
519 str::Str result;
520 bool starting = true; // false after the 1st non-?
521 for_( chp, encoded_r.begin(), encoded_r.end() )
522 {
523 switch ( *chp )
524 {
525 case '\\': // may stay quoted
526 ++chp;
527 if ( chIsWfnUnescaped( *chp ) )
528 result << *chp;
529 else if ( chIsValidRange( *chp ) )
530 result << '\\' << *chp;
531 else if ( *chp )
532 throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal quoted character '\\" << *chp << "'" );
533 else
534 throw std::invalid_argument( "CpeId:Fs: Backslash escapes nothing" );
535 break;
536
537 case '?': // sequence at beginning or end of string
538 result << '?';
539 while ( *(chp+1) == '?' )
540 {
541 ++chp;
542 result << '?';
543 }
544 if ( ! ( starting || chp+1 == encoded_r.end() ) )
545 throw std::invalid_argument( "CpeId:Fs: embedded ?" );
546 break;
547
548 case '*': // single at beginning or end of string
549 if ( starting || chp+1 == encoded_r.end() )
550 result << '*';
551 else
552 throw std::invalid_argument( "CpeId:Fs: embedded *" );
553 break;
554
555 default:
556 if ( chIsWfnUnescaped( *chp ) )
557 result << *chp;
558 else if ( chIsValidRange( *chp ) )
559 result << '\\' << *chp;
560 else
561 throw std::invalid_argument( str::Str() << "CpeId:Fs: illegal character '" << reinterpret_cast<void*>(*chp) << "'" );
562 break;
563 }
564 if ( starting )
565 starting = false;
566 }
567 if ( starting )
568 throw std::invalid_argument( "CpeId:Fs: '' value is illegal" );
569 _value.reset( new std::string( result ) );
570 }
571 }
572 }
573
574 CpeId::Value::Value( const std::string & encoded_r, UriFormatType )
575 {
576 if ( ! encoded_r.empty() ) // ANY is default constructed
577 {
578 if ( encoded_r == "-" ) // NA
579 {
580 _value = CpeId::Value::NA._value;
581 }
582 else
583 {
584 str::Str result;
585 bool starting = true; // false after the 1st non-? (%01)
586 for_( chp, encoded_r.begin(), encoded_r.end() )
587 {
588 char ch = *chp;
589
590 if ( ch == '%' ) // legal '%xx' sequence first
591 {
592 int d1 = heDecodeCh( *(chp+1) );
593 if ( d1 != -1 )
594 {
595 int d2 = heDecodeCh( *(chp+2) );
596 if ( d2 != -1 )
597 {
598 chp += 2; // skip sequence
599 if ( d1 == 0 )
600 {
601 if ( d2 == 1 ) // %01 - ? valid sequence at begin or end
602 {
603 result << '?';
604 while ( *(chp+1) == '%' && *(chp+2) == '0' && *(chp+3) == '1' )
605 {
606 chp += 3;
607 result << '?';
608 }
609 if ( starting || chp+1 == encoded_r.end() )
610 {
611 starting = false;
612 continue; // -> continue;
613 }
614 else
615 throw std::invalid_argument( "CpeId:Uri: embedded %01" );
616 }
617 else if ( d2 == 2 ) // %02 - * valid at begin or end
618 {
619 if ( starting || chp+1 == encoded_r.end() )
620 {
621 result << '*';
622 starting = false;
623 continue; // -> continue;
624 }
625 else
626 throw std::invalid_argument( "CpeId:Uri: embedded %02" );
627 }
628 }
629 ch = (d1<<4)|d2;
630 if ( ! chIsValidRange( ch ) )
631 throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal % encoded character '" << reinterpret_cast<void*>(ch) << "'" );
632 }
633 }
634 }
635 else if ( ! chIsValidRange( ch ) )
636 throw std::invalid_argument( str::Str() << "CpeId:Uri: illegal character '" << reinterpret_cast<void*>(ch) << "'" );
637
638 if ( chIsWfnUnescaped( ch ) )
639 result << ch;
640 else
641 result << '\\' << ch;
642
643 if ( starting )
644 starting = false;
645 }
646 _value.reset( new std::string( result ) );
647 }
648 }
649 }
650
651 std::string CpeId::Value::asWfn() const
652 {
653 std::string ret;
654 if ( ! _value )
655 {
656 static const std::string any( "*" );
657 ret = any;
658 }
659 else
660 ret = *_value; // includes "" for NA
661 return ret;
662 }
663
664 std::string CpeId::Value::asFs() const
665 {
666 std::string ret;
667 if ( isANY() )
668 {
669 static const std::string asterisk( "*" );
670 ret = asterisk;
671 }
672 else if ( isNA() )
673 {
674 static const std::string dash( "-" );
675 ret = dash;
676 }
677 else
678 {
679 str::Str result;
680 for_( chp, _value->begin(), _value->end() )
681 {
682 if ( *chp != '\\' )
683 result << *chp;
684 else
685 {
686 ++chp;
687 switch ( *chp )
688 {
689 case '-':
690 case '.':
691 case '_':
692 result << *chp; // without escaping
693 break;
694
695 case '\0':
696 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
697 break;
698
699 default:
700 result << '\\' << *chp;
701 break;
702 }
703 }
704 }
705 ret = result;
706 }
707 return ret;
708 }
709
710 std::string CpeId::Value::asUri() const
711 {
712 std::string ret; // ANY
713 if ( ! isANY() )
714 {
715 if ( isNA() )
716 {
717 static const std::string dash( "-" );
718 ret = dash;
719 }
720 else
721 {
722 str::Str result;
723 for_( chp, _value->begin(), _value->end() )
724 {
725 if ( chIsWfnUnescaped( *chp ) )
726 {
727 result << *chp;
728 }
729 else
730 {
731 static const char *const hdig = "0123456789abcdef";
732 switch ( *chp )
733 {
734 case '\\':
735 ++chp;
736 switch ( *chp )
737 {
738 case '-':
739 case '.':
740 result << *chp; // without encodeing
741 break;
742
743 case '\0':
744 throw std::invalid_argument( "CpeId:Wfn: Backslash escapes nothing" );
745 break;
746
747 default:
748 result << '%' << hdig[(unsigned char)(*chp)/16] << hdig[(unsigned char)(*chp)%16];
749 break;
750 }
751 break;
752
753 case '?':
754 result << "%01";
755 break;
756
757 case '*':
758 result << "%02";
759 break;
760
761 default:
762 throw std::invalid_argument( str::Str() << "CpeId:Wfn: illegal char '" << *chp << "' in WFN" );
763 break;
764 }
765 }
766 }
767 ret = result;
768 }
769 }
770 return ret;
771 }
772
774 namespace
775 {
777 inline bool isWildchar( char ch_r )
778 { return( ch_r == '*' || ch_r == '?' ); }
779
783 inline bool evenNumberOfBackslashes( const std::string::const_reverse_iterator& rbegin_r, const std::string::const_reverse_iterator& rend_r )
784 {
785 unsigned backslashes = 0;
786 for_( it, rbegin_r, rend_r )
787 {
788 if ( *it == '\\' )
789 ++backslashes;
790 else
791 break;
792 }
793 return !(backslashes & 1U);
794 }
795
797 inline unsigned trueCharsIn( const std::string & str_r, std::string::size_type begin_r, std::string::size_type end_r )
798 {
799 unsigned chars = 0;
800 for_( it, begin_r, end_r )
801 {
802 ++chars;
803 if ( str_r[it] == '\\' )
804 {
805 if ( ++it == end_r )
806 break;
807 }
808 }
809 return chars;
810 }
811
813 inline bool matchWildcardfreeString( const std::string & lhs, const std::string & rhs )
814 { return( str::compareCI( lhs, rhs ) == 0 ); }
815
842 inline bool matchWildcardedString( std::string src, std::string trg )
843 {
844 // std::string::npos remembers an asterisk
845 // unescaped wildcard prefix
846 std::string::size_type prefx = 0;
847 switch ( *src.begin() ) // wellformed implies not empty
848 {
849 case '*':
850 if ( src.size() == 1 )
851 return true; // "*" matches always: superset
852 // else
853 prefx = std::string::npos;
854 src.erase( 0, 1 );
855 break;
856 case '?':
857 ++prefx;
858 for_( it, ++src.begin(), src.end() )
859 { if ( *it == '?' ) ++prefx; else break; }
860 if ( src.size() == prefx )
861 return( trg.size() <= prefx ); // "??..?": superset if at most #prefx chars
862 // else
863 src.erase( 0, prefx );
864 break;
865 default:
866 break;
867 }
868 // unescaped wildcard suffix
869 std::string::size_type suffx = 0;
870 if ( ! src.empty() )
871 {
872 switch ( *src.rbegin() )
873 {
874 case '*':
875 if ( evenNumberOfBackslashes( ++src.rbegin(), src.rend() ) )
876 {
877 suffx = std::string::npos;
878 src.erase( src.size()-1 );
879 }
880 break;
881 case '?':
882 ++suffx;
883 for_( it, ++src.rbegin(), src.rend() )
884 { if ( *it == '?' ) ++suffx; else break; }
885 if ( ! evenNumberOfBackslashes( src.rbegin()+suffx, src.rend() ) )
886 --suffx; // last '?' was escaped.
887 src.erase( src.size()-suffx );
888 break;
889 default:
890 break;
891 }
892 }
893 // now match; find src in trg an check surrounding wildcards
894 src = str::toLower( src );
895 trg = str::toLower( trg );
896 for ( std::string::size_type match = trg.find( src, 0 );
897 match != std::string::npos;
898 match = trg.find( src, match+1 ) )
899 {
900 if ( prefx != std::string::npos && trueCharsIn( trg, 0, match ) > prefx )
901 break; // not "*", and already more chars than "?"s before match: disjoint
902 std::string::size_type frontSize = match + src.size();
903 if ( suffx != std::string::npos && trueCharsIn( trg, frontSize, trg.size() ) > suffx )
904 continue; // not "*", and still more chars than "?"s after match: check next match
905 return true; // match: superset
906 }
907 return false; // disjoint
908 }
909 } // namespace
911
913 {
914 const std::string & value( *_value );
915 return ( isWildchar( *value.begin() )
916 || ( isWildchar( *value.rbegin() ) && evenNumberOfBackslashes( ++value.rbegin(), value.rend() ) ) );
917 }
918
933#define WFN_STRICT_SPEC 0
934#if WFN_STRICT_SPEC
935 //SetCompare CpeId::Value::setRelationMixinCompare( const CpeId::Value & trg ) const
936 {
937 static const SetCompare kNeedsCloserLook( SetCompare::Enum(-1) ); // artificial Compare value
938 static const SetCompare matchTabel[4][4] = {{
939 /* ANY, ANY */ SetCompare::equal,
940 /* ANY, NA */ SetCompare::properSuperset,
941 /* ANY, wildcardfree */ SetCompare::properSuperset,
942 /* ANY, wildcarded */ SetCompare::uncomparable,
943 },{
944 /* NA, ANY */ SetCompare::properSubset,
945 /* NA, NA */ SetCompare::equal,
946 /* NA, wildcardfree */ SetCompare::disjoint,
947 /* NA, wildcarded */ SetCompare::uncomparable,
948 },{
949 /* wildcardfree, ANY */ SetCompare::properSubset,
950 /* wildcardfree, NA */ SetCompare::disjoint,
951 /* wildcardfree, wildcardfree */ kNeedsCloserLook, // equal or disjoint
952 /* wildcardfree, wildcarded */ SetCompare::uncomparable,
953 },{
954 /* wildcarded, ANY */ SetCompare::properSubset,
955 /* wildcarded, NA */ SetCompare::disjoint,
956 /* wildcarded, wildcardfree */ kNeedsCloserLook, // superset or disjoint
957 /* wildcarded, wildcarded */ SetCompare::uncomparable,
958 }};
959
960 Type srcType = type();
961 Type trgType = trg.type();
962 SetCompare ret = matchTabel[srcType.asIntegral()][trgType.asIntegral()];
963 if ( ret == kNeedsCloserLook )
964 {
965 if ( srcType == Type::wildcardfree ) // trgType == Type::wildcardfree
966 {
967 // simple string compare
968 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::disjoint;
969 }
970 else if ( srcType == Type::wildcarded ) // trgType == Type::wildcardfree
971 {
972 // Needs wildcard compare
973 ret = matchWildcardedString( *_value, *trg._value ) ? SetCompare::properSuperset : SetCompare::disjoint;
974 }
975 }
976 return ret;
977 }
978#else
980 {
982 // ANY, ANY => equal
983 // ANY, NA => properSuperset
984 // ANY, wildcardfree => properSuperset
985 // ANY, wildcarded => properSuperset
986 //
987 // NA, ANY => properSubset
988 // NA, NA => equal
989 // NA, wildcardfree => disjoint
990 // NA, wildcarded => disjoint
991 //
992 // wildcardfree, ANY => properSubset
993 // wildcardfree, NA => disjoint
994 // wildcardfree, wildcardfree => NeedsCloserLook: equal or disjoint
995 // wildcardfree, wildcarded => NeedsCloserLook: subset or disjoint
996 //
997 // wildcarded, ANY => properSubset
998 // wildcarded, NA => disjoint
999 // wildcarded, wildcardfree => NeedsCloserLook: superset or disjoint
1000 // wildcarded, wildcarded => NeedsCloserLook" equal or uncomparable
1002
1003 SetCompare ret = SetCompare::disjoint;
1004
1005 if ( isANY() )
1006 {
1007 ret = trg.isANY() ? SetCompare::equal : SetCompare::properSuperset;
1008 }
1009 else if ( trg.isANY() )
1010 {
1011 ret = SetCompare::properSubset;
1012 }
1013 else if ( isNA() )
1014 {
1015 if ( trg.isNA() ) ret = SetCompare::equal; // else: SetCompare::disjoint;
1016 }
1017 else if ( ! trg.isNA() ) // else: SetCompare::disjoint;
1018 {
1019 // NeedsCloserLook:
1020 if ( isWildcarded() )
1021 {
1022 if ( trg.isWildcarded() )
1023 {
1024 // simple string compare just to detect 'equal'
1025 ret = matchWildcardfreeString( *_value, *trg._value ) ? SetCompare::equal : SetCompare::uncomparable;
1026 }
1027 else
1028 {
1029 // Needs wildcard compare (src,trg)
1030 if ( matchWildcardedString( *_value, *trg._value ) ) ret = SetCompare::properSuperset; // else: SetCompare::disjoint;
1031 }
1032 }
1033 else
1034 {
1035 if ( trg.isWildcarded() )
1036 {
1037 // Needs wildcard compare (trg,src)
1038 if ( matchWildcardedString( *trg._value, *_value ) ) ret = SetCompare::properSubset; // else: SetCompare::disjoint;
1039 }
1040 else
1041 {
1042 // simple string compare
1043 if ( matchWildcardfreeString( *_value, *trg._value ) ) ret = SetCompare::equal; // else: SetCompare::disjoint;
1044 }
1045 }
1046 }
1047 return ret;
1048 }
1049#endif // WFN_STRICT_SPEC
1050
1051 std::ostream & operator<<( std::ostream & str, const CpeId::Value & obj )
1052 { return str << obj.asString(); }
1053
1054} // namespace zypp
#define WFN_ATTRIBUTES
Initializer list with all wfn attributes.
Definition CpeId.cc:23
#define OUTS(V)
CpeId implementation.
Definition CpeId.cc:85
std::string asUri() const
Definition CpeId.cc:110
Impl(const std::string &cpe_r)
Definition CpeId.cc:91
SetCompare setRelationMixinCompare(const Impl &trg) const
Definition CpeId.cc:173
static void assignAttr(Wfn &wfn_r, Attribute attr_r, const Value &val_r)
Assign val_r if it meets attr_r specific contraints.
Definition CpeId.cc:215
static Wfn unbindUri(const std::string &cpe_r)
Parse Uri and unbind.
Definition CpeId.cc:315
std::string asFs() const
Definition CpeId.cc:99
static Wfn unbind(const std::string &cpe_r)
Parse magic and unbind accordingly.
Definition CpeId.cc:288
std::array< Value, Attribute::numAttributes > Wfn
Definition CpeId.cc:86
static Wfn unbindFs(const std::string &cpe_r)
Parse Fs and unbind.
Definition CpeId.cc:349
std::string asWfn() const
Definition CpeId.cc:152
WFN attribute value.
Definition CpeId.h:160
static const Value ANY
Logical value matching ANY value.
Definition CpeId.h:163
bool isNA() const
Whether value is NA.
Definition CpeId.h:231
static const Value NA
Logical value indicating “not applicable/not used".
Definition CpeId.h:166
bool isWildcarded() const
An attribute value string with wildcards ([*?
Definition CpeId.h:253
RWCOW_pointer< std::string > _value
Definition CpeId.h:293
bool isANY() const
Whether value is ANY.
Definition CpeId.h:227
static constexpr UriFormatType uriFormat
Indicator argument for ctor arg in URI format.
Definition CpeId.h:177
static constexpr FsFormatType fsFormat
Indicator argument for ctor arg in FS format.
Definition CpeId.h:172
bool isString() const
Whether it's an attribute value string (not logical value).
Definition CpeId.h:242
std::string asString() const
Default string representation [asWfn].
Definition CpeId.h:258
std::string asFs() const
String representation as in Formated-String (ANY:"*", NA:"-")
Definition CpeId.cc:664
SetCompare setRelationMixinCompare(const Value &trg) const
CPE name matching hook for SetRelationMixin.
Definition CpeId.cc:979
Value()
Default ctor: ANY.
Definition CpeId.h:181
bool containsWildcard() const
HAs unquoted [*?
Definition CpeId.cc:912
std::string asUri() const
String representation as in URI (ANY:"", NA:"-")
Definition CpeId.cc:710
std::string asWfn() const
String representation as in Well-Formed-Name (ANY:"*", NA:"").
Definition CpeId.cc:651
std::string asUri() const
String representation as URI (in/out).
Definition CpeId.cc:406
~CpeId()
Dtor.
Definition CpeId.cc:397
std::ostream & operator<<(std::ostream &str, const CpeId &obj)
Stream output.
Definition CpeId.h:133
static constexpr NoThrowType noThrow
Indicator argument for non-trowing ctor.
Definition CpeId.h:63
base::EnumClass< EAttributeDef > Attribute
'enum class Attribute'
Definition CpeId.h:57
std::string asWfn() const
String representation as Well-Formed-Name (internal format, out only).
Definition CpeId.cc:409
std::string asFs() const
String representation as Formated-String (in/out).
Definition CpeId.cc:403
SetCompare setRelationMixinCompare(const CpeId &trg) const
CPE name matching hook for SetRelationMixin.
Definition CpeId.cc:412
CpeId()
Default ctor: ANY-Cpeid, all attribute values are ANY.
Definition CpeId.cc:375
RWCOW_pointer< Impl > _pimpl
Implementation class.
Definition CpeId.h:126
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.
Definition NonCopyable.h:26
unsigned splitFields(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=":")
Split line_r into fields.
Definition String.h:795
std::string toLower(const std::string &s)
Return lowercase version of s.
Definition String.cc:180
int compareCI(const C_Str &lhs, const C_Str &rhs)
Definition String.h:1054
Easy-to use interface to the ZYPP dependency resolver.
static const std::string & asString(Enum val_r)
string representantion
Definition CpeId.cc:419
Indicator type for non-trowing ctor.
Definition CpeId.h:61
static std::string lastMalformed
Definition CpeId.h:61
Indicator type for ctor arg in FS format.
Definition CpeId.h:170
Indicator type for ctor arg in URI format.
Definition CpeId.h:175
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:213
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:27