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