#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.
*
/** String related utilities and \ref ZYPP_STR_REGEX.
\see \ref ZYPP_STR_REGEX
*/
+
namespace str
{ /////////////////////////////////////////////////////////////////
template<>
inline std::string asString( const bool &t )
- { return t ? "+" : "-"; }
+ { return t ? "true" : "false"; }
///////////////////////////////////////////////////////////////////
/** Printf style construction of std::string. */
};
///////////////////////////////////////////////////////////////////
- /** 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.
*
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 );
+
//@}
/**
*/
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 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
* \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
}
// 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
{
// 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;
///////////////////////////////////////////////////////////////////
+ /** \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. */
//@{
/**
{ 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
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