KiCad PCB EDA Suite
common.cpp File Reference
#include <eda_base_frame.h>
#include <project.h>
#include <common.h>
#include <reporter.h>
#include <macros.h>
#include <mutex>
#include <wx/process.h>
#include <wx/config.h>
#include <wx/utils.h>
#include <wx/stdpaths.h>
#include <wx/url.h>
#include <wx/wx.h>

Go to the source code of this file.

Enumerations

enum  Bracket { Bracket_None, Bracket_Normal = ')', Bracket_Curly = '}', Bracket_Max }
 

Functions

int ProcessExecute (const wxString &aCommandLine, int aFlags, wxProcess *callback)
 Run a command in a child process. More...
 
wxString ExpandTextVars (const wxString &aSource, const std::function< bool(wxString *)> *aLocalResolver, const PROJECT *aProject, const std::function< bool(wxString *)> *aFallbackResolver)
 Expand '${var-name}' templates in text. More...
 
wxString KIwxExpandEnvVars (const wxString &str, const PROJECT *aProject)
 
const wxString ExpandEnvVarSubstitutions (const wxString &aString, PROJECT *aProject)
 Replace any environment variable & text variable references with their values. More...
 
const wxString ResolveUriByEnvVars (const wxString &aUri, PROJECT *aProject)
 Replace any environment and/or text variables in file-path uris (leaving network-path URIs alone). More...
 
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. More...
 
std::ostream & operator<< (std::ostream &out, const wxSize &size)
 Helper function to print the given wxSize to a stream. More...
 
std::ostream & operator<< (std::ostream &out, const wxPoint &pt)
 Helper function to print the given wxPoint to a stream. More...
 
bool matchWild (const char *pat, const char *text, bool dot_special)
 Performance enhancements to file and directory operations. More...
 
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/filename.cpp. More...
 

Enumeration Type Documentation

◆ Bracket

enum Bracket
Enumerator
Bracket_None 
Bracket_Normal 
Bracket_Curly 
Bracket_Max 

Definition at line 46 of file common.cpp.

47 {
49  Bracket_Normal = ')',
50  Bracket_Curly = '}',
51 #ifdef __WINDOWS__
52  Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
53 #endif
55 };

Function Documentation

◆ EnsureFileDirectoryExists()

bool EnsureFileDirectoryExists ( wxFileName *  aTargetFullFileName,
const wxString &  aBaseFilename,
REPORTER aReporter = NULL 
)

Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.

Parameters
aTargetFullFileNamethe wxFileName containing the full path and file name to modify. The path may be absolute or relative to aBaseFilename .
aBaseFilenamea full filename. Only its path is used to set the aTargetFullFileName path.
aReportera point to a REPORTER object use to show messages (can be NULL)
Returns
true if aOutputDir already exists or was successfully created.

Definition at line 283 of file common.cpp.

286 {
287  wxString msg;
288  wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
289 
290  // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
291  // already an absolute path) absolute:
292  if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
293  {
294  if( aReporter )
295  {
296  msg.Printf( _( "Cannot make path \"%s\" absolute with respect to \"%s\"." ),
297  aTargetFullFileName->GetPath(),
298  baseFilePath );
299  aReporter->Report( msg, RPT_SEVERITY_ERROR );
300  }
301 
302  return false;
303  }
304 
305  // Ensure the path of aTargetFullFileName exists, and create it if needed:
306  wxString outputPath( aTargetFullFileName->GetPath() );
307 
308  if( !wxFileName::DirExists( outputPath ) )
309  {
310  if( wxMkdir( outputPath ) )
311  {
312  if( aReporter )
313  {
314  msg.Printf( _( "Output directory \"%s\" created.\n" ), outputPath );
315  aReporter->Report( msg, RPT_SEVERITY_INFO );
316  return true;
317  }
318  }
319  else
320  {
321  if( aReporter )
322  {
323  msg.Printf( _( "Cannot create output directory \"%s\".\n" ), outputPath );
324  aReporter->Report( msg, RPT_SEVERITY_ERROR );
325  }
326 
327  return false;
328  }
329  }
330 
331  return true;
332 }
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
#define _(s)
Definition: 3d_actions.cpp:33

