KiCad PCB EDA Suite
Loading...
Searching...
No Matches
filename_resolver.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) 2015-2020 Cirilo Bernardo <[email protected]>
5 * Copyright (C) 2015-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <fstream>
26#include <mutex>
27#include <sstream>
28
29#include <wx/log.h>
30#include <pgm_base.h>
31#include <trace_helpers.h>
32
33#include <common.h>
34#include <filename_resolver.h>
35#include <confirm.h>
36#include <wx_filename.h>
37
38// configuration file version
39#define CFGFILE_VERSION 1
40
41// flag bits used to track different one-off messages to users
42#define ERRFLG_ALIAS (1)
43#define ERRFLG_RELPATH (2)
44#define ERRFLG_ENVPATH (4)
45
46#define MASK_3D_RESOLVER "3D_RESOLVER"
47
48static std::mutex mutex_resolver;
49
50
52 m_pgm( nullptr ),
53 m_project( nullptr )
54{
55 m_errflags = 0;
56}
57
58
59bool FILENAME_RESOLVER::Set3DConfigDir( const wxString& aConfigDir )
60{
61 if( aConfigDir.empty() )
62 return false;
63
64 wxFileName cfgdir( ExpandEnvVarSubstitutions( aConfigDir, m_project ), "" );
65
66 cfgdir.Normalize( FN_NORMALIZE_FLAGS );
67
68 if( !cfgdir.DirExists() )
69 return false;
70
71 m_configDir = cfgdir.GetPath();
73
74 return true;
75}
76
77
78bool FILENAME_RESOLVER::SetProject( PROJECT* aProject, bool* flgChanged )
79{
80 m_project = aProject;
81
82 if( !aProject )
83 return false;
84
85 wxFileName projdir( ExpandEnvVarSubstitutions( aProject->GetProjectPath(), aProject ), "" );
86
87 projdir.Normalize( FN_NORMALIZE_FLAGS );
88
89 if( !projdir.DirExists() )
90 return false;
91
92 m_curProjDir = projdir.GetPath();
93
94 if( flgChanged )
95 *flgChanged = false;
96
97 if( m_paths.empty() )
98 {
99 SEARCH_PATH al;
100 al.m_Alias = wxS( "${KIPRJMOD}" );
101 al.m_Pathvar = wxS( "${KIPRJMOD}" );
103 m_paths.push_back( al );
104
105 if( flgChanged )
106 *flgChanged = true;
107 }
108 else
109 {
110 if( m_paths.front().m_Pathexp != m_curProjDir )
111 {
112 m_paths.front().m_Pathexp = m_curProjDir;
113
114 if( flgChanged )
115 *flgChanged = true;
116 }
117 else
118 {
119 return true;
120 }
121 }
122
123#ifdef DEBUG
124 {
125 std::ostringstream ostr;
126 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
127 ostr << " * [INFO] changed project dir to ";
128 ostr << m_paths.front().m_Pathexp.ToUTF8();
129 wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
130 }
131#endif
132
133 return true;
134}
135
136
138{
139 return m_curProjDir;
140}
141
142
144{
145 m_pgm = aBase;
146
147 if( !m_pgm || m_paths.empty() )
148 return;
149
150 // recreate the path list
151 m_paths.clear();
153}
154
155
157{
158 if( !m_paths.empty() )
159 return true;
160
161 // add an entry for the default search path; at this point
162 // we cannot set a sensible default so we use an empty string.
163 // the user may change this later with a call to SetProjectDir()
164
165 SEARCH_PATH lpath;
166 lpath.m_Alias = wxS( "${KIPRJMOD}" );
167 lpath.m_Pathvar = wxS( "${KIPRJMOD}" );
168 lpath.m_Pathexp = m_curProjDir;
169 m_paths.push_back( lpath );
170 wxFileName fndummy;
171 wxUniChar psep = fndummy.GetPathSeparator();
172 std::list< wxString > epaths;
173
174 if( GetKicadPaths( epaths ) )
175 {
176 for( const wxString& currPath : epaths )
177 {
178 wxString currPathVarFormat = currPath;
179 currPathVarFormat.Prepend( wxS( "${" ) );
180 currPathVarFormat.Append( wxS( "}" ) );
181
182 wxString pathVal = ExpandEnvVarSubstitutions( currPathVarFormat, m_project );
183
184 if( pathVal.empty() )
185 {
186 lpath.m_Pathexp.clear();
187 }
188 else
189 {
190 fndummy.Assign( pathVal, "" );
191 fndummy.Normalize( FN_NORMALIZE_FLAGS );
192 lpath.m_Pathexp = fndummy.GetFullPath();
193 }
194
195 lpath.m_Alias = currPath;
196 lpath.m_Pathvar = currPath;
197
198 if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
199 lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
200
201 // we add it first with the alias set to the non-variable format
202 m_paths.push_back( lpath );
203
204 // now add it with the "new variable format ${VAR}"
205 lpath.m_Alias = currPathVarFormat;
206 m_paths.push_back( lpath );
207 }
208 }
209
210 if( m_paths.empty() )
211 return false;
212
213#ifdef DEBUG
214 wxLogTrace( MASK_3D_RESOLVER, wxS( " * [3D model] search paths:\n" ) );
215 std::list< SEARCH_PATH >::const_iterator sPL = m_paths.begin();
216
217 while( sPL != m_paths.end() )
218 {
219 wxLogTrace( MASK_3D_RESOLVER, wxS( " + %s : '%s'\n" ), (*sPL).m_Alias.GetData(),
220 (*sPL).m_Pathexp.GetData() );
221 ++sPL;
222 }
223#endif
224
225 return true;
226}
227
228
229bool FILENAME_RESOLVER::UpdatePathList( const std::vector< SEARCH_PATH >& aPathList )
230{
231 wxUniChar envMarker( '$' );
232
233 while( !m_paths.empty() && envMarker != *m_paths.back().m_Alias.rbegin() )
234 m_paths.pop_back();
235
236 for( const SEARCH_PATH& path : aPathList )
237 addPath( path );
238
239 return true;
240}
241
242
243wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxString& aWorkingPath )
244{
245 std::lock_guard<std::mutex> lock( mutex_resolver );
246
247 if( aFileName.empty() )
248 return wxEmptyString;
249
250 if( m_paths.empty() )
252
253 // first attempt to use the name as specified:
254 wxString tname = aFileName;
255
256 // Note: variable expansion must preferably be performed via a threadsafe wrapper for the
257 // getenv() system call. If we allow the wxFileName::Normalize() routine to perform expansion
258 // then we will have a race condition since wxWidgets does not assure a threadsafe wrapper
259 // for getenv().
260 tname = ExpandEnvVarSubstitutions( tname, m_project );
261
262 wxFileName tmpFN( tname );
263
264 // this case covers full paths, leading expanded vars, and paths relative to the current
265 // working directory (which is not necessarily the current project directory)
266 if( tmpFN.FileExists() )
267 {
268 tmpFN.Normalize( FN_NORMALIZE_FLAGS );
269 tname = tmpFN.GetFullPath();
270
271 // special case: if a path begins with ${ENV_VAR} but is not in the resolver's path list
272 // then add it.
273 if( aFileName.StartsWith( wxS( "${" ) ) || aFileName.StartsWith( wxS( "$(" ) ) )
274 checkEnvVarPath( aFileName );
275
276 return tname;
277 }
278
279 // if a path begins with ${ENV_VAR}/$(ENV_VAR) and is not resolved then the file either does
280 // not exist or the ENV_VAR is not defined
281 if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
282 {
283 if( !( m_errflags & ERRFLG_ENVPATH ) )
284 {
286 wxString errmsg = "[3D File Resolver] No such path; ensure the environment var is defined";
287 errmsg.append( "\n" );
288 errmsg.append( tname );
289 errmsg.append( "\n" );
290 wxLogTrace( tracePathsAndFiles, errmsg );
291 }
292
293 return wxEmptyString;
294 }
295
296 // at this point aFileName is:
297 // a. an aliased shortened name or
298 // b. cannot be determined
299
300 // check the path relative to the current project directory;
301 // NB: this is not necessarily the same as the current working directory, which has already
302 // been checked. This case accounts for partial paths which do not contain ${KIPRJMOD}.
303 // This check is performed before checking the path relative to ${KICAD7_3DMODEL_DIR} so that
304 // users can potentially override a model within ${KICAD7_3DMODEL_DIR}.
305 if( !m_paths.begin()->m_Pathexp.empty() && !tname.StartsWith( ":" ) )
306 {
307 tmpFN.Assign( m_paths.begin()->m_Pathexp, "" );
308 wxString fullPath = tmpFN.GetPathWithSep() + tname;
309
310 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
311
312 if( wxFileName::FileExists( fullPath ) )
313 {
314 tmpFN.Assign( fullPath );
315 tmpFN.Normalize( FN_NORMALIZE_FLAGS );
316 tname = tmpFN.GetFullPath();
317 return tname;
318 }
319
320 }
321
322 // check path relative to search path
323 if( !aWorkingPath.IsEmpty() && !tname.StartsWith( ":" ) )
324 {
325 wxString tmp = aWorkingPath;
326 tmp.Append( tmpFN.GetPathSeparator() );
327 tmp.Append( tname );
328 tmpFN.Assign( tmp );
329
330 if( tmpFN.MakeAbsolute() && tmpFN.FileExists() )
331 {
332 tname = tmpFN.GetFullPath();
333 return tname;
334 }
335 }
336
337 // check the partial path relative to ${KICAD7_3DMODEL_DIR} (legacy behavior)
338 if( !tname.StartsWith( wxS( ":" ) ) )
339 {
340 wxFileName fpath;
341 wxString fullPath( wxS( "${KICAD7_3DMODEL_DIR}" ) );
342 fullPath.Append( fpath.GetPathSeparator() );
343 fullPath.Append( tname );
344 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
345 fpath.Assign( fullPath );
346
347 if( fpath.Normalize( FN_NORMALIZE_FLAGS ) && fpath.FileExists() )
348 {
349 tname = fpath.GetFullPath();
350 return tname;
351 }
352
353 }
354
355 // at this point the filename must contain an alias or else it is invalid
356 wxString alias; // the alias portion of the short filename
357 wxString relpath; // the path relative to the alias
358
359 if( !SplitAlias( tname, alias, relpath ) )
360 {
361 if( !( m_errflags & ERRFLG_RELPATH ) )
362 {
363 // this can happen if the file was intended to be relative to ${KICAD7_3DMODEL_DIR}
364 // but ${KICAD7_3DMODEL_DIR} is not set or is incorrect.
366 wxString errmsg = "[3D File Resolver] No such path";
367 errmsg.append( wxS( "\n" ) );
368 errmsg.append( tname );
369 errmsg.append( wxS( "\n" ) );
370 wxLogTrace( tracePathsAndFiles, errmsg );
371 }
372
373 return wxEmptyString;
374 }
375
376 for( const SEARCH_PATH& path : m_paths )
377 {
378 // ${ENV_VAR} paths have already been checked; skip them
379 if( path.m_Alias.StartsWith( wxS( "${" ) ) || path.m_Alias.StartsWith( wxS( "$(" ) ) )
380 continue;
381
382 if( path.m_Alias == alias && !path.m_Pathexp.empty() )
383 {
384 wxFileName fpath( wxFileName::DirName( path.m_Pathexp ) );
385 wxString fullPath = fpath.GetPathWithSep() + relpath;
386
387 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
388
389 if( wxFileName::FileExists( fullPath ) )
390 {
391 tname = fullPath;
392
393 wxFileName tmp( fullPath );
394
395 if( tmp.Normalize( FN_NORMALIZE_FLAGS ) )
396 tname = tmp.GetFullPath();
397
398 return tname;
399 }
400 }
401 }
402
403 if( !( m_errflags & ERRFLG_ALIAS ) )
404 {
406 wxString errmsg = "[3D File Resolver] No such path; ensure the path alias is defined";
407 errmsg.append( "\n" );
408 errmsg.append( tname.substr( 1 ) );
409 errmsg.append( "\n" );
410 wxLogTrace( tracePathsAndFiles, errmsg );
411 }
412
413 return wxEmptyString;
414}
415
416
418{
419 if( aPath.m_Alias.empty() || aPath.m_Pathvar.empty() )
420 return false;
421
422 std::lock_guard<std::mutex> lock( mutex_resolver );
423
424 SEARCH_PATH tpath = aPath;
425
426 #ifdef _WIN32
427 while( tpath.m_Pathvar.EndsWith( wxT( "\\" ) ) )
428 tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
429 #else
430 while( tpath.m_Pathvar.EndsWith( wxT( "/" ) ) && tpath.m_Pathvar.length() > 1 )
431 tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
432 #endif
433
434 wxFileName path( ExpandEnvVarSubstitutions( tpath.m_Pathvar, m_project ), "" );
435
436 path.Normalize( FN_NORMALIZE_FLAGS );
437
438 if( !path.DirExists() )
439 {
440 if( aPath.m_Pathvar == wxS( "${KICAD7_3DMODEL_DIR}" )
441 || aPath.m_Pathvar == wxS( "${KIPRJMOD}" ) || aPath.m_Pathvar == wxS( "$(KIPRJMOD)" )
442 || aPath.m_Pathvar == wxS( "${KISYS3DMOD}" ) || aPath.m_Pathvar == wxS( "$(KISYS3DMOD)" ) )
443 {
444 // suppress the message if the missing pathvar is a system variable
445 }
446 else
447 {
448 wxString msg = _( "The given path does not exist" );
449 msg.append( wxT( "\n" ) );
450 msg.append( tpath.m_Pathvar );
451 DisplayErrorMessage( nullptr, msg );
452 }
453
454 tpath.m_Pathexp.clear();
455 }
456 else
457 {
458 tpath.m_Pathexp = path.GetFullPath();
459
460#ifdef _WIN32
461 while( tpath.m_Pathexp.EndsWith( wxT( "\\" ) ) )
462 tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
463#else
464 while( tpath.m_Pathexp.EndsWith( wxT( "/" ) ) && tpath.m_Pathexp.length() > 1 )
465 tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
466#endif
467 }
468
469 std::list< SEARCH_PATH >::iterator sPL = m_paths.begin();
470 std::list< SEARCH_PATH >::iterator ePL = m_paths.end();
471
472 while( sPL != ePL )
473 {
474 if( tpath.m_Alias == sPL->m_Alias )
475 {
476 wxString msg = _( "Alias: " );
477 msg.append( tpath.m_Alias );
478 msg.append( wxT( "\n" ) );
479 msg.append( _( "This path:" ) + wxS( " " ) );
480 msg.append( tpath.m_Pathvar );
481 msg.append( wxT( "\n" ) );
482 msg.append( _( "Existing path:" ) + wxS( " " ) );
483 msg.append( sPL->m_Pathvar );
484 DisplayErrorMessage( nullptr, _( "Bad alias (duplicate name)" ), msg );
485 return false;
486 }
487
488 ++sPL;
489 }
490
491 m_paths.push_back( tpath );
492 return true;
493}
494
495
496void FILENAME_RESOLVER::checkEnvVarPath( const wxString& aPath )
497{
498 bool useParen = false;
499
500 if( aPath.StartsWith( wxS( "$(" ) ) )
501 useParen = true;
502 else if( !aPath.StartsWith( wxS( "${" ) ) )
503 return;
504
505 size_t pEnd;
506
507 if( useParen )
508 pEnd = aPath.find( wxS( ")" ) );
509 else
510 pEnd = aPath.find( wxS( "}" ) );
511
512 if( pEnd == wxString::npos )
513 return;
514
515 wxString envar = aPath.substr( 0, pEnd + 1 );
516
517 // check if the alias exists; if not then add it to the end of the
518 // env var section of the path list
519 auto sPL = m_paths.begin();
520 auto ePL = m_paths.end();
521
522 while( sPL != ePL )
523 {
524 if( sPL->m_Alias == envar )
525 return;
526
527 if( !sPL->m_Alias.StartsWith( wxS( "${" ) ) )
528 break;
529
530 ++sPL;
531 }
532
533 SEARCH_PATH lpath;
534 lpath.m_Alias = envar;
535 lpath.m_Pathvar = lpath.m_Alias;
536 wxFileName tmpFN( ExpandEnvVarSubstitutions( lpath.m_Alias, m_project ), "" );
537
538 wxUniChar psep = tmpFN.GetPathSeparator();
539 tmpFN.Normalize( FN_NORMALIZE_FLAGS );
540
541 if( !tmpFN.DirExists() )
542 return;
543
544 lpath.m_Pathexp = tmpFN.GetFullPath();
545
546 if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
547 lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
548
549 if( lpath.m_Pathexp.empty() )
550 return;
551
552 m_paths.insert( sPL, lpath );
553}
554
555
556wxString FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
557{
558 wxString fname = aFullPathName;
559
560 if( m_paths.empty() )
562
563 std::lock_guard<std::mutex> lock( mutex_resolver );
564
565 std::list< SEARCH_PATH >::const_iterator sL = m_paths.begin();
566 size_t idx;
567
568 while( sL != m_paths.end() )
569 {
570 // undefined paths do not participate in the
571 // file name shortening procedure
572 if( sL->m_Pathexp.empty() )
573 {
574 ++sL;
575 continue;
576 }
577
578 wxFileName fpath;
579
580 // in the case of aliases, ensure that we use the most recent definition
581 if( sL->m_Alias.StartsWith( wxS( "${" ) ) || sL->m_Alias.StartsWith( wxS( "$(" ) ) )
582 {
583 wxString tpath = ExpandEnvVarSubstitutions( sL->m_Alias, m_project );
584
585 if( tpath.empty() )
586 {
587 ++sL;
588 continue;
589 }
590
591 fpath.Assign( tpath, wxT( "" ) );
592 }
593 else
594 {
595 fpath.Assign( sL->m_Pathexp, wxT( "" ) );
596 }
597
598 wxString fps = fpath.GetPathWithSep();
599 wxString tname;
600
601 idx = fname.find( fps );
602
603 if( idx == 0 )
604 {
605 fname = fname.substr( fps.size() );
606
607 #ifdef _WIN32
608 // ensure only the '/' separator is used in the internal name
609 fname.Replace( wxT( "\\" ), wxT( "/" ) );
610 #endif
611
612 if( sL->m_Alias.StartsWith( wxS( "${" ) ) || sL->m_Alias.StartsWith( wxS( "$(" ) ) )
613 {
614 // old style ENV_VAR
615 tname = sL->m_Alias;
616 tname.Append( wxS( "/" ) );
617 tname.append( fname );
618 }
619 else
620 {
621 // new style alias
622 tname = "${";
623 tname.append( sL->m_Alias );
624 tname.append( wxS( "}/" ) );
625 tname.append( fname );
626 }
627
628 return tname;
629 }
630
631 ++sL;
632 }
633
634#ifdef _WIN32
635 // it is strange to convert an MSWin full path to use the
636 // UNIX separator but this is done for consistency and can
637 // be helpful even when transferring project files from
638 // MSWin to *NIX.
639 fname.Replace( wxT( "\\" ), wxT( "/" ) );
640#endif
641
642 return fname;
643}
644
645
646
647const std::list< SEARCH_PATH >* FILENAME_RESOLVER::GetPaths() const
648{
649 return &m_paths;
650}
651
652
653bool FILENAME_RESOLVER::SplitAlias( const wxString& aFileName,
654 wxString& anAlias, wxString& aRelPath ) const
655{
656 anAlias.clear();
657 aRelPath.clear();
658
659 size_t searchStart = 0;
660
661 if( aFileName.StartsWith( wxT( ":" ) ) )
662 searchStart = 1;
663
664 size_t tagpos = aFileName.find( wxT( ":" ), searchStart );
665
666 if( tagpos == wxString::npos || tagpos == searchStart )
667 return false;
668
669 if( tagpos + 1 >= aFileName.length() )
670 return false;
671
672 anAlias = aFileName.substr( searchStart, tagpos - searchStart );
673 aRelPath = aFileName.substr( tagpos + 1 );
674
675 return true;
676}
677
678
679bool FILENAME_RESOLVER::ValidateFileName( const wxString& aFileName, bool& hasAlias ) const
680{
681 // Rules:
682 // 1. The generic form of an aliased 3D relative path is:
683 // ALIAS:relative/path
684 // 2. ALIAS is a UTF string excluding wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" )
685 // 3. The relative path must be a valid relative path for the platform
686 hasAlias = false;
687
688 if( aFileName.empty() )
689 return false;
690
691 wxString filename = aFileName;
692 wxString lpath;
693 size_t aliasStart = aFileName.StartsWith( ':' ) ? 1 : 0;
694 size_t aliasEnd = aFileName.find( ':', aliasStart );
695
696 // ensure that the file separators suit the current platform
697#ifdef __WINDOWS__
698 filename.Replace( wxT( "/" ), wxT( "\\" ) );
699
700 // if we see the :\ pattern then it must be a drive designator
701 if( aliasEnd != wxString::npos )
702 {
703 size_t pos1 = filename.find( wxT( ":\\" ) );
704
705 if( pos1 != wxString::npos && ( pos1 != aliasEnd || pos1 != 1 ) )
706 return false;
707
708 // if we have a drive designator then we have no alias
709 if( pos1 != wxString::npos )
710 aliasEnd = wxString::npos;
711 }
712#else
713 filename.Replace( wxT( "\\" ), wxT( "/" ) );
714#endif
715
716 // names may not end with ':'
717 if( aliasEnd == aFileName.length() -1 )
718 return false;
719
720 if( aliasEnd != wxString::npos )
721 {
722 // ensure the alias component is not empty
723 if( aliasEnd == aliasStart )
724 return false;
725
726 lpath = filename.substr( aliasStart, aliasEnd );
727
728 // check the alias for restricted characters
729 if( wxString::npos != lpath.find_first_of( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
730 return false;
731
732 hasAlias = true;
733 lpath = aFileName.substr( aliasEnd + 1 );
734 }
735 else
736 {
737 lpath = aFileName;
738
739 // in the case of ${ENV_VAR}|$(ENV_VAR)/path, strip the
740 // environment string before testing
741 aliasEnd = wxString::npos;
742
743 if( aFileName.StartsWith( wxS( "${" ) ) )
744 aliasEnd = aFileName.find( '}' );
745 else if( aFileName.StartsWith( wxS( "$(" ) ) )
746 aliasEnd = aFileName.find( ')' );
747
748 if( aliasEnd != wxString::npos )
749 lpath = aFileName.substr( aliasEnd + 1 );
750
751 }
752
753 // Test for forbidden chars in filenames. Should be wxFileName::GetForbiddenChars()
754 // On MSW, the list returned by wxFileName::GetForbiddenChars() contains separators
755 // '\'and '/' used here because lpath can be a full path.
756 // So remove separators
757 wxString lpath_no_sep = lpath;
758#ifdef __WINDOWS__
759 lpath_no_sep.Replace( "/", " " );
760 lpath_no_sep.Replace( "\\", " " );
761
762 // A disk identifier is allowed, and therefore remove its separator
763 if( lpath_no_sep.Length() > 1 && lpath_no_sep[1] == ':' )
764 lpath_no_sep[1] = ' ';
765#endif
766
767 if( wxString::npos != lpath_no_sep.find_first_of( wxFileName::GetForbiddenChars() ) )
768 return false;
769
770 return true;
771}
772
773
774bool FILENAME_RESOLVER::GetKicadPaths( std::list< wxString >& paths ) const
775{
776 paths.clear();
777
778 if( !m_pgm )
779 return false;
780
781 bool hasKisys3D = false;
782
783
784 // iterate over the list of internally defined ENV VARs
785 // and add them to the paths list
788
789 while( mS != mE )
790 {
791 // filter out URLs, template directories, and known system paths
792 if( mS->first == wxS( "KICAD_PTEMPLATES" )
793 || mS->first.Matches( wxS( "KICAD*_FOOTPRINT_DIR") ) )
794 {
795 ++mS;
796 continue;
797 }
798
799 if( wxString::npos != mS->second.GetValue().find( wxS( "://" ) ) )
800 {
801 ++mS;
802 continue;
803 }
804
805 //also add the path without the ${} to act as legacy alias support for older files
806 paths.push_back( mS->first );
807
808 if( mS->first.Matches( wxS("KICAD*_3DMODEL_DIR") ) )
809 hasKisys3D = true;
810
811 ++mS;
812 }
813
814 if( !hasKisys3D )
815 paths.emplace_back( wxS("KICAD7_3DMODEL_DIR") );
816
817 return true;
818}
bool ValidateFileName(const wxString &aFileName, bool &hasAlias) const
Returns true if the given path is a valid aliased relative path.
bool createPathList(void)
Build the path list using available information such as KICAD7_3DMODEL_DIR and the 3d_path_list confi...
bool addPath(const SEARCH_PATH &aPath)
Check that a path is valid and adds it to the search list.
wxString GetProjectDir() const
bool GetKicadPaths(std::list< wxString > &paths) const
Return a list of path environment variables local to KiCad.
bool Set3DConfigDir(const wxString &aConfigDir)
Set the user's configuration directory for 3D models.
void checkEnvVarPath(const wxString &aPath)
Check the ${ENV_VAR} component of a path and adds it to the resolver's path list if it is not yet in ...
std::list< SEARCH_PATH > m_paths
bool SetProject(PROJECT *aProject, bool *flgChanged=nullptr)
Set the current KiCad project directory as the first entry in the model path list.
void SetProgramBase(PGM_BASE *aBase)
Set a pointer to the application's PGM_BASE instance used to extract the local env vars.
bool SplitAlias(const wxString &aFileName, wxString &anAlias, wxString &aRelPath) const
Return true if the given name contains an alias and populates the string anAlias with the alias and a...
const std::list< SEARCH_PATH > * GetPaths() const
Return a pointer to the internal path list; the items in:load.
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath)
Determines the full path of the given file name.
bool UpdatePathList(const std::vector< SEARCH_PATH > &aPathList)
Clear the current path list and substitutes the given path list and update the path configuration fil...
wxString ShortenPath(const wxString &aFullPathName)
Produce a relative path based on the existing search directories or returns the same path if the path...
Container for data for KiCad programs.
Definition: pgm_base.h:99
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:871
Container for project specific data.
Definition: project.h:62
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:143
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:317
The common library.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:305
This file is part of the common library.
#define _(s)
#define ERRFLG_RELPATH
static std::mutex mutex_resolver
#define ERRFLG_ENVPATH
#define MASK_3D_RESOLVER
#define ERRFLG_ALIAS
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
std::map< wxString, ENV_VAR_ITEM >::const_iterator ENV_VAR_MAP_CITER
see class PGM_BASE
wxString m_Pathvar
wxString m_Pathexp
wxString m_Alias
wxLogTrace helper definitions.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition: wx_filename.h:39