Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / base / String.h
index e331216..285c7dd 100644 (file)
 #ifndef ZYPP_BASE_STRING_H
 #define ZYPP_BASE_STRING_H
 
+#include <cstring>
+
 #include <iosfwd>
+#include <vector>
 #include <string>
-#include <boost/regex.hpp>
+#include <sstream>
+#include <boost/format.hpp>
+#include <boost/utility/string_ref.hpp>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/Function.h"
+
+///////////////////////////////////////////////////////////////////
+namespace boost { namespace logic { class tribool; } }
+namespace zypp { typedef boost::logic::tribool TriBool; }
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  /** Request a human readable (translated) string representation of Tp [Tp.asUserString()]
+   * Classes may implement a default as member function.
+   */
+  template <class Tp>
+  std::string asUserString( const Tp & val_r )
+  { return val_r.asUserString(); }
+
+}// namespace zypp
+///////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
+
+  /** Convenience \c char* constructible from \c std::string and \c char*,
+   *  it maps \c (char*)0 to an empty string.
+   *
+   * \code
+   * bool hasPrefix( const std::string & str_r, const std::string & prefix_r )
+   * { return( ::strncmp( str_r.c_str(), prefix_r.c_str(), prefix_r.size() ) == 0 ); }
+   * \endcode
+   *
+   * Called with a plain \c char* as argument, the \c std::string is created form
+   * for nothing. The implementation actually does not use the \c std::string.
+   *
+   * Best would be to implement \c hasPrefix for each combination of \c char*
+   * and \c std::string arguments:
+   *
+   * \code
+   * bool hasPrefix( const std::string & str_r, const std::string & prefix_r )
+   * { return( ::strncmp( str_r.c_str(), prefix_r.c_str(), prefix_r.size() ) == 0 ); }
+   *
+   * bool hasPrefix( const std::string & str_r, const char * prefix_r )
+   * { return( !prefix_r || ::strncmp( str_r.c_str(), prefix_r, ::strlen(prefix_r) ) == 0 ); }
+   *
+   * bool hasPrefix( const char * str_r, const std::string & prefix_r )
+   * { return( str_r ? ::strncmp( str_r, prefix_r.c_str(), prefix_r.size() ) == 0 : prefix_r.empty() ); }
+   *
+   * bool hasPrefix( const char * str_r, const char * prefix_r )
+   * { return( str && prefix_r ? ::strncmp( str_r, prefix_r, ::strlen(prefix_r) ) == 0
+   *                           : !((str_r && *str_r) || (prefix_r && *prefix_r)); }
+   * \endcode
+   *
+   * This is where \ref C_Str can help. Constructible from \c std::string and \c char*,
+   * it \e reduces the \c std::string to it's \c char*. At the same time it converts
+   * \c (char*)0 into an \c "" string.
+   *
+   * \code
+   * bool hasPrefix( const C_Str & str_r, const C_Str & prefix_r )
+   * { return( ::strncmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
+   * \endcode
+   *
+   * \todo Check whether to replace by boost::string_ref
+   */
+  class C_Str
+  {
+    public:
+      typedef std::string::size_type size_type;
+
+    public:
+      C_Str()                            : _val( 0 ),             _sze( 0 ) {}
+      C_Str( char * c_str_r )            : _val( c_str_r ),       _sze( std::string::npos ) {}
+      C_Str( const char * c_str_r )      : _val( c_str_r ),       _sze( std::string::npos ) {}
+      C_Str( const std::string & str_r ) : _val( str_r.c_str() ), _sze( str_r.size() ) {}
+      C_Str( const boost::string_ref & str_r ) : _val( str_r.data() ), _sze( str_r.size() ) {}
+
+    public:
+      bool      isNull()       const { return !_val; }
+      bool      empty()        const { return !(_val && *_val); }
+      size_type size()         const
+      {
+        if ( _sze == std::string::npos )
+        { _sze = _val ? ::strlen( _val ) : 0; }
+        return _sze;
+      };
+
+      operator const char *() const { return c_str(); }
+      const char * c_str()    const { return _val ? _val : ""; }
+
+    private:
+      const char *const _val;
+      mutable size_type _sze;
+  };
+
+  /** \relates C_Str Stream output */
+  inline std::ostream & operator<<( std::ostream & str, const C_Str & obj )
+  { return str << obj.c_str(); }
+
   ///////////////////////////////////////////////////////////////////
   /** String related utilities and \ref ZYPP_STR_REGEX.
    \see \ref ZYPP_STR_REGEX
   */
