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