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