KiCad PCB EDA Suite
FILENAME_RESOLVER Class Reference

Provide an extensible class to resolve 3D model paths. More...

#include <filename_resolver.h>

Public Member Functions

 FILENAME_RESOLVER ()
 
bool Set3DConfigDir (const wxString &aConfigDir)
 Set the user's configuration directory for 3D models. More...
 
bool SetProject (PROJECT *aProject, bool *flgChanged=nullptr)
 Set the current KiCad project directory as the first entry in the model path list. More...
 
wxString GetProjectDir () const
 
void SetProgramBase (PGM_BASE *aBase)
 Set a pointer to the application's PGM_BASE instance used to extract the local env vars. More...
 
bool UpdatePathList (const std::vector< SEARCH_PATH > &aPathList)
 Clear the current path list and substitutes the given path list and update the path configuration file on success. More...
 
wxString ResolvePath (const wxString &aFileName)
 Determines the full path of the given file name. More...
 
wxString ShortenPath (const wxString &aFullPathName)
 Produce a relative path based on the existing search directories or returns the same path if the path is not a superset of an existing search path. More...
 
const std::list< SEARCH_PATH > * GetPaths () const
 Return a pointer to the internal path list; the items in:load. More...
 
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 aRelPath with the relative path. More...
 
bool ValidateFileName (const wxString &aFileName, bool &hasAlias) const
 Returns true if the given path is a valid aliased relative path. More...
 
bool GetKicadPaths (std::list< wxString > &paths) const
 Return a list of path environment variables local to KiCad. More...
 

Private Member Functions

bool createPathList (void)
 Build the path list using available information such as KICAD6_3DMODEL_DIR and the 3d_path_list configuration file. More...
 
bool addPath (const SEARCH_PATH &aPath)
 Check that a path is valid and adds it to the search list. More...
 
bool readPathList (void)
 Read a list of path names from a configuration file. More...
 
bool writePathList (void)
 Write the current path list to a configuration file. More...
 
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 the list. More...
 

Private Attributes

wxString m_configDir
 
std::list< SEARCH_PATHm_paths
 
int m_errflags
 
PGM_BASEm_pgm
 
PROJECTm_project
 
wxString m_curProjDir
 

Detailed Description

Provide an extensible class to resolve 3D model paths.

Initially the legacy behavior will be implemented and an incomplete path would be checked against the project directory or the KICAD6_3DMODEL_DIR environment variable. In the future a configurable set of search paths may be specified.

Definition at line 55 of file filename_resolver.h.

Constructor & Destructor Documentation

◆ FILENAME_RESOLVER()

FILENAME_RESOLVER::FILENAME_RESOLVER ( )

Definition at line 53 of file filename_resolver.cpp.

53  :
54  m_pgm( nullptr ),
55  m_project( nullptr )
56 {
57  m_errflags = 0;
58 }

References m_errflags.

Member Function Documentation

◆ addPath()

bool FILENAME_RESOLVER::addPath ( const SEARCH_PATH aPath)
private

Check that a path is valid and adds it to the search list.

Parameters
aPathis the alias set to be checked and added.
Returns
true if aPath is valid.

Definition at line 424 of file filename_resolver.cpp.

425 {
426  if( aPath.m_Alias.empty() || aPath.m_Pathvar.empty() )
427  return false;
428 
429  std::lock_guard<std::mutex> lock( mutex_resolver );
430 
431  SEARCH_PATH tpath = aPath;
432 
433  #ifdef _WIN32
434  while( tpath.m_Pathvar.EndsWith( wxT( "\\" ) ) )
435  tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
436  #else
437  while( tpath.m_Pathvar.EndsWith( wxT( "/" ) ) && tpath.m_Pathvar.length() > 1 )
438  tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
439  #endif
440 
441  wxFileName path( ExpandEnvVarSubstitutions( tpath.m_Pathvar, m_project ), "" );
442 
443  path.Normalize();
444 
445  if( !path.DirExists() )
446  {
447  // suppress the message if the missing pathvar is the
448  // legacy KICAD6_3DMODEL_DIR variable
449  if( aPath.m_Pathvar.compare( wxT( "${KICAD6_3DMODEL_DIR}" ) ) )
450  {
451  wxString msg = _( "The given path does not exist" );
452  msg.append( wxT( "\n" ) );
453  msg.append( tpath.m_Pathvar );
454  wxMessageBox( msg, _( "3D model search path" ) );
455  }
456 
457  tpath.m_Pathexp.clear();
458  }
459  else
460  {
461  tpath.m_Pathexp = path.GetFullPath();
462 
463 #ifdef _WIN32
464  while( tpath.m_Pathexp.EndsWith( wxT( "\\" ) ) )
465  tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
466 #else
467  while( tpath.m_Pathexp.EndsWith( wxT( "/" ) ) && tpath.m_Pathexp.length() > 1 )
468  tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
469 #endif
470  }
471 
472  std::list< SEARCH_PATH >::iterator sPL = m_paths.begin();
473  std::list< SEARCH_PATH >::iterator ePL = m_paths.end();
474 
475  while( sPL != ePL )
476  {
477  if( !tpath.m_Alias.Cmp( sPL->m_Alias ) )
478  {
479  wxString msg = _( "Alias: " );
480  msg.append( tpath.m_Alias );
481  msg.append( wxT( "\n" ) );
482  msg.append( _( "This path:" ) + wxS( " " ) );
483  msg.append( tpath.m_Pathvar );
484  msg.append( wxT( "\n" ) );
485  msg.append( _( "Existing path:" ) + wxS( " " ) );
486  msg.append( sPL->m_Pathvar );
487  wxMessageBox( msg, _( "Bad alias (duplicate name)" ) );
488 
489  return false;
490  }
491 
492  ++sPL;
493  }
494 
495  m_paths.push_back( tpath );
496  return true;
497 }
std::list< SEARCH_PATH > m_paths
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273
wxString m_Alias
wxString m_Pathvar
static std::mutex mutex_resolver
#define _(s)
Definition: 3d_actions.cpp:33
wxString m_Pathexp

