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