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