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 == "~" )
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 
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 
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 
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 
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 
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 
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 
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 
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 
477 bool NoPrintableChars( const wxString& aString )
478 {
479  wxString tmp = aString;
480 
481  return tmp.Trim( true ).Trim( false ).IsEmpty();
482 }
483 
484 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
939 {
941 }
942 
943 
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 
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 
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 
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 
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 
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 }
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.