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 // Skip over escape markers - don't count their braces
170 // This prevents <<<ESC_DOLLAR:X} from interfering with outer brace counting
171 if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT( "<<<ESC_DOLLAR:" ) )
172 {
173 token.append( wxT( "<<<ESC_DOLLAR:" ) );
174 i += 14;
175
176 // Copy contents until matching closing brace (tracking nested braces)
177 int markerBraceCount = 1;
178
179 while( i < sourceLen && markerBraceCount > 0 )
180 {
181 if( aSource[i] == '{' )
182 markerBraceCount++;
183 else if( aSource[i] == '}' )
184 markerBraceCount--;
185
186 token.append( aSource[i] );
187 i++;
188 }
189
190 i--; // Adjust for outer loop increment
191 continue;
192 }
193 else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT( "<<<ESC_AT:" ) )
194 {
195 token.append( wxT( "<<<ESC_AT:" ) );
196 i += 10;
197
198 // Copy contents until matching closing brace (tracking nested braces)
199 int markerBraceCount = 1;
200
201 while( i < sourceLen && markerBraceCount > 0 )
202 {
203 if( aSource[i] == '{' )
204 markerBraceCount++;
205 else if( aSource[i] == '}' )
206 markerBraceCount--;
207
208 token.append( aSource[i] );
209 i++;
210 }
211
212 i--; // Adjust for outer loop increment
213 continue;
214 }
215
216 if( aSource[i] == '{' )
217 {
218 braceDepth++;
219 token.append( aSource[i] );
220 }
221 else if( aSource[i] == '}' )
222 {
223 braceDepth--;
224
225 if( braceDepth == 0 )
226 break; // Found the matching closing brace
227 else
228 token.append( aSource[i] );
229 }
230 else
231 {
232 token.append( aSource[i] );
233 }
234 }
235
236 if( token.IsEmpty() )
237 continue;
238
239 // For math expressions @{...}, recursively expand any nested ${...} variables
240 // but DON'T evaluate the math - leave that for EvaluateText() called by the user
241 if( isMathExpr )
242 {
243 if( ( token.Contains( wxT( "${" ) ) || token.Contains( wxT( "@{" ) ) ) && aDepth < maxDepth )
244 {
245 token = ExpandTextVars( token, aResolver, aFlags, aDepth + 1 );
246 }
247
248 // Return the expression with variables expanded but NOT evaluated
249 // The caller will use EvaluateText() to handle the math evaluation
250 newbuf.append( wxT( "@{" ) + token + wxT( "}" ) );
251 }
252 else // Variable reference ${...}
253 {
254 // Recursively expand nested variables BEFORE passing to resolver
255 // This ensures innermost variables are expanded first (standard evaluation order)
256 if( ( token.Contains( wxT( "${" ) ) || token.Contains( wxT( "@{" ) ) ) && aDepth < maxDepth )
257 {
258 token = ExpandTextVars( token, aResolver, aFlags, aDepth + 1 );
259
260 // Also evaluate math expressions after expanding variables
261 if( token.Contains( wxT( "@{" ) ) )
262 {
263 static EXPRESSION_EVALUATOR evaluator;
264 token = evaluator.Evaluate( token );
265 }
266 }
267
268 if( ( aFlags & FOR_ERC_DRC ) == 0
269 && ( token.StartsWith( wxS( "ERC_WARNING" ) ) || token.StartsWith( wxS( "ERC_ERROR" ) )
270 || token.StartsWith( wxS( "DRC_WARNING" ) ) || token.StartsWith( wxS( "DRC_ERROR" ) ) ) )
271 {
272 // Only show user-defined warnings/errors during ERC/DRC
273 }
274 else if( aResolver && ( *aResolver )( &token ) )
275 {
276 newbuf.append( token );
277 }
278 else
279 {
280 // Token not resolved: leave the reference unchanged
281 newbuf.append( "${" + token + "}" );
282 }
283 }
284 }
285 else
286 {
287 newbuf.append( aSource[i] );
288 }
289 }
290
291 return newbuf;
292}
293
294
295wxString ResolveTextVars( const wxString& aSource, const std::function<bool( wxString* )>* aResolver, int& aDepth )
296{
297 // Multi-pass resolution to handle nested variables like ${J601:UNIT(${ROW})}
298 // and math expressions like @{${ROW}-1}
299 wxString text = aSource;
301
302 static EXPRESSION_EVALUATOR evaluator;
303
304 while( ( text.Contains( wxT( "${" ) ) || text.Contains( wxT( "@{" ) ) ) && ++aDepth <= maxDepth )
305 {
306 // Always expand when ${} or @{} present to handle escape sequences (\${} and \@{})
307 // ExpandTextVars converts escapes to markers and expands ${} variables
308 // Don't expand if the only remaining $ or @ are in escape markers like <<<ESC_DOLLAR: or <<<ESC_AT:
309 if( text.Contains( wxT( "${" ) ) || text.Contains( wxT( "@{" ) ) )
310 text = ExpandTextVars( text, aResolver );
311
312 // Only evaluate if there are @{} expressions present (not escape markers)
313 // Don't evaluate if the only remaining @ are in escape markers like <<<ESC_AT:
314 if( text.Contains( wxT( "@{" ) ) )
315 text = evaluator.Evaluate( text ); // Evaluate math expressions
316 }
317
318 return text;
319}
320
321
322wxString GetGeneratedFieldDisplayName( const wxString& aSource )
323{
324 std::function<bool( wxString* )> tokenExtractor = [&]( wxString* token ) -> bool
325 {
326 *token = *token; // token value is the token name
327 return true;
328 };
329
330 return ExpandTextVars( aSource, &tokenExtractor );
331}
332
333
334bool IsGeneratedField( const wxString& aSource )
335{
336 static wxRegEx expr( wxS( "^\\$\\{\\w*\\}$" ) );
337 return expr.Matches( aSource );
338}
339
340
341wxString DescribeRef( const wxString& aRef )
342{
343 if( aRef.IsEmpty() )
344 return wxT( "<i>" ) + _( "unannotated footprint" ) + wxT( " </i>" );
345 else
346 return EscapeHTML( aRef );
347}
348
349
350//
351// Stolen from wxExpandEnvVars and then heavily optimized
352//
353wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject, std::set<wxString>* aSet = nullptr )
354{
355 // If the same string is inserted twice, we have a loop
356 if( aSet )
357 {
358 if( auto [_, result] = aSet->insert( str ); !result )
359 return str;
360 }
361
362 size_t strlen = str.length();
363
364 wxString strResult;
365 strResult.Alloc( strlen ); // best guess (improves performance)
366
367 auto getVersionedEnvVar = []( const wxString& aMatch, wxString& aResult ) -> bool
368 {
369 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
370 {
371 if( var.Matches( aMatch ) )
372 {
373 const auto value = ENV_VAR::GetEnvVar<wxString>( var );
374
375 if( !value )
376 continue;
377
378 aResult += *value;
379 return true;
380 }
381 }
382
383 return false;
384 };
385
386 for( size_t n = 0; n < strlen; n++ )
387 {
388 wxUniChar str_n = str[n];
389
390 switch( str_n.GetValue() )
391 {
392#ifdef __WINDOWS__
393 case wxT( '%' ):
394#endif // __WINDOWS__
395 case wxT( '$' ):
396 {
397 Bracket bracket;
398#ifdef __WINDOWS__
399 if( str_n == wxT( '%' ) )
400 {
401 bracket = Bracket_Windows;
402 }
403 else
404#endif // __WINDOWS__
405 if( n == strlen - 1 )
406 {
407 bracket = Bracket_None;
408 }
409 else
410 {
411 switch( str[n + 1].GetValue() )
412 {
413 case wxT( '(' ):
414 bracket = Bracket_Normal;
415 str_n = str[++n]; // skip the bracket
416 break;
417
418 case wxT( '{' ):
419 bracket = Bracket_Curly;
420 str_n = str[++n]; // skip the bracket
421 break;
422
423 default: bracket = Bracket_None;
424 }
425 }
426
427 size_t m = n + 1;
428
429 if( m >= strlen )
430 break;
431
432 wxUniChar str_m = str[m];
433
434 while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
435 {
436 if( ++m == strlen )
437 {
438 str_m = 0;
439 break;
440 }
441
442 str_m = str[m];
443 }
444
445 wxString strVarName( str.c_str() + n + 1, m - n - 1 );
446
447 // NB: use wxGetEnv instead of wxGetenv as otherwise variables
448 // set through wxSetEnv may not be read correctly!
449 bool expanded = false;
450 wxString tmp = strVarName;
451
452 if( aProject && aProject->TextVarResolver( &tmp ) )
453 {
454 strResult += tmp;
455 expanded = true;
456 }
457 else if( wxGetEnv( strVarName, &tmp ) )
458 {
459 strResult += tmp;
460 expanded = true;
461 }
462 // Replace unmatched older variables with current locations
463 // If the user has the older location defined, that will be matched
464 // first above. But if they do not, this will ensure that their board still
465 // displays correctly
466 else if( strVarName.Contains( "KISYS3DMOD" ) || strVarName.Matches( "KICAD*_3DMODEL_DIR" ) )
467 {
468 if( getVersionedEnvVar( "KICAD*_3DMODEL_DIR", strResult ) )
469 expanded = true;
470 }
471 else if( strVarName.Matches( "KICAD*_SYMBOL_DIR" ) )
472 {
473 if( getVersionedEnvVar( "KICAD*_SYMBOL_DIR", strResult ) )
474 expanded = true;
475 }
476 else if( strVarName.Matches( "KICAD*_FOOTPRINT_DIR" ) )
477 {
478 if( getVersionedEnvVar( "KICAD*_FOOTPRINT_DIR", strResult ) )
479 expanded = true;
480 }
481 else if( strVarName.Matches( "KICAD*_3RD_PARTY" ) )
482 {
483 if( getVersionedEnvVar( "KICAD*_3RD_PARTY", strResult ) )
484 expanded = true;
485 }
486 else
487 {
488 // variable doesn't exist => don't change anything
489#ifdef __WINDOWS__
490 if( bracket != Bracket_Windows )
491#endif
492 if( bracket != Bracket_None )
493 strResult << str[n - 1];
494
495 strResult << str_n << strVarName;
496 }
497
498 // check the closing bracket
499 if( bracket != Bracket_None )
500 {
501 if( m == strlen || str_m != (wxChar) bracket )
502 {
503 // under MSW it's common to have '%' characters in the registry
504 // and it's annoying to have warnings about them each time, so
505 // ignore them silently if they are not used for env vars
506 //
507 // under Unix, OTOH, this warning could be useful for the user to
508 // understand why isn't the variable expanded as intended
509#ifndef __WINDOWS__
510 wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
511 "at position %u in '%s'." ),
512 (char) bracket, (unsigned int) ( m + 1 ), str.c_str() );
513#endif // __WINDOWS__
514 }
515 else
516 {
517 // skip closing bracket unless the variables wasn't expanded
518 if( !expanded )
519 strResult << (wxChar) bracket;
520
521 m++;
522 }
523 }
524
525 n = m - 1; // skip variable name
526 str_n = str[n];
527 }
528 break;
529
530 case wxT( '\\' ):
531 // backslash can be used to suppress special meaning of % and $
532 if( n < strlen - 1 && ( str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' ) ) )
533 {
534 str_n = str[++n];
535 strResult += str_n;
536
537 break;
538 }
539
541
542 default: strResult += str_n;
543 }
544 }
545
546 std::set<wxString> loop_check;
547 auto first_pos = strResult.find_first_of( wxS( "{(%" ) );
548 auto last_pos = strResult.find_last_of( wxS( "})%" ) );
549
550 if( first_pos != strResult.npos && last_pos != strResult.npos && first_pos != last_pos )
551 strResult = KIwxExpandEnvVars( strResult, aProject, aSet ? aSet : &loop_check );
552
553 return strResult;
554}
555
556
557const wxString ExpandEnvVarSubstitutions( const wxString& aString, const PROJECT* aProject )
558{
559 // wxGetenv( wchar_t* ) is not re-entrant on linux.
560 // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
561 static std::mutex getenv_mutex;
562
563 std::lock_guard<std::mutex> lock( getenv_mutex );
564
565 // We reserve the right to do this another way, by providing our own member function.
566 return KIwxExpandEnvVars( aString, aProject );
567}
568
569
570const wxString ResolveUriByEnvVars( const wxString& aUri, const PROJECT* aProject )
571{
572 wxString uri = ExpandTextVars( aUri, aProject );
573
574 return ExpandEnvVarSubstitutions( uri, aProject );
575}
576
577
578bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName, const wxString& aBaseFilename, REPORTER* aReporter )
579{
580 wxString msg;
581 wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
582
583 // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
584 // already an absolute path) absolute:
585 if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
586 {
587 if( aReporter )
588 {
589 msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ), aTargetFullFileName->GetPath(),
590 baseFilePath );
591 aReporter->Report( msg, RPT_SEVERITY_ERROR );
592 }
593
594 return false;
595 }
596
597 // Ensure the path of aTargetFullFileName exists, and create it if needed:
598 wxString outputPath( aTargetFullFileName->GetPath() );
599
600 if( !wxFileName::DirExists( outputPath ) )
601 {
602 // Make every directory provided when the provided path doesn't exist
603 if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
604 {
605 if( aReporter )
606 {
607 msg.Printf( _( "Output directory '%s' created." ), outputPath );
608 aReporter->Report( msg, RPT_SEVERITY_INFO );
609 return true;
610 }
611 }
612 else
613 {
614 if( aReporter )
615 {
616 msg.Printf( _( "Cannot create output directory '%s'." ), outputPath );
617 aReporter->Report( msg, RPT_SEVERITY_ERROR );
618 }
619
620 return false;
621 }
622 }
623
624 return true;
625}
626
627
628wxString EnsureFileExtension( const wxString& aFilename, const wxString& aExtension )
629{
630 wxString newFilename( aFilename );
631
632 // It's annoying to throw up nag dialogs when the extension isn't right. Just fix it,
633 // but be careful not to destroy existing after-dot-text that isn't actually a bad
634 // extension, such as "Schematic_1.1".
635 if( newFilename.Lower().AfterLast( '.' ) != aExtension )
636 {
637 if( !newFilename.EndsWith( '.' ) )
638 newFilename.Append( '.' );
639
640 newFilename.Append( aExtension );
641 }
642
643 return newFilename;
644}
645
646
647wxString JoinExtensions( const std::vector<std::string>& aExts )
648{
649 wxString joined;
650
651 for( const std::string& ext : aExts )
652 {
653 if( !joined.empty() )
654 joined << wxS( ", " );
655
656 joined << wxS( "*." ) << ext;
657 }
658
659 return joined;
660}
661
662
670
677bool matchWild( const char* pat, const char* text, bool dot_special )
678{
679 if( !*text )
680 {
681 /* Match if both are empty. */
682 return !*pat;
683 }
684
685 const char *m = pat, *n = text, *ma = nullptr, *na = nullptr;
686 int just = 0, acount = 0, count = 0;
687
688 if( dot_special && ( *n == '.' ) )
689 {
690 /* Never match so that hidden Unix files
691 * are never found. */
692 return false;
693 }
694
695 for( ;; )
696 {
697 if( *m == '*' )
698 {
699 ma = ++m;
700 na = n;
701 just = 1;
702 acount = count;
703 }
704 else if( *m == '?' )
705 {
706 m++;
707
708 if( !*n++ )
709 return false;
710 }
711 else
712 {
713 if( *m == '\\' )
714 {
715 m++;
716
717 /* Quoting "nothing" is a bad thing */
718 if( !*m )
719 return false;
720 }
721
722 if( !*m )
723 {
724 /*
725 * If we are out of both strings or we just
726 * saw a wildcard, then we can say we have a
727 * match
728 */
729 if( !*n )
730 return true;
731
732 if( just )
733 return true;
734
735 just = 0;
736 goto not_matched;
737 }
738
739 /*
740 * We could check for *n == NULL at this point, but
741 * since it's more common to have a character there,
742 * check to see if they match first (m and n) and
743 * then if they don't match, THEN we can check for
744 * the NULL of n
745 */
746 just = 0;
747
748 if( *m == *n )
749 {
750 m++;
751 count++;
752 n++;
753 }
754 else
755 {
756 not_matched:
757
758 /*
759 * If there are no more characters in the
760 * string, but we still need to find another
761 * character (*m != NULL), then it will be
762 * impossible to match it
763 */
764 if( !*n )
765 return false;
766
767 if( ma )
768 {
769 m = ma;
770 n = ++na;
771 count = acount;
772 }
773 else
774 return false;
775 }
776 }
777 }
778}
779
780
785#if wxUSE_DATETIME && defined( __WIN32__ ) && !defined( __WXMICROWIN__ )
786
787// Convert between wxDateTime and FILETIME which is a 64-bit value representing
788// the number of 100-nanosecond intervals since January 1, 1601 UTC.
789//
790// This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
791static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL( 11644473600000 );
792
793
794static void ConvertFileTimeToWx( wxDateTime* dt, const FILETIME& ft )
795{
796 wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
797 t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
798 t -= EPOCH_OFFSET_IN_MSEC;
799
800 *dt = wxDateTime( t );
801}
802
803#endif // wxUSE_DATETIME && __WIN32__
804
805
814long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
815{
816 long long timestamp = 0;
817
818#if defined( __WIN32__ )
819 // Win32 version.
820 // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
821 // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
822 // conversion by staying on the MSW side of things.
823 std::wstring filespec( aDirPath.t_str() );
824 filespec += '\\';
825 filespec += aFilespec.t_str();
826
827 WIN32_FIND_DATA findData;
828 wxDateTime lastModDate;
829
830 HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
831
832 if( fileHandle != INVALID_HANDLE_VALUE )
833 {
834 do
835 {
836 ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
837 timestamp += lastModDate.GetValue().GetValue();
838
839 // Get the file size (partial) as well to check for sneaky changes.
840 timestamp += findData.nFileSizeLow;
841 } while( FindNextFile( fileHandle, &findData ) != 0 );
842 }
843
844 FindClose( fileHandle );
845#else
846 // POSIX version.
847 // Save time by not converting between encodings -- do everything on the file-system side.
848 std::string filespec( aFilespec.fn_str() );
849 std::string dir_path( aDirPath.fn_str() );
850
851 DIR* dir = opendir( dir_path.c_str() );
852
853 if( dir )
854 {
855 for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
856 {
857 if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
858 continue;
859
860 std::string entry_path = dir_path + '/' + dir_entry->d_name;
861 struct stat entry_stat;
862
863 if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
864 {
865 // Timestamp the source file, not the symlink
866 if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
867 {
868 char buffer[PATH_MAX + 1];
869 ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
870
871 if( pathLen > 0 )
872 {
873 struct stat linked_stat;
874 buffer[pathLen] = '\0';
875 entry_path = dir_path + buffer;
876
877 if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
878 {
879 entry_stat = linked_stat;
880 }
881 else
882 {
883 // if we couldn't lstat the linked file we'll have to just use
884 // the symbolic link info
885 }
886 }
887 }
888
889 if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
890 {
891 timestamp += entry_stat.st_mtime * 1000;
892
893 // Get the file size as well to check for sneaky changes.
894 timestamp += entry_stat.st_size;
895 }
896 }
897 else
898 {
899 // if we couldn't lstat the file itself all we can do is use the name
900 timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
901 }
902 }
903
904 closedir( dir );
905 }
906#endif
907
908 return timestamp;
909}
910
911
913{
915 return false;
916
917 wxMessageDialog dialog( nullptr,
918 _( "This operating system is not supported "
919 "by KiCad and its dependencies." ),
920 _( "Unsupported Operating System" ), wxOK | wxICON_EXCLAMATION );
921
922 dialog.SetExtendedMessage( _( "Any issues with KiCad on this system cannot "
923 "be reported to the official bugtracker." ) );
924 dialog.ShowModal();
925
926 return true;
927}
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:557
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:647
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition common.cpp:322
const wxString ResolveUriByEnvVars(const wxString &aUri, const PROJECT *aProject)
Replace any environment and/or text variables in URIs.
Definition common.cpp:570
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:628
bool WarnUserIfOperatingSystemUnsupported()
Checks if the operating system is explicitly unsupported and displays a disclaimer message box.
Definition common.cpp:912
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:677
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:578
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject, std::set< wxString > *aSet=nullptr)
Definition common.cpp:353
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:334
wxString DescribeRef(const wxString &aRef)
Returns a user-visible HTML string describing a footprint reference designator.
Definition common.cpp:341
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:295
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:814
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.