KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
6 * Copyright The 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 <env_vars.h>
31#include <advanced_config.h>
32#include <reporter.h>
33#include <macros.h>
34#include <string_utils.h>
36#include <mutex>
37#include <wx/config.h>
38#include <wx/log.h>
39#include <wx/msgdlg.h>
40#include <wx/stdpaths.h>
41#include <wx/url.h>
42#include <wx/utils.h>
43#include <wx/regex.h>
44
45#ifdef _WIN32
46#include <windows.h>
47#endif
48
49
51{
55#ifdef __WINDOWS__
56 Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
57#endif
59};
60
61wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject, int aFlags )
62{
63 std::function<bool( wxString* )> projectResolver = [&]( wxString* token ) -> bool
64 {
65 return aProject->TextVarResolver( token );
66 };
67
68 return ExpandTextVars( aSource, &projectResolver, aFlags );
69}
70
71
72wxString ExpandTextVars( const wxString& aSource, const std::function<bool( wxString* )>* aResolver, int aFlags,
73 int aDepth )
74{
75 wxString newbuf;
76 size_t sourceLen = aSource.length();
77
78 newbuf.Alloc( sourceLen ); // best guess (improves performance)
79
80 // Get the maximum recursion depth from advanced config
82
83 for( size_t i = 0; i < sourceLen; ++i )
84 {
85 // Skip over existing escape markers without processing their contents
86 // This prevents expanding ${} or @{} that are inside escaped expressions
87 if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT( "<<<ESC_DOLLAR:" ) )
88 {
89 // Copy the entire escape marker including contents until matching closing }
90 newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
91 i += 14;
92
93 // Count braces to find the matching closing }
94 int braceCount = 1;
95 while( i < sourceLen && braceCount > 0 )
96 {
97 if( aSource[i] == '{' )
98 braceCount++;
99 else if( aSource[i] == '}' )
100 braceCount--;
101
102 newbuf.append( aSource[i] );
103 i++;
104 }
105 i--; // Back up one since the for loop will increment
106 continue;
107 }
108 else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT( "<<<ESC_AT:" ) )
109 {
110 // Copy the entire escape marker including contents until matching closing }
111 newbuf.append( wxT( "<<<ESC_AT:" ) );
112 i += 10;
113
114 // Count braces to find the matching closing }
115 int braceCount = 1;
116 while( i < sourceLen && braceCount > 0 )
117 {
118 if( aSource[i] == '{' )
119 braceCount++;
120 else if( aSource[i] == '}' )
121 braceCount--;
122
123 newbuf.append( aSource[i] );
124 i++;
125 }
126 i--; // Back up one since the for loop will increment
127 continue;
128 }
129
130 // Handle escaped variable references: \${...} or \@{...}
131 // Replace with escape markers that won't be expanded by multi-pass loops
132 // The markers will be converted back to ${...} or @{...} only at the final display stage
133 if( aSource[i] == '\\' && i + 1 < sourceLen )
134 {
135 if( ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) && i + 2 < sourceLen && aSource[i + 2] == '{' )
136 {
137 // Replace \${ with <<<ESC_DOLLAR: and \@{ with <<<ESC_AT:
138 // Using unique delimiters without braces to avoid confusing the expression evaluator
139 if( aSource[i + 1] == '$' )
140 newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
141 else
142 newbuf.append( wxT( "<<<ESC_AT:" ) );
143 i += 2;
144
145 // Copy everything until the matching closing brace, including the brace
146 int braceDepth = 1;
147 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
148 {
149 if( aSource[i] == '{' )
150 braceDepth++;
151 else if( aSource[i] == '}' )
152 braceDepth--;
153
154 newbuf.append( aSource[i] );
155 }
156 i--; // Adjust because loop will increment
157 continue;
158 }
159 }
160
161 if( ( aSource[i] == '$' || aSource[i] == '@' ) && i + 1 < sourceLen && aSource[i + 1] == '{' )
162 {
163 bool isMathExpr = ( aSource[i] == '@' );
164 wxString token;
165 int braceDepth = 1; // Track brace depth for nested expressions like @{${VAR}}
166
167 for( i = i + 2; i < sourceLen; ++i )
168 {
169 if( aSource[i] == '{' )
170 {
171 braceDepth++;
172 token.append( aSource[i] );
173 }
174 else if( aSource[i] == '}' )
175 {
176 braceDepth--;
177 if( braceDepth == 0 )
178 break; // Found the matching closing brace
179 else
180 token.append( aSource[i] );
181 }
182 else
183 {
184 token.append( aSource[i] );
185 }
186 }
187
188 if( token.IsEmpty() )
189 continue;
190
191 // For math expressions @{...}, recursively expand any nested ${...} variables
192 // but DON'T evaluate the math - leave that for EvaluateText() called by the user
193 if( isMathExpr )
194 {
195 if( ( token.Contains( wxT( "${" ) ) || token.Contains( wxT( "@{" ) ) ) && aDepth < maxDepth )
196 {
197 token = ExpandTextVars( token, aResolver, aFlags, aDepth + 1 );
198 }
199
200 // Return the expression with variables expanded but NOT evaluated
201 // The caller will use EvaluateText() to handle the math evaluation
202 newbuf.append( wxT( "@{" ) + token + wxT( "}" ) );
203 }
204 else // Variable reference ${...}
205 {
206 // Recursively expand nested variables BEFORE passing to resolver
207 // This ensures innermost variables are expanded first (standard evaluation order)
208 if( ( token.Contains( wxT( "${" ) ) || token.Contains( wxT( "@{" ) ) ) && aDepth < maxDepth )
209 {
210 token = ExpandTextVars( token, aResolver, aFlags, aDepth + 1 );
211
212 // Also evaluate math expressions after expanding variables
213 if( token.Contains( wxT( "@{" ) ) )
214 {
215 static EXPRESSION_EVALUATOR evaluator;
216 token = evaluator.Evaluate( token );
217 }
218 }
219
220 if( ( aFlags & FOR_ERC_DRC ) == 0
221 && ( token.StartsWith( wxS( "ERC_WARNING" ) ) || token.StartsWith( wxS( "ERC_ERROR" ) )
222 || token.StartsWith( wxS( "DRC_WARNING" ) ) || token.StartsWith( wxS( "DRC_ERROR" ) ) ) )
223 {
224 // Only show user-defined warnings/errors during ERC/DRC
225 }
226 else if( aResolver && ( *aResolver )( &token ) )
227 {
228 newbuf.append( token );
229 }
230 else
231 {
232 // Token not resolved: leave the reference unchanged
233 newbuf.append( "${" + token + "}" );
234 }
235 }
236 }
237 else
238 {
239 newbuf.append( aSource[i] );
240 }
241 }
242
243 return newbuf;
244}
245
246
247wxString ResolveTextVars( const wxString& aSource, const std::function<bool( wxString* )>* aResolver, int& aDepth )
248{
249 // Multi-pass resolution to handle nested variables like ${J601:UNIT(${ROW})}
250 // and math expressions like @{${ROW}-1}
251 wxString text = aSource;
253
254 static EXPRESSION_EVALUATOR evaluator;
255
256 while( ( text.Contains( wxT( "${" ) ) || text.Contains( wxT( "@{" ) ) ) && ++aDepth <= maxDepth )
257 {
258 // Always expand when ${} or @{} present to handle escape sequences (\${} and \@{})
259 // ExpandTextVars converts escapes to markers and expands ${} variables
260 // Don't expand if the only remaining $ or @ are in escape markers like <<<ESC_DOLLAR: or <<<ESC_AT:
261 if( text.Contains( wxT( "${" ) ) || text.Contains( wxT( "@{" ) ) )
262 text = ExpandTextVars( text, aResolver );
263
264 // Only evaluate if there are @{} expressions present (not escape markers)
265 // Don't evaluate if the only remaining @ are in escape markers like <<<ESC_AT:
266 if( text.Contains( wxT( "@{" ) ) )
267 text = evaluator.Evaluate( text ); // Evaluate math expressions
268 }
269
270 return text;
271}
272
273
274wxString GetGeneratedFieldDisplayName( const wxString& aSource )
275{
276 std::function<bool( wxString* )> tokenExtractor = [&]( wxString* token ) -> bool
277 {
278 *token = *token; // token value is the token name
279 return true;
280 };
281
282 return ExpandTextVars( aSource, &tokenExtractor );
283}
284
285
286bool IsGeneratedField( const wxString& aSource )
287{
288 static wxRegEx expr( wxS( "^\\$\\{\\w*\\}$" ) );
289 return expr.Matches( aSource );
290}
291
292
293wxString DescribeRef( const wxString& aRef )
294{
295 if( aRef.IsEmpty() )
296 return wxT( "<i>" ) + _( "unannotated footprint" ) + wxT( " </i>" );
297 else
298 return EscapeHTML( aRef );
299}
300
301
302//
303// Stolen from wxExpandEnvVars and then heavily optimized
304//
305wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject, std::set<wxString>* aSet = nullptr )
306{
307 // If the same string is inserted twice, we have a loop
308 if( aSet )
309 {
310 if( auto [_, result] = aSet->insert( str ); !result )
311 return str;
312 }
313
314 size_t strlen = str.length();
315
316 wxString strResult;
317 strResult.Alloc( strlen ); // best guess (improves performance)
318
319 auto getVersionedEnvVar = []( const wxString& aMatch, wxString& aResult ) -> bool
320 {
321 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
322 {
323 if( var.Matches( aMatch ) )
324 {
325 const auto value = ENV_VAR::GetEnvVar<wxString>( var );
326
327 if( !value )
328 continue;
329
330 aResult += *value;
331 return true;
332 }
333 }
334
335 return false;
336 };
337
338 for( size_t n = 0; n < strlen; n++ )
339 {
340 wxUniChar str_n = str[n];
341
342 switch( str_n.GetValue() )
343 {
344#ifdef __WINDOWS__
345 case wxT( '%' ):
346#endif // __WINDOWS__
347 case wxT( '$' ):
348 {
349 Bracket bracket;
350#ifdef __WINDOWS__
351 if( str_n == wxT( '%' ) )
352 {
353 bracket = Bracket_Windows;
354 }
355 else
356#endif // __WINDOWS__
357 if( n == strlen - 1 )
358 {
359 bracket = Bracket_None;
360 }
361 else
362 {
363 switch( str[n + 1].GetValue() )
364 {
365 case wxT( '(' ):
366 bracket = Bracket_Normal;
367 str_n = str[++n]; // skip the bracket
368 break;
369
370 case wxT( '{' ):
371 bracket = Bracket_Curly;
372 str_n = str[++n]; // skip the bracket
373 break;
374
375 default: bracket = Bracket_None;
376 }
377 }
378
379 size_t m = n + 1;
380
381 if( m >= strlen )
382 break;
383
384 wxUniChar str_m = str[m];
385
386 while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
387 {
388 if( ++m == strlen )
389 {
390 str_m = 0;
391 break;
392 }
393
394 str_m = str[m];
395 }
396
397 wxString strVarName( str.c_str() + n + 1, m - n - 1 );
398
399 // NB: use wxGetEnv instead of wxGetenv as otherwise variables
400 // set through wxSetEnv may not be read correctly!
401 bool expanded = false;
402 wxString tmp = strVarName;
403
404 if( aProject && aProject->TextVarResolver( &tmp ) )
405 {
406 strResult += tmp;
407 expanded = true;
408 }
409 else if( wxGetEnv( strVarName, &tmp ) )
410 {
411 strResult += tmp;
412 expanded = true;
413 }
414 // Replace unmatched older variables with current locations
415 // If the user has the older location defined, that will be matched
416 // first above. But if they do not, this will ensure that their board still
417 // displays correctly
418 else if( strVarName.Contains( "KISYS3DMOD" ) || strVarName.Matches( "KICAD*_3DMODEL_DIR" ) )
419 {
420 if( getVersionedEnvVar( "KICAD*_3DMODEL_DIR", strResult ) )
421 expanded = true;
422 }
423 else if( strVarName.Matches( "KICAD*_SYMBOL_DIR" ) )
424 {
425 if( getVersionedEnvVar( "KICAD*_SYMBOL_DIR", strResult ) )
426 expanded = true;
427 }
428 else if( strVarName.Matches( "KICAD*_FOOTPRINT_DIR" ) )
429 {
430 if( getVersionedEnvVar( "KICAD*_FOOTPRINT_DIR", strResult ) )
431 expanded = true;
432 }
433 else if( strVarName.Matches( "KICAD*_3RD_PARTY" ) )
434 {
435 if( getVersionedEnvVar( "KICAD*_3RD_PARTY", strResult ) )
436 expanded = true;
437 }
438 else
439 {
440 // variable doesn't exist => don't change anything
441#ifdef __WINDOWS__
442 if( bracket != Bracket_Windows )
443#endif
444 if( bracket != Bracket_None )
445 strResult << str[n - 1];
446
447 strResult << str_n << strVarName;
448 }
449
450 // check the closing bracket
451 if( bracket != Bracket_None )
452 {
453 if( m == strlen || str_m != (wxChar) bracket )
454 {
455 // under MSW it's common to have '%' characters in the registry
456 // and it's annoying to have warnings about them each time, so
457 // ignore them silently if they are not used for env vars
458 //
459 // under Unix, OTOH, this warning could be useful for the user to
460 // understand why isn't the variable expanded as intended
461#ifndef __WINDOWS__
462 wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
463 "at position %u in '%s'." ),
464 (char) bracket, (unsigned int) ( m + 1 ), str.c_str() );
465#endif // __WINDOWS__
466 }
467 else
468 {
469 // skip closing bracket unless the variables wasn't expanded
470 if( !expanded )
471 strResult << (wxChar) bracket;
472
473 m++;
474 }
475 }
476
477 n = m - 1; // skip variable name
478 str_n = str[n];
479 }
480 break;
481
482 case wxT( '\\' ):
483 // backslash can be used to suppress special meaning of % and $
484 if( n < strlen - 1 && ( str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' ) ) )
485 {
486 str_n = str[++n];
487 strResult += str_n;
488
489 break;
490 }
491
493
494 default: strResult += str_n;
495 }
496 }
497
498 std::set<wxString> loop_check;
499 auto first_pos = strResult.find_first_of( wxS( "{(%" ) );
500 auto last_pos = strResult.find_last_of( wxS( "})%" ) );
501
502 if( first_pos != strResult.npos && last_pos != strResult.npos && first_pos != last_pos )
503 strResult = KIwxExpandEnvVars( strResult, aProject, aSet ? aSet : &loop_check );
504
505 return strResult;
506}
507
508
509const wxString ExpandEnvVarSubstitutions( const wxString& aString, const PROJECT* aProject )
510{
511 // wxGetenv( wchar_t* ) is not re-entrant on linux.
512 // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
513 static std::mutex getenv_mutex;
514
515 std::lock_guard<std::mutex> lock( getenv_mutex );
516
517 // We reserve the right to do this another way, by providing our own member function.
518 return KIwxExpandEnvVars( aString, aProject );
519}
520
521
522const wxString ResolveUriByEnvVars( const wxString& aUri, const PROJECT* aProject )
523{
524 wxString uri = ExpandTextVars( aUri, aProject );
525
526 return ExpandEnvVarSubstitutions( uri, aProject );
527}
528
529
530bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName, const wxString& aBaseFilename, REPORTER* aReporter )
531{
532 wxString msg;
533 wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
534
535 // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
536 // already an absolute path) absolute:
537 if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
538 {
539 if( aReporter )
540 {
541 msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ), aTargetFullFileName->GetPath(),
542 baseFilePath );
543 aReporter->Report( msg, RPT_SEVERITY_ERROR );
544 }
545
546 return false;
547 }
548
549 // Ensure the path of aTargetFullFileName exists, and create it if needed:
550 wxString outputPath( aTargetFullFileName->GetPath() );
551
552 if( !wxFileName::DirExists( outputPath ) )
553 {
554 // Make every directory provided when the provided path doesn't exist
555 if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
556 {
557 if( aReporter )
558 {
559 msg.Printf( _( "Output directory '%s' created." ), outputPath );
560 aReporter->Report( msg, RPT_SEVERITY_INFO );
561 return true;
562 }
563 }
564 else
565 {
566 if( aReporter )
567 {
568 msg.Printf( _( "Cannot create output directory '%s'." ), outputPath );
569 aReporter->Report( msg, RPT_SEVERITY_ERROR );
570 }
571
572 return false;
573 }
574 }
575
576 return true;
577}
578
579
580wxString EnsureFileExtension( const wxString& aFilename, const wxString& aExtension )
581{
582 wxString newFilename( aFilename );
583
584 // It's annoying to throw up nag dialogs when the extension isn't right. Just fix it,
585 // but be careful not to destroy existing after-dot-text that isn't actually a bad
586 // extension, such as "Schematic_1.1".
587 if( newFilename.Lower().AfterLast( '.' ) != aExtension )
588 {
589 if( !newFilename.EndsWith( '.' ) )
590 newFilename.Append( '.' );
591
592 newFilename.Append( aExtension );
593 }
594
595 return newFilename;
596}
597
598
599wxString JoinExtensions( const std::vector<std::string>& aExts )
600{
601 wxString joined;
602
603 for( const std::string& ext : aExts )
604 {
605 if( !joined.empty() )
606 joined << wxS( ", " );
607
608 joined << wxS( "*." ) << ext;
609 }
610
611 return joined;
612}
613
614
622
629bool matchWild( const char* pat, const char* text, bool dot_special )
630{
631 if( !*text )
632 {
633 /* Match if both are empty. */
634 return !*pat;
635 }
636
637 const char *m = pat, *n = text, *ma = nullptr, *na = nullptr;
638 int just = 0, acount = 0, count = 0;
639
640 if( dot_special && ( *n == '.' ) )
641 {
642 /* Never match so that hidden Unix files
643 * are never found. */
644 return false;
645 }
646
647 for( ;; )
648 {
649 if( *m == '*' )
650 {
651 ma = ++m;
652 na = n;
653 just = 1;
654 acount = count;
655 }
656 else if( *m == '?' )
657 {
658 m++;
659
660 if( !*n++ )
661 return false;
662 }
663 else
664 {
665 if( *m == '\\' )
666 {
667 m++;
668
669 /* Quoting "nothing" is a bad thing */
670 if( !*m )
671 return false;
672 }
673
674 if( !*m )
675 {
676 /*
677 * If we are out of both strings or we just
678 * saw a wildcard, then we can say we have a
679 * match
680 */
681 if( !*n )
682 return true;
683
684 if( just )
685 return true;
686
687 just = 0;
688 goto not_matched;
689 }
690
691 /*
692 * We could check for *n == NULL at this point, but
693 * since it's more common to have a character there,
694 * check to see if they match first (m and n) and
695 * then if they don't match, THEN we can check for
696 * the NULL of n
697 */
698 just = 0;
699
700 if( *m == *n )
701 {
702 m++;
703 count++;
704 n++;
705 }
706 else
707 {
708 not_matched:
709
710 /*
711 * If there are no more characters in the
712 * string, but we still need to find another
713 * character (*m != NULL), then it will be
714 * impossible to match it
715 */
716 if( !*n )
717 return false;
718
719 if( ma )
720 {
721 m = ma;
722 n = ++na;
723 count = acount;
724 }
725 else
726 return false;
727 }
728 }
729 }
730}
731
732
737#if wxUSE_DATETIME && defined( __WIN32__ ) && !defined( __WXMICROWIN__ )
738
739// Convert between wxDateTime and FILETIME which is a 64-bit value representing
740// the number of 100-nanosecond intervals since January 1, 1601 UTC.
741//
742// This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
743static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL( 11644473600000 );
744
745
746static void ConvertFileTimeToWx( wxDateTime* dt, const FILETIME& ft )
747{
748 wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
749 t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
750 t -= EPOCH_OFFSET_IN_MSEC;
751
752 *dt = wxDateTime( t );
753}
754
755#endif // wxUSE_DATETIME && __WIN32__
756
757
766long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
767{
768 long long timestamp = 0;
769
770#if defined( __WIN32__ )
771 // Win32 version.
772 // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
773 // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
774 // conversion by staying on the MSW side of things.
775 std::wstring filespec( aDirPath.t_str() );
776 filespec += '\\';
777 filespec += aFilespec.t_str();
778
779 WIN32_FIND_DATA findData;
780 wxDateTime lastModDate;
781
782 HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
783
784 if( fileHandle != INVALID_HANDLE_VALUE )
785 {
786 do
787 {
788 ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
789 timestamp += lastModDate.GetValue().GetValue();
790
791 // Get the file size (partial) as well to check for sneaky changes.
792 timestamp += findData.nFileSizeLow;
793 } while( FindNextFile( fileHandle, &findData ) != 0 );
794 }
795
796 FindClose( fileHandle );
797#else
798 // POSIX version.
799 // Save time by not converting between encodings -- do everything on the file-system side.
800 std::string filespec( aFilespec.fn_str() );
801 std::string dir_path( aDirPath.fn_str() );
802
803 DIR* dir = opendir( dir_path.c_str() );
804
805 if( dir )
806 {
807 for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
808 {
809 if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
810 continue;
811
812 std::string entry_path = dir_path + '/' + dir_entry->d_name;
813 struct stat entry_stat;
814
815 if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
816 {
817 // Timestamp the source file, not the symlink
818 if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
819 {
820 char buffer[PATH_MAX + 1];
821 ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
822
823 if( pathLen > 0 )
824 {
825 struct stat linked_stat;
826 buffer[pathLen] = '\0';
827 entry_path = dir_path + buffer;
828
829 if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
830 {
831 entry_stat = linked_stat;
832 }
833 else
834 {
835 // if we couldn't lstat the linked file we'll have to just use
836 // the symbolic link info
837 }
838 }
839 }
840
841 if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
842 {
843 timestamp += entry_stat.st_mtime * 1000;
844
845 // Get the file size as well to check for sneaky changes.
846 timestamp += entry_stat.st_size;
847 }
848 }
849 else
850 {
851 // if we couldn't lstat the file itself all we can do is use the name
852 timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
853 }
854 }
855
856 closedir( dir );
857 }
858#endif
859
860 return timestamp;
861}
862
863
865{
867 return false;
868
869 wxMessageDialog dialog( nullptr,
870 _( "This operating system is not supported "
871 "by KiCad and its dependencies." ),
872 _( "Unsupported Operating System" ), wxOK | wxICON_EXCLAMATION );
873
874 dialog.SetExtendedMessage( _( "Any issues with KiCad on this system cannot "
875 "be reported to the official bugtracker." ) );
876 dialog.ShowModal();
877
878 return true;
879}
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
High-level wrapper for evaluating mathematical and string expressions in wxString format.
wxString Evaluate(const wxString &aInput)
Main evaluation function - processes input string and evaluates all} expressions.
Container for project specific data.
Definition project.h:65
virtual bool TextVarResolver(wxString *aToken) const
Definition project.cpp:84
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:509
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:599
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition common.cpp:274
const wxString ResolveUriByEnvVars(const wxString &aUri, const PROJECT *aProject)
Replace any environment and/or text variables in URIs.
Definition common.cpp:522
wxString EnsureFileExtension(const wxString &aFilename, const wxString &aExtension)
It's annoying to throw up nag dialogs when the extension isn't right.
Definition common.cpp:580
bool WarnUserIfOperatingSystemUnsupported()
Checks if the operating system is explicitly unsupported and displays a disclaimer message box.
Definition common.cpp:864
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:61
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition common.cpp:629
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:530
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject, std::set< wxString > *aSet=nullptr)
Definition common.cpp:305
Bracket
Definition common.cpp:51
@ Bracket_Max
Definition common.cpp:58
@ Bracket_None
Definition common.cpp:52
@ Bracket_Normal
Definition common.cpp:53
@ Bracket_Curly
Definition common.cpp:54
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:286
wxString DescribeRef(const wxString &aRef)
Returns a user-visible HTML string describing a footprint reference designator.
Definition common.cpp:293
wxString ResolveTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aResolver, int &aDepth)
Multi-pass text variable expansion and math expression evaluation.
Definition common.cpp:247
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:766
The common library.
#define FOR_ERC_DRC
Expand '${var-name}' templates in text.
Definition common.h:96
#define _(s)
Base window classes and related definitions.
Functions related to environment variables, including help functions.
int m_ResolveTextRecursionDepth
The number of recursions to resolve text variables.
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
KICOMMON_API std::optional< VAL_TYPE > GetEnvVar(const wxString &aEnvVarName)
Get an environment variable as a specific type, if set correctly.
KICOMMON_API const std::vector< wxString > & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
Definition env_vars.cpp:61
bool IsOperatingSystemUnsupported()
Checks if the Operating System is explicitly unsupported and we want to prevent users from sending bu...
Definition unix/app.cpp:58
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
wxString result
Test unit parsing edge cases and error handling.