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-2019 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 <fctsys.h>
30 #include <macros.h>
31 #include <richio.h> // StrPrintf
32 #include <kicad_string.h>
33 
34 
40 static const char illegalFileNameChars[] = "\\/:\"<>|";
41 
42 
43 bool ConvertSmartQuotesAndDashes( wxString* aString )
44 {
45  bool retVal = false;
46 
47  for( wxString::iterator ii = aString->begin(); ii != aString->end(); ++ii )
48  {
49  if( *ii == L'\u0060' || *ii == L'\u00B4' || *ii == L'\u2018' || *ii == L'\u2019' )
50  {
51  *ii = '\'';
52  retVal = true;
53  }
54  if( *ii == L'\u201C' || *ii == L'\u201D' )
55  {
56  *ii = '"';
57  retVal = true;
58  }
59  if( *ii == L'\u2013' || *ii == L'\u2014' )
60  {
61  *ii = '-';
62  retVal = true;
63  }
64  }
65 
66  return retVal;
67 }
68 
69 
77 wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext )
78 {
79  wxString converted;
80 
81  for( wxUniChar c: aSource )
82  {
83  if( aContext == CTX_NETNAME )
84  {
85  if( c == '/' )
86  converted += "{slash}";
87  else if( c == '\n' || c == '\r' )
88  converted += ""; // drop
89  else
90  converted += c;
91  }
92  else if( aContext == CTX_LIBID )
93  {
94  if( c == '{' )
95  converted += "{brace}";
96  else if( c == ':' )
97  converted += "{colon}";
98  else if( c == '\n' || c == '\r' )
99  converted += ""; // drop
100  else
101  converted += c;
102  }
103  else if( aContext == CTX_QUOTED_STR )
104  {
105  if( c == '{' )
106  converted += "{brace}";
107  else if( c == '\"' )
108  converted += "{dblquote}";
109  else
110  converted += c;
111  }
112  else if( aContext == CTX_LINE )
113  {
114  if( c == '\n' || c == '\r' )
115  converted += "{return}";
116  else
117  converted += c;
118  }
119  else if( aContext == CTX_FILENAME )
120  {
121  if( c == '{' )
122  converted += "{brace}";
123  else if( c == '/' )
124  converted += "{slash}";
125  else if( c == '\\' )
126  converted += "{backslash}";
127  else if( c == '\"' )
128  converted += "{dblquote}";
129  else if( c == '<' )
130  converted += "{lt}";
131  else if( c == '>' )
132  converted += "{gt}";
133  else if( c == '|' )
134  converted += "{bar}";
135  else if( c == ':' )
136  converted += "{colon}";
137  else if( c == '\t' )
138  converted += "{tab}";
139  else if( c == '\n' || c == '\r' )
140  converted += "{return}";
141  else
142  converted += c;
143  }
144  else
145  converted += c;
146  }
147 
148  return converted;
149 }
150 
151 
152 wxString UnescapeString( const wxString& aSource )
153 {
154  wxString newbuf;
155  size_t sourceLen = aSource.length();
156 
157  for( size_t i = 0; i < sourceLen; ++i )
158  {
159  if( ( aSource[i] == '$' || aSource[i] == '^' || aSource[i] == '_' )
160  && i + 1 < sourceLen && aSource[i+1] == '{' )
161  {
162  for( ; i < sourceLen; ++i )
163  {
164  newbuf += aSource[i];
165 
166  if( aSource[i] == '}' )
167  break;
168  }
169  }
170  else if( aSource[i] == '{' )
171  {
172  wxString token;
173 
174  for( i = i + 1; i < sourceLen; ++i )
175  {
176  if( aSource[i] == '}' )
177  break;
178  else
179  token.append( aSource[i] );
180  }
181 
182  if( token == wxS( "dblquote" ) ) newbuf.append( wxS( "\"" ) );
183  else if( token == wxS( "quote" ) ) newbuf.append( wxS( "'" ) );
184  else if( token == wxS( "lt" ) ) newbuf.append( wxS( "<" ) );
185  else if( token == wxS( "gt" ) ) newbuf.append( wxS( ">" ) );
186  else if( token == wxS( "backslash" ) ) newbuf.append( wxS( "\\" ) );
187  else if( token == wxS( "slash" ) ) newbuf.append( wxS( "/" ) );
188  else if( token == wxS( "bar" ) ) newbuf.append( wxS( "|" ) );
189  else if( token == wxS( "colon" ) ) newbuf.append( wxS( ":" ) );
190  else if( token == wxS( "space" ) ) newbuf.append( wxS( " " ) );
191  else if( token == wxS( "dollar" ) ) newbuf.append( wxS( "$" ) );
192  else if( token == wxS( "tab" ) ) newbuf.append( wxS( "\t" ) );
193  else if( token == wxS( "return" ) ) newbuf.append( wxS( "\n" ) );
194  else if( token == wxS( "brace" ) ) newbuf.append( wxS( "{" ) );
195  else
196  {
197  newbuf.append( "{" + token + "}" );
198  }
199  }
200  else
201  {
202  newbuf.append( aSource[i] );
203  }
204  }
205 
206  return newbuf;
207 }
208 
209 
210 int ReadDelimitedText( wxString* aDest, const char* aSource )
211 {
212  std::string utf8; // utf8 but without escapes and quotes.
213  bool inside = false;
214  const char* start = aSource;
215  char cc;
216 
217  while( (cc = *aSource++) != 0 )
218  {
219  if( cc == '"' )
220  {
221  if( inside )
222  break; // 2nd double quote is end of delimited text
223 
224  inside = true; // first delimiter found, make note, do not copy
225  }
226 
227  else if( inside )
228  {
229  if( cc == '\\' )
230  {
231  cc = *aSource++;
232 
233  if( !cc )
234  break;
235 
236  // do no copy the escape byte if it is followed by \ or "
237  if( cc != '"' && cc != '\\' )
238  utf8 += '\\';
239 
240  utf8 += cc;
241  }
242  else
243  {
244  utf8 += cc;
245  }
246  }
247  }
248 
249  *aDest = FROM_UTF8( utf8.c_str() );
250 
251  return aSource - start;
252 }
253 
254 
255 int ReadDelimitedText( char* aDest, const char* aSource, int aDestSize )
256 {
257  if( aDestSize <= 0 )
258  return 0;
259 
260  bool inside = false;
261  const char* start = aSource;
262  char* limit = aDest + aDestSize - 1;
263  char cc;
264 
265  while( (cc = *aSource++) != 0 && aDest < limit )
266  {
267  if( cc == '"' )
268  {
269  if( inside )
270  break; // 2nd double quote is end of delimited text
271 
272  inside = true; // first delimiter found, make note, do not copy
273  }
274 
275  else if( inside )
276  {
277  if( cc == '\\' )
278  {
279  cc = *aSource++;
280 
281  if( !cc )
282  break;
283 
284  // do no copy the escape byte if it is followed by \ or "
285  if( cc != '"' && cc != '\\' )
286  *aDest++ = '\\';
287 
288  if( aDest < limit )
289  *aDest++ = cc;
290  }
291  else
292  {
293  *aDest++ = cc;
294  }
295  }
296  }
297 
298  *aDest = 0;
299 
300  return aSource - start;
301 }
302 
303 
304 std::string EscapedUTF8( wxString aString )
305 {
306  // No new-lines allowed in quoted strings
307  aString.Replace( "\r\n", "\r" );
308  aString.Replace( "\n", "\r" );
309 
310  std::string utf8 = TO_UTF8( aString );
311 
312  std::string ret;
313 
314  ret += '"';
315 
316  for( std::string::const_iterator it = utf8.begin(); it!=utf8.end(); ++it )
317  {
318  // this escaping strategy is designed to be compatible with ReadDelimitedText():
319  if( *it == '"' )
320  {
321  ret += '\\';
322  ret += '"';
323  }
324  else if( *it == '\\' )
325  {
326  ret += '\\'; // double it up
327  ret += '\\';
328  }
329  else
330  {
331  ret += *it;
332  }
333  }
334 
335  ret += '"';
336 
337  return ret;
338 }
339 
340 
341 wxString EscapedHTML( const wxString& aString )
342 {
343  wxString converted;
344 
345  for( wxUniChar c: aString )
346  {
347  if( c == '\"' )
348  converted += "&quot;";
349  else if( c == '\'' )
350  converted += "&apos;";
351  else if( c == '&' )
352  converted += "&amp;";
353  else if( c == '<' )
354  converted += "&lt;";
355  else if( c == '>' )
356  converted += "&gt;";
357  else
358  converted += c;
359  }
360 
361  return converted;
362 }
363 
364 
365 char* StrPurge( char* text )
366 {
367  static const char whitespace[] = " \t\n\r\f\v";
368 
369  if( text )
370  {
371  while( *text && strchr( whitespace, *text ) )
372  ++text;
373 
374  char* cp = text + strlen( text ) - 1;
375 
376  while( cp >= text && strchr( whitespace, *cp ) )
377  *cp-- = '\0';
378  }
379 
380  return text;
381 }
382 
383 
384 char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine )
385 {
386  do {
387  if( fgets( Line, SizeLine, File ) == NULL )
388  return NULL;
389 
390  if( LineNum )
391  *LineNum += 1;
392 
393  } while( Line[0] == '#' || Line[0] == '\n' || Line[0] == '\r' || Line[0] == 0 );
394 
395  strtok( Line, "\n\r" );
396  return Line;
397 }
398 
399 
400 wxString DateAndTime()
401 {
402  wxDateTime datetime = wxDateTime::Now();
403 
404  datetime.SetCountry( wxDateTime::Country_Default );
405  return datetime.Format( wxDefaultDateTimeFormat, wxDateTime::Local );
406 }
407 
408 
409 int StrNumCmp( const wxString& aString1, const wxString& aString2, bool aIgnoreCase )
410 {
411  int nb1 = 0, nb2 = 0;
412 
413  auto str1 = aString1.begin();
414  auto str2 = aString2.begin();
415 
416  while( str1 != aString1.end() && str2 != aString2.end() )
417  {
418  wxUniChar c1 = *str1;
419  wxUniChar c2 = *str2;
420 
421  if( wxIsdigit( c1 ) && wxIsdigit( c2 ) ) // Both characters are digits, do numeric compare.
422  {
423  nb1 = 0;
424  nb2 = 0;
425 
426  do
427  {
428  c1 = *str1;
429  nb1 = nb1 * 10 + (int) c1 - '0';
430  ++str1;
431  } while( str1 != aString1.end() && wxIsdigit( *str1 ) );
432 
433  do
434  {
435  c2 = *str2;
436  nb2 = nb2 * 10 + (int) c2 - '0';
437  ++str2;
438  } while( str2 != aString2.end() && wxIsdigit( *str2 ) );
439 
440  if( nb1 < nb2 )
441  return -1;
442 
443  if( nb1 > nb2 )
444  return 1;
445 
446  c1 = ( str1 != aString1.end() ) ? *str1 : wxUniChar( 0 );
447  c2 = ( str2 != aString2.end() ) ? *str2 : wxUniChar( 0 );
448  }
449 
450  // Any numerical comparisons to here are identical.
451  if( aIgnoreCase )
452  {
453  if( wxToupper( c1 ) < wxToupper( c2 ) )
454  return -1;
455 
456  if( wxToupper( c1 ) > wxToupper( c2 ) )
457  return 1;
458  }
459  else
460  {
461  if( c1 < c2 )
462  return -1;
463 
464  if( c1 > c2 )
465  return 1;
466  }
467 
468  if( str1 != aString1.end() )
469  ++str1;
470 
471  if( str2 != aString2.end() )
472  ++str2;
473  }
474 
475  if( str1 == aString1.end() && str2 != aString2.end() )
476  {
477  return -1; // Identical to here but aString1 is longer.
478  }
479  else if( str1 != aString1.end() && str2 == aString2.end() )
480  {
481  return 1; // Identical to here but aString2 is longer.
482  }
483 
484  return 0;
485 }
486 
487 
488 bool WildCompareString( const wxString& pattern, const wxString& string_to_tst,
489  bool case_sensitive )
490 {
491  const wxChar* cp = NULL, * mp = NULL;
492  const wxChar* wild, * string;
493  wxString _pattern, _string_to_tst;
494 
495  if( case_sensitive )
496  {
497  wild = pattern.GetData();
498  string = string_to_tst.GetData();
499  }
500  else
501  {
502  _pattern = pattern;
503  _pattern.MakeUpper();
504  _string_to_tst = string_to_tst;
505  _string_to_tst.MakeUpper();
506  wild = _pattern.GetData();
507  string = _string_to_tst.GetData();
508  }
509 
510  while( ( *string ) && ( *wild != '*' ) )
511  {
512  if( ( *wild != *string ) && ( *wild != '?' ) )
513  return false;
514 
515  wild++; string++;
516  }
517 
518  while( *string )
519  {
520  if( *wild == '*' )
521  {
522  if( !*++wild )
523  return 1;
524  mp = wild;
525  cp = string + 1;
526  }
527  else if( ( *wild == *string ) || ( *wild == '?' ) )
528  {
529  wild++;
530  string++;
531  }
532  else
533  {
534  wild = mp;
535  string = cp++;
536  }
537  }
538 
539  while( *wild == '*' )
540  {
541  wild++;
542  }
543 
544  return !*wild;
545 }
546 
547 
548 bool ApplyModifier( double& value, const wxString& aString )
549 {
550  static const wxString modifiers( wxT( "pnumkKM" ) );
551 
552  if( !aString.length() )
553  return false;
554 
555  wxChar modifier;
556  wxString units;
557 
558  if( modifiers.Find( aString[ 0 ] ) >= 0 )
559  {
560  modifier = aString[ 0 ];
561  units = aString.Mid( 1 ).Trim();
562  }
563  else
564  {
565  modifier = ' ';
566  units = aString.Mid( 0 ).Trim();
567  }
568 
569  if( units.length()
570  && !units.CmpNoCase( wxT( "F" ) )
571  && !units.CmpNoCase( wxT( "hz" ) )
572  && !units.CmpNoCase( wxT( "W" ) )
573  && !units.CmpNoCase( wxT( "V" ) )
574  && !units.CmpNoCase( wxT( "H" ) ) )
575  return false;
576 
577  if( modifier == 'p' )
578  value *= 1.0e-12;
579  if( modifier == 'n' )
580  value *= 1.0e-9;
581  else if( modifier == 'u' )
582  value *= 1.0e-6;
583  else if( modifier == 'm' )
584  value *= 1.0e-3;
585  else if( modifier == 'k' || modifier == 'K' )
586  value *= 1.0e3;
587  else if( modifier == 'M' )
588  value *= 1.0e6;
589  else if( modifier == 'G' )
590  value *= 1.0e9;
591 
592  return true;
593 }
594 
595 
596 int ValueStringCompare( wxString strFWord, wxString strSWord )
597 {
598  // Compare unescaped text
599  strFWord = UnescapeString( strFWord );
600  strSWord = UnescapeString( strSWord );
601 
602  // The different sections of the two strings
603  wxString strFWordBeg, strFWordMid, strFWordEnd;
604  wxString strSWordBeg, strSWordMid, strSWordEnd;
605 
606  // Split the two strings into separate parts
607  SplitString( strFWord, &strFWordBeg, &strFWordMid, &strFWordEnd );
608  SplitString( strSWord, &strSWordBeg, &strSWordMid, &strSWordEnd );
609 
610  // Compare the Beginning section of the strings
611  int isEqual = strFWordBeg.CmpNoCase( strSWordBeg );
612 
613  if( isEqual > 0 )
614  return 1;
615  else if( isEqual < 0 )
616  return -1;
617  else
618  {
619  // If the first sections are equal compare their digits
620  double lFirstNumber = 0;
621  double lSecondNumber = 0;
622  bool endingIsModifier = false;
623 
624  strFWordMid.ToDouble( &lFirstNumber );
625  strSWordMid.ToDouble( &lSecondNumber );
626 
627  endingIsModifier |= ApplyModifier( lFirstNumber, strFWordEnd );
628  endingIsModifier |= ApplyModifier( lSecondNumber, strSWordEnd );
629 
630  if( lFirstNumber > lSecondNumber )
631  return 1;
632  else if( lFirstNumber < lSecondNumber )
633  return -1;
634  // If the first two sections are equal and the endings are modifiers then compare them
635  else if( !endingIsModifier )
636  return strFWordEnd.CmpNoCase( strSWordEnd );
637  // Ran out of things to compare; they must match
638  else
639  return 0;
640  }
641 }
642 
643 
644 int SplitString( wxString strToSplit,
645  wxString* strBeginning,
646  wxString* strDigits,
647  wxString* strEnd )
648 {
649  static const wxString separators( wxT( ".," ) );
650 
651  // Clear all the return strings
652  strBeginning->Empty();
653  strDigits->Empty();
654  strEnd->Empty();
655 
656  // There no need to do anything if the string is empty
657  if( strToSplit.length() == 0 )
658  return 0;
659 
660  // Starting at the end of the string look for the first digit
661  int ii;
662 
663  for( ii = (strToSplit.length() - 1); ii >= 0; ii-- )
664  {
665  if( wxIsdigit( strToSplit[ii] ) )
666  break;
667  }
668 
669  // If there were no digits then just set the single string
670  if( ii < 0 )
671  {
672  *strBeginning = strToSplit;
673  }
674  else
675  {
676  // Since there is at least one digit this is the trailing string
677  *strEnd = strToSplit.substr( ii + 1 );
678 
679  // Go to the end of the digits
680  int position = ii + 1;
681 
682  for( ; ii >= 0; ii-- )
683  {
684  if( !wxIsdigit( strToSplit[ii] ) && separators.Find( strToSplit[ii] ) < 0 )
685  break;
686  }
687 
688  // If all that was left was digits, then just set the digits string
689  if( ii < 0 )
690  *strDigits = strToSplit.substr( 0, position );
691 
692  /* We were only looking for the last set of digits everything else is
693  * part of the preamble */
694  else
695  {
696  *strDigits = strToSplit.substr( ii + 1, position - ii - 1 );
697  *strBeginning = strToSplit.substr( 0, ii + 1 );
698  }
699  }
700 
701  return 0;
702 }
703 
704 
705 int GetTrailingInt( const wxString& aStr )
706 {
707  int number = 0;
708  int base = 1;
709 
710  // Trim and extract the trailing numeric part
711  int index = aStr.Len() - 1;
712 
713  while( index >= 0 )
714  {
715  const char chr = aStr.GetChar( index );
716 
717  if( chr < '0' || chr > '9' )
718  break;
719 
720  number += ( chr - '0' ) * base;
721  base *= 10;
722  index--;
723  }
724 
725  return number;
726 }
727 
728 
730 {
732 }
733 
734 
735 bool ReplaceIllegalFileNameChars( std::string* aName, int aReplaceChar )
736 {
737  bool changed = false;
738  std::string result;
739  result.reserve( aName->length() );
740 
741  for( std::string::iterator it = aName->begin(); it != aName->end(); ++it )
742  {
743  if( strchr( illegalFileNameChars, *it ) )
744  {
745  if( aReplaceChar )
746  StrPrintf( &result, "%c", aReplaceChar );
747  else
748  StrPrintf( &result, "%%%02x", *it );
749 
750  changed = true;
751  }
752  else
753  {
754  result += *it;
755  }
756  }
757 
758  if( changed )
759  *aName = result;
760 
761  return changed;
762 }
763 
764 
765 bool ReplaceIllegalFileNameChars( wxString& aName, int aReplaceChar )
766 {
767  bool changed = false;
768  wxString result;
769  result.reserve( aName.Length() );
770  wxString illWChars = GetIllegalFileNameWxChars();
771 
772  for( wxString::iterator it = aName.begin(); it != aName.end(); ++it )
773  {
774  if( illWChars.Find( *it ) != wxNOT_FOUND )
775  {
776  if( aReplaceChar )
777  result += aReplaceChar;
778  else
779  result += wxString::Format( "%%%02x", *it );
780 
781  changed = true;
782  }
783  else
784  {
785  result += *it;
786  }
787  }
788 
789  if( changed )
790  aName = result;
791 
792  return changed;
793 }
static const char illegalFileNameChars[]
Illegal file name characters used to insure file names will be valid on all supported platforms.
Definition: string.cpp:40
ESCAPE_CONTEXT
Escape/Unescape routines to safely encode reserved-characters in various contexts.
Definition: kicad_string.h:52
int SplitString(wxString strToSplit, wxString *strBeginning, wxString *strDigits, wxString *strEnd)
Breaks a string into three parts: he alphabetic preamble, the numeric part, and any alphabetic ending...
Definition: string.cpp:644
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Definition: string.cpp:409
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:114
char * StrPurge(char *text)
Remove leading and training spaces, tabs and end of line chars in text.
Definition: string.cpp:365
wxString EscapedHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
Definition: string.cpp:341
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
Definition: string.cpp:735
This file contains miscellaneous commonly used macros and functions.
std::string EscapedUTF8(wxString aString)
Function EscapedUTF8 returns an 8 bit UTF8 string given aString in unicode form.
Definition: string.cpp:304
int StrPrintf(std::string *result, const char *format,...)
Function StrPrintf is like sprintf() but the output is appended to a std::string instead of to a char...
Definition: richio.cpp:74
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:100
#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:596
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:488
wxString GetIllegalFileNameWxChars()
Definition: string.cpp:729
char * GetLine(FILE *File, char *Line, int *LineNum, int SizeLine)
Read one line line from aFile.
Definition: string.cpp:384
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:152
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
These Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which ar...
Definition: string.cpp:77
int GetTrailingInt(const wxString &aStr)
Gets the trailing int, if any, from a string.
Definition: string.cpp:705
int ReadDelimitedText(wxString *aDest, const char *aSource)
Copy bytes from aSource delimited string segment to aDest wxString.
Definition: string.cpp:210
bool ApplyModifier(double &value, const wxString &aString)
Definition: string.cpp:548
bool ConvertSmartQuotesAndDashes(wxString *aString)
Converts curly quotes and em/en dashes to straight quotes and dashes.
Definition: string.cpp:43
wxString DateAndTime()
Definition: string.cpp:400