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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <fstream>
22#include <mutex>
23#include <sstream>
24
25#include <wx/log.h>
26#include <wx/uri.h>
27#include <pgm_base.h>
28#include <trace_helpers.h>
29
30#include <common.h>
31#include <embedded_files.h>
32#include <env_vars.h>
33#include <filename_resolver.h>
34#include <confirm.h>
35#include <wx_filename.h>
36
37// configuration file version
38#define CFGFILE_VERSION 1
39
40// flag bits used to track different one-off messages to users
41#define ERRFLG_ALIAS (1)
42#define ERRFLG_RELPATH (2)
43#define ERRFLG_ENVPATH (4)
44
45#define MASK_3D_RESOLVER "3D_RESOLVER"
46
47static std::mutex mutex_resolver;
48
49
51 m_pgm( nullptr ),
52 m_project( nullptr )
53{
54 m_errflags = 0;
55}
56
57
58bool FILENAME_RESOLVER::Set3DConfigDir( const wxString& aConfigDir )
59{
60 if( aConfigDir.empty() )
61 return false;
62
63 wxFileName cfgdir( ExpandEnvVarSubstitutions( aConfigDir, m_project ), "" );
64
65 cfgdir.Normalize( FN_NORMALIZE_FLAGS );
66
67 if( !cfgdir.DirExists() )
68 return false;
69
70 m_configDir = cfgdir.GetPath();
72
73 return true;
74}
75
76
77bool FILENAME_RESOLVER::SetProject( const PROJECT* aProject, bool* flgChanged )
78{
79 m_project = aProject;
80
81 if( !aProject )
82 return false;
83
84 wxFileName projdir( ExpandEnvVarSubstitutions( aProject->GetProjectPath(), aProject ), "" );
85
86 projdir.Normalize( FN_NORMALIZE_FLAGS );
87
88 if( !projdir.DirExists() )
89 return false;
90
91 m_curProjDir = projdir.GetPath();
92
93 if( flgChanged )
94 *flgChanged = false;
95
96 if( m_paths.empty() )
97 {
98 SEARCH_PATH al;
99 al.m_Alias = wxS( "${KIPRJMOD}" );
100 al.m_Pathvar = wxS( "${KIPRJMOD}" );
102 m_paths.push_back( al );
103
104 if( flgChanged )
105 *flgChanged = true;
106 }
107 else
108 {
109 if( m_paths.front().m_Pathexp != m_curProjDir )
110 {
111 m_paths.front().m_Pathexp = m_curProjDir;
112
113 if( flgChanged )
114 *flgChanged = true;
115 }
116 else
117 {
118 return true;
119 }
120 }
121
122#ifdef DEBUG
123 {
124 std::ostringstream ostr;
125 ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
126 ostr << " * [INFO] changed project dir to ";
127 ostr << m_paths.front().m_Pathexp.ToUTF8();
128 wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
129 }
130#endif
131
132 return true;
133}
134
135
137{
138 return m_curProjDir;
139}
140
141
143{
144 m_pgm = aBase;
145
146 if( !m_pgm || m_paths.empty() )
147 return;
148
149 // recreate the path list
150 m_paths.clear();
152}
153
154
156{
157 if( !m_paths.empty() )
158 return true;
159
160 // add an entry for the default search path; at this point
161 // we cannot set a sensible default so we use an empty string.
162 // the user may change this later with a call to SetProjectDir()
163
164 SEARCH_PATH lpath;
165 lpath.m_Alias = wxS( "${KIPRJMOD}" );
166 lpath.m_Pathvar = wxS( "${KIPRJMOD}" );
167 lpath.m_Pathexp = m_curProjDir;
168 m_paths.push_back( lpath );
169 wxFileName fndummy;
170 wxUniChar psep = fndummy.GetPathSeparator();
171 std::list< wxString > epaths;
172
173 if( GetKicadPaths( epaths ) )
174 {
175 for( const wxString& currPath : epaths )
176 {
177 wxString currPathVarFormat = currPath;
178 currPathVarFormat.Prepend( wxS( "${" ) );
179 currPathVarFormat.Append( wxS( "}" ) );
180
181 wxString pathVal = ExpandEnvVarSubstitutions( currPathVarFormat, m_project );
182
183 if( pathVal.empty() )
184 {
185 lpath.m_Pathexp.clear();
186 }
187 else
188 {
189 fndummy.Assign( pathVal, "" );
190 fndummy.Normalize( FN_NORMALIZE_FLAGS );
191 lpath.m_Pathexp = fndummy.GetFullPath();
192 }
193
194 lpath.m_Alias = currPath;
195 lpath.m_Pathvar = currPath;
196
197 if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
198 lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
199
200 // we add it first with the alias set to the non-variable format
201 m_paths.push_back( lpath );
202
203 // now add it with the "new variable format ${VAR}"
204 lpath.m_Alias = currPathVarFormat;
205 m_paths.push_back( lpath );
206 }
207 }
208
209 if( m_paths.empty() )
210 return false;
211
212#ifdef DEBUG
213 wxLogTrace( MASK_3D_RESOLVER, wxS( " * [3D model] search paths:\n" ) );
214 std::list< SEARCH_PATH >::const_iterator sPL = m_paths.begin();
215
216 while( sPL != m_paths.end() )
217 {
218 wxLogTrace( MASK_3D_RESOLVER, wxS( " + %s : '%s'\n" ), (*sPL).m_Alias.GetData(),
219 (*sPL).m_Pathexp.GetData() );
220 ++sPL;
221 }
222#endif
223
224 return true;
225}
226
227
228bool FILENAME_RESOLVER::UpdatePathList( const std::vector< SEARCH_PATH >& aPathList )
229{
230 wxUniChar envMarker( '$' );
231
232 while( !m_paths.empty() && envMarker != *m_paths.back().m_Alias.rbegin() )
233 m_paths.pop_back();
234
235 for( const SEARCH_PATH& path : aPathList )
236 addPath( path );
237
238 return true;
239}
240
241
242wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxString& aWorkingPath,
243 std::vector<const EMBEDDED_FILES*> aEmbeddedFilesStack )
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 // Swap separators for non-Windows, in case a Windows path is being passed
257#ifndef __WIN32__
258 tname.Replace( "\\", "/" );
259#endif
260
261 // Note: variable expansion must preferably be performed via a threadsafe wrapper for the
262 // getenv() system call. If we allow the wxFileName::Normalize() routine to perform expansion
263 // then we will have a race condition since wxWidgets does not assure a threadsafe wrapper
264 // for getenv().
265 tname = ExpandEnvVarSubstitutions( tname, m_project );
266
267 // Check to see if the file is a URI for an embedded file.
268 if( tname.StartsWith( FILEEXT::KiCadUriPrefix + "://" ) )
269 {
270 if( aEmbeddedFilesStack.empty() )
271 {
272 wxLogTrace( wxT( "KICAD_EMBED" ),
273 wxT( "No EMBEDDED_FILES object provided for kicad_embed URI" ) );
274 return wxEmptyString;
275 }
276
277 wxString path = tname.Mid( wxString( FILEEXT::KiCadUriPrefix + "://" ).length() );
278
279 wxFileName temp_file = aEmbeddedFilesStack[0]->GetTemporaryFileName( path );
280 int ii = 1;
281
282 while( !temp_file.IsOk() && ii < (int) aEmbeddedFilesStack.size() )
283 temp_file = aEmbeddedFilesStack[ii++]->GetTemporaryFileName( path );
284
285 if( !temp_file.IsOk() )
286 {
287 wxLogTrace( wxT( "KICAD_EMBED" ),
288 wxT( "Failed to get temp file '%s' for kicad_embed URI" ), path );
289 return wxEmptyString;
290 }
291
292 wxLogTrace( wxT( "KICAD_EMBED" ), wxT( "Opening embedded file '%s' as '%s'" ),
293 tname, temp_file.GetFullPath() );
294
295 return temp_file.GetFullPath();
296 }
297
298 wxFileName tmpFN( tname );
299
300 // this case covers full paths, leading expanded vars, and paths relative to the current
301 // working directory (which is not necessarily the current project directory)
302 if( tmpFN.FileExists() )
303 {
304 tmpFN.Normalize( FN_NORMALIZE_FLAGS );
305 tname = tmpFN.GetFullPath();
306
307 // special case: if a path begins with ${ENV_VAR} but is not in the resolver's path list
308 // then add it.
309 if( aFileName.StartsWith( wxS( "${" ) ) || aFileName.StartsWith( wxS( "$(" ) ) )
310 checkEnvVarPath( aFileName );
311
312 return tname;
313 }
314
315 // if a path begins with ${ENV_VAR}/$(ENV_VAR) and is not resolved then either the ENV_VAR is
316 // not defined, or this is a user-defined alias that was incorrectly stored in ${} format by
317 // older versions of KiCad. Try to match against user-defined aliases before giving up.
318 if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
319 {
320 // If expansion left the string unchanged, the variable was not found in the environment.
321 // Try matching the variable name against user-defined (non-env-var) aliases in m_paths.
322 if( tname == aFileName )
323 {
324 bool useBrace = aFileName.StartsWith( "${" );
325 size_t aliasEnd = aFileName.find( useBrace ? '}' : ')' );
326
327 // Require a non-empty variable name (aliasEnd > 2) so ${} / $() fall through.
328 if( aliasEnd != wxString::npos && aliasEnd > 2 )
329 {
330 wxString alias = aFileName.substr( 2, aliasEnd - 2 );
331
332 // Skip the separator if present
333 size_t relStart = aliasEnd + 1;
334
335 if( relStart < aFileName.length()
336 && ( aFileName[relStart] == '/' || aFileName[relStart] == '\\' ) )
337 {
338 relStart++;
339 }
340
341 wxString relpath;
342
343 if( relStart < aFileName.length() )
344 relpath = aFileName.substr( relStart );
345
346 for( const SEARCH_PATH& path : m_paths )
347 {
348 if( path.m_Alias.StartsWith( wxS( "${" ) ) || path.m_Alias.StartsWith( wxS( "$(" ) ) )
349 continue;
350
351 if( path.m_Alias == alias && !path.m_Pathexp.empty() )
352 {
353 wxFileName fpath( wxFileName::DirName( path.m_Pathexp ) );
354 wxString fullPath = fpath.GetPathWithSep() + relpath;
355
356 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
357
358 if( wxFileName::FileExists( fullPath ) )
359 {
360 wxFileName tmp( fullPath );
361
362 if( tmp.Normalize( FN_NORMALIZE_FLAGS ) )
363 fullPath = tmp.GetFullPath();
364
365 return fullPath;
366 }
367 }
368 }
369 }
370 }
371
372 if( !( m_errflags & ERRFLG_ENVPATH ) )
373 {
375 wxString errmsg = "[3D File Resolver] No such path; ensure the environment var is "
376 "defined";
377 errmsg.append( "\n" );
378 errmsg.append( tname );
379 errmsg.append( "\n" );
380 wxLogTrace( tracePathsAndFiles, errmsg );
381 }
382
383 return wxEmptyString;
384 }
385
386 // at this point aFileName is:
387 // a. an aliased shortened name or
388 // b. cannot be determined
389
390 // check the path relative to the current project directory;
391 // NB: this is not necessarily the same as the current working directory, which has already
392 // been checked. This case accounts for partial paths which do not contain ${KIPRJMOD}.
393 // This check is performed before checking the path relative to ${KICAD7_3DMODEL_DIR} so that
394 // users can potentially override a model within ${KICAD7_3DMODEL_DIR}.
395 if( !m_paths.begin()->m_Pathexp.empty() && !tname.StartsWith( ":" ) )
396 {
397 tmpFN.Assign( m_paths.begin()->m_Pathexp, "" );
398 wxString fullPath = tmpFN.GetPathWithSep() + tname;
399
400 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
401
402 if( wxFileName::FileExists( fullPath ) )
403 {
404 tmpFN.Assign( fullPath );
405 tmpFN.Normalize( FN_NORMALIZE_FLAGS );
406 tname = tmpFN.GetFullPath();
407 return tname;
408 }
409
410 }
411
412 // check path relative to search path
413 if( !aWorkingPath.IsEmpty() && !tname.StartsWith( ":" ) )
414 {
415 wxString tmp = aWorkingPath;
416 tmp.Append( tmpFN.GetPathSeparator() );
417 tmp.Append( tname );
418 tmpFN.Assign( tmp );
419
420 if( tmpFN.MakeAbsolute() && tmpFN.FileExists() )
421 {
422 tname = tmpFN.GetFullPath();
423 return tname;
424 }
425 }
426
427 // check the partial path relative to ${KICAD7_3DMODEL_DIR} (legacy behavior)
428 if( !tname.StartsWith( wxS( ":" ) ) )
429 {
430 wxFileName fpath;
431 wxString fullPath( wxString::Format( wxS( "${%s}" ),
432 ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) ) );
433 fullPath.Append( fpath.GetPathSeparator() );
434 fullPath.Append( tname );
435 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
436 fpath.Assign( fullPath );
437
438 if( fpath.Normalize( FN_NORMALIZE_FLAGS ) && fpath.FileExists() )
439 {
440 tname = fpath.GetFullPath();
441 return tname;
442 }
443
444 }
445
446 // at this point the filename must contain an alias or else it is invalid
447 wxString alias; // the alias portion of the short filename
448 wxString relpath; // the path relative to the alias
449
450 if( !SplitAlias( tname, alias, relpath ) )
451 {
452 if( !( m_errflags & ERRFLG_RELPATH ) )
453 {
454 // this can happen if the file was intended to be relative to ${KICAD7_3DMODEL_DIR}
455 // but ${KICAD7_3DMODEL_DIR} is not set or is incorrect.
457 wxString errmsg = "[3D File Resolver] No such path";
458 errmsg.append( wxS( "\n" ) );
459 errmsg.append( tname );
460 errmsg.append( wxS( "\n" ) );
461 wxLogTrace( tracePathsAndFiles, errmsg );
462 }
463
464 return wxEmptyString;
465 }
466
467 for( const SEARCH_PATH& path : m_paths )
468 {
469 // ${ENV_VAR} paths have already been checked; skip them
470 if( path.m_Alias.StartsWith( wxS( "${" ) ) || path.m_Alias.StartsWith( wxS( "$(" ) ) )
471 continue;
472
473 if( path.m_Alias == alias && !path.m_Pathexp.empty() )
474 {
475 wxFileName fpath( wxFileName::DirName( path.m_Pathexp ) );
476 wxString fullPath = fpath.GetPathWithSep() + relpath;
477
478 fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
479
480 if( wxFileName::FileExists( fullPath ) )
481 {
482 tname = fullPath;
483
484 wxFileName tmp( fullPath );
485
486 if( tmp.Normalize( FN_NORMALIZE_FLAGS ) )
487 tname = tmp.GetFullPath();
488
489 return tname;
490 }
491 }
492 }
493
494 if( !( m_errflags & ERRFLG_ALIAS ) )
495 {
497 wxString errmsg = "[3D File Resolver] No such path; ensure the path alias is defined";
498 errmsg.append( "\n" );
499 errmsg.append( tname.substr( 1 ) );
500 errmsg.append( "\n" );
501 wxLogTrace( tracePathsAndFiles, errmsg );
502 }
503
504 return wxEmptyString;
505}
506
507
509{
510 if( aPath.m_Alias.empty() || aPath.m_Pathvar.empty() )
511 return false;
512
513 std::lock_guard<std::mutex> lock( mutex_resolver );
514
515 SEARCH_PATH tpath = aPath;
516
517 #ifdef _WIN32
518 while( tpath.m_Pathvar.EndsWith( wxT( "\\" ) ) )
519 tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
520 #else
521 while( tpath.m_Pathvar.EndsWith( wxT( "/" ) ) && tpath.m_Pathvar.length() > 1 )
522 tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
523 #endif
524
525 wxFileName path( ExpandEnvVarSubstitutions( tpath.m_Pathvar, m_project ), "" );
526
527 path.Normalize( FN_NORMALIZE_FLAGS );
528
529 if( !path.DirExists() )
530 {
531 wxString versionedPath = wxString::Format( wxS( "${%s}" ),
532 ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
533
534 if( aPath.m_Pathvar == versionedPath
535 || aPath.m_Pathvar == wxS( "${KIPRJMOD}" ) || aPath.m_Pathvar == wxS( "$(KIPRJMOD)" )
536 || aPath.m_Pathvar == wxS( "${KISYS3DMOD}" )
537 || aPath.m_Pathvar == wxS( "$(KISYS3DMOD)" ) )
538 {
539 // suppress the message if the missing pathvar is a system variable
540 }
541 else
542 {
543 wxString msg = _( "The given path does not exist" );
544 msg.append( wxT( "\n" ) );
545 msg.append( tpath.m_Pathvar );
546 DisplayErrorMessage( nullptr, msg );
547 }
548
549 tpath.m_Pathexp.clear();
550 }
551 else
552 {
553 tpath.m_Pathexp = path.GetFullPath();
554
555#ifdef _WIN32
556 while( tpath.m_Pathexp.EndsWith( wxT( "\\" ) ) )
557 tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
558#else
559 while( tpath.m_Pathexp.EndsWith( wxT( "/" ) ) && tpath.m_Pathexp.length() > 1 )
560 tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
561#endif
562 }
563
564 std::list< SEARCH_PATH >::iterator sPL = m_paths.begin();
565 std::list< SEARCH_PATH >::iterator ePL = m_paths.end();
566
567 while( sPL != ePL )
568 {
569 if( tpath.m_Alias == sPL->m_Alias )
570 {
571 wxString msg = _( "Alias: " );
572 msg.append( tpath.m_Alias );
573 msg.append( wxT( "\n" ) );
574 msg.append( _( "This path:" ) + wxS( " " ) );
575 msg.append( tpath.m_Pathvar );
576 msg.append( wxT( "\n" ) );
577 msg.append( _( "Existing path:" ) + wxS( " " ) );
578 msg.append( sPL->m_Pathvar );
579 DisplayErrorMessage( nullptr, _( "Bad alias (duplicate name)" ), msg );
580 return false;
581 }
582
583 ++sPL;
584 }
585
586 m_paths.push_back( tpath );
587 return true;
588}
589
590
591void FILENAME_RESOLVER::checkEnvVarPath( const wxString& aPath )
592{
593 bool useParen = false;
594
595 if( aPath.StartsWith( wxS( "$(" ) ) )
596 useParen = true;
597 else if( !aPath.StartsWith( wxS( "${" ) ) )
598 return;
599
600 size_t pEnd;
601
602 if( useParen )
603 pEnd = aPath.find( wxS( ")" ) );
604 else
605 pEnd = aPath.find( wxS( "}" ) );
606
607 if( pEnd == wxString::npos )
608 return;
609
610 wxString envar = aPath.substr( 0, pEnd + 1 );
611
612 // check if the alias exists; if not then add it to the end of the
613 // env var section of the path list
614 auto sPL = m_paths.begin();
615 auto ePL = m_paths.end();
616
617 while( sPL != ePL )
618 {
619 if( sPL->m_Alias == envar )
620 return;
621
622 if( !sPL->m_Alias.StartsWith( wxS( "${" ) ) )
623 break;
624
625 ++sPL;
626 }
627
628 SEARCH_PATH lpath;
629 lpath.m_Alias = envar;
630 lpath.m_Pathvar = lpath.m_Alias;
631 wxFileName tmpFN( ExpandEnvVarSubstitutions( lpath.m_Alias, m_project ), "" );
632
633 wxUniChar psep = tmpFN.GetPathSeparator();
634 tmpFN.Normalize( FN_NORMALIZE_FLAGS );
635
636 if( !tmpFN.DirExists() )
637 return;
638
639 lpath.m_Pathexp = tmpFN.GetFullPath();
640
641 if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
642 lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
643
644 if( lpath.m_Pathexp.empty() )
645 return;
646
647 m_paths.insert( sPL, lpath );
648}
649
650
651wxString FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
652{
653 wxString fname = aFullPathName;
654
655 if( m_paths.empty() )
657
658 std::lock_guard<std::mutex> lock( mutex_resolver );
659
660 std::list< SEARCH_PATH >::const_iterator sL = m_paths.begin();
661 size_t idx;
662
663 while( sL != m_paths.end() )
664 {
665 // undefined paths do not participate in the
666 // file name shortening procedure
667 if( sL->m_Pathexp.empty() )
668 {
669 ++sL;
670 continue;
671 }
672
673 wxFileName fpath;
674
675 // in the case of aliases, ensure that we use the most recent definition
676 if( sL->m_Alias.StartsWith( wxS( "${" ) ) || sL->m_Alias.StartsWith( wxS( "$(" ) ) )
677 {
678 wxString tpath = ExpandEnvVarSubstitutions( sL->m_Alias, m_project );
679
680 if( tpath.empty() )
681 {
682 ++sL;
683 continue;
684 }
685
686 fpath.Assign( tpath, wxT( "" ) );
687 }
688 else
689 {
690 fpath.Assign( sL->m_Pathexp, wxT( "" ) );
691 }
692
693 wxString fps = fpath.GetPathWithSep();
694 wxString tname;
695
696 idx = fname.find( fps );
697
698 if( idx == 0 )
699 {
700 fname = fname.substr( fps.size() );
701
702 #ifdef _WIN32
703 // ensure only the '/' separator is used in the internal name
704 fname.Replace( wxT( "\\" ), wxT( "/" ) );
705 #endif
706
707 if( sL->m_Alias.StartsWith( wxS( "${" ) ) || sL->m_Alias.StartsWith( wxS( "$(" ) ) )
708 {
709 // old style ENV_VAR
710 tname = sL->m_Alias;
711 tname.Append( wxS( "/" ) );
712 tname.append( fname );
713 }
714 else
715 {
716 // user-defined alias: use ${alias}/path format
717 tname = wxS( "${" );
718 tname.append( sL->m_Alias );
719 tname.append( wxS( "}/" ) );
720 tname.append( fname );
721 }
722
723 return tname;
724 }
725
726 ++sL;
727 }
728
729#ifdef _WIN32
730 // it is strange to convert an MSWin full path to use the
731 // UNIX separator but this is done for consistency and can
732 // be helpful even when transferring project files from
733 // MSWin to *NIX.
734 fname.Replace( wxT( "\\" ), wxT( "/" ) );
735#endif
736
737 return fname;
738}
739
740
741const std::list< SEARCH_PATH >* FILENAME_RESOLVER::GetPaths() const
742{
743 return &m_paths;
744}
745
746
747bool FILENAME_RESOLVER::SplitAlias( const wxString& aFileName,
748 wxString& anAlias, wxString& aRelPath ) const
749{
750 anAlias.clear();
751 aRelPath.clear();
752
753 size_t searchStart = 0;
754
755 if( aFileName.StartsWith( wxT( ":" ) ) )
756 searchStart = 1;
757
758 size_t tagpos = aFileName.find( wxT( ":" ), searchStart );
759
760 if( tagpos == wxString::npos || tagpos == searchStart )
761 return false;
762
763 if( tagpos + 1 >= aFileName.length() )
764 return false;
765
766 anAlias = aFileName.substr( searchStart, tagpos - searchStart );
767 aRelPath = aFileName.substr( tagpos + 1 );
768
769 return true;
770}
771
772
773bool FILENAME_RESOLVER::ValidateFileName( const wxString& aFileName, bool& hasAlias ) const
774{
775 // Rules:
776 // 1. The generic form of an aliased 3D relative path is:
777 // ALIAS:relative/path
778 // 2. ALIAS is a UTF string excluding wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" )
779 // 3. The relative path must be a valid relative path for the platform
780 // 4. We allow a URI for embedded files, but only if it has a name
781
782 hasAlias = false;
783
784 if( aFileName.empty() )
785 return false;
786
787 if( aFileName.StartsWith( wxT( "file://" ) )
788 || aFileName.StartsWith( FILEEXT::KiCadUriPrefix + "://" ) )
789 {
790 size_t prefixLength = aFileName.StartsWith( wxT( "file://" ) ) ? 7 : 14;
791 if( aFileName.length() > prefixLength && aFileName[prefixLength] != '/' )
792 return true;
793 else
794 return false;
795 }
796
797 wxString filename = aFileName;
798 wxString lpath;
799 size_t aliasStart = aFileName.StartsWith( ':' ) ? 1 : 0;
800 size_t aliasEnd = aFileName.find( ':', aliasStart );
801
802 // ensure that the file separators suit the current platform
803#ifdef __WINDOWS__
804 filename.Replace( wxT( "/" ), wxT( "\\" ) );
805
806 // if we see the :\ pattern then it must be a drive designator
807 if( aliasEnd != wxString::npos )
808 {
809 size_t pos1 = filename.find( wxT( ":\\" ) );
810
811 if( pos1 != wxString::npos && ( pos1 != aliasEnd || pos1 != 1 ) )
812 return false;
813
814 // if we have a drive designator then we have no alias
815 if( pos1 != wxString::npos )
816 aliasEnd = wxString::npos;
817 }
818#else
819 filename.Replace( wxT( "\\" ), wxT( "/" ) );
820#endif
821
822 // names may not end with ':'
823 if( aliasEnd == aFileName.length() -1 )
824 return false;
825
826 if( aliasEnd != wxString::npos )
827 {
828 // ensure the alias component is not empty
829 if( aliasEnd == aliasStart )
830 return false;
831
832 lpath = filename.substr( aliasStart, aliasEnd );
833
834 // check the alias for restricted characters
835 if( wxString::npos != lpath.find_first_of( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
836 return false;
837
838 hasAlias = true;
839 lpath = aFileName.substr( aliasEnd + 1 );
840 }
841 else
842 {
843 lpath = aFileName;
844
845 // in the case of ${ENV_VAR}|$(ENV_VAR)/path, strip the
846 // environment string before testing
847 aliasEnd = wxString::npos;
848
849 if( aFileName.StartsWith( wxS( "${" ) ) )
850 aliasEnd = aFileName.find( '}' );
851 else if( aFileName.StartsWith( wxS( "$(" ) ) )
852 aliasEnd = aFileName.find( ')' );
853
854 if( aliasEnd != wxString::npos )
855 lpath = aFileName.substr( aliasEnd + 1 );
856
857 }
858
859 // Test for forbidden chars in filenames. Should be wxFileName::GetForbiddenChars()
860 // On MSW, the list returned by wxFileName::GetForbiddenChars() contains separators
861 // '\'and '/' used here because lpath can be a full path.
862 // So remove separators
863 wxString lpath_no_sep = lpath;
864
865#ifdef __WINDOWS__
866 lpath_no_sep.Replace( "/", " " );
867 lpath_no_sep.Replace( "\\", " " );
868
869 // A disk identifier is allowed, and therefore remove its separator
870 if( lpath_no_sep.Length() > 1 && lpath_no_sep[1] == ':' )
871 lpath_no_sep[1] = ' ';
872#endif
873
874 if( wxString::npos != lpath_no_sep.find_first_of( wxFileName::GetForbiddenChars() ) )
875 return false;
876
877 return true;
878}
879
880
881bool FILENAME_RESOLVER::GetKicadPaths( std::list< wxString >& paths ) const
882{
883 paths.clear();
884
885 if( !m_pgm )
886 return false;
887
888 bool hasKisys3D = false;
889
890
891 // iterate over the list of internally defined ENV VARs
892 // and add them to the paths list
893 ENV_VAR_MAP_CITER mS = m_pgm->GetLocalEnvVariables().begin();
894 ENV_VAR_MAP_CITER mE = m_pgm->GetLocalEnvVariables().end();
895
896 while( mS != mE )
897 {
898 // filter out URLs, template directories, and known system paths
899 if( mS->first == wxS( "KICAD_PTEMPLATES" )
900 || mS->first.Matches( wxS( "KICAD*_FOOTPRINT_DIR") ) )
901 {
902 ++mS;
903 continue;
904 }
905
906 if( wxString::npos != mS->second.GetValue().find( wxS( "://" ) ) )
907 {
908 ++mS;
909 continue;
910 }
911
912 //also add the path without the ${} to act as legacy alias support for older files
913 paths.push_back( mS->first );
914
915 if( mS->first.Matches( wxS("KICAD*_3DMODEL_DIR") ) )
916 hasKisys3D = true;
917
918 ++mS;
919 }
920
921 if( !hasKisys3D )
922 paths.emplace_back( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
923
924 return true;
925}
bool ValidateFileName(const wxString &aFileName, bool &hasAlias) const
Return 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...
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath, std::vector< const EMBEDDED_FILES * > aEmbeddedFilesStack)
Determine the full path of the given file name.
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
List of base paths to search from.
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...
wxString m_configDir
3D configuration directory.
const std::list< SEARCH_PATH > * GetPaths() const
Return a pointer to the internal path list; the items in:load.
bool SetProject(const PROJECT *aProject, bool *flgChanged=nullptr)
Set the current KiCad project directory as the first entry in the model path list.
const PROJECT * m_project
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:102
Container for project specific data.
Definition project.h:62
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:183
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:704
The common library.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
#define _(s)
Functions related to environment variables, including help functions.
#define ERRFLG_RELPATH
static std::mutex mutex_resolver
#define ERRFLG_ENVPATH
#define MASK_3D_RESOLVER
#define ERRFLG_ALIAS
static const std::string KiCadUriPrefix
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
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:77
see class PGM_BASE
wxString m_Pathvar
Base path as stored in the configuration file.
wxString m_Pathexp
Expanded base path.
wxString m_Alias
Alias to the base path.
std::string path
wxLogTrace helper definitions.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:35