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 <kiplatform/app.h>
28 #include <project.h>
29 #include <common.h>
30 #include <reporter.h>
31 #include <macros.h>
32 #include <mutex>
33 #include <wx/config.h>
34 #include <wx/log.h>
35 #include <wx/msgdlg.h>
36 #include <wx/process.h>
37 #include <wx/stdpaths.h>
38 #include <wx/url.h>
39 #include <wx/utils.h>
40 
41 #ifdef _WIN32
42 #include <Windows.h>
43 #endif
44 
45 
46 int ProcessExecute( const wxString& aCommandLine, int aFlags, wxProcess *callback )
47 {
48  return (int) wxExecute( aCommandLine, aFlags, callback );
49 }
50 
51 
52 enum Bracket
53 {
57 #ifdef __WINDOWS__
58  Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
59 #endif
61 };
62 
63 
64 wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject )
65 {
66  return ExpandTextVars( aSource, nullptr, nullptr, aProject );
67 }
68 
69 
70 wxString ExpandTextVars( const wxString& aSource,
71  const std::function<bool( wxString* )>* aLocalResolver,
72  const std::function<bool( wxString* )>* aFallbackResolver,
73  const PROJECT* aProject )
74 {
75  wxString newbuf;
76  size_t sourceLen = aSource.length();
77 
78  newbuf.Alloc( sourceLen ); // best guess (improves performance)
79 
80  for( size_t i = 0; i < sourceLen; ++i )
81  {
82  if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
83  {
84  wxString token;
85 
86  for( i = i + 2; i < sourceLen; ++i )
87  {
88  if( aSource[i] == '}' )
89  break;
90  else
91  token.append( aSource[i] );
92  }
93 
94  if( token.IsEmpty() )
95  continue;
96 
97  if( aLocalResolver && (*aLocalResolver)( &token ) )
98  {
99  newbuf.append( token );
100  }
101  else if( aProject && aProject->TextVarResolver( &token ) )
102  {
103  newbuf.append( token );
104  }
105  else if( aFallbackResolver && (*aFallbackResolver)( &token ) )
106  {
107  newbuf.append( token );
108  }
109  else
110  {
111  // Token not resolved: leave the reference unchanged
112  newbuf.append( "${" + token + "}" );
113  }
114  }
115  else
116  {
117  newbuf.append( aSource[i] );
118  }
119  }
120 
121  return newbuf;
122 }
123 
124 
125 //
126 // Stolen from wxExpandEnvVars and then heavily optimized
127 //
128 wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject )
129 {
130  size_t strlen = str.length();
131 
132  wxString strResult;
133  strResult.Alloc( strlen ); // best guess (improves performance)
134 
135  for( size_t n = 0; n < strlen; n++ )
136  {
137  wxUniChar str_n = str[n];
138 
139  switch( str_n.GetValue() )
140  {
141 #ifdef __WINDOWS__
142  case wxT( '%' ):
143 #endif // __WINDOWS__
144  case wxT( '$' ):
145  {
146  Bracket bracket;
147 #ifdef __WINDOWS__
148  if( str_n == wxT( '%' ) )
149  bracket = Bracket_Windows;
150  else
151 #endif // __WINDOWS__
152  if( n == strlen - 1 )
153  {
154  bracket = Bracket_None;
155  }
156  else
157  {
158  switch( str[n + 1].GetValue() )
159  {
160  case wxT( '(' ):
161  bracket = Bracket_Normal;
162  str_n = str[++n]; // skip the bracket
163  break;
164 
165  case wxT( '{' ):
166  bracket = Bracket_Curly;
167  str_n = str[++n]; // skip the bracket
168  break;
169 
170  default:
171  bracket = Bracket_None;
172  }
173  }
174 
175  size_t m = n + 1;
176 
177  if( m >= strlen )
178  break;
179 
180  wxUniChar str_m = str[m];
181 
182  while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
183  {
184  if( ++m == strlen )
185  {
186  str_m = 0;
187  break;
188  }
189 
190  str_m = str[m];
191  }
192 
193  wxString strVarName( str.c_str() + n + 1, m - n - 1 );
194 
195  // NB: use wxGetEnv instead of wxGetenv as otherwise variables
196  // set through wxSetEnv may not be read correctly!
197  bool expanded = false;
198  wxString tmp = strVarName;
199 
200  if( aProject && aProject->TextVarResolver( &tmp ) )
201  {
202  strResult += tmp;
203  expanded = true;
204  }
205  else if( wxGetEnv( strVarName, &tmp ) )
206  {
207  strResult += tmp;
208  expanded = true;
209  }
210  else
211  {
212  // variable doesn't exist => don't change anything
213 #ifdef __WINDOWS__
214  if ( bracket != Bracket_Windows )
215 #endif
216  if ( bracket != Bracket_None )
217  strResult << str[n - 1];
218 
219  strResult << str_n << strVarName;
220  }
221 
222  // check the closing bracket
223  if( bracket != Bracket_None )
224  {
225  if( m == strlen || str_m != (wxChar)bracket )
226  {
227  // under MSW it's common to have '%' characters in the registry
228  // and it's annoying to have warnings about them each time, so
229  // ignore them silently if they are not used for env vars
230  //
231  // under Unix, OTOH, this warning could be useful for the user to
232  // understand why isn't the variable expanded as intended
233 #ifndef __WINDOWS__
234  wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
235  "at position %u in '%s'." ),
236  (char)bracket, (unsigned int) (m + 1), str.c_str() );
237 #endif // __WINDOWS__
238  }
239  else
240  {
241  // skip closing bracket unless the variables wasn't expanded
242  if( !expanded )
243  strResult << (wxChar)bracket;
244 
245  m++;
246  }
247  }
248 
249  n = m - 1; // skip variable name
250  str_n = str[n];
251  }
252  break;
253 
254  case wxT( '\\' ):
255  // backslash can be used to suppress special meaning of % and $
256  if( n < strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
257  {
258  str_n = str[++n];
259  strResult += str_n;
260 
261  break;
262  }
264 
265  default:
266  strResult += str_n;
267  }
268  }
269 
270 #ifndef __WINDOWS__
271  if( strResult.StartsWith( "~" ) )
272  strResult.Replace( "~", wxGetHomeDir(), false );
273 #endif // __WINDOWS__
274 
275  return strResult;
276 }
277 
278 
279 const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aProject )
280 {
281  // wxGetenv( wchar_t* ) is not re-entrant on linux.
282  // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
283  static std::mutex getenv_mutex;
284 
285  std::lock_guard<std::mutex> lock( getenv_mutex );
286 
287  // We reserve the right to do this another way, by providing our own member function.
288  return KIwxExpandEnvVars( aString, aProject );
289 }
290 
291 
292 const wxString ResolveUriByEnvVars( const wxString& aUri, PROJECT* aProject )
293 {
294  wxString uri = ExpandTextVars( aUri, aProject );
295 
296  // URL-like URI: return as is.
297  wxURL url( uri );
298 
299  if( url.GetError() == wxURL_NOERR )
300  return uri;
301 
302  // Otherwise, the path points to a local file. Resolve environment variables if any.
303  return ExpandEnvVarSubstitutions( aUri, aProject );
304 }
305 
306 
307 bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName,
308  const wxString& aBaseFilename,
309  REPORTER* aReporter )
310 {
311  wxString msg;
312  wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
313 
314  // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
315  // already an absolute path) absolute:
316  if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
317  {
318  if( aReporter )
319  {
320  msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ),
321  aTargetFullFileName->GetPath(),
322  baseFilePath );
323  aReporter->Report( msg, RPT_SEVERITY_ERROR );
324  }
325 
326  return false;
327  }
328 
329  // Ensure the path of aTargetFullFileName exists, and create it if needed:
330  wxString outputPath( aTargetFullFileName->GetPath() );
331 
332  if( !wxFileName::DirExists( outputPath ) )
333  {
334  // Make every directory provided when the provided path doesn't exist
335  if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
336  {
337  if( aReporter )
338  {
339  msg.Printf( _( "Output directory '%s' created.\n" ), outputPath );
340  aReporter->Report( msg, RPT_SEVERITY_INFO );
341  return true;
342  }
343  }
344  else
345  {
346  if( aReporter )
347  {
348  msg.Printf( _( "Cannot create output directory '%s'.\n" ), outputPath );
349  aReporter->Report( msg, RPT_SEVERITY_ERROR );
350  }
351 
352  return false;
353  }
354  }
355 
356  return true;
357 }
358 
359 
374 bool matchWild( const char* pat, const char* text, bool dot_special )
375 {
376  if( !*text )
377  {
378  /* Match if both are empty. */
379  return !*pat;
380  }
381 
382  const char *m = pat,
383  *n = text,
384  *ma = NULL,
385  *na = NULL;
386  int just = 0,
387  acount = 0,
388  count = 0;
389 
390  if( dot_special && (*n == '.') )
391  {
392  /* Never match so that hidden Unix files
393  * are never found. */
394  return false;
395  }
396 
397  for(;;)
398  {
399  if( *m == '*' )
400  {
401  ma = ++m;
402  na = n;
403  just = 1;
404  acount = count;
405  }
406  else if( *m == '?' )
407  {
408  m++;
409 
410  if( !*n++ )
411  return false;
412  }
413  else
414  {
415  if( *m == '\\' )
416  {
417  m++;
418 
419  /* Quoting "nothing" is a bad thing */
420  if( !*m )
421  return false;
422  }
423  if( !*m )
424  {
425  /*
426  * If we are out of both strings or we just
427  * saw a wildcard, then we can say we have a
428  * match
429  */
430  if( !*n )
431  return true;
432 
433  if( just )
434  return true;
435 
436  just = 0;
437  goto not_matched;
438  }
439 
440  /*
441  * We could check for *n == NULL at this point, but
442  * since it's more common to have a character there,
443  * check to see if they match first (m and n) and
444  * then if they don't match, THEN we can check for
445  * the NULL of n
446  */
447  just = 0;
448 
449  if( *m == *n )
450  {
451  m++;
452  count++;
453  n++;
454  }
455  else
456  {
457  not_matched:
458 
459  /*
460  * If there are no more characters in the
461  * string, but we still need to find another
462  * character (*m != NULL), then it will be
463  * impossible to match it
464  */
465  if( !*n )
466  return false;
467 
468  if( ma )
469  {
470  m = ma;
471  n = ++na;
472  count = acount;
473  }
474  else
475  return false;
476  }
477  }
478  }
479 }
480 
481 
486 #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
487 
488 // Convert between wxDateTime and FILETIME which is a 64-bit value representing
489 // the number of 100-nanosecond intervals since January 1, 1601 UTC.
490 //
491 // This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
492 static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
493 
494 
495 static void ConvertFileTimeToWx( wxDateTime *dt, const FILETIME &ft )
496 {
497  wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
498  t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
499  t -= EPOCH_OFFSET_IN_MSEC;
500 
501  *dt = wxDateTime( t );
502 }
503 
504 #endif // wxUSE_DATETIME && __WIN32__
505 
506 
516 long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
517 {
518  long long timestamp = 0;
519 
520 #if defined( __WIN32__ )
521  // Win32 version.
522  // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
523  // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
524  // conversion by staying on the MSW side of things.
525  std::wstring filespec( aDirPath.t_str() );
526  filespec += '\\';
527  filespec += aFilespec.t_str();
528 
529  WIN32_FIND_DATA findData;
530  wxDateTime lastModDate;
531 
532  HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
533 
534  if( fileHandle != INVALID_HANDLE_VALUE )
535  {
536  do
537  {
538  ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
539  timestamp += lastModDate.GetValue().GetValue();
540  timestamp += findData.nFileSizeLow; // Get the file size (partial) as well to check for sneaky changes
541  }
542  while ( FindNextFile( fileHandle, &findData ) != 0 );
543  }
544 
545  FindClose( fileHandle );
546 #else
547  // POSIX version.
548  // Save time by not converting between encodings -- do everything on the file-system side.
549  std::string filespec( aFilespec.fn_str() );
550  std::string dir_path( aDirPath.fn_str() );
551 
552  DIR* dir = opendir( dir_path.c_str() );
553 
554  if( dir )
555  {
556  for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
557  {
558  if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
559  continue;
560 
561  std::string entry_path = dir_path + '/' + dir_entry->d_name;
562  struct stat entry_stat;
563 
564  if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
565  {
566  // Timestamp the source file, not the symlink
567  if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
568  {
569  char buffer[ PATH_MAX + 1 ];
570  ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
571 
572  if( pathLen > 0 )
573  {
574  struct stat linked_stat;
575  buffer[ pathLen ] = '\0';
576  entry_path = dir_path + buffer;
577 
578  if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
579  {
580  entry_stat = linked_stat;
581  }
582  else
583  {
584  // if we couldn't lstat the linked file we'll have to just use
585  // the symbolic link info
586  }
587  }
588  }
589 
590  if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
591  {
592  timestamp += entry_stat.st_mtime * 1000;
593  timestamp += entry_stat.st_size; // Get the file size as well to check for sneaky changes
594  }
595  }
596  else
597  {
598  // if we couldn't lstat the file itself all we can do is use the name
599  timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
600  }
601  }
602 
603  closedir( dir );
604  }
605 #endif
606 
607  return timestamp;
608 }
609 
611 {
613  return false;
614 
615  wxMessageDialog dialog( NULL, _( "This operating system is not supported "
616  "by KiCad and its dependencies." ),
617  _( "Unsupported Operating System" ),
618  wxOK | wxICON_EXCLAMATION );
619 
620  dialog.SetExtendedMessage( _( "Any issues with KiCad on this system cannot "
621  "be reported to the official bugtracker." ) );
622  dialog.ShowModal();
623 
624  return true;
625 }
bool IsOperatingSystemUnsupported()
Checks if the Operating System is explicitly unsupported and we want to prevent users from sending bu...
Definition: gtk/app.cpp:51
int ProcessExecute(const wxString &aCommandLine, int aFlags, wxProcess *callback)
Run a command in a child process.
Definition: common.cpp:46
Container for project specific data.
Definition: project.h:62
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:64
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:279
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:307
Bracket
Definition: common.cpp:52
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:292
Base window classes and related definitions.
virtual bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:67
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:516
#define _(s)
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition: common.cpp:374
bool WarnUserIfOperatingSystemUnsupported()
Checks if the operating system is explicitly unsupported and displays a disclaimer message box.
Definition: common.cpp:610
The common library.
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject)
Definition: common.cpp:128