Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / base / String.h
index c6f589d..acff23a 100644 (file)
 #include "zypp/base/PtrTypes.h"
 #include "zypp/base/Function.h"
 
+///////////////////////////////////////////////////////////////////
+namespace boost { namespace logic { class tribool; } }
+namespace zypp { typedef boost::logic::tribool TriBool; }
+///////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////
-namespace boost
+namespace zypp
 {
-  /** A formater 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.
+  /** Request a human readable (translated) string representation of _Tp [_Tp.asUserString()]
+   * Classes may implement a default as member function.
    */
-  inline format formatNAC( const std::string & string_r ) {
-    using namespace boost::io;
-    format fmter( string_r );
-    fmter.exceptions( all_error_bits ^ ( too_many_args_bit | too_few_args_bit ) );
-    return fmter;
-  }
-} // namespace boost
+  template <class _Tp>
+  std::string asUserString( const _Tp & val_r )
+  { return val_r.asUserString(); }
+
+}// namespace zypp
 ///////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
 
+  struct MessageString : public std::string
+  {
+    MessageString() {}
+    MessageString( const char * str_r )                : std::string( str_r ? str_r : "" ) {}
+    MessageString( const std::string & str_r )         : std::string( str_r ) {}
+    // boost::format, std::ostringstream, str::Str ...
+    template<class _Str>
+    MessageString( const _Str & str_r )        : std::string( str_r.str() ) {}
+  };
+
   /** Convenience \c char* constructible from \c std::string and \c char*,
    *  it maps \c (char*)0 to an empty string.
    *
@@ -121,6 +131,7 @@ namespace zypp
   /** String related utilities and \ref ZYPP_STR_REGEX.
    \see \ref ZYPP_STR_REGEX
   */
+
   namespace str
   { /////////////////////////////////////////////////////////////////
 
@@ -156,7 +167,7 @@ namespace zypp
 
     template<>
         inline std::string asString( const bool &t )
-        { return t ? "+" : "-"; }
+        { return t ? "true" : "false"; }
 
     ///////////////////////////////////////////////////////////////////
     /** Printf style construction of std::string. */
@@ -191,28 +202,98 @@ namespace zypp
     };
 
     ///////////////////////////////////////////////////////////////////
-    /** Convenient building of std::string via std::ostream::operator<<.
-     * Basically this is an \ref ostringstream which is autocenvertible
-     * into a \ref string.
-     * \code
-     *  void fnc( const std::string & txt_r );
-     *  fnc( str::Str() << "Hello " << 13 );
-     *
-     *  std::string txt( str::Str() << 45 );
-     * \endcode
-    */
+    /// \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<<( const _Tp & val )
+      template<class Tp>
+      Str & operator<<( const Tp & val )
       { _str << val; return *this; }
 
-      operator std::string() const
-      { return _str.str(); }
+      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.
      *
@@ -367,6 +448,9 @@ 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 );
+
     //@}
 
     /**
@@ -388,6 +472,28 @@ namespace zypp
      */
     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. */
@@ -428,7 +534,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
@@ -441,10 +556,10 @@ 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
@@ -482,97 +597,93 @@ namespace zypp
         }
 
         // 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 )
-          {
-            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, "\\\\", "\\" );
-
-              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;
-              }
-
-              *result_r = s;
-            }
-            // 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;
-            }
-          }
+       {
+         // 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
@@ -602,7 +713,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;
@@ -709,6 +824,78 @@ namespace zypp
 
 
     ///////////////////////////////////////////////////////////////////
+    /** \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. */
     //@{
       /**
@@ -807,9 +994,9 @@ namespace zypp
     { return trim( 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
@@ -872,10 +1059,12 @@ namespace zypp
     inline bool endsWith( const C_Str & str_r, const C_Str & prefix_r )
     { return hasSuffix( str_r, prefix_r ); }
     //@}
-    /////////////////////////////////////////////////////////////////
   } // namespace str
   ///////////////////////////////////////////////////////////////////
-  /////////////////////////////////////////////////////////////////
+
+  // drag into zypp:: namespace
+  using str::asString;
+
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
 #endif // ZYPP_BASE_STRING_H