Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / base / String.h
index a562f97..285c7dd 100644 (file)
 #include <iosfwd>
 #include <vector>
 #include <string>
+#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
@@ -62,6 +84,8 @@ namespace zypp
    * 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
   {
@@ -73,6 +97,7 @@ namespace zypp
       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; }
@@ -100,6 +125,7 @@ namespace zypp
   /** String related utilities and \ref ZYPP_STR_REGEX.
    \see \ref ZYPP_STR_REGEX
   */
+
   namespace str
   { /////////////////////////////////////////////////////////////////
 
@@ -107,21 +133,35 @@ namespace zypp
     /**
      * Global asString() that works with std::string too
      */
-    template<class _T>
-        inline std::string asString( const _T &t )
+    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 _T>
-        inline std::string asString( const intrusive_ptr<_T> &p )
+    template<class Tp>
+        inline std::string asString( const intrusive_ptr<Tp> &p )
         { return p->asString(); }
 
-    template<class _T>
-        inline std::string asString( const weak_ptr<_T> &p )
+    template<class Tp>
+        inline std::string asString( const weak_ptr<Tp> &p )
         { return p->asString(); }
 
     template<>
-        inline std::string asString( const std::string &t )
-        { return t; }
+        inline std::string asString( const bool &t )
+        { return t ? "true" : "false"; }
 
     ///////////////////////////////////////////////////////////////////
     /** Printf style construction of std::string. */
@@ -156,6 +196,99 @@ namespace zypp
     };
 
     ///////////////////////////////////////////////////////////////////
+    /// \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; }
+
+      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).
@@ -179,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 ); }
     //@}
 
     ///////////////////////////////////////////////////////////////////
@@ -229,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.
     */
@@ -239,8 +397,8 @@ namespace zypp
      * time_t t = strtonum<time_t>( "42" );
      * \endcode
     */
-    template<typename _It>
-      _It strtonum( const C_Str & str );
+    template<typename TInt>
+      TInt strtonum( const C_Str & str );
 
     template<>
       inline short              strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
@@ -265,9 +423,9 @@ namespace zypp
      * time_t t; strtonum( "42", t );
      * \endcode
     */
-    template<typename _It>
-      inline _It strtonum( const C_Str & 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 ); }
     //@}
 
     ///////////////////////////////////////////////////////////////////
@@ -298,23 +456,52 @@ namespace zypp
       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 Looks for text in a string and replaces it.
-     *
-     * \note It only perform substtution in one pass
+     * \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 gsub( const std::string& sData, const std::string& sFrom, const std::string& sTo);
+    std::string gsubFun( const std::string & str_r, const std::string & from_r, function<std::string()> to_r );
 
     /**
-     * \short Looks for text in string and replaces it in place
-     *
-     *
-     * \note It only perform substtution in one pass
-     * \note use only if you replace same lenght strings, otherwise use gsub
+     * \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& replaceAll( std::string & str, const std::string & from, const std::string & to);
+    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. */
@@ -328,10 +515,8 @@ namespace zypp
      * \endcode
      *
     */