References _, REPORTER::Report(), RPT_SEVERITY_ERROR, and RPT_SEVERITY_INFO.

Referenced by DIALOG_GEN_FOOTPRINT_POSITION::CreateAsciiFiles(), DIALOG_GEN_FOOTPRINT_POSITION::CreateGerberFiles(), DIALOG_PLOT_SCHEMATIC::createPlotFileName(), DIALOG_EXPORT_SVG::ExportSVGFile(), DIALOG_GENDRILL::GenDrillAndMapFiles(), PLOT_CONTROLLER::OpenPlotfile(), and DIALOG_PLOT::Plot().

◆ ExpandEnvVarSubstitutions()

const wxString ExpandEnvVarSubstitutions ( const wxString &  aString,
PROJECT aProject 
)

Replace any environment variable & text variable references with their values.

Parameters
aString= a string containing (perhaps) references to env var
Returns
a string where env var are replaced by their value

Definition at line 255 of file common.cpp.

256 {
257  // wxGetenv( wchar_t* ) is not re-entrant on linux.
258  // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
259  static std::mutex getenv_mutex;
260 
261  std::lock_guard<std::mutex> lock( getenv_mutex );
262 
263  // We reserve the right to do this another way, by providing our own member function.
264  return KIwxExpandEnvVars( aString, aProject );
265 }
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject)
Definition: common.cpp:116

References KIwxExpandEnvVars().

Referenced by FILENAME_RESOLVER::addPath(), GITHUB_PLUGIN::cacheLib(), FILENAME_RESOLVER::checkEnvVarPath(), S3D_PLUGIN_MANAGER::checkPluginName(), S3D_PLUGIN_MANAGER::checkPluginPath(), DIALOG_GEN_FOOTPRINT_POSITION::CreateGerberFiles(), FILENAME_RESOLVER::createPathList(), DIALOG_PLOT_SCHEMATIC::createPlotFileName(), DIALOG_EXPORT_SVG::ExportSVGFile(), DIALOG_GENDRILL::GenDrillAndMapFiles(), LIB_TABLE_ROW::GetFullURI(), WS_DATA_MODEL::MakeFullFileName(), TEXT_BUTTON_FILE_BROWSER::OnButtonClick(), DIALOG_GENDRILL::OnGenReportFile(), DIALOG_EXPORT_SVG::OnOutputDirectoryBrowseClicked(), DIALOG_PLOT::OnOutputDirectoryBrowseClicked(), DIALOG_GEN_FOOTPRINT_POSITION::OnOutputDirectoryBrowseClicked(), DIALOG_PLOT_SCHEMATIC::OnOutputDirectoryBrowseClicked(), DIALOG_GENDRILL::OnOutputDirectoryBrowseClicked(), DIALOG_PLOT::Plot(), FILENAME_RESOLVER::ResolvePath(), ResolveUriByEnvVars(), FILENAME_RESOLVER::Set3DConfigDir(), S3D_CACHE::Set3DConfigDir(), FILENAME_RESOLVER::SetProject(), FILENAME_RESOLVER::ShortenPath(), and ERC_TESTER::TestTextVars().

◆ ExpandTextVars()

wxString ExpandTextVars ( const wxString &  aSource,
const std::function< bool(wxString *)> *  aLocalResolver,
const PROJECT aProject,
const std::function< bool(wxString *)> *  aFallbackResolver = nullptr 
)

Expand '${var-name}' templates in text.

The LocalResolver is given first crack at it, after which the PROJECT's resolver is called.

Definition at line 58 of file common.cpp.

62 {
63  wxString newbuf;
64  size_t sourceLen = aSource.length();
65 
66  newbuf.Alloc( sourceLen ); // best guess (improves performance)
67 
68  for( size_t i = 0; i < sourceLen; ++i )
69  {
70  if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
71  {
72  wxString token;
73 
74  for( i = i + 2; i < sourceLen; ++i )
75  {
76  if( aSource[i] == '}' )
77  break;
78  else
79  token.append( aSource[i] );
80  }
81 
82  if( token.IsEmpty() )
83  continue;
84 
85  if( aLocalResolver && (*aLocalResolver)( &token ) )
86  {
87  newbuf.append( token );
88  }
89  else if( aProject && aProject->TextVarResolver( &token ) )
90  {
91  newbuf.append( token );
92  }
93  else if( aFallbackResolver && (*aFallbackResolver)( &token ) )
94  {
95  newbuf.append( token );
96  }
97  else
98  {
99  // Token not resolved: leave the reference unchanged
100  newbuf.append( "${" + token + "}" );
101  }
102  }
103  else
104  {
105  newbuf.append( aSource[i] );
106  }
107  }
108 
109  return newbuf;
110 }
VTBL_ENTRY bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:65

