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