Add str::gapify
[platform/upstream/libzypp.git] / zypp / base / String.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/base/String.h
10  *
11 */
12 #ifndef ZYPP_BASE_STRING_H
13 #define ZYPP_BASE_STRING_H
14
15 #include <cstring>
16
17 #include <iosfwd>
18 #include <vector>
19 #include <string>
20 #include <sstream>
21 #include <boost/format.hpp>
22
23 #include "zypp/base/Easy.h"
24 #include "zypp/base/PtrTypes.h"
25 #include "zypp/base/Function.h"
26
27
28 ///////////////////////////////////////////////////////////////////
29 namespace boost
30 {
31   /** A formater with (N)o (A)rgument (C)heck.
32    * It won't complain about missing or excess arguments. Sometimes
33    * usefull when dealing with translations or classes providing a
34    * default formater.
35    */
36   inline format formatNAC( const std::string & string_r ) {
37     using namespace boost::io;
38     format fmter( string_r );
39     fmter.exceptions( all_error_bits ^ ( too_many_args_bit | too_few_args_bit ) );
40     return fmter;
41   }
42 } // namespace boost
43 ///////////////////////////////////////////////////////////////////
44
45 ///////////////////////////////////////////////////////////////////
46 namespace zypp
47 {
48   /** Request a human readable (translated) string representation of _Tp [_Tp.asUserString()]
49    * Classes may implement a default as member function.
50    */
51   template <class _Tp>
52   std::string asUserString( const _Tp & val_r )
53   { return val_r.asUserString(); }
54
55 }// namespace zypp
56 ///////////////////////////////////////////////////////////////////
57
58 ///////////////////////////////////////////////////////////////////
59 namespace zypp
60 { /////////////////////////////////////////////////////////////////
61
62   /** Convenience \c char* constructible from \c std::string and \c char*,
63    *  it maps \c (char*)0 to an empty string.
64    *
65    * \code
66    * bool hasPrefix( const std::string & str_r, const std::string & prefix_r )
67    * { return( ::strncmp( str_r.c_str(), prefix_r.c_str(), prefix_r.size() ) == 0 ); }
68    * \endcode
69    *
70    * Called with a plain \c char* as argument, the \c std::string is created form
71    * for nothing. The implementation actually does not use the \c std::string.
72    *
73    * Best would be to implement \c hasPrefix for each combination of \c char*
74    * and \c std::string arguments:
75    *
76    * \code
77    * bool hasPrefix( const std::string & str_r, const std::string & prefix_r )
78    * { return( ::strncmp( str_r.c_str(), prefix_r.c_str(), prefix_r.size() ) == 0 ); }
79    *
80    * bool hasPrefix( const std::string & str_r, const char * prefix_r )
81    * { return( !prefix_r || ::strncmp( str_r.c_str(), prefix_r, ::strlen(prefix_r) ) == 0 ); }
82    *
83    * bool hasPrefix( const char * str_r, const std::string & prefix_r )
84    * { return( str_r ? ::strncmp( str_r, prefix_r.c_str(), prefix_r.size() ) == 0 : prefix_r.empty() ); }
85    *
86    * bool hasPrefix( const char * str_r, const char * prefix_r )
87    * { return( str && prefix_r ? ::strncmp( str_r, prefix_r, ::strlen(prefix_r) ) == 0
88    *                           : !((str_r && *str_r) || (prefix_r && *prefix_r)); }
89    * \endcode
90    *
91    * This is where \ref C_Str can help. Constructible from \c std::string and \c char*,
92    * it \e reduces the \c std::string to it's \c char*. At the same time it converts
93    * \c (char*)0 into an \c "" string.
94    *
95    * \code
96    * bool hasPrefix( const C_Str & str_r, const C_Str & prefix_r )
97    * { return( ::strncmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
98    * \endcode
99    */
100   class C_Str
101   {
102     public:
103       typedef std::string::size_type size_type;
104
105     public:
106       C_Str()                            : _val( 0 ),             _sze( 0 ) {}
107       C_Str( char * c_str_r )            : _val( c_str_r ),       _sze( std::string::npos ) {}
108       C_Str( const char * c_str_r )      : _val( c_str_r ),       _sze( std::string::npos ) {}
109       C_Str( const std::string & str_r ) : _val( str_r.c_str() ), _sze( str_r.size() ) {}
110
111     public:
112       bool      isNull()       const { return !_val; }
113       bool      empty()        const { return !(_val && *_val); }
114       size_type size()         const
115       {
116         if ( _sze == std::string::npos )
117         { _sze = _val ? ::strlen( _val ) : 0; }
118         return _sze;
119       };
120
121       operator const char *() const { return c_str(); }
122       const char * c_str()    const { return _val ? _val : ""; }
123
124     private:
125       const char *const _val;
126       mutable size_type _sze;
127   };
128
129   /** \relates C_Str Stream output */
130   inline std::ostream & operator<<( std::ostream & str, const C_Str & obj )
131   { return str << obj.c_str(); }
132
133   ///////////////////////////////////////////////////////////////////
134   /** String related utilities and \ref ZYPP_STR_REGEX.
135    \see \ref ZYPP_STR_REGEX
136   */
137   namespace str
138   { /////////////////////////////////////////////////////////////////
139
140     ///////////////////////////////////////////////////////////////////
141     /**
142      * Global asString() that works with std::string too
143      */
144     inline std::string asString( const std::string &t )
145     { return t; }
146
147 #ifndef SWIG // Swig treats it as syntax error
148     inline std::string asString( std::string && t )
149     { return std::move(t); }
150 #endif
151
152     inline std::string asString( const char * t )
153     { return t; }
154
155     inline std::string asString( char * t )
156     { return t; }
157
158     template<class _T>
159         inline std::string asString( const _T &t )
160         { return t.asString(); }
161
162     template<class _T>
163         inline std::string asString( const intrusive_ptr<_T> &p )
164         { return p->asString(); }
165
166     template<class _T>
167         inline std::string asString( const weak_ptr<_T> &p )
168         { return p->asString(); }
169
170     template<>
171         inline std::string asString( const bool &t )
172         { return t ? "true" : "false"; }
173
174     ///////////////////////////////////////////////////////////////////
175     /** Printf style construction of std::string. */
176     std::string form( const char * format, ... )
177     __attribute__ ((format (printf, 1, 2)));
178
179     ///////////////////////////////////////////////////////////////////
180     /** Return string describing the \a error_r code.
181      * Like ::strerror, but the numerical value is included in
182      * the string as well.
183     */
184     std::string strerror( int errno_r );
185
186     ///////////////////////////////////////////////////////////////////
187     /** Assert \c free called for allocated <tt>char *</tt>.
188      * \code
189      * ...
190      * SafeBuf safe;
191      * vasprintf( &safe._buf, format, ap );
192      * return safe.asString();
193      * \endcode
194      *
195      * \ingroup g_RAII
196     */
197     struct SafeBuf
198     {
199       char * _buf;
200       SafeBuf() : _buf( 0 ) {}
201       ~SafeBuf() { if ( _buf ) free( _buf ); }
202       std::string asString() const
203       { return _buf ? std::string(_buf) : std::string(); }
204     };
205
206     ///////////////////////////////////////////////////////////////////
207     /** Convenient building of std::string via std::ostream::operator<<.
208      * Basically this is an \ref ostringstream which is autocenvertible
209      * into a \ref string.
210      * \code
211      *  void fnc( const std::string & txt_r );
212      *  fnc( str::Str() << "Hello " << 13 );
213      *
214      *  std::string txt( str::Str() << 45 );
215      * \endcode
216     */
217     struct Str
218     {
219       template<class _Tp>
220       Str & operator<<( const _Tp & val )
221       { _str << val; return *this; }
222
223       operator std::string() const
224       { return _str.str(); }
225
226       std::ostream & stream()
227       { return _str; }
228
229       void clear()
230       { _str.str( std::string() ); }
231
232       std::ostringstream _str;
233     };
234
235     inline std::ostream & operator<<( std::ostream & str, const Str & obj )
236     { return str << (std::string)obj; }
237
238     ///////////////////////////////////////////////////////////////////
239     /** \name String representation of number.
240      *
241      * Optional second argument sets the minimal string width (' ' padded).
242      * Negative values will cause the number to be left adjusted within the string.
243      *
244      * Default width is 0.
245      * \code
246      * numstring(42)           -> "42"
247      * numstring(42, 4)        -> "  42"
248      * numstring(42,-4)        -> "42  "
249      * \endcode
250      **/
251     //@{
252     inline std::string numstring( char n,               int w = 0 ) { return form( "%*hhd",  w, n ); }
253     inline std::string numstring( unsigned char n,      int w = 0 ) { return form( "%*hhu",  w, n ); }
254     inline std::string numstring( short n,              int w = 0 ) { return form( "%*hd",   w, n ); }
255     inline std::string numstring( unsigned short n,     int w = 0 ) { return form( "%*hu",   w, n ); }
256     inline std::string numstring( int n,                int w = 0 ) { return form( "%*d",    w, n ); }
257     inline std::string numstring( unsigned n,           int w = 0 ) { return form( "%*u",    w, n ); }
258     inline std::string numstring( long n,               int w = 0 ) { return form( "%*ld",   w, n ); }
259     inline std::string numstring( unsigned long n,      int w = 0 ) { return form( "%*lu",   w, n ); }
260     inline std::string numstring( long long n,          int w = 0 ) { return form( "%*lld",  w, n ); }
261     inline std::string numstring( unsigned long long n, int w = 0 ) { return form( "%*llu",  w, n ); }
262
263     template<> inline std::string asString( const char & t )                    { return numstring( t ); }
264     template<> inline std::string asString( const unsigned char & t )           { return numstring( t ); }
265     template<> inline std::string asString( const short & t )                   { return numstring( t ); }
266     template<> inline std::string asString( const unsigned short & t )          { return numstring( t ); }
267     template<> inline std::string asString( const int & t )                     { return numstring( t ); }
268     template<> inline std::string asString( const unsigned & t )                { return numstring( t ); }
269     template<> inline std::string asString( const long & t )                    { return numstring( t ); }
270     template<> inline std::string asString( const unsigned long & t )           { return numstring( t ); }
271     template<> inline std::string asString( const long long & t )               { return numstring( t ); }
272     template<> inline std::string asString( const unsigned long long & t )      { return numstring( t ); }
273     //@}
274
275     ///////////////////////////////////////////////////////////////////
276     /** \name String representation of number as hex value with leading '0x'.
277      * Optional second argument sets the minimal
278      * string width (0 padded). Negative values will cause the number to be left adjusted
279      * within the string. Default width is 10 (4 for char).
280      * <PRE>
281      * hexstring(42)           -> "0x0000002a"
282      * hexstring(42, 4)        -> "0x2a"
283      * hexstring(42,-4)        -> "0x2a"
284      * </PRE>
285      **/
286     //@{
287     inline std::string hexstring( char n,               int w = 4 ) { return form( "%#0*hhx", w, n ); }
288     inline std::string hexstring( unsigned char n,      int w = 4 ) { return form( "%#0*hhx", w, n ); }
289     inline std::string hexstring( short n,              int w = 10 ){ return form( "%#0*hx",  w, n ); }
290     inline std::string hexstring( unsigned short n,     int w = 10 ){ return form( "%#0*hx",  w, n ); }
291     inline std::string hexstring( int n,                int w = 10 ){ return form( "%#0*x",   w, n ); }
292     inline std::string hexstring( unsigned n,           int w = 10 ){ return form( "%#0*x",   w, n ); }
293     inline std::string hexstring( long n,               int w = 10 ){ return form( "%#0*lx",  w, n ); }
294     inline std::string hexstring( unsigned long n,      int w = 10 ){ return form( "%#0*lx",  w, n ); }
295     inline std::string hexstring( long long n,          int w = 0 ) { return form( "%#0*llx", w, n ); }
296     inline std::string hexstring( unsigned long long n, int w = 0 ) { return form( "%#0*llx", w, n ); }
297     //@}
298
299     ///////////////////////////////////////////////////////////////////
300     /** \name String representation of number as octal value with leading '0'.
301      * Optional second argument sets the minimal
302      * string width (0 padded). Negative values will cause the number to be left adjusted
303      * within the string. Default width is 5 (4 for char).
304      * <PRE>
305      * octstring(42)           -> "00052"
306      * octstring(42, 4)        -> "0052"
307      * octstring(42,-4)        -> "052 "
308      * </PRE>
309      **/
310     //@{
311     inline std::string octstring( char n,               int w = 4 ) { return form( "%#0*hho",  w, n ); }
312     inline std::string octstring( unsigned char n,      int w = 4 ) { return form( "%#0*hho",  w, n ); }
313     inline std::string octstring( short n,              int w = 5 ) { return form( "%#0*ho",   w, n ); }
314     inline std::string octstring( unsigned short n,     int w = 5 ) { return form( "%#0*ho",   w, n ); }
315     inline std::string octstring( int n,                int w = 5 ) { return form( "%#0*o",    w, n ); }
316     inline std::string octstring( unsigned n,           int w = 5 ) { return form( "%#0*o",    w, n ); }
317     inline std::string octstring( long n,               int w = 5 ) { return form( "%#0*lo",   w, n ); }
318     inline std::string octstring( unsigned long n,      int w = 5 ) { return form( "%#0*lo",   w, n ); }
319     inline std::string octstring( long long n,          int w = 0 ) { return form( "%#0*llo",  w, n ); }
320     inline std::string octstring( unsigned long long n, int w = 0 ) { return form( "%#0*llo",  w, n ); }
321     //@}
322
323     ///////////////////////////////////////////////////////////////////
324     /** Parsing numbers from string.
325     */
326     //@{
327     /** String to integer type determined by template arg.
328      * \note Only specializations are defined.
329      * \code
330      * time_t t = strtonum<time_t>( "42" );
331      * \endcode
332     */
333     template<typename _It>
334       _It strtonum( const C_Str & str );
335
336     template<>
337       inline short              strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
338     template<>
339       inline int                strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
340     template<>
341       inline long               strtonum( const C_Str & str ) { return ::strtol  ( str, NULL, 0 ); }
342     template<>
343       inline long long          strtonum( const C_Str & str ) { return ::strtoll ( str, NULL, 0 ); }
344
345     template<>
346       inline unsigned short     strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
347     template<>
348       inline unsigned           strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
349     template<>
350       inline unsigned long      strtonum( const C_Str & str ) { return ::strtoul ( str, NULL, 0 ); }
351     template<>
352       inline unsigned long long strtonum( const C_Str & str ) { return ::strtoull( str, NULL, 0 ); }
353
354     /** String to integer type detemined 2nd function arg \a i.
355      * \code
356      * time_t t; strtonum( "42", t );
357      * \endcode
358     */
359     template<typename _It>
360       inline _It strtonum( const C_Str & str, _It & i )
361       { return i = strtonum<_It>( str ); }
362     //@}
363
364     ///////////////////////////////////////////////////////////////////
365     /** Parsing boolean from string.
366     */
367     //@{
368     /** Return \c true if str is <tt>1, true, yes, on</tt> (or a nonzero number). */
369     bool strToTrue( const C_Str & str );
370
371     /** Return \c false if str is <tt>0, false, no, off</tt>. */
372     bool strToFalse( const C_Str & str );
373
374     /** Parse \c str into a bool depending on the default value.
375      * If the \c default is true, look for a legal \c false string.
376      * If the \c default is false, look for a legal \c true string.
377      */
378     inline bool strToBool( const C_Str & str, bool default_r )
379     { return( default_r ? strToFalse( str ) : strToTrue( str ) ); }
380
381     /** Parse \c str into a bool if it's a legal \c true or \c false string.
382      * If \c str is not a recognized \c true or \c false string, \a return_r
383      * is left unchanged.
384      */
385     inline bool strToBoolNodefault( const C_Str & str, bool & return_r )
386     {
387       if ( strToTrue( str ) ) return (return_r = true);
388       if ( !strToFalse( str ) ) return (return_r = false);
389       return return_r;
390     }
391
392     //@}
393
394     /**
395      * \short Return a string with all occurrences of \c from_r replaced with \c to_r.
396      */
397     std::string gsub( const std::string & str_r, const std::string & from_r, const std::string & to_r );
398
399     /** \overload A function is called on demand to compute each replacement value.
400      */
401     std::string gsubFun( const std::string & str_r, const std::string & from_r, function<std::string()> to_r );
402
403     /**
404      * \short Replace all occurrences of \c from_r with \c to_r in \c str_r (inplace).
405      * A reference to \c str_r is also returned for convenience.
406      */
407     std::string & replaceAll( std::string & str_r, const std::string & from_r, const std::string & to_r );
408
409     /** \overload A function is called on demand to compute each replacement value.
410      */
411     std::string & replaceAllFun( std::string & str_r, const std::string & from_r, function<std::string()> to_r );
412
413     /** Enhance readability: insert gaps at regular distance
414      * \code
415      *   // no gaps
416      *   Key Fingerprint:  22C07BA534178CD02EFE22AAB88B2FD43DBDC284
417      *   // gapify 8
418      *   Key Fingerprint:  22C07BA5 34178CD0 2EFE22AA B88B2FD4 3DBDC284
419      *   // gapify 4
420      *   Key Fingerprint:  22C0 7BA5 3417 8CD0 2EFE 22AA B88B 2FD4 3DBD C284
421      *   // gapify 4, '-'
422      *   Key Fingerprint:  22C0-7BA5-3417-8CD0-2EFE-22AA-B88B-2FD4-3DBD-C284
423      * \endcode
424      */
425     inline std::string gapify( std::string inp_r, std::string::size_type gap_r = 1, char gapchar = ' ' )
426     {
427       if ( gap_r &&  inp_r.size() > gap_r )
428       {
429         inp_r.reserve( inp_r.size() + (inp_r.size()-1)/gap_r );
430         for ( std::string::size_type pos = gap_r; pos < inp_r.size(); pos += gap_r+1 )
431           inp_r.insert( pos, 1, gapchar );
432       }
433       return inp_r;
434     }
435
436     ///////////////////////////////////////////////////////////////////
437     /** \name Split. */
438     //@{
439     /** Split \a line_r into words.
440      * Any sequence of characters in \a sepchars_r is treated as
441      * delimiter. The words are passed to OutputIterator \a result_r.
442      * \code
443      * std::vector<std::string> words;
444      * str::split( "some line", std::back_inserter(words) )
445      * \endcode
446      *
447     */
448     template<class _OutputIterator>
449       unsigned split( const C_Str &   line_r,
450                       _OutputIterator result_r,
451                       const C_Str &   sepchars_r = " \t" )
452       {
453         const char * beg = line_r;
454         const char * cur = beg;
455         // skip leading sepchars
456         while ( *cur && ::strchr( sepchars_r, *cur ) )
457           ++cur;
458         unsigned ret = 0;
459         for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
460           {
461             // skip non sepchars
462             while( *cur && !::strchr( sepchars_r, *cur ) )
463               ++cur;
464             // build string
465             *result_r = std::string( beg, cur-beg );
466             // skip sepchars
467             while ( *cur && ::strchr( sepchars_r, *cur ) )
468               ++cur;
469           }
470         return ret;
471       }
472
473     /** Split \a line_r into words with respect to escape delimeters.
474      * Any sequence of characters in \a sepchars_r is treated as
475      * delimiter if not inside "" or "" or escaped by \, but not \\.
476      * The words are passed to OutputIterator \a result_r.
477      *
478      * \see \ref splitEscaped
479      *
480      * \code
481      * std::vector<std::string> words;
482      * str::splitEscaped( "some line", std::back_inserter(words) )
483      * \endcode
484      *
485      * \code
486      * example splitted strings
487      * normal line -> 2 elements ( "normal", "line" )
488      * escaped\ line -> 1 element( "escaped line" )
489      * "quoted line" -> 1 element same as above
490      * 'quoted line' -> 1 element same as above
491      * "escaped quote\'" -> 1 element ( "escaped quote'" )
492      *
493      * \param line_r   The string to parse.
494      * \param result_r
495      * \param sepchars_r  String of separator characters.
496      * \param withEmpty   Whether to include empty fields between separators in the result.
497      *
498      * \endcode
499      */
500     template<class _OutputIterator>
501       unsigned splitEscaped( const C_Str &   line_r,
502                       _OutputIterator result_r,
503                       const C_Str &   sepchars_r = " \t",
504                       bool withEmpty = false)
505       {
506         const char * beg = line_r;
507         const char * cur = beg;
508         unsigned ret = 0;
509
510         // skip leading sepchars
511         while ( *cur && ::strchr( sepchars_r, *cur ) )
512         {
513           ++cur;
514           if (withEmpty)
515           {
516             *result_r = "";
517             ++ret;
518           }
519         }
520
521         // there were only sepchars in the string
522         if (!*cur && withEmpty)
523         {
524           *result_r = "";
525           return ++ret;
526         }
527
528         // after the leading sepchars
529         for ( beg = cur; *beg; beg = cur, ++result_r, ++ret )
530           {
531             if ( *cur == '"'  || *cur == '\'' )
532             {
533               char closeChar = *cur;
534               ++cur;
535               bool cont = true;
536               while (cont)
537               {
538                 while ( *cur && *cur != closeChar)
539                   ++cur;
540                 if ( *cur == '\0' )
541                 {
542                   return ret; //TODO parsing exception no closing quote
543                 }
544                 int escCount = 0;
545                 const char * esc = cur-1;
546                 while ( esc != beg && *esc == '\\' )
547                 {
548                   escCount++;
549                   --esc;
550                 }
551                 cont = (escCount % 2 == 1); // find some non escaped escape char
552                 cur++; //skip quote
553               }
554
555               std::string s( beg+1, cur-beg-2 ); //without quotes
556               //transform escaped escape
557               replaceAll( s, "\\\\", "\\" );
558               //transform escaped quotes (only same as open
559               char tmpn[2] = { closeChar, 0 };
560               char tmpo[3] = { '\\', closeChar, 0 };
561               replaceAll( s, tmpo, tmpn );
562
563               *result_r = s;
564             }
565             else
566             {
567               // skip non sepchars
568               while( *cur && !::strchr( sepchars_r, *cur ) )
569               {
570                 //ignore char after backslash
571                 if ( *cur == '\\' )
572                 {
573                   ++cur;
574                 }
575                 ++cur;
576               }
577               // build string
578               std::string s( beg, cur-beg );
579               //transform escaped escape
580               replaceAll( s, "\\\\", "\\" );
581
582               const char *delimeter = sepchars_r;
583               while ( *delimeter )
584               {
585                 std::string ds("\\");
586                 const char tmp[2] = { *delimeter, '\0' };
587                 std::string del(tmp);
588                 ds+= del;
589                 replaceAll( s, ds, del );
590                 ++delimeter;
591               }
592
593               *result_r = s;
594             }
595             // skip sepchars
596             if ( *cur && ::strchr( sepchars_r, *cur ) )
597               ++cur;
598             while ( *cur && ::strchr( sepchars_r, *cur ) )
599             {
600               ++cur;
601               if (withEmpty)
602               {
603                 *result_r = "";
604                 ++ret;
605               }
606             }
607             // the last was a separator => one more field
608             if ( !*cur && withEmpty && ::strchr( sepchars_r, *(cur-1) ) )
609             {
610               *result_r = "";
611               ++ret;
612             }
613           }
614         return ret;
615       }
616
617     /** Split \a line_r into fields.
618      * Any single character in \a sepchars_r is treated as a
619      * field separator unless \-escaped. The words are passed
620      * to OutputIterator.
621      * \a result_r.
622      * \code
623      * ""        -> words 0
624      * ":"       -> words 2  |||
625      * "a"       -> words 1  |a|
626      * ":a"      -> words 2  ||a|
627      * "a:"      -> words 2  |a||
628      * ":a:"     -> words 3  ||a||
629      *
630      * \endcode
631      *
632      * \code
633      * std::vector<std::string> words;
634      * str::split( "some line", std::back_inserter(words) )
635      * \endcode
636      *
637     */
638     template<class _OutputIterator>
639       unsigned splitFields( const C_Str &   line_r,
640                             _OutputIterator result_r,
641                             const C_Str &   sepchars_r = ":" )
642       {
643         const char * beg = line_r;
644         const char * cur = beg;
645         unsigned ret = 0;
646         for ( beg = cur; *beg; beg = cur, ++result_r )
647           {
648             // skip non sepchars
649             while( *cur && !::strchr( sepchars_r, *cur ) )
650             {
651               if ( *cur == '\\' && *(cur+1) )
652                 ++cur;
653               ++cur;
654             }
655             // build string
656             *result_r = std::string( beg, cur-beg );
657             ++ret;
658             // skip sepchar
659             if ( *cur )
660             {
661               ++cur;
662               if ( ! *cur )                // ending with sepchar
663               {
664                 *result_r = std::string(); // add final empty field
665                 ++ret;
666                 break;
667               }
668             }
669           }
670         return ret;
671       }
672
673     /**
674      * Split \a line_r into fields handling also escaped separators.
675      *
676      * \see splitFields()
677      * \see splitEscaped()
678      */
679     template<class _OutputIterator>
680       unsigned splitFieldsEscaped( const C_Str &   line_r,
681                             _OutputIterator result_r,
682                             const C_Str &   sepchars_r = ":" )
683       {
684         return
685           splitEscaped( line_r, result_r, sepchars_r, true /* withEmpty */ );
686       }
687
688     //@}
689
690     ///////////////////////////////////////////////////////////////////
691     /** \name Join. */
692     //@{
693     /** Join strings using separator \a sep_r (defaults to BLANK). */
694     template <class _Iterator>
695       std::string join( _Iterator begin, _Iterator end,
696                         const C_Str & sep_r = " " )
697       {
698         std::string res;
699         for ( _Iterator iter = begin; iter != end; ++ iter )
700           {
701             if ( iter != begin )
702               res += sep_r;
703             res += asString(*iter);
704           }
705         return res;
706       }
707
708     /** Join strings using separator \a sep_r (defaults to BLANK). */
709     template <class _Container>
710       std::string join( const _Container & cont_r,
711                         const C_Str & sep_r = " " )
712       { return join( cont_r.begin(), cont_r.end(), sep_r ); }
713
714     /** Join strings using separator \a sep_r, quoting or escaping the values.
715      * Separator defaults to BLANK. Use \ref splitEscaped to restore the
716      * values.
717      */
718     template <class _Iterator>
719       std::string joinEscaped( _Iterator begin, _Iterator end,
720                                const char sep_r = ' ' )
721       {
722         std::vector<char> buf;
723         for ( _Iterator iter = begin; iter != end; ++ iter )
724         {
725           if ( iter != begin )
726             buf.push_back( sep_r );
727
728           if ( iter->empty() )
729           {
730             // empty string goes ""
731             buf.push_back( '"' );
732             buf.push_back( '"' );
733           }
734           else
735           {
736             std::string toadd( asString(*iter) );
737             for_( ch, toadd.begin(), toadd.end() )
738             {
739               switch ( *ch )
740               {
741                 case '"':
742                 case '\'':
743                 case '\\':
744                   buf.push_back( '\\' );
745                   buf.push_back( *ch );
746                   break;
747                 default:
748                   if ( *ch == sep_r )
749                     buf.push_back( '\\' );
750                   buf.push_back( *ch );
751               }
752             }
753           }
754         }
755         return std::string( buf.begin(), buf.end() );
756       }
757     //@}
758
759
760     ///////////////////////////////////////////////////////////////////
761     /** \name Indent. */
762     //@{
763       /** Indent by string ["  "] optionally wrap.
764        * Prints nothing for an empty string. Asserts a trainling '\n' on
765        * the last line. Optionally wrap lines at ' ' at a given length.
766        */
767       inline std::ostream & printIndented( std::ostream & str, const std::string & text_r, const std::string & indent_r = "  ", unsigned maxWitdh_r = 0 )
768       {
769         if ( maxWitdh_r )
770         {
771           if ( indent_r.size() >= maxWitdh_r )
772             maxWitdh_r = 0;     // nonsense: indent larger than line witdh
773           else
774             maxWitdh_r -= indent_r.size();
775         }
776         unsigned width = 0;
777         for ( const char * e = text_r.c_str(), * s = e; *e; s = ++e )
778         {
779           for ( ; *e && *e != '\n'; ++e ) ;/*searching*/
780           width = e-s;
781           if ( maxWitdh_r && width > maxWitdh_r )
782           {
783             // must break line
784             width = maxWitdh_r;
785             for ( e = s+width; e > s && *e != ' '; --e ) ;/*searching*/
786             if ( e > s )
787               width = e-s;      // on a ' ', replaced by '\n'
788             else
789               e = s+width-1;    // cut line;
790           }
791           str << indent_r;
792           str.write( s, width );
793           str << "\n";
794           if ( !*e )    // on '\0'
795             break;
796         }
797         return str;
798       }
799       /** \overload Indent by number of chars [' '] optionally wrap. */
800       inline std::ostream & printIndented( std::ostream & str, const std::string & text_r, unsigned indent_r, char indentch_r = ' ', unsigned maxWitdh_r = 0 )
801       { return printIndented( str, text_r, std::string( indent_r, indentch_r ), maxWitdh_r ); }
802       /** \overload Indent by number of chars [' '] wrap. */
803       inline std::ostream & printIndented( std::ostream & str, const std::string & text_r, unsigned indent_r, unsigned maxWitdh_r, char indentch_r = ' ' )
804       { return printIndented( str, text_r, std::string( indent_r, indentch_r ), maxWitdh_r ); }
805
806       /** Prefix lines by string computed by function taking line begin/end [std::string(const char*, const char*)]
807        * Prints nothing for an empty string. Asserts a trainling '\n' on the last line.
808        */
809       inline std::ostream & autoPrefix( std::ostream & str, const std::string & text_r, function<std::string(const char*, const char*)> fnc_r )
810       {
811         for ( const char * e = text_r.c_str(); *e; ++e )
812         {
813           const char * s = e;
814           for ( ; *e && *e != '\n'; ++e ) /*searching*/;
815           str << fnc_r( s, e );
816           str.write( s, e-s );
817           str << "\n";
818           if ( !*e )    // on '\0'
819             break;
820         }
821         return str;
822       }
823       /** \overload Prefix lines by string generated by function [std::string()] */
824       inline std::ostream & autoPrefix0( std::ostream & str, const std::string & text_r, function<std::string()> fnc_r )
825       {
826         auto wrap = [&fnc_r]( const char*, const char* )-> std::string {
827           return fnc_r();
828         };
829         return autoPrefix( str, text_r, wrap );
830       }
831     //@}
832     ///////////////////////////////////////////////////////////////////
833     /** \name Escape. */
834     //@{
835       /**
836        * Escape desired character \a c using a backslash.
837        *
838        * For use when printing \a c separated values, and where
839        * \ref joinEscaped() is too heavy.
840        */
841       std::string escape( const C_Str & str_r, const char c = ' ' );
842
843       /** Escape \a next_r and append it to \a str_r using separator \a sep_r. */
844       inline void appendEscaped( std::string & str_r, const C_Str & next_r, const char sep_r = ' ' )
845       {
846         if ( ! str_r.empty() )
847           str_r += sep_r;
848         if ( next_r.empty() )
849           str_r += "\"\"";
850         else
851           str_r += escape( next_r, sep_r );
852       }
853
854       //! \todo unsecape()
855
856     //@}
857     ///////////////////////////////////////////////////////////////////
858     ///////////////////////////////////////////////////////////////////
859     /** \name Hexencode.
860      * Encode all characters other than [a-zA-Z0-9] as %XX.
861      * This includes the % character itself, which becomes %25.
862      */
863     //@{
864     /** Encode all characters other than [a-zA-Z0-9] as %XX.
865      * This includes the % character itself, which becomes %25.
866      */
867     std::string hexencode( const C_Str & str_r );
868     /** Decode hexencoded %XX sequences. */
869     std::string hexdecode( const C_Str & str_r );
870     //@}
871     ///////////////////////////////////////////////////////////////////
872
873     /** \name Case conversion. */
874     //@{
875     /** Return lowercase version of \a s
876      * \todo improve
877     */
878     std::string toLower( const std::string & s );
879     /** \overload */
880     inline std::string toLower( const char * s )
881     { return( s ? toLower( std::string(s) ) : std::string() ); }
882
883     /** Return uppercase version of \a s
884      * \todo improve
885     */
886     std::string toUpper( const std::string & s );
887     /** \overload */
888     inline std::string toUpper( const char * s )
889     { return( s ? toUpper( std::string(s) ) : std::string() ); }
890     //@}
891
892
893     /** \name Case insensitive comparison. */
894     //@{
895     inline int compareCI( const C_Str & lhs, const C_Str & rhs )
896     { return ::strcasecmp( lhs, rhs ); }
897     //@}
898
899     /** \name Locate substring. */
900     //@{
901     /** Locate substring case sensitive. */
902     inline bool contains( const C_Str & str_r, const C_Str & val_r )
903     { return ::strstr( str_r, val_r ); }
904     /** Locate substring case insensitive. */
905     inline bool containsCI( const C_Str & str_r, const C_Str & val_r )
906     { return ::strcasestr( str_r, val_r ); }
907     //@}
908
909     ///////////////////////////////////////////////////////////////////
910     /** \name Trimming whitepace.
911      * \todo optimize l/r trim.
912     */
913     //@{
914     /** To define how to trim. */
915     enum Trim {
916       NO_TRIM = 0x00,
917       L_TRIM  = 0x01,
918       R_TRIM  = 0x02,
919       TRIM    = (L_TRIM|R_TRIM)
920     };
921
922     std::string trim( const std::string & s, const Trim trim_r = TRIM );
923
924     inline std::string ltrim( const std::string & s )
925     { return trim( s, L_TRIM ); }
926
927     inline std::string rtrim( const std::string & s )
928     { return trim( s, R_TRIM ); }
929     //@}
930
931     std::string stripFirstWord( std::string & line, const bool ltrim_first = true );
932
933     std::string stripLastWord( std::string & line, const bool rtrim_first = true );
934
935     /** Return stream content up to (but not returning) the next newline.
936      * \see \ref receiveUpTo
937      */
938     std::string getline( std::istream & str, bool trim = false );
939
940     /** Return stream content up to (but not returning) the next newline.
941      * \see \ref receiveUpTo
942      */
943     std::string getline( std::istream & str, const Trim trim_r );
944
945     /** Return stream content up to the next ocurrence of \c delim_r or EOF
946      * \c delim_r, if found, is always read from the stream. Whether it is
947      * also returned in the string depends on \c returnDelim_r.
948      * If the stream status is \c good, \c delim_r was found in the stream.
949      * If we reached EOF while looking for \c delim_r, \c eof is set; and
950      * also \c fail, if we did not read any data before.
951      */
952     std::string receiveUpTo( std::istream & str, const char delim_r, bool returnDelim_r = false );
953
954     ///////////////////////////////////////////////////////////////////
955
956     /** \name String prefix/suffix handling.
957      */
958     //@{
959     /** Return whether \a str_r has prefix \a prefix_r. */
960     inline bool hasPrefix( const C_Str & str_r, const C_Str & prefix_r )
961     { return( ::strncmp( str_r, prefix_r, prefix_r.size() ) == 0 ); }
962
963     /** Strip a \a prefix_r from \a str_r and return the resulting string. */
964     inline std::string stripPrefix( const C_Str & str_r, const C_Str & prefix_r )
965     { return( hasPrefix( str_r, prefix_r ) ? str_r + prefix_r.size() : str_r.c_str() ); }
966
967     /** Return whether \a str_r has suffix \a suffix_r. */
968     inline bool hasSuffix( const C_Str & str_r, const C_Str & suffix_r )
969     { return( str_r.size() >= suffix_r.size() && ::strncmp( str_r + str_r.size() - suffix_r.size() , suffix_r, suffix_r.size() ) == 0 ); }
970
971     /** Strip a \a suffix_r from \a str_r and return the resulting string. */
972     inline std::string stripSuffix( const C_Str & str_r, const C_Str & suffix_r )
973     {
974       if ( hasSuffix( str_r, suffix_r ) )
975         return std::string( str_r, str_r.size() - suffix_r.size() );
976       return str_r.c_str();
977     }
978     /** Return size of the common prefix of \a lhs and \a rhs. */
979     inline std::string::size_type commonPrefix( const C_Str & lhs, const C_Str & rhs )
980     {
981       const char * lp = lhs.c_str();
982       const char * rp = rhs.c_str();
983       std::string::size_type ret = 0;
984       while ( *lp == *rp && *lp != '\0' )
985       { ++lp, ++rp, ++ret; }
986       return ret;
987     }
988
989     /** alias for \ref hasPrefix */
990     inline bool startsWith( const C_Str & str_r, const C_Str & prefix_r )
991     { return hasPrefix( str_r, prefix_r ); }
992     /** alias for \ref hasSuffix */
993     inline bool endsWith( const C_Str & str_r, const C_Str & prefix_r )
994     { return hasSuffix( str_r, prefix_r ); }
995     //@}
996     /////////////////////////////////////////////////////////////////
997   } // namespace str
998   ///////////////////////////////////////////////////////////////////
999   /////////////////////////////////////////////////////////////////
1000 } // namespace zypp
1001 ///////////////////////////////////////////////////////////////////
1002 #endif // ZYPP_BASE_STRING_H