KiCad PCB EDA Suite
common.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) 2014-2020 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <eda_base_frame.h>
27 #include <project.h>
28 #include <common.h>
29 #include <reporter.h>
30 #include <macros.h>
31 #include <mutex>
32 #include <wx/process.h>
33 #include <wx/config.h>
34 #include <wx/utils.h>
35 #include <wx/stdpaths.h>
36 #include <wx/url.h>
37 #include <wx/wx.h>
38 
39 
40 int ProcessExecute( const wxString& aCommandLine, int aFlags, wxProcess *callback )
41 {
42  return (int) wxExecute( aCommandLine, aFlags, callback );
43 }
44 
45 
46 enum Bracket
47 {
51 #ifdef __WINDOWS__
52  Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
53 #endif
55 };
56 
57 
58 wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject )
59 {
60  return ExpandTextVars( aSource, nullptr, nullptr, aProject );
61 }
62 
63 
64 wxString ExpandTextVars( const wxString& aSource,
65  const std::function<bool( wxString* )>* aLocalResolver,
66  const std::function<bool( wxString* )>* aFallbackResolver,
67  const PROJECT* aProject )
68 {
69  wxString newbuf;
70  size_t sourceLen = aSource.length();
71 
72  newbuf.Alloc( sourceLen ); // best guess (improves performance)
73 
74  for( size_t i = 0; i < sourceLen; ++i )
75  {
76  if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
77  {
78  wxString token;
79 
80  for( i = i + 2; i < sourceLen; ++i )
81  {
82  if( aSource[i] == '}' )
83  break;
84  else
85  token.append( aSource[i] );
86  }
87 
88  if( token.IsEmpty() )
89  continue;
90 
91  if( aLocalResolver && (*aLocalResolver)( &token ) )
92  {
93  newbuf.append( token );
94  }
95  else if( aProject && aProject->TextVarResolver( &token ) )
96  {
97  newbuf.append( token );
98  }
99  else if( aFallbackResolver && (*aFallbackResolver)( &token ) )
100  {
101  newbuf.append( token );
102  }
103  else
104  {
105  // Token not resolved: leave the reference unchanged
106  newbuf.append( "${" + token + "}" );
107  }
108  }
109  else
110  {
111  newbuf.append( aSource[i] );
112  }
113  }
114 
115  return newbuf;
116 }
117 
118 
119 //
120 // Stolen from wxExpandEnvVars and then heavily optimized
121 //
122 wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject )
123 {
124  size_t strlen = str.length();
125 
126  wxString strResult;
127  strResult.Alloc( strlen ); // best guess (improves performance)
128 
129  for( size_t n = 0; n < strlen; n++ )
130  {
131  wxUniChar str_n = str[n];
132 
133  switch( str_n.GetValue() )
134  {
135 #ifdef __WINDOWS__
136  case wxT( '%' ):
137 #endif // __WINDOWS__
138  case wxT( '$' ):
139  {
140  Bracket bracket;
141 #ifdef __WINDOWS__
142  if( str_n == wxT( '%' ) )
143  bracket = Bracket_Windows;
144  else
145 #endif // __WINDOWS__
146  if( n == strlen - 1 )
147  {
148  bracket = Bracket_None;
149  }
150  else
151  {
152  switch( str[n + 1].GetValue() )
153  {
154  case wxT( '(' ):
155  bracket = Bracket_Normal;
156  str_n = str[++n]; // skip the bracket
157  break;
158 
159  case wxT( '{' ):
160  bracket = Bracket_Curly;
161  str_n = str[++n]; // skip the bracket
162  break;
163 
164  default:
165  bracket = Bracket_None;
166  }
167  }
168 
169  size_t m = n + 1;
170  wxUniChar str_m = str[m];
171 
172  while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
173  {
174  if( ++m == strlen )
175  {
176  str_m = 0;
177  break;
178  }
179 
180  str_m = str[m];
181  }
182 
183  wxString strVarName( str.c_str() + n + 1, m - n - 1 );
184 
185  // NB: use wxGetEnv instead of wxGetenv as otherwise variables
186  // set through wxSetEnv may not be read correctly!
187  bool expanded = false;
188  wxString tmp = strVarName;
189 
190  if( aProject && aProject->TextVarResolver( &tmp ) )
191  {
192  strResult += tmp;
193  expanded = true;
194  }
195  else if( wxGetEnv( strVarName, &tmp ) )
196  {
197  strResult += tmp;
198  expanded = true;
199  }
200  else
201  {
202  // variable doesn't exist => don't change anything
203 #ifdef __WINDOWS__
204  if ( bracket != Bracket_Windows )
205 #endif
206  if ( bracket != Bracket_None )
207  strResult << str[n - 1];
208 
209  strResult << str_n << strVarName;
210  }
211 
212  // check the closing bracket
213  if( bracket != Bracket_None )
214  {
215  if( m == strlen || str_m != (wxChar)bracket )
216  {
217  // under MSW it's common to have '%' characters in the registry
218  // and it's annoying to have warnings about them each time, so
219  // ignore them silently if they are not used for env vars
220  //
221  // under Unix, OTOH, this warning could be useful for the user to
222  // understand why isn't the variable expanded as intended
223 #ifndef __WINDOWS__
224  wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
225  "at position %u in '%s'." ),
226  (char)bracket, (unsigned int) (m + 1), str.c_str() );
227 #endif // __WINDOWS__
228  }
229  else
230  {
231  // skip closing bracket unless the variables wasn't expanded
232  if( !expanded )
233  strResult << (wxChar)bracket;
234 
235  m++;
236  }
237  }
238 
239  n = m - 1; // skip variable name
240  str_n = str[n];
241  }
242  break;
243 
244  case wxT( '\\' ):
245  // backslash can be used to suppress special meaning of % and $
246  if( n != strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
247  {
248  str_n = str[++n];
249  strResult += str_n;
250 
251  break;
252  }
254 
255  default:
256  strResult += str_n;
257  }
258  }
259 
260 #ifndef __WINDOWS__
261  if( strResult.StartsWith( "~" ) )
262  strResult.Replace( "~", wxGetHomeDir(), false );
263 #endif // __WINDOWS__
264 
265  return strResult;
266 }
267 
268 
269 const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aProject )
270 {
271  // wxGetenv( wchar_t* ) is not re-entrant on linux.
272  // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
273  static std::mutex getenv_mutex;
274 
275  std::lock_guard<std::mutex> lock( getenv_mutex );
276 
277  // We reserve the right to do this another way, by providing our own member function.
278  return KIwxExpandEnvVars( aString, aProject );
279 }
280 
281 
282 const wxString ResolveUriByEnvVars( const wxString& aUri, PROJECT* aProject )
283 {
284  wxString uri = ExpandTextVars( aUri, aProject );
285 
286  // URL-like URI: return as is.
287  wxURL url( uri );
288 
289  if( url.GetError() == wxURL_NOERR )
290  return uri;
291 
292  // Otherwise, the path points to a local file. Resolve environment variables if any.
293  return ExpandEnvVarSubstitutions( aUri, aProject );
294 }
295 
296 
297 bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName,
298  const wxString& aBaseFilename,
299  REPORTER* aReporter )
300 {
301  wxString msg;
302  wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
303 
304  // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
305  // already an absolute path) absolute:
306  if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
307  {
308  if( aReporter )
309  {
310  msg.Printf( _( "Cannot make path \"%s\" absolute with respect to \"%s\"." ),
311  aTargetFullFileName->GetPath(),
312  baseFilePath );
313  aReporter->Report( msg, RPT_SEVERITY_ERROR );
314  }
315 
316  return false;
317  }
318 
319  // Ensure the path of aTargetFullFileName exists, and create it if needed:
320  wxString outputPath( aTargetFullFileName->GetPath() );
321 
322  if( !wxFileName::DirExists( outputPath ) )
323  {
324  // Make every directory provided when the provided path doesn't exist
325  if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
326  {
327  if( aReporter )
328  {
329  msg.Printf( _( "Output directory \"%s\" created.\n" ), outputPath );
330  aReporter->Report( msg, RPT_SEVERITY_INFO );
331  return true;
332  }
333  }
334  else
335  {
336  if( aReporter )
337  {
338  msg.Printf( _( "Cannot create output directory \"%s\".\n" ), outputPath );
339  aReporter->Report( msg, RPT_SEVERITY_ERROR );
340  }
341 
342  return false;
343  }
344  }
345 
346  return true;
347 }
348 
349 
350 // add this only if it is not in wxWidgets (for instance before 3.1.0)
351 #ifdef USE_KICAD_WXSTRING_HASH
352 size_t std::hash<wxString>::operator()( const wxString& s ) const
353 {
354  return std::hash<std::wstring>{}( s.ToStdWstring() );
355 }
356 #endif
357 
358 #ifdef USE_KICAD_WXPOINT_LESS_AND_HASH
359 size_t std::hash<wxPoint>::operator() ( const wxPoint& k ) const
360 {
361  auto xhash = std::hash<int>()( k.x );
362 
363  // 0x9e3779b9 is 2^33 / ( 1 + sqrt(5) )
364  // Adding this value ensures that consecutive bits of y will not be close to each other
365  // decreasing the likelihood of hash collision in similar values of x and y
366  return xhash ^ ( std::hash<int>()( k.y ) + 0x9e3779b9 + ( xhash << 6 ) + ( xhash >> 2 ) );
367 }
368 
369 bool std::less<wxPoint>::operator()( const wxPoint& aA, const wxPoint& aB ) const
370 {
371  if( aA.x == aB.x )
372  return aA.y < aB.y;
373 
374  return aA.x < aB.x;
375 }
376 #endif
377 
378 
379 std::ostream& operator<<( std::ostream& out, const wxSize& size )
380 {
381  out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\"";
382  return out;
383 }
384 
385 
386 std::ostream& operator<<( std::ostream& out, const wxPoint& pt )
387 {
388  out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\"";
389  return out;
390 }
391 
392 
407 bool matchWild( const char* pat, const char* text, bool dot_special )
408 {
409  if( !*text )
410  {
411  /* Match if both are empty. */
412  return !*pat;
413  }
414 
415  const char *m = pat,
416  *n = text,
417  *ma = NULL,
418  *na = NULL;
419  int just = 0,
420  acount = 0,
421  count = 0;
422 
423  if( dot_special && (*n == '.') )
424  {
425  /* Never match so that hidden Unix files
426  * are never found. */
427  return false;
428  }
429 
430  for(;;)
431  {
432  if( *m == '*' )
433  {
434  ma = ++m;
435  na = n;
436  just = 1;
437  acount = count;
438  }
439  else if( *m == '?' )
440  {
441  m++;
442 
443  if( !*n++ )
444  return false;
445  }
446  else
447  {
448  if( *m == '\\' )
449  {
450  m++;
451 
452  /* Quoting "nothing" is a bad thing */
453  if( !*m )
454  return false;
455  }
456  if( !*m )
457  {
458  /*
459  * If we are out of both strings or we just
460  * saw a wildcard, then we can say we have a
461  * match
462  */
463  if( !*n )
464  return true;
465 
466  if( just )
467  return true;
468 
469  just = 0;
470  goto not_matched;
471  }
472 
473  /*
474  * We could check for *n == NULL at this point, but
475  * since it's more common to have a character there,
476  * check to see if they match first (m and n) and
477  * then if they don't match, THEN we can check for
478  * the NULL of n
479  */
480  just = 0;
481 
482  if( *m == *n )
483  {
484  m++;
485  count++;
486  n++;
487  }
488  else
489  {
490  not_matched:
491 
492  /*
493  * If there are no more characters in the
494  * string, but we still need to find another
495  * character (*m != NULL), then it will be
496  * impossible to match it
497  */
498  if( !*n )
499  return false;
500 
501  if( ma )
502  {
503  m = ma;
504  n = ++na;
505  count = acount;
506  }
507  else
508  return false;
509  }
510  }
511  }
512 }
513 
514 
519 #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
520 
521 // Convert between wxDateTime and FILETIME which is a 64-bit value representing
522 // the number of 100-nanosecond intervals since January 1, 1601 UTC.
523 //
524 // This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
525 static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
526 
527 
528 static void ConvertFileTimeToWx( wxDateTime *dt, const FILETIME &ft )
529 {
530  wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
531  t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
532  t -= EPOCH_OFFSET_IN_MSEC;
533 
534  *dt = wxDateTime( t );
535 }
536 
537 #endif // wxUSE_DATETIME && __WIN32__
538 
539 
549 long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
550 {
551  long long timestamp = 0;
552 
553 #if defined( __WIN32__ )
554  // Win32 version.
555  // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
556  // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
557  // conversion by staying on the MSW side of things.
558  std::wstring filespec( aDirPath.t_str() );
559  filespec += '\\';
560  filespec += aFilespec.t_str();
561 
562  WIN32_FIND_DATA findData;
563  wxDateTime lastModDate;
564 
565  HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
566 
567  if( fileHandle != INVALID_HANDLE_VALUE )
568  {
569  do
570  {
571  ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
572  timestamp += lastModDate.GetValue().GetValue();
573  timestamp += findData.nFileSizeLow; // Get the file size (partial) as well to check for sneaky changes
574  }
575  while ( FindNextFile( fileHandle, &findData ) != 0 );
576  }
577 
578  FindClose( fileHandle );
579 #else
580  // POSIX version.
581  // Save time by not converting between encodings -- do everything on the file-system side.
582  std::string filespec( aFilespec.fn_str() );
583  std::string dir_path( aDirPath.fn_str() );
584 
585  DIR* dir = opendir( dir_path.c_str() );
586 
587  if( dir )
588  {
589  for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
590  {
591  if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
592  continue;
593 
594  std::string entry_path = dir_path + '/' + dir_entry->d_name;
595  struct stat entry_stat;
596 
597  if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
598  {
599  // Timestamp the source file, not the symlink
600  if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
601  {
602  char buffer[ PATH_MAX + 1 ];
603  ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
604 
605  if( pathLen > 0 )
606  {
607  struct stat linked_stat;
608  buffer[ pathLen ] = '\0';
609  entry_path = dir_path + buffer;
610 
611  if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
612  {
613  entry_stat = linked_stat;
614  }
615  else
616  {
617  // if we couldn't lstat the linked file we'll have to just use
618  // the symbolic link info
619  }
620  }
621  }
622 
623  if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
624  {
625  timestamp += entry_stat.st_mtime * 1000;
626  timestamp += entry_stat.st_size; // Get the file size as well to check for sneaky changes
627  }
628  }
629  else
630  {
631  // if we couldn't lstat the file itself all we can do is use the name
632  timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
633  }
634  }
635 
636  closedir( dir );
637  }
638 #endif
639 
640  return timestamp;
641 }
int ProcessExecute(const wxString &aCommandLine, int aFlags, wxProcess *callback)
Run a command in a child process.
Definition: common.cpp:40
Container for project specific data.
Definition: project.h:62
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
size_t operator()(const wxPoint &k) const
Definition: common.cpp:359
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:269
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:64
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
This file contains miscellaneous commonly used macros and functions.
#define NULL
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition: common.cpp:297
Bracket
Definition: common.cpp:46
bool operator()(const wxPoint &aA, const wxPoint &aB) const
Definition: common.cpp:369
const wxString ResolveUriByEnvVars(const wxString &aUri, PROJECT *aProject)
Replace any environment and/or text variables in file-path uris (leaving network-path URIs alone).
Definition: common.cpp:282
Base window classes and related definitions.
virtual bool TextVarResolver(wxString *aToken) const
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function private to src/common/...
Definition: common.cpp:549
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition: common.cpp:407
#define _(s)
Definition: 3d_actions.cpp:33
The common library.
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject)
Definition: common.cpp:122
std::ostream & operator<<(std::ostream &out, const wxSize &size)
Helper function to print the given wxSize to a stream.
Definition: common.cpp:379
size_t operator()(const wxString &s) const
Definition: common.cpp:352