+
   namespace str
   { /////////////////////////////////////////////////////////////////
 
     ///////////////////////////////////////////////////////////////////
+    /**
+     * Global asString() that works with std::string too
+     */
+    inline const std::string & asString( const std::string & t )
+    { return t; }
+
+#ifndef SWIG // Swig treats it as syntax error
+    inline std::string && asString( std::string && t )
+    { return std::move(t); }
+#endif
+
+    inline std::string asString( const char * t )
+    { return t; }
+
+    inline std::string asString( char * t )
+    { return t; }
+
+    template<class Tp>
+        inline std::string asString( const Tp &t )
+        { return t.asString(); }
+
+    template<class Tp>
+        inline std::string asString( const intrusive_ptr<Tp> &p )
+        { return p->asString(); }
+
+    template<class Tp>
+        inline std::string asString( const weak_ptr<Tp> &p )
+        { return p->asString(); }
+
+    template<>
+        inline std::string asString( const bool &t )
+        { return t ? "true" : "false"; }
+
+    ///////////////////////////////////////////////////////////////////
     /** Printf style construction of std::string. */
     std::string form( const char * format, ... )
     __attribute__ ((format (printf, 1, 2)));
@@ -59,38 +196,99 @@ namespace zypp
     };
 
     ///////////////////////////////////////////////////////////////////
-    /** \defgroup ZYPP_STR_REGEX Regular expressions
-     *
-     * Namespace zypp::str regular expressions \b using the
-     * boost regex library
-     * \url http://www.boost.org/libs/regex/doc/index.html.
-     *
-     * \li \c regex
-     * \li \c regex_match
-     * \li \c regex_search
-     * \li \c regex_replace
-     * \li \c match_results
-     * \li \c cmatch
-     * \li \c wcmatch
-     * \li \c smatch
-     * \li \c wsmatch
-    */
+    /// \class Str
+    /// \brief Convenient building of std::string via \ref std::ostringstream
+    /// Basically a \ref std::ostringstream autoconvertible to \ref std::string
+    /// for building string arguments.
+    /// \code
+    ///   void fnc( const std::string & txt_r );
+    ///   fnc( str::Str() << "Hello " << 13 );
+    ///
+    ///   std::string txt( str::Str() << 45 );
+    /// \endcode
+    ///////////////////////////////////////////////////////////////////
+    struct Str
+    {
+      template<class Tp>
+      Str & operator<<( Tp && val )
+      { _str << std::forward<Tp>(val); return *this; }
 
-    //@{
-    /** regex */
-    using boost::regex;
-    using boost::regex_match;
-    using boost::regex_search;
-    using boost::regex_replace;
-    using boost::match_results;
-    using boost::match_extra;
-    using boost::cmatch;
-    using boost::wcmatch;
-    using boost::smatch;
-    using boost::wsmatch;
-    //@}
+      Str & operator<<( std::ostream& (*iomanip)( std::ostream& ) )
+      { _str << iomanip; return *this; }
+
+      operator std::string() const             { return _str.str(); }
+      std::string asString() const             { return _str.str(); }
+      std::string str() const                  { return _str.str(); }
+
+      const std::ostream & stream() const      { return _str; }
+      std::ostream & stream()                  { return _str; }
+
+      void clear()                             { _str.str( std::string() ); }
+
+    private:
+      std::ostringstream _str;
+    };
+
+    /** \relates Str Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Str & obj )
+    { return str << obj.str(); }
 
     ///////////////////////////////////////////////////////////////////
+    /// \class Format
+    /// \brief Convenient building of std::string with \ref boost::format.
+    /// Basically a \ref boost::format autoconvertible to \ref std::string
+    /// for building string arguments.
+    /// \code
+    ///   void fnc( const std::string & txt_r );
+    ///   fnc( str::Format("Hello %1%") % 13 );
+    ///
+    ///   std::string txt( str::Format("Hello %1%") % 13 );
+    /// \endcode
+    ///////////////////////////////////////////////////////////////////
+    struct Format
+    {
+      Format() {}
+      Format( const std::string & format_r ) : _fmter( format_r ) {}
+
+      template<class Tp>
+      Format & operator%( Tp && arg )
+      { _fmter % std::forward<Tp>(arg); return *this; }
+
+      operator std::string() const             { return _fmter.str(); }
+      std::string asString() const             { return _fmter.str(); }
+      std::string str() const                  { return _fmter.str(); }
+
+      const boost::format & fmter() const      { return _fmter; }
+      boost::format & fmter()                  { return _fmter; }
+
+    protected:
+      boost::format _fmter;
+    };
+
+    /** \relates Format Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Format & obj )
+    { return str << obj.fmter(); }
+
+    ///////////////////////////////////////////////////////////////////
+    /// \class FormatNAC
+    /// \brief \ref Format with (N)o (A)rgument (C)heck.
+    /// It won't complain about missing or excess arguments. Sometimes
+    /// usefull when dealing with translations or classes providing a
+    /// default formater.
+    ///////////////////////////////////////////////////////////////////
+    struct FormatNAC : public Format
+    {
+      FormatNAC() { relax(); }
+      FormatNAC( const std::string & format_r ) : Format( format_r ) { relax(); }
+
+    private:
+      void relax()
+      {
+       using namespace boost::io;
+       _fmter.exceptions( all_error_bits ^ ( too_many_args_bit | too_few_args_bit ) );
+      }
+    };
+    ///////////////////////////////////////////////////////////////////
     /** \name String representation of number.
      *
      * Optional second argument sets the minimal string width (' ' padded).
@@ -114,6 +312,17 @@ namespace zypp
     inline std::string numstring( unsigned long n,      int w = 0 ) { return form( "%*lu",   w, n ); }
     inline std::string numstring( long long n,          int w = 0 ) { return form( "%*lld",  w, n ); }
     inline std::string numstring( unsigned long long n, int w = 0 ) { return form( "%*llu",  w, n ); }
+
+    template<> inline std::string asString( const char & t )                   { return numstring( t ); }
+    template<> inline std::string asString( const unsigned char & t )          { return numstring( t ); }
+    template<> inline std::string asString( const short & t )                  { return numstring( t ); }
+    template<> inline std::string asString( const unsigned short & t )         { return numstring( t ); }
+    template<> inline std::string asString( const int & t )                    { return numstring( t ); }
+    template<> inline std::string asString( const unsigned & t )               { return numstring( t ); }
+    template<> inline std::string asString( const long & t )                   { return numstring( t ); }
+    template<> inline std::string asString( const unsigned long & t )          { return numstring( t ); }
+    template<> inline std::string asString( const long long & t )              { return numstring( t ); }
+    template<> inline std::string asString( const unsigned long long & t )     { return numstring( t ); }
     //@}
 
     ///////////////////////////////////////////////////////////////////
@@ -164,6 +373,20 @@ namespace zypp
     inline std::string octstring( unsigned long long n, int w = 0 ) { return form( "%#0*llo",  w, n ); }
     //@}
 
+
+    ///////////////////////////////////////////////////////////////////
+    /** String representation of number as bit-string with leading '0's. */
+    template <typename TInt>
+    std::string binstring( TInt val_r )
+    {
+      constexpr unsigned bits = sizeof(TInt)*8;
+      std::string ret( bits, ' ' );
+      TInt bit = 1;
+      for ( unsigned pos = bits; pos > 0; )
+      { --pos; ret[pos] = ((val_r & bit)?'1':'0'); bit = bit<<1; }
+      return ret;
+    }
+    
     ///////////////////////////////////////////////////////////////////
     /** Parsing numbers from string.
     */
