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

Go to the source code of this file.

Enumerations

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

Functions

wxString ExpandTextVars (const wxString &aSource, const PROJECT *aProject)
 
wxString ExpandTextVars (const wxString &aSource, const std::function< bool(wxString *)> *aResolver)
 Expand '${var-name}' templates in text. More...
 
wxString KIwxExpandEnvVars (const wxString &str, const PROJECT *aProject, std::set< wxString > *aSet=nullptr)
 
const wxString ExpandEnvVarSubstitutions (const wxString &aString, const 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...
 
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...
 
bool WarnUserIfOperatingSystemUnsupported ()
 Checks if the operating system is explicitly unsupported and displays a disclaimer message box. 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};
@ Bracket_Max
Definition: common.cpp:54
@ Bracket_None
Definition: common.cpp:48
@ Bracket_Normal
Definition: common.cpp:49
@ Bracket_Curly
Definition: common.cpp:50

Function Documentation

◆ EnsureFileDirectoryExists()

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

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 327 of file common.cpp.

330{
331 wxString msg;
332 wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
333
334 // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
335 // already an absolute path) absolute:
336 if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
337 {
338 if( aReporter )
339 {
340 msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ),
341 aTargetFullFileName->GetPath(),
342 baseFilePath );
343 aReporter->Report( msg, RPT_SEVERITY_ERROR );
344 }
345
346 return false;
347 }
348
349 // Ensure the path of aTargetFullFileName exists, and create it if needed:
350 wxString outputPath( aTargetFullFileName->GetPath() );
351
352 if( !wxFileName::DirExists( outputPath ) )
353 {
354 // Make every directory provided when the provided path doesn't exist
355 if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
356 {
357 if( aReporter )
358 {
359 msg.Printf( _( "Output directory '%s' created." ), outputPath );
360 aReporter->Report( msg, RPT_SEVERITY_INFO );
361 return true;
362 }
363 }
364 else
365 {
366 if( aReporter )
367 {
368 msg.Printf( _( "Cannot create output directory '%s'." ), outputPath );
369 aReporter->Report( msg, RPT_SEVERITY_ERROR );
370 }
371
372 return false;
373 }
374 }
375
376 return true;
377}
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
#define _(s)
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO

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

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

◆ ExpandEnvVarSubstitutions()

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

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

Parameters
aStringa string containing (perhaps) references to env var
Returns
the expanded environment variable.

Definition at line 299 of file common.cpp.

300{
301 // wxGetenv( wchar_t* ) is not re-entrant on linux.
302 // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
303 static std::mutex getenv_mutex;
304
305 std::lock_guard<std::mutex> lock( getenv_mutex );
306
307 // We reserve the right to do this another way, by providing our own member function.
308 return KIwxExpandEnvVars( aString, aProject );
309}
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject, std::set< wxString > *aSet=nullptr)
Definition: common.cpp:118

References KIwxExpandEnvVars().

Referenced by FILENAME_RESOLVER::addPath(), FILENAME_RESOLVER::checkEnvVarPath(), S3D_PLUGIN_MANAGER::checkPluginName(), DIALOG_GEN_FOOTPRINT_POSITION::CreateAsciiFiles(), DIALOG_GEN_FOOTPRINT_POSITION::CreateGerberFiles(), FILENAME_RESOLVER::createPathList(), DIALOG_EXPORT_SVG::ExportSVGFile(), DIALOG_GENDRILL::GenDrillAndMapFiles(), LIB_TABLE_ROW::GetFullURI(), DIALOG_PLOT_SCHEMATIC::getOutputPath(), TEXT_BUTTON_FILE_BROWSER::OnButtonClick(), PANEL_SYM_LIB_TABLE::onConvertLegacyLibraries(), DIALOG_GENDRILL::OnGenReportFile(), DIALOG_PLOT_SCHEMATIC::OnOutputDirectoryBrowseClicked(), DIALOG_EXPORT_SVG::OnOutputDirectoryBrowseClicked(), DIALOG_GEN_FOOTPRINT_POSITION::OnOutputDirectoryBrowseClicked(), DIALOG_GENDRILL::OnOutputDirectoryBrowseClicked(), DIALOG_PLOT::OnOutputDirectoryBrowseClicked(), DIALOG_PAGES_SETTINGS::OnWksFileSelection(), PLOT_CONTROLLER::OpenPlotfile(), DIALOG_PLOT::Plot(), SIM_LIB_MGR::ResolveLibraryPath(), FILENAME_RESOLVER::ResolvePath(), DS_DATA_MODEL::ResolvePath(), ResolveUriByEnvVars(), S3D_CACHE::Set3DConfigDir(), FILENAME_RESOLVER::Set3DConfigDir(), FILENAME_RESOLVER::SetProject(), FILENAME_RESOLVER::ShortenPath(), ERC_TESTER::TestTextVars(), and NETLIST_EXPORTER_SPICE::writeInclude().