References PROJECT::TextVarResolver().

Referenced by WS_DRAW_ITEM_LIST::BuildFullText(), PCB_TEXT::GetShownText(), SCH_FIELD::GetShownText(), SCH_TEXT::GetShownText(), FP_TEXT::GetShownText(), and ResolveUriByEnvVars().

◆ KIwxExpandEnvVars()

wxString KIwxExpandEnvVars ( const wxString &  str,
const PROJECT aProject 
)

Definition at line 116 of file common.cpp.

117 {
118  size_t strlen = str.length();
119 
120  wxString strResult;
121  strResult.Alloc( strlen ); // best guess (improves performance)
122 
123  for( size_t n = 0; n < strlen; n++ )
124  {
125  wxUniChar str_n = str[n];
126 
127  switch( str_n.GetValue() )
128  {
129 #ifdef __WINDOWS__
130  case wxT( '%' ):
131 #endif // __WINDOWS__
132  case wxT( '$' ):
133  {
134  Bracket bracket;
135 #ifdef __WINDOWS__
136  if( str_n == wxT( '%' ) )
137  bracket = Bracket_Windows;
138  else
139 #endif // __WINDOWS__
140  if( n == strlen - 1 )
141  {
142  bracket = Bracket_None;
143  }
144  else
145  {
146  switch( str[n + 1].GetValue() )
147  {
148  case wxT( '(' ):
149  bracket = Bracket_Normal;
150  str_n = str[++n]; // skip the bracket
151  break;
152 
153  case wxT( '{' ):
154  bracket = Bracket_Curly;
155  str_n = str[++n]; // skip the bracket
156  break;
157 
158  default:
159  bracket = Bracket_None;
160  }
161  }
162 
163  size_t m = n + 1;
164  wxUniChar str_m = str[m];
165 
166  while( m < strlen && ( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) ) )
167  str_m = str[++m];
168 
169  wxString strVarName( str.c_str() + n + 1, m - n - 1 );
170 
171  // NB: use wxGetEnv instead of wxGetenv as otherwise variables
172  // set through wxSetEnv may not be read correctly!
173  bool expanded = false;
174  wxString tmp = strVarName;
175 
176  if( aProject && aProject->TextVarResolver( &tmp ) )
177  {
178  strResult += tmp;
179  expanded = true;
180  }
181  else if( wxGetEnv( strVarName, &tmp ) )
182  {
183  strResult += tmp;
184  expanded = true;
185  }
186  else
187  {
188  // variable doesn't exist => don't change anything
189 #ifdef __WINDOWS__
190  if ( bracket != Bracket_Windows )
191 #endif
192  if ( bracket != Bracket_None )
193  strResult << str[n - 1];
194 
195  strResult << str_n << strVarName;
196  }
197 
198  // check the closing bracket
199  if( bracket != Bracket_None )
200  {
201  if( m == strlen || str_m != (wxChar)bracket )
202  {
203  // under MSW it's common to have '%' characters in the registry
204  // and it's annoying to have warnings about them each time, so
205  // ignore them silently if they are not used for env vars
206  //
207  // under Unix, OTOH, this warning could be useful for the user to
208  // understand why isn't the variable expanded as intended
209 #ifndef __WINDOWS__
210  wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
211  "at position %u in '%s'." ),
212  (char)bracket, (unsigned int) (m + 1), str.c_str() );
213 #endif // __WINDOWS__
214  }
215  else
216  {
217  // skip closing bracket unless the variables wasn't expanded
218  if( !expanded )
219  strResult << (wxChar)bracket;
220 
221  m++;
222  }
223  }
224 
225  n = m - 1; // skip variable name
226  str_n = str[n];
227  }
228  break;
229 
230  case wxT( '\\' ):
231  // backslash can be used to suppress special meaning of % and $
232  if( n != strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
233  {
234  str_n = str[++n];
235  strResult += str_n;
236 
237  break;
238  }
240 
241  default:
242  strResult += str_n;
243  }
244  }
245 
246 #ifndef __WINDOWS__
247  if( strResult.StartsWith( "~" ) )
248  strResult.Replace( "~", wxGetHomeDir(), false );
249 #endif // __WINDOWS__
250 
251  return strResult;
252 }
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
Bracket
Definition: common.cpp:46
VTBL_ENTRY bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:65
#define _(s)
Definition: 3d_actions.cpp:33

