libzypp 17.37.17
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>
24
25namespace ztui {
26
28namespace 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
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
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 };
354
356 { return ScopedIndentIncrement( *this, increment_r ); }
357
358
360 void startPar( boost::string_ref text_r )
361 {
362 gotoParBegin();
363 write( text_r );
364 }
365
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 }
375
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 }
386
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 }
398
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 }
424
425 void writeDefinition( boost::string_ref tag_r, boost::string_ref text_r )
426 { writeDefinition( tag_r, text_r, 0, 28 ); }
427
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 ); }
430
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 )
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
586
588 void clearGap()
589 { _gap = 0; _gapForced = 0; _gapLines = 0; }
590
593 { _word = nullptr; _wSize = 0; _wColumns = 0; }
594
595 std::ostream & _out;
596 const size_t _defaultWrap;
597 const size_t _defaultIndent;
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
616
617
631inline 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}
636
637inline 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
641inline 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
655std::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_ */
Assign a vaiable a certain value when going out of scope.
Definition dtorreset.h:50
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
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
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
static const char _XXoooooo
Definition text.h:36
static const char _oooooooo
Definition text.h:34
static const char _XXXXoooo
Definition text.h:38
bool add(char ch)
Definition text.h:58
static const char _XXXXXooo
Definition text.h:39
MbToWc(char ch)
Definition text.h:42
static const char _Xooooooo
Definition text.h:35
static const char _XXXooooo
Definition text.h:37
wchar_t _wc
Definition text.h:71
static const char _ooXXXXXX
Definition text.h:40
MbsIteratorNoSGR & operator++()
Definition text.h:231
MbsIteratorNoSGR(boost::string_ref text_r)
Definition text.h:227
Iterate chars and ANSI SGR in a multi-byte character string.
Definition text.h:87
MbsIterator(boost::string_ref text_r)
Definition text.h:88
bool isNL() const
Definition text.h:122
const wchar_t & operator*() const
Definition text.h:99
wchar_t & operator*()
Use with care; all WS are faked to either ' ' or ' '.
Definition text.h:98
bool atEnd() const
Definition text.h:121
boost::string_ref _text
Definition text.h:211
const char * pos() const
Definition text.h:101
unsigned ansiSize(const char *pos_r)
Definition text.h:191
boost::string_ref ref() const
Definition text.h:119
bool isWS() const
Definition text.h:123
mbstate_t _mbstate
Definition text.h:218
bool isCH() const
Definition text.h:124
const char * _tpos
Definition text.h:212
size_t columns() const
Definition text.h:103
bool isSGR() const
Definition text.h:125
size_t size() const
Definition text.h:102
MbsIterator & operator++()
Definition text.h:127
ScopedIndentIncrement(MbsWriteWrapped &mww_r, size_t increment_r)
Definition text.h:350
Write MBString optionally wrapped and indented.
Definition text.h:261
void gotoParBegin()
Open a new paragraph if not atParBegin.
Definition text.h:321
void startPar(boost::string_ref text_r)
Write text_r; starting a new paragraph.
Definition text.h:360
void gotoNextLine(size_t count_r=1)
Add count_r (1) new lines in this par (BR unconditionally)
Definition text.h:326
MbsWriteWrapped(std::ostream &out, size_t wrap_r)
Definition text.h:266
void writeText(boost::string_ref text_r)
Continue writing text (separated by WS if not atLineBegin).
Definition text.h:381
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
void resetToParBegin()
Reset housekeeping data to the beginning of a paragraph (does not write anything)
Definition text.h:298
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 writeDefinition(boost::string_ref tag_r, boost::string_ref text_r)
Definition text.h:425
void startPar(boost::string_ref text_r, size_t increment_r)
Definition text.h:366
void writeText(boost::string_ref text_r, size_t increment_r)
Definition text.h:387
int defaultIndentFix() const
Definition text.h:289
void writeout(bool force_r=false)
Write any pending "indent/gap+word" and reset for next word.
Definition text.h:477
MbsWriteWrapped(std::ostream &out, size_t indent_r, size_t wrap_r, int indentFix_r=0)
Definition text.h:270
bool atParBegin() const
Definition text.h:295
MbsWriteWrapped(std::ostream &out)
Definition text.h:262
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
void writeDefinition(boost::string_ref tag_r, boost::string_ref text_r, size_t increment_r)
Definition text.h:431
const char * _word
Definition text.h:610
bool atLineBegin() const
Definition text.h:294
size_t indent() const
Definition text.h:291
size_t fixIndent(size_t indent_r)
Return fixed indent_r (unsets _indentFix)
Definition text.h:556
size_t defaultIndent() const
Definition text.h:288
size_t lpos() const
Definition text.h:292
size_t defaultWrap() const
Definition text.h:287
const int _defaultIndentFix
Definition text.h:598
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
static size_t fixIndent(size_t indent_r, int indentFix_r)
Return fixed indent_r.
Definition text.h:543
void writePar(boost::string_ref text_r, size_t increment_r)
Definition text.h:399
void clearGap()
Set no gaps.
Definition text.h:588
std::ostream & _out
Definition text.h:595
void addString(boost::string_ref text_r)
Continue writing text at the current position.
Definition text.h:371
const size_t _defaultIndent
Definition text.h:597
ScopedIndentIncrement scopedIndentIncrement(size_t increment_r)
Temporarily increase indent.
Definition text.h:355
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
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
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
void clearIndent()
Set default indent at par start (reloads _indentFix)
Definition text.h:584
void addString(boost::string_ref text_r, size_t increment_r)
Definition text.h:376
void gotoNextPar()
Write out any pending word and start a new par (NL unconditionally)
Definition text.h:308
void clearWord()
Set no word pending.
Definition text.h:592
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:213
std::string str() const
Definition String.h:223