◆ ExpandTextVars() [1/2]

◆ ExpandTextVars() [2/2]

wxString ExpandTextVars ( const wxString &  aSource,
const std::function< bool(wxString *)> *  aResolver 
)

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

Definition at line 70 of file common.cpp.

72{
73 wxString newbuf;
74 size_t sourceLen = aSource.length();
75
76 newbuf.Alloc( sourceLen ); // best guess (improves performance)
77
78 for( size_t i = 0; i < sourceLen; ++i )
79 {
80 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
81 {
82 wxString token;
83
84 for( i = i + 2; i < sourceLen; ++i )
85 {
86 if( aSource[i] == '}' )
87 break;
88 else
89 token.append( aSource[i] );
90 }
91
92 if( token.IsEmpty() )
93 continue;
94
95 if( aResolver && (*aResolver)( &token ) )
96 {
97 newbuf.append( token );
98 }
99 else
100 {
101 // Token not resolved: leave the reference unchanged
102 newbuf.append( "${" + token + "}" );
103 }
104 }
105 else
106 {
107 newbuf.append( aSource[i] );
108 }
109 }
110
111 return newbuf;
112}

◆ KIwxExpandEnvVars()

wxString KIwxExpandEnvVars ( const wxString &  str,
const PROJECT aProject,
std::set< wxString > *  aSet = nullptr 
)

Definition at line 118 of file common.cpp.

