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