-    template<class _OutputIterator>
-      unsigned split( const C_Str &   line_r,
-                      _OutputIterator result_r,
-                      const C_Str &   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;
         const char * cur = beg;
@@ -355,7 +540,16 @@ namespace zypp
 
     /** 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 "" or "" or escaped by \, but not \\.
+     * 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
@@ -368,99 +562,131 @@ namespace zypp
      * \code
      * example splitted strings
      * normal line -> 2 elements ( "normal", "line" )
-     * escaped\ line -> 1 element( "escaped 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'" )
+     * "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 _OutputIterator>
-      unsigned splitEscaped( const C_Str &   line_r,
-                      _OutputIterator result_r,
-                      const C_Str &   sepchars_r = " \t" )
+     */
+    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;
-        unsigned ret = 0;
-        for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
+          if (withEmpty)
           {
-            if ( *cur == '"'  || *cur == '\'' )
-            {
-              char closeChar = *cur;
-              ++cur;
-              bool cont = true;
-              while (cont)
-              {
-                while ( *cur && *cur != closeChar)
-                  ++cur;
-                if ( *cur == '\0' )
-                {
-                  return ret; //TODO parsing exception no closing quote
-                }
-                int escCount = 0;
-                const char * esc = cur-1;
-                while ( esc != beg && *esc == '\\' )
-                {
-                  escCount++;
-                  --esc;
-                }
-                cont = (escCount % 2 == 1); // find some non escaped escape char
-                cur++; //skip quote
-              }
-
-              std::string s( beg+1, cur-beg-2 ); //without quotes
-              //transform escaped escape
-              replaceAll( s, "\\\\", "\\" );
-              //transform escaped quotes (only same as open
-              char tmpn[2] = { closeChar, 0 };
-              char tmpo[3] = { '\\', closeChar, 0 };
-              replaceAll( s, tmpo, tmpn );
-
-              *result_r = s;
-            }
-            else
-            {
-              // skip non sepchars
-              while( *cur && !::strchr( sepchars_r, *cur ) )
-              {
-                //ignore char after backslash
-                if ( *cur == '\\' )
-                {
-                  ++cur;
-                }
-                ++cur;
-              }
-              // build string
-              std::string s( beg, cur-beg );
-              //transform escaped escape
-              replaceAll( s, "\\\\", "\\" );
+            *result_r = "";
+            ++ret;
+          }
+        }
 
-              const char *delimeter = sepchars_r;
-              while ( *delimeter )
-              {
-                std::string ds("\\");
-                const char tmp[2] = { *delimeter, '\0' };
-                std::string del(tmp);
-                ds+= del;
-                replaceAll( s, ds, del );
-                ++delimeter;
-              }
+        // there were only sepchars in the string
+        if (!*cur && withEmpty)
+        {
+          *result_r = "";
+          return ++ret;
+        }
 
-              *result_r = s;
-            }
-            // skip sepchars
-            while ( *cur && ::strchr( sepchars_r, *cur ) )
-              ++cur;
-          }
+        // 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. The words are passed to OutputIterator
+     * field separator unless \-escaped. The words are passed
+     * to OutputIterator.
      * \a result_r.
      * \code
      * ""        -> words 0
@@ -471,17 +697,15 @@ namespace zypp
      * ":a:"     -> words 3  ||a||
      *
      * \endcode
-    *
+     *
      * \code
      * std::vector<std::string> words;
      * str::split( "some line", std::back_inserter(words) )
      * \endcode
      *
     */
-    template<class _OutputIterator>
-      unsigned splitFields( const C_Str &   line_r,
-                            _OutputIterator result_r,
-                            const C_Str &   sepchars_r = ":" )
+    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;
@@ -490,7 +714,11 @@ namespace zypp
           {
             // 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;
@@ -508,18 +736,30 @@ namespace zypp
           }
         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 C_Str & 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;
@@ -529,21 +769,19 @@ namespace zypp
       }
 
     /** Join strings using separator \a sep_r (defaults to BLANK). */
-    template <class _Container>
-      std::string join( const _Container & cont_r,
-                        const C_Str & 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 _Iterator>
-      std::string joinEscaped( _Iterator begin, _Iterator end,
-                               const char sep_r = ' ' )
+    template <class TIterator>
+      std::string joinEscaped( TIterator begin, TIterator end, const char sep_r = ' ' )
       {
         std::vector<char> buf;
-        for ( _Iterator iter = begin; iter != end; ++ iter )
+        for ( TIterator iter = begin; iter != end; ++ iter )
         {
           if ( iter != begin )
             buf.push_back( sep_r );
@@ -577,20 +815,102 @@ namespace zypp
         }
         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.
-       *
-       * \todo use C_Str instead of std::string to prevent unnecessary
-       * promotion to string if used with "string".
-       *
-       * \todo shoud not be documented in doxy-group 'Join'
        */
-      std::string escape(const std::string & str_r, const char c = ' ' );
+      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()
 
@@ -617,6 +937,7 @@ namespace zypp
      * \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() ); }
@@ -625,6 +946,7 @@ namespace zypp
      * \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() ); }
@@ -634,11 +956,7 @@ namespace zypp
     /** \name Case insensitive comparison. */
     //@{
     inline int compareCI( const C_Str & lhs, const C_Str & rhs )
-    {
-      if ( lhs == rhs )
-        return 0;
-      return ::strcasecmp( lhs, rhs );
-    }
+    { return ::strcasecmp( lhs, rhs ); }
     //@}
 
     /** \name Locate substring. */
@@ -665,22 +983,42 @@ namespace zypp
     };
 
     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 );
+    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.
@@ -689,14 +1027,23 @@ namespace zypp
     /** 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 )
@@ -705,18 +1052,56 @@ namespace zypp
         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