29 #include <wx/filename.h> 31 #include <wx/msgdlg.h> 39 #define CFGFILE_VERSION 1 40 #define RESOLVER_CONFIG wxT( "3Dresolver.cfg" ) 43 #define ERRFLG_ALIAS (1) 44 #define ERRFLG_RELPATH (2) 45 #define ERRFLG_ENVPATH (4) 47 #define MASK_3D_RESOLVER wxT( "3D_RESOLVER" ) 51 static bool getHollerith(
const std::string& aString,
size_t& aIndex, wxString& aResult );
64 if( aConfigDir.empty() )
71 if( !cfgdir.DirExists() )
92 if( !projdir.DirExists() )
103 al.
m_Alias = wxT(
"${KIPRJMOD}" );
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();
171 lpath.
m_Alias = wxT(
"${KIPRJMOD}" );
176 wxUniChar psep = fndummy.GetPathSeparator();
177 std::list< wxString > epaths;
181 for(
const wxString& curr_path : epaths )
185 if( pathVal.empty() )
191 fndummy.Assign( pathVal, wxEmptyString );
214 std::list< SEARCH_PATH >::const_iterator sPL =
m_paths.begin();
218 wxLogTrace(
MASK_3D_RESOLVER, wxT(
" + %s : '%s'\n" ), (*sPL).m_Alias.GetData(),
219 (*sPL).m_Pathexp.GetData() );
230 wxUniChar envMarker(
'$' );
232 while( !
m_paths.empty() && envMarker != *
m_paths.back().m_Alias.rbegin() )
246 if( aFileName.empty() )
247 return wxEmptyString;
253 wxString tname = aFileName;
257 tname.Replace( wxT(
"/" ), wxT(
"\\" ) );
266 wxFileName tmpFN( tname );
270 if( tmpFN.FileExists() )
273 tname = tmpFN.GetFullPath();
277 if( aFileName.StartsWith( wxT(
"${" ) ) || aFileName.StartsWith( wxT(
"$(" ) ) )
285 if( aFileName.StartsWith( wxT(
"${" ) ) || aFileName.StartsWith( wxT(
"$(" ) ) )
290 wxString errmsg = wxT(
"[3D File Resolver] No such path; ensure the environment var is defined" );
291 errmsg.append( wxT(
"\n" ) );
292 errmsg.append( tname );
293 errmsg.append( wxT(
"\n" ) );
297 return wxEmptyString;
309 if( !
m_paths.begin()->m_Pathexp.empty() && !tname.StartsWith( wxT(
":" ) ) )
311 tmpFN.Assign(
m_paths.begin()->m_Pathexp, wxEmptyString );
312 wxString fullPath = tmpFN.GetPathWithSep() + tname;
316 if( wxFileName::FileExists( fullPath ) )
318 tmpFN.Assign( fullPath );
320 tname = tmpFN.GetFullPath();
327 if( !tname.StartsWith( wxT(
":" ) ) )
330 wxString fullPath( wxT(
"${KICAD6_3DMODEL_DIR}" ) );
331 fullPath.Append( fpath.GetPathSeparator() );
332 fullPath.Append( tname );
334 fpath.Assign( fullPath );
336 if( fpath.Normalize() && fpath.FileExists() )
338 tname = fpath.GetFullPath();
355 wxString errmsg = wxT(
"[3D File Resolver] No such path" );
356 errmsg.append( wxT(
"\n" ) );
357 errmsg.append( tname );
358 errmsg.append( wxT(
"\n" ) );
362 return wxEmptyString;
368 if(
path.m_Alias.StartsWith( wxT(
"${" ) ) ||
path.m_Alias.StartsWith( wxT(
"$(" ) ) )
371 if(
path.m_Alias == alias && !
path.m_Pathexp.empty() )
373 wxFileName fpath( wxFileName::DirName(
path.m_Pathexp ) );
374 wxString fullPath = fpath.GetPathWithSep() + relpath;
378 if( wxFileName::FileExists( fullPath ) )
382 wxFileName tmp( fullPath );
384 if( tmp.Normalize() )
385 tname = tmp.GetFullPath();
395 wxString errmsg = wxT(
"[3D File Resolver] No such path; ensure the path alias is defined" );
396 errmsg.append( wxT(
"\n" ) );
397 errmsg.append( tname.substr( 1 ) );
398 errmsg.append( wxT(
"\n" ) );
402 return wxEmptyString;
416 while( tpath.
m_Pathvar.EndsWith( wxT(
"\\" ) ) )
427 if( !
path.DirExists() )
429 if( aPath.
m_Pathvar == wxT(
"${KICAD6_3DMODEL_DIR}" )
431 || aPath.
m_Pathvar == wxT(
"${KISYS3DMOD}" ) || aPath.
m_Pathvar == wxT(
"$(KISYS3DMOD)" ) )
437 wxString msg =
_(
"The given path does not exist" );
438 msg.append( wxT(
"\n" ) );
440 wxMessageBox( msg,
_(
"3D model search path" ) );
450 while( tpath.
m_Pathexp.EndsWith( wxT(
"\\" ) ) )
458 std::list< SEARCH_PATH >::iterator sPL =
m_paths.begin();
459 std::list< SEARCH_PATH >::iterator ePL =
m_paths.end();
463 if( tpath.
m_Alias == sPL->m_Alias )
465 wxString msg =
_(
"Alias: " );
467 msg.append( wxT(
"\n" ) );
468 msg.append(
_(
"This path:" ) + wxS(
" " ) );
470 msg.append( wxT(
"\n" ) );
471 msg.append(
_(
"Existing path:" ) + wxS(
" " ) );
472 msg.append( sPL->m_Pathvar );
473 wxMessageBox( msg,
_(
"Bad alias (duplicate name)" ) );
490 std::ostringstream ostr;
491 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
492 wxString errmsg = wxT(
"3D configuration directory is unknown" );
493 ostr << wxT(
" * " ) << errmsg.ToUTF8();
500 wxString cfgname = cfgpath.GetFullPath();
502 size_t nitems =
m_paths.size();
504 std::ifstream cfgFile;
507 if( !wxFileName::Exists( cfgname ) )
509 std::ostringstream ostr;
510 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
511 wxString errmsg = wxT(
"no 3D configuration file" );
512 ostr <<
" * " << errmsg.ToUTF8() <<
" '";
513 ostr << cfgname.ToUTF8() <<
"'";
518 cfgFile.open( cfgname.ToUTF8() );
520 if( !cfgFile.is_open() )
522 std::ostringstream ostr;
523 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
524 wxString errmsg = wxT(
"Could not open configuration file" );
525 ostr <<
" * " << errmsg.ToUTF8() <<
" '" << cfgname.ToUTF8() <<
"'";
535 while( cfgFile.good() )
538 std::getline( cfgFile, cfgLine );
541 if( cfgLine.empty() )
549 if( 1 == lineno && cfgLine.compare( 0, 2,
"#V" ) == 0 )
552 if( cfgLine.size() > 2 )
554 std::istringstream istr;
555 istr.str( cfgLine.substr( 2 ) );
569 if( al.
m_Alias == wxT(
"${KICAD6_3DMODEL_DIR}" )
570 || al.
m_Alias == wxT(
"${KIPRJMOD}" ) || al.
m_Alias == wxT(
"$(KIPRJMOD)" )
571 || al.
m_Alias == wxT(
"${KISYS3DMOD}" ) || al.
m_Alias == wxT(
"$(KISYS3DMOD)" ) )
590 return(
m_paths.size() != nitems );
599 std::ostringstream ostr;
600 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
601 wxString errmsg =
_(
"3D configuration directory is unknown" );
602 ostr <<
" * " << errmsg.ToUTF8();
604 wxMessageBox( errmsg,
_(
"Write 3D search path list" ) );
609 std::list<SEARCH_PATH>::const_iterator sPL =
m_paths.begin();
616 && ( sPL->m_Alias.StartsWith( wxT(
"${" ) ) || sPL->m_Alias.StartsWith( wxT(
"$(" ) ) ) )
622 wxFileName cfgpath( aDir, aFilename );
623 wxString cfgname = cfgpath.GetFullPath();
624 std::ofstream cfgFile;
626 cfgFile.open( cfgname.ToUTF8(), std::ios_base::trunc );
628 if( !cfgFile.is_open() )
630 std::ostringstream ostr;
631 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
632 wxString errmsg =
_(
"Could not open configuration file" );
633 ostr <<
" * " << errmsg.ToUTF8() <<
" '" << cfgname.ToUTF8() <<
"'";
635 wxMessageBox( errmsg,
_(
"Write 3D search path list" ) );
645 tstr = sPL->m_Alias.ToUTF8();
646 cfgFile <<
"\"" << tstr.size() <<
":" << tstr <<
"\",";
651 tstr = sPL->m_Pathvar.ToUTF8();
653 cfgFile <<
"\"" << tstr.size() <<
":" << tstr <<
"\",";
655 tstr = sPL->m_Description.ToUTF8();
656 cfgFile <<
"\"" << tstr.size() <<
":" << tstr <<
"\"\n";
661 bool bad = cfgFile.bad();
666 wxMessageBox(
_(
"Problems writing configuration file" ),
667 _(
"Write 3D search path list" ) );
678 bool useParen =
false;
680 if( aPath.StartsWith( wxT(
"$(" ) ) )
682 else if( !aPath.StartsWith( wxT(
"${" ) ) )
688 pEnd = aPath.find( wxT(
")" ) );
690 pEnd = aPath.find( wxT(
"}" ) );
692 if( pEnd == wxString::npos )
695 wxString envar = aPath.substr( 0, pEnd + 1 );
704 if( sPL->m_Alias == envar )
707 if( !sPL->m_Alias.StartsWith( wxT(
"${" ) ) )
718 wxUniChar psep = tmpFN.GetPathSeparator();
721 if( !tmpFN.DirExists() )
738 wxString fname = aFullPathName;
745 std::list< SEARCH_PATH >::const_iterator sL =
m_paths.begin();
752 if( sL->m_Pathexp.empty() )
761 if( sL->m_Alias.StartsWith( wxT(
"${" ) ) || sL->m_Alias.StartsWith( wxT(
"$(" ) ) )
771 fpath.Assign( tpath, wxEmptyString );
775 fpath.Assign( sL->m_Pathexp, wxEmptyString );
778 wxString fps = fpath.GetPathWithSep();
781 idx = fname.find( fps );
785 fname = fname.substr( fps.size() );
789 fname.Replace( wxT(
"\\" ), wxT(
"/" ) );
792 if( sL->m_Alias.StartsWith( wxT(
"${" ) ) || sL->m_Alias.StartsWith( wxT(
"$(" ) ) )
796 tname.Append( wxT(
"/" ) );
797 tname.append( fname );
803 tname.append( sL->m_Alias );
804 tname.append( wxT(
":" ) );
805 tname.append( fname );
819 fname.Replace( wxT(
"\\" ), wxT(
"/" ) );
834 wxString& anAlias, wxString& aRelPath )
const 839 size_t searchStart = 0;
841 if( aFileName.StartsWith( wxT(
":" ) ) )
844 size_t tagpos = aFileName.find( wxT(
":" ), searchStart );
846 if( tagpos == wxString::npos || tagpos == searchStart )
849 if( tagpos + 1 >= aFileName.length() )
852 anAlias = aFileName.substr( searchStart, tagpos - searchStart );
853 aRelPath = aFileName.substr( tagpos + 1 );
859 static bool getHollerith(
const std::string& aString,
size_t& aIndex, wxString& aResult )
863 if( aIndex >= aString.size() )
865 std::ostringstream ostr;
866 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
867 wxString errmsg = wxT(
"bad Hollerith string on line" );
868 ostr <<
" * " << errmsg.ToUTF8() <<
"\n'" << aString <<
"'";
874 size_t i2 = aString.find(
'"', aIndex );
876 if( std::string::npos == i2 )
878 std::ostringstream ostr;
879 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
880 wxString errmsg = wxT(
"missing opening quote mark in config file" );
881 ostr <<
" * " << errmsg.ToUTF8() <<
"\n'" << aString <<
"'";
889 if( i2 >= aString.size() )
891 std::ostringstream ostr;
892 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
893 wxString errmsg = wxT(
"invalid entry (unexpected end of line)" );
894 ostr <<
" * " << errmsg.ToUTF8() <<
"\n'" << aString <<
"'";
902 while( aString[i2] >=
'0' && aString[i2] <=
'9' )
903 tnum.append( 1, aString[i2++] );
905 if( tnum.empty() || aString[i2++] !=
':' )
907 std::ostringstream ostr;
908 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
909 wxString errmsg = wxT(
"bad Hollerith string on line" );
910 ostr <<
" * " << errmsg.ToUTF8() <<
"\n'" << aString <<
"'";
916 std::istringstream istr;
921 if( (i2 + nchars) >= aString.size() )
923 std::ostringstream ostr;
924 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
925 wxString errmsg = wxT(
"invalid entry (unexpected end of line)" );
926 ostr <<
" * " << errmsg.ToUTF8() <<
"\n'" << aString <<
"'";
934 aResult = wxString::FromUTF8( aString.substr( i2, nchars ).c_str() );
938 if( i2 >= aString.size() || aString[i2] !=
'"' )
940 std::ostringstream ostr;
941 ostr << __FILE__ <<
": " << __FUNCTION__ <<
": " << __LINE__ <<
"\n";
942 wxString errmsg = wxT(
"missing closing quote mark in config file" );
943 ostr <<
" * " << errmsg.ToUTF8() <<
"\n'" << aString <<
"'";
963 if( aFileName.empty() )
966 wxString filename = aFileName;
968 size_t aliasStart = aFileName.StartsWith(
':' ) ? 1 : 0;
969 size_t aliasEnd = aFileName.find(
':', aliasStart );
973 filename.Replace( wxT(
"/" ), wxT(
"\\" ) );
976 if( aliasEnd != wxString::npos )
978 size_t pos1 = filename.find( wxT(
":\\" ) );
980 if( pos1 != wxString::npos && ( pos1 != aliasEnd || pos1 != 1 ) )
984 if( pos1 != wxString::npos )
985 aliasEnd = wxString::npos;
988 filename.Replace( wxT(
"\\" ), wxT(
"/" ) );
992 if( aliasEnd == aFileName.length() -1 )
995 if( aliasEnd != wxString::npos )
998 if( aliasEnd == aliasStart )
1001 lpath = filename.substr( aliasStart, aliasEnd );
1004 if( wxString::npos != lpath.find_first_of( wxT(
"{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
1008 lpath = aFileName.substr( aliasEnd + 1 );
1016 aliasEnd = wxString::npos;
1018 if( aFileName.StartsWith( wxT(
"${" ) ) )
1019 aliasEnd = aFileName.find(
'}' );
1020 else if( aFileName.StartsWith( wxT(
"$(" ) ) )
1021 aliasEnd = aFileName.find(
')' );
1023 if( aliasEnd != wxString::npos )
1024 lpath = aFileName.substr( aliasEnd + 1 );
1032 wxString lpath_no_sep = lpath;
1034 lpath_no_sep.Replace( wxT(
"/" ), wxT(
" " ) );
1035 lpath_no_sep.Replace( wxT(
"\\" ), wxT(
" " ) );
1038 if( lpath_no_sep.Length() > 1 && lpath_no_sep[1] ==
':' )
1039 lpath_no_sep[1] =
' ';
1042 if( wxString::npos != lpath_no_sep.find_first_of( wxFileName::GetForbiddenChars() ) )
1056 bool hasKisys3D =
false;
1067 if( mS->first == wxString( wxT(
"KICAD_PTEMPLATES" ) )
1068 || mS->first == wxString( wxT(
"KICAD6_FOOTPRINT_DIR" ) ) )
1074 if( wxString::npos != mS->second.GetValue().find( wxString( wxT(
"://" ) ) ) )
1080 wxString tmp( wxT(
"${" ) );
1081 tmp.Append( mS->first );
1082 tmp.Append( wxT(
"}" ) );
1083 paths.push_back( tmp );
1085 if( tmp == wxT(
"${KICAD6_3DMODEL_DIR}" ) )
1092 paths.emplace_back(wxT(
"${KICAD6_3DMODEL_DIR}" ) );
std::list< SEARCH_PATH > m_paths
Container for project specific data.
bool readPathList(void)
Read a list of path names from a configuration file.
Container for data for KiCad programs.
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...
bool WritePathList(const wxString &aDir, const wxString &aFilename, bool aResolvePaths)
Write the current path list to a config file.
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...
static bool getHollerith(const std::string &aString, size_t &aIndex, wxString &aResult)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
virtual const wxString GetProjectPath() const
Return the full path of the project.
bool ValidateFileName(const wxString &aFileName, bool &hasAlias) const
Returns true if the given path is a valid aliased relative path.
bool addPath(const SEARCH_PATH &aPath)
Check that a path is valid and adds it to the search list.
wxString ShortenPath(const wxString &aFullPathName)
Produce a relative path based on the existing search directories or returns the same path if the path...
const std::list< SEARCH_PATH > * GetPaths() const
Return a pointer to the internal path list; the items in:load.
wxLogTrace helper definitions.
wxString ResolvePath(const wxString &aFileName)
Determines the full path of the given file name.
static std::mutex mutex_resolver
bool SetProject(PROJECT *aProject, bool *flgChanged=nullptr)
Set the current KiCad project directory as the first entry in the model path list.
bool Set3DConfigDir(const wxString &aConfigDir)
Set the user's configuration directory for 3D models.
bool GetKicadPaths(std::list< wxString > &paths) const
Return a list of path environment variables local to KiCad.
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 ...
void SetProgramBase(PGM_BASE *aBase)
Set a pointer to the application's PGM_BASE instance used to extract the local env vars.
std::map< wxString, ENV_VAR_ITEM >::const_iterator ENV_VAR_MAP_CITER
wxString GetProjectDir() const
virtual ENV_VAR_MAP & GetLocalEnvVariables() const