KiCad PCB EDA Suite
FILENAME_RESOLVER Class Reference

#include <filename_resolver.h>

Public Member Functions

 FILENAME_RESOLVER ()
 
bool Set3DConfigDir (const wxString &aConfigDir)
 Function Set3DConfigDir sets the user's configuration directory for 3D models. More...
 
bool SetProject (PROJECT *aProject, bool *flgChanged=NULL)
 Function SetProjectDir sets the current KiCad project directory as the first entry in the model path list. More...
 
wxString GetProjectDir (void)
 
void SetProgramBase (PGM_BASE *aBase)
 Function SetProgramBase sets a pointer to the application's PGM_BASE instance; the pointer is used to extract the local env vars. More...
 
bool UpdatePathList (std::vector< SEARCH_PATH > &aPathList)
 Function UpdatePathList clears the current path list and substitutes the given path list, updating the path configuration file on success. More...
 
wxString ResolvePath (const wxString &aFileName)
 Function ResolvePath determines the full path of the given file name. More...
 
wxString ShortenPath (const wxString &aFullPathName)
 Function ShortenPath produces 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 (void)
 Function GetPaths returns a pointer to the internal path list; the items in:load. More...
 
bool SplitAlias (const wxString &aFileName, wxString &anAlias, wxString &aRelPath)
 Function SplitAlias returns 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)
 Function ValidateName returns true if the given path is a valid aliased relative path. More...
 
bool GetKicadPaths (std::list< wxString > &paths)
 Function GetKicadPaths returns a list of path environment variables local to Kicad; this list always includes KISYS3DMOD even if it is not defined locally. More...
 

Private Member Functions

bool createPathList (void)
 Function createPathList builds the path list using available information such as KISYS3DMOD and the 3d_path_list configuration file. More...
 
bool addPath (const SEARCH_PATH &aPath)
 Function addPath checks that a path is valid and adds it to the search list. More...
 
bool readPathList (void)
 Function readPathList reads a list of path names from a configuration file. More...
 
bool writePathList (void)
 Function writePathList writes the current path list to a configuration file. More...
 
void checkEnvVarPath (const wxString &aPath)
 Function checkEnvVarPath checks 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

Definition at line 51 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

Function addPath checks 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 426 of file filename_resolver.cpp.

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

References _, ExpandEnvVarSubstitutions(), SEARCH_PATH::m_alias, SEARCH_PATH::m_pathexp, m_Paths, SEARCH_PATH::m_pathvar, m_project, and mutex_resolver.

Referenced by readPathList(), and UpdatePathList().

◆ checkEnvVarPath()

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

Function checkEnvVarPath checks 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 674 of file filename_resolver.cpp.

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

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

Function createPathList builds the path list using available information such as KISYS3DMOD and the 3d_path_list configuration file.

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 }
bool readPathList(void)
Function readPathList reads a list of path names from a configuration file.
wxString m_alias
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:574
bool GetKicadPaths(std::list< wxString > &paths)
Function GetKicadPaths returns a list of path environment variables local to Kicad; this list always ...
wxString m_pathexp
wxString m_pathvar
std::list< SEARCH_PATH > m_Paths
#define MASK_3D_RESOLVER

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)

Function GetKicadPaths returns a list of path environment variables local to Kicad; this list always includes KISYS3DMOD even if it is not defined locally.

Definition at line 1008 of file filename_resolver.cpp.

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

References PGM_BASE::GetLocalEnvVariables(), and m_pgm.

Referenced by createPathList().

◆ GetPaths()

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

Function GetPaths returns 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 825 of file filename_resolver.cpp.

826 {
827  return &m_Paths;
828 }
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 ( void  )

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

Function readPathList reads 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 503 of file filename_resolver.cpp.

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

Function ResolvePath 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 243 of file filename_resolver.cpp.

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

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

Referenced by export_vrml_module(), idf_export_module(), and S3D_CACHE::load().

◆ Set3DConfigDir()

bool FILENAME_RESOLVER::Set3DConfigDir ( const wxString &  aConfigDir)

Function Set3DConfigDir sets 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)
Function createPathList builds the path list using available information such as KISYS3DMOD and the 3...
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:574

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

Referenced by S3D_CACHE::Set3DConfigDir().

◆ SetProgramBase()

void FILENAME_RESOLVER::SetProgramBase ( PGM_BASE aBase)

Function SetProgramBase sets a pointer to the application's PGM_BASE instance; the pointer is 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 }
bool createPathList(void)
Function createPathList builds the path list using available information such as KISYS3DMOD and the 3...
std::list< SEARCH_PATH > m_Paths

References createPathList(), m_Paths, and m_pgm.

Referenced by S3D_CACHE::SetProgramBase().

◆ SetProject()

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

Function SetProjectDir sets 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 }
wxString m_alias
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:574
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:123
wxString m_pathexp
wxString m_pathvar
std::list< SEARCH_PATH > m_Paths
#define MASK_3D_RESOLVER

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)

Function ShortenPath produces 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 734 of file filename_resolver.cpp.

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

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 
)

Function SplitAlias returns 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 831 of file filename_resolver.cpp.

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

Referenced by DIALOG_FOOTPRINT_FP_EDITOR::OnAdd3DModel(), DIALOG_FOOTPRINT_BOARD_EDITOR::OnAdd3DModel(), ResolvePath(), DIALOG_FOOTPRINT_FP_EDITOR::TransferDataToWindow(), and DIALOG_FOOTPRINT_BOARD_EDITOR::TransferDataToWindow().

◆ UpdatePathList()

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

Function UpdatePathList clears the current path list and substitutes the given path list, updating 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  size_t nI = aPathList.size();
235 
236  for( size_t i = 0; i < nI; ++i )
237  addPath( aPathList[i] );
238 
239  return writePathList();
240 }
bool writePathList(void)
Function writePathList writes the current path list to a configuration file.
bool addPath(const SEARCH_PATH &aPath)
Function addPath checks that a path is valid and adds it to the search list.
std::list< SEARCH_PATH > m_Paths

References addPath(), m_Paths, and writePathList().

Referenced by DIALOG_CONFIGURE_PATHS::TransferDataFromWindow().

◆ ValidateFileName()

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

Function ValidateName 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 950 of file filename_resolver.cpp.

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

Referenced by DIALOG_FOOTPRINT_FP_EDITOR::On3DModelCellChanged(), and DIALOG_FOOTPRINT_BOARD_EDITOR::On3DModelCellChanged().

◆ writePathList()

bool FILENAME_RESOLVER::writePathList ( void  )
private

Function writePathList writes 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 606 of file filename_resolver.cpp.

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

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

◆ m_curProjDir

wxString FILENAME_RESOLVER::m_curProjDir
private

Definition at line 59 of file filename_resolver.h.

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

◆ m_errflags

int FILENAME_RESOLVER::m_errflags
private

Definition at line 56 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 57 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: