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 == '\'' )
200 converted += wxT( "{quote}" );
201 else
202 converted += c;
203 }
204 else if( aContext == CTX_LINE )
205 {
206 if( c == '\n' || c == '\r' )
207 converted += wxT( "{return}" );
208 else
209 converted += c;
210 }
211 else if( aContext == CTX_FILENAME )
212 {
213 if( c == '/' )
214 converted += wxT( "{slash}" );
215 else if( c == '\\' )
216 converted += wxT( "{backslash}" );
217 else if( c == '\"' )
218 converted += wxT( "{dblquote}" );
219 else if( c == '<' )
220 converted += wxT( "{lt}" );
221 else if( c == '>' )
222 converted += wxT( "{gt}" );
223 else if( c == '|' )
224 converted += wxT( "{bar}" );
225 else if( c == ':' )
226 converted += wxT( "{colon}" );
227 else if( c == '\t' )
228 converted += wxT( "{tab}" );
229 else if( c == '\n' || c == '\r' )
230 converted += wxT( "{return}" );
231 else
232 converted += c;
233 }
234 else if( aContext == CTX_NO_SPACE )
235 {
236 if( c == ' ' )
237 converted += wxT( "{space}" );
238 else
239 converted += c;
240 }
241 else if( aContext == CTX_CSV )
242 {
243 if( c == ',' )
244 converted += wxT( "{comma}" );
245 else if( c == '\n' || c == '\r' )
246 converted += wxT( "{return}" );
247 else
248 converted += c;
249 }
250 else
251 {
252 converted += c;
253 }
254 }
255
256 return converted;
257}
258
259
260wxString UnescapeString( const wxString& aSource )
261{
262 size_t sourceLen = aSource.length();
263
264 // smallest escape string is three characters, shortcut everything else
265 if( sourceLen <= 2 )
266 {
267 return aSource;
268 }
269
270 wxString newbuf;
271 newbuf.reserve( sourceLen );
272
273 for( size_t i = 0; i < sourceLen; ++i )
274 {
275 wxUniChar ch = aSource[i];
276
277 if( ( ch == '$' || ch == '~' || ch == '^' || ch == '_' )
278 && i + 1 < sourceLen && aSource[i+1] == '{' )
279 {
280 for( ; i < sourceLen; ++i )
281 {
282 ch = aSource[i];
283 newbuf += ch;
284
285 if( ch == '}' )
286 break;
287 }
288 }
289 else if( ch == '{' )
290 {
291 wxString token;
292 int depth = 1;
293
294 for( i = i + 1; i < sourceLen; ++i )
295 {
296 ch = aSource[i];
297 if( ch == '{' )
298 depth++;
299 else if( ch == '}' )
300 depth--;
301
302 if( depth <= 0 )
303 break;
304 else
305 token.append( ch );
306 }
307
308 if( token == wxT( "dblquote" ) ) newbuf.append( wxT( "\"" ) );
309 else if( token == wxT( "quote" ) ) newbuf.append( wxT( "'" ) );
310 else if( token == wxT( "lt" ) ) newbuf.append( wxT( "<" ) );
311 else if( token == wxT( "gt" ) ) newbuf.append( wxT( ">" ) );
312 else if( token == wxT( "backslash" ) ) newbuf.append( wxT( "\\" ) );
313 else if( token == wxT( "slash" ) ) newbuf.append( wxT( "/" ) );
314 else if( token == wxT( "bar" ) ) newbuf.append( wxT( "|" ) );
315 else if( token == wxT( "comma" ) ) newbuf.append( wxT( "," ) );
316 else if( token == wxT( "colon" ) ) newbuf.append( wxT( ":" ) );
317 else if( token == wxT( "space" ) ) newbuf.append( wxT( " " ) );
318 else if( token == wxT( "dollar" ) ) newbuf.append( wxT( "$" ) );
319 else if( token == wxT( "tab" ) ) newbuf.append( wxT( "\t" ) );
320 else if( token == wxT( "return" ) ) newbuf.append( wxT( "\n" ) );
321 else if( token == wxT( "brace" ) ) newbuf.append( wxT( "{" ) );
322 else if( token.IsEmpty() ) newbuf.append( wxT( "{" ) );
323 else
324 {
325 newbuf.append( wxT( "{" ) + UnescapeString( token ) + wxT( "}" ) );
326 }
327 }
328 else
329 {
330 newbuf.append( ch );
331 }
332 }
333
334 return newbuf;
335}
336
337
338wxString TitleCaps( const wxString& aString )
339{
340 wxArrayString words;
341 wxString result;
342
343 wxStringSplit( aString, words, ' ' );
344
345 result.reserve( aString.length() );
346
347 for( const wxString& word : words )
348 {
349 if( !result.IsEmpty() )
350 result += wxT( " " );
351
352 result += word.Capitalize();
353 }
354
355 return result;
356}
357
358
359int ReadDelimitedText( wxString* aDest, const char* aSource )
360{
361 std::string utf8; // utf8 but without escapes and quotes.
362 bool inside = false;
363 const char* start = aSource;
364 char cc;
365
366 while( (cc = *aSource++) != 0 )
367 {
368 if( cc == '"' )
369 {
370 if( inside )
371 break; // 2nd double quote is end of delimited text
372
373 inside = true; // first delimiter found, make note, do not copy
374 }
375
376 else if( inside )
377 {
378 if( cc == '\\' )
379 {
380 cc = *aSource++;
381
382 if( !cc )
383 break;
384
385 // do no copy the escape byte if it is followed by \ or "
386 if( cc != '"' && cc != '\\' )
387 utf8 += '\\';
388
389 utf8 += cc;
390 }
391 else
392 {
393 utf8 += cc;
394 }
395 }
396 }
397
398 *aDest = FROM_UTF8( utf8.c_str() );
399
400 return aSource - start;
401}
402
403
404int ReadDelimitedText( char* aDest, const char* aSource, int aDestSize )
405{
406 if( aDestSize <= 0 )
407 return 0;
408
409 bool inside = false;
410 const char* start = aSource;
411 char* limit = aDest + aDestSize - 1;
412 char cc;
413
414 while( (cc = *aSource++) != 0 && aDest < limit )
415 {
416 if( cc == '"' )
417 {
418 if( inside )
419 break; // 2nd double quote is end of delimited text
420
421 inside = true; // first delimiter found, make note, do not copy
422 }
423
424 else if( inside )
425 {
426 if( cc == '\\' )
427 {
428 cc = *aSource++;
429
430 if( !cc )
431 break;
432
433 // do no copy the escape byte if it is followed by \ or "
434 if( cc != '"' && cc != '\\' )
435 *aDest++ = '\\';
436
437 if( aDest < limit )
438 *aDest++ = cc;
439 }
440 else
441 {
442 *aDest++ = cc;
443 }
444 }
445 }
446
447 *aDest = 0;
448
449 return aSource - start;
450}
451
452
453std::string EscapedUTF8( const wxString& aString )
454{
455 wxString str = aString;
456
457 // No new-lines allowed in quoted strings
458 str.Replace( wxT( "\r\n" ), wxT( "\r" ) );
459 str.Replace( wxT( "\n" ), wxT( "\r" ) );
460
461 std::string utf8 = TO_UTF8( aString );
462
463 std::string ret;
464
465 ret.reserve( utf8.length() + 2 );
466
467 ret += '"';
468
469 for( std::string::const_iterator it = utf8.begin(); it!=utf8.end(); ++it )
470 {
471 // this escaping strategy is designed to be compatible with ReadDelimitedText():
472 if( *it == '"' )
473 {
474 ret += '\\';
475 ret += '"';
476 }
477 else if( *it == '\\' )
478 {
479 ret += '\\'; // double it up
480 ret += '\\';
481 }
482 else
483 {
484 ret += *it;
485 }
486 }
487
488 ret += '"';
489
490 return ret;
491}
492
493
494wxString EscapeHTML( const wxString& aString )
495{
496 wxString converted;
497
498 converted.reserve( aString.length() );
499
500 for( wxUniChar c : aString )
501 {
502 if( c == '\"' )
503 converted += wxT( "&quot;" );
504 else if( c == '\'' )
505 converted += wxT( "&apos;" );
506 else if( c == '&' )
507 converted += wxT( "&amp;" );
508 else if( c == '<' )
509 converted += wxT( "&lt;" );
510 else if( c == '>' )
511 converted += wxT( "&gt;" );
512 else
513 converted += c;
514 }
515
516 return converted;
517}
518
519
520bool NoPrintableChars( const wxString& aString )
521{
522 wxString tmp = aString;
523
524 return tmp.Trim( true ).Trim( false ).IsEmpty();
525}
526
527
532int PrintableCharCount( const wxString& aString )
533{
534 int char_count = 0;
535 int overbarDepth = -1;
536 int superSubDepth = -1;
537 int braceNesting = 0;
538
539 for( auto chIt = aString.begin(), end = aString.end(); chIt < end; ++chIt )
540 {
541 if( *chIt == '\t' )
542 {
543 // We don't format tabs in bitmap text (where this is currently used), so just
544 // drop them from the count.
545 continue;
546 }
547 else if( *chIt == '^' && superSubDepth == -1 )
548 {
549 auto lookahead = chIt;
550
551 if( ++lookahead != end && *lookahead == '{' )
552 {
553 chIt = lookahead;
554 superSubDepth = braceNesting;
555 braceNesting++;
556 continue;
557 }
558 }
559 else if( *chIt == '_' && superSubDepth == -1 )
560 {
561 auto lookahead = chIt;
562
563 if( ++lookahead != end && *lookahead == '{' )
564 {
565 chIt = lookahead;
566 superSubDepth = braceNesting;
567 braceNesting++;
568 continue;
569 }
570 }
571 else if( *chIt == '~' && overbarDepth == -1 )
572 {
573 auto lookahead = chIt;
574
575 if( ++lookahead != end && *lookahead == '{' )
576 {
577 chIt = lookahead;
578 overbarDepth = braceNesting;
579 braceNesting++;
580 continue;
581 }
582 }
583 else if( *chIt == '{' )
584 {
585 braceNesting++;
586 }
587 else if( *chIt == '}' )
588 {
589 if( braceNesting > 0 )
590 braceNesting--;
591
592 if( braceNesting == superSubDepth )
593 {
594 superSubDepth = -1;
595 continue;
596 }
597
598 if( braceNesting == overbarDepth )
599 {
600 overbarDepth = -1;
601 continue;
602 }
603 }
604
605 char_count++;
606 }
607
608 return char_count;
609}
610
611
612char* StrPurge( char* text )
613{
614 static const char whitespace[] = " \t\n\r\f\v";
615
616 if( text )
617 {
618 while( *text && strchr( whitespace, *text ) )
619 ++text;
620
621 char* cp = text + strlen( text ) - 1;
622
623 while( cp >= text && strchr( whitespace, *cp ) )
624 *cp-- = '\0';
625 }
626
627 return text;
628}
629
630
631char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine )
632{
633 do {
634 if( fgets( Line, SizeLine, File ) == nullptr )
635 return nullptr;
636
637 if( LineNum )
638 *LineNum += 1;
639
640 } while( Line[0] == '#' || Line[0] == '\n' || Line[0] == '\r' || Line[0] == 0 );
641
642 strtok( Line, "\n\r" );
643 return Line;
644}
645
646
647wxString DateAndTime()
648{
649 wxDateTime datetime = wxDateTime::Now();
650
651 datetime.SetCountry( wxDateTime::Country_Default );
652 return datetime.Format( wxDefaultDateTimeFormat, wxDateTime::Local );
653}
654
655
656int StrNumCmp( const wxString& aString1, const wxString& aString2, bool aIgnoreCase )
657{
658 int nb1 = 0, nb2 = 0;
659
660 auto str1 = aString1.begin();
661 auto str2 = aString2.begin();
662
663 while( str1 != aString1.end() && str2 != aString2.end() )
664 {
665 wxUniChar c1 = *str1;
666 wxUniChar c2 = *str2;
667
668 if( wxIsdigit( c1 ) && wxIsdigit( c2 ) ) // Both characters are digits, do numeric compare.
669 {
670 nb1 = 0;
671 nb2 = 0;
672
673 do
674 {
675 c1 = *str1;
676 nb1 = nb1 * 10 + (int) c1 - '0';
677 ++str1;
678 } while( str1 != aString1.end() && wxIsdigit( *str1 ) );
679
680 do
681 {
682 c2 = *str2;
683 nb2 = nb2 * 10 + (int) c2 - '0';
684 ++str2;
685 } while( str2 != aString2.end() && wxIsdigit( *str2 ) );
686
687 if( nb1 < nb2 )
688 return -1;
689
690 if( nb1 > nb2 )
691 return 1;
692
693 c1 = ( str1 != aString1.end() ) ? *str1 : wxUniChar( 0 );
694 c2 = ( str2 != aString2.end() ) ? *str2 : wxUniChar( 0 );
695 }
696
697 // Any numerical comparisons to here are identical.
698 if( aIgnoreCase )
699 {
700 if( wxToupper( c1 ) < wxToupper( c2 ) )
701 return -1;
702
703 if( wxToupper( c1 ) > wxToupper( c2 ) )
704 return 1;
705 }
706 else
707 {
708 if( c1 < c2 )
709 return -1;
710
711 if( c1 > c2 )
712 return 1;
713 }
714
715 if( str1 != aString1.end() )
716 ++str1;
717
718 if( str2 != aString2.end() )
719 ++str2;
720 }
721
722 if( str1 == aString1.end() && str2 != aString2.end() )
723 {
724 return -1; // Identical to here but aString1 is longer.
725 }
726 else if( str1 != aString1.end() && str2 == aString2.end() )
727 {
728 return 1; // Identical to here but aString2 is longer.
729 }
730
731 return 0;
732}
733
734
735bool WildCompareString( const wxString& pattern, const wxString& string_to_tst,
736 bool case_sensitive )
737{
738 const wxChar* cp = nullptr, * mp = nullptr;
739 const wxChar* wild, * str;
740 wxString _pattern, _string_to_tst;
741
742 if( case_sensitive )
743 {
744 wild = pattern.GetData();
745 str = string_to_tst.GetData();
746 }
747 else
748 {
749 _pattern = pattern;
750 _pattern.MakeUpper();
751 _string_to_tst = string_to_tst;
752 _string_to_tst.MakeUpper();
753 wild = _pattern.GetData();
754 str = _string_to_tst.GetData();
755 }
756
757 while( ( *str ) && ( *wild != '*' ) )
758 {
759 if( ( *wild != *str ) && ( *wild != '?' ) )
760 return false;
761
762 wild++;
763 str++;
764 }
765
766 while( *str )
767 {
768 if( *wild == '*' )
769 {
770 if( !*++wild )
771 return 1;
772 mp = wild;
773 cp = str + 1;
774 }
775 else if( ( *wild == *str ) || ( *wild == '?' ) )
776 {
777 wild++;
778 str++;
779 }
780 else
781 {
782 wild = mp;
783 str = cp++;
784 }
785 }
786
787 while( *wild == '*' )
788 {
789 wild++;
790 }
791
792 return !*wild;
793}
794
795
796bool ApplyModifier( double& value, const wxString& aString )
797{
798 static const wxString modifiers( wxT( "pnumkKM" ) );
799
800 if( !aString.length() )
801 return false;
802
803 wxChar modifier;
804 wxString units;
805
806 if( modifiers.Find( aString[ 0 ] ) >= 0 )
807 {
808 modifier = aString[ 0 ];
809 units = aString.Mid( 1 ).Trim();
810 }
811 else
812 {
813 modifier = ' ';
814 units = aString.Mid( 0 ).Trim();
815 }
816
817 if( units.length()
818 && !units.CmpNoCase( wxT( "F" ) )
819 && !units.CmpNoCase( wxT( "hz" ) )
820 && !units.CmpNoCase( wxT( "W" ) )
821 && !units.CmpNoCase( wxT( "V" ) )
822 && !units.CmpNoCase( wxT( "H" ) ) )
823 return false;
824
825 if( modifier == 'p' )
826 value *= 1.0e-12;
827 if( modifier == 'n' )
828 value *= 1.0e-9;
829 else if( modifier == 'u' )
830 value *= 1.0e-6;
831 else if( modifier == 'm' )
832 value *= 1.0e-3;
833 else if( modifier == 'k' || modifier == 'K' )
834 value *= 1.0e3;
835 else if( modifier == 'M' )
836 value *= 1.0e6;
837 else if( modifier == 'G' )
838 value *= 1.0e9;
839
840 return true;
841}
842
843
844int ValueStringCompare( const wxString& strFWord, const wxString& strSWord )
845{
846 // Compare unescaped text
847 wxString fWord = UnescapeString( strFWord );
848 wxString sWord = UnescapeString( strSWord );
849
850 // The different sections of the two strings
851 wxString strFWordBeg, strFWordMid, strFWordEnd;
852 wxString strSWordBeg, strSWordMid, strSWordEnd;
853
854 // Split the two strings into separate parts
855 SplitString( fWord, &strFWordBeg, &strFWordMid, &strFWordEnd );
856 SplitString( sWord, &strSWordBeg, &strSWordMid, &strSWordEnd );
857
858 // Compare the Beginning section of the strings
859 int isEqual = strFWordBeg.CmpNoCase( strSWordBeg );
860
861 if( isEqual > 0 )
862 {
863 return 1;
864 }
865 else if( isEqual < 0 )
866 {
867 return -1;
868 }
869 else
870 {
871 // If the first sections are equal compare their digits
872 double lFirstNumber = 0;
873 double lSecondNumber = 0;
874 bool endingIsModifier = false;
875
876 strFWordMid.ToDouble( &lFirstNumber );
877 strSWordMid.ToDouble( &lSecondNumber );
878
879 endingIsModifier |= ApplyModifier( lFirstNumber, strFWordEnd );
880 endingIsModifier |= ApplyModifier( lSecondNumber, strSWordEnd );
881
882 if( lFirstNumber > lSecondNumber )
883 return 1;
884 else if( lFirstNumber < lSecondNumber )
885 return -1;
886 // If the first two sections are equal and the endings are modifiers then compare them
887 else if( !endingIsModifier )
888 return strFWordEnd.CmpNoCase( strSWordEnd );
889 // Ran out of things to compare; they must match
890 else
891 return 0;
892 }
893}
894
895
896int SplitString( const wxString& strToSplit,
897 wxString* strBeginning,
898 wxString* strDigits,
899 wxString* strEnd )
900{
901 static const wxString separators( wxT( ".," ) );
902
903 // Clear all the return strings
904 strBeginning->Empty();
905 strDigits->Empty();
906 strEnd->Empty();
907
908 // There no need to do anything if the string is empty
909 if( strToSplit.length() == 0 )
910 return 0;
911
912 // Starting at the end of the string look for the first digit
913 int ii;
914
915 for( ii = (strToSplit.length() - 1); ii >= 0; ii-- )
916 {
917 if( wxIsdigit( strToSplit[ii] ) )
918 break;
919 }
920
921 // If there were no digits then just set the single string
922 if( ii < 0 )
923 {
924 *strBeginning = strToSplit;
925 }
926 else
927 {
928 // Since there is at least one digit this is the trailing string
929 *strEnd = strToSplit.substr( ii + 1 );
930
931 // Go to the end of the digits
932 int position = ii + 1;
933
934 for( ; ii >= 0; ii-- )
935 {
936 if( !wxIsdigit( strToSplit[ii] ) && separators.Find( strToSplit[ii] ) < 0 )
937 break;
938 }
939
940 // If all that was left was digits, then just set the digits string
941 if( ii < 0 )
942 *strDigits = strToSplit.substr( 0, position );
943
944 /* We were only looking for the last set of digits everything else is
945 * part of the preamble */
946 else
947 {
948 *strDigits = strToSplit.substr( ii + 1, position - ii - 1 );
949 *strBeginning = strToSplit.substr( 0, ii + 1 );
950 }
951 }
952
953 return 0;
954}
955
956
957int GetTrailingInt( const wxString& aStr )
958{
959 int number = 0;
960 int base = 1;
961
962 // Trim and extract the trailing numeric part
963 int index = aStr.Len() - 1;
964
965 while( index >= 0 )
966 {
967 const char chr = aStr.GetChar( index );
968
969 if( chr < '0' || chr > '9' )
970 break;
971
972 number += ( chr - '0' ) * base;
973 base *= 10;
974 index--;
975 }
976
977 return number;
978}
979
980
982{
984}
985
986
987bool ReplaceIllegalFileNameChars( std::string* aName, int aReplaceChar )
988{
989 bool changed = false;
990 std::string result;
991 result.reserve( aName->length() );
992
993 for( std::string::iterator it = aName->begin(); it != aName->end(); ++it )
994 {
995 if( strchr( illegalFileNameChars, *it ) )
996 {
997 if( aReplaceChar )
998 StrPrintf( &result, "%c", aReplaceChar );
999 else
1000 StrPrintf( &result, "%%%02x", *it );
1001
1002 changed = true;
1003 }
1004 else
1005 {
1006 result += *it;
1007 }
1008 }
1009
1010 if( changed )
1011 *aName = result;
1012
1013 return changed;
1014}
1015
1016
1017bool ReplaceIllegalFileNameChars( wxString& aName, int aReplaceChar )
1018{
1019 bool changed = false;
1020 wxString result;
1021 result.reserve( aName.Length() );
1022 wxString illWChars = GetIllegalFileNameWxChars();
1023
1024 for( wxString::iterator it = aName.begin(); it != aName.end(); ++it )
1025 {
1026 if( illWChars.Find( *it ) != wxNOT_FOUND )
1027 {
1028 if( aReplaceChar )
1029 result += aReplaceChar;
1030 else
1031 result += wxString::Format( "%%%02x", *it );
1032
1033 changed = true;
1034 }
1035 else
1036 {
1037 result += *it;
1038 }
1039 }
1040
1041 if( changed )
1042 aName = result;
1043
1044 return changed;
1045}
1046
1047
1048void wxStringSplit( const wxString& aText, wxArrayString& aStrings, wxChar aSplitter )
1049{
1050 wxString tmp;
1051
1052 for( unsigned ii = 0; ii < aText.Length(); ii++ )
1053 {
1054 if( aText[ii] == aSplitter )
1055 {
1056 aStrings.Add( tmp );
1057 tmp.Clear();
1058 }
1059 else
1060 {
1061 tmp << aText[ii];
1062 }
1063 }
1064
1065 if( !tmp.IsEmpty() )
1066 aStrings.Add( tmp );
1067}
1068
1069
1070void StripTrailingZeros( wxString& aStringValue, unsigned aTrailingZeroAllowed )
1071{
1072 struct lconv* lc = localeconv();
1073 char sep = lc->decimal_point[0];
1074 unsigned sep_pos = aStringValue.Find( sep );
1075
1076 if( sep_pos > 0 )
1077 {
1078 // We want to keep at least aTrailingZeroAllowed digits after the separator
1079 unsigned min_len = sep_pos + aTrailingZeroAllowed + 1;
1080
1081 while( aStringValue.Len() > min_len )
1082 {
1083 if( aStringValue.Last() == '0' )
1084 aStringValue.RemoveLast();
1085 else
1086 break;
1087 }
1088 }
1089}
1090
1091
1092std::string FormatDouble2Str( double aValue )
1093{
1094 std::string buf;
1095
1096 if( aValue != 0.0 && std::fabs( aValue ) <= 0.0001 )
1097 {
1098 buf = fmt::format( "{:.16f}", aValue );
1099
1100 // remove trailing zeros
1101 while( !buf.empty() && buf[buf.size() - 1] == '0' )
1102 {
1103 buf.pop_back();
1104 }
1105 }
1106 else
1107 {
1108 buf = fmt::format( "{:.10g}", aValue );
1109 }
1110
1111 return buf;
1112}
1113
1114
1115std::string UIDouble2Str( double aValue )
1116{
1117 char buf[50];
1118 int len;
1119
1120 if( aValue != 0.0 && std::fabs( aValue ) <= 0.0001 )
1121 {
1122 // For these small values, %f works fine,
1123 // and %g gives an exponent
1124 len = sprintf( buf, "%.16f", aValue );
1125
1126 while( --len > 0 && buf[len] == '0' )
1127 buf[len] = '\0';
1128
1129 if( buf[len] == '.' )
1130 buf[len] = '\0';
1131 else
1132 ++len;
1133 }
1134 else
1135 {
1136 // For these values, %g works fine, and sometimes %f
1137 // gives a bad value (try aValue = 1.222222222222, with %.16f format!)
1138 len = sprintf( buf, "%.10g", aValue );
1139 }
1140
1141 return std::string( buf, len );
1142}
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