References _, Bracket_Curly, Bracket_None, Bracket_Normal, KI_FALLTHROUGH, and PROJECT::TextVarResolver().

Referenced by ExpandEnvVarSubstitutions().

◆ matchWild()

bool matchWild ( const char *  pat,
const char *  text,
bool  dot_special 
)

Performance enhancements to file and directory operations.

Note: while it's annoying to have to make copies of wxWidgets stuff and then add platform-specific performance optimizations, the following routines offer SIGNIFICANT performance benefits.A copy of wxMatchWild(), which wxWidgets attributes to Douglas A. Lewis dalew.nosp@m.is@c.nosp@m.s.Buf.nosp@m.falo.nosp@m..EDU and ircII's reg.c.

This version is modified to skip any encoding conversions (for performance).

Definition at line 440 of file common.cpp.

441 {
442  if( !*text )
443  {
444  /* Match if both are empty. */
445  return !*pat;
446  }
447 
448  const char *m = pat,
449  *n = text,
450  *ma = NULL,
451  *na = NULL;
452  int just = 0,
453  acount = 0,
454  count = 0;
455 
456  if( dot_special && (*n == '.') )
457  {
458  /* Never match so that hidden Unix files
459  * are never found. */
460  return false;
461  }
462 
463  for(;;)
464  {
465  if( *m == '*' )
466  {
467  ma = ++m;
468  na = n;
469  just = 1;
470  acount = count;
471  }
472  else if( *m == '?' )
473  {
474  m++;
475 
476  if( !*n++ )
477  return false;
478  }
479  else
480  {
481  if( *m == '\\' )
482  {
483  m++;
484 
485  /* Quoting "nothing" is a bad thing */
486  if( !*m )
487  return false;
488  }
489  if( !*m )
490  {
491  /*
492  * If we are out of both strings or we just
493  * saw a wildcard, then we can say we have a
494  * match
495  */
496  if( !*n )
497  return true;
498 
499  if( just )
500  return true;
501 
502  just = 0;
503  goto not_matched;
504  }
505 
506  /*
507  * We could check for *n == NULL at this point, but
508  * since it's more common to have a character there,
509  * check to see if they match first (m and n) and
510  * then if they don't match, THEN we can check for
511  * the NULL of n
512  */
513  just = 0;
514 
515  if( *m == *n )
516  {
517  m++;
518  count++;
519  n++;
520  }
521  else
522  {
523  not_matched:
524 
525  /*
526  * If there are no more characters in the
527  * string, but we still need to find another
528  * character (*m != NULL), then it will be
529  * impossible to match it
530  */
531  if( !*n )
532  return false;
533 
534  if( ma )
535  {
536  m = ma;
537  n = ++na;
538  count = acount;
539  }
540  else
541  return false;
542  }
543  }
544  }
545 }
#define NULL

References NULL.

Referenced by TimestampDir().

◆ operator<<() [1/2]

std::ostream& operator<< ( std::ostream &  out,
const wxSize &  size 
)

Helper function to print the given wxSize to a stream.

Used for debugging functions like EDA_ITEM::Show and also in unit testing fixtures.

Definition at line 412 of file common.cpp.

413 {
414  out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\"";
415  return out;
416 }

◆ operator<<() [2/2]

std::ostream& operator<< ( std::ostream &  out,
const wxPoint &  pt 
)

Helper function to print the given wxPoint to a stream.

Used for debugging functions like EDA_ITEM::Show and also in unit testing fixtures.

Definition at line 419 of file common.cpp.

420 {
421  out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\"";
422  return out;
423 }

◆ ProcessExecute()