References _, ExpandEnvVarSubstitutions(), SEARCH_PATH::m_Alias, SEARCH_PATH::m_Pathexp, m_paths, SEARCH_PATH::m_Pathvar, m_project, mutex_resolver, and path.

Referenced by readPathList(), and UpdatePathList().

◆ checkEnvVarPath()

void FILENAME_RESOLVER::checkEnvVarPath ( const wxString &  aPath)
private

Check the ${ENV_VAR} component of a path and adds it to the resolver's path list if it is not yet in the list.

Definition at line 671 of file filename_resolver.cpp.

672 {
673  bool useParen = false;
674 
675  if( aPath.StartsWith( "$(" ) )
676  useParen = true;
677  else if( !aPath.StartsWith( "${" ) )
678  return;
679 
680  size_t pEnd;
681 
682  if( useParen )
683  pEnd = aPath.find( ")" );
684  else
685  pEnd = aPath.find( "}" );
686 
687  if( pEnd == wxString::npos )
688  return;
689 
690  wxString envar = aPath.substr( 0, pEnd + 1 );
691 
692  // check if the alias exists; if not then add it to the end of the
693  // env var section of the path list
694  auto sPL = m_paths.begin();
695  auto ePL = m_paths.end();
696 
697  while( sPL != ePL )
698  {
699  if( sPL->m_Alias == envar )
700  return;
701 
702  if( !sPL->m_Alias.StartsWith( "${" ) )
703  break;
704 
705  ++sPL;
706  }
707 
708  SEARCH_PATH lpath;
709  lpath.m_Alias = envar;
710  lpath.m_Pathvar = lpath.m_Alias;
711  wxFileName tmpFN( ExpandEnvVarSubstitutions( lpath.m_Alias, m_project ), "" );
712 
713  wxUniChar psep = tmpFN.GetPathSeparator();
714  tmpFN.Normalize();
715 
716  if( !tmpFN.DirExists() )
717  return;
718 
719  lpath.m_Pathexp = tmpFN.GetFullPath();
720 
721  if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
722  lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
723 
724  if( lpath.m_Pathexp.empty() )
725  return;
726 
727  m_paths.insert( sPL, lpath );
728 }
std::list< SEARCH_PATH > m_paths
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273
wxString m_Alias
wxString m_Pathvar
wxString m_Pathexp

References ExpandEnvVarSubstitutions(), SEARCH_PATH::m_Alias, SEARCH_PATH::m_Pathexp, m_paths, SEARCH_PATH::m_Pathvar, and m_project.

Referenced by ResolvePath().

◆ createPathList()

bool FILENAME_RESOLVER::createPathList ( void  )
private

Build the path list using available information such as KICAD6_3DMODEL_DIR and the 3d_path_list configuration file.

Warning
Invalid paths are silently discarded and removed from the configuration file.
Returns
true if at least one valid path was found.

Definition at line 158 of file filename_resolver.cpp.

159 {
160  if( !m_paths.empty() )
161  return true;
162 
163  wxString kmod;
164 
165  // add an entry for the default search path; at this point
166  // we cannot set a sensible default so we use an empty string.
167  // the user may change this later with a call to SetProjectDir()
168 
169  SEARCH_PATH lpath;
170  lpath.m_Alias = "${KIPRJMOD}";
171  lpath.m_Pathvar = "${KIPRJMOD}";
172  lpath.m_Pathexp = m_curProjDir;
173  m_paths.push_back( lpath );
174  wxFileName fndummy;
175  wxUniChar psep = fndummy.GetPathSeparator();
176  std::list< wxString > epaths;
177 
178  if( GetKicadPaths( epaths ) )
179  {
180  for( const wxString& curr_path : epaths )
181  {
182  wxString pathVal = ExpandEnvVarSubstitutions( curr_path, m_project );
183 
184  if( pathVal.empty() )
185  {
186  lpath.m_Pathexp.clear();
187  }
188  else
189  {
190  fndummy.Assign( pathVal, "" );
191  fndummy.Normalize();
192  lpath.m_Pathexp = fndummy.GetFullPath();
193  }
194 
195  lpath.m_Alias = curr_path;
196  lpath.m_Pathvar = curr_path;
197 
198  if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
199  lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
200 
201  m_paths.push_back( lpath );
202  }
203  }
204 
205  if( !m_configDir.empty() )
206  readPathList();
207 
208  if( m_paths.empty() )
209  return false;
210 
211 #ifdef DEBUG
212  wxLogTrace( MASK_3D_RESOLVER, " * [3D model] search paths:\n" );
213  std::list< SEARCH_PATH >::const_iterator sPL = m_paths.begin();
214 
215  while( sPL != m_paths.end() )
216  {
217  wxLogTrace( MASK_3D_RESOLVER, " + %s : '%s'\n", (*sPL).m_Alias.GetData(),
218  (*sPL).m_Pathexp.GetData() );
219  ++sPL;
220  }
221 #endif
222 
223  return true;
224 }
std::list< SEARCH_PATH > m_paths
bool readPathList(void)
Read a list of path names from a configuration file.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273
wxString m_Alias
wxString m_Pathvar
bool GetKicadPaths(std::list< wxString > &paths) const
Return a list of path environment variables local to KiCad.
#define MASK_3D_RESOLVER
wxString m_Pathexp

References ExpandEnvVarSubstitutions(), GetKicadPaths(), SEARCH_PATH::m_Alias, m_configDir, m_curProjDir, SEARCH_PATH::m_Pathexp, m_paths, SEARCH_PATH::m_Pathvar, m_project, MASK_3D_RESOLVER, and readPathList().

Referenced by ResolvePath(), Set3DConfigDir(), SetProgramBase(), and ShortenPath().

◆ GetKicadPaths()

bool FILENAME_RESOLVER::GetKicadPaths ( std::list< wxString > &  paths) const

Return a list of path environment variables local to KiCad.

This list always includes KICAD6_3DMODEL_DIR even if it is not defined locally.

Definition at line 1005 of file filename_resolver.cpp.

1006 {
1007  paths.clear();
1008 
1009  if( !m_pgm )
1010  return false;
1011 
1012  bool hasKisys3D = false;
1013 
1014 
1015  // iterate over the list of internally defined ENV VARs
1016  // and add them to the paths list
1019 
1020  while( mS != mE )
1021  {
1022  // filter out URLs, template directories, and known system paths
1023  if( mS->first == wxString( "KICAD_PTEMPLATES" )
1024  || mS->first == wxString( "KICAD6_FOOTPRINT_DIR" ) )
1025  {
1026  ++mS;
1027  continue;
1028  }
1029 
1030  if( wxString::npos != mS->second.GetValue().find( wxString( "://" ) ) )
1031  {
1032  ++mS;
1033  continue;
1034  }
1035 
1036  wxString tmp( "${" );
1037  tmp.Append( mS->first );
1038  tmp.Append( "}" );
1039  paths.push_back( tmp );
1040 
1041  if( tmp == "${KICAD6_3DMODEL_DIR}" )
1042  hasKisys3D = true;
1043 
1044  ++mS;
1045  }
1046 
1047  if( !hasKisys3D )
1048  paths.emplace_back("${KICAD6_3DMODEL_DIR}" );
1049 
1050  return true;
1051 }
std::map< wxString, ENV_VAR_ITEM >::const_iterator ENV_VAR_MAP_CITER
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition: pgm_base.cpp:645

References PGM_BASE::GetLocalEnvVariables(), and m_pgm.

Referenced by createPathList().

◆ GetPaths()

const std::list< SEARCH_PATH > * FILENAME_RESOLVER::GetPaths ( ) const

Return a pointer to the internal path list; the items in:load.

The list can be used to set up the list of search paths available to a 3D file browser.

Returns
pointer to the internal path list.

Definition at line 822 of file filename_resolver.cpp.

823 {
824  return &m_paths;
825 }
std::list< SEARCH_PATH > m_paths

References m_paths.

Referenced by DIALOG_CONFIGURE_PATHS::TransferDataToWindow(), and DLG_SELECT_3DMODEL::updateDirChoiceList().

◆ GetProjectDir()

wxString FILENAME_RESOLVER::GetProjectDir ( ) const

Definition at line 139 of file filename_resolver.cpp.

140 {
141  return m_curProjDir;
142 }

References m_curProjDir.

◆ readPathList()

bool FILENAME_RESOLVER::readPathList ( void  )
private

Read a list of path names from a configuration file.

Returns
true if a file was found and contained at least one valid path.

Definition at line 500 of file filename_resolver.cpp.

501 {
502  if( m_configDir.empty() )
503  {
504  std::ostringstream ostr;
505  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
506  wxString errmsg = "3D configuration directory is unknown";
507  ostr << " * " << errmsg.ToUTF8();
508  wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
509  return false;
510  }
511 
512  wxFileName cfgpath( m_configDir, RESOLVER_CONFIG );
513  cfgpath.Normalize();
514  wxString cfgname = cfgpath.GetFullPath();
515 
516  size_t nitems = m_paths.size();
517 
518  std::ifstream cfgFile;
519  std::string cfgLine;
520 
521  if( !wxFileName::Exists( cfgname ) )
522  {
523  std::ostringstream ostr;
524  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
525  wxString errmsg = "no 3D configuration file";
526  ostr << " * " << errmsg.ToUTF8() << " '";
527  ostr << cfgname.ToUTF8() << "'";
528  wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
529  return false;
530  }
531 
532  cfgFile.open( cfgname.ToUTF8() );
533 
534  if( !cfgFile.is_open() )
535  {
536  std::ostringstream ostr;
537  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
538  wxString errmsg = "Could not open configuration file";
539  ostr << " * " << errmsg.ToUTF8() << " '" << cfgname.ToUTF8() << "'";
540  wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
541  return false;
542  }
543 
544  int lineno = 0;
545  SEARCH_PATH al;
546  size_t idx;
547  int vnum = 0; // version number
548 
549  while( cfgFile.good() )
550  {
551  cfgLine.clear();
552  std::getline( cfgFile, cfgLine );
553  ++lineno;
554 
555  if( cfgLine.empty() )
556  {
557  if( cfgFile.eof() )
558  break;
559 
560  continue;
561  }
562 
563  if( 1 == lineno && cfgLine.compare( 0, 2, "#V" ) == 0 )
564  {
565  // extract the version number and parse accordingly
566  if( cfgLine.size() > 2 )
567  {
568  std::istringstream istr;
569  istr.str( cfgLine.substr( 2 ) );
570  istr >> vnum;
571  }
572 
573  continue;
574  }
575 
576  idx = 0;
577 
578  if( !getHollerith( cfgLine, idx, al.m_Alias ) )
579  continue;
580 
581  // never add on KICAD6_3DMODEL_DIR from a config file
582  if( !al.m_Alias.Cmp( wxT( "KICAD6_3DMODEL_DIR" ) ) )
583  continue;
584 
585  if( !getHollerith( cfgLine, idx, al.m_Pathvar ) )
586  continue;
587 
588  if( !getHollerith( cfgLine, idx, al.m_Description ) )
589  continue;
590 
591  addPath( al );
592  }
593 
594  cfgFile.close();
595 
596  if( vnum < CFGFILE_VERSION )
597  writePathList();
598 
599  return( m_paths.size() != nitems );
600 }
#define CFGFILE_VERSION
std::list< SEARCH_PATH > m_paths
bool writePathList(void)
Write the current path list to a configuration file.
static bool getHollerith(const std::string &aString, size_t &aIndex, wxString &aResult)
wxString m_Alias
bool addPath(const SEARCH_PATH &aPath)
Check that a path is valid and adds it to the search list.
wxString m_Description
wxString m_Pathvar
#define RESOLVER_CONFIG
#define MASK_3D_RESOLVER

References addPath(), CFGFILE_VERSION, getHollerith(), SEARCH_PATH::m_Alias, m_configDir, SEARCH_PATH::m_Description, m_paths, SEARCH_PATH::m_Pathvar, MASK_3D_RESOLVER, RESOLVER_CONFIG, and writePathList().

Referenced by createPathList().

◆ ResolvePath()

wxString FILENAME_RESOLVER::ResolvePath ( const wxString &  aFileName)

Determines the full path of the given file name.

In the future remote files may be supported, in which case it is best to require a full URI in which case ResolvePath should check that the URI conforms to RFC-2396 and related documents and copies aFileName into aResolvedName if the URI is valid.

Definition at line 241 of file filename_resolver.cpp.

