Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / CpeId.cc
index a1c90df..776b8c5 100644 (file)
@@ -9,6 +9,7 @@
 /** \file      zypp/CpeId.cc
  */
 #include <iostream>
+#include <array>
 
 #include "zypp/base/String.h"
 #include "zypp/base/LogTools.h"
@@ -228,7 +229,7 @@ namespace zypp
                    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;
              }
            }
@@ -251,7 +252,7 @@ namespace zypp
                }
              }
              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;
 
@@ -303,10 +304,10 @@ namespace zypp
        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;
   }
 
@@ -314,11 +315,12 @@ namespace zypp
   {
     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 )
     {
@@ -329,7 +331,7 @@ namespace zypp
        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] );
@@ -350,7 +352,9 @@ namespace zypp
     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 )
@@ -365,6 +369,8 @@ namespace zypp
   //   class CpeId
   ///////////////////////////////////////////////////////////////////
 
+  std::string CpeId::NoThrowType::lastMalformed;
+
   CpeId::CpeId()
     : _pimpl( new Impl )
   {}
@@ -376,9 +382,15 @@ namespace zypp
   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()
@@ -451,14 +463,14 @@ namespace zypp
            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
@@ -477,9 +489,9 @@ namespace zypp
            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;
        }
@@ -513,7 +525,7 @@ namespace zypp
              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;
@@ -542,14 +554,14 @@ namespace zypp
              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 ) );
       }
     }
@@ -612,12 +624,12 @@ namespace zypp
                }
                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;
@@ -900,7 +912,23 @@ namespace zypp
        || ( 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] = {{
@@ -943,6 +971,78 @@ namespace zypp
     }
     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(); }