@@ -174,38 +397,113 @@ namespace zypp
      * time_t t = strtonum<time_t>( "42" );
      * \endcode
     */
-    template<typename _It>
-      inline _It strtonum( const std::string & str );
+    template<typename TInt>
+      TInt strtonum( const C_Str & str );
 
     template<>
-      inline short              strtonum( const std::string & str ) { return ::strtol  ( str.c_str(), NULL, 0 ); }
+      inline short              strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
     template<>
-      inline int                strtonum( const std::string & str ) { return ::strtol  ( str.c_str(), NULL, 0 ); }
+      inline int                strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
     template<>
-      inline long               strtonum( const std::string & str ) { return ::strtol  ( str.c_str(), NULL, 0 ); }
+      inline long               strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
     template<>
-      inline long long          strtonum( const std::string & str ) { return ::strtoll ( str.c_str(), NULL, 0 ); }
+      inline long long          strtonum( const C_Str & str ) { return ::strtoll ( str, NULL, 0 ); }
 
     template<>
-      inline unsigned short     strtonum( const std::string & str ) { return ::strtoul ( str.c_str(), NULL, 0 ); }
+      inline unsigned short     strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
     template<>
-      inline unsigned           strtonum( const std::string & str ) { return ::strtoul ( str.c_str(), NULL, 0 ); }
+      inline unsigned           strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
     template<>
-      inline unsigned long      strtonum( const std::string & str ) { return ::strtoul ( str.c_str(), NULL, 0 ); }
+      inline unsigned long      strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
     template<>
-      inline unsigned long long strtonum( const std::string & str ) { return ::strtoull( str.c_str(), NULL, 0 ); }
+      inline unsigned long long strtonum( const C_Str & str ) { return ::strtoull( str, NULL, 0 ); }
 
     /** String to integer type detemined 2nd function arg \a i.
      * \code
      * time_t t; strtonum( "42", t );
      * \endcode
     */
-    template<typename _It>
-      inline _It strtonum( const std::string & str, _It & i )
-      { return i = strtonum<_It>( str ); }
+    template<typename TInt>
+      inline TInt strtonum( const C_Str & str, TInt & i )
+      { return i = strtonum<TInt>( str ); }
     //@}
 
     ///////////////////////////////////////////////////////////////////