242 {
243  std::lock_guard<std::mutex> lock( mutex_resolver );
244 
245  if( aFileName.empty() )
246  return wxEmptyString;
247 
248  if( m_paths.empty() )
249  createPathList();
250 
251  // first attempt to use the name as specified:
252  wxString tname = aFileName;
253 
254  #ifdef _WIN32
255  // translate from KiCad's internal UNIX-like path to MSWin paths
256  tname.Replace( wxT( "/" ), wxT( "\\" ) );
257  #endif
258 
259  // Note: variable expansion must be performed using a threadsafe
260  // wrapper for the getenv() system call. If we allow the
261  // wxFileName::Normalize() routine to perform expansion then
262  // we will have a race condition since wxWidgets does not assure
263  // a threadsafe wrapper for getenv().
264  tname = ExpandEnvVarSubstitutions( tname, m_project );
265 
266  wxFileName tmpFN( tname );
267 
268  // in the case of absolute filenames we don't store a map item
269  if( !aFileName.StartsWith( "${" ) && !aFileName.StartsWith( "$(" )
270  && !aFileName.StartsWith( ":" ) && tmpFN.IsAbsolute() )
271  {
272  tmpFN.Normalize();
273 
274  if( tmpFN.FileExists() )
275  return tmpFN.GetFullPath();
276 
277  return wxEmptyString;
278  }
279 
280  // this case covers full paths, leading expanded vars, and paths
281  // relative to the current working directory (which is not necessarily
282  // the current project directory)
283  if( tmpFN.FileExists() )
284  {
285  tmpFN.Normalize();
286  tname = tmpFN.GetFullPath();
287 
288  // special case: if a path begins with ${ENV_VAR} but is not in the
289  // resolver's path list then add it.
290  if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
291  checkEnvVarPath( aFileName );
292 
293  return tname;
294  }
295 
296  // if a path begins with ${ENV_VAR}/$(ENV_VAR) and is not resolved then the
297  // file either does not exist or the ENV_VAR is not defined
298  if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
299  {
300  if( !( m_errflags & ERRFLG_ENVPATH ) )
301  {
303  wxString errmsg = "[3D File Resolver] No such path; ensure the environment var is defined";
304  errmsg.append( "\n" );
305  errmsg.append( tname );
306  errmsg.append( "\n" );
307  wxLogTrace( tracePathsAndFiles, errmsg );
308  }
309 
310  return wxEmptyString;
311  }
312 
313  // at this point aFileName is:
314  // a. an aliased shortened name or
315  // b. cannot be determined
316 
317  std::list< SEARCH_PATH >::const_iterator sPL = m_paths.begin();
318  std::list< SEARCH_PATH >::const_iterator ePL = m_paths.end();
319 
320  // check the path relative to the current project directory;
321  // note: this is not necessarily the same as the current working
322  // directory, which has already been checked. This case accounts
323  // for partial paths which do not contain ${KIPRJMOD}.
324  // This check is performed before checking the path relative to
325  // ${KICAD6_3DMODEL_DIR} so that users can potentially override a model
326  // within ${KICAD6_3DMODEL_DIR}
327  if( !sPL->m_Pathexp.empty() && !tname.StartsWith( ":" ) )
328  {
329  tmpFN.Assign( sPL->m_Pathexp, "" );
330  wxString fullPath = tmpFN.GetPathWithSep() + tname;
331 
332  fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
333 
334  if( wxFileName::FileExists( fullPath ) )
335  {
336  tmpFN.Assign( fullPath );
337  tmpFN.Normalize();
338  tname = tmpFN.GetFullPath();
339  return tname;
340  }
341 
342  }
343 
344  // check the partial path relative to ${KICAD6_3DMODEL_DIR} (legacy behavior)
345  if( !tname.StartsWith( ":" ) )
346  {
347  wxFileName fpath;
348  wxString fullPath( "${KICAD6_3DMODEL_DIR}" );
349  fullPath.Append( fpath.GetPathSeparator() );
350  fullPath.Append( tname );
351  fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
352  fpath.Assign( fullPath );
353 
354  if( fpath.Normalize() && fpath.FileExists() )
355  {
356  tname = fpath.GetFullPath();
357  return tname;
358  }
359 
360  }
361 
362  // ${ENV_VAR} paths have already been checked; skip them
363  while( sPL != ePL && ( sPL->m_Alias.StartsWith( "${" ) || sPL->m_Alias.StartsWith( "$(" ) ) )
364  ++sPL;
365 
366  // at this point the filename must contain an alias or else it is invalid
367  wxString alias; // the alias portion of the short filename
368  wxString relpath; // the path relative to the alias
369 
370  if( !SplitAlias( tname, alias, relpath ) )
371  {
372  if( !( m_errflags & ERRFLG_RELPATH ) )
373  {
374  // this can happen if the file was intended to be relative to
375  // ${KICAD6_3DMODEL_DIR} but ${KICAD6_3DMODEL_DIR} not set or incorrect.
377  wxString errmsg = "[3D File Resolver] No such path";
378  errmsg.append( "\n" );
379  errmsg.append( tname );
380  errmsg.append( "\n" );
381  wxLogTrace( tracePathsAndFiles, errmsg );
382  }
383 
384  return wxEmptyString;
385  }
386 
387  while( sPL != ePL )
388  {
389  if( !sPL->m_Alias.Cmp( alias ) && !sPL->m_Pathexp.empty() )
390  {
391  wxFileName fpath( wxFileName::DirName( sPL->m_Pathexp ) );
392  wxString fullPath = fpath.GetPathWithSep() + relpath;
393 
394  fullPath = ExpandEnvVarSubstitutions( fullPath, m_project );
395 
396  if( wxFileName::FileExists( fullPath ) )
397  {
398  wxFileName tmp( fullPath );
399 
400  if( tmp.Normalize() )
401  tname = tmp.GetFullPath();
402 
403  return tname;
404  }
405  }
406 
407  ++sPL;
408  }
409 
410  if( !( m_errflags & ERRFLG_ALIAS ) )
411  {
413  wxString errmsg = "[3D File Resolver] No such path; ensure the path alias is defined";
414  errmsg.append( "\n" );
415  errmsg.append( tname.substr( 1 ) );
416  errmsg.append( "\n" );
417  wxLogTrace( tracePathsAndFiles, errmsg );
418  }
419 
420  return wxEmptyString;
421 }
std::list< SEARCH_PATH > m_paths
#define ERRFLG_ENVPATH
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 wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
bool createPathList(void)
Build the path list using available information such as KICAD6_3DMODEL_DIR and the 3d_path_list confi...
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273
#define ERRFLG_ALIAS
#define ERRFLG_RELPATH
static std::mutex mutex_resolver
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 ...

References checkEnvVarPath(), createPathList(), ERRFLG_ALIAS, ERRFLG_ENVPATH, ERRFLG_RELPATH, ExpandEnvVarSubstitutions(), m_errflags, m_paths, m_project, mutex_resolver, SplitAlias(), and tracePathsAndFiles.