int ProcessExecute ( const wxString &  aCommandLine,
int  aFlags = wxEXEC_ASYNC,
wxProcess *  callback = NULL 
)

Run a command in a child process.

Parameters
aCommandLineThe process and any arguments to it all in a single string.
aFlagsThe same args as allowed for wxExecute()
callbackwxProcess implementing OnTerminate to be run when the child process finishes
Returns
int - pid of process, 0 in case of error (like return values of wxExecute())

Definition at line 40 of file common.cpp.

41 {
42  return (int) wxExecute( aCommandLine, aFlags, callback );
43 }

Referenced by doPrintFile(), ExecuteFile(), GetAssociatedDocument(), OpenFile(), OpenPDF(), and SCH_EDIT_FRAME::WriteNetListFile().

◆ ResolveUriByEnvVars()

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 at line 268 of file common.cpp.

269 {
270  wxString uri = ExpandTextVars( aUri, nullptr, aProject );
271 
272  // URL-like URI: return as is.
273  wxURL url( uri );
274 
275  if( url.GetError() == wxURL_NOERR )
276  return uri;
277 
278  // Otherwise, the path points to a local file. Resolve environment variables if any.
279  return ExpandEnvVarSubstitutions( aUri, aProject );
280 }
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:255
wxString ExpandTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aLocalResolver, const PROJECT *aProject, const std::function< bool(wxString *)> *aFallbackResolver)
Expand '${var-name}' templates in text.
Definition: common.cpp:58

References ExpandEnvVarSubstitutions(), and ExpandTextVars().

Referenced by GetAssociatedDocument().

◆ TimestampDir()

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/filename.cpp.

TimestampDir

This routine offers SIGNIFICANT performance benefits over using wxWidgets to gather timestamps from matching files in a directory.

Parameters
aDirPaththe directory to search
aFilespeca (wildcarded) file spec to match against
Returns
a hash of the last-mod-dates of all matching files in the directory

Definition at line 582 of file common.cpp.

583 {
584  long long timestamp = 0;
585 
586 #if defined( __WIN32__ )
587  // Win32 version.
588  // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
589  // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
590  // conversion by staying on the MSW side of things.
591  std::wstring filespec( aDirPath.t_str() );
592  filespec += '\\';
593  filespec += aFilespec.t_str();
594 
595  WIN32_FIND_DATA findData;
596  wxDateTime lastModDate;
597 
598  HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
599 
600  if( fileHandle != INVALID_HANDLE_VALUE )
601  {
602  do
603  {
604  ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
605  timestamp += lastModDate.GetValue().GetValue();
606  }
607  while ( FindNextFile( fileHandle, &findData ) != 0 );
608  }
609 
610  FindClose( fileHandle );
611 #else
612  // POSIX version.
613  // Save time by not converting between encodings -- do everything on the file-system side.
614  std::string filespec( aFilespec.fn_str() );
615  std::string dir_path( aDirPath.fn_str() );
616 
617  DIR* dir = opendir( dir_path.c_str() );
618 
619  if( dir )
620  {
621  for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
622  {
623  if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
624  continue;
625 
626  std::string entry_path = dir_path + '/' + dir_entry->d_name;
627  struct stat entry_stat;
628 
629  if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
630  {
631  // Timestamp the source file, not the symlink
632  if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
633  {
634  char buffer[ PATH_MAX + 1 ];
635  ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
636 
637  if( pathLen > 0 )
638  {
639  struct stat linked_stat;
640  buffer[ pathLen ] = '\0';
641  entry_path = dir_path + buffer;
642 
643  if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
644  {
645  entry_stat = linked_stat;
646  }
647  else
648  {
649  // if we couldn't lstat the linked file we'll have to just use
650  // the symbolic link info
651  }
652  }
653  }
654 
655  if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
656  timestamp += entry_stat.st_mtime * 1000;
657  }
658  else
659  {
660  // if we couldn't lstat the file itself all we can do is use the name
661  timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
662  }
663  }
664 
665  closedir( dir );
666  }
667 #endif
668 
669  return timestamp;
670 }
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition: common.cpp:440

References matchWild().

Referenced by FP_CACHE::GetTimestamp(), and GPCB_FPL_CACHE::GetTimestamp().