+    /** Parsing boolean from string.
+    */
+    //@{
+    /** Return \c true if str is <tt>1, true, yes, on</tt> (or a nonzero number). */
+    bool strToTrue( const C_Str & str );
+
+    /** Return \c false if str is <tt>0, false, no, off</tt>. */
+    bool strToFalse( const C_Str & str );
+
+    /** Parse \c str into a bool depending on the default value.
+     * If the \c default is true, look for a legal \c false string.
+     * If the \c default is false, look for a legal \c true string.
+     */
+    inline bool strToBool( const C_Str & str, bool default_r )
+    { return( default_r ? strToFalse( str ) : strToTrue( str ) ); }
+
+    /** Parse \c str into a bool if it's a legal \c true or \c false string.
+     * If \c str is not a recognized \c true or \c false string, \a return_r
+     * is left unchanged.
+     */
+    inline bool strToBoolNodefault( const C_Str & str, bool & return_r )
+    {
+      if ( strToTrue( str ) ) return (return_r = true);
+      if ( !strToFalse( str ) ) return (return_r = false);
+      return return_r;
+    }
+
+    /** Parse \c str into a bool if it's a legal \c true or \c false string; else \c indterminate. */
+    TriBool strToTriBool( const C_Str & str );
+
+    //@}
+
+    /**
+     * \short Return a string with all occurrences of \c from_r replaced with \c to_r.
+     */
+    std::string gsub( const std::string & str_r, const std::string & from_r, const std::string & to_r );
+
+    /** \overload A function is called on demand to compute each replacement value.
+     */
+    std::string gsubFun( const std::string & str_r, const std::string & from_r, function<std::string()> to_r );
+
+    /**
+     * \short Replace all occurrences of \c from_r with \c to_r in \c str_r (inplace).
+     * A reference to \c str_r is also returned for convenience.
+     */
+    std::string & replaceAll( std::string & str_r, const std::string & from_r, const std::string & to_r );
+
+    /** \overload A function is called on demand to compute each replacement value.
+     */
+    std::string & replaceAllFun( std::string & str_r, const std::string & from_r, function<std::string()> to_r );
+
+    /** Enhance readability: insert gaps at regular distance
+     * \code
+     *   // no gaps
+     *   Key Fingerprint:  22C07BA534178CD02EFE22AAB88B2FD43DBDC284
+     *   // gapify 8
+     *   Key Fingerprint:  22C07BA5 34178CD0 2EFE22AA B88B2FD4 3DBDC284
+     *   // gapify 4
+     *   Key Fingerprint:  22C0 7BA5 3417 8CD0 2EFE 22AA B88B 2FD4 3DBD C284
+     *   // gapify 4, '-'
+     *   Key Fingerprint:  22C0-7BA5-3417-8CD0-2EFE-22AA-B88B-2FD4-3DBD-C284
+     * \endcode
+     */
+    inline std::string gapify( std::string inp_r, std::string::size_type gap_r = 1, char gapchar = ' ' )
+    {
+      if ( gap_r &&  inp_r.size() > gap_r )
+      {
+       inp_r.reserve( inp_r.size() + (inp_r.size()-1)/gap_r );
+       for ( std::string::size_type pos = gap_r; pos < inp_r.size(); pos += gap_r+1 )
+         inp_r.insert( pos, 1, gapchar );
+      }
+      return inp_r;
+    }
+
+    ///////////////////////////////////////////////////////////////////
     /** \name Split. */
     //@{
     /** Split \a line_r into words.
@@ -217,70 +515,458 @@ namespace zypp
      * \endcode
      *
     */