Referenced by EXPORTER_PCB_VRML::ExportVrmlFootprint(), idf_export_footprint(), and S3D_CACHE::load().

◆ Set3DConfigDir()

bool FILENAME_RESOLVER::Set3DConfigDir ( const wxString &  aConfigDir)

Set the user's configuration directory for 3D models.

Parameters
aConfigDir
Returns
true if the call succeeds (directory exists).

Definition at line 61 of file filename_resolver.cpp.

62 {
63  if( aConfigDir.empty() )
64  return false;
65 
66  wxFileName cfgdir( ExpandEnvVarSubstitutions( aConfigDir, m_project ), "" );
67 
68  cfgdir.Normalize();
69 
70  if( !cfgdir.DirExists() )
71  return false;
72 
73  m_configDir = cfgdir.GetPath();
75 
76  return true;
77 }
bool createPathList(void)
Build the path list using available information such as KICAD6_3DMODEL_DIR and the 3d_path_list confi...
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273

References createPathList(), ExpandEnvVarSubstitutions(), m_configDir, and m_project.

Referenced by S3D_CACHE::Set3DConfigDir().

◆ SetProgramBase()

void FILENAME_RESOLVER::SetProgramBase ( PGM_BASE aBase)

Set a pointer to the application's PGM_BASE instance used to extract the local env vars.

Definition at line 145 of file filename_resolver.cpp.

146 {
147  m_pgm = aBase;
148 
149  if( !m_pgm || m_paths.empty() )
150  return;
151 
152  // recreate the path list
153  m_paths.clear();
154  createPathList();
155 }
std::list< SEARCH_PATH > m_paths
bool createPathList(void)
Build the path list using available information such as KICAD6_3DMODEL_DIR and the 3d_path_list confi...

References createPathList(), m_paths, and m_pgm.

Referenced by S3D_CACHE::SetProgramBase().

◆ SetProject()

bool FILENAME_RESOLVER::SetProject ( PROJECT aProject,
bool *  flgChanged = nullptr 
)

Set the current KiCad project directory as the first entry in the model path list.

Parameters
[in]aProjDircurrent project directory
[out]flgChangedoptional, set to true if directory was changed
Return values
truesuccess
falsefailure

Definition at line 80 of file filename_resolver.cpp.

81 {
82  m_project = aProject;
83 
84  if( !aProject )
85  return false;
86 
87  wxFileName projdir( ExpandEnvVarSubstitutions( aProject->GetProjectPath(), aProject ), "" );
88 
89  projdir.Normalize();
90 
91  if( !projdir.DirExists() )
92  return false;
93 
94  m_curProjDir = projdir.GetPath();
95 
96  if( flgChanged )
97  *flgChanged = false;
98 
99  if( m_paths.empty() )
100  {
101  SEARCH_PATH al;
102  al.m_Alias = "${KIPRJMOD}";
103  al.m_Pathvar = "${KIPRJMOD}";
104  al.m_Pathexp = m_curProjDir;
105  m_paths.push_back( al );
106 
107  if( flgChanged )
108  *flgChanged = true;
109  }
110  else
111  {
112  if( m_paths.front().m_Pathexp.Cmp( m_curProjDir ) )
113  {
114  m_paths.front().m_Pathexp = m_curProjDir;
115 
116  if( flgChanged )
117  *flgChanged = true;
118  }
119  else
120  {
121  return true;
122  }
123  }
124 
125 #ifdef DEBUG
126  {
127  std::ostringstream ostr;
128  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
129  ostr << " * [INFO] changed project dir to ";
130  ostr << m_paths.front().m_Pathexp.ToUTF8();
131  wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
132  }
133 #endif
134 
135  return true;
136 }
std::list< SEARCH_PATH > m_paths
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:123
wxString m_Alias
wxString m_Pathvar
#define MASK_3D_RESOLVER
wxString m_Pathexp

References ExpandEnvVarSubstitutions(), PROJECT::GetProjectPath(), SEARCH_PATH::m_Alias, m_curProjDir, SEARCH_PATH::m_Pathexp, m_paths, SEARCH_PATH::m_Pathvar, m_project, and MASK_3D_RESOLVER.

Referenced by S3D_CACHE::SetProject().

◆ ShortenPath()

wxString FILENAME_RESOLVER::ShortenPath ( const wxString &  aFullPathName)

Produce a relative path based on the existing search directories or returns the same path if the path is not a superset of an existing search path.

Parameters
aFullPathNameis an absolute path to shorten.
Returns
the shortened path or aFullPathName.

Definition at line 731 of file filename_resolver.cpp.

