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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <eda_base_frame.h>
23#include <kiplatform/app.h>
24#include <project.h>
25#include <common.h>
26#include <confirm.h>
27#include <env_vars.h>
28#include <advanced_config.h>
29#include <reporter.h>
30#include <macros.h>
31#include <string_utils.h>
33#include <text_var_dependency.h>
34#include <mutex>
35#include <wx/config.h>
36#include <wx/log.h>
37#include <wx/msgdlg.h>
38#include <wx/stdpaths.h>
39#include <wx/url.h>
40#include <wx/utils.h>
41#include <wx/regex.h>
42
43#ifdef _WIN32
44#include <windows.h>
45#endif
46
47
49{
53#ifdef __WINDOWS__
54 Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
55#endif
57};
58
59wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject, int aFlags )
60{
61 std::function<bool( wxString* )> projectResolver = [&]( wxString* token ) -> bool
62 {
63 return aProject->TextVarResolver( token );
64 };
65
66 return ExpandTextVars( aSource, &projectResolver, aFlags );
67}
68
69
70wxString ExpandTextVars( const wxString& aSource, const std::function<bool( wxString* )>* aResolver, int aFlags,
71 int aDepth )
72{
73 wxString newbuf;
74 size_t sourceLen = aSource.length();
75
76 newbuf.Alloc( sourceLen ); // best guess (improves performance)
77
78 // Get the maximum recursion depth from advanced config
80
81 for( size_t i = 0; i < sourceLen; ++i )
82 {
83 // Skip over existing escape markers without processing their contents
84 // This prevents expanding ${} or @{} that are inside escaped expressions
85 if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT( "<<<ESC_DOLLAR:" ) )
86 {
87 // Copy the entire escape marker including contents until matching closing }
88 newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
89 i += 14;
90
91 // Count braces to find the matching closing }
92 int braceCount = 1;
93 while( i < sourceLen && braceCount > 0 )
94 {
95 if( aSource[i] == '{' )
96 braceCount++;
97 else if( aSource[i] == '}' )
98 braceCount--;
99
100 newbuf.append( aSource[i] );
101 i++;
102 }
103 i--; // Back up one since the for loop will increment
104 continue;
105 }
106 else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT( "<<<ESC_AT:" ) )
107 {
108 // Copy the entire escape marker including contents until matching closing }
109 newbuf.append( wxT( "<<<ESC_AT:" ) );
110 i += 10;
111
112 // Count braces to find the matching closing }
113 int braceCount = 1;
114 while( i < sourceLen && braceCount > 0 )
115 {
116 if( aSource[i] == '{' )
117 braceCount++;
118 else if( aSource[i] == '}' )
119 braceCount--;
120
121 newbuf.append( aSource[i] );
122 i++;
123 }
124 i--; // Back up one since the for loop will increment
125 continue;
126 }
127
128 // Handle escaped variable references: \${...} or \@{...}
129 // Replace with escape markers that won't be expanded by multi-pass loops
130 // The markers will be converted back to ${...} or @{...} only at the final display stage
131 if( aSource[i] == '\\' && i + 1 < sourceLen )
132 {
133 if( ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) && i + 2 < sourceLen && aSource[i + 2] == '{' )
134 {
135 // Replace \${ with <<<ESC_DOLLAR: and \@{ with <<<ESC_AT:
136 // Using unique delimiters without braces to avoid confusing the expression evaluator
137 if( aSource[i + 1] == '$' )
138 newbuf.append( wxT( "<<<ESC_DOLLAR:" ) );
139 else
140 newbuf.append( wxT( "<<<ESC_AT:" ) );
141 i += 2;
142
143 // Copy everything until the matching closing brace, including the brace
144 int braceDepth = 1;
145 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
146 {
147 if( aSource[i] == '{' )
148 braceDepth++;
149 else if( aSource[i] == '}' )
150 braceDepth--;
151
152 newbuf.append( aSource[i] );
153 }
154 i--; // Adjust because loop will increment
155 continue;
156 }
157 }
158
159 if( ( aSource[i] == '$' || aSource[i] == '@' ) && i + 1 < sourceLen && aSource[i + 1] == '{' )
160 {
161 bool isMathExpr = ( aSource[i] == '@' );
162 wxString token;
163 int braceDepth = 1; // Track brace depth for nested expressions like @{${VAR}}
164
165 for( i = i + 2; i < sourceLen; ++i )
166 {
167 // Skip over escape markers - don't count their braces
168 // This prevents <<<ESC_DOLLAR:X} from interfering with outer brace counting
169 if( i + 14 <= sourceLen && aSource.Mid( i, 14 ) == wxT( "<<<ESC_DOLLAR:" ) )
170 {
171 token.append( wxT( "<<<ESC_DOLLAR:" ) );
172 i += 14;
173
174 // Copy contents until matching closing brace (tracking nested braces)
175 int markerBraceCount = 1;
176
177 while( i < sourceLen && markerBraceCount > 0 )
178 {
179 if( aSource[i] == '{' )
180 markerBraceCount++;
181 else if( aSource[i] == '}' )
182 markerBraceCount--;
183
184 token.append( aSource[i] );
185 i++;
186 }
187
188 i--; // Adjust for outer loop increment
189 continue;
190 }
191 else if( i + 10 <= sourceLen && aSource.Mid( i, 10 ) == wxT( "<<<ESC_AT:" ) )
192 {
193 token.append( wxT( "<<<ESC_AT:" ) );
194 i += 10;
195
196 // Copy contents until matching closing brace (tracking nested braces)
197 int markerBraceCount = 1;
198
199 while( i < sourceLen && markerBraceCount > 0 )
200 {
201 if( aSource[i] == '{' )
202 markerBraceCount++;
203 else if( aSource[i] == '}' )
204 markerBraceCount--;
205
206 token.append( aSource[i] );
207 i++;
208 }
209
210 i--; // Adjust for outer loop increment
211 continue;
212 }
213
214 if( aSource[i] == '{' )
215 {
216 braceDepth++;
217 token.append( aSource[i] );
218 }
219 else if( aSource[i] == '}' )
220 {
221 braceDepth--;
222
223 if( braceDepth == 0 )
224 break; // Found the matching closing brace
225 else
226 token.append( aSource[i] );
227 }
228 else
229 {
230 token.append( aSource[i] );
231 }
232 }
233
234 if( token.IsEmpty() )
235 continue;
236
237 // For math expressions @{...}, recursively expand any nested ${...} variables
238 // but DON'T evaluate the math - leave that for EvaluateText() called by the user
239 if( isMathExpr )
240 {
241 if( ( token.Contains( wxT( "${" ) ) || token.Contains( wxT( "@{" ) ) ) && aDepth < maxDepth )
242 {
243 token = ExpandTextVars( token, aResolver, aFlags, aDepth + 1 );
244 }
245
246 // Return the expression with variables expanded but NOT evaluated
247 // The caller will use EvaluateText() to handle the math evaluation
248 newbuf.append( wxT( "@{" ) + token + wxT( "}" ) );
249 }
250 else // Variable reference ${...}
251 {
252 // Recursively expand nested variables BEFORE passing to resolver
253 // This ensures innermost variables are expanded first (standard evaluation order)
254 if( ( token.Contains( wxT( "${" ) ) || token.Contains( wxT( "@{" ) ) ) && aDepth < maxDepth )
255 {
256 token = ExpandTextVars( token, aResolver, aFlags, aDepth + 1 );
257
258 // Also evaluate math expressions after expanding variables
259 if( token.Contains( wxT( "@{" ) ) )
260 {
261 // Must not be static. ExpandTextVars runs on parallel workers
262 // (e.g. CONNECTION_GRAPH) and a shared evaluator races on its
263 // internal error collector.
264 EXPRESSION_EVALUATOR evaluator;
265 token = evaluator.Evaluate( token );
266 }
267 }
268
269 if( ( aFlags & FOR_ERC_DRC ) == 0
270 && ( token.StartsWith( wxS( "ERC_WARNING" ) ) || token.StartsWith( wxS( "ERC_ERROR" ) )
271 || token.StartsWith( wxS( "DRC_WARNING" ) ) || token.StartsWith( wxS( "DRC_ERROR" ) ) ) )
272 {
273 // Only show user-defined warnings/errors during ERC/DRC
274 }
275 else if( aResolver && ( *aResolver )( &token ) )
276 {
277 newbuf.append( token );
278 }
279 else
280 {
281 // Token not resolved: leave the reference unchanged
282 newbuf.append( "${" + token + "}" );
283 }
284 }
285 }
286 else
287 {
288 newbuf.append( aSource[i] );
289 }
290 }
291
292 return newbuf;
293}
294
295
296wxString ResolveTextVars( const wxString& aSource, const std::function<bool( wxString* )>* aResolver, int& aDepth )
297{
298 // Multi-pass resolution to handle nested variables like ${J601:UNIT(${ROW})}
299 // and math expressions like @{${ROW}-1}
300 wxString text = aSource;
302
303 // Must not be static. ResolveTextVars runs on parallel workers (e.g.
304 // CONNECTION_GRAPH) and a shared evaluator races on its internal error
305 // collector.
306 EXPRESSION_EVALUATOR evaluator;
307
308 while( ( text.Contains( wxT( "${" ) ) || text.Contains( wxT( "@{" ) ) ) && ++aDepth <= maxDepth )
309 {
310 // Always expand when ${} or @{} present to handle escape sequences (\${} and \@{})
311 // ExpandTextVars converts escapes to markers and expands ${} variables
312 // Don't expand if the only remaining $ or @ are in escape markers like <<<ESC_DOLLAR: or <<<ESC_AT:
313 if( text.Contains( wxT( "${" ) ) || text.Contains( wxT( "@{" ) ) )
314 text = ExpandTextVars( text, aResolver );
315
316 // Only evaluate if there are @{} expressions present (not escape markers)
317 // Don't evaluate if the only remaining @ are in escape markers like <<<ESC_AT:
318 if( text.Contains( wxT( "@{" ) ) )
319 text = evaluator.Evaluate( text ); // Evaluate math expressions
320 }
321
322 return text;
323}
324
325
326namespace
327{
328// Scan @p aText beginning at @p aStart (pointing just past an opening `${` or
329// `@{`), collect every nested and sibling reference, and advance @p aStart past
330// the matching closing brace. Returns the raw token body for the outer
331// reference so the caller can classify it. Escape markers (\${…} and \@{…})
332// are skipped. Brace nesting mirrors the ExpandTextVars state machine so
333// tokens like `${FOO:BAR(${BAZ})}` are captured as a single outer token with
334// BAZ picked up as a nested sibling.
335wxString walkRef( const wxString& aText, std::size_t& aPos, std::vector<TEXT_VAR_REF_KEY>& aOut );
336
337void collectRefsFrom( const wxString& aText, std::size_t aStart, std::size_t aEnd,
338 std::vector<TEXT_VAR_REF_KEY>& aOut )
339{
340 for( std::size_t i = aStart; i < aEnd; ++i )
341 {
342 const wxUniChar c = aText[i];
343
344 // Escape sequences: skip the whole escaped expression; its contents
345 // are a user literal, not a dependency source.
346 if( c == wxT( '\\' ) && i + 2 < aEnd && ( aText[i + 1] == wxT( '$' ) || aText[i + 1] == wxT( '@' ) )
347 && aText[i + 2] == wxT( '{' ) )
348 {
349 std::size_t j = i + 3;
350 int depth = 1;
351
352 while( j < aEnd && depth > 0 )
353 {
354 if( aText[j] == wxT( '{' ) )
355 depth++;
356 else if( aText[j] == wxT( '}' ) )
357 depth--;
358
359 ++j;
360 }
361
362 i = j - 1;
363 continue;
364 }
365
366 if( ( c == wxT( '$' ) || c == wxT( '@' ) ) && i + 1 < aEnd && aText[i + 1] == wxT( '{' ) )
367 {
368 std::size_t pos = i + 2;
369 const wxString token = walkRef( aText, pos, aOut );
370
371 // Math expressions (`@{...}`) contribute their nested variables as
372 // dependency edges but do not produce a named edge themselves — the
373 // expression text is not a resolvable source name.
374 if( c == wxT( '$' ) && !token.IsEmpty() )
375 aOut.push_back( TEXT_VAR_REF_KEY::FromToken( token ) );
376
377 // Clamp to aEnd - 1 so the outer for-loop increment doesn't go past
378 // the end on a malformed token.
379 i = ( pos > 0 ? pos - 1 : pos );
380 }
381 }
382}
383
384
385wxString walkRef( const wxString& aText, std::size_t& aPos, std::vector<TEXT_VAR_REF_KEY>& aOut )
386{
387 const std::size_t len = aText.length();
388 const std::size_t start = aPos;
389 int depth = 1;
390
391 while( aPos < len )
392 {
393 const wxUniChar c = aText[aPos];
394
395 if( c == wxT( '{' ) )
396 {
397 depth++;
398 }
399 else if( c == wxT( '}' ) )
400 {
401 depth--;
402
403 if( depth == 0 )
404 {
405 wxString body = aText.Mid( start, aPos - start );
406 aPos++; // consume the matching close-brace
407
408 // Recurse into the body so nested references are captured
409 // regardless of whether the outer resolves. ExpandTextVars'
410 // resolver path would suppress this for outer tokens beginning
411 // with ERC_WARNING / DRC_WARNING; a dependency tracker must
412 // not care about that (codex finding 3).
413 collectRefsFrom( body, 0, body.length(), aOut );
414 return body;
415 }
416 }
417
418 aPos++;
419 }
420
421 // Malformed token (ran off the end without a matching close-brace).
422 // Recurse into the partial body so nested inner references are still
423 // captured — `${FOO${BAR}` at EOF must still produce a BAR dependency.
424 wxString partial = aText.Mid( start, aPos - start );
425 collectRefsFrom( partial, 0, partial.length(), aOut );
426 return partial;
427}
428}
429
430
431std::vector<TEXT_VAR_REF_KEY> ExtractTextVarReferences( const wxString& aSource )
432{
433 std::vector<TEXT_VAR_REF_KEY> refs;
434
435 // Fast path: no reference syntax at all.
436 if( !aSource.Contains( wxT( "${" ) ) && !aSource.Contains( wxT( "@{" ) ) )
437 return refs;
438
439 collectRefsFrom( aSource, 0, aSource.length(), refs );
440 return refs;
441}
442
443
444wxString GetGeneratedFieldDisplayName( const wxString& aSource )
445{
446 std::function<bool( wxString* )> tokenExtractor = [&]( wxString* token ) -> bool
447 {
448 *token = *token; // token value is the token name
449 return true;
450 };
451
452 return ExpandTextVars( aSource, &tokenExtractor );
453}
454
455
456bool IsGeneratedField( const wxString& aSource )
457{
458 // Per-thread regex. Callers include parallel ERC/connection-graph workers.
459 thread_local wxRegEx expr( wxS( "^\\$\\{\\w*\\}$" ) );
460 return expr.Matches( aSource );
461}
462
463
464wxString DescribeRef( const wxString& aRef )
465{
466 if( aRef.IsEmpty() )
467 return wxT( "<i>" ) + _( "unannotated footprint" ) + wxT( " </i>" );
468 else
469 return EscapeHTML( aRef );
470}
471
472
473//
474// Stolen from wxExpandEnvVars and then heavily optimized
475//
476wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject, std::set<wxString>* aSet = nullptr )
477{
478 // If the same string is inserted twice, we have a loop
479 if( aSet )
480 {
481 if( auto [_, result] = aSet->insert( str ); !result )
482 return str;
483 }
484
485 size_t strlen = str.length();
486
487 wxString strResult;
488 strResult.Alloc( strlen ); // best guess (improves performance)
489
490 auto getVersionedEnvVar = []( const wxString& aMatch, wxString& aResult ) -> bool
491 {
492 for( const wxString& var : ENV_VAR::GetPredefinedEnvVars() )
493 {
494 if( var.Matches( aMatch ) )
495 {
496 const auto value = ENV_VAR::GetEnvVar<wxString>( var );
497
498 if( !value )
499 continue;
500
501 aResult += *value;
502 return true;
503 }
504 }
505
506 return false;
507 };
508
509 for( size_t n = 0; n < strlen; n++ )
510 {
511 wxUniChar str_n = str[n];
512
513 switch( str_n.GetValue() )
514 {
515#ifdef __WINDOWS__
516 case wxT( '%' ):
517#endif // __WINDOWS__
518 case wxT( '$' ):
519 {
520 Bracket bracket;
521#ifdef __WINDOWS__
522 if( str_n == wxT( '%' ) )
523 {
524 bracket = Bracket_Windows;
525 }
526 else
527#endif // __WINDOWS__
528 if( n == strlen - 1 )
529 {
530 bracket = Bracket_None;
531 }
532 else
533 {
534 switch( str[n + 1].GetValue() )
535 {
536 case wxT( '(' ):
537 bracket = Bracket_Normal;
538 str_n = str[++n]; // skip the bracket
539 break;
540
541 case wxT( '{' ):
542 bracket = Bracket_Curly;
543 str_n = str[++n]; // skip the bracket
544 break;
545
546 default: bracket = Bracket_None;
547 }
548 }
549
550 size_t m = n + 1;
551
552 if( m >= strlen )
553 break;
554
555 wxUniChar str_m = str[m];
556
557 while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
558 {
559 if( ++m == strlen )
560 {
561 str_m = 0;
562 break;
563 }
564
565 str_m = str[m];
566 }
567
568 wxString strVarName( str.c_str() + n + 1, m - n - 1 );
569
570 // NB: use wxGetEnv instead of wxGetenv as otherwise variables
571 // set through wxSetEnv may not be read correctly!
572 bool expanded = false;
573 wxString tmp = strVarName;
574
575 if( aProject && aProject->TextVarResolver( &tmp ) )
576 {
577 strResult += tmp;
578 expanded = true;
579 }
580 else if( wxGetEnv( strVarName, &tmp ) )
581 {
582 strResult += tmp;
583 expanded = true;
584 }
585 // Replace unmatched older variables with current locations
586 // If the user has the older location defined, that will be matched
587 // first above. But if they do not, this will ensure that their board still
588 // displays correctly
589 else if( strVarName.Contains( "KISYS3DMOD" ) || strVarName.Matches( "KICAD*_3DMODEL_DIR" ) )
590 {
591 if( getVersionedEnvVar( "KICAD*_3DMODEL_DIR", strResult ) )
592 expanded = true;
593 }
594 else if( strVarName.Matches( "KICAD*_SYMBOL_DIR" ) )
595 {
596 if( getVersionedEnvVar( "KICAD*_SYMBOL_DIR", strResult ) )
597 expanded = true;
598 }
599 else if( strVarName.Matches( "KICAD*_FOOTPRINT_DIR" ) )
600 {
601 if( getVersionedEnvVar( "KICAD*_FOOTPRINT_DIR", strResult ) )
602 expanded = true;
603 }
604 else if( strVarName.Matches( "KICAD*_3RD_PARTY" ) )
605 {
606 if( getVersionedEnvVar( "KICAD*_3RD_PARTY", strResult ) )
607 expanded = true;
608 }
609 else
610 {
611 // variable doesn't exist => don't change anything
612#ifdef __WINDOWS__
613 if( bracket != Bracket_Windows )
614#endif
615 if( bracket != Bracket_None )
616 strResult << str[n - 1];
617
618 strResult << str_n << strVarName;
619 }
620
621 // When a versioned-wildcard branch matched but no env var was found, emit
622 // the original ${VARNAME} text so the closing-bracket handler can append the
623 // closing bracket. Without this, the handler emits only '}', producing a
624 // garbage path like "}/Device.kicad_sym" instead of the full unexpanded var.
625 if( !expanded && bracket != Bracket_None )
626 {
627 auto isVersionedWildcard =
628 strVarName.Contains( wxT( "KISYS3DMOD" ) )
629 || strVarName.Matches( wxT( "KICAD*_3DMODEL_DIR" ) )
630 || strVarName.Matches( wxT( "KICAD*_SYMBOL_DIR" ) )
631 || strVarName.Matches( wxT( "KICAD*_FOOTPRINT_DIR" ) )
632 || strVarName.Matches( wxT( "KICAD*_3RD_PARTY" ) );
633
634 if( isVersionedWildcard )
635 {
636#ifdef __WINDOWS__
637 if( bracket != Bracket_Windows )
638#endif
639 strResult << str[n - 1];
640
641 strResult << str_n << strVarName;
642 }
643 }
644
645 // check the closing bracket
646 if( bracket != Bracket_None )
647 {
648 if( m == strlen || str_m != (wxChar) bracket )
649 {
650 // under MSW it's common to have '%' characters in the registry
651 // and it's annoying to have warnings about them each time, so
652 // ignore them silently if they are not used for env vars
653 //
654 // under Unix, OTOH, this warning could be useful for the user to
655 // understand why isn't the variable expanded as intended
656#ifndef __WINDOWS__
657 wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
658 "at position %u in '%s'." ),
659 (char) bracket, (unsigned int) ( m + 1 ), str.c_str() );
660#endif // __WINDOWS__
661 }
662 else
663 {
664 // skip closing bracket unless the variables wasn't expanded
665 if( !expanded )
666 strResult << (wxChar) bracket;
667
668 m++;
669 }
670 }
671
672 n = m - 1; // skip variable name
673 str_n = str[n];
674 }
675 break;
676
677 case wxT( '\\' ):
678 // backslash can be used to suppress special meaning of % and $
679 if( n < strlen - 1 && ( str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' ) ) )
680 {
681 str_n = str[++n];
682 strResult += str_n;
683
684 break;
685 }
686
688
689 default: strResult += str_n;
690 }
691 }
692
693 std::set<wxString> loop_check;
694 auto first_pos = strResult.find_first_of( wxS( "{(%" ) );
695 auto last_pos = strResult.find_last_of( wxS( "})%" ) );
696
697 if( first_pos != strResult.npos && last_pos != strResult.npos && first_pos != last_pos )
698 strResult = KIwxExpandEnvVars( strResult, aProject, aSet ? aSet : &loop_check );
699
700 return strResult;
701}
702
703
704const wxString ExpandEnvVarSubstitutions( const wxString& aString, const PROJECT* aProject )
705{
706 // wxGetenv( wchar_t* ) is not re-entrant on linux.
707 // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
708 static std::mutex getenv_mutex;
709
710 std::lock_guard<std::mutex> lock( getenv_mutex );
711
712 // We reserve the right to do this another way, by providing our own member function.
713 return KIwxExpandEnvVars( aString, aProject );
714}
715
716
717const wxString ResolveUriByEnvVars( const wxString& aUri, const PROJECT* aProject )
718{
719 wxString uri = ExpandTextVars( aUri, aProject );
720
721 return ExpandEnvVarSubstitutions( uri, aProject );
722}
723
724
725bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName, const wxString& aBaseFilename, REPORTER* aReporter )
726{
727 wxString msg;
728 wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
729
730 // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
731 // already an absolute path) absolute:
732 if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
733 {
734 if( aReporter )
735 {
736 msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ), aTargetFullFileName->GetPath(),
737 baseFilePath );
738 aReporter->Report( msg, RPT_SEVERITY_ERROR );
739 }
740
741 return false;
742 }
743
744 // Ensure the path of aTargetFullFileName exists, and create it if needed:
745 wxString outputPath( aTargetFullFileName->GetPath() );
746
747 if( !wxFileName::DirExists( outputPath ) )
748 {
749 // Make every directory provided when the provided path doesn't exist
750 if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
751 {
752 if( aReporter )
753 {
754 msg.Printf( _( "Output directory '%s' created." ), outputPath );
755 aReporter->Report( msg, RPT_SEVERITY_INFO );
756 return true;
757 }
758 }
759 else
760 {
761 if( aReporter )
762 {
763 msg.Printf( _( "Cannot create output directory '%s'." ), outputPath );
764 aReporter->Report( msg, RPT_SEVERITY_ERROR );
765 }
766
767 return false;
768 }
769 }
770
771 return true;
772}
773
774
775wxString EnsureFileExtension( const wxString& aFilename, const wxString& aExtension )
776{
777 wxString newFilename( aFilename );
778
779 // It's annoying to throw up nag dialogs when the extension isn't right. Just fix it,
780 // but be careful not to destroy existing after-dot-text that isn't actually a bad
781 // extension, such as "Schematic_1.1".
782 if( newFilename.Lower().AfterLast( '.' ) != aExtension )
783 {
784 if( !newFilename.EndsWith( '.' ) )
785 newFilename.Append( '.' );
786
787 newFilename.Append( aExtension );
788 }
789
790 return newFilename;
791}
792
793
794wxString JoinExtensions( const std::vector<std::string>& aExts )
795{
796 wxString joined;
797
798 for( const std::string& ext : aExts )
799 {
800 if( !joined.empty() )
801 joined << wxS( ", " );
802
803 joined << wxS( "*." ) << ext;
804 }
805
806 return joined;
807}
808
809
817
824bool matchWild( const char* pat, const char* text, bool dot_special )
825{
826 if( !*text )
827 {
828 /* Match if both are empty. */
829 return !*pat;
830 }
831
832 const char *m = pat, *n = text, *ma = nullptr, *na = nullptr;
833 int just = 0, acount = 0, count = 0;
834
835 if( dot_special && ( *n == '.' ) )
836 {
837 /* Never match so that hidden Unix files
838 * are never found. */
839 return false;
840 }
841
842 for( ;; )
843 {
844 if( *m == '*' )
845 {
846 ma = ++m;
847 na = n;
848 just = 1;
849 acount = count;
850 }
851 else if( *m == '?' )
852 {
853 m++;
854
855 if( !*n++ )
856 return false;
857 }
858 else
859 {
860 if( *m == '\\' )
861 {
862 m++;
863
864 /* Quoting "nothing" is a bad thing */
865 if( !*m )
866 return false;
867 }
868
869 if( !*m )
870 {
871 /*
872 * If we are out of both strings or we just
873 * saw a wildcard, then we can say we have a
874 * match
875 */
876 if( !*n )
877 return true;
878
879 if( just )
880 return true;
881
882 just = 0;
883 goto not_matched;
884 }
885
886 /*
887 * We could check for *n == NULL at this point, but
888 * since it's more common to have a character there,
889 * check to see if they match first (m and n) and
890 * then if they don't match, THEN we can check for
891 * the NULL of n
892 */
893 just = 0;
894
895 if( *m == *n )
896 {
897 m++;
898 count++;
899 n++;
900 }
901 else
902 {
903 not_matched:
904
905 /*
906 * If there are no more characters in the
907 * string, but we still need to find another
908 * character (*m != NULL), then it will be
909 * impossible to match it
910 */
911 if( !*n )
912 return false;
913
914 if( ma )
915 {
916 m = ma;
917 n = ++na;
918 count = acount;
919 }
920 else
921 return false;
922 }
923 }
924 }
925}
926
927
929{
931 return false;
932
933 KICAD_MESSAGE_DIALOG dialog( nullptr,
934 _( "This operating system is not supported "
935 "by KiCad and its dependencies." ),
936 _( "Unsupported Operating System" ), wxOK | wxICON_EXCLAMATION );
937
938 dialog.SetExtendedMessage( _( "Any issues with KiCad on this system cannot "
939 "be reported to the official bugtracker." ) );
940 dialog.ShowModal();
941
942 return true;
943}
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:62
virtual bool TextVarResolver(wxString *aToken) const
Definition project.cpp:81
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:100
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:704
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:794
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition common.cpp:444
const wxString ResolveUriByEnvVars(const wxString &aUri, const PROJECT *aProject)
Replace any environment and/or text variables in URIs.
Definition common.cpp:717
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:775
bool WarnUserIfOperatingSystemUnsupported()
Checks if the operating system is explicitly unsupported and displays a disclaimer message box.
Definition common.cpp:928
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:59
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition common.cpp:824
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:725
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject, std::set< wxString > *aSet=nullptr)
Definition common.cpp:476
Bracket
Definition common.cpp:49
@ Bracket_Max
Definition common.cpp:56
@ Bracket_None
Definition common.cpp:50
@ Bracket_Normal
Definition common.cpp:51
@ Bracket_Curly
Definition common.cpp:52
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:456
wxString DescribeRef(const wxString &aRef)
Returns a user-visible HTML string describing a footprint reference designator.
Definition common.cpp:464
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:296
std::vector< TEXT_VAR_REF_KEY > ExtractTextVarReferences(const wxString &aSource)
Lex-scan aSource and return every ${...} reference that appears, without resolving.
Definition common.cpp:431
The common library.
#define FOR_ERC_DRC
Expand '${var-name}' templates in text.
Definition common.h:95
This file is part of the common library.
#define KICAD_MESSAGE_DIALOG
Definition confirm.h:48
#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:79
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:70
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
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.