-    template<class _OutputIterator>
-      unsigned split( const std::string & line_r,
-                      _OutputIterator     result_r,
-                      const std::string & sepchars_r = " \t" )
+    template<class TOutputIterator>
+      unsigned split( const C_Str & line_r, TOutputIterator result_r, const C_Str & sepchars_r = " \t" )
       {
-        const char * beg = line_r.c_str();
+        const char * beg = line_r;
         const char * cur = beg;
         // skip leading sepchars
-        while ( sepchars_r.find( *cur ) != std::string::npos )
+        while ( *cur && ::strchr( sepchars_r, *cur ) )
           ++cur;
         unsigned ret = 0;
         for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
           {
             // skip non sepchars
-            while( *cur && sepchars_r.find( *cur ) == std::string::npos )
+            while( *cur && !::strchr( sepchars_r, *cur ) )
               ++cur;
             // build string
             *result_r = std::string( beg, cur-beg );
             // skip sepchars
-            while ( sepchars_r.find( *cur ) != std::string::npos )
+            while ( *cur && ::strchr( sepchars_r, *cur ) )
+              ++cur;
+          }
+        return ret;
+      }
+
+    /** Split \a line_r into words with respect to escape delimeters.
+     * Any sequence of characters in \a sepchars_r is treated as
+     * delimiter if not inside \c "" or \c '' or escaped by \c \.
+     *
+     * \li A non-quoted backslash (\) preserves the literal value of the next character.
+     * \li Enclosing characters in single quotes preserves the literal value of each
+     *     character within the quotes.  A single quote may not occur between single
+     *     quotes, even when preceded by a backslash.
+     * \li Enclosing characters in double quotes preserves the literal value of all
+     *     characters within the quotes, with the exception of \c \. The backslash
+     *     retains its special meaning only when followed by \c " or \c \.
+     *
+     * The words are passed to OutputIterator \a result_r.
+     *
+     * \see \ref splitEscaped
+     *
+     * \code
+     * std::vector<std::string> words;
+     * str::splitEscaped( "some line", std::back_inserter(words) )
+     * \endcode
+     *
+     * \code
+     * example splitted strings
+     * normal line -> 2 elements ( "normal", "line" )
+     * escaped\ line -> 1 element(escaped line)
+     * "quoted line" -> 1 element same as above
+     * 'quoted line' -> 1 element same as above
+     * "escaped quote\"" -> 1 element (escaped quote")
+     *
+     * \param line_r   The string to parse.
+     * \param result_r
+     * \param sepchars_r  String of separator characters.
+     * \param withEmpty   Whether to include empty fields between separators in the result.
+     *
+     * \endcode
+     */
+    template<class TOutputIterator>
+      unsigned splitEscaped( const C_Str & line_r, TOutputIterator result_r, const C_Str & sepchars_r = " \t", bool withEmpty = false)
+      {
+        const char * beg = line_r;
+        const char * cur = beg;
+        unsigned ret = 0;
+
+        // skip leading sepchars
+        while ( *cur && ::strchr( sepchars_r, *cur ) )
+        {
+          ++cur;
+          if (withEmpty)
+          {
+            *result_r = "";
+            ++ret;
+          }
+        }
+
+        // there were only sepchars in the string
+        if (!*cur && withEmpty)
+        {
+          *result_r = "";
+          return ++ret;
+        }
+
+        // after the leading sepchars
+       enum class Quote { None, Slash, Single, Double, DoubleSlash };
+       std::vector<char> buf;
+       Quote quoting = Quote::None;
+        for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
+       {
+         // read next value until unquoted sepchar
+         buf.clear();
+         quoting = Quote::None;
+         do {
+           switch ( quoting )
+           {
+             case Quote::None:
+               switch ( *cur )
+               {
+                 case '\\':    quoting = Quote::Slash;         break;
+                 case '\'':    quoting = Quote::Single;        break;
+                 case '"':     quoting = Quote::Double;        break;
+                 default:      buf.push_back( *cur );          break;
+               }
+               break;
+
+             case Quote::Slash:
+               buf.push_back( *cur );
+               quoting = Quote::None;
+               break;
+
+             case Quote::Single:
+               switch ( *cur )
+               {
+                 case '\'':    quoting = Quote::None;          break;
+                 default:      buf.push_back( *cur );          break;
+               }
+               break;
+
+             case Quote::Double:
+               switch ( *cur )
+               {
+                 case '\"':    quoting = Quote::None;          break;
+                 case '\\':    quoting = Quote::DoubleSlash;   break;
+                 default:      buf.push_back( *cur );          break;
+               }
+               break;
+
+             case Quote::DoubleSlash:
+               switch ( *cur )
+               {
+                 case '\"':    /*fallthrough*/
+                 case '\\':    buf.push_back( *cur );          break;
+                 default:
+                   buf.push_back( '\\' );
+                   buf.push_back( *cur );
+                   break;
+               }
+               quoting = Quote::Double;
+               break;
+           }
+           ++cur;
+         } while ( *cur && ( quoting != Quote::None || !::strchr( sepchars_r, *cur ) ) );
+         *result_r = std::string( buf.begin(), buf.end() );
+
+
+         // skip sepchars
+         if ( *cur && ::strchr( sepchars_r, *cur ) )
+           ++cur;
+         while ( *cur && ::strchr( sepchars_r, *cur ) )
+         {
+           ++cur;
+           if (withEmpty)
+           {
+             *result_r = "";
+             ++ret;
+           }
+         }
+         // the last was a separator => one more field
+         if ( !*cur && withEmpty && ::strchr( sepchars_r, *(cur-1) ) )
+         {
+           *result_r = "";
+           ++ret;
+         }
+       }
+        return ret;
+      }
+
+    /** Split \a line_r into fields.
+     * Any single character in \a sepchars_r is treated as a
+     * field separator unless \-escaped. The words are passed
+     * to OutputIterator.
+     * \a result_r.
+     * \code
+     * ""        -> words 0
+     * ":"       -> words 2  |||
+     * "a"       -> words 1  |a|
+     * ":a"      -> words 2  ||a|
+     * "a:"      -> words 2  |a||
+     * ":a:"     -> words 3  ||a||
+     *
+     * \endcode
+     *
+     * \code
+     * std::vector<std::string> words;
+     * str::split( "some line", std::back_inserter(words) )
+     * \endcode
+     *
+    */
+    template<class TOutputIterator>
+      unsigned splitFields( const C_Str & line_r, TOutputIterator result_r, const C_Str & sepchars_r = ":" )
+      {
+        const char * beg = line_r;
+        const char * cur = beg;
+        unsigned ret = 0;
+        for ( beg = cur; *beg; beg = cur, ++result_r )
+          {
+            // skip non sepchars
+            while( *cur && !::strchr( sepchars_r, *cur ) )
+           {
+             if ( *cur == '\\' && *(cur+1) )
+               ++cur;
+              ++cur;
+           }
+            // build string
+            *result_r = std::string( beg, cur-beg );
+            ++ret;
+            // skip sepchar
+            if ( *cur )
+            {
               ++cur;
+              if ( ! *cur )                // ending with sepchar
+              {
+                *result_r = std::string(); // add final empty field
+                ++ret;
+                break;
+              }
+            }
           }
         return ret;
       }