119{
120 // If the same string is inserted twice, we have a loop
121 if( aSet )
122 {
123 if( auto [ _, result ] = aSet->insert( str ); !result )
124 return str;
125 }
126
127 size_t strlen = str.length();
128
129 wxString strResult;
130 strResult.Alloc( strlen ); // best guess (improves performance)
131
132 for( size_t n = 0; n < strlen; n++ )
133 {
134 wxUniChar str_n = str[n];
135
136 switch( str_n.GetValue() )
137 {
138#ifdef __WINDOWS__
139 case wxT( '%' ):
140#endif // __WINDOWS__
141 case wxT( '$' ):
142 {
143 Bracket bracket;
144#ifdef __WINDOWS__
145 if( str_n == wxT( '%' ) )
146 bracket = Bracket_Windows;
147 else
148#endif // __WINDOWS__
149 if( n == strlen - 1 )
150 {
151 bracket = Bracket_None;
152 }
153 else
154 {
155 switch( str[n + 1].GetValue() )
156 {
157 case wxT( '(' ):
158 bracket = Bracket_Normal;
159 str_n = str[++n]; // skip the bracket
160 break;
161
162 case wxT( '{' ):
163 bracket = Bracket_Curly;
164 str_n = str[++n]; // skip the bracket
165 break;
166
167 default:
168 bracket = Bracket_None;
169 }
170 }
171
172 size_t m = n + 1;
173
174 if( m >= strlen )
175 break;
176
177 wxUniChar str_m = str[m];
178
179 while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
180 {
181 if( ++m == strlen )
182 {
183 str_m = 0;
184 break;
185 }
186
187 str_m = str[m];
188 }
189
190 wxString strVarName( str.c_str() + n + 1, m - n - 1 );
191
192 // NB: use wxGetEnv instead of wxGetenv as otherwise variables
193 // set through wxSetEnv may not be read correctly!
194 bool expanded = false;
195 wxString tmp = strVarName;
196
197 if( aProject && aProject->TextVarResolver( &tmp ) )
198 {
199 strResult += tmp;
200 expanded = true;
201 }
202 else if( wxGetEnv( strVarName, &tmp ) )
203 {
204 strResult += tmp;
205 expanded = true;
206 }
207 // Replace unmatched older variables with current 3d model locations
208 // If the user has the older model location defined, that will be matched
209 // first above. But if they do not, this will ensure that their board still
210 // displays correctly
211 else if( strVarName.Contains( "KISYS3DMOD") || strVarName.Matches( "KICAD*_3DMODEL_DIR" ) )
212 {
213 for( auto& var : ENV_VAR::GetPredefinedEnvVars() )
214 {
215 if( var.Matches( "KICAD*_3DMODEL_DIR" ) )
216 {
217 const auto value = ENV_VAR::GetEnvVar<wxString>( var );
218
219 if( !value )
220 continue;
221
222 strResult += *value;
223 expanded = true;
224 break;
225 }
226 }
227 }
228 else
229 {
230 // variable doesn't exist => don't change anything
231#ifdef __WINDOWS__
232 if ( bracket != Bracket_Windows )
233#endif
234 if ( bracket != Bracket_None )
235 strResult << str[n - 1];
236
237 strResult << str_n << strVarName;
238 }
239
240 // check the closing bracket
241 if( bracket != Bracket_None )
242 {
243 if( m == strlen || str_m != (wxChar)bracket )
244 {
245 // under MSW it's common to have '%' characters in the registry
246 // and it's annoying to have warnings about them each time, so
247 // ignore them silently if they are not used for env vars
248 //
249 // under Unix, OTOH, this warning could be useful for the user to
250 // understand why isn't the variable expanded as intended
251#ifndef __WINDOWS__
252 wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
253 "at position %u in '%s'." ),
254 (char)bracket, (unsigned int) (m + 1), str.c_str() );
255#endif // __WINDOWS__
256 }
257 else
258 {
259 // skip closing bracket unless the variables wasn't expanded
260 if( !expanded )
261 strResult << (wxChar)bracket;
262
263 m++;
264 }
265 }
266
267 n = m - 1; // skip variable name
268 str_n = str[n];
269 }
270 break;
271
272 case wxT( '\\' ):
273 // backslash can be used to suppress special meaning of % and $
274 if( n < strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
275 {
276 str_n = str[++n];
277 strResult += str_n;
278
279 break;
280 }
282
283 default:
284 strResult += str_n;
285 }
286 }
287
288 std::set<wxString> loop_check;
289 auto first_pos = strResult.find_first_of( wxS( "{(%" ) );
290 auto last_pos = strResult.find_last_of( wxS( "})%" ) );
291
292 if( first_pos != strResult.npos && last_pos != strResult.npos && first_pos != last_pos )
293 strResult = KIwxExpandEnvVars( strResult, aProject, aSet ? aSet : &loop_check );
294
295 return strResult;
296}
Bracket
Definition: common.cpp:47
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
const ENV_VAR_LIST & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition: env_vars.cpp:60

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

Referenced by ExpandEnvVarSubstitutions(), and KIwxExpandEnvVars().

◆ 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[email protected][email protected][email protected]s.Buf[email protected]falo[email protected].EDU and ircII's reg.c.

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

Definition at line 394 of file common.cpp.

395{
396 if( !*text )
397 {
398 /* Match if both are empty. */
399 return !*pat;
400 }
401
402 const char *m = pat,
403 *n = text,
404 *ma = nullptr,
405 *na = nullptr;
406 int just = 0,
407 acount = 0,
408 count = 0;
409
410 if( dot_special && (*n == '.') )
411 {
412 /* Never match so that hidden Unix files
413 * are never found. */
414 return false;
415 }
416
417 for(;;)
418 {
419 if( *m == '*' )
420 {
421 ma = ++m;
422 na = n;
423 just = 1;
424 acount = count;
425 }
426 else if( *m == '?' )
427 {
428 m++;
429
430 if( !*n++ )
431 return false;
432 }
433 else
434 {
435 if( *m == '\\' )
436 {
437 m++;
438
439 /* Quoting "nothing" is a bad thing */
440 if( !*m )
441 return false;
442 }
443
444 if( !*m )
445 {
446 /*
447 * If we are out of both strings or we just
448 * saw a wildcard, then we can say we have a
449 * match
450 */
451 if( !*n )
452 return true;
453
454 if( just )
455 return true;
456
457 just = 0;
458 goto not_matched;
459 }
460
461 /*
462 * We could check for *n == NULL at this point, but
463 * since it's more common to have a character there,
464 * check to see if they match first (m and n) and
465 * then if they don't match, THEN we can check for
466 * the NULL of n
467 */
468 just = 0;
469
470 if( *m == *n )
471 {
472 m++;
473 count++;
474 n++;
475 }
476 else
477 {
478 not_matched:
479
480 /*
481 * If there are no more characters in the
482 * string, but we still need to find another
483 * character (*m != NULL), then it will be
484 * impossible to match it
485 */
486 if( !*n )
487 return false;
488
489 if( ma )
490 {
491 m = ma;
492 n = ++na;
493 count = acount;
494 }
495 else
496 return false;
497 }
498 }
499 }
500}

