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