+
+    /**
+     * Split \a line_r into fields handling also escaped separators.
+     *
+     * \see splitFields()
+     * \see splitEscaped()
+     */
+    template<class TOutputIterator>
+      unsigned splitFieldsEscaped( const C_Str & line_r, TOutputIterator result_r, const C_Str & sepchars_r = ":" )
+      {
+        return splitEscaped( line_r, result_r, sepchars_r, true /* withEmpty */ );
+      }
+
     //@}
 
     ///////////////////////////////////////////////////////////////////
     /** \name Join. */
     //@{
     /** Join strings using separator \a sep_r (defaults to BLANK). */
-    template <class _Iterator>
-      std::string join( _Iterator begin, _Iterator end,
-                        const std::string & sep_r = " " )
+    template <class TIterator>
+      std::string join( TIterator begin, TIterator end, const C_Str & sep_r = " " )
       {
         std::string res;
-        for ( _Iterator iter = begin; iter != end; ++ iter )
+        for ( TIterator iter = begin; iter != end; ++ iter )
           {
             if ( iter != begin )
               res += sep_r;
-            res += *iter;
+            res += asString(*iter);
           }
         return res;
       }
 
     /** Join strings using separator \a sep_r (defaults to BLANK). */
-    template <class _Container>
-      std::string join( const _Container & cont_r,
-                        const std::string & sep_r = " " )
+    template <class TContainer>
+      std::string join( const TContainer & cont_r, const C_Str & sep_r = " " )
       { return join( cont_r.begin(), cont_r.end(), sep_r ); }
+
+    /** Join strings using separator \a sep_r, quoting or escaping the values.
+     * Separator defaults to BLANK. Use \ref splitEscaped to restore the
+     * values.
+     */
+    template <class TIterator>
+      std::string joinEscaped( TIterator begin, TIterator end, const char sep_r = ' ' )
+      {
+        std::vector<char> buf;
+        for ( TIterator iter = begin; iter != end; ++ iter )
+        {
+          if ( iter != begin )
+            buf.push_back( sep_r );
+
+          if ( iter->empty() )
+          {
+            // empty string goes ""
+            buf.push_back( '"' );
+            buf.push_back( '"' );
+          }
+          else
+          {
+            std::string toadd( asString(*iter) );
+            for_( ch, toadd.begin(), toadd.end() )
+            {
+              switch ( *ch )
+              {
+                case '"':
+                case '\'':
+                case '\\':
+                  buf.push_back( '\\' );
+                  buf.push_back( *ch );
+                  break;
+                default:
+                  if ( *ch == sep_r )
+                    buf.push_back( '\\' );
+                  buf.push_back( *ch );
+              }
+            }
+          }
+        }
+        return std::string( buf.begin(), buf.end() );
+      }
     //@}
 
 
     ///////////////////////////////////////////////////////////////////
