39#include <wx/stdpaths.h>
55 Bracket_Windows =
'%',
62 std::function<bool( wxString* )> projectResolver =
63 [&]( wxString* token ) ->
bool
74 wxString
path = aPath;
79 path.Replace( wxT(
"\\${" ), wxT(
"/${" ) );
80 path.Replace( wxT(
"\\@{" ), wxT(
"/@{" ) );
92 aText.Replace( wxT(
"<<<ESC_DOLLAR:" ), wxT(
"${" ) );
93 aText.Replace( wxT(
"<<<ESC_AT:" ), wxT(
"@{" ) );
97 aText.Replace( wxT(
"<<<ESC_DOLLAR:" ), wxT(
"\\${" ) );
98 aText.Replace( wxT(
"<<<ESC_AT:" ), wxT(
"\\@{" ) );
103wxString
ExpandTextVars(
const wxString& aSource,
const std::function<
bool( wxString* )>* aResolver,
110 size_t sourceLen = aSource.length();
112 newbuf.Alloc( sourceLen );
117 for(
size_t i = 0; i < sourceLen; ++i )
121 if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT(
"<<<ESC_DOLLAR:" ) )
124 newbuf.append( wxT(
"<<<ESC_DOLLAR:" ) );
129 while( i < sourceLen && braceCount > 0 )
131 if( aSource[i] ==
'{' )
133 else if( aSource[i] ==
'}' )
136 newbuf.append( aSource[i] );
142 else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT(
"<<<ESC_AT:" ) )
145 newbuf.append( wxT(
"<<<ESC_AT:" ) );
150 while( i < sourceLen && braceCount > 0 )
152 if( aSource[i] ==
'{' )
154 else if( aSource[i] ==
'}' )
157 newbuf.append( aSource[i] );
167 if( aSource[i] ==
'\\' && i + 1 < sourceLen )
169 if( ( aSource[i + 1] ==
'$' || aSource[i + 1] ==
'@' ) && i + 2 < sourceLen && aSource[i + 2] ==
'{' )
173 if( aSource[i + 1] ==
'$' )
174 newbuf.append( wxT(
"<<<ESC_DOLLAR:" ) );
176 newbuf.append( wxT(
"<<<ESC_AT:" ) );
181 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
183 if( aSource[i] ==
'{' )
185 else if( aSource[i] ==
'}' )
188 newbuf.append( aSource[i] );
195 if( ( aSource[i] ==
'$' || aSource[i] ==
'@' ) && i + 1 < sourceLen && aSource[i + 1] ==
'{' )
197 bool isMathExpr = ( aSource[i] ==
'@' );
201 for( i = i + 2; i < sourceLen; ++i )
205 if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT(
"<<<ESC_DOLLAR:" ) )
207 token.append( wxT(
"<<<ESC_DOLLAR:" ) );
211 int markerBraceCount = 1;
213 while( i < sourceLen && markerBraceCount > 0 )
215 if( aSource[i] ==
'{' )
217 else if( aSource[i] ==
'}' )
220 token.append( aSource[i] );
227 else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT(
"<<<ESC_AT:" ) )
229 token.append( wxT(
"<<<ESC_AT:" ) );
233 int markerBraceCount = 1;
235 while( i < sourceLen && markerBraceCount > 0 )
237 if( aSource[i] ==
'{' )
239 else if( aSource[i] ==
'}' )
242 token.append( aSource[i] );
250 if( aSource[i] ==
'{' )
253 token.append( aSource[i] );
255 else if( aSource[i] ==
'}' )
259 if( braceDepth == 0 )
262 token.append( aSource[i] );
266 token.append( aSource[i] );
270 if( token.IsEmpty() )
277 if( ( token.Contains( wxT(
"${" ) ) || token.Contains( wxT(
"@{" ) ) ) && aDepth < maxDepth )
284 newbuf.append( wxT(
"@{" ) + token + wxT(
"}" ) );
290 if( ( token.Contains( wxT(
"${" ) ) || token.Contains( wxT(
"@{" ) ) ) && aDepth < maxDepth )
295 if( token.Contains( wxT(
"@{" ) ) )
301 token = evaluator.
Evaluate( token );
305 if( aContext ==
FOR_ERC_DRC && ( token.StartsWith( wxS(
"ERC_WARNING" ) )
306 || token.StartsWith( wxS(
"ERC_ERROR" ) )
307 || token.StartsWith( wxS(
"DRC_WARNING" ) )
308 || token.StartsWith( wxS(
"DRC_ERROR" ) ) ) )
312 else if( aResolver && ( *aResolver )( &token ) )
314 newbuf.append( token );
319 newbuf.append(
"${" + token +
"}" );
325 newbuf.append( aSource[i] );
333wxString
ResolveTextVars(
const wxString& aSource,
const std::function<
bool( wxString* )>* aResolver,
int& aDepth )
337 wxString
text = aSource;
345 while( (
text.Contains( wxT(
"${" ) ) ||
text.Contains( wxT(
"@{" ) ) ) && ++aDepth <= maxDepth )
350 if(
text.Contains( wxT(
"${" ) ) ||
text.Contains( wxT(
"@{" ) ) )
355 if(
text.Contains( wxT(
"@{" ) ) )
372wxString walkRef(
const wxString& aText, std::size_t& aPos, std::vector<TEXT_VAR_REF_KEY>& aOut );
374void collectRefsFrom(
const wxString& aText, std::size_t aStart, std::size_t aEnd,
375 std::vector<TEXT_VAR_REF_KEY>& aOut )
377 for( std::size_t i = aStart; i < aEnd; ++i )
379 const wxUniChar c = aText[i];
383 if( c == wxT(
'\\' ) && i + 2 < aEnd && ( aText[i + 1] == wxT(
'$' ) || aText[i + 1] == wxT(
'@' ) )
384 && aText[i + 2] == wxT(
'{' ) )
386 std::size_t j = i + 3;
389 while( j < aEnd && depth > 0 )
391 if( aText[j] == wxT(
'{' ) )
393 else if( aText[j] == wxT(
'}' ) )
403 if( ( c == wxT(
'$' ) || c == wxT(
'@' ) ) && i + 1 < aEnd && aText[i + 1] == wxT(
'{' ) )
405 std::size_t pos = i + 2;
406 const wxString token = walkRef( aText, pos, aOut );
411 if( c == wxT(
'$' ) && !token.IsEmpty() )
416 i = ( pos > 0 ? pos - 1 : pos );
422wxString walkRef(
const wxString& aText, std::size_t& aPos, std::vector<TEXT_VAR_REF_KEY>& aOut )
424 const std::size_t len = aText.length();
425 const std::size_t start = aPos;
430 const wxUniChar c = aText[aPos];
432 if( c == wxT(
'{' ) )
436 else if( c == wxT(
'}' ) )
442 wxString body = aText.Mid( start, aPos - start );
450 collectRefsFrom( body, 0, body.length(), aOut );
461 wxString partial = aText.Mid( start, aPos - start );
462 collectRefsFrom( partial, 0, partial.length(), aOut );
470 std::vector<TEXT_VAR_REF_KEY> refs;
473 if( !aSource.Contains( wxT(
"${" ) ) && !aSource.Contains( wxT(
"@{" ) ) )
476 collectRefsFrom( aSource, 0, aSource.length(), refs );
483 std::function<bool( wxString* )> tokenExtractor =
484 [&]( wxString* token ) ->
bool
497 thread_local wxRegEx expr( wxS(
"^(\\$\\{[\\w.]*\\}|@\\{.*\\})$" ) );
498 return expr.Matches( aFieldName );
504 return aValue.Contains( wxT(
"${" ) ) || aValue.Contains( wxT(
"@{" ) );
511 return wxT(
"<i>" ) +
_(
"unannotated footprint" ) + wxT(
" </i>" );
529 size_t strlen = str.length();
532 strResult.Alloc( strlen );
534 auto readEnvironment = [](
const wxString& aName, wxString* aValue ) ->
bool
536 const bool found = wxGetEnv( aName, aValue );
539 environment->RecordEnvironmentVariable( aName, found ? std::optional<wxString>( *aValue ) : std::nullopt );
544 auto getVersionedEnvVar =
545 [&](
const wxString& aMatch, wxString& aResult ) ->
bool
549 if( var.Matches( aMatch ) )
553 if( !readEnvironment( var, &value ) )
564 for(
size_t n = 0; n < strlen; n++ )
566 wxUniChar str_n = str[n];
568 switch( str_n.GetValue() )
575 char controlChar = str_n.GetValue();
579 if( str_n == wxT(
'%' ) )
581 bracket = Bracket_Windows;
585 if( n == strlen - 1 )
591 switch( str[n + 1].GetValue() )
613 wxUniChar str_m = str[m];
615 while( wxIsalnum( str_m ) || str_m == wxT(
'_' ) || str_m == wxT(
':' ) )
626 wxString strVarName( str.c_str() + n + 1, m - n - 1 );
628 if( controlChar ==
'$' && bracket ==
Bracket_Curly && ( strVarName == wxT(
"DRC_WARNING" )
629 || strVarName == wxT(
"DRC_ERROR" )
630 || strVarName == wxT(
"ERC_WARNING" )
631 || strVarName == wxT(
"ERC_ERROR" ) ) )
634 strResult << controlChar << bracket << strVarName << str_m;
641 bool expanded =
false;
642 wxString tmp = strVarName;
649 else if( readEnvironment( strVarName, &tmp ) )
658 else if( strVarName.Contains(
"KISYS3DMOD" )
661 if( getVersionedEnvVar(
"KICAD*_3DMODEL_DIR", strResult ) )
664 else if( strVarName ==
"KICAD_SYMBOL_DIR"
667 if( getVersionedEnvVar(
"KICAD*_SYMBOL_DIR", strResult ) )
672 if( getVersionedEnvVar(
"KICAD*_FOOTPRINT_DIR", strResult ) )
677 if( getVersionedEnvVar(
"KICAD*_3RD_PARTY", strResult ) )
684 if( bracket != Bracket_Windows )
687 strResult << str[n - 1];
689 strResult << str_n << strVarName;
698 auto isVersionedWildcard =
699 strVarName.Contains( wxT(
"KISYS3DMOD" ) )
700 || strVarName == wxT(
"KICAD_SYMBOL_DIR" )
706 if( isVersionedWildcard )
709 if( bracket != Bracket_Windows )
711 strResult << str[n - 1];
713 strResult << str_n << strVarName;
720 if( m == strlen || str_m != (wxChar) bracket )
729 wxLogWarning(
_(
"Environment variables expansion failed: missing '%c' "
730 "at position %u in '%s'." ),
731 (
char) bracket, (
unsigned int) ( m + 1 ), str.c_str() );
738 strResult << (wxChar) bracket;
750 if( n < strlen - 1 && ( str[n + 1] == wxT(
'%' ) || str[n + 1] == wxT(
'$' ) ) )
765 std::set<wxString> loop_check;
766 size_t first_pos = strResult.find_first_of( wxS(
"{(%" ) );
767 size_t last_pos = strResult.find_last_of( wxS(
"})%" ) );
769 if( first_pos != strResult.npos && last_pos != strResult.npos && first_pos != last_pos )
780 static std::mutex getenv_mutex;
782 std::lock_guard<std::mutex> lock( getenv_mutex );
800 wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
804 if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
808 msg.Printf(
_(
"Cannot make path '%s' absolute with respect to '%s'." ),
809 aTargetFullFileName->GetPath(),
818 wxString outputPath( aTargetFullFileName->GetPath() );
820 if( !wxFileName::DirExists( outputPath ) )
823 if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
827 msg.Printf(
_(
"Output directory '%s' created." ), outputPath );
836 msg.Printf(
_(
"Cannot create output directory '%s'." ), outputPath );
850 wxString newFilename( aFilename );
855 if( newFilename.Lower().AfterLast(
'.' ) != aExtension )
857 if( !newFilename.EndsWith(
'.' ) )
858 newFilename.Append(
'.' );
860 newFilename.Append( aExtension );
871 for(
const std::string& ext : aExts )
873 if( !joined.empty() )
874 joined << wxS(
", " );
876 joined << wxS(
"*." ) << ext;
905 const char *m = pat, *n =
text, *ma =
nullptr, *na =
nullptr;
906 int just = 0, acount = 0, count = 0;
908 if( dot_special && ( *n ==
'.' ) )
1008 wxLog::FlushActive();
1011 _(
"This operating system is not supported "
1012 "by KiCad and its dependencies." ),
1013 _(
"Unsupported Operating System" ), wxOK | wxICON_EXCLAMATION );
1015 dialog.SetExtendedMessage(
_(
"Any issues with KiCad on this system cannot "
1016 "be reported to the official bugtracker." ) );
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.
virtual bool TextVarResolver(wxString *aToken) const
A pure virtual class used to derive REPORTER objects from.
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
A text evaluation frame with one frozen clock and memoized external queries.
static ENVIRONMENT * Current()
void FinalizeTextVarExpansion(wxString &aText, RESOLUTION_CONTEXT aContext)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
const wxString ResolveUriByEnvVars(const wxString &aUri, const PROJECT *aProject)
Replace any environment and/or text variables in URIs.
wxString EnsureFileExtension(const wxString &aFilename, const wxString &aExtension)
It's annoying to throw up nag dialogs when the extension isn't right.
bool WarnUserIfOperatingSystemUnsupported()
Checks if the operating system is explicitly unsupported and displays a disclaimer message box.
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, RESOLUTION_CONTEXT aContext)
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
wxString NormalizeFilePathForTextVars(const wxString &aPath)
Normalize a file path so its text variables survive ExpandTextVars.
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.
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject, std::set< wxString > *aSet=nullptr)
wxString DescribeRef(const wxString &aRef)
Returns a user-visible HTML string describing a footprint reference designator.
bool IsGeneratedValue(const wxString &aValue)
Returns true if some of the string is generated, e.g contains a text var or expression.
bool IsGeneratedField(const wxString &aFieldName)
Returns true if the entire string is generated, e.g is a single text var reference.
wxString ResolveTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aResolver, int &aDepth)
Multi-pass text variable expansion and math expression evaluation.
std::vector< TEXT_VAR_REF_KEY > ExtractTextVarReferences(const wxString &aSource)
Lex-scan aSource and return every ${...} reference that appears, without resolving.
This file is part of the common library.
#define KICAD_MESSAGE_DIALOG
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 ...
KICOMMON_API bool IsVersionedEnvVar(const wxString &aName, const wxString &aBaseName)
Test whether a name is a versioned KiCad environment variable for the given base, i....
KICOMMON_API const std::vector< wxString > & GetPredefinedEnvVars()
Get the list of pre-defined environment variables.
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
static TEXT_VAR_REF_KEY FromToken(const wxString &aToken)
Parse a raw token (the text between ${ and }) into a key using lexical classification only — no looku...
wxString result
Test unit parsing edge cases and error handling.