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 += "&quot;";
461         else if( c == '\'' )
462             converted += "&apos;";
463         else if( c == '&' )
464             converted += "&amp;";
465         else if( c == '<' )
466             converted += "&lt;";
467         else if( c == '>' )
468             converted += "&gt;";
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