+    /** \name Indent. */
+    //@{
+      /** Indent by string ["  "] optionally wrap.
+       * Prints nothing for an empty string. Asserts a trainling '\n' on
+       * the last line. Optionally wrap lines at ' ' at a given length.
+       */
+      inline std::ostream & printIndented( std::ostream & str, const std::string & text_r, const std::string & indent_r = "  ", unsigned maxWitdh_r = 0 )
+      {
+       if ( maxWitdh_r )
+       {
+         if ( indent_r.size() >= maxWitdh_r )
+           maxWitdh_r = 0;     // nonsense: indent larger than line witdh
+         else
+           maxWitdh_r -= indent_r.size();
+       }
+       unsigned width = 0;
+       for ( const char * e = text_r.c_str(), * s = e; *e; s = ++e )
+       {
+         for ( ; *e && *e != '\n'; ++e ) ;/*searching*/
+         width = e-s;
+         if ( maxWitdh_r && width > maxWitdh_r )
+         {
+           // must break line
+           width = maxWitdh_r;
+           for ( e = s+width; e > s && *e != ' '; --e ) ;/*searching*/
+           if ( e > s )
+             width = e-s;      // on a ' ', replaced by '\n'
+           else
+             e = s+width-1;    // cut line;
+         }
+         str << indent_r;
+         str.write( s, width );
+         str << "\n";
+         if ( !*e )    // on '\0'
+           break;
+       }
+       return str;
+      }
+      /** \overload Indent by number of chars [' '] optionally wrap. */
+      inline std::ostream & printIndented( std::ostream & str, const std::string & text_r, unsigned indent_r, char indentch_r = ' ', unsigned maxWitdh_r = 0 )
+      { return printIndented( str, text_r, std::string( indent_r, indentch_r ), maxWitdh_r ); }
+      /** \overload Indent by number of chars [' '] wrap. */
+      inline std::ostream & printIndented( std::ostream & str, const std::string & text_r, unsigned indent_r, unsigned maxWitdh_r, char indentch_r = ' ' )
+      { return printIndented( str, text_r, std::string( indent_r, indentch_r ), maxWitdh_r ); }
+
+      /** Prefix lines by string computed by function taking line begin/end [std::string(const char*, const char*)]
+       * Prints nothing for an empty string. Asserts a trainling '\n' on the last line.
+       */
+      inline std::ostream & autoPrefix( std::ostream & str, const std::string & text_r, function<std::string(const char*, const char*)> fnc_r )
+      {
+       for ( const char * e = text_r.c_str(); *e; ++e )
+       {
+         const char * s = e;
+         for ( ; *e && *e != '\n'; ++e ) /*searching*/;
+         str << fnc_r( s, e );
+         str.write( s, e-s );
+         str << "\n";
+         if ( !*e )    // on '\0'
+           break;
+       }
+       return str;
+      }
+      /** \overload Prefix lines by string generated by function [std::string()] */
+      inline std::ostream & autoPrefix0( std::ostream & str, const std::string & text_r, function<std::string()> fnc_r )
+      {
+       auto wrap = [&fnc_r]( const char*, const char* )-> std::string {
+         return fnc_r();
+       };
+       return autoPrefix( str, text_r, wrap );
+      }
+    //@}
+    ///////////////////////////////////////////////////////////////////
+    /** \name Escape. */
+    //@{
+      /**
+       * Escape desired character \a c using a backslash.
+       *
+       * For use when printing \a c separated values, and where
+       * \ref joinEscaped() is too heavy.
+       */
+      std::string escape( const C_Str & str_r, const char c = ' ' );
+
+      /** Escape \a next_r and append it to \a str_r using separator \a sep_r. */
+      inline void appendEscaped( std::string & str_r, const C_Str & next_r, const char sep_r = ' ' )
+      {
+        if ( ! str_r.empty() )
+          str_r += sep_r;
+        if ( next_r.empty() )
+          str_r += "\"\"";
+        else
+          str_r += escape( next_r, sep_r );
+      }
+
+      //! \todo unsecape()
+
+    //@}
+    ///////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////
+    /** \name Hexencode.
+     * Encode all characters other than [a-zA-Z0-9] as %XX.
+     * This includes the % character itself, which becomes %25.
+     */
+    //@{
+    /** Encode all characters other than [a-zA-Z0-9] as %XX.
+     * This includes the % character itself, which becomes %25.
+     */
+    std::string hexencode( const C_Str & str_r );
+    /** Decode hexencoded %XX sequences. */
+    std::string hexdecode( const C_Str & str_r );
+    //@}
+    ///////////////////////////////////////////////////////////////////
+
     /** \name Case conversion. */
     //@{
     /** Return lowercase version of \a s
      * \todo improve
     */
     std::string toLower( const std::string & s );
+    std::string toLower( std::string && s );
+    /** \overload */
+    inline std::string toLower( const char * s )
+    { return( s ? toLower( std::string(s) ) : std::string() ); }
 
     /** Return uppercase version of \a s
      * \todo improve
     */
     std::string toUpper( const std::string & s );
+    std::string toUpper( std::string && s );
+    /** \overload */
+    inline std::string toUpper( const char * s )
+    { return( s ? toUpper( std::string(s) ) : std::string() ); }
+    //@}
+
+
+    /** \name Case insensitive comparison. */
+    //@{
+    inline int compareCI( const C_Str & lhs, const C_Str & rhs )
+    { return ::strcasecmp( lhs, rhs ); }
+    //@}
+
+    /** \name Locate substring. */
+    //@{
+    /** Locate substring case sensitive. */
+    inline bool contains( const C_Str & str_r, const C_Str & val_r )
+    { return ::strstr( str_r, val_r ); }
+    /** Locate substring case insensitive. */
+    inline bool containsCI( const C_Str & str_r, const C_Str & val_r )
+    { return ::strcasestr( str_r, val_r ); }
     //@}
 
     ///////////////////////////////////////////////////////////////////
@@ -296,25 +982,126 @@ namespace zypp
       TRIM    = (L_TRIM|R_TRIM)
     };
 
-    std::string  trim( const std::string & s, const Trim trim_r = TRIM );
+    std::string trim( const std::string & s, const Trim trim_r = TRIM );
+    std::string trim( std::string && s, const Trim trim_r = TRIM );
 
     inline std::string ltrim( const std::string & s )
     { return trim( s, L_TRIM ); }
+    inline std::string ltrim( std::string && s )
+    { return trim( std::move(s), L_TRIM ); }
 
     inline std::string rtrim( const std::string & s )
     { return trim( s, R_TRIM ); }
+    inline std::string rtrim( std::string && s )
+    { return trim( std::move(s), R_TRIM ); }
     //@}
 
-    std::string stripFirstWord( std::string & line, const bool ltrim_first );
+    std::string stripFirstWord( std::string & line, const bool ltrim_first = true );
+
+    std::string stripLastWord( std::string & line, const bool rtrim_first = true );
 
+    /** Return stream content up to (but not returning) the next newline.
+     * \see \ref receiveUpTo
+     */
     std::string getline( std::istream & str, bool trim = false );
 
