KiCad PCB EDA Suite
string_utils.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004-2022 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
29#include <clocale>
30#include <cmath>
31#include <fmt/core.h>
32#include <macros.h>
33#include <richio.h> // StrPrintf
34#include <string_utils.h>
35
36
42static const char illegalFileNameChars[] = "\\/:\"<>|";
43
44
45wxString ConvertToNewOverbarNotation( const wxString& aOldStr )
46{
47 wxString newStr;
48 bool inOverbar = false;
49
50 // Don't get tripped up by the legacy empty-string token.
51 if( aOldStr == wxT( "~" ) )
52 return aOldStr;
53
54 newStr.reserve( aOldStr.length() );
55
56 for( wxString::const_iterator chIt = aOldStr.begin(); chIt != aOldStr.end(); ++chIt )
57 {
58 if( *chIt == '~' )
59 {
60 wxString::const_iterator lookahead = chIt + 1;
61
62 if( lookahead != aOldStr.end() && *lookahead == '~' )
63 {
64 if( ++lookahead != aOldStr.end() && *lookahead == '{' )
65 {
66 // This way the subsequent opening curly brace will not start an
67 // overbar.
68 newStr << wxT( "~~{}" );
69 continue;
70 }
71
72 // Two subsequent tildes mean a tilde.
73 newStr << wxT( "~" );
74 ++chIt;
75 continue;
76 }
77 else if( lookahead != aOldStr.end() && *lookahead == '{' )
78 {
79 // Could mean the user wants "{" with an overbar, but more likely this
80 // is a case of double notation conversion. Bail out.
81 return aOldStr;
82 }
83 else
84 {
85 if( inOverbar )
86 {
87 newStr << wxT( "}" );
88 inOverbar = false;
89 }
90 else
91 {
92 newStr << wxT( "~{" );
93 inOverbar = true;
94 }
95
96 continue;
97 }
98 }
99 else if( ( *chIt == ' ' || *chIt == '}' || *chIt == ')' ) && inOverbar )
100 {
101 // Spaces were used to terminate overbar as well
102 newStr << wxT( "}" );
103 inOverbar = false;
104 }
105
106 newStr << *chIt;
107 }
108
109 // Explicitly end the overbar even if there was no terminating '~' in the aOldStr.
110 if( inOverbar )
111 newStr << wxT( "}" );
112
113 return newStr;
114}
115
116
117bool ConvertSmartQuotesAndDashes( wxString* aString )
118{
119 bool retVal = false;
120
121 for( wxString::iterator ii = aString->begin(); ii != aString->end(); ++ii )
122 {
123 if( *ii == L'\u00B4' || *ii == L'\u2018' || *ii == L'\u2019' )
124 {
125 *ii = '\'';
126 retVal = true;
127 }
128 if( *ii == L'\u201C' || *ii == L'\u201D' )
129 {
130 *ii = '"';
131 retVal = true;
132 }
133 if( *ii == L'\u2013' || *ii == L'\u2014' )
134 {
135 *ii = '-';
136 retVal = true;
137 }
138 }
139
140 return retVal;
141}
142
143
144wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext )
145{
146 wxString converted;
147 std::vector<bool> braceStack; // true == formatting construct
148
149 converted.reserve( aSource.length() );
150
151 for( wxUniChar c: aSource )
152 {
153 if( aContext == CTX_NETNAME )
154 {
155 if( c == '/' )
156 converted += wxT( "{slash}" );
157 else if( c == '\n' || c == '\r' )
158 converted += wxEmptyString; // drop
159 else
160 converted += c;
161 }
162 else if( aContext == CTX_LIBID )
163 {
164 if( c == '\\' )
165 converted += wxT( "{backslash}" );
166 else if( c == '<' )
167 converted += wxT( "{lt}" );
168 else if( c == '>' )
169 converted += wxT( "{gt}" );
170 else if( c == ':' )
171 converted += wxT( "{colon}" );
172 else if( c == '\"' )
173 converted += wxT( "{dblquote}" );
174 else if( c == '\n' || c == '\r' )
175 converted += wxEmptyString; // drop
176 else
177 converted += c;
178 }
179 else if( aContext == CTX_IPC )
180 {
181 if( c == '/' )
182 converted += wxT( "{slash}" );
183 else if( c == ',' )
184 converted += wxT( "{comma}" );
185 else if( c == '\"' )
186 converted += wxT( "{dblquote}" );
187 else
188 converted += c;
189 }
190 else if( aContext == CTX_QUOTED_STR )
191 {
192 if( c == '\"' )
193 converted += wxT( "{dblquote}" );
194 else
195 converted += c;
196 }
197 else if( aContext == CTX_JS_STR )
198 {
199 if( c >= 0x7F || c == '\'' || c == '\\' )
200 {
201 unsigned int code = c;
202 char buffer[16];
203 snprintf( buffer, sizeof(buffer), "\\\\u%4.4X", code );
204 converted += buffer;
205 }
206 else
207 {
208 converted += c;
209 }
210 }
211 else if( aContext == CTX_LINE )
212 {
213 if( c == '\n' || c == '\r' )
214 converted += wxT( "{return}" );
215 else
216 converted += c;
217 }
218 else if( aContext == CTX_FILENAME )
219 {
220 if( c == '/' )
221 converted += wxT( "{slash}" );
222 else if( c == '\\' )
223 converted += wxT( "{backslash}" );
224 else if( c == '\"' )
225 converted += wxT( "{dblquote}" );
226 else if( c == '<' )
227 converted += wxT( "{lt}" );
228 else if( c == '>' )
229 converted += wxT( "{gt}" );
230 else if( c == '|' )
231 converted += wxT( "{bar}" );
232 else if( c == ':' )
233 converted += wxT( "{colon}" );
234 else if( c == '\t' )
235 converted += wxT( "{tab}" );
236 else if( c == '\n' || c == '\r' )
237 converted += wxT( "{return}" );
238 else
239 converted += c;
240 }
241 else if( aContext == CTX_NO_SPACE )
242 {
243 if( c == ' ' )
244 converted += wxT( "{space}" );
245 else
246 converted += c;
247 }
248 else if( aContext == CTX_CSV )
249 {
250 if( c == ',' )
251 converted += wxT( "{comma}" );
252 else if( c == '\n' || c == '\r' )
253 converted += wxT( "{return}" );
254 else
255 converted += c;
256 }
257 else
258 {
259 converted += c;
260 }
261 }
262
263 return converted;
264}
265
266
267wxString UnescapeString( const wxString& aSource )
268{
269 size_t sourceLen = aSource.length();
270
271 // smallest escape string is three characters, shortcut everything else
272 if( sourceLen <= 2 )
273 {
274 return aSource;
275 }
276
277 wxString newbuf;
278 newbuf.reserve( sourceLen );
279
280 for( size_t i = 0; i < sourceLen; ++i )
281 {
282 wxUniChar ch = aSource[i];
283
284 if( ( ch == '$' || ch == '~' || ch == '^' || ch == '_' )
285 && i + 1 < sourceLen && aSource[i+1] == '{' )
286 {
287 for( ; i < sourceLen; ++i )
288 {
289 ch = aSource[i];
290 newbuf += ch;
291
292 if( ch == '}' )
293 break;
294 }
295 }
296 else if( ch == '{' )
297 {
298 wxString token;
299 int depth = 1;
300
301 for( i = i + 1; i < sourceLen; ++i )
302 {
303 ch = aSource[i];
304 if( ch == '{' )
305 depth++;
306 else if( ch == '}' )
307 depth--;
308
309 if( depth <= 0 )
310 break;
311 else
312 token.append( ch );
313 }
314
315 if( token == wxT( "dblquote" ) ) newbuf.append( wxT( "\"" ) );
316 else if( token == wxT( "quote" ) ) newbuf.append( wxT( "'" ) );
317 else if( token == wxT( "lt" ) ) newbuf.append( wxT( "<" ) );
318 else if( token == wxT( "gt" ) ) newbuf.append( wxT( ">" ) );
319 else if( token == wxT( "backslash" ) ) newbuf.append( wxT( "\\" ) );
320 else if( token == wxT( "slash" ) ) newbuf.append( wxT( "/" ) );
321 else if( token == wxT( "bar" ) ) newbuf.append( wxT( "|" ) );
322 else if( token == wxT( "comma" ) ) newbuf.append( wxT( "," ) );
323 else if( token == wxT( "colon" ) ) newbuf.append( wxT( ":" ) );
324 else if( token == wxT( "space" ) ) newbuf.append( wxT( " " ) );
325 else if( token == wxT( "dollar" ) ) newbuf.append( wxT( "$" ) );
326 else if( token == wxT( "tab" ) ) newbuf.append( wxT( "\t" ) );
327 else if( token == wxT( "return" ) ) newbuf.append( wxT( "\n" ) );
328 else if( token == wxT( "brace" ) ) newbuf.append( wxT( "{" ) );
329 else if( token.IsEmpty() ) newbuf.append( wxT( "{" ) );
330 else
331 {
332 newbuf.append( wxT( "{" ) + UnescapeString( token ) + wxT( "}" ) );
333 }
334 }
335 else
336 {
337 newbuf.append( ch );
338 }
339 }
340
341 return newbuf;
342}
343
344
345wxString TitleCaps( const wxString& aString )
346{
347 wxArrayString words;
348 wxString result;
349
350 wxStringSplit( aString, words, ' ' );
351
352 result.reserve( aString.length() );
353
354 for( const wxString& word : words )
355 {
356 if( !result.IsEmpty() )
357 result += wxT( " " );
358
359 result += word.Capitalize();
360 }
361
362 return result;
363}
364
365
366int ReadDelimitedText( wxString* aDest, const char* aSource )
367{
368 std::string utf8; // utf8 but without escapes and quotes.
369 bool inside = false;
370 const char* start = aSource;
371 char cc;
372
373 while( (cc = *aSource++) != 0 )
374 {
375 if( cc == '"' )
376 {
377 if( inside )
378 break; // 2nd double quote is end of delimited text
379
380 inside = true; // first delimiter found, make note, do not copy
381 }
382
383 else if( inside )
384 {
385 if( cc == '\\' )
386 {
387 cc = *aSource++;
388
389 if( !cc )
390 break;
391
392 // do no copy the escape byte if it is followed by \ or "
393 if( cc != '"' && cc != '\\' )
394 utf8 += '\\';
395
396 utf8 += cc;
397 }
398 else
399 {
400 utf8 += cc;
401 }
402 }
403 }
404
405 *aDest = FROM_UTF8( utf8.c_str() );
406
407 return aSource - start;
408}
409
410
411int ReadDelimitedText( char* aDest, const char* aSource, int aDestSize )
412{
413 if( aDestSize <= 0 )
414 return 0;
415
416 bool inside = false;
417 const char* start = aSource;
418 char* limit = aDest + aDestSize - 1;
419 char cc;
420
421 while( (cc = *aSource++) != 0 && aDest < limit )
422 {
423 if( cc == '"' )
424 {
425 if( inside )
426 break; // 2nd double quote is end of delimited text
427
428 inside = true; // first delimiter found, make note, do not copy
429 }
430
431 else if( inside )
432 {
433 if( cc == '\\' )
434 {
435 cc = *aSource++;
436
437 if( !cc )
438 break;
439
440 // do no copy the escape byte if it is followed by \ or "
441 if( cc != '"' && cc != '\\' )
442 *aDest++ = '\\';
443
444 if( aDest < limit )
445 *aDest++ = cc;
446 }
447 else
448 {
449 *aDest++ = cc;
450 }
451 }
452 }
453
454 *aDest = 0;
455
456 return aSource - start;
457}
458
459
460std::string EscapedUTF8( const wxString& aString )
461{
462 wxString str = aString;
463
464 // No new-lines allowed in quoted strings
465 str.Replace( wxT( "\r\n" ), wxT( "\r" ) );
466 str.Replace( wxT( "\n" ), wxT( "\r" ) );
467
468 std::string utf8 = TO_UTF8( aString );
469
470 std::string ret;
471
472 ret.reserve( utf8.length() + 2 );
473
474 ret += '"';
475
476 for( std::string::const_iterator it = utf8.begin(); it!=utf8.end(); ++it )
477 {
478 // this escaping strategy is designed to be compatible with ReadDelimitedText():
479 if( *it == '"' )
480 {
481 ret += '\\';
482 ret += '"';
483 }
484 else if( *it == '\\' )
485 {
486 ret += '\\'; // double it up
487 ret += '\\';
488 }
489 else
490 {
491 ret += *it;
492 }
493 }
494
495 ret += '"';
496
497 return ret;
498}
499
500
501wxString EscapeHTML( const wxString& aString )
502{
503 wxString converted;
504
505 converted.reserve( aString.length() );
506
507 for( wxUniChar c : aString )
508 {
509 if( c == '\"' )
510 converted += wxT( "&quot;" );
511 else if( c == '\'' )
512 converted += wxT( "&apos;" );
513 else if( c == '&' )
514 converted += wxT( "&amp;" );
515 else if( c == '<' )
516 converted += wxT( "&lt;" );
517 else if( c == '>' )
518 converted += wxT( "&gt;" );
519 else
520 converted += c;
521 }
522
523 return converted;
524}
525
526
527bool NoPrintableChars( const wxString& aString )
528{
529 wxString tmp = aString;
530
531 return tmp.Trim( true ).Trim( false ).IsEmpty();
532}
533
534
539int PrintableCharCount( const wxString& aString )
540{
541 int char_count = 0;
542 int overbarDepth = -1;
543 int superSubDepth = -1;
544 int braceNesting = 0;
545
546 for( auto chIt = aString.begin(), end = aString.end(); chIt < end; ++chIt )
547 {
548 if( *chIt == '\t' )
549 {
550 // We don't format tabs in bitmap text (where this is currently used), so just
551 // drop them from the count.
552 continue;
553 }
554 else if( *chIt == '^' && superSubDepth == -1 )
555 {
556 auto lookahead = chIt;
557
558 if( ++lookahead != end && *lookahead == '{' )
559 {
560 chIt = lookahead;
561 superSubDepth = braceNesting;
562 braceNesting++;
563 continue;
564 }
565 }
566 else if( *chIt == '_' && superSubDepth == -1 )
567 {
568 auto lookahead = chIt;
569
570 if( ++lookahead != end && *lookahead == '{' )
571 {
572 chIt = lookahead;
573 superSubDepth = braceNesting;
574 braceNesting++;
575 continue;
576 }
577 }
578 else if( *chIt == '~' && overbarDepth == -1 )
579 {
580 auto lookahead = chIt;
581
582 if( ++lookahead != end && *lookahead == '{' )
583 {
584 chIt = lookahead;
585 overbarDepth = braceNesting;
586 braceNesting++;
587 continue;
588 }
589 }
590 else if( *chIt == '{' )
591 {
592 braceNesting++;
593 }
594 else if( *chIt == '}' )
595 {
596 if( braceNesting > 0 )
597 braceNesting--;
598
599 if( braceNesting == superSubDepth )
600 {
601 superSubDepth = -1;
602 continue;
603 }
604
605 if( braceNesting == overbarDepth )
606 {
607 overbarDepth = -1;
608 continue;
609 }
610 }
611
612 char_count++;
613 }
614
615 return char_count;
616}
617
618
619char* StrPurge( char* text )
620{
621 static const char whitespace[] = " \t\n\r\f\v";
622
623 if( text )
624 {
625 while( *text && strchr( whitespace, *text ) )
626 ++text;
627
628 char* cp = text + strlen( text ) - 1;
629
630 while( cp >= text && strchr( whitespace, *cp ) )
631 *cp-- = '\0';
632 }
633
634 return text;
635}
636
637
638char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine )
639{
640 do {
641 if( fgets( Line, SizeLine, File ) == nullptr )
642 return nullptr;
643
644 if( LineNum )
645 *LineNum += 1;
646
647 } while( Line[0] == '#' || Line[0] == '\n' || Line[0] == '\r' || Line[0] == 0 );
648
649 strtok( Line, "\n\r" );
650 return Line;
651}
652
653
654wxString DateAndTime()
655{
656 wxDateTime datetime = wxDateTime::Now();
657
658 datetime.SetCountry( wxDateTime::Country_Default );
659 return datetime.Format( wxDefaultDateTimeFormat, wxDateTime::Local );
660}
661
662
663int StrNumCmp( const wxString& aString1, const wxString& aString2, bool aIgnoreCase )
664{
665 int nb1 = 0, nb2 = 0;
666
667 auto str1 = aString1.begin();
668 auto str2 = aString2.begin();
669
670 while( str1 != aString1.end() && str2 != aString2.end() )
671 {
672 wxUniChar c1 = *str1;
673 wxUniChar c2 = *str2;
674
675 if( wxIsdigit( c1 ) && wxIsdigit( c2 ) ) // Both characters are digits, do numeric compare.
676 {
677 nb1 = 0;
678 nb2 = 0;
679
680 do
681 {
682 c1 = *str1;
683 nb1 = nb1 * 10 + (int) c1 - '0';
684 ++str1;
685 } while( str1 != aString1.end() && wxIsdigit( *str1 ) );
686
687 do
688 {
689 c2 = *str2;
690 nb2 = nb2 * 10 + (int) c2 - '0';
691 ++str2;
692 } while( str2 != aString2.end() && wxIsdigit( *str2 ) );
693
694 if( nb1 < nb2 )
695 return -1;
696
697 if( nb1 > nb2 )
698 return 1;
699
700 c1 = ( str1 != aString1.end() ) ? *str1 : wxUniChar( 0 );
701 c2 = ( str2 != aString2.end() ) ? *str2 : wxUniChar( 0 );
702 }
703
704 // Any numerical comparisons to here are identical.
705 if( aIgnoreCase )
706 {
707 if( wxToupper( c1 ) < wxToupper( c2 ) )
708 return -1;
709
710 if( wxToupper( c1 ) > wxToupper( c2 ) )
711 return 1;
712 }
713 else
714 {
715 if( c1 < c2 )
716 return -1;
717
718 if( c1 > c2 )
719 return 1;
720 }
721
722 if( str1 != aString1.end() )
723 ++str1;
724
725 if( str2 != aString2.end() )
726 ++str2;
727 }
728
729 if( str1 == aString1.end() && str2 != aString2.end() )
730 {
731 return -1; // Identical to here but aString1 is longer.
732 }
733 else if( str1 != aString1.end() && str2 == aString2.end() )
734 {
735 return 1; // Identical to here but aString2 is longer.
736 }
737
738 return 0;
739}
740
741
742bool WildCompareString( const wxString& pattern, const wxString& string_to_tst,
743 bool case_sensitive )
744{
745 const wxChar* cp = nullptr, * mp = nullptr;
746 const wxChar* wild, * str;
747 wxString _pattern, _string_to_tst;
748
749 if( case_sensitive )
750 {
751 wild = pattern.GetData();
752 str = string_to_tst.GetData();
753 }
754 else
755 {
756 _pattern = pattern;
757 _pattern.MakeUpper();
758 _string_to_tst = string_to_tst;
759 _string_to_tst.MakeUpper();
760 wild = _pattern.GetData();
761 str = _string_to_tst.GetData();
762 }
763
764 while( ( *str ) && ( *wild != '*' ) )
765 {
766 if( ( *wild != *str ) && ( *wild != '?' ) )
767 return false;
768
769 wild++;
770 str++;
771 }
772
773 while( *str )
774 {
775 if( *wild == '*' )
776 {
777 if( !*++wild )
778 return 1;
779 mp = wild;
780 cp = str + 1;
781 }
782 else if( ( *wild == *str ) || ( *wild == '?' ) )
783 {
784 wild++;
785 str++;
786 }
787 else
788 {
789 wild = mp;
790 str = cp++;
791 }
792 }
793
794 while( *wild == '*' )
795 {
796 wild++;
797 }
798
799 return !*wild;
800}
801
802
803bool ApplyModifier( double& value, const wxString& aString )
804{
805 static const wxString modifiers( wxT( "pnumkKM" ) );
806
807 if( !aString.length() )
808 return false;
809
810 wxChar modifier;
811 wxString units;
812
813 if( modifiers.Find( aString[ 0 ] ) >= 0 )
814 {
815 modifier = aString[ 0 ];
816 units = aString.Mid( 1 ).Trim();
817 }
818 else
819 {
820 modifier = ' ';
821 units = aString.Mid( 0 ).Trim();
822 }
823
824 if( units.length()
825 && !units.CmpNoCase( wxT( "F" ) )
826 && !units.CmpNoCase( wxT( "hz" ) )
827 && !units.CmpNoCase( wxT( "W" ) )
828 && !units.CmpNoCase( wxT( "V" ) )
829 && !units.CmpNoCase( wxT( "H" ) ) )
830 return false;
831
832 if( modifier == 'p' )
833 value *= 1.0e-12;
834 if( modifier == 'n' )
835 value *= 1.0e-9;
836 else if( modifier == 'u' )
837 value *= 1.0e-6;
838 else if( modifier == 'm' )
839 value *= 1.0e-3;
840 else if( modifier == 'k' || modifier == 'K' )
841 value *= 1.0e3;
842 else if( modifier == 'M' )
843 value *= 1.0e6;
844 else if( modifier == 'G' )
845 value *= 1.0e9;
846
847 return true;
848}
849
850
851int ValueStringCompare( const wxString& strFWord, const wxString& strSWord )
852{
853 // Compare unescaped text
854 wxString fWord = UnescapeString( strFWord );
855 wxString sWord = UnescapeString( strSWord );
856
857 // The different sections of the two strings
858 wxString strFWordBeg, strFWordMid, strFWordEnd;
859 wxString strSWordBeg, strSWordMid, strSWordEnd;
860
861 // Split the two strings into separate parts
862 SplitString( fWord, &strFWordBeg, &strFWordMid, &strFWordEnd );
863 SplitString( sWord, &strSWordBeg, &strSWordMid, &strSWordEnd );
864
865 // Compare the Beginning section of the strings
866 int isEqual = strFWordBeg.CmpNoCase( strSWordBeg );
867
868 if( isEqual > 0 )
869 {
870 return 1;
871 }
872 else if( isEqual < 0 )
873 {
874 return -1;
875 }
876 else
877 {
878 // If the first sections are equal compare their digits
879 double lFirstNumber = 0;
880 double lSecondNumber = 0;
881 bool endingIsModifier = false;
882
883 strFWordMid.ToDouble( &lFirstNumber );
884 strSWordMid.ToDouble( &lSecondNumber );
885
886 endingIsModifier |= ApplyModifier( lFirstNumber, strFWordEnd );
887 endingIsModifier |= ApplyModifier( lSecondNumber, strSWordEnd );
888
889 if( lFirstNumber > lSecondNumber )
890 return 1;
891 else if( lFirstNumber < lSecondNumber )
892 return -1;
893 // If the first two sections are equal and the endings are modifiers then compare them
894 else if( !endingIsModifier )
895 return strFWordEnd.CmpNoCase( strSWordEnd );
896 // Ran out of things to compare; they must match
897 else
898 return 0;
899 }
900}
901
902
903int SplitString( const wxString& strToSplit,
904 wxString* strBeginning,
905 wxString* strDigits,
906 wxString* strEnd )
907{
908 static const wxString separators( wxT( ".," ) );
909
910 // Clear all the return strings
911 strBeginning->Empty();
912 strDigits->Empty();
913 strEnd->Empty();
914
915 // There no need to do anything if the string is empty
916 if( strToSplit.length() == 0 )
917 return 0;
918
919 // Starting at the end of the string look for the first digit
920 int ii;
921
922 for( ii = (strToSplit.length() - 1); ii >= 0; ii-- )
923 {
924 if( wxIsdigit( strToSplit[ii] ) )
925 break;
926 }
927
928 // If there were no digits then just set the single string
929 if( ii < 0 )
930 {
931 *strBeginning = strToSplit;
932 }
933 else
934 {
935 // Since there is at least one digit this is the trailing string
936 *strEnd = strToSplit.substr( ii + 1 );
937
938 // Go to the end of the digits
939 int position = ii + 1;
940
941 for( ; ii >= 0; ii-- )
942 {
943 if( !wxIsdigit( strToSplit[ii] ) && separators.Find( strToSplit[ii] ) < 0 )
944 break;
945 }
946
947 // If all that was left was digits, then just set the digits string
948 if( ii < 0 )
949 *strDigits = strToSplit.substr( 0, position );
950
951 /* We were only looking for the last set of digits everything else is
952 * part of the preamble */
953 else
954 {
955 *strDigits = strToSplit.substr( ii + 1, position - ii - 1 );
956 *strBeginning = strToSplit.substr( 0, ii + 1 );
957 }
958 }
959
960 return 0;
961}
962
963
964int GetTrailingInt( const wxString& aStr )
965{
966 int number = 0;
967 int base = 1;
968
969 // Trim and extract the trailing numeric part
970 int index = aStr.Len() - 1;
971
972 while( index >= 0 )
973 {
974 const char chr = aStr.GetChar( index );
975
976 if( chr < '0' || chr > '9' )
977 break;
978
979 number += ( chr - '0' ) * base;
980 base *= 10;
981 index--;
982 }
983
984 return number;
985}
986
987
989{
991}
992
993
994bool ReplaceIllegalFileNameChars( std::string* aName, int aReplaceChar )
995{
996 bool changed = false;
997 std::string result;
998 result.reserve( aName->length() );
999
1000 for( std::string::iterator it = aName->begin(); it != aName->end(); ++it )
1001 {
1002 if( strchr( illegalFileNameChars, *it ) )
1003 {
1004 if( aReplaceChar )
1005 StrPrintf( &result, "%c", aReplaceChar );
1006 else
1007 StrPrintf( &result, "%%%02x", *it );
1008
1009 changed = true;
1010 }
1011 else
1012 {
1013 result += *it;
1014 }
1015 }
1016
1017 if( changed )
1018 *aName = result;
1019
1020 return changed;
1021}
1022
1023
1024bool ReplaceIllegalFileNameChars( wxString& aName, int aReplaceChar )
1025{
1026 bool changed = false;
1027 wxString result;
1028 result.reserve( aName.Length() );
1029 wxString illWChars = GetIllegalFileNameWxChars();
1030
1031 for( wxString::iterator it = aName.begin(); it != aName.end(); ++it )
1032 {
1033 if( illWChars.Find( *it ) != wxNOT_FOUND )
1034 {
1035 if( aReplaceChar )
1036 result += aReplaceChar;
1037 else
1038 result += wxString::Format( "%%%02x", *it );
1039
1040 changed = true;
1041 }
1042 else
1043 {
1044 result += *it;
1045 }
1046 }
1047
1048 if( changed )
1049 aName = result;
1050
1051 return changed;
1052}
1053
1054
1055void wxStringSplit( const wxString& aText, wxArrayString& aStrings, wxChar aSplitter )
1056{
1057 wxString tmp;
1058
1059 for( unsigned ii = 0; ii < aText.Length(); ii++ )
1060 {
1061 if( aText[ii] == aSplitter )
1062 {
1063 aStrings.Add( tmp );
1064 tmp.Clear();
1065 }
1066 else
1067 {
1068 tmp << aText[ii];
1069 }
1070 }
1071
1072 if( !tmp.IsEmpty() )
1073 aStrings.Add( tmp );
1074}
1075
1076
1077void StripTrailingZeros( wxString& aStringValue, unsigned aTrailingZeroAllowed )
1078{
1079 struct lconv* lc = localeconv();
1080 char sep = lc->decimal_point[0];
1081 unsigned sep_pos = aStringValue.Find( sep );
1082
1083 if( sep_pos > 0 )
1084 {
1085 // We want to keep at least aTrailingZeroAllowed digits after the separator
1086 unsigned min_len = sep_pos + aTrailingZeroAllowed + 1;
1087
1088 while( aStringValue.Len() > min_len )
1089 {
1090 if( aStringValue.Last() == '0' )
1091 aStringValue.RemoveLast();
1092 else
1093 break;
1094 }
1095 }
1096}
1097
1098
1099std::string FormatDouble2Str( double aValue )
1100{
1101 std::string buf;
1102
1103 if( aValue != 0.0 && std::fabs( aValue ) <= 0.0001 )
1104 {
1105 buf = fmt::format( "{:.16f}", aValue );
1106
1107 // remove trailing zeros (and the decimal marker if needed)
1108 while( !buf.empty() && buf[buf.size() - 1] == '0' )
1109 {
1110 buf.pop_back();
1111 }
1112
1113 // if the value was really small
1114 // we may have just stripped all the zeros after the decimal
1115 if( buf[buf.size() - 1] == '.' )
1116 {
1117 buf.pop_back();
1118 }
1119 }
1120 else
1121 {
1122 buf = fmt::format( "{:.10g}", aValue );
1123 }
1124
1125 return buf;
1126}
1127
1128
1129std::string UIDouble2Str( double aValue )
1130{
1131 char buf[50];
1132 int len;
1133
1134 if( aValue != 0.0 && std::fabs( aValue ) <= 0.0001 )
1135 {
1136 // For these small values, %f works fine,
1137 // and %g gives an exponent
1138 len = snprintf( buf, sizeof(buf), "%.16f", aValue );
1139
1140 while( --len > 0 && buf[len] == '0' )
1141 buf[len] = '\0';
1142
1143 if( buf[len] == '.' || buf[len] == ',' )
1144 buf[len] = '\0';
1145 else
1146 ++len;
1147 }
1148 else
1149 {
1150 // For these values, %g works fine, and sometimes %f
1151 // gives a bad value (try aValue = 1.222222222222, with %.16f format!)
1152 len = snprintf( buf, sizeof(buf), "%.10g", aValue );
1153 }
1154
1155 return std::string( buf, len );
1156}
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:84
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString DateAndTime()
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
int GetTrailingInt(const wxString &aStr)
Gets the trailing int, if any, from a string.
wxString UnescapeString(const wxString &aSource)
static const char illegalFileNameChars[]
Illegal file name characters used to ensure file names will be valid on all supported platforms.
wxString GetIllegalFileNameWxChars()
bool ConvertSmartQuotesAndDashes(wxString *aString)
Convert curly quotes and em/en dashes to straight quotes and dashes.
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
int ReadDelimitedText(wxString *aDest, const char *aSource)
Copy bytes from aSource delimited string segment to aDest wxString.
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
wxString TitleCaps(const wxString &aString)
Capitalize the first letter in each word.
std::string UIDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 We want to avoid scientific ...
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
int PrintableCharCount(const wxString &aString)
Return the number of printable (ie: non-formatting) chars.
std::string EscapedUTF8(const wxString &aString)
Return an 8 bit UTF8 string given aString in Unicode form.
char * GetLine(FILE *File, char *Line, int *LineNum, int SizeLine)
Read one line line from aFile.
bool ApplyModifier(double &value, const wxString &aString)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
int SplitString(const wxString &strToSplit, wxString *strBeginning, wxString *strDigits, wxString *strEnd)
Break a string into three parts: he alphabetic preamble, the numeric part, and any alphabetic ending.
void StripTrailingZeros(wxString &aStringValue, unsigned aTrailingZeroAllowed)
Remove trailing zeros from a string containing a converted float number.
char * StrPurge(char *text)
Remove leading and training spaces, tabs and end of line chars in text.
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
ESCAPE_CONTEXT
Escape/Unescape routines to safely encode reserved-characters in various contexts.
Definition: string_utils.h:53
@ CTX_FILENAME
Definition: string_utils.h:61
@ CTX_QUOTED_STR
Definition: string_utils.h:57
@ CTX_LINE
Definition: string_utils.h:59
@ CTX_NO_SPACE
Definition: string_utils.h:62
@ CTX_LIBID
Definition: string_utils.h:55
@ CTX_NETNAME
Definition: string_utils.h:54
@ CTX_CSV
Definition: string_utils.h:60
@ CTX_IPC
Definition: string_utils.h:56
@ CTX_JS_STR
Definition: string_utils.h:58