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 The 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
28
29#include <clocale>
30#include <cmath>
31#include <map>
32#include <core/map_helpers.h>
33#include <fmt/core.h>
34#include <macros.h>
35#include <richio.h> // StrPrintf
36#include <string_utils.h>
37#include <wx_filename.h>
38#include <fmt/chrono.h>
39#include <wx/log.h>
40#include <wx/regex.h>
41#include <wx/tokenzr.h>
42#include "locale_io.h"
43
44
50static const char illegalFileNameChars[] = "\\/:\"<>|*?";
51
52static const wxChar defaultVariantName[] = wxT( "< Default >" );
53
54
55// Checks if a full filename is valid, i.e. does not contains illegal chars
56bool IsFullFileNameValid( const wxString& aFullFilename )
57{
58
59 // Test for forbidden chars in aFullFilename.
60 // '\'and '/' are allowed here because aFullFilename can be a full path, and
61 // ':' is allowed on Windows as second char in string.
62 // So remove allowed separators from string to test
63 wxString filtered_fullpath = aFullFilename;
64
65#ifdef __WINDOWS__
66 // On MSW, the list returned by wxFileName::GetForbiddenChars() contains separators
67 // '\'and '/'
68 filtered_fullpath.Replace( "/", "_" );
69 filtered_fullpath.Replace( "\\", "_" );
70
71 // A disk identifier is allowed, and therefore remove its separator
72 if( filtered_fullpath.Length() > 1 && filtered_fullpath[1] == ':' )
73 filtered_fullpath[1] = ' ';
74#endif
75
76 if( wxString::npos != filtered_fullpath.find_first_of( wxFileName::GetForbiddenChars() ) )
77 return false;
78
79 return true;
80}
81
82
83wxString ConvertToNewOverbarNotation( const wxString& aOldStr )
84{
85 wxString newStr;
86 bool inOverbar = false;
87
88 // Don't get tripped up by the legacy empty-string token.
89 if( aOldStr == wxT( "~" ) )
90 return aOldStr;
91
92 newStr.reserve( aOldStr.length() );
93
94 for( wxString::const_iterator chIt = aOldStr.begin(); chIt != aOldStr.end(); ++chIt )
95 {
96 if( *chIt == '~' )
97 {
98 wxString::const_iterator lookahead = chIt + 1;
99
100 if( lookahead != aOldStr.end() && *lookahead == '~' )
101 {
102 if( ++lookahead != aOldStr.end() && *lookahead == '{' )
103 {
104 // This way the subsequent opening curly brace will not start an
105 // overbar.
106 newStr << wxT( "~~{}" );
107 continue;
108 }
109
110 // Two subsequent tildes mean a tilde.
111 newStr << wxT( "~" );
112 ++chIt;
113 continue;
114 }
115 else if( lookahead != aOldStr.end() && *lookahead == '{' )
116 {
117 // Could mean the user wants "{" with an overbar, but more likely this
118 // is a case of double notation conversion. Bail out.
119 return aOldStr;
120 }
121 else
122 {
123 if( inOverbar )
124 {
125 newStr << wxT( "}" );
126 inOverbar = false;
127 }
128 else
129 {
130 newStr << wxT( "~{" );
131 inOverbar = true;
132 }
133
134 continue;
135 }
136 }
137 else if( ( *chIt == ' ' || *chIt == '}' || *chIt == ')' ) && inOverbar )
138 {
139 // Spaces were used to terminate overbar as well
140 newStr << wxT( "}" );
141 inOverbar = false;
142 }
143
144 newStr << *chIt;
145 }
146
147 // Explicitly end the overbar even if there was no terminating '~' in the aOldStr.
148 if( inOverbar )
149 newStr << wxT( "}" );
150
151 return newStr;
152}
153
154
155bool ConvertSmartQuotesAndDashes( wxString* aString )
156{
157 bool retVal = false;
158
159 for( wxString::iterator ii = aString->begin(); ii != aString->end(); ++ii )
160 {
161 if( *ii == L'\u00B4' || *ii == L'\u2018' || *ii == L'\u2019' )
162 {
163 *ii = '\'';
164 retVal = true;
165 }
166 if( *ii == L'\u201C' || *ii == L'\u201D' )
167 {
168 *ii = '"';
169 retVal = true;
170 }
171 if( *ii == L'\u2013' || *ii == L'\u2014' )
172 {
173 *ii = '-';
174 retVal = true;
175 }
176 }
177
178 return retVal;
179}
180
181
182wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext )
183{
184 wxString converted;
185 std::vector<bool> braceStack; // true == formatting construct
186
187 converted.reserve( aSource.length() );
188
189 for( wxUniChar c: aSource )
190 {
191 if( aContext == CTX_NETNAME )
192 {
193 if( c == '/' )
194 converted += wxT( "{slash}" );
195 else if( c == '\n' || c == '\r' )
196 converted += wxEmptyString; // drop
197 else
198 converted += c;
199 }
200 else if( aContext == CTX_LIBID || aContext == CTX_LEGACY_LIBID )
201 {
202 // We no longer escape '/' in LIB_IDs, but we used to
203 if( c == '/' && aContext == CTX_LEGACY_LIBID )
204 converted += wxT( "{slash}" );
205 else if( c == '\\' )
206 converted += wxT( "{backslash}" );
207 else if( c == '<' )
208 converted += wxT( "{lt}" );
209 else if( c == '>' )
210 converted += wxT( "{gt}" );
211 else if( c == ':' )
212 converted += wxT( "{colon}" );
213 else if( c == '\"' )
214 converted += wxT( "{dblquote}" );
215 else if( c == '\n' || c == '\r' )
216 converted += wxEmptyString; // drop
217 else
218 converted += c;
219 }
220 else if( aContext == CTX_IPC )
221 {
222 if( c == '/' )
223 converted += wxT( "{slash}" );
224 else if( c == ',' )
225 converted += wxT( "{comma}" );
226 else if( c == '\"' )
227 converted += wxT( "{dblquote}" );
228 else
229 converted += c;
230 }
231 else if( aContext == CTX_QUOTED_STR )
232 {
233 if( c == '\"' )
234 converted += wxT( "{dblquote}" );
235 else
236 converted += c;
237 }
238 else if( aContext == CTX_JS_STR )
239 {
240 if( c >= 0x7F || c == '\'' || c == '\\' || c == '(' || c == ')' )
241 {
242 unsigned int code = c;
243 char buffer[16];
244 snprintf( buffer, sizeof(buffer), "\\u%4.4X", code );
245 converted += buffer;
246 }
247 else
248 {
249 converted += c;
250 }
251 }
252 else if( aContext == CTX_LINE )
253 {
254 if( c == '\n' || c == '\r' )
255 converted += wxT( "{return}" );
256 else
257 converted += c;
258 }
259 else if( aContext == CTX_FILENAME )
260 {
261 if( c == '/' )
262 converted += wxT( "{slash}" );
263 else if( c == '\\' )
264 converted += wxT( "{backslash}" );
265 else if( c == '\"' )
266 converted += wxT( "{dblquote}" );
267 else if( c == '<' )
268 converted += wxT( "{lt}" );
269 else if( c == '>' )
270 converted += wxT( "{gt}" );
271 else if( c == '|' )
272 converted += wxT( "{bar}" );
273 else if( c == ':' )
274 converted += wxT( "{colon}" );
275 else if( c == '\t' )
276 converted += wxT( "{tab}" );
277 else if( c == '\n' || c == '\r' )
278 converted += wxT( "{return}" );
279 else
280 converted += c;
281 }
282 else if( aContext == CTX_NO_SPACE )
283 {
284 if( c == ' ' )
285 converted += wxT( "{space}" );
286 else
287 converted += c;
288 }
289 else if( aContext == CTX_CSV )
290 {
291 if( c == ',' )
292 converted += wxT( "{comma}" );
293 else if( c == '\n' || c == '\r' )
294 converted += wxT( "{return}" );
295 else
296 converted += c;
297 }
298 else
299 {
300 converted += c;
301 }
302 }
303
304 return converted;
305}
306
307
308wxString UnescapeString( const wxString& aSource )
309{
310 size_t sourceLen = aSource.length();
311
312 // smallest escape string is three characters, shortcut everything else
313 if( sourceLen <= 2 )
314 {
315 return aSource;
316 }
317
318 wxString newbuf;
319 newbuf.reserve( sourceLen );
320
321 wxUniChar prev = 0;
322 wxUniChar ch = 0;
323
324 for( size_t i = 0; i < sourceLen; ++i )
325 {
326 prev = ch;
327 ch = aSource[i];
328
329 if( ch == '{' )
330 {
331 wxString token;
332 int depth = 1;
333 bool terminated = false;
334
335 for( i = i + 1; i < sourceLen; ++i )
336 {
337 ch = aSource[i];
338
339 if( ch == '{' )
340 depth++;
341 else if( ch == '}' )
342 depth--;
343
344 if( depth <= 0 )
345 {
346 terminated = true;
347 break;
348 }
349 else
350 {
351 token << ch;
352 }
353 }
354
355 if( !terminated )
356 {
357 newbuf << wxT( "{" ) << UnescapeString( token );
358 }
359 else if( prev == '$' || prev == '~' || prev == '^' || prev == '_' )
360 {
361 newbuf << wxT( "{" ) << UnescapeString( token ) << wxT( "}" );
362 }
363 else if( token == wxT( "dblquote" ) ) newbuf << wxT( "\"" );
364 else if( token == wxT( "quote" ) ) newbuf << wxT( "'" );
365 else if( token == wxT( "lt" ) ) newbuf << wxT( "<" );
366 else if( token == wxT( "gt" ) ) newbuf << wxT( ">" );
367 else if( token == wxT( "backslash" ) ) newbuf << wxT( "\\" );
368 else if( token == wxT( "slash" ) ) newbuf << wxT( "/" );
369 else if( token == wxT( "bar" ) ) newbuf << wxT( "|" );
370 else if( token == wxT( "comma" ) ) newbuf << wxT( "," );
371 else if( token == wxT( "colon" ) ) newbuf << wxT( ":" );
372 else if( token == wxT( "space" ) ) newbuf << wxT( " " );
373 else if( token == wxT( "dollar" ) ) newbuf << wxT( "$" );
374 else if( token == wxT( "tab" ) ) newbuf << wxT( "\t" );
375 else if( token == wxT( "return" ) ) newbuf << wxT( "\n" );
376 else if( token == wxT( "brace" ) ) newbuf << wxT( "{" );
377 else
378 {
379 newbuf << wxT( "{" ) << UnescapeString( token ) << wxT( "}" );
380 }
381 }
382 else
383 {
384 newbuf << ch;
385 }
386 }
387
388 return newbuf;
389}
390
391
392wxString TitleCaps( const wxString& aString )
393{
394 wxArrayString words;
395 wxString result;
396
397 wxStringSplit( aString, words, ' ' );
398
399 result.reserve( aString.length() );
400
401 for( const wxString& word : words )
402 {
403 if( !result.IsEmpty() )
404 result += wxT( " " );
405
406 result += word.Capitalize();
407 }
408
409 return result;
410}
411
412
413wxString InitialCaps( const wxString& aString )
414{
415 wxArrayString words;
416 wxString result;
417
418 wxStringSplit( aString, words, ' ' );
419
420 result.reserve( aString.length() );
421
422 for( const wxString& word : words )
423 {
424 if( result.IsEmpty() )
425 result += word.Capitalize();
426 else
427 result += wxT( " " ) + word.Lower();
428 }
429
430 return result;
431}
432
433
434int ReadDelimitedText( wxString* aDest, const char* aSource )
435{
436 std::string utf8; // utf8 but without escapes and quotes.
437 bool inside = false;
438 const char* start = aSource;
439 char cc;
440
441 while( (cc = *aSource++) != 0 )
442 {
443 if( cc == '"' )
444 {
445 if( inside )
446 break; // 2nd double quote is end of delimited text
447
448 inside = true; // first delimiter found, make note, do not copy
449 }
450
451 else if( inside )
452 {
453 if( cc == '\\' )
454 {
455 cc = *aSource++;
456
457 if( !cc )
458 break;
459
460 // do no copy the escape byte if it is followed by \ or "
461 if( cc != '"' && cc != '\\' )
462 utf8 += '\\';
463
464 utf8 += cc;
465 }
466 else
467 {
468 utf8 += cc;
469 }
470 }
471 }
472
473 *aDest = From_UTF8( utf8.c_str() );
474
475 return aSource - start;
476}
477
478
479int ReadDelimitedText( char* aDest, const char* aSource, int aDestSize )
480{
481 if( aDestSize <= 0 )
482 return 0;
483
484 bool inside = false;
485 const char* start = aSource;
486 char* limit = aDest + aDestSize - 1;
487 char cc;
488
489 while( ( cc = *aSource++ ) != 0 && aDest < limit )
490 {
491 if( cc == '"' )
492 {
493 if( inside )
494 break; // 2nd double quote is end of delimited text
495
496 inside = true; // first delimiter found, make note, do not copy
497 }
498 else if( inside )
499 {
500 if( cc == '\\' )
501 {
502 cc = *aSource++;
503
504 if( !cc )
505 break;
506
507 // do no copy the escape byte if it is followed by \ or "
508 if( cc != '"' && cc != '\\' )
509 *aDest++ = '\\';
510
511 if( aDest < limit )
512 *aDest++ = cc;
513 }
514 else
515 {
516 *aDest++ = cc;
517 }
518 }
519 }
520
521 *aDest = 0;
522
523 return aSource - start;
524}
525
526
527std::string EscapedUTF8( const wxString& aString )
528{
529 wxString str = aString;
530
531 // No new-lines allowed in quoted strings
532 str.Replace( wxT( "\r\n" ), wxT( "\r" ) );
533 str.Replace( wxT( "\n" ), wxT( "\r" ) );
534
535 std::string utf8 = TO_UTF8( aString );
536
537 std::string ret;
538
539 ret.reserve( utf8.length() + 2 );
540
541 ret += '"';
542
543 for( std::string::const_iterator it = utf8.begin(); it!=utf8.end(); ++it )
544 {
545 // this escaping strategy is designed to be compatible with ReadDelimitedText():
546 if( *it == '"' )
547 {
548 ret += '\\';
549 ret += '"';
550 }
551 else if( *it == '\\' )
552 {
553 ret += '\\'; // double it up
554 ret += '\\';
555 }
556 else
557 {
558 ret += *it;
559 }
560 }
561
562 ret += '"';
563
564 return ret;
565}
566
567
568wxString EscapeHTML( const wxString& aString )
569{
570 wxString converted;
571
572 converted.reserve( aString.length() );
573
574 for( wxUniChar c : aString )
575 {
576 if( c == '\"' )
577 converted += wxT( "&quot;" );
578 else if( c == '\'' )
579 converted += wxT( "&apos;" );
580 else if( c == '&' )
581 converted += wxT( "&amp;" );
582 else if( c == '<' )
583 converted += wxT( "&lt;" );
584 else if( c == '>' )
585 converted += wxT( "&gt;" );
586 else
587 converted += c;
588 }
589
590 return converted;
591}
592
593
594wxString UnescapeHTML( const wxString& aString )
595{
596 // clang-format off
597 static const std::map<wxString, wxString> c_replacements = {
598 { wxS( "quot" ), wxS( "\"" ) },
599 { wxS( "apos" ), wxS( "'" ) },
600 { wxS( "amp" ), wxS( "&" ) },
601 { wxS( "lt" ), wxS( "<" ) },
602 { wxS( "gt" ), wxS( ">" ) }
603 };
604 // clang-format on
605
606 // Construct regex
607 wxString regexStr = "&(#(\\d*)|#x([a-zA-Z0-9]{4})";
608
609 for( auto& [key, value] : c_replacements )
610 regexStr << '|' << key;
611
612 regexStr << ");";
613
614 wxRegEx regex( regexStr );
615
616 // Process matches
617 size_t start = 0;
618 size_t len = 0;
619
620 wxString result;
621 wxString str = aString;
622
623 while( regex.Matches( str ) )
624 {
625 std::vector<wxString> matches;
626 regex.GetMatch( &start, &len );
627
628 result << str.Left( start );
629
630 wxString code = regex.GetMatch( str, 1 );
631 wxString codeDec = regex.GetMatch( str, 2 );
632 wxString codeHex = regex.GetMatch( str, 3 );
633
634 if( !codeDec.IsEmpty() || !codeHex.IsEmpty() )
635 {
636 unsigned long codeVal = 0;
637
638 if( !codeDec.IsEmpty() )
639 codeDec.ToCULong( &codeVal );
640 else if( !codeHex.IsEmpty() )
641 codeHex.ToCULong( &codeVal, 16 );
642
643 if( codeVal != 0 )
644 result << wxUniChar( codeVal );
645 }
646 else if( auto val = get_opt( c_replacements, code ) )
647 {
648 result << *val;
649 }
650
651 str = str.Mid( start + len );
652 }
653
654 result << str;
655
656 return result;
657}
658
659
660wxString RemoveHTMLTags( const wxString& aInput )
661{
662 wxString str = aInput;
663 wxRegEx( wxS( "<[^>]*>" ) ).ReplaceAll( &str, wxEmptyString );
664
665 return str;
666}
667
668
669wxString LinkifyHTML( wxString aStr )
670{
671 static wxRegEx regex( wxS( "\\b(https?|ftp|file)://([-\\w+&@#/%?=~|!:,.;]*[^.,:;<>\\(\\)\\s\u00b6])" ),
672 wxRE_ICASE );
673
674 regex.ReplaceAll( &aStr, "<a href=\"\\0\">\\0</a>" );
675
676 return aStr;
677}
678
679
680bool IsURL( wxString aStr )
681{
682 static wxRegEx regex( wxS( "(https?|ftp|file)://([-\\w+&@#/%?=~|!:,.;]*[^.,:;<>\\s\u00b6])" ),
683 wxRE_ICASE );
684
685 regex.ReplaceAll( &aStr, "<a href=\"\\0\">\\0</a>" );
686
687 return regex.Matches( aStr );
688}
689
690
691bool NoPrintableChars( const wxString& aString )
692{
693 wxString tmp = aString;
694
695 return tmp.Trim( true ).Trim( false ).IsEmpty();
696}
697
698
699int PrintableCharCount( const wxString& aString )
700{
701 int char_count = 0;
702 int overbarDepth = -1;
703 int superSubDepth = -1;
704 int braceNesting = 0;
705
706 for( auto chIt = aString.begin(), end = aString.end(); chIt < end; ++chIt )
707 {
708 if( *chIt == '\t' )
709 {
710 // We don't format tabs in bitmap text (where this is currently used), so just
711 // drop them from the count.
712 continue;
713 }
714 else if( *chIt == '^' && superSubDepth == -1 )
715 {
716 auto lookahead = chIt;
717
718 if( ++lookahead != end && *lookahead == '{' )
719 {
720 chIt = lookahead;
721 superSubDepth = braceNesting;
722 braceNesting++;
723 continue;
724 }
725 }
726 else if( *chIt == '_' && superSubDepth == -1 )
727 {
728 auto lookahead = chIt;
729
730 if( ++lookahead != end && *lookahead == '{' )
731 {
732 chIt = lookahead;
733 superSubDepth = braceNesting;
734 braceNesting++;
735 continue;
736 }
737 }
738 else if( *chIt == '~' && overbarDepth == -1 )
739 {
740 auto lookahead = chIt;
741
742 if( ++lookahead != end && *lookahead == '{' )
743 {
744 chIt = lookahead;
745 overbarDepth = braceNesting;
746 braceNesting++;
747 continue;
748 }
749 }
750 else if( *chIt == '{' )
751 {
752 braceNesting++;
753 }
754 else if( *chIt == '}' )
755 {
756 if( braceNesting > 0 )
757 braceNesting--;
758
759 if( braceNesting == superSubDepth )
760 {
761 superSubDepth = -1;
762 continue;
763 }
764
765 if( braceNesting == overbarDepth )
766 {
767 overbarDepth = -1;
768 continue;
769 }
770 }
771
772 char_count++;
773 }
774
775 return char_count;
776}
777
778
779char* StrPurge( char* text )
780{
781 static const char whitespace[] = " \t\n\r\f\v";
782
783 if( text )
784 {
785 while( *text && strchr( whitespace, *text ) )
786 ++text;
787
788 char* cp = text + strlen( text ) - 1;
789
790 while( cp >= text && strchr( whitespace, *cp ) )
791 *cp-- = '\0';
792 }
793
794 return text;
795}
796
797
798char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine )
799{
800 do {
801 if( fgets( Line, SizeLine, File ) == nullptr )
802 return nullptr;
803
804 if( LineNum )
805 *LineNum += 1;
806
807 } while( Line[0] == '#' || Line[0] == '\n' || Line[0] == '\r' || Line[0] == 0 );
808
809 strtok( Line, "\n\r" );
810 return Line;
811}
812
813
815{
816 // on msys2 variant mingw64, in fmt::format the %z format
817 // (offset from UTC in the ISO 8601 format, e.g. -0430) does not work,
818 // and is in fact %Z (locale-dependent time zone name or abbreviation) and breaks our date.
819 // However, on msys2 variant ucrt64, it works (this is not the same code in fmt::format)
820#if defined(__MINGW32__) && !defined(_UCRT)
821 return fmt::format( "{:%FT%T}", fmt::localtime( std::time( nullptr ) ) );
822#else
823 return fmt::format( "{:%FT%T%z}", fmt::localtime( std::time( nullptr ) ) );
824#endif
825}
826
827
828int StrNumCmp( const wxString& aString1, const wxString& aString2, bool aIgnoreCase )
829{
830 int nb1 = 0, nb2 = 0;
831
832 auto str1 = aString1.begin();
833 auto str2 = aString2.begin();
834
835 while( str1 != aString1.end() && str2 != aString2.end() )
836 {
837 wxUniChar c1 = *str1;
838 wxUniChar c2 = *str2;
839
840 if( wxIsdigit( c1 ) && wxIsdigit( c2 ) ) // Both characters are digits, do numeric compare.
841 {
842 nb1 = 0;
843 nb2 = 0;
844
845 do
846 {
847 c1 = *str1;
848 nb1 = nb1 * 10 + (int) c1 - '0';
849 ++str1;
850 } while( str1 != aString1.end() && wxIsdigit( *str1 ) );
851
852 do
853 {
854 c2 = *str2;
855 nb2 = nb2 * 10 + (int) c2 - '0';
856 ++str2;
857 } while( str2 != aString2.end() && wxIsdigit( *str2 ) );
858
859 if( nb1 < nb2 )
860 return -1;
861
862 if( nb1 > nb2 )
863 return 1;
864
865 c1 = ( str1 != aString1.end() ) ? *str1 : wxUniChar( 0 );
866 c2 = ( str2 != aString2.end() ) ? *str2 : wxUniChar( 0 );
867 }
868
869 // Any numerical comparisons to here are identical.
870 if( aIgnoreCase )
871 {
872 if( c1 != c2 )
873 {
874 wxUniChar uc1 = wxToupper( c1 );
875 wxUniChar uc2 = wxToupper( c2 );
876
877 if( uc1 != uc2 )
878 return uc1 < uc2 ? -1 : 1;
879 }
880 }
881 else
882 {
883 if( c1 < c2 )
884 return -1;
885
886 if( c1 > c2 )
887 return 1;
888 }
889
890 if( str1 != aString1.end() )
891 ++str1;
892
893 if( str2 != aString2.end() )
894 ++str2;
895 }
896
897 if( str1 == aString1.end() && str2 != aString2.end() )
898 {
899 return -1; // Identical to here but aString1 is longer.
900 }
901 else if( str1 != aString1.end() && str2 == aString2.end() )
902 {
903 return 1; // Identical to here but aString2 is longer.
904 }
905
906 return 0;
907}
908
909
910bool WildCompareString( const wxString& pattern, const wxString& string_to_tst,
911 bool case_sensitive )
912{
913 const wxChar* cp = nullptr;
914 const wxChar* mp = nullptr;
915 const wxChar* wild = nullptr;
916 const wxChar* str = nullptr;
917 wxString _pattern, _string_to_tst;
918
919 if( case_sensitive )
920 {
921 wild = pattern.GetData();
922 str = string_to_tst.GetData();
923 }
924 else
925 {
926 _pattern = pattern;
927 _pattern.MakeUpper();
928 _string_to_tst = string_to_tst;
929 _string_to_tst.MakeUpper();
930 wild = _pattern.GetData();
931 str = _string_to_tst.GetData();
932 }
933
934 while( ( *str ) && ( *wild != '*' ) )
935 {
936 if( ( *wild != *str ) && ( *wild != '?' ) )
937 return false;
938
939 wild++;
940 str++;
941 }
942
943 while( *str )
944 {
945 if( *wild == '*' )
946 {
947 if( !*++wild )
948 return true;
949
950 mp = wild;
951 cp = str + 1;
952 }
953 else if( ( *wild == *str ) || ( *wild == '?' ) )
954 {
955 wild++;
956 str++;
957 }
958 else
959 {
960 wild = mp;
961 str = cp++;
962 }
963 }
964
965 while( *wild == '*' )
966 {
967 wild++;
968 }
969
970 return !*wild;
971}
972
973
974bool ApplyModifier( double& value, const wxString& aString )
975{
977 static const wxString modifiers( wxT( "pnuµμmkKM" ) );
978
979 if( !aString.length() )
980 return false;
981
982 wxChar modifier;
983 wxString units;
984
985 if( modifiers.Find( aString[ 0 ] ) >= 0 )
986 {
987 modifier = aString[ 0 ];
988 units = aString.Mid( 1 ).Trim();
989 }
990 else
991 {
992 modifier = ' ';
993 units = aString.Mid( 0 ).Trim();
994 }
995
996 if( units.length()
997 && !units.IsSameAs( wxT( "F" ), false )
998 && !units.IsSameAs( wxT( "hz" ), false )
999 && !units.IsSameAs( wxT( "W" ), false )
1000 && !units.IsSameAs( wxT( "V" ), false )
1001 && !units.IsSameAs( wxT( "A" ), false )
1002 && !units.IsSameAs( wxT( "H" ), false ) )
1003 {
1004 return false;
1005 }
1006
1007 if( modifier == 'p' )
1008 value *= 1.0e-12;
1009 if( modifier == 'n' )
1010 value *= 1.0e-9;
1011 else if( modifier == 'u' || modifier == wxS( "µ" )[0] || modifier == wxS( "μ" )[0] )
1012 value *= 1.0e-6;
1013 else if( modifier == 'm' )
1014 value *= 1.0e-3;
1015 else if( modifier == 'k' || modifier == 'K' )
1016 value *= 1.0e3;
1017 else if( modifier == 'M' )
1018 value *= 1.0e6;
1019 else if( modifier == 'G' )
1020 value *= 1.0e9;
1021
1022 return true;
1023}
1024
1025
1026bool convertSeparators( wxString* value )
1027{
1028 // Note: fetching the decimal separator from the current locale isn't a silver bullet because
1029 // it assumes the current computer's locale is the same as the locale the schematic was
1030 // authored in -- something that isn't true, for instance, when sharing designs through
1031 // DIYAudio.com.
1032 //
1033 // Some values are self-describing: multiple instances of a single separator character must be
1034 // thousands separators; a single instance of each character must be a thousands separator
1035 // followed by a decimal separator; etc.
1036 //
1037 // Only when presented with an ambiguous value do we fall back on the current locale.
1038
1039 value->Replace( wxS( " " ), wxEmptyString );
1040
1041 wxChar ambiguousSeparator = '?';
1042 wxChar thousandsSeparator = '?';
1043 bool thousandsSeparatorFound = false;
1044 wxChar decimalSeparator = '?';
1045 bool decimalSeparatorFound = false;
1046 int digits = 0;
1047
1048 for( int ii = (int) value->length() - 1; ii >= 0; --ii )
1049 {
1050 wxChar c = value->GetChar( ii );
1051
1052 if( c >= '0' && c <= '9' )
1053 {
1054 digits += 1;
1055 }
1056 else if( c == '.' || c == ',' )
1057 {
1058 if( decimalSeparator != '?' || thousandsSeparator != '?' )
1059 {
1060 // We've previously found a non-ambiguous separator...
1061
1062 if( c == decimalSeparator )
1063 {
1064 if( thousandsSeparatorFound )
1065 return false; // decimal before thousands
1066 else if( decimalSeparatorFound )
1067 return false; // more than one decimal
1068 else
1069 decimalSeparatorFound = true;
1070 }
1071 else if( c == thousandsSeparator )
1072 {
1073 if( digits != 3 )
1074 return false; // thousands not followed by 3 digits
1075 else
1076 thousandsSeparatorFound = true;
1077 }
1078 }
1079 else if( ambiguousSeparator != '?' )
1080 {
1081 // We've previously found a separator, but we don't know for sure which...
1082
1083 if( c == ambiguousSeparator )
1084 {
1085 // They both must be thousands separators
1086 thousandsSeparator = ambiguousSeparator;
1087 thousandsSeparatorFound = true;
1088 decimalSeparator = c == '.' ? ',' : '.';
1089 }
1090 else
1091 {
1092 // The first must have been a decimal, and this must be a thousands.
1093 decimalSeparator = ambiguousSeparator;
1094 decimalSeparatorFound = true;
1095 thousandsSeparator = c;
1096 thousandsSeparatorFound = true;
1097 }
1098 }
1099 else
1100 {
1101 // This is the first separator...
1102
1103 // If it's preceded by a '0' (only), or if it's followed by some number of
1104 // digits not equal to 3, then it -must- be a decimal separator.
1105 //
1106 // In all other cases we don't really know what it is yet.
1107
1108 if( ( ii == 1 && value->GetChar( 0 ) == '0' ) || digits != 3 )
1109 {
1110 decimalSeparator = c;
1111 decimalSeparatorFound = true;
1112 thousandsSeparator = c == '.' ? ',' : '.';
1113 }
1114 else
1115 {
1116 ambiguousSeparator = c;
1117 }
1118 }
1119
1120 digits = 0;
1121 }
1122 else
1123 {
1124 digits = 0;
1125 }
1126 }
1127
1128 // If we found nothing definitive then we have to look at the current locale
1129 if( decimalSeparator == '?' && thousandsSeparator == '?' )
1130 {
1131 const struct lconv* lc = localeconv();
1132
1133 decimalSeparator = lc->decimal_point[0];
1134 thousandsSeparator = decimalSeparator == '.' ? ',' : '.';
1135 }
1136
1137 // Convert to C-locale
1138 value->Replace( thousandsSeparator, wxEmptyString );
1139 value->Replace( decimalSeparator, '.' );
1140
1141 return true;
1142}
1143
1144
1145int ValueStringCompare( const wxString& strFWord, const wxString& strSWord )
1146{
1147 // Compare unescaped text
1148 wxString fWord = UnescapeString( strFWord );
1149 wxString sWord = UnescapeString( strSWord );
1150
1151 // The different sections of the two strings
1152 wxString strFWordBeg, strFWordMid, strFWordEnd;
1153 wxString strSWordBeg, strSWordMid, strSWordEnd;
1154
1155 // Split the two strings into separate parts
1156 SplitString( fWord, &strFWordBeg, &strFWordMid, &strFWordEnd );
1157 SplitString( sWord, &strSWordBeg, &strSWordMid, &strSWordEnd );
1158
1159 // Compare the Beginning section of the strings
1160 int isEqual = strFWordBeg.CmpNoCase( strSWordBeg );
1161
1162 if( isEqual > 0 )
1163 {
1164 return 1;
1165 }
1166 else if( isEqual < 0 )
1167 {
1168 return -1;
1169 }
1170 else
1171 {
1172 // If the first sections are equal compare their digits
1173 double lFirstNumber = 0;
1174 double lSecondNumber = 0;
1175 bool endingIsModifier = false;
1176
1177 convertSeparators( &strFWordMid );
1178 convertSeparators( &strSWordMid );
1179
1180 strFWordMid.ToCDouble( &lFirstNumber );
1181 strSWordMid.ToCDouble( &lSecondNumber );
1182
1183 endingIsModifier |= ApplyModifier( lFirstNumber, strFWordEnd );
1184 endingIsModifier |= ApplyModifier( lSecondNumber, strSWordEnd );
1185
1186 if( lFirstNumber > lSecondNumber )
1187 return 1;
1188 else if( lFirstNumber < lSecondNumber )
1189 return -1;
1190 // If the first two sections are equal and the endings are modifiers then compare them
1191 else if( !endingIsModifier )
1192 return strFWordEnd.CmpNoCase( strSWordEnd );
1193 // Ran out of things to compare; they must match
1194 else
1195 return 0;
1196 }
1197}
1198
1199
1200int SplitString( const wxString& strToSplit,
1201 wxString* strBeginning,
1202 wxString* strDigits,
1203 wxString* strEnd )
1204{
1205 static const wxString separators( wxT( ".," ) );
1206
1207 // Clear all the return strings
1208 strBeginning->Empty();
1209 strDigits->Empty();
1210 strEnd->Empty();
1211
1212 // There no need to do anything if the string is empty
1213 if( strToSplit.length() == 0 )
1214 return 0;
1215
1216 // Starting at the end of the string look for the first digit
1217 int ii;
1218
1219 for( ii = (strToSplit.length() - 1); ii >= 0; ii-- )
1220 {
1221 if( wxIsdigit( strToSplit[ii] ) )
1222 break;
1223 }
1224
1225 // If there were no digits then just set the single string
1226 if( ii < 0 )
1227 {
1228 *strBeginning = strToSplit;
1229 }
1230 else
1231 {
1232 // Since there is at least one digit this is the trailing string
1233 *strEnd = strToSplit.substr( ii + 1 );
1234
1235 // Go to the end of the digits
1236 int position = ii + 1;
1237
1238 for( ; ii >= 0; ii-- )
1239 {
1240 if( !wxIsdigit( strToSplit[ii] ) && separators.Find( strToSplit[ii] ) < 0 )
1241 break;
1242 }
1243
1244 // If all that was left was digits, then just set the digits string
1245 if( ii < 0 )
1246 *strDigits = strToSplit.substr( 0, position );
1247
1248 /* We were only looking for the last set of digits everything else is
1249 * part of the preamble */
1250 else
1251 {
1252 *strDigits = strToSplit.substr( ii + 1, position - ii - 1 );
1253 *strBeginning = strToSplit.substr( 0, ii + 1 );
1254 }
1255 }
1256
1257 return 0;
1258}
1259
1260
1261int GetTrailingInt( const wxString& aStr )
1262{
1263 int number = 0;
1264 int base = 1;
1265
1266 // Trim and extract the trailing numeric part
1267 int index = aStr.Len() - 1;
1268
1269 while( index >= 0 )
1270 {
1271 const char chr = aStr.GetChar( index );
1272
1273 if( chr < '0' || chr > '9' )
1274 break;
1275
1276 number += ( chr - '0' ) * base;
1277 base *= 10;
1278 index--;
1279 }
1280
1281 return number;
1282}
1283
1284
1286{
1288}
1289
1290
1291bool ReplaceIllegalFileNameChars( std::string* aName, int aReplaceChar )
1292{
1293 bool changed = false;
1294 std::string result;
1295 result.reserve( aName->length() );
1296
1297 for( std::string::iterator it = aName->begin(); it != aName->end(); ++it )
1298 {
1299 if( strchr( illegalFileNameChars, *it ) )
1300 {
1301 if( aReplaceChar )
1302 StrPrintf( &result, "%c", aReplaceChar );
1303 else
1304 StrPrintf( &result, "%%%02x", *it );
1305
1306 changed = true;
1307 }
1308 else
1309 {
1310 result += *it;
1311 }
1312 }
1313
1314 if( changed )
1315 *aName = std::move( result );
1316
1317 return changed;
1318}
1319
1320
1321bool ReplaceIllegalFileNameChars( wxString& aName, int aReplaceChar )
1322{
1323 bool changed = false;
1324 wxString result;
1325 result.reserve( aName.Length() );
1326 wxString illWChars = GetIllegalFileNameWxChars();
1327
1328 for( wxString::iterator it = aName.begin(); it != aName.end(); ++it )
1329 {
1330 if( illWChars.Find( *it ) != wxNOT_FOUND )
1331 {
1332 if( aReplaceChar )
1333 result += aReplaceChar;
1334 else
1335 result += wxString::Format( "%%%02x", *it );
1336
1337 changed = true;
1338 }
1339 else
1340 {
1341 result += *it;
1342 }
1343 }
1344
1345 if( changed )
1346 aName = std::move( result );
1347
1348 return changed;
1349}
1350
1351
1352void wxStringSplit( const wxString& aText, wxArrayString& aStrings, wxChar aSplitter )
1353{
1354 wxString tmp;
1355
1356 for( unsigned ii = 0; ii < aText.Length(); ii++ )
1357 {
1358 if( aText[ii] == aSplitter )
1359 {
1360 aStrings.Add( tmp );
1361 tmp.Clear();
1362 }
1363 else
1364 {
1365 tmp << aText[ii];
1366 }
1367 }
1368
1369 if( !tmp.IsEmpty() )
1370 aStrings.Add( tmp );
1371}
1372
1373
1374void StripTrailingZeros( wxString& aStringValue, unsigned aTrailingZeroAllowed )
1375{
1376 struct lconv* lc = localeconv();
1377 char sep = lc->decimal_point[0];
1378 unsigned sep_pos = aStringValue.Find( sep );
1379
1380 if( sep_pos > 0 )
1381 {
1382 // We want to keep at least aTrailingZeroAllowed digits after the separator
1383 unsigned min_len = sep_pos + aTrailingZeroAllowed + 1;
1384
1385 while( aStringValue.Len() > min_len )
1386 {
1387 if( aStringValue.Last() == '0' )
1388 aStringValue.RemoveLast();
1389 else
1390 break;
1391 }
1392 }
1393}
1394
1395
1396std::string FormatDouble2Str( double aValue )
1397{
1398 std::string buf;
1399
1400 if( aValue != 0.0 && std::fabs( aValue ) <= 0.0001 )
1401 {
1402 buf = fmt::format( "{:.16f}", aValue );
1403
1404 // remove trailing zeros (and the decimal marker if needed)
1405 while( !buf.empty() && buf[buf.size() - 1] == '0' )
1406 {
1407 buf.pop_back();
1408 }
1409
1410 // if the value was really small
1411 // we may have just stripped all the zeros after the decimal
1412 if( buf[buf.size() - 1] == '.' )
1413 {
1414 buf.pop_back();
1415 }
1416 }
1417 else
1418 {
1419 buf = fmt::format( "{:.10g}", aValue );
1420 }
1421
1422 return buf;
1423}
1424
1425
1426std::string UIDouble2Str( double aValue )
1427{
1428 char buf[50];
1429 int len;
1430
1431 if( aValue != 0.0 && std::fabs( aValue ) <= 0.0001 )
1432 {
1433 // For these small values, %f works fine,
1434 // and %g gives an exponent
1435 len = snprintf( buf, sizeof( buf ), "%.16f", aValue );
1436
1437 while( --len > 0 && buf[len] == '0' )
1438 buf[len] = '\0';
1439
1440 if( buf[len] == '.' || buf[len] == ',' )
1441 buf[len] = '\0';
1442 else
1443 ++len;
1444 }
1445 else
1446 {
1447 // For these values, %g works fine, and sometimes %f
1448 // gives a bad value (try aValue = 1.222222222222, with %.16f format!)
1449 len = snprintf( buf, sizeof( buf ), "%.10g", aValue );
1450 }
1451
1452 return std::string( buf, len );
1453}
1454
1455
1456wxString From_UTF8( const char* cstring )
1457{
1458 // Convert an expected UTF8 encoded C string to a wxString
1459 wxString line = wxString::FromUTF8( cstring );
1460
1461 if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence
1462 {
1463 line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion
1464
1465 if( line.IsEmpty() )
1466 line = wxString::From8BitData( cstring ); // try to use native string
1467 }
1468
1469 return line;
1470}
1471
1472
1473wxString From_UTF8( const std::string& aString )
1474{
1475 // Convert an expected UTF8 encoded std::string to a wxString
1476 wxString line = wxString::FromUTF8( aString );
1477
1478 if( line.IsEmpty() ) // happens when aString is not a valid UTF8 sequence
1479 {
1480 line = wxConvCurrent->cMB2WC( aString.c_str() ); // try to use locale conversion
1481
1482 if( line.IsEmpty() )
1483 line = wxString::From8BitData( aString.c_str() ); // try to use native string
1484 }
1485
1486 return line;
1487}
1488
1489
1490wxString NormalizeFileUri( const wxString& aFileUri )
1491{
1492 wxString uriPathAndFileName;
1493
1494 wxCHECK( aFileUri.StartsWith( wxS( "file://" ), &uriPathAndFileName ), aFileUri );
1495
1496 wxString tmp = uriPathAndFileName;
1497 wxString retv = wxS( "file://" );
1498
1499 tmp.Replace( wxS( "\\" ), wxS( "/" ) );
1500 tmp.Replace( wxS( ":" ), wxS( "" ) );
1501
1502 if( !tmp.IsEmpty() && tmp[0] != '/' )
1503 tmp = wxS( "/" ) + tmp;
1504
1505 retv += tmp;
1506
1507 return retv;
1508}
1509
1510
1511namespace
1512{
1513 // Extract (prefix, numericValue) where numericValue = -1 if no numeric suffix
1514 std::pair<wxString, long> ParseAlphaNumericPin( const wxString& pinNum )
1515 {
1516 wxString prefix;
1517 long numValue = -1;
1518
1519 size_t numStart = pinNum.length();
1520 for( int i = static_cast<int>( pinNum.length() ) - 1; i >= 0; --i )
1521 {
1522 if( !wxIsdigit( pinNum[i] ) )
1523 {
1524 numStart = i + 1;
1525 break;
1526 }
1527 if( i == 0 )
1528 numStart = 0; // all digits
1529 }
1530
1531 if( numStart < pinNum.length() )
1532 {
1533 prefix = pinNum.Left( numStart );
1534 wxString numericPart = pinNum.Mid( numStart );
1535 numericPart.ToLong( &numValue );
1536 }
1537
1538 return { prefix, numValue };
1539 }
1540}
1541
1542std::vector<wxString> ExpandStackedPinNotation( const wxString& aPinName, bool* aValid )
1543{
1544 if( aValid )
1545 *aValid = true;
1546
1547 std::vector<wxString> expanded;
1548
1549 const bool hasOpenBracket = aPinName.Contains( wxT( "[" ) );
1550 const bool hasCloseBracket = aPinName.Contains( wxT( "]" ) );
1551
1552 if( hasOpenBracket || hasCloseBracket )
1553 {
1554 if( !aPinName.StartsWith( wxT( "[" ) ) || !aPinName.EndsWith( wxT( "]" ) ) )
1555 {
1556 if( aValid )
1557 *aValid = false;
1558 expanded.push_back( aPinName );
1559 return expanded;
1560 }
1561 }
1562
1563 if( !aPinName.StartsWith( wxT( "[" ) ) || !aPinName.EndsWith( wxT( "]" ) ) )
1564 {
1565 expanded.push_back( aPinName );
1566 return expanded;
1567 }
1568
1569 const wxString inner = aPinName.Mid( 1, aPinName.Length() - 2 );
1570
1571 size_t start = 0;
1572 while( start < inner.length() )
1573 {
1574 size_t comma = inner.find( ',', start );
1575 wxString part = ( comma == wxString::npos ) ? inner.Mid( start ) : inner.Mid( start, comma - start );
1576 part.Trim( true ).Trim( false );
1577 if( part.empty() )
1578 {
1579 start = ( comma == wxString::npos ) ? inner.length() : comma + 1;
1580 continue;
1581 }
1582
1583 int dashPos = part.Find( '-' );
1584 if( dashPos != wxNOT_FOUND )
1585 {
1586 wxString startTxt = part.Left( dashPos );
1587 wxString endTxt = part.Mid( dashPos + 1 );
1588 startTxt.Trim( true ).Trim( false );
1589 endTxt.Trim( true ).Trim( false );
1590
1591 auto [startPrefix, startVal] = ParseAlphaNumericPin( startTxt );
1592 auto [endPrefix, endVal] = ParseAlphaNumericPin( endTxt );
1593
1594 if( startPrefix != endPrefix || startVal == -1 || endVal == -1 || startVal > endVal )
1595 {
1596 if( aValid )
1597 *aValid = false;
1598 expanded.clear();
1599 expanded.push_back( aPinName );
1600 return expanded;
1601 }
1602
1603 for( long ii = startVal; ii <= endVal; ++ii )
1604 {
1605 if( startPrefix.IsEmpty() )
1606 expanded.emplace_back( wxString::Format( wxT( "%ld" ), ii ) );
1607 else
1608 expanded.emplace_back( wxString::Format( wxT( "%s%ld" ), startPrefix, ii ) );
1609 }
1610 }
1611 else
1612 {
1613 expanded.push_back( part );
1614 }
1615
1616 if( comma == wxString::npos )
1617 break;
1618 start = comma + 1;
1619 }
1620
1621 if( expanded.empty() )
1622 {
1623 expanded.push_back( aPinName );
1624 if( aValid )
1625 *aValid = false;
1626 }
1627
1628 return expanded;
1629}
1630
1631
1633{
1634 return wxString( defaultVariantName );
1635}
1636
1637
1638int SortVariantNames( const wxString& aLhs, const wxString& aRhs )
1639{
1640 if( ( aLhs == defaultVariantName ) && ( aRhs != defaultVariantName ) )
1641 return -1;
1642
1643 if( ( aLhs != defaultVariantName ) && ( aRhs == defaultVariantName ) )
1644 return 1;
1645
1646 return StrNumCmp( aLhs, aRhs );
1647}
1648
This file contains miscellaneous commonly used macros and functions.
std::optional< V > get_opt(const std::map< wxString, V > &aMap, const wxString &aKey)
Definition map_helpers.h:34
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:71
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString RemoveHTMLTags(const wxString &aInput)
Removes HTML tags from a string.
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 GetDefaultVariantName()
std::vector< wxString > ExpandStackedPinNotation(const wxString &aPinName, bool *aValid)
Expand stacked pin notation like [1,2,3], [1-4], [A1-A4], or [AA1-AA3,AB4,CD12-CD14] into individual ...
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 LinkifyHTML(wxString aStr)
Wraps links in HTML tags.
bool convertSeparators(wxString *value)
wxString GetIllegalFileNameWxChars()
wxString From_UTF8(const char *cstring)
int SortVariantNames(const wxString &aLhs, const wxString &aRhs)
bool ConvertSmartQuotesAndDashes(wxString *aString)
Convert curly quotes and em/en dashes to straight quotes and dashes.
static const wxChar defaultVariantName[]
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 InitialCaps(const wxString &aString)
Capitalize only the first word.
wxString NormalizeFileUri(const wxString &aFileUri)
Normalize file path aFileUri to URI convention.
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
wxString GetISO8601CurrentDateTime()
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.
bool IsFullFileNameValid(const wxString &aFullFilename)
Checks if a full filename is valid, i.e.
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.
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
bool IsURL(wxString aStr)
Performs a URL sniff-test on a string.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
ESCAPE_CONTEXT
Escape/Unescape routines to safely encode reserved-characters in various contexts.
@ CTX_FILENAME
@ CTX_QUOTED_STR
@ CTX_LINE
@ CTX_NO_SPACE
@ CTX_LIBID
@ CTX_NETNAME
@ CTX_CSV
@ CTX_IPC
@ CTX_LEGACY_LIBID
@ CTX_JS_STR
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.