732 {
733  wxString fname = aFullPathName;
734 
735  if( m_paths.empty() )
736  createPathList();
737 
738  std::lock_guard<std::mutex> lock( mutex_resolver );
739 
740  std::list< SEARCH_PATH >::const_iterator sL = m_paths.begin();
741  size_t idx;
742 
743  while( sL != m_paths.end() )
744  {
745  // undefined paths do not participate in the
746  // file name shortening procedure
747  if( sL->m_Pathexp.empty() )
748  {
749  ++sL;
750  continue;
751  }
752 
753  wxFileName fpath;
754 
755  // in the case of aliases, ensure that we use the most recent definition
756  if( sL->m_Alias.StartsWith( "${" ) || sL->m_Alias.StartsWith( "$(" ) )
757  {
758  wxString tpath = ExpandEnvVarSubstitutions( sL->m_Alias, m_project );
759 
760  if( tpath.empty() )
761  {
762  ++sL;
763  continue;
764  }
765 
766  fpath.Assign( tpath, wxT( "" ) );
767  }
768  else
769  {
770  fpath.Assign( sL->m_Pathexp, wxT( "" ) );
771  }
772 
773  wxString fps = fpath.GetPathWithSep();
774  wxString tname;
775 
776  idx = fname.find( fps );
777 
778  if( idx == 0 )
779  {
780  fname = fname.substr( fps.size() );
781 
782  #ifdef _WIN32
783  // ensure only the '/' separator is used in the internal name
784  fname.Replace( wxT( "\\" ), wxT( "/" ) );
785  #endif
786 
787  if( sL->m_Alias.StartsWith( "${" ) || sL->m_Alias.StartsWith( "$(" ) )
788  {
789  // old style ENV_VAR
790  tname = sL->m_Alias;
791  tname.Append( "/" );
792  tname.append( fname );
793  }
794  else
795  {
796  // new style alias
797  tname = ":";
798  tname.append( sL->m_Alias );
799  tname.append( ":" );
800  tname.append( fname );
801  }
802 
803  return tname;
804  }
805 
806  ++sL;
807  }
808 
809 #ifdef _WIN32
810  // it is strange to convert an MSWin full path to use the
811  // UNIX separator but this is done for consistency and can
812  // be helpful even when transferring project files from
813  // MSWin to *NIX.
814  fname.Replace( wxT( "\\" ), wxT( "/" ) );
815 #endif
816 
817  return fname;
818 }
std::list< SEARCH_PATH > m_paths
bool createPathList(void)
Build the path list using available information such as KICAD6_3DMODEL_DIR and the 3d_path_list confi...
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:273
static std::mutex mutex_resolver

References createPathList(), ExpandEnvVarSubstitutions(), m_paths, m_project, and mutex_resolver.

Referenced by DLG_SELECT_3DMODEL::TransferDataFromWindow().

◆ SplitAlias()

bool FILENAME_RESOLVER::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 aRelPath with the relative path.

Definition at line 828 of file filename_resolver.cpp.

830 {
831  anAlias.clear();
832  aRelPath.clear();
833 
834  if( !aFileName.StartsWith( wxT( ":" ) ) )
835  return false;
836 
837  size_t tagpos = aFileName.find( wxT( ":" ), 1 );
838 
839  if( wxString::npos == tagpos || 1 == tagpos )
840  return false;
841 
842  if( tagpos + 1 >= aFileName.length() )
843  return false;
844 
845  anAlias = aFileName.substr( 1, tagpos - 1 );
846  aRelPath = aFileName.substr( tagpos + 1 );
847 
848  return true;
849 }

Referenced by DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::OnAdd3DModel(), DIALOG_FOOTPRINT_PROPERTIES::OnAdd3DModel(), ResolvePath(), DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::TransferDataToWindow(), and DIALOG_FOOTPRINT_PROPERTIES::TransferDataToWindow().

◆ UpdatePathList()

bool FILENAME_RESOLVER::UpdatePathList ( const std::vector< SEARCH_PATH > &  aPathList)

Clear the current path list and substitutes the given path list and update the path configuration file on success.

Definition at line 227 of file filename_resolver.cpp.

228 {
229  wxUniChar envMarker( '$' );
230 
231  while( !m_paths.empty() && envMarker != *m_paths.back().m_Alias.rbegin() )
232  m_paths.pop_back();
233 
234  for( const SEARCH_PATH& path : aPathList )
235  addPath( path );
236 
237  return writePathList();
238 }
std::list< SEARCH_PATH > m_paths
bool writePathList(void)
Write the current path list to a configuration file.
bool addPath(const SEARCH_PATH &aPath)
Check that a path is valid and adds it to the search list.

References addPath(), m_paths, path, and writePathList().

Referenced by DIALOG_CONFIGURE_PATHS::TransferDataFromWindow().

◆ ValidateFileName()

bool FILENAME_RESOLVER::ValidateFileName ( const wxString &  aFileName,
bool &  hasAlias 
) const

Returns true if the given path is a valid aliased relative path.

If the path contains an alias then hasAlias is set true.

Definition at line 947 of file filename_resolver.cpp.

