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