E-MailRelay
gstr.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gstr.cpp
19///
20
21#include "gdef.h"
22#include "gstr.h"
23#include "gassert.h"
24#include <algorithm>
25#include <type_traits> // std::make_unsigned
26#include <stdexcept>
27#include <iterator>
28#include <limits>
29#include <functional>
30#include <iomanip>
31#include <string>
32#include <sstream>
33#include <cstring>
34#include <cerrno>
35
36namespace G
37{
38 namespace StrImp /// An implementation namespace for G::Str.
39 {
40 static constexpr string_view chars_meta( "~<>[]*$|?\\(){}\"`'&;=" , nullptr ) ; // bash meta-chars plus "~"
41
42 static constexpr string_view chars_alnum( "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
43 "0123456789" "abcdefghijklmnopqrstuvwxyz" , nullptr ) ;
44 static_assert( chars_alnum.size() == 26U+10U+26U , "" ) ;
45
46 static constexpr string_view chars_hexmap( "0123456789abcdef" , nullptr ) ;
47 static_assert( chars_hexmap.size() == 16U , "" ) ;
48
49 static constexpr string_view chars_ws( " \t\n\r" , nullptr ) ;
50 static_assert( chars_ws.size() == 4U , "" ) ;
51
52 bool isDigit( char c ) ;
53 bool isHex( char c ) ;
54 bool isPrintableAscii( char c ) ;
55 char toLower( char c ) ;
56 char toUpper( char c ) ;
57 unsigned short toUShort( const std::string & s , bool & overflow , bool & invalid ) ;
58 unsigned long toULong( const std::string & s , bool & overflow , bool & invalid ) ;
59 unsigned long toULongHex( const std::string & s , bool limited ) ;
60 unsigned int toUInt( const std::string & s , bool & overflow , bool & invalid ) ;
61 short toShort( const std::string & s , bool & overflow , bool & invalid ) ;
62 long toLong( const std::string & s , bool & overflow , bool & invalid ) ;
63 int toInt( const std::string & s , bool & overflow , bool & invalid ) ;
64 void strncpy( char * , const char * , std::size_t ) noexcept ;
65 void escape( std::string & s , char c_escape , const char * specials_in , const char * specials_out ,
66 bool with_nul ) ;
67 void readLineFrom( std::istream & stream , const std::string & eol , std::string & line ) ;
68 template <typename S, typename T, typename SV> void splitIntoTokens( const S & in , T & out , const SV & ws ) ;
69 template <typename S, typename T> void splitIntoTokens( const S & in , T & out , const S & ws , typename S::value_type esc ) ;
70 template <typename T> void splitIntoFields( const std::string & in , T & out , string_view ws ) ;
71 template <typename T> void splitIntoFields( const std::string & in_in , T & out , string_view ws ,
72 char escape , bool remove_escapes ) ;
73 template <typename T1, typename T2, typename P> bool equal4( T1 p1 , T1 end1 , T2 p2 , T2 end2 , P p ) ;
74 bool ilessc( char c1 , char c2 ) ;
75 bool iless( const std::string & a , const std::string & b ) ;
76 bool imatchc( char c1 , char c2 ) ;
77 bool imatch( const std::string & a , const std::string & b ) ;
78 bool match( const std::string & a , const std::string & b , bool ignore_case ) ;
79 template <typename T, typename V> T unique( T in , T end , V repeat , V replacement ) ;
80 bool inList( StringArray::const_iterator begin , StringArray::const_iterator end ,
81 const std::string & s , bool i ) ;
82 bool notInList( StringArray::const_iterator begin , StringArray::const_iterator end ,
83 const std::string & s , bool i ) ;
84 template <typename T> struct Joiner ;
85 void join( const std::string & , std::string & , const std::string & ) ;
86 template <typename Tout> std::size_t outputHex( Tout out , char c ) ;
87 template <typename Tout> std::size_t outputHex( Tout out , wchar_t c ) ;
88 template <typename Tout, typename Tchar> std::size_t outputPrintable( Tout , Tchar , Tchar , char , bool ) ;
89 struct InPlaceBackInserter ;
90 template <typename Tchar = char> struct PrintableAppender ;
91 }
92}
93
94std::string G::Str::escaped( const std::string & s_in )
95{
96 std::string s( s_in ) ;
97 escape( s ) ;
98 return s ;
99}
100
101std::string G::Str::escaped( const std::string & s_in , char c_escape , const std::string & specials_in ,
102 const std::string & specials_out )
103{
104 std::string s( s_in ) ;
105 escape( s , c_escape , specials_in , specials_out ) ;
106 return s ;
107}
108
109std::string G::Str::escaped( const std::string & s_in , char c_escape , const char * specials_in ,
110 const char * specials_out )
111{
112 std::string s( s_in ) ;
113 escape( s , c_escape , specials_in , specials_out ) ;
114 return s ;
115}
116
117void G::StrImp::escape( std::string & s , char c_escape , const char * specials_in ,
118 const char * specials_out , bool with_nul )
119{
120 G_ASSERT( specials_in != nullptr ) ;
121 G_ASSERT( specials_out != nullptr && (std::strlen(specials_out)-std::strlen(specials_in)) <= 1U ) ;
122 std::size_t pos = 0U ;
123 for(;;)
124 {
125 char c_in = '\0' ;
126 pos = s.find_first_of( specials_in , pos ) ;
127 if( pos != std::string::npos )
128 c_in = s.at( pos ) ;
129 else if( with_nul )
130 pos = s.find( '\0' , pos ) ;
131 if( pos == std::string::npos )
132 break ;
133
134 G_ASSERT( std::strchr(specials_in,c_in) != nullptr ) ;
135 const std::size_t special_index = std::strchr(specials_in,c_in) - specials_in ;
136 G_ASSERT( special_index < std::strlen(specials_out) ) ;
137
138 s.insert( pos , 1U , c_escape ) ;
139 pos++ ;
140 s.at(pos) = specials_out[special_index] ;
141 pos++ ;
142 }
143}
144
145void G::Str::escape( std::string & s )
146{
147 namespace imp = G::StrImp ;
148 imp::escape( s , '\\' , "\\\r\n\t" , "\\rnt0" , true ) ;
149}
150
151void G::Str::escape( std::string & s , char c_escape , const char * specials_in , const char * specials_out )
152{
153 namespace imp = G::StrImp ;
154 bool with_nul = std::strlen(specials_in) != std::strlen(specials_out) ;
155 imp::escape( s , c_escape , specials_in , specials_out , with_nul ) ;
156}
157
158void G::Str::escape( std::string & s , char c_escape , const std::string & specials_in ,
159 const std::string & specials_out )
160{
161 namespace imp = G::StrImp ;
162 G_ASSERT( specials_in.length() == specials_out.length() ) ;
163 bool with_nul = !specials_in.empty() && specials_in.at(specials_in.length()-1U) == '\0' ;
164 imp::escape( s , c_escape , specials_in.c_str() , specials_out.c_str() , with_nul ) ;
165}
166
167std::string G::Str::dequote( const std::string & s , char qq , char esc , string_view ws , string_view nbws )
168{
169 std::string result ;
170 result.reserve( s.size() ) ;
171 bool in_quote = false ;
172 bool escaped = false ;
173 for( char c : s )
174 {
175 if( c == esc && !escaped )
176 {
177 escaped = true ;
178 result.append( 1U , esc ) ;
179 }
180 else
181 {
182 std::size_t wspos = 0 ;
183 if( c == qq && !escaped && !in_quote )
184 {
185 in_quote = true ;
186 }
187 else if( c == qq && !escaped )
188 {
189 in_quote = false ;
190 }
191 else if( in_quote && (wspos=ws.find(c)) != std::string::npos )
192 {
193 if( escaped )
194 {
195 result.append( 1U , nbws.at(wspos) ) ;
196 }
197 else
198 {
199 result.append( 1U , esc ) ;
200 result.append( 1U , c ) ;
201 }
202 }
203 else
204 {
205 result.append( 1U , c ) ;
206 }
207 escaped = false ;
208 }
209 }
210 return result ;
211}
212
213void G::Str::unescape( std::string & s )
214{
215 unescape( s , '\\' , "rnt0" , "\r\n\t" ) ;
216}
217
218void G::Str::unescape( std::string & s , char c_escape , const char * specials_in , const char * specials_out )
219{
220 G_ASSERT( specials_in != nullptr ) ;
221 G_ASSERT( specials_out != nullptr && (std::strlen(specials_in)-std::strlen(specials_out)) <= 1U ) ;
222 bool escaped = false ;
223 auto out = s.begin() ; // output in-place
224 for( char & c_in : s )
225 {
226 const char * specials_in_p = std::strchr( specials_in , c_in ) ;
227 const char * specials_out_p = specials_in_p ? (specials_out+(specials_in_p-specials_in)) : nullptr ;
228 if( escaped && specials_out_p )
229 *out++ = *specials_out_p , escaped = false ;
230 else if( escaped && c_in == c_escape )
231 *out++ = c_escape , escaped = false ;
232 else if( escaped )
233 *out++ = c_in , escaped = false ;
234 else if( c_in == c_escape )
235 escaped = true ;
236 else
237 *out++ = c_in , escaped = false ;
238 }
239 if( out != s.end() ) s.erase( out , s.end() ) ;
240}
241
242std::string G::Str::unescaped( const std::string & s_in )
243{
244 std::string s( s_in ) ;
245 unescape( s ) ;
246 return s ;
247}
248
249void G::Str::replace( std::string & s , char from , char to )
250{
251 for( char & c : s )
252 {
253 if( c == from )
254 c = to ;
255 }
256}
257
258void G::Str:: replace( StringArray & a , char from , char to )
259{
260 for( std::string & s : a )
261 replace( s , from , to ) ;
262}
263
264bool G::Str::replace( std::string & s , const std::string & from , const std::string & to , std::size_t * pos_p )
265{
266 if( from.length() == 0 )
267 return false ;
268
269 std::size_t pos = pos_p == nullptr ? 0 : *pos_p ;
270 if( pos >= s.length() )
271 return false ;
272
273 pos = s.find( from , pos ) ;
274 if( pos == std::string::npos )
275 {
276 return false ;
277 }
278 else
279 {
280 s.replace( pos , from.length() , to ) ;
281 if( pos_p != nullptr )
282 *pos_p = pos + to.length() ;
283 return true ;
284 }
285}
286
287unsigned int G::Str::replaceAll( std::string & s , const std::string & from , const std::string & to )
288{
289 unsigned int count = 0U ;
290 for( std::size_t pos = 0U ; replace(s,from,to,&pos) ; count++ )
291 {;} // no-op
292 return count ;
293}
294
295unsigned int G::Str::replaceAll( std::string & s , const char * from , const char * to )
296{
297 if( s.find(from) != std::string::npos )
298 {
299 unsigned int count = 0U ;
300 std::string f( from ) ;
301 for( std::size_t pos = 0U ; replace(s,f,to,&pos) ; count++ )
302 {;} // no-op
303 return count ;
304 }
305 else
306 {
307 return 0U ;
308 }
309}
310
311std::string G::Str::replaced( const std::string & s , char from , char to )
312{
313 std::string result( s ) ;
314 replaceAll( result , std::string(1U,from) , std::string(1U,to) ) ;
315 return result ;
316}
317
318void G::Str::removeAll( std::string & s , char c )
319{
320 s.erase( std::remove_if( s.begin() , s.end() , [c](char x){return x==c;} ) , s.end() ) ;
321}
322
323std::string G::Str::only( const std::string & chars , const std::string & s )
324{
325 std::string result ;
326 result.reserve( s.size() ) ;
327 for( char c : s )
328 {
329 if( chars.find(c) != std::string::npos )
330 result.append( 1U , c ) ;
331 }
332 return result ;
333}
334
335std::string & G::Str::trimLeft( std::string & s , string_view ws , std::size_t limit )
336{
337 std::size_t n = s.find_first_not_of( ws.data() , 0U , ws.size() ) ;
338 if( limit != 0U && ( n == std::string::npos || n > limit ) )
339 n = limit >= s.length() ? std::string::npos : limit ;
340 if( n == std::string::npos )
341 s = std::string() ;
342 else if( n != 0U )
343 s.erase( 0U , n ) ;
344 return s ;
345}
346
347std::string & G::Str::trimRight( std::string & s , string_view ws , std::size_t limit )
348{
349 std::size_t n = s.find_last_not_of( ws.data() , std::string::npos , ws.size() ) ;
350 if( limit != 0U && ( n == std::string::npos || s.length() > (limit+n+1U) ) )
351 n = limit >= s.length() ? std::string::npos : (s.length()-limit-1U) ;
352 if( n == std::string::npos )
353 s = std::string() ;
354 else if( (n+1U) != s.length() )
355 s.resize( n + 1U ) ;
356 return s ;
357}
358
359std::string & G::Str::trim( std::string & s , string_view ws )
360{
361 return trimLeft( trimRight(s,ws) , ws ) ;
362}
363
364std::string G::Str::trimmed( const std::string & s_in , string_view ws )
365{
366 std::string s( s_in ) ;
367 return trim( s , ws ) ;
368}
369
370std::string G::Str::trimmed( std::string && s , string_view ws )
371{
372 return std::move( trimLeft(trimRight(s,ws),ws) ) ;
373}
374
375bool G::StrImp::isDigit( char c )
376{
377 auto uc = static_cast<unsigned char>(c) ;
378 return uc >= 48U && uc <= 57U ;
379}
380
381bool G::StrImp::isHex( char c )
382{
383 auto uc = static_cast<unsigned char>(c) ;
384 return ( uc >= 48U && uc <= 57U ) || ( uc >= 65U && uc <= 70U ) || ( uc >= 97U && uc <= 102U ) ;
385}
386
387bool G::StrImp::isPrintableAscii( char c )
388{
389 auto uc = static_cast<unsigned char>(c) ;
390 return uc >= 32U && uc < 127U ;
391}
392
393char G::StrImp::toLower( char c )
394{
395 const auto uc = static_cast<unsigned char>(c) ;
396 return ( uc >= 65U && uc <= 90U ) ? static_cast<char>(c+'\x20') : c ;
397}
398
399char G::StrImp::toUpper( char c )
400{
401 const auto uc = static_cast<unsigned char>(c) ;
402 return ( uc >= 97U && uc <= 122U ) ? static_cast<char>(c-'\x20') : c ;
403}
404
405bool G::Str::isNumeric( const std::string & s , bool allow_minus_sign )
406{
407 namespace imp = G::StrImp ;
408 const auto end = s.end() ;
409 auto p = s.begin() ;
410 if( allow_minus_sign && p != end && *p == '-' ) ++p ;
411 return std::all_of( p , end , imp::isDigit ) ;
412}
413
414bool G::Str::isHex( const std::string & s )
415{
416 namespace imp = G::StrImp ;
417 return std::all_of( s.begin() , s.end() , imp::isHex ) ;
418}
419
420bool G::Str::isPrintableAscii( const std::string & s )
421{
422 namespace imp = G::StrImp ;
423 return std::all_of( s.begin() , s.end() , imp::isPrintableAscii ) ;
424}
425
426bool G::Str::isInt( const std::string & s )
427{
428 namespace imp = G::StrImp ;
429 bool overflow = false ;
430 bool invalid = false ;
431 imp::toInt( s , overflow , invalid ) ;
432 return !overflow && !invalid ;
433}
434
435bool G::Str::isUShort( const std::string & s )
436{
437 namespace imp = G::StrImp ;
438 bool overflow = false ;
439 bool invalid = false ;
440 imp::toUShort( s , overflow , invalid ) ;
441 return !overflow && !invalid ;
442}
443
444bool G::Str::isUInt( const std::string & s )
445{
446 namespace imp = G::StrImp ;
447 bool overflow = false ;
448 bool invalid = false ;
449 imp::toUInt( s , overflow , invalid ) ;
450 return !overflow && !invalid ;
451}
452
453bool G::Str::isULong( const std::string & s )
454{
455 namespace imp = G::StrImp ;
456 bool overflow = false ;
457 bool invalid = false ;
458 imp::toULong( s , overflow , invalid ) ;
459 return !overflow && !invalid ;
460}
461
462std::string G::Str::fromBool( bool b )
463{
464 return b ? "true" : "false" ;
465}
466
467std::string G::Str::fromDouble( double d )
468{
469 std::ostringstream ss ;
470 ss << std::setprecision(16) << d ;
471 return ss.str() ;
472}
473
474bool G::Str::toBool( const std::string & s )
475{
476 std::string str = lower( s ) ;
477 bool result = true ;
478 if( str == "true" )
479 {;}
480 else if( str == "false" )
481 result = false ;
482 else
483 throw InvalidFormat( "expected true/false" , s ) ;
484 return result ;
485}
486
487double G::Str::toDouble( const std::string & s )
488{
489 try
490 {
491 std::size_t end = 0U ;
492 double result = std::stod( s , &end ) ;
493 if( end == 0U || end == s.size() )
494 throw InvalidFormat( "expected floating point number" , s ) ;
495 return result ;
496 }
497 catch( std::invalid_argument & )
498 {
499 throw InvalidFormat( "expected floating point number" , s ) ;
500 }
501 catch( std::out_of_range & )
502 {
503 throw Overflow( s ) ;
504 }
505}
506
507int G::Str::toInt( const std::string & s )
508{
509 namespace imp = G::StrImp ;
510 bool overflow = false ;
511 bool invalid = false ;
512 int result = imp::toInt( s , overflow , invalid ) ;
513 if( invalid )
514 throw InvalidFormat( "expected integer" , s ) ;
515 if( overflow )
516 throw Overflow( s ) ;
517 return result ;
518}
519
520int G::StrImp::toInt( const std::string & s , bool & overflow , bool & invalid )
521{
522 long long_val = toLong( s , overflow , invalid ) ;
523 int result = static_cast<int>( long_val ) ;
524 if( result != long_val )
525 overflow = true ;
526 return result ;
527}
528
529long G::Str::toLong( const std::string & s )
530{
531 namespace imp = G::StrImp ;
532 bool overflow = false ;
533 bool invalid = false ;
534 long result = imp::toLong( s , overflow , invalid ) ;
535 if( invalid )
536 throw InvalidFormat( "expected long integer" , s ) ;
537 if( overflow )
538 throw Overflow( s ) ;
539 return result ;
540}
541
542long G::StrImp::toLong( const std::string & s , bool & overflow , bool & invalid )
543{
544 try
545 {
546 std::size_t end = 0U ;
547 long result = std::stol( s , &end , 10 ) ;
548 if( end == 0U || end != s.size() )
549 invalid = true ;
550 return result ;
551 }
552 catch( std::invalid_argument & )
553 {
554 invalid = true ;
555 return 0L ;
556 }
557 catch( std::out_of_range & )
558 {
559 overflow = true ;
560 return 0L ;
561 }
562}
563
564short G::Str::toShort( const std::string & s )
565{
566 namespace imp = G::StrImp ;
567 bool overflow = false ;
568 bool invalid = false ;
569 short result = imp::toShort( s , overflow , invalid ) ;
570 if( invalid )
571 throw InvalidFormat( "expected short integer" , s ) ;
572 if( overflow )
573 throw Overflow( s ) ;
574 return result ;
575}
576
577short G::StrImp::toShort( const std::string & s , bool & overflow , bool & invalid )
578{
579 long long_val = toLong( s , overflow , invalid ) ;
580 auto result = static_cast<short>( long_val ) ;
581 if( result != long_val )
582 overflow = true ;
583 return result ;
584}
585
586unsigned int G::Str::toUInt( const std::string & s1 , const std::string & s2 )
587{
588 return !s1.empty() && isUInt(s1) ? toUInt(s1) : toUInt(s2) ;
589}
590
591unsigned int G::Str::toUInt( const std::string & s , Limited )
592{
593 namespace imp = G::StrImp ;
594 bool overflow = false ;
595 bool invalid = false ;
596 unsigned int result = imp::toUInt( s , overflow , invalid ) ;
597 if( invalid )
598 throw InvalidFormat( "expected unsigned integer" , s ) ;
599 if( overflow )
600 result = std::numeric_limits<unsigned int>::max() ;
601 return result ;
602}
603
604unsigned int G::Str::toUInt( const std::string & s )
605{
606 namespace imp = G::StrImp ;
607 bool overflow = false ;
608 bool invalid = false ;
609 unsigned int result = imp::toUInt( s , overflow , invalid ) ;
610 if( invalid )
611 throw InvalidFormat( "expected unsigned integer" , s ) ;
612 if( overflow )
613 throw Overflow( s ) ;
614 return result ;
615}
616
617unsigned int G::StrImp::toUInt( const std::string & s , bool & overflow , bool & invalid )
618{
619 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
620 auto result = static_cast<unsigned int>( ulong_val ) ;
621 if( result != ulong_val )
622 overflow = true ;
623 return result ;
624}
625
626unsigned long G::Str::toULong( const std::string & s , Limited )
627{
628 namespace imp = G::StrImp ;
629 bool overflow = false ;
630 bool invalid = false ;
631 unsigned long result = imp::toULong( s , overflow , invalid ) ;
632 if( invalid )
633 throw InvalidFormat( "expected unsigned long integer" , s ) ;
634 if( overflow )
635 result = std::numeric_limits<unsigned long>::max() ;
636 return result ;
637}
638
639unsigned long G::Str::toULong( const std::string & s , Hex , Limited )
640{
641 namespace imp = G::StrImp ;
642 return imp::toULongHex( s , true ) ;
643}
644
645unsigned long G::Str::toULong( const std::string & s , Hex )
646{
647 namespace imp = G::StrImp ;
648 return imp::toULongHex( s , false ) ;
649}
650
651unsigned long G::StrImp::toULongHex( const std::string & s , bool limited )
652{
653 unsigned long n = 0U ;
654 if( s.empty() ) return 0U ;
655 std::size_t i0 = s.find_first_not_of('0') ;
656 if( i0 == std::string::npos ) i0 = 0U ;
657 if( (s.size()-i0) > (sizeof(unsigned long)*2U) )
658 {
659 if( limited ) return ~0UL ;
660 throw Str::Overflow( s ) ;
661 }
662 for( std::size_t i = i0 ; i < s.size() ; i++ )
663 {
664 unsigned int c = static_cast<unsigned char>(s.at(i)) ;
665 if( c >= 97U && c <= 102U ) c -= 87U ;
666 else if( c >= 65U && c <= 70U ) c -= 55U ;
667 else if( c >= 48U && c <= 57U ) c -= 48U ;
668 else throw Str::InvalidFormat( "invalid hexadecimal" , s ) ;
669 n <<= 4U ;
670 n += c ;
671 }
672 return n ;
673}
674
675unsigned long G::Str::toULong( const std::string & s )
676{
677 namespace imp = G::StrImp ;
678 bool overflow = false ;
679 bool invalid = false ;
680 unsigned long result = imp::toULong( s , overflow , invalid ) ;
681 if( invalid )
682 throw InvalidFormat( "expected unsigned long integer" , s ) ;
683 else if( overflow )
684 throw Overflow( s ) ;
685 return result ;
686}
687
688unsigned long G::Str::toULong( const std::string & s1 , const std::string & s2 )
689{
690 return !s1.empty() && isULong(s1) ? toULong(s1) : toULong(s2) ;
691}
692
693unsigned long G::StrImp::toULong( const std::string & s , bool & overflow , bool & invalid )
694{
695 // note that stoul()/strtoul() do too much in that they skip leading
696 // spaces, allow initial +/- (!) and are C-locale dependent
697 return Str::toUnsigned<unsigned long>( s.data() , s.data()+s.size() , overflow , invalid ) ;
698}
699
700unsigned short G::Str::toUShort( const std::string & s , Limited )
701{
702 namespace imp = G::StrImp ;
703 bool overflow = false ;
704 bool invalid = false ;
705 unsigned short result = imp::toUShort( s , overflow , invalid ) ;
706 if( invalid )
707 throw InvalidFormat( "expected unsigned short integer" , s ) ;
708 if( overflow )
709 result = std::numeric_limits<unsigned short>::max() ;
710 return result ;
711}
712
713unsigned short G::Str::toUShort( const std::string & s )
714{
715 namespace imp = G::StrImp ;
716 bool overflow = false ;
717 bool invalid = false ;
718 unsigned short result = imp::toUShort( s , overflow , invalid ) ;
719 if( invalid )
720 throw InvalidFormat( "expected unsigned short integer" , s ) ;
721 else if( overflow )
722 throw Overflow( s ) ;
723 return result ;
724}
725
726unsigned short G::StrImp::toUShort( const std::string & s , bool & overflow , bool & invalid )
727{
728 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
729 auto result = static_cast<unsigned short>( ulong_val ) ;
730 if( result != ulong_val )
731 overflow = true ;
732 return result ;
733}
734
735void G::Str::toLower( std::string & s )
736{
737 namespace imp = G::StrImp ;
738 std::transform( s.begin() , s.end() , s.begin() , imp::toLower ) ;
739}
740
741std::string G::Str::lower( const std::string & in )
742{
743 std::string out = in ;
744 toLower( out ) ;
745 return out ;
746}
747
748void G::Str::toUpper( std::string & s )
749{
750 namespace imp = G::StrImp ;
751 std::transform( s.begin() , s.end() , s.begin() , imp::toUpper ) ;
752}
753
754std::string G::Str::upper( const std::string & in )
755{
756 std::string out = in ;
757 toUpper( out ) ;
758 return out ;
759}
760
761template <typename Tout>
762std::size_t G::StrImp::outputHex( Tout out , char c )
763{
764 namespace imp = G::StrImp ;
765 std::size_t n = static_cast<unsigned char>( c ) ;
766 n &= 0xFFU ;
767 *out++ = imp::chars_hexmap[(n>>4U)%16U] ;
768 *out++ = imp::chars_hexmap[(n>>0U)%16U] ;
769 return 2U ;
770}
771
772template <typename Tout>
773std::size_t G::StrImp::outputHex( Tout out , wchar_t c )
774{
775 namespace imp = G::StrImp ;
776 using uwchar_t = typename std::make_unsigned<wchar_t>::type ;
777 std::size_t n = static_cast<uwchar_t>( c ) ;
778 n &= 0xFFFFU ;
779 *out++ = imp::chars_hexmap[(n>>12U)%16U] ;
780 *out++ = imp::chars_hexmap[(n>>8U)%16U] ;
781 *out++ = imp::chars_hexmap[(n>>4U)%16U] ;
782 *out++ = imp::chars_hexmap[(n>>0U)%16U] ;
783 return 4U ;
784}
785
786template <typename Tout, typename Tchar>
787std::size_t G::StrImp::outputPrintable( Tout out , Tchar c , Tchar escape_in , char escape_out , bool eight_bit )
788{
789 using Tuchar = typename std::make_unsigned<Tchar>::type ;
790 const auto uc = static_cast<Tuchar>( c ) ; // NOLINT not bugprone-signed-char-misuse
791 std::size_t n = 1U ;
792 if( c == escape_in )
793 {
794 *out++ = escape_out , n++ ;
795 *out++ = escape_out ;
796 }
797 else if( !eight_bit && uc >= 0x20U && uc < 0x7FU && uc != 0xFFU )
798 {
799 *out++ = static_cast<char>(c) ;
800 }
801 else if( eight_bit && ( ( uc >= 0x20U && uc < 0x7FU ) || uc >= 0xA0 ) && uc != 0xFFU )
802 {
803 *out++ = static_cast<char>(c) ;
804 }
805 else
806 {
807 *out++ = escape_out , n++ ;
808 if( uc == 10U )
809 {
810 *out++ = 'n' ;
811 }
812 else if( uc == 13U )
813 {
814 *out++ = 'r' ;
815 }
816 else if( uc == 9U )
817 {
818 *out++ = 't' ;
819 }
820 else if( uc == 0U )
821 {
822 *out++ = '0' ;
823 }
824 else
825 {
826 *out++ = 'x' ;
827 n += outputHex( out , c ) ;
828 }
829 }
830 return n ;
831}
832
833template <typename Tchar>
834struct G::StrImp::PrintableAppender /// A character appender used by G::Str.
835{
836 std::string & s ;
837 const Tchar escape_in ;
838 const char escape_out ;
839 const bool eight_bit ;
840 PrintableAppender( std::string & s_ , Tchar escape_in_ , char escape_out_ , bool eight_bit_ ) :
841 s(s_) ,
842 escape_in(escape_in_) ,
843 escape_out(escape_out_) ,
844 eight_bit(eight_bit_)
845 {
846 }
847 void operator()( Tchar c )
848 {
849 outputPrintable( std::back_inserter(s) , c , escape_in , escape_out , eight_bit ) ;
850 }
851} ;
852
853struct G::StrImp::InPlaceBackInserter /// A character appender used by G::Str.
854{
855 std::string & m_s ;
856 std::size_t m_pos ;
857 std::size_t m_i{0U} ;
858 InPlaceBackInserter( std::string & s , std::size_t pos ) :
859 m_s(s) ,
860 m_pos(pos)
861 {
862 }
863 InPlaceBackInserter & operator=( char c )
864 {
865 if( m_i == 0U )
866 m_s.at(m_pos) = c ;
867 else
868 m_s.insert( m_pos , 1U , c ) ;
869 return *this ;
870 }
871 InPlaceBackInserter & operator*()
872 {
873 return *this ;
874 }
875 InPlaceBackInserter operator++(int) // NOLINT cert-dcl21-cpp
876 {
877 InPlaceBackInserter old( *this ) ;
878 m_pos++ ;
879 m_i++ ;
880 return old ;
881 }
882 void operator++() = delete ;
883} ;
884
885std::string G::Str::printable( const std::string & in , char escape )
886{
887 namespace imp = G::StrImp ;
888 std::string result ;
889 result.reserve( in.length() + (in.length()/8U) + 1U ) ;
890 std::for_each( in.begin() , in.end() , imp::PrintableAppender<>(result,escape,escape,true) ) ;
891 return result ;
892}
893
894std::string G::Str::printable( std::string && s , char escape )
895{
896 namespace imp = G::StrImp ;
897 for( std::size_t pos = 0U ; pos < s.size() ; )
898 {
899 imp::InPlaceBackInserter out( s , pos ) ;
900 pos += imp::outputPrintable( out , s.at(pos) , escape , escape , true ) ;
901 }
902 return std::move( s ) ;
903}
904
905std::string G::Str::toPrintableAscii( const std::string & in , char escape )
906{
907 namespace imp = G::StrImp ;
908 std::string result ;
909 result.reserve( in.length() + (in.length()/8U) + 1U ) ;
910 std::for_each( in.begin() , in.end() , imp::PrintableAppender<>(result,escape,escape,false) ) ;
911 return result ;
912}
913
914std::string G::Str::toPrintableAscii( const std::wstring & in , wchar_t escape )
915{
916 namespace imp = G::StrImp ;
917 std::string result ;
918 result.reserve( in.length() + (in.length()/8U) + 1U ) ;
919 std::for_each( in.begin() , in.end() ,
920 imp::PrintableAppender<wchar_t>(result,escape,static_cast<char>(escape),false) ) ;
921 return result ;
922}
923
924std::string G::Str::readLineFrom( std::istream & stream , const std::string & eol )
925{
926 std::string result ;
927 readLineFrom( stream , eol.empty() ? std::string(1U,'\n') : eol , result , true ) ;
928 return result ;
929}
930
931void G::StrImp::readLineFrom( std::istream & stream , const std::string & eol , std::string & line )
932{
933 const std::size_t limit = line.max_size() ;
934 const std::size_t eol_length = eol.length() ;
935 const char eol_final = eol.at( eol_length - 1U ) ;
936 std::size_t line_length = line.length() ;
937
938 bool changed = false ;
939 char c = '\0' ;
940 for(;;)
941 {
942 // (maybe optimise by hoisting the sentry and calling rdbuf() methods)
943 stream.get( c ) ;
944
945 if( stream.fail() ) // get(char) always sets the failbit at eof, not necessarily eofbit
946 {
947 // set eofbit, reset failbit -- cf. std::getline() in <string>
948 stream.clear( ( stream.rdstate() & ~std::ios_base::failbit ) | std::ios_base::eofbit ) ;
949 break ;
950 }
951
952 if( line_length == limit ) // pathological case -- see also std::getline()
953 {
954 stream.setstate( std::ios_base::failbit ) ;
955 break ;
956 }
957
958 line.append( 1U , c ) ;
959 changed = true ;
960 ++line_length ;
961
962 if( line_length >= eol_length && c == eol_final )
963 {
964 const std::size_t offset = line_length - eol_length ;
965 if( line.find(eol,offset) == offset )
966 {
967 line.erase(offset) ;
968 break ;
969 }
970 }
971 }
972 if( !changed )
973 stream.setstate( std::ios_base::failbit ) ;
974}
975
976void G::Str::readLineFrom( std::istream & stream , const std::string & eol , std::string & line , bool pre_erase )
977{
978 namespace imp = G::StrImp ;
979 G_ASSERT( eol.length() != 0U ) ;
980
981 if( pre_erase )
982 line.erase() ;
983
984 // this is a special speed optimisation for a two-character terminator with a one-character initial string ;-)
985 if( eol.length() == 2U && eol[0] != eol[1] && line.length() == 1U )
986 {
987 // save the initial character, use std::getline() for speed (terminating
988 // on the second character of the two-character terminator), check that the
989 // one-character terminator was actually part of the required two-character
990 // terminator, remove the first character of the two-character terminator,
991 // and finally re-insert the initial character
992 //
993 const char c = line[0] ;
994 line.erase() ; // since getline() doesnt erase it if already at eof
995 std::getline( stream , line , eol[1] ) ; // fast
996 const std::size_t line_length = line.length() ;
997 bool complete = line_length > 0U && line[line_length-1U] == eol[0] ;
998 if( complete )
999 {
1000 line.resize( line_length - 1U ) ;
1001 line.insert( 0U , &c , 1U ) ;
1002 }
1003 else
1004 {
1005 line.insert( 0U , &c , 1U ) ;
1006 if( stream.good() )
1007 {
1008 line.append( 1U , eol[1] ) ;
1009 imp::readLineFrom( stream , eol , line ) ;
1010 }
1011 }
1012 }
1013 else
1014 {
1015 imp::readLineFrom( stream , eol , line ) ;
1016 }
1017}
1018
1019template <typename S, typename T, typename SV>
1020void G::StrImp::splitIntoTokens( const S & in , T & out , const SV & ws )
1021{
1022 for( std::size_t p = 0U ; p != S::npos ; )
1023 {
1024 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1025 if( p != S::npos )
1026 {
1027 std::size_t end = in.find_first_of( ws.data() , p , ws.size() ) ;
1028 std::size_t len = end == S::npos ? end : (end-p) ;
1029 out.push_back( in.substr(p,len) ) ;
1030 p = end ;
1031 }
1032 }
1033}
1034
1035template <typename S, typename T>
1036void G::StrImp::splitIntoTokens( const S & in , T & out , const S & ws , typename S::value_type esc )
1037{
1038 using string_type = S ;
1039 string_type ews = ws + string_type(1U,esc) ;
1040 for( std::size_t p = 0U ; p != S::npos ; )
1041 {
1042 // find the token start
1043 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1044 if( p == S::npos || ( in.at(p) == esc && (p+1) == in.size() ) )
1045 break ;
1046
1047 // find the token end
1048 std::size_t end = in.find_first_of( ews.data() , p , ews.size() ) ;
1049 while( end != S::npos && end < in.size() && in.at(end) == esc )
1050 end = (end+2) < in.size() ? in.find_first_of( ews.data() , end+2 , ews.size() ) : S::npos ;
1051
1052 // extract the token
1053 std::size_t len = end == std::string::npos ? end : (end-p) ;
1054 string_type w( in.substr(p,len) ) ;
1055
1056 // remove whitespace escapes
1057 for( std::size_t i = 0 ; esc && i < w.size() ; i++ )
1058 {
1059 if( w[i] == esc )
1060 {
1061 if( (i+1) < w.size() && ws.find(w[i+1]) != S::npos )
1062 w.erase( i , 1U ) ;
1063 else
1064 i++ ;
1065 }
1066 }
1067
1068 out.push_back( w ) ;
1069 p = end ;
1070 }
1071}
1072
1073void G::Str::splitIntoTokens( const std::string & in , StringArray & out , string_view ws , char esc )
1074{
1075 namespace imp = G::StrImp ;
1076 if( esc && in.find(esc) != std::string::npos )
1077 imp::splitIntoTokens( in , out , sv_to_string(ws) , esc ) ;
1078 else
1079 imp::splitIntoTokens( in , out , ws ) ;
1080}
1081
1082G::StringArray G::Str::splitIntoTokens( const std::string & in , string_view ws , char esc )
1083{
1084 StringArray out ;
1085 splitIntoTokens( in , out , ws , esc ) ;
1086 return out ;
1087}
1088
1089template <typename T>
1090void G::StrImp::splitIntoFields( const std::string & in , T & out , string_view ws )
1091{
1092 if( in.length() )
1093 {
1094 std::size_t start = 0U ;
1095 std::size_t pos = 0U ;
1096 for(;;)
1097 {
1098 if( pos >= in.length() ) break ;
1099 pos = in.find_first_of( ws.data() , pos , ws.size() ) ;
1100 if( pos == std::string::npos ) break ;
1101 out.push_back( in.substr(start,pos-start) ) ;
1102 pos++ ;
1103 start = pos ;
1104 }
1105 out.push_back( in.substr(start,pos-start) ) ;
1106 }
1107}
1108
1109template <typename T>
1110void G::StrImp::splitIntoFields( const std::string & in_in , T & out , string_view ws ,
1111 char escape , bool remove_escapes )
1112{
1113 std::string ews ; // escape+whitespace
1114 ews.reserve( ws.size() + 1U ) ;
1115 ews.assign( ws.data() , ws.size() ) ;
1116 if( escape != '\0' ) ews.append( 1U , escape ) ;
1117 if( in_in.length() )
1118 {
1119 std::string in = in_in ;
1120 std::size_t start = 0U ;
1121 std::size_t pos = 0U ;
1122 for(;;)
1123 {
1124 if( pos >= in.length() ) break ;
1125 pos = in.find_first_of( ews , pos ) ;
1126 if( pos == std::string::npos ) break ;
1127 if( in.at(pos) == escape )
1128 {
1129 if( remove_escapes )
1130 in.erase( pos , 1U ) ;
1131 else
1132 pos++ ;
1133 pos++ ;
1134 }
1135 else
1136 {
1137 out.push_back( in.substr(start,pos-start) ) ;
1138 pos++ ;
1139 start = pos ;
1140 }
1141 }
1142 out.push_back( in.substr(start,pos-start) ) ;
1143 }
1144}
1145
1146void G::Str::splitIntoFields( const std::string & in , StringArray & out , string_view ws ,
1147 char escape , bool remove_escapes )
1148{
1149 namespace imp = G::StrImp ;
1150 imp::splitIntoFields( in , out , ws , escape , remove_escapes ) ;
1151}
1152
1154{
1155 namespace imp = G::StrImp ;
1156 G::StringArray out ;
1157 imp::splitIntoFields( in , out , ws ) ;
1158 return out ;
1159}
1160
1161template <typename T>
1162struct G::StrImp::Joiner /// A sub-string joiner used by G::Str.
1163{
1164 T & result ;
1165 const T & sep ;
1166 bool & first ;
1167 Joiner( T & result_ , const T & sep_ , bool & first_ ) :
1168 result(result_) ,
1169 sep(sep_) ,
1170 first(first_)
1171 {
1172 first = true ;
1173 }
1174 void operator()( const T & s )
1175 {
1176 if( !first ) result.append( sep ) ;
1177 result.append( s ) ;
1178 first = false ;
1179 }
1180} ;
1181
1182std::string G::Str::join( const std::string & sep , const StringMap & map ,
1183 const std::string & pre_in , const std::string & post )
1184{
1185 namespace imp = G::StrImp ;
1186 std::string pre = pre_in.empty() ? std::string("=") : pre_in ;
1187 std::string result ;
1188 bool first = true ;
1189 imp::Joiner<std::string> joiner( result , sep , first ) ;
1190 for( const auto & map_item : map )
1191 joiner( std::string(map_item.first).append(pre).append(map_item.second).append(post) ) ;
1192 return result ;
1193}
1194
1195std::string G::Str::join( const std::string & sep , const StringArray & strings )
1196{
1197 namespace imp = G::StrImp ;
1198 std::string result ;
1199 bool first = true ;
1200 std::for_each( strings.begin() , strings.end() , imp::Joiner<std::string>(result,sep,first) ) ;
1201 return result ;
1202}
1203
1204std::string G::Str::join( const std::string & sep , const std::set<std::string> & strings )
1205{
1206 namespace imp = G::StrImp ;
1207 std::string result ;
1208 bool first = true ;
1209 std::for_each( strings.begin() , strings.end() , imp::Joiner<std::string>(result,sep,first) ) ;
1210 return result ;
1211}
1212
1213std::string G::Str::join( const std::string & sep , const std::string & s1 , const std::string & s2 ,
1214 const std::string & s3 , const std::string & s4 , const std::string & s5 , const std::string & s6 ,
1215 const std::string & s7 , const std::string & s8 , const std::string & s9 )
1216{
1217 namespace imp = G::StrImp ;
1218 std::string result ;
1219 imp::join( sep , result , s1 ) ;
1220 imp::join( sep , result , s2 ) ;
1221 imp::join( sep , result , s3 ) ;
1222 imp::join( sep , result , s4 ) ;
1223 imp::join( sep , result , s5 ) ;
1224 imp::join( sep , result , s6 ) ;
1225 imp::join( sep , result , s7 ) ;
1226 imp::join( sep , result , s8 ) ;
1227 imp::join( sep , result , s9 ) ;
1228 return result ;
1229}
1230
1231void G::StrImp::join( const std::string & sep , std::string & result , const std::string & s )
1232{
1233 if( !result.empty() && !s.empty() )
1234 result.append( sep ) ;
1235 result.append( s ) ;
1236}
1237
1238std::set<std::string> G::Str::keySet( const StringMap & map )
1239{
1240 std::set<std::string> result ;
1241 std::transform( map.begin() , map.end() , std::inserter(result,result.end()) ,
1242 [](const StringMap::value_type & pair){return pair.first;} ) ;
1243 return result ;
1244}
1245
1247{
1248 StringArray result ;
1249 result.reserve( map.size() ) ;
1250 std::transform( map.begin() , map.end() , std::back_inserter(result) ,
1251 [](const StringMap::value_type & pair){return pair.first;} ) ;
1252 return result ;
1253}
1254
1256{
1257 namespace imp = G::StrImp ;
1258 return imp::chars_ws ;
1259}
1260
1262{
1263 namespace imp = G::StrImp ;
1264 return imp::chars_alnum ;
1265}
1266
1268{
1269 namespace imp = G::StrImp ;
1270 return imp::chars_meta ;
1271}
1272
1273std::string G::Str::head( const std::string & in , std::size_t pos , const std::string & default_ )
1274{
1275 return
1276 pos == std::string::npos ?
1277 default_ :
1278 ( pos == 0U ? std::string() : ( pos >= in.length() ? in : in.substr(0U,pos) ) ) ;
1279}
1280
1281std::string G::Str::head( const std::string & in , const std::string & sep , bool default_empty )
1282{
1283 std::size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1284 return head( in , pos , default_empty ? std::string() : in ) ;
1285}
1286
1287std::string G::Str::tail( const std::string & in , std::size_t pos , const std::string & default_ )
1288{
1289 return
1290 pos == std::string::npos ?
1291 default_ :
1292 ( (pos+1U) >= in.length() ? std::string() : in.substr(pos+1U) ) ;
1293}
1294
1295std::string G::Str::tail( const std::string & in , const std::string & sep , bool default_empty )
1296{
1297 std::size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1298 if( pos != std::string::npos ) pos += (sep.length()-1U) ;
1299 return tail( in , pos , default_empty ? std::string() : in ) ;
1300}
1301
1302bool G::Str::tailMatch( const std::string & in , const std::string & tail )
1303{
1304 return
1305 tail.empty() ||
1306 ( in.length() >= tail.length() && 0 == in.compare(in.length()-tail.length(),tail.length(),tail) ) ;
1307}
1308
1309bool G::Str::tailMatch( const StringArray & in , const std::string & tail )
1310{
1311 return std::any_of( in.begin() , in.end() ,
1312 [&tail](const std::string &x){return tailMatch(x,tail);} ) ;
1313}
1314
1315bool G::Str::headMatch( const std::string & in , const char * head )
1316{
1317 if( head == nullptr || *head == '\0' ) return true ;
1318 std::size_t head_length = std::strlen( head ) ;
1319 return ( in.length() >= head_length && 0 == in.compare(0U,head_length,head) ) ;
1320}
1321
1322bool G::Str::headMatch( const std::string & in , const std::string & head )
1323{
1324 return
1325 head.empty() ||
1326 ( in.length() >= head.length() && 0 == in.compare(0U,head.length(),head) ) ;
1327}
1328
1329bool G::Str::headMatch( const StringArray & in , const std::string & head )
1330{
1331 return std::any_of( in.begin() , in.end() ,
1332 [&head](const std::string &x){return headMatch(x,head);} ) ;
1333}
1334
1335std::string G::Str::headMatchResidue( const StringArray & in , const std::string & head )
1336{
1337 const auto end = in.end() ;
1338 for( auto p = in.begin() ; p != end ; ++p )
1339 {
1340 if( headMatch( *p , head ) )
1341 return (*p).substr( head.length() ) ;
1342 }
1343 return std::string() ;
1344}
1345
1346std::string G::Str::positive()
1347{
1348 return "yes" ;
1349}
1350
1351std::string G::Str::negative()
1352{
1353 return "no" ;
1354}
1355
1356bool G::Str::isPositive( const std::string & s_in )
1357{
1358 std::string s = trimmed( lower(s_in) , ws() ) ;
1359 return !s.empty() && ( s == "y" || s == "yes" || s == "t" || s == "true" || s == "1" || s == "on" ) ;
1360}
1361
1362bool G::Str::isNegative( const std::string & s_in )
1363{
1364 std::string s = trimmed( lower(s_in) , ws() ) ;
1365 return !s.empty() && ( s == "n" || s == "no" || s == "f" || s == "false" || s == "0" || s == "off" ) ;
1366}
1367
1368bool G::Str::match( const std::string & a , const std::string & b )
1369{
1370 return a == b ;
1371}
1372
1373bool G::Str::match( const StringArray & a , const std::string & b )
1374{
1375 return std::find( a.begin() , a.end() , b ) != a.end() ;
1376}
1377
1378template <typename T1, typename T2, typename P>
1379bool G::StrImp::equal4( T1 p1 , T1 end1 , T2 p2 , T2 end2 , P p )
1380{
1381 // (std::equal with four iterators is c++14 or later)
1382 for( ; p1 != end1 && p2 != end2 ; ++p1 , ++p2 )
1383 {
1384 if( !p(*p1,*p2) )
1385 return false ;
1386 }
1387 return p1 == end1 && p2 == end2 ;
1388}
1389
1390bool G::StrImp::ilessc( char c1 , char c2 )
1391{
1392 if( c1 >= 'a' && c1 <= 'z' ) c1 -= '\x20' ;
1393 if( c2 >= 'a' && c2 <= 'z' ) c2 -= '\x20' ;
1394 return c1 < c2 ;
1395}
1396
1397bool G::StrImp::iless( const std::string & a , const std::string & b )
1398{
1399 return std::lexicographical_compare( a.begin() , a.end() , b.begin() , b.end() , StrImp::ilessc ) ;
1400}
1401
1402bool G::Str::iless( const std::string & a , const std::string & b )
1403{
1404 return StrImp::iless( a , b ) ;
1405}
1406
1407bool G::StrImp::imatchc( char c1 , char c2 )
1408{
1409 if( c1 >= 'A' && c1 <= 'Z' ) c1 += '\x20' ;
1410 if( c2 >= 'A' && c2 <= 'Z' ) c2 += '\x20' ;
1411 return c1 == c2 ;
1412}
1413
1414bool G::Str::imatch( char c1 , char c2 )
1415{
1416 return StrImp::imatchc( c1 , c2 ) ;
1417}
1418
1419bool G::StrImp::imatch( const std::string & a , const std::string & b )
1420{
1421 return a.size() == b.size() && equal4( a.begin() , a.end() , b.begin() , b.end() , imatchc ) ;
1422}
1423
1424bool G::StrImp::match( const std::string & a , const std::string & b , bool ignore_case )
1425{
1426 return ignore_case ? imatch(a,b) : (a==b) ;
1427}
1428
1429bool G::Str::imatch( const std::string & a , const std::string & b )
1430{
1431 namespace imp = G::StrImp ;
1432 return imp::imatch( a , b ) ;
1433}
1434
1435bool G::Str::imatch( const StringArray & a , const std::string & b )
1436{
1437 namespace imp = G::StrImp ;
1438 using namespace std::placeholders ;
1439 return std::any_of( a.begin() , a.end() , std::bind(imp::imatch,_1,std::cref(b)) ) ;
1440}
1441
1442std::size_t G::Str::ifind( const std::string & s , const std::string & key , std::size_t pos )
1443{
1444 namespace imp = G::StrImp ;
1445 if( s.empty() || key.empty() || pos > s.length() ) return std::string::npos ;
1446 auto p = std::search( s.begin()+pos , s.end() , key.begin() , key.end() , imp::imatchc ) ; // NOLINT narrowing
1447 return p == s.end() ? std::string::npos : std::distance(s.begin(),p) ;
1448}
1449
1450template <typename T, typename V>
1451T G::StrImp::unique( T in , T end , V repeat , V replacement )
1452{
1453 // (maybe use std::adjacent_find())
1454 T out = in ;
1455 while( in != end )
1456 {
1457 T in_next = in ; ++in_next ;
1458 if( *in == repeat && *in_next == repeat )
1459 {
1460 while( *in == repeat )
1461 ++in ;
1462 *out++ = replacement ;
1463 }
1464 else
1465 {
1466 *out++ = *in++ ;
1467 }
1468 }
1469 return out ; // new end
1470}
1471
1472std::string G::Str::unique( const std::string & s_in , char c , char r )
1473{
1474 namespace imp = G::StrImp ;
1475 std::string s( s_in ) ;
1476 s.erase( imp::unique( s.begin() , s.end() , c , r ) , s.end() ) ;
1477 return s ;
1478}
1479
1480std::string G::Str::unique( const std::string & s , char c )
1481{
1482 return s.find(c) == std::string::npos ? s : unique( s , c , c ) ;
1483}
1484
1485bool G::StrImp::inList( StringArray::const_iterator begin , StringArray::const_iterator end ,
1486 const std::string & s , bool i )
1487{
1488 using namespace std::placeholders ;
1489 return std::any_of( begin , end , std::bind(match,_1,std::cref(s),i) ) ;
1490}
1491
1492bool G::StrImp::notInList( StringArray::const_iterator begin , StringArray::const_iterator end ,
1493 const std::string & s , bool i )
1494{
1495 return !inList( begin , end , s , i ) ;
1496}
1497
1498G::StringArray::iterator G::Str::keepMatch( StringArray::iterator begin , StringArray::iterator end ,
1499 const StringArray & match_list , bool ignore_case )
1500{
1501 namespace imp = G::StrImp ;
1502 using namespace std::placeholders ;
1503 if( match_list.empty() ) return end ;
1504 return std::remove_if( begin , end ,
1505 std::bind(imp::notInList,match_list.begin(),match_list.end(),_1,ignore_case) ) ;
1506}
1507
1508G::StringArray::iterator G::Str::removeMatch( StringArray::iterator begin , StringArray::iterator end ,
1509 const StringArray & match_list , bool ignore_case )
1510{
1511 namespace imp = G::StrImp ;
1512 using namespace std::placeholders ;
1513 return std::remove_if( begin , end ,
1514 std::bind(imp::inList,match_list.begin(),match_list.end(),_1,ignore_case) ) ;
1515}
1516
1517void G::StrImp::strncpy( char * dst , const char * src , std::size_t n ) noexcept
1518{
1519 // (because 'strncpy considered dangerous' analytics)
1520 for( ; n ; n-- , dst++ , src++ )
1521 {
1522 *dst = *src ;
1523 if( *src == '\0' )
1524 break ;
1525 }
1526}
1527
1528errno_t G::Str::strncpy_s( char * dst , std::size_t n_dst , const char * src , std::size_t count ) noexcept
1529{
1530 namespace imp = G::StrImp ;
1531 if( dst == nullptr || n_dst == 0U )
1532 return EINVAL ;
1533
1534 if( src == nullptr )
1535 {
1536 *dst = '\0' ;
1537 return EINVAL ;
1538 }
1539
1540 std::size_t n = std::strlen( src ) ;
1541 if( count != truncate && count < n )
1542 n = count ;
1543
1544 if( count == truncate && n >= n_dst )
1545 {
1546 imp::strncpy( dst , src , n_dst ) ;
1547 dst[n_dst-1U] = '\0' ;
1548 return 0 ; // STRUNCATE
1549 }
1550 else if( n >= n_dst )
1551 {
1552 *dst = '\0' ;
1553 return ERANGE ; // dst too small
1554 }
1555 else
1556 {
1557 imp::strncpy( dst , src , n ) ;
1558 dst[n] = '\0' ;
1559 return 0 ;
1560 }
1561}
1562
static void unescape(std::string &s, char c_escape, const char *specials_in, const char *specials_out)
Unescapes the string by replacing e-e with e, e-special-in with special-out, and e-other with other.
Definition: gstr.cpp:218
static string_view alnum()
Returns a string of seven-bit alphanumeric characters, ie A-Z, a-z and 0-9.
Definition: gstr.cpp:1261
static bool isNegative(const std::string &)
Returns true if the string has a negative meaning, such as "0", "false", "no".
Definition: gstr.cpp:1362
static bool isPositive(const std::string &)
Returns true if the string has a positive meaning, such as "1", "true", "yes".
Definition: gstr.cpp:1356
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1195
static bool imatch(char, char)
Returns true if the two characters are the same, ignoring Latin-1 case.
Definition: gstr.cpp:1414
static bool isNumeric(const std::string &s, bool allow_minus_sign=false)
Returns true if every character is a decimal digit.
Definition: gstr.cpp:405
static bool toBool(const std::string &s)
Converts string 's' to a bool.
Definition: gstr.cpp:474
static void toLower(std::string &s)
Replaces all Latin-1 upper-case characters in string 's' by lower-case characters.
Definition: gstr.cpp:735
static string_view ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1255
static std::set< std::string > keySet(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:1238
static StringArray keys(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:1246
static std::string positive()
Returns a default positive string. See isPositive().
Definition: gstr.cpp:1346
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1287
static void splitIntoFields(const std::string &in, StringArray &out, string_view ws, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:1146
static std::string unescaped(const std::string &s)
Returns the unescape()d version of s.
Definition: gstr.cpp:242
static bool isPrintableAscii(const std::string &s)
Returns true if every character is a 7-bit, non-control character (ie.
Definition: gstr.cpp:420
static double toDouble(const std::string &s)
Converts string 's' to a double.
Definition: gstr.cpp:487
static std::string replaced(const std::string &s, char from, char to)
Returns the string 's' with all occurrences of 'from' replaced by 'to'.
Definition: gstr.cpp:311
static std::string fromBool(bool b)
Converts boolean 'b' to a string.
Definition: gstr.cpp:462
static std::string & trimLeft(std::string &s, string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:335
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:1073
static std::string escaped(const std::string &, char c_escape, const std::string &specials_in, const std::string &specials_out)
Returns the escape()d string.
Definition: gstr.cpp:101
static std::string toPrintableAscii(const std::string &in, char escape='\\')
Returns a 7-bit printable representation of the given input string.
Definition: gstr.cpp:905
static unsigned short toUShort(const std::string &s, Limited)
Converts string 's' to an unsigned short.
Definition: gstr.cpp:700
static std::string unique(const std::string &s, char c, char r)
Returns a string with repeated 'c' characters replaced by one 'r' character.
Definition: gstr.cpp:1472
static bool tailMatch(const std::string &in, const std::string &ending)
Returns true if the string has the given ending (or the given ending is empty).
Definition: gstr.cpp:1302
static bool isULong(const std::string &s)
Returns true if the string can be converted into an unsigned long without throwing an exception.
Definition: gstr.cpp:453
static long toLong(const std::string &s)
Converts string 's' to a long.
Definition: gstr.cpp:529
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string. See also only().
Definition: gstr.cpp:318
static StringArray::iterator removeMatch(StringArray::iterator begin, StringArray::iterator end, const StringArray &match_list, bool ignore_case=false)
Removes items in the begin/end list that match one of the elements in the match-list (blocklist).
Definition: gstr.cpp:1508
static std::string upper(const std::string &s)
Returns a copy of 's' in which all Latin-1 lower-case characters have been replaced by upper-case cha...
Definition: gstr.cpp:754
static std::string negative()
Returns a default negative string. See isNegative().
Definition: gstr.cpp:1351
static bool match(const std::string &, const std::string &)
Returns true if the two strings are the same.
Definition: gstr.cpp:1368
static string_view meta()
Returns a list of shell meta-characters with a tilde as the first character.
Definition: gstr.cpp:1267
static std::string & trimRight(std::string &s, string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:347
static std::string dequote(const std::string &, char qq='\"' , char esc = '\\' , string_view ws = Str::ws() , string_view nbws = Str::ws() )
Dequotes a string by removing unescaped quotes and escaping quoted whitespace, so "qq-aaa-esc-qq-bbb-...
Definition: gstr.cpp:167
static bool isHex(const std::string &s)
Returns true if every character is a hexadecimal digit.
Definition: gstr.cpp:414
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
Definition: gstr.cpp:885
static std::string only(const std::string &allow_chars, const std::string &s)
Returns the 's' with all occurrences of the characters not appearing in the fist string deleted.
Definition: gstr.cpp:323
static bool headMatch(const std::string &in, const std::string &head)
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1322
static std::string headMatchResidue(const StringArray &in, const std::string &head)
Returns the unmatched part of the first string in the array that has the given start.
Definition: gstr.cpp:1335
static void toUpper(std::string &s)
Replaces all Latin-1 lower-case characters in string 's' by upper-case characters.
Definition: gstr.cpp:748
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
Definition: gstr.cpp:287
static int toInt(const std::string &s)
Converts string 's' to an int.
Definition: gstr.cpp:507
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:604
static unsigned long toULong(const std::string &s, Limited)
Converts string 's' to an unsigned long.
Definition: gstr.cpp:626
static std::string fromDouble(double d)
Converts double 'd' to a string.
Definition: gstr.cpp:467
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception.
Definition: gstr.cpp:444
static errno_t strncpy_s(char *dst, std::size_t n_dst, const char *src, std::size_t count) noexcept
Does the same as windows strncpy_s().
Definition: gstr.cpp:1528
static std::string head(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1273
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:364
static bool isInt(const std::string &s)
Returns true if the string can be converted into an integer without throwing an exception.
Definition: gstr.cpp:426
static StringArray::iterator keepMatch(StringArray::iterator begin, StringArray::iterator end, const StringArray &match_list, bool ignore_case=false)
Removes items in the begin/end list that do not match any of the elements in the match-list (whitelis...
Definition: gstr.cpp:1498
static bool replace(std::string &s, const std::string &from, const std::string &to, std::size_t *pos_p=nullptr)
Replaces 'from' with 'to', starting at offset '*pos_p'.
Definition: gstr.cpp:264
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 upper-case characters have been replaced by lower-case cha...
Definition: gstr.cpp:741
static bool iless(const std::string &, const std::string &)
Returns true if the first string is lexicographically less than the first, after Latin-1 lower-case l...
Definition: gstr.cpp:1402
static void escape(std::string &s, char c_escape, const std::string &specials_in, const std::string &specials_out)
Prefixes each occurrence of one of the special-in characters with the escape character and its corres...
Definition: gstr.cpp:158
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:924
static bool isUShort(const std::string &s)
Returns true if the string can be converted into an unsigned short without throwing an exception.
Definition: gstr.cpp:435
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:359
static short toShort(const std::string &s)
Converts string 's' to a short.
Definition: gstr.cpp:564
static std::size_t ifind(const std::string &s, const std::string &key, std::size_t pos=0U)
Returns the position of the key in 's' using a Latin-1 case-insensitive search.
Definition: gstr.cpp:1442
A class template like c++17's std::basic_string_view.
Definition: gstringview.h:73
An implementation namespace for G::Str.
Definition: gstr.cpp:39
Low-level classes.
Definition: galign.h:28
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:31
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstrings.h:32
A character appender used by G::Str.
Definition: gstr.cpp:854
A sub-string joiner used by G::Str.
Definition: gstr.cpp:1163
A character appender used by G::Str.
Definition: gstr.cpp:835
Overload discrimiator for G::Str::toUWhatever() indicating hexadecimal strings.
Definition: gstr.h:56
Overload discrimiator for G::Str::toUWhatever() requesting a range-limited result.
Definition: gstr.h:53