948 {
949  // Rules:
950  // 1. The generic form of an aliased 3D relative path is:
951  // ALIAS:relative/path
952  // 2. ALIAS is a UTF string excluding wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" )
953  // 3. The relative path must be a valid relative path for the platform
954  hasAlias = false;
955 
956  if( aFileName.empty() )
957  return false;
958 
959  wxString filename = aFileName;
960  size_t pos0 = aFileName.find( ':' );
961 
962  // ensure that the file separators suit the current platform
963  #ifdef __WINDOWS__
964  filename.Replace( wxT( "/" ), wxT( "\\" ) );
965 
966  // if we see the :\ pattern then it must be a drive designator
967  if( pos0 != wxString::npos )
968  {
969  size_t pos1 = filename.find( wxT( ":\\" ) );
970 
971  if( pos1 != wxString::npos && ( pos1 != pos0 || pos1 != 1 ) )
972  return false;
973 
974  // if we have a drive designator then we have no alias
975  if( pos1 != wxString::npos )
976  pos0 = wxString::npos;
977  }
978  #else
979  filename.Replace( wxT( "\\" ), wxT( "/" ) );
980  #endif
981 
982  // names may not end with ':'
983  if( pos0 == aFileName.length() -1 )
984  return false;
985 
986  if( pos0 != wxString::npos )
987  {
988  // ensure the alias component is not empty
989  if( pos0 == 0 )
990  return false;
991 
992  wxString lpath = filename.substr( 0, pos0 );
993 
994  // check the alias for restricted characters
995  if( wxString::npos != lpath.find_first_of( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
996  return false;
997 
998  hasAlias = true;
999  }
1000 
1001  return true;
1002 }

Referenced by DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::On3DModelCellChanged(), and DIALOG_FOOTPRINT_PROPERTIES::On3DModelCellChanged().

◆ writePathList()

bool FILENAME_RESOLVER::writePathList ( void  )
private

Write the current path list to a configuration file.

Returns
true if the path list was not empty and was successfully written to the configuration file.

Definition at line 603 of file filename_resolver.cpp.

604 {
605  if( m_configDir.empty() )
606  {
607  std::ostringstream ostr;
608  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
609  wxString errmsg = _( "3D configuration directory is unknown" );
610  ostr << " * " << errmsg.ToUTF8();
611  wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
612  wxMessageBox( errmsg, _( "Write 3D search path list" ) );
613 
614  return false;
615  }
616 
617  // skip all ${ENV_VAR} alias names
618  std::list< SEARCH_PATH >::const_iterator sPL = m_paths.begin();
619 
620  while( sPL != m_paths.end() &&
621  ( sPL->m_Alias.StartsWith( "${" ) || sPL->m_Alias.StartsWith( "$(" ) ) )
622  ++sPL;
623 
624  wxFileName cfgpath( m_configDir, RESOLVER_CONFIG );
625  wxString cfgname = cfgpath.GetFullPath();
626  std::ofstream cfgFile;
627 
628  cfgFile.open( cfgname.ToUTF8(), std::ios_base::trunc );
629 
630  if( !cfgFile.is_open() )
631  {
632  std::ostringstream ostr;
633  ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
634  wxString errmsg = _( "Could not open configuration file" );
635  ostr << " * " << errmsg.ToUTF8() << " '" << cfgname.ToUTF8() << "'";
636  wxLogTrace( MASK_3D_RESOLVER, "%s\n", ostr.str().c_str() );
637  wxMessageBox( errmsg, _( "Write 3D search path list" ) );
638 
639  return false;
640  }
641 
642  cfgFile << "#V" << CFGFILE_VERSION << "\n";
643  std::string tstr;
644 
645  while( sPL != m_paths.end() )
646  {
647  tstr = sPL->m_Alias.ToUTF8();
648  cfgFile << "\"" << tstr.size() << ":" << tstr << "\",";
649  tstr = sPL->m_Pathvar.ToUTF8();
650  cfgFile << "\"" << tstr.size() << ":" << tstr << "\",";
651  tstr = sPL->m_Description.ToUTF8();
652  cfgFile << "\"" << tstr.size() << ":" << tstr << "\"\n";
653  ++sPL;
654  }
655 
656  bool bad = cfgFile.bad();
657  cfgFile.close();
658 
659  if( bad )
660  {
661  wxMessageBox( _( "Problems writing configuration file" ),
662  _( "Write 3D search path list" ) );
663 
664  return false;
665  }
666 
667  return true;
668 }
#define CFGFILE_VERSION
std::list< SEARCH_PATH > m_paths
#define RESOLVER_CONFIG
#define _(s)
Definition: 3d_actions.cpp:33
#define MASK_3D_RESOLVER

References _, CFGFILE_VERSION, m_configDir, m_paths, MASK_3D_RESOLVER, and RESOLVER_CONFIG.

Referenced by readPathList(), and UpdatePathList().

Member Data Documentation

◆ m_configDir

wxString FILENAME_RESOLVER::m_configDir
private

Definition at line 179 of file filename_resolver.h.

Referenced by createPathList(), readPathList(), Set3DConfigDir(), and writePathList().

◆ m_curProjDir

wxString FILENAME_RESOLVER::m_curProjDir
private

Definition at line 184 of file filename_resolver.h.

Referenced by createPathList(), GetProjectDir(), and SetProject().

◆ m_errflags

int FILENAME_RESOLVER::m_errflags
private

Definition at line 181 of file filename_resolver.h.

Referenced by FILENAME_RESOLVER(), and ResolvePath().

◆ m_paths

◆ m_pgm

PGM_BASE* FILENAME_RESOLVER::m_pgm
private

Definition at line 182 of file filename_resolver.h.

Referenced by GetKicadPaths(), and SetProgramBase().

◆ m_project

PROJECT* FILENAME_RESOLVER::m_project
private

The documentation for this class was generated from the following files: