libzypp  17.36.7
text.h
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 ----------------------------------------------------------------------/
9 *
10 * This file contains private API, this might break at any time between releases.
11 * Strictly for internal use!
12 */
13 
14 
15 #ifndef ZYPP_UTILS_TEXT_H_
16 #define ZYPP_UTILS_TEXT_H_
17 
18 #include <iosfwd>
19 #include <string>
20 
21 #include <boost/utility/string_ref.hpp>
22 #include <zypp-core/base/DtorReset>
23 #include <zypp-core/base/String.h>
24 
25 namespace ztui {
26 
28 namespace mbs
29 {
30 #define ZYPPER_TRACE_MBS 0
31 
32  struct MbToWc
33  {
34  static const char _oooooooo = static_cast<char>(0000);
35  static const char _Xooooooo = static_cast<char>(0200);
36  static const char _XXoooooo = static_cast<char>(0300);
37  static const char _XXXooooo = static_cast<char>(0340);
38  static const char _XXXXoooo = static_cast<char>(0360);
39  static const char _XXXXXooo = static_cast<char>(0370);
40  static const char _ooXXXXXX = static_cast<char>(0077);
41 
42  MbToWc( char ch )
43  : _wc( ch )
44  , _cont( -1 )
45  {
46  if ( (_wc & _XXXooooo) == _XXoooooo ) // '110xxxxx'
47  { _wc &= ~_XXXooooo; _cont = 1; }
48  else if ( (_wc & _XXXXoooo) == _XXXooooo ) // '1110xxxx'
49  { _wc &= ~_XXXXoooo; _cont = 2; }
50  else if ( (_wc & _XXXXXooo) == _XXXXoooo ) // '11110xxx'
51  { _wc &= ~_XXXXXooo; _cont = 3; }
52  else if ( (_wc & _Xooooooo) == _oooooooo ) // '0xxxxxxx'
53  { _cont = 0; }
54  else // something broken
55  { _wc = L'?'; }
56  }
57 
58  bool add( char ch ) // return whether ch is a continuation char
59  {
60  if ( (ch & _XXoooooo) == _Xooooooo ) // '10xxxxxx'
61  {
62  if ( _cont > 0 )
63  { _wc = (_wc<<6)+(ch & _ooXXXXXX); --_cont; }
64  return true;
65  }
66  if ( _cont > 0 ) // error, else ignore excess chars
67  { _wc = L'?'; _cont = -1; }
68  return false;
69  }
70 
71  wchar_t _wc;
72  char _cont;
73  };
74 
86  struct MbsIterator
87  {
88  MbsIterator( boost::string_ref text_r )
89  : _text( text_r )
90  , _tpos( _text.data() )
91  , _trest( _text.size() )
92  , _tread( 0 )
93  , _cols( size_t(-1) )
94  , _wc( L'\0' )
95  { memset( &_mbstate, 0, sizeof(_mbstate) ); operator++(); }
96 
98  wchar_t & operator*() { return _wc; }
99  const wchar_t & operator*() const { return _wc; }
100 
101  const char * pos() const { return _tpos; }
102  size_t size() const { return _tread; }
103  size_t columns() const
104  {
105  if ( _cols == size_t(-1) )
106  {
107  if ( _wc < L' ' )
108  _cols = 0; // CTRLs
109  else
110  {
111  _cols = ::wcwidth( _wc );
112  if ( _cols == size_t(-1) )
113  _cols = 1; // -1 due to LC_CTYPE?
114  }
115  }
116  return _cols;
117  }
118 
119  boost::string_ref ref() const { return boost::string_ref( _tpos, _tread ); }
120 
121  bool atEnd() const { return _trest == 0; }
122  bool isNL() const { return( _wc == L'\n' ); }
123  bool isWS() const { return( _wc == L' ' ); }
124  bool isCH() const { return !( atEnd() || isNL() || isWS() ); }
125  bool isSGR() const { return not atEnd() && _wc == L'\033'; }
126 
128  {
129  if ( !atEnd() )
130  {
131  _tpos += _tread;
132  _trest -= _tread;
133 
134  //we hit the end
135  if ( _trest == 0 ) {
136  setToEnd();
137  return *this;
138  }
139 
140  _tread = ::mbrtowc( &_wc, _tpos, _trest, &_mbstate );
141 
142  _cols = size_t(-1);
143 
144  if ( _tread >= (size_t)-2 )
145  {
146  // common case is -1 due to LC_CTYPE
147  // skip this and continue with next mb
148  memset( &_mbstate, 0, sizeof(_mbstate) );
149  _tread = 1;
150  MbToWc c( *_tpos );
151  while ( ( _tread < _trest ) && c.add( *(_tpos+_tread) ) )
152  _tread += 1;
153  _wc = c._wc;
154  }
155 
156  switch ( _tread )
157  {
158 // case (size_t)-2:
159 // case (size_t)-1:
160 // _tread = 0;
161 // // fall through
162  case 0:
163  setToEnd();
164  break;
165 
166  default:
167  if ( ::iswspace(_wc) )
168  {
169  switch ( _wc )
170  {
171  case L'\n':
172  case L' ':
173  break;
174  default:
175  _wc = L' ';
176  }
177  }
178  else if ( _wc == L'\033' ) // ansi SGR ?
179  {
180  unsigned asize = ansiSize( _tpos );
181  if ( asize && asize <= _trest )
182  _tread = asize;
183  }
184  break;
185  }
186  }
187  return *this;
188  }
189 
190  private:
191  unsigned ansiSize( const char * pos_r )
192  {
193  unsigned ret = 0;
194  const char * p = pos_r;
195  if ( *p == '\033' && *(++p) == '[' )
196  {
197  for ( char ch = *(++p); ( '0' <= ch && ch <= '9' ) || ch ==';'; ch = *(++p) )
198  {;}
199  if ( *p == 'm' )
200  ret = p+1 - pos_r;
201  }
202  return ret;
203  }
204 
205  void setToEnd ()
206  {
207  _trest = 0; // atEnd
208  _wc = L'\0';
209  }
210 
211  boost::string_ref _text;
212  const char * _tpos; // start of last ::mbrtowc
213  size_t _trest; // _tpos to end of string
214  size_t _tread; // consumed in last ::mbrtowc
215  mutable size_t _cols; // number of columns occupied on screen
216 
217  wchar_t _wc; // result of last ::mbrtowc
218  mbstate_t _mbstate;
219  };
220 
226  {
227  MbsIteratorNoSGR( boost::string_ref text_r )
228  : MbsIterator( text_r )
229  { skipSGR(); }
230 
232  {
234  skipSGR();
235  return *this;
236  }
237 
238  private:
239  void skipSGR()
240  { while ( isSGR() ) MbsIterator::operator++(); }
241  };
242 
261  {
262  MbsWriteWrapped( std::ostream & out )
263  : MbsWriteWrapped( out, 0, 0 )
264  {}
265 
266  MbsWriteWrapped( std::ostream & out, size_t wrap_r )
267  : MbsWriteWrapped( out, 0, wrap_r )
268  {}
269 
270  MbsWriteWrapped( std::ostream & out, size_t indent_r, size_t wrap_r, int indentFix_r = 0 )
271  : _out( out )
272  , _defaultWrap( wrap_r )
273  , _defaultIndent( indent_r )
274  , _defaultIndentFix( indentFix_r )
277  , _indentGap( 0 )
278  , _lpos( 0 )
279  , _gap( 0 )
280  , _gapForced( 0 )
281  , _gapLines( 0 )
282  , _word( nullptr )
283  , _wSize( 0 )
284  , _wColumns( 0 )
285  {}
286 
287  size_t defaultWrap() const { return _defaultWrap; }
288  size_t defaultIndent() const { return _defaultIndent; }
289  int defaultIndentFix() const { return _defaultIndentFix; }
290 
291  size_t indent() const { return _indent; }
292  size_t lpos() const { return _lpos; }
293 
294  bool atLineBegin() const { return( _lpos == 0 ); }
295  bool atParBegin() const { return( _lpos == 0 && !_gapLines ); }
296 
299  {
300  clearGap();
301  clearWord();
302  clearIndent();
303  _lpos = 0;
304  }
305 
306 
308  void gotoNextPar()
309  {
310  writeout(true);
311  clearIndent();
312 #if ( ZYPPER_TRACE_MBS)
313  _out << "<NL>" << std::endl; // "<NL>"
314 #else
315  _out << std::endl; // "<NL>"
316 #endif
317  _lpos = 0;
318  }
319 
322  { if ( ! atParBegin() ) gotoNextPar(); }
323 
324 
326  void gotoNextLine( size_t count_r = 1 )
327  {
328  if ( count_r )
329  {
330  writeout(true); // but keep indent
331  _gapLines += count_r;
332 #if ( ZYPPER_TRACE_MBS )
333  while ( count_r-- )
334  _out << "<BR>" << std::endl; // "<BR>"
335 #else
336  _out << std::string( count_r, '\n' );
337 #endif
338  _lpos = 0;
339  }
340  }
341 
344  { if ( ! atLineBegin() ) gotoNextLine(); }
345 
346 
349  {
350  ScopedIndentIncrement( MbsWriteWrapped & mww_r, size_t increment_r )
351  : DtorReset( mww_r._indent )
352  { mww_r._indent = mww_r.saneIncrementIndent( increment_r ); }
353  };
356  { return ScopedIndentIncrement( *this, increment_r ); }
357 
358 
360  void startPar( boost::string_ref text_r )
361  {
362  gotoParBegin();
363  write( text_r );
364  }
366  void startPar( boost::string_ref text_r, size_t increment_r )
367  { ScopedIndentIncrement s1( *this, increment_r ); startPar( text_r ); }
368 
369 
371  void addString( boost::string_ref text_r )
372  {
373  write( text_r );
374  }
376  void addString( boost::string_ref text_r, size_t increment_r )
377  { ScopedIndentIncrement s1( *this, increment_r ); addString( text_r ); }
378 
379 
381  void writeText( boost::string_ref text_r )
382  {
383  _gapForced = 1;
384  write( text_r );
385  }
387  void writeText( boost::string_ref text_r, size_t increment_r )
388  { ScopedIndentIncrement s1( *this, increment_r ); writeText( text_r ); }
389 
390 
392  void writePar( boost::string_ref text_r )
393  {
394  gotoParBegin();
395  write( text_r );
396  gotoParBegin();
397  }
399  void writePar( boost::string_ref text_r, size_t increment_r )
400  { ScopedIndentIncrement s1( *this, increment_r ); writePar( text_r ); }
401 
402 
407  void writeDefinition( boost::string_ref tag_r, boost::string_ref text_r, size_t tagincr_r, size_t textincr_r )
408  {
409  gotoParBegin();
410  {
411  ScopedIndentIncrement s1( *this, tagincr_r );
412  write( tag_r, /*leadingWSindents_r*/false );
413  }
414  {
415  ScopedIndentIncrement s1( *this, textincr_r );
416  if ( _lpos < _indent )
418  else
419  gotoNextLine();
420  write( text_r );
421  }
422  gotoParBegin();
423  }
425  void writeDefinition( boost::string_ref tag_r, boost::string_ref text_r )
426  { writeDefinition( tag_r, text_r, 0, 28 ); }
428  void writeDefinition( boost::string_ref tag_r, boost::string_ref text_r, size_t tagincr_r, size_t textincr_r, size_t increment_r )
429  { ScopedIndentIncrement s1( *this, increment_r ); writeDefinition( tag_r, text_r, tagincr_r, textincr_r ); }
431  void writeDefinition( boost::string_ref tag_r, boost::string_ref text_r, size_t increment_r )
432  { ScopedIndentIncrement s1( *this, increment_r ); writeDefinition( tag_r, text_r ); }
433 
434 
435  private:
439  void write( boost::string_ref text_r, bool leadingWSindents_r = true )
440  {
441  for( MbsIterator it( text_r ); ! it.atEnd(); ++it )
442  {
443  if ( it.isNL() )
444  {
445  gotoNextPar(); // write out any pending word and start new par
446  }
447  else if ( it.isWS() )
448  {
449  if ( _word ) // write out pending word and start new gap
450  {
451  writeout();
452  ++_gap;
453  }
454  else
455  {
456  if ( atParBegin() && leadingWSindents_r ) // ws at par begin may increment indent
457  ++_indentGap;
458  else
459  ++_gap;
460  }
461  }
462  else // non WS // remember in word
463  {
464  if ( !_word )
465  _word = it.pos();
466  _wSize += it.size();
467  _wColumns += it.columns();
468  }
469  }
470  writeout(); // write out any pending word; gaps are remembered for next text
471  }
472 
477  void writeout( bool force_r = false )
478  {
479  if ( _word )
480  {
482  // reset gaps and word
483  clearGap();
484  clearWord();
485  }
486  else if ( force_r )
487  clearGap();
488  }
489 
491  {
492  // NOTE: we are either atLineBegin or in a _gap between words
493  if ( !atLineBegin() )
494  {
495  if ( _gap < _gapForced )
496  _gap = _gapForced;
498  {
499  _out << std::string( _gap, ' ' ) << boost::string_ref( _word, _wSize );
500  _lpos += _gap + _wColumns;
501  return;
502  }
503  // Here: did not fit on this line
504  // suppress gap and write indented on next line
505  clearGap();
506  _out << std::endl;
507  _lpos = 0;
508  }
509 
510  // Here: atLineBegin
511  unsigned useIndent = fixIndent( _indent + _indentGap + _gap );
512 
513  if ( _defaultWrap ) // fix large indent
514  { while ( useIndent >= _defaultWrap ) useIndent -= _defaultWrap; }
515 
516  // Try writing the whole word
517  if ( !_defaultWrap || useIndent+_wColumns <= _defaultWrap )
518  {
519  _out << std::string( useIndent, ' ' ) << boost::string_ref( _word, _wSize );
520  _lpos += useIndent + _wColumns;
521  return;
522  }
523 
524  // Still here: word is too big, we need to split it :(
525  for( MbsIterator it( boost::string_ref( _word, _wSize ) ); ! it.atEnd(); ++it )
526  {
527  if ( atLineBegin() )
528  {
529  _out << std::string( useIndent, ' ' );
530  _lpos += useIndent;
531  }
532  _out << it.ref();
533  ++_lpos;
534  if ( _lpos >= _defaultWrap )
535  {
536  _out << std::endl;
537  _lpos = 0;
538  }
539  }
540  }
541 
543  static size_t fixIndent( size_t indent_r, int indentFix_r )
544  {
545  if ( indentFix_r )
546  {
547  if ( indentFix_r < 0 && ( size_t(-indentFix_r) >= indent_r ) )
548  indent_r = 0;
549  else
550  indent_r += indentFix_r;
551  }
552  return indent_r;
553  }
554 
556  size_t fixIndent( size_t indent_r )
557  {
558  indent_r = fixIndent( indent_r, _indentFix );
559  _indentFix = 0;
560  return indent_r;
561  }
562 
564  static size_t saneIncrementIndent( size_t current_r, size_t increment_r, size_t wrap_r )
565  {
566  if ( ! increment_r )
567  return current_r;
568 
569  increment_r += current_r;
570  if ( wrap_r && current_r < wrap_r )
571  {
572  size_t limit = current_r + ( (wrap_r-current_r)/2 );
573  if ( limit < increment_r )
574  increment_r = limit;
575  }
576  return increment_r;
577  }
578 
580  size_t saneIncrementIndent( size_t increment_r )
581  { return saneIncrementIndent( _indent, increment_r, _defaultWrap ); }
582 
584  void clearIndent()
586 
588  void clearGap()
589  { _gap = 0; _gapForced = 0; _gapLines = 0; }
590 
592  void clearWord()
593  { _word = nullptr; _wSize = 0; _wColumns = 0; }
594 
595  std::ostream & _out;
596  const size_t _defaultWrap;
597  const size_t _defaultIndent;
598  const int _defaultIndentFix;
599 
600  size_t _indent; // base indent for par
601  int _indentFix; // indent correction for a pars 1st line
602  size_t _indentGap; // additional indent for current par
603 
604  size_t _lpos; // cursor pos on current line
605 
606  size_t _gap; // amount of WS before next word
607  size_t _gapForced; // forced WS before next word
608  size_t _gapLines; // forced NL before next word/par
609 
610  const char * _word; // current word start in text
611  size_t _wSize; // current word size in byte
612  size_t _wColumns; // current word screen columns
613  };
614 #undef ZYPPER_TRACE_MBS
615 } // namespace mbs
617 
631 inline void mbs_write_wrapped( std::ostream & out, boost::string_ref text_r, size_t indent_r, size_t wrap_r, int indentFix_r = 0 )
632 {
633  mbs::MbsWriteWrapped mww( out, indent_r, wrap_r, indentFix_r );
634  mww.addString( text_r );
635 }
637 inline void mbs_write_wrapped( std::ostream & out, const zypp::str::Str & text_r, size_t indent_r, size_t wrap_r, int indentFix_r = 0 )
638 { mbs_write_wrapped( out, text_r.str(), indent_r, wrap_r, indentFix_r ); }
639 
641 inline size_t mbs_width( boost::string_ref text_r )
642 {
643  size_t ret = 0;
644  for( mbs::MbsIterator it( text_r ); ! it.atEnd(); ++it )
645  ret += it.columns();
646  return ret;
647 }
648 
655 std::string mbs_substr_by_width( boost::string_ref text_r, std::string::size_type colpos_r = 0, std::string::size_type collen_r = std::string::npos );
656 
657 }
658 
659 #endif /* ZYPP_UTILS_TEXT_H_ */
wchar_t _wc
Definition: text.h:71
static const char _XXoooooo
Definition: text.h:36
void mbs_write_wrapped(std::ostream &out, boost::string_ref text_r, size_t indent_r, size_t wrap_r, int indentFix_r=0)
Wrap and indent given text and write it to the output stream out.
Definition: text.h:631
size_t defaultIndent() const
Definition: text.h:288
void clearWord()
Set no word pending.
Definition: text.h:592
std::string str() const
Definition: String.h:222
const wchar_t & operator*() const
Definition: text.h:99
static const char _XXXXXooo
Definition: text.h:39
ScopedIndentIncrement(MbsWriteWrapped &mww_r, size_t increment_r)
Definition: text.h:350
void startPar(boost::string_ref text_r)
Write text_r; starting a new paragraph.
Definition: text.h:360
void gotoLineBegin()
Open a new line in this paragraph if not atLineBegin.
Definition: text.h:343
const size_t _defaultWrap
Definition: text.h:596
void clearGap()
Set no gaps.
Definition: text.h:588
bool isSGR() const
Definition: text.h:125
std::string mbs_substr_by_width(boost::string_ref text_r, std::string::size_type colpos_r, std::string::size_type collen_r)
Returns a substring of a multi-byte character string text_r starting at screen column cpos_r and bein...
Definition: text.cc:16
boost::string_ref _text
Definition: text.h:211
std::ostream & _out
Definition: text.h:595
Write MBString optionally wrapped and indented.
Definition: text.h:260
const char * _tpos
Definition: text.h:212
void write(boost::string_ref text_r, bool leadingWSindents_r=true)
Append text_r indented and wrapped at the current position.
Definition: text.h:439
bool atLineBegin() const
Definition: text.h:294
void gotoParBegin()
Open a new paragraph if not atParBegin.
Definition: text.h:321
void addString(boost::string_ref text_r, size_t increment_r)
Definition: text.h:376
size_t saneIncrementIndent(size_t increment_r)
Return incremented _indent, but not more than 50% of the remaining line size if wrapped.
Definition: text.h:580
MbsIteratorNoSGR(boost::string_ref text_r)
Definition: text.h:227
MbsIterator & operator++()
Definition: text.h:127
const char * _word
Definition: text.h:610
MbsWriteWrapped(std::ostream &out)
Definition: text.h:262
MbsWriteWrapped(std::ostream &out, size_t indent_r, size_t wrap_r, int indentFix_r=0)
Definition: text.h:270
Assign a vaiable a certain value when going out of scope.
Definition: dtorreset.h:49
size_t mbs_width(boost::string_ref text_r)
Returns the column width of a multi-byte character string text_r.
Definition: text.h:641
void writeText(boost::string_ref text_r, size_t increment_r)
Definition: text.h:387
void writeDefinition(boost::string_ref tag_r, boost::string_ref text_r)
Definition: text.h:425
static size_t saneIncrementIndent(size_t current_r, size_t increment_r, size_t wrap_r)
Return incremented indent, but not to more than 50% of the remaining line size if wrapped...
Definition: text.h:564
MbToWc(char ch)
Definition: text.h:42
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition: String.h:211
static const char _oooooooo
Definition: text.h:34
size_t lpos() const
Definition: text.h:292
const size_t _defaultIndent
Definition: text.h:597
void writeText(boost::string_ref text_r)
Continue writing text (separated by WS if not atLineBegin).
Definition: text.h:381
int defaultIndentFix() const
Definition: text.h:289
size_t columns() const
Definition: text.h:103
static const char _XXXooooo
Definition: text.h:37
const int _defaultIndentFix
Definition: text.h:598
bool atEnd() const
Definition: text.h:121
bool isNL() const
Definition: text.h:122
size_t fixIndent(size_t indent_r)
Return fixed indent_r (unsets _indentFix)
Definition: text.h:556
void gotoNextPar()
Write out any pending word and start a new par (NL unconditionally)
Definition: text.h:308
size_t size() const
Definition: text.h:102
void writeDefinition(boost::string_ref tag_r, boost::string_ref text_r, size_t increment_r)
Definition: text.h:431
char _cont
Definition: text.h:72
bool add(char ch)
Definition: text.h:58
static const char _Xooooooo
Definition: text.h:35
void clearIndent()
Set default indent at par start (reloads _indentFix)
Definition: text.h:584
bool atParBegin() const
Definition: text.h:295
MbsWriteWrapped(std::ostream &out, size_t wrap_r)
Definition: text.h:266
const char * pos() const
Definition: text.h:101
MbsIteratorNoSGR & operator++()
Definition: text.h:231
Temporarily increase indent.
Definition: text.h:348
static const char _ooXXXXXX
Definition: text.h:40
void startPar(boost::string_ref text_r, size_t increment_r)
Definition: text.h:366
wchar_t & operator*()
Use with care; all WS are faked to either &#39; &#39; or &#39; &#39;.
Definition: text.h:98
unsigned ansiSize(const char *pos_r)
Definition: text.h:191
void resetToParBegin()
Reset housekeeping data to the beginning of a paragraph (does not write anything) ...
Definition: text.h:298
MbsIterator skipping ANSI SGR
Definition: text.h:225
MbsIterator(boost::string_ref text_r)
Definition: text.h:88
void writeout(bool force_r=false)
Write any pending "indent/gap+word" and reset for next word.
Definition: text.h:477
static const char _XXXXoooo
Definition: text.h:38
void writePar(boost::string_ref text_r)
Write text_r; starting a new paragraph and ending it after the text was written.
Definition: text.h:392
ScopedIndentIncrement scopedIndentIncrement(size_t increment_r)
Definition: text.h:355
void writeDefinition(boost::string_ref tag_r, boost::string_ref text_r, size_t tagincr_r, size_t textincr_r)
Write a tag_r with indented definition text_r.
Definition: text.h:407
boost::string_ref ref() const
Definition: text.h:119
bool isCH() const
Definition: text.h:124
void writeDefinition(boost::string_ref tag_r, boost::string_ref text_r, size_t tagincr_r, size_t textincr_r, size_t increment_r)
Definition: text.h:428
size_t defaultWrap() const
Definition: text.h:287
bool isWS() const
Definition: text.h:123
size_t indent() const
Definition: text.h:291
SolvableIdType size_type
Definition: PoolMember.h:126
void gotoNextLine(size_t count_r=1)
Add count_r (1) new lines in this par (BR unconditionally)
Definition: text.h:326
void writePar(boost::string_ref text_r, size_t increment_r)
Definition: text.h:399
mbstate_t _mbstate
Definition: text.h:218
Iterate chars and ANSI SGR in a multi-byte character string.
Definition: text.h:86
static size_t fixIndent(size_t indent_r, int indentFix_r)
Return fixed indent_r.
Definition: text.h:543
void addString(boost::string_ref text_r)
Continue writing text at the current position.
Definition: text.h:371