References text.

Referenced by TimestampDir().

◆ 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 312 of file common.cpp.

313{
314 wxString uri = ExpandTextVars( aUri, aProject );
315
316 // URL-like URI: return as is.
317 wxURL url( uri );
318
319 if( url.GetError() == wxURL_NOERR )
320 return uri;
321
322 // Otherwise, the path points to a local file. Resolve environment variables if any.
323 return ExpandEnvVarSubstitutions( aUri, aProject );
324}
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:299

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.

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

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

Definition at line 536 of file common.cpp.

537{
538 long long timestamp = 0;
539
540#if defined( __WIN32__ )
541 // Win32 version.
542 // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
543 // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
544 // conversion by staying on the MSW side of things.
545 std::wstring filespec( aDirPath.t_str() );
546 filespec += '\\';
547 filespec += aFilespec.t_str();
548
549 WIN32_FIND_DATA findData;
550 wxDateTime lastModDate;
551
552 HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
553
554 if( fileHandle != INVALID_HANDLE_VALUE )
555 {
556 do
557 {
558 ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
559 timestamp += lastModDate.GetValue().GetValue();
560
561 // Get the file size (partial) as well to check for sneaky changes.
562 timestamp += findData.nFileSizeLow;
563 }
564 while ( FindNextFile( fileHandle, &findData ) != 0 );
565 }
566
567 FindClose( fileHandle );
568#else
569 // POSIX version.
570 // Save time by not converting between encodings -- do everything on the file-system side.
571 std::string filespec( aFilespec.fn_str() );
572 std::string dir_path( aDirPath.fn_str() );
573
574 DIR* dir = opendir( dir_path.c_str() );
575
576 if( dir )
577 {
578 for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
579 {
580 if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
581 continue;
582
583 std::string entry_path = dir_path + '/' + dir_entry->d_name;
584 struct stat entry_stat;
585
586 if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
587 {
588 // Timestamp the source file, not the symlink
589 if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
590 {
591 char buffer[ PATH_MAX + 1 ];
592 ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
593
594 if( pathLen > 0 )
595 {
596 struct stat linked_stat;
597 buffer[ pathLen ] = '\0';
598 entry_path = dir_path + buffer;
599
600 if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
601 {
602 entry_stat = linked_stat;
603 }
604 else
605 {
606 // if we couldn't lstat the linked file we'll have to just use
607 // the symbolic link info
608 }
609 }
610 }
611
612 if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
613 {
614 timestamp += entry_stat.st_mtime * 1000;
615
616 // Get the file size as well to check for sneaky changes.
617 timestamp += entry_stat.st_size;
618 }
619 }
620 else
621 {
622 // if we couldn't lstat the file itself all we can do is use the name
623 timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
624 }
625 }
626
627 closedir( dir );
628 }
629#endif
630
631 return timestamp;
632}
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition: common.cpp:394

References matchWild().

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

◆ WarnUserIfOperatingSystemUnsupported()

bool WarnUserIfOperatingSystemUnsupported ( )

Checks if the operating system is explicitly unsupported and displays a disclaimer message box.

Returns
true if the operating system is unsupported

Definition at line 635 of file common.cpp.

636{
638 return false;
639
640 wxMessageDialog dialog( nullptr, _( "This operating system is not supported "
641 "by KiCad and its dependencies." ),
642 _( "Unsupported Operating System" ),
643 wxOK | wxICON_EXCLAMATION );
644
645 dialog.SetExtendedMessage( _( "Any issues with KiCad on this system cannot "
646 "be reported to the official bugtracker." ) );
647 dialog.ShowModal();
648
649 return true;
650}
bool IsOperatingSystemUnsupported()
Checks if the Operating System is explicitly unsupported and we want to prevent users from sending bu...
Definition: gtk/app.cpp:51

References _, and KIPLATFORM::APP::IsOperatingSystemUnsupported().

Referenced by PGM_BASE::InitPgm(), and COMMON_CONTROL::ReportBug().