+    /** Return stream content up to (but not returning) the next newline.
+     * \see \ref receiveUpTo
+     */
     std::string getline( std::istream & str, const Trim trim_r );
 
-    /////////////////////////////////////////////////////////////////
+    /** Return stream content up to the next ocurrence of \c delim_r or EOF
+     * \c delim_r, if found, is always read from the stream. Whether it is
+     * also returned in the string depends on \c returnDelim_r.
+     * If the stream status is \c good, \c delim_r was found in the stream.
+     * If we reached EOF while looking for \c delim_r, \c eof is set; and
+     * also \c fail, if we did not read any data before.
+     */
+    std::string receiveUpTo( std::istream & str, const char delim_r, bool returnDelim_r = false );
+
+    ///////////////////////////////////////////////////////////////////
+
+    /** \name String prefix/suffix handling.
+     */
+    //@{
+    /** Return whether \a str_r has prefix \a prefix_r. */
+    inline bool hasPrefix( const C_Str & str_r, const C_Str & prefix_r )
+    { return( ::strncmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
+    /** \overload Case insensitive */
+    inline bool hasPrefixCI( const C_Str & str_r, const C_Str & prefix_r )
+    { return( ::strncasecmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
+
+    /** Strip a \a prefix_r from \a str_r and return the resulting string. */
+    inline std::string stripPrefix( const C_Str & str_r, const C_Str & prefix_r )
+    { return( hasPrefix( str_r, prefix_r ) ? str_r + prefix_r.size() : str_r.c_str() ); }
+    /** \overload Case insensitive */
+    inline std::string stripPrefixCI( const C_Str & str_r, const C_Str & prefix_r )
+    { return( hasPrefixCI( str_r, prefix_r ) ? str_r + prefix_r.size() : str_r.c_str() ); }
+
+    /** Return whether \a str_r has suffix \a suffix_r. */
+    inline bool hasSuffix( const C_Str & str_r, const C_Str & suffix_r )
+    { return( str_r.size() >= suffix_r.size() && ::strncmp( str_r + str_r.size() - suffix_r.size() , suffix_r, suffix_r.size() ) == 0 ); }
+    /** \overload Case insensitive */
+    inline bool hasSuffixCI( const C_Str & str_r, const C_Str & suffix_r )
+    { return( str_r.size() >= suffix_r.size() && ::strncasecmp( str_r + str_r.size() - suffix_r.size() , suffix_r, suffix_r.size() ) == 0 ); }
+
+    /** Strip a \a suffix_r from \a str_r and return the resulting string. */
+    inline std::string stripSuffix( const C_Str & str_r, const C_Str & suffix_r )
+    {
+      if ( hasSuffix( str_r, suffix_r ) )
+        return std::string( str_r, str_r.size() - suffix_r.size() );
+      return str_r.c_str();
+    }
+    /** \overload Case insensitive */
+    inline std::string stripSuffixCI( const C_Str & str_r, const C_Str & suffix_r )
+    {
+      if ( hasSuffixCI( str_r, suffix_r ) )
+        return std::string( str_r, str_r.size() - suffix_r.size() );
+      return str_r.c_str();
+    }
+
+    /** Return size of the common prefix of \a lhs and \a rhs. */
+    inline std::string::size_type commonPrefix( const C_Str & lhs, const C_Str & rhs )
+    {
+      const char * lp = lhs.c_str();
+      const char * rp = rhs.c_str();
+      std::string::size_type ret = 0;
+      while ( *lp == *rp && *lp != '\0' )
+      { ++lp, ++rp, ++ret; }
+      return ret;
+    }
+    /** \overload Case insensitive */
+    inline std::string::size_type commonPrefixCI( const C_Str & lhs, const C_Str & rhs )
+    {
+      const char * lp = lhs.c_str();
+      const char * rp = rhs.c_str();
+      std::string::size_type ret = 0;
+      while ( tolower(*lp) == tolower(*rp) && *lp != '\0' )
+      { ++lp, ++rp, ++ret; }
+      return ret;
+    }
+
+
+    /** alias for \ref hasPrefix */
+    inline bool startsWith( const C_Str & str_r, const C_Str & prefix_r )
+    { return hasPrefix( str_r, prefix_r ); }
+    /** \overload Case insensitive */
+    inline bool startsWithCI( const C_Str & str_r, const C_Str & prefix_r )
+    { return hasPrefixCI( str_r, prefix_r ); }
+
+    /** alias for \ref hasSuffix */
+    inline bool endsWith( const C_Str & str_r, const C_Str & prefix_r )
+    { return hasSuffix( str_r, prefix_r ); }
+    /** \overload Case insensitive */
+    inline bool endsWithCI( const C_Str & str_r, const C_Str & prefix_r )
+    { return hasSuffixCI( str_r, prefix_r ); }
+    //@}
   } // namespace str
   ///////////////////////////////////////////////////////////////////
-  /////////////////////////////////////////////////////////////////
+
+  // drag into zypp:: namespace
+  using str::asString;
+
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
 #endif // ZYPP_BASE_STRING_H