9 changed files with 11 additions and 1111 deletions
-
82common/filename_resolver.cpp
-
7include/filename_resolver.h
-
5pcbnew/dialogs/dialog_export_step.cpp
-
2pcbnew/exporters/step/CMakeLists.txt
-
797pcbnew/exporters/step/pcb/3d_resolver.cpp
-
203pcbnew/exporters/step/pcb/3d_resolver.h
-
18pcbnew/exporters/step/pcb/kicadfootprint.cpp
-
4pcbnew/exporters/step/pcb/kicadfootprint.h
-
4pcbnew/exporters/step/pcb/kicadpcb.h
@ -1,797 +0,0 @@ |
|||
/*
|
|||
* This program source code file is part kicad2mcad |
|||
* |
|||
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com> |
|||
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <cstdlib>
|
|||
#include <cstring>
|
|||
#include <fstream>
|
|||
#include <iostream>
|
|||
#include <mutex>
|
|||
#include <sstream>
|
|||
|
|||
#include <wx/fileconf.h>
|
|||
#include <wx/log.h>
|
|||
#include <wx/thread.h>
|
|||
#include <wx/msgdlg.h>
|
|||
#include <wx/stdpaths.h>
|
|||
|
|||
#include <wx_filename.h>
|
|||
|
|||
#include "3d_resolver.h"
|
|||
|
|||
// configuration file version
|
|||
#define CFGFILE_VERSION 1
|
|||
#define S3D_RESOLVER_CONFIG "ExportPaths.cfg"
|
|||
|
|||
// flag bits used to track different one-off messages to users
|
|||
#define ERRFLG_ALIAS (1)
|
|||
#define ERRFLG_RELPATH (2)
|
|||
#define ERRFLG_ENVPATH (4)
|
|||
|
|||
|
|||
/**
|
|||
* Flag to enable plugin loader trace output. |
|||
* |
|||
* @ingroup trace_env_vars |
|||
*/ |
|||
const wxChar* const trace3dResolver = wxT( "KICAD_3D_RESOLVER" ); |
|||
|
|||
|
|||
static std::mutex mutex3D_resolver; |
|||
|
|||
|
|||
static bool getHollerith( const std::string& aString, size_t& aIndex, wxString& aResult ); |
|||
|
|||
|
|||
S3D_RESOLVER::S3D_RESOLVER() |
|||
{ |
|||
m_errflags = 0; |
|||
} |
|||
|
|||
|
|||
bool S3D_RESOLVER::Set3DConfigDir( const wxString& aConfigDir ) |
|||
{ |
|||
createPathList(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool S3D_RESOLVER::createPathList( void ) |
|||
{ |
|||
if( !m_Paths.empty() ) |
|||
return true; |
|||
|
|||
readPathList(); |
|||
|
|||
if( m_Paths.empty() ) |
|||
return false; |
|||
|
|||
#ifdef DEBUG
|
|||
wxLogTrace( trace3dResolver, wxT( " * [3D model] search paths:\n" ) ); |
|||
|
|||
for( const SEARCH_PATH& searchPath : m_Paths ) |
|||
wxLogTrace( trace3dResolver, wxT( " + '%s'\n" ), searchPath.m_Pathexp ); |
|||
#endif
|
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
wxString S3D_RESOLVER::ResolvePath( const wxString& aFileName, |
|||
std::vector<wxString>& aSearchedPaths ) |
|||
{ |
|||
std::lock_guard<std::mutex> lock( mutex3D_resolver ); |
|||
|
|||
if( aFileName.empty() ) |
|||
return wxEmptyString; |
|||
|
|||
if( m_Paths.empty() ) |
|||
createPathList(); |
|||
|
|||
// look up the filename in the internal filename map
|
|||
std::map<wxString, wxString, S3D::rsort_wxString>::iterator mi; |
|||
mi = m_NameMap.find( aFileName ); |
|||
|
|||
if( mi != m_NameMap.end() ) |
|||
return mi->second; |
|||
|
|||
// first attempt to use the name as specified:
|
|||
wxString tname = aFileName; |
|||
|
|||
#ifdef _WIN32
|
|||
// translate from KiCad's internal UNIX-like path to MSWin paths
|
|||
tname.Replace( wxT( "/" ), wxT( "\\" ) ); |
|||
#endif
|
|||
|
|||
// Note: variable expansion must preferably be performed via a threadsafe wrapper for the
|
|||
// getenv() system call. If we allow the wxFileName::Normalize() routine to perform expansion
|
|||
// then we will have a race condition since wxWidgets does not assure a threadsafe wrapper
|
|||
// for getenv().
|
|||
if( tname.StartsWith( wxT( "${" ) ) || tname.StartsWith( wxT( "$(" ) ) ) |
|||
tname = expandVars( tname ); |
|||
|
|||
wxFileName tmpFN( tname ); |
|||
|
|||
// in the case of absolute filenames we don't store a map item
|
|||
if( !aFileName.StartsWith( wxT( "${" ) ) && !aFileName.StartsWith( wxT( "$(" ) ) |
|||
&& tmpFN.IsAbsolute() ) |
|||
{ |
|||
if( tmpFN.FileExists() ) |
|||
{ |
|||
tmpFN.Normalize( FN_NORMALIZE_FLAGS ); |
|||
return tmpFN.GetFullPath(); |
|||
} |
|||
else |
|||
{ |
|||
aSearchedPaths.push_back( tmpFN.GetFullPath() ); |
|||
} |
|||
} |
|||
|
|||
// this case covers full paths, leading expanded vars, and paths relative to the current
|
|||
// working directory (which is not necessarily the current project directory)
|
|||
tmpFN.Normalize( FN_NORMALIZE_FLAGS ); |
|||
|
|||
if( tmpFN.FileExists() ) |
|||
{ |
|||
tname = tmpFN.GetFullPath(); |
|||
m_NameMap[ aFileName ] = tname; |
|||
|
|||
// special case: if a path begins with ${ENV_VAR} but is not in the resolver's path list
|
|||
// then add it
|
|||
if( aFileName.StartsWith( wxT( "${" ) ) || aFileName.StartsWith( wxT( "$(" ) ) ) |
|||
checkEnvVarPath( aFileName ); |
|||
|
|||
return tname; |
|||
} |
|||
else if( tmpFN.GetFullPath() != aFileName ) |
|||
{ |
|||
aSearchedPaths.push_back( tmpFN.GetFullPath() ); |
|||
} |
|||
|
|||
// if a path begins with ${ENV_VAR}/$(ENV_VAR) and is not resolved then the file either does
|
|||
// not exist or the ENV_VAR is not defined
|
|||
if( aFileName.StartsWith( wxT( "${" ) ) || aFileName.StartsWith( wxT( "$(" ) ) ) |
|||
{ |
|||
m_errflags |= ERRFLG_ENVPATH; |
|||
return aFileName; |
|||
} |
|||
|
|||
// at this point aFileName is:
|
|||
// a. an aliased shortened name or
|
|||
// b. cannot be determined
|
|||
|
|||
// check the path relative to the current project directory;
|
|||
// NB: this is not necessarily the same as the current working directory, which has already
|
|||
// been checked. This case accounts for partial paths which do not contain ${KIPRJMOD}.
|
|||
// This check is performed before checking the path relative to ${KICAD6_3DMODEL_DIR} so that
|
|||
// users can potentially override a model within ${KICAD6_3DMODEL_DIR}.
|
|||
if( !m_Paths.empty() && !m_Paths.begin()->m_Pathexp.empty() && !tname.StartsWith( wxT( ":" ) ) ) |
|||
{ |
|||
tmpFN.Assign( m_Paths.begin()->m_Pathexp, wxT( "" ) ); |
|||
wxString fullPath = tmpFN.GetPathWithSep() + tname; |
|||
|
|||
if( fullPath.StartsWith( wxT( "${" ) ) || fullPath.StartsWith( wxT( "$(" ) ) ) |
|||
fullPath = expandVars( fullPath ); |
|||
|
|||
tmpFN.Assign( fullPath ); |
|||
tmpFN.Normalize( FN_NORMALIZE_FLAGS ); |
|||
|
|||
if( tmpFN.FileExists() ) |
|||
{ |
|||
tname = tmpFN.GetFullPath(); |
|||
m_NameMap[ aFileName ] = tname; |
|||
return tname; |
|||
} |
|||
else if( tmpFN.GetFullPath() != aFileName ) |
|||
{ |
|||
aSearchedPaths.push_back( tmpFN.GetFullPath() ); |
|||
} |
|||
} |
|||
|
|||
// check the partial path relative to ${KICAD6_3DMODEL_DIR} (legacy behavior)
|
|||
if( !tname.Contains( wxT( ":" ) ) ) |
|||
{ |
|||
wxFileName fpath; |
|||
wxString fullPath( wxT( "${KICAD6_3DMODEL_DIR}" ) ); |
|||
fullPath.Append( fpath.GetPathSeparator() ); |
|||
fullPath.Append( tname ); |
|||
fullPath = expandVars( fullPath ); |
|||
fpath.Assign( fullPath ); |
|||
fpath.Normalize( FN_NORMALIZE_FLAGS ); |
|||
|
|||
if( fpath.FileExists() ) |
|||
{ |
|||
tname = fpath.GetFullPath(); |
|||
m_NameMap[ aFileName ] = tname; |
|||
return tname; |
|||
} |
|||
else |
|||
{ |
|||
aSearchedPaths.push_back( fpath.GetFullPath() ); |
|||
} |
|||
} |
|||
|
|||
// at this point the filename must contain an alias or else it is invalid
|
|||
wxString alias; // the alias portion of the short filename
|
|||
wxString relpath; // the path relative to the alias
|
|||
|
|||
if( !SplitAlias( tname, alias, relpath ) ) |
|||
{ |
|||
// this can happen if the file was intended to be relative to ${KICAD6_3DMODEL_DIR}
|
|||
// but ${KICAD6_3DMODEL_DIR} is not set or is incorrect.
|
|||
m_errflags |= ERRFLG_RELPATH; |
|||
return aFileName; |
|||
} |
|||
|
|||
for( const SEARCH_PATH& path : m_Paths ) |
|||
{ |
|||
// ${ENV_VAR} paths have already been checked; skip them
|
|||
if( path.m_Alias.StartsWith( wxT( "${" ) ) || path.m_Alias.StartsWith( wxT( "$(" ) ) ) |
|||
continue; |
|||
|
|||
if( path.m_Alias == alias && !path.m_Pathexp.empty() ) |
|||
{ |
|||
wxFileName fpath( wxFileName::DirName( path.m_Pathexp ) ); |
|||
wxString fullPath = fpath.GetPathWithSep() + relpath; |
|||
|
|||
if( fullPath.StartsWith( wxT( "${" ) ) || fullPath.StartsWith( wxT( "$(" ) ) ) |
|||
fullPath = expandVars( fullPath ); |
|||
|
|||
wxFileName tmp( fullPath ); |
|||
tmp.Normalize( FN_NORMALIZE_FLAGS ); |
|||
|
|||
if( tmp.FileExists() ) |
|||
{ |
|||
tname = tmp.GetFullPath(); |
|||
m_NameMap[ aFileName ] = tname; |
|||
return tname; |
|||
} |
|||
else |
|||
{ |
|||
aSearchedPaths.push_back( tmp.GetFullPath() ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
m_errflags |= ERRFLG_ALIAS; |
|||
return aFileName; |
|||
} |
|||
|
|||
|
|||
bool S3D_RESOLVER::addPath( const SEARCH_PATH& aPath ) |
|||
{ |
|||
if( aPath.m_Alias.empty() || aPath.m_Pathvar.empty() ) |
|||
return false; |
|||
|
|||
std::lock_guard<std::mutex> lock( mutex3D_resolver ); |
|||
|
|||
SEARCH_PATH tpath = aPath; |
|||
|
|||
#ifdef _WIN32
|
|||
while( tpath.m_Pathvar.EndsWith( wxT( "\\" ) ) ) |
|||
tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 ); |
|||
#else
|
|||
while( tpath.m_Pathvar.EndsWith( wxT( "/" ) ) && tpath.m_Pathvar.length() > 1 ) |
|||
tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 ); |
|||
#endif
|
|||
|
|||
wxFileName path( tpath.m_Pathvar, wxT( "" ) ); |
|||
path.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS ); |
|||
|
|||
if( !path.DirExists() ) |
|||
{ |
|||
// Show a message only in debug mode
|
|||
#ifdef DEBUG
|
|||
if( aPath.m_Pathvar == wxT( "${KICAD6_3DMODEL_DIR}" ) |
|||
|| aPath.m_Pathvar == wxT( "${KIPRJMOD}" ) |
|||
|| aPath.m_Pathvar == wxT( "${KISYS3DMOD}" ) ) |
|||
{ |
|||
// suppress the message if the missing pathvar is a system variable
|
|||
} |
|||
else |
|||
{ |
|||
wxString msg = _( "The given path does not exist" ); |
|||
msg.append( wxT( "\n" ) ); |
|||
msg.append( tpath.m_Pathvar ); |
|||
wxLogMessage( wxT( "%s\n" ), msg.ToUTF8() ); |
|||
} |
|||
#endif
|
|||
|
|||
tpath.m_Pathexp.clear(); |
|||
} |
|||
else |
|||
{ |
|||
tpath.m_Pathexp = path.GetFullPath(); |
|||
|
|||
#ifdef _WIN32
|
|||
while( tpath.m_Pathexp.EndsWith( wxT( "\\" ) ) ) |
|||
tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 ); |
|||
#else
|
|||
while( tpath.m_Pathexp.EndsWith( wxT( "/" ) ) && tpath.m_Pathexp.length() > 1 ) |
|||
tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 ); |
|||
#endif
|
|||
} |
|||
|
|||
std::list< SEARCH_PATH >::iterator sPL = m_Paths.begin(); |
|||
std::list< SEARCH_PATH >::iterator ePL = m_Paths.end(); |
|||
|
|||
while( sPL != ePL ) |
|||
{ |
|||
if( tpath.m_Alias == sPL->m_Alias ) |
|||
{ |
|||
wxString msg = _( "Alias:" ) + wxS( " " ); |
|||
msg.append( tpath.m_Alias ); |
|||
msg.append( wxS( "\n" ) ); |
|||
msg.append( _( "This path:" ) + wxS( " " ) ); |
|||
msg.append( tpath.m_Pathvar ); |
|||
msg.append( wxS( "\n" ) ); |
|||
msg.append( _( "Existing path:" ) + wxS( " " ) ); |
|||
msg.append( sPL->m_Pathvar ); |
|||
wxMessageBox( msg, _( "Bad alias (duplicate name)" ) ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
++sPL; |
|||
} |
|||
|
|||
m_Paths.push_back( tpath ); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool S3D_RESOLVER::readPathList( void ) |
|||
{ |
|||
wxFileName cfgpath( wxStandardPaths::Get().GetTempDir(), S3D_RESOLVER_CONFIG ); |
|||
cfgpath.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS ); |
|||
wxString cfgname = cfgpath.GetFullPath(); |
|||
|
|||
size_t nitems = m_Paths.size(); |
|||
|
|||
std::ifstream cfgFile; |
|||
std::string cfgLine; |
|||
|
|||
if( !wxFileName::Exists( cfgname ) ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * no 3D configuration file '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, cfgname ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
cfgFile.open( cfgname.ToUTF8() ); |
|||
|
|||
if( !cfgFile.is_open() ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * Could not open configuration file '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, cfgname ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
int lineno = 0; |
|||
SEARCH_PATH al; |
|||
size_t idx; |
|||
int vnum = 0; // version number
|
|||
|
|||
while( cfgFile.good() ) |
|||
{ |
|||
cfgLine.clear(); |
|||
std::getline( cfgFile, cfgLine ); |
|||
++lineno; |
|||
|
|||
if( cfgLine.empty() ) |
|||
{ |
|||
if( cfgFile.eof() ) |
|||
break; |
|||
|
|||
continue; |
|||
} |
|||
|
|||
if( 1 == lineno && cfgLine.compare( 0, 2, "#V" ) == 0 ) |
|||
{ |
|||
// extract the version number and parse accordingly
|
|||
if( cfgLine.size() > 2 ) |
|||
{ |
|||
std::istringstream istr; |
|||
istr.str( cfgLine.substr( 2 ) ); |
|||
istr >> vnum; |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
idx = 0; |
|||
|
|||
if( !getHollerith( cfgLine, idx, al.m_Alias ) ) |
|||
continue; |
|||
|
|||
if( !getHollerith( cfgLine, idx, al.m_Pathvar ) ) |
|||
continue; |
|||
|
|||
if( !getHollerith( cfgLine, idx, al.m_Description ) ) |
|||
continue; |
|||
|
|||
addPath( al ); |
|||
} |
|||
|
|||
cfgFile.close(); |
|||
|
|||
if( m_Paths.size() != nitems ) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
void S3D_RESOLVER::checkEnvVarPath( const wxString& aPath ) |
|||
{ |
|||
bool useParen = false; |
|||
|
|||
if( aPath.StartsWith( wxT( "$(" ) ) ) |
|||
useParen = true; |
|||
else if( !aPath.StartsWith( wxT( "${" ) ) ) |
|||
return; |
|||
|
|||
size_t pEnd; |
|||
|
|||
if( useParen ) |
|||
pEnd = aPath.find( wxT( ")" ) ); |
|||
else |
|||
pEnd = aPath.find( wxT( "}" ) ); |
|||
|
|||
if( pEnd == wxString::npos ) |
|||
return; |
|||
|
|||
wxString envar = aPath.substr( 0, pEnd + 1 ); |
|||
|
|||
// check if the alias exists; if not then add it to the end of the
|
|||
// env var section of the path list
|
|||
std::list< SEARCH_PATH >::iterator sPL = m_Paths.begin(); |
|||
std::list< SEARCH_PATH >::iterator ePL = m_Paths.end(); |
|||
|
|||
while( sPL != ePL ) |
|||
{ |
|||
if( sPL->m_Alias == envar ) |
|||
return; |
|||
|
|||
if( !sPL->m_Alias.StartsWith( wxT( "${" ) ) ) |
|||
break; |
|||
|
|||
++sPL; |
|||
} |
|||
|
|||
SEARCH_PATH lpath; |
|||
lpath.m_Alias = envar; |
|||
lpath.m_Pathvar = lpath.m_Alias; |
|||
wxFileName tmpFN( lpath.m_Alias, wxT( "" ) ); |
|||
wxUniChar psep = tmpFN.GetPathSeparator(); |
|||
tmpFN.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS ); |
|||
|
|||
if( !tmpFN.DirExists() ) |
|||
return; |
|||
|
|||
lpath.m_Pathexp = tmpFN.GetFullPath(); |
|||
|
|||
if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() ) |
|||
lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() ); |
|||
|
|||
if( lpath.m_Pathexp.empty() ) |
|||
return; |
|||
|
|||
m_Paths.insert( sPL, lpath ); |
|||
return; |
|||
} |
|||
|
|||
|
|||
wxString S3D_RESOLVER::expandVars( const wxString& aPath ) |
|||
{ |
|||
if( aPath.empty() ) |
|||
return wxEmptyString; |
|||
|
|||
wxString result; |
|||
|
|||
for( const std::pair<const wxString, wxString>& i : m_EnvVars ) |
|||
{ |
|||
if( !aPath.compare( 2, i.first.length(), i.first ) ) |
|||
{ |
|||
result = i.second; |
|||
result.append( aPath.substr( 3 + i.first.length() ) ); |
|||
|
|||
if( result.StartsWith( wxT( "${" ) ) || result.StartsWith( wxT( "$(" ) ) ) |
|||
result = expandVars( result ); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
result = wxExpandEnvVars( aPath ); |
|||
|
|||
if( result == aPath ) |
|||
return wxEmptyString; |
|||
|
|||
if( result.StartsWith( wxT( "${" ) ) || result.StartsWith( wxT( "$(" ) ) ) |
|||
result = expandVars( result ); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
|
|||
wxString S3D_RESOLVER::ShortenPath( const wxString& aFullPathName ) |
|||
{ |
|||
wxString fname = aFullPathName; |
|||
|
|||
if( m_Paths.empty() ) |
|||
createPathList(); |
|||
|
|||
std::lock_guard<std::mutex> lock( mutex3D_resolver ); |
|||
|
|||
std::list< SEARCH_PATH >::const_iterator sL = m_Paths.begin(); |
|||
std::list< SEARCH_PATH >::const_iterator eL = m_Paths.end(); |
|||
size_t idx; |
|||
|
|||
while( sL != eL ) |
|||
{ |
|||
// undefined paths do not participate in the file name shortening procedure.
|
|||
if( sL->m_Pathexp.empty() ) |
|||
{ |
|||
++sL; |
|||
continue; |
|||
} |
|||
|
|||
wxFileName fpath( sL->m_Pathexp, wxT( "" ) ); |
|||
wxString fps = fpath.GetPathWithSep(); |
|||
wxString tname; |
|||
|
|||
idx = fname.find( fps ); |
|||
|
|||
if( std::string::npos != idx && 0 == idx ) |
|||
{ |
|||
fname = fname.substr( fps.size() ); |
|||
|
|||
#ifdef _WIN32
|
|||
// ensure only the '/' separator is used in the internal name
|
|||
fname.Replace( wxT( "\\" ), wxT( "/" ) ); |
|||
#endif
|
|||
|
|||
if( sL->m_Alias.StartsWith( wxT( "${" ) ) || sL->m_Alias.StartsWith( wxT( "$(" ) ) ) |
|||
{ |
|||
// old style ENV_VAR
|
|||
tname = sL->m_Alias; |
|||
tname.Append( wxT( "/" ) ); |
|||
tname.append( fname ); |
|||
} |
|||
else |
|||
{ |
|||
// new style alias
|
|||
tname = wxT( ":" ); |
|||
tname.append( sL->m_Alias ); |
|||
tname.append( wxT( ":" ) ); |
|||
tname.append( fname ); |
|||
} |
|||
|
|||
return tname; |
|||
} |
|||
|
|||
++sL; |
|||
} |
|||
|
|||
#ifdef _WIN32
|
|||
// it is strange to convert an MSWin full path to use the
|
|||
// UNIX separator but this is done for consistency and can
|
|||
// be helpful even when transferring project files from
|
|||
// MSWin to *NIX.
|
|||
fname.Replace( wxT( "\\" ), wxT( "/" ) ); |
|||
#endif
|
|||
|
|||
return fname; |
|||
} |
|||
|
|||
|
|||
const std::list< SEARCH_PATH >* S3D_RESOLVER::GetPaths( void ) |
|||
{ |
|||
return &m_Paths; |
|||
} |
|||
|
|||
|
|||
bool S3D_RESOLVER::SplitAlias( const wxString& aFileName, wxString& anAlias, wxString& aRelPath ) |
|||
{ |
|||
anAlias.clear(); |
|||
aRelPath.clear(); |
|||
|
|||
size_t searchStart = 0; |
|||
|
|||
if( aFileName.StartsWith( wxT( ":" ) ) ) |
|||
searchStart = 1; |
|||
|
|||
size_t tagpos = aFileName.find( wxT( ":" ), searchStart ); |
|||
|
|||
if( tagpos == wxString::npos || tagpos == searchStart ) |
|||
return false; |
|||
|
|||
if( tagpos + 1 >= aFileName.length() ) |
|||
return false; |
|||
|
|||
anAlias = aFileName.substr( searchStart, tagpos - searchStart ); |
|||
aRelPath = aFileName.substr( tagpos + 1 ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
static bool getHollerith( const std::string& aString, size_t& aIndex, wxString& aResult ) |
|||
{ |
|||
aResult.clear(); |
|||
|
|||
if( aIndex >= aString.size() ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * Bad Hollerith string in line '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, aString ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
size_t i2 = aString.find( '"', aIndex ); |
|||
|
|||
if( std::string::npos == i2 ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * missing opening quote mark in line '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, aString ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
++i2; |
|||
|
|||
if( i2 >= aString.size() ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * unexpected end of line in line '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, aString ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
std::string tnum; |
|||
|
|||
while( aString[i2] >= '0' && aString[i2] <= '9' ) |
|||
tnum.append( 1, aString[i2++] ); |
|||
|
|||
if( tnum.empty() || aString[i2++] != ':' ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * Bad Hollerith string in line '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, aString ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
std::istringstream istr; |
|||
istr.str( tnum ); |
|||
size_t nchars; |
|||
istr >> nchars; |
|||
|
|||
if( ( i2 + nchars ) >= aString.size() ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * unexpected end of line in line '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, aString ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
if( nchars > 0 ) |
|||
{ |
|||
aResult = wxString::FromUTF8( aString.substr( i2, nchars ).c_str() ); |
|||
i2 += nchars; |
|||
} |
|||
|
|||
if( i2 >= aString.size() || aString[i2] != '"' ) |
|||
{ |
|||
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * missing closing quote mark in line '%s'" ), |
|||
__FILE__, __FUNCTION__, __LINE__, aString ); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
aIndex = i2 + 1; |
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool S3D_RESOLVER::ValidateFileName( const wxString& aFileName, bool& hasAlias ) |
|||
{ |
|||
// Rules:
|
|||
// 1. The generic form of an aliased 3D relative path is:
|
|||
// ALIAS:relative/path
|
|||
// 2. ALIAS is a UTF string excluding "{}[]()%~<>\"='`;:.,&?/\\|$"
|
|||
// 3. The relative path must be a valid relative path for the platform
|
|||
hasAlias = false; |
|||
|
|||
if( aFileName.empty() ) |
|||
return false; |
|||
|
|||
wxString filename = aFileName; |
|||
wxString lpath; |
|||
size_t aliasStart = aFileName.StartsWith( ':' ) ? 1 : 0; |
|||
size_t aliasEnd = aFileName.find( ':' ); |
|||
|
|||
// ensure that the file separators suit the current platform
|
|||
#ifdef __WINDOWS__
|
|||
filename.Replace( wxT( "/" ), wxT( "\\" ) ); |
|||
|
|||
// if we see the :\ pattern then it must be a drive designator
|
|||
if( aliasEnd != wxString::npos ) |
|||
{ |
|||
size_t pos1 = aFileName.find( wxT( ":\\" ) ); |
|||
|
|||
if( pos1 != wxString::npos && ( pos1 != aliasEnd || pos1 != 1 ) ) |
|||
return false; |
|||
|
|||
// if we have a drive designator then we have no alias
|
|||
if( pos1 != wxString::npos ) |
|||
aliasEnd = wxString::npos; |
|||
} |
|||
#else
|
|||
filename.Replace( wxT( "\\" ), wxT( "/" ) ); |
|||
#endif
|
|||
|
|||
// names may not end with ':'
|
|||
if( aliasEnd == aFileName.length() - 1 ) |
|||
return false; |
|||
|
|||
if( aliasEnd != wxString::npos ) |
|||
{ |
|||
// ensure the alias component is not empty
|
|||
if( aliasEnd == aliasStart ) |
|||
return false; |
|||
|
|||
lpath = filename.substr( aliasStart, aliasEnd ); |
|||
|
|||
// check the alias for restricted characters
|
|||
if( wxString::npos != lpath.find_first_of( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) |
|||
return false; |
|||
|
|||
hasAlias = true; |
|||
lpath = aFileName.substr( aliasEnd + 1 ); |
|||
} |
|||
else |
|||
{ |
|||
lpath = aFileName; |
|||
|
|||
// in the case of ${ENV_VAR}|$(ENV_VAR)/path, strip the
|
|||
// environment string before testing
|
|||
aliasEnd = wxString::npos; |
|||
|
|||
if( aFileName.StartsWith( wxT( "${" ) ) ) |
|||
aliasEnd = aFileName.find( '}' ); |
|||
else if( aFileName.StartsWith( wxT( "$(" ) ) ) |
|||
aliasEnd = aFileName.find( ')' ); |
|||
|
|||
if( aliasEnd != wxString::npos ) |
|||
lpath = aFileName.substr( aliasEnd + 1 ); |
|||
} |
|||
|
|||
if( wxString::npos != lpath.find_first_of( wxFileName::GetForbiddenChars() ) ) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
@ -1,203 +0,0 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com> |
|||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
|||
* or you may search the http://www.gnu.org website for the version 2 license, |
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
/** |
|||
* @file 3d_resolver.h |
|||
* provides an extensible class to resolve 3D model paths. |
|||
* Derived from 3d_filename_resolver.h,cpp and modified for |
|||
* use in stand-alone utilities. |
|||
*/ |
|||
|
|||
#ifndef RESOLVER_3D_H |
|||
#define RESOLVER_3D_H |
|||
|
|||
#include <list> |
|||
#include <map> |
|||
#include <vector> |
|||
#include <wx/string.h> |
|||
|
|||
namespace S3D |
|||
{ |
|||
struct rsort_wxString |
|||
{ |
|||
bool operator() (const wxString& strA, const wxString& strB ) const |
|||
{ |
|||
// sort a wxString using the reverse character order; for 3d model |
|||
// filenames this will typically be a much faster operation than |
|||
// a normal alphabetic sort |
|||
wxString::const_reverse_iterator sA = strA.rbegin(); |
|||
wxString::const_reverse_iterator eA = strA.rend(); |
|||
|
|||
wxString::const_reverse_iterator sB = strB.rbegin(); |
|||
wxString::const_reverse_iterator eB = strB.rend(); |
|||
|
|||
if( strA.empty() ) |
|||
{ |
|||
if( strB.empty() ) |
|||
return false; |
|||
|
|||
// note: this rule implies that a null string is first in the sort order |
|||
return true; |
|||
} |
|||
|
|||
if( strB.empty() ) |
|||
return false; |
|||
|
|||
while( sA != eA && sB != eB ) |
|||
{ |
|||
if( (*sA) == (*sB) ) |
|||
{ |
|||
++sA; |
|||
++sB; |
|||
continue; |
|||
} |
|||
|
|||
if( (*sA) < (*sB) ) |
|||
return true; |
|||
else |
|||
return false; |
|||
} |
|||
|
|||
if( sB == eB ) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
}; |
|||
|
|||
}; // end NAMESPACE |
|||
|
|||
|
|||
class KICADPCB; |
|||
|
|||
struct SEARCH_PATH |
|||
{ |
|||
wxString m_Alias; // alias to the base path |
|||
wxString m_Pathvar; // base path as stored in the config file |
|||
wxString m_Pathexp; // expanded base path |
|||
wxString m_Description; // description of the aliased path |
|||
}; |
|||
|
|||
|
|||
class S3D_RESOLVER |
|||
{ |
|||
public: |
|||
S3D_RESOLVER(); |
|||
|
|||
/** |
|||
* Set the user's configuration directory for 3D models. |
|||
* |
|||
* @param aConfigDir |
|||
* @return true if the call succeeds (directory exists). |
|||
*/ |
|||
bool Set3DConfigDir( const wxString& aConfigDir ); |
|||
|
|||
/** |
|||
* Determine 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 \a aFileName into the resolved name if the URI is valid. |
|||
* |
|||
* If the file is not found, \a aSearchedPaths will contain the paths that were searched. |
|||
*/ |
|||
wxString ResolvePath( const wxString& aFileName, std::vector<wxString>& aSearchedPaths ); |
|||
|
|||
/** |
|||
* 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. |
|||
* |
|||
* @param aFullPathName is an absolute path to shorten. |
|||
* @return the shortened path or aFullPathName. |
|||
*/ |
|||
wxString ShortenPath( const wxString& aFullPathName ); |
|||
|
|||
/** |
|||
* Return a pointer to the internal path list. |
|||
* |
|||
* The list can be used to set up the list of search paths available to a 3D file browser. |
|||
* |
|||
* @return the search path list. |
|||
*/ |
|||
const std::list< SEARCH_PATH >* GetPaths( void ); |
|||
|
|||
/** |
|||
* Return true if the given name contains an alias and populates the string with the alias |
|||
* and the relative path. |
|||
*/ |
|||
bool SplitAlias( const wxString& aFileName, wxString& anAlias, wxString& aRelPath ); |
|||
|
|||
/** |
|||
* If the path contains an alias then \a hasAlias is set true. |
|||
* |
|||
* @return true if the given path is a valid aliased relative path. |
|||
*/ |
|||
bool ValidateFileName( const wxString& aFileName, bool& hasAlias ); |
|||
|
|||
private: |
|||
/** |
|||
* Build the path list using available information such as KICAD6_3DMODEL_DIR and the |
|||
* 3d_path_list configuration file. |
|||
* |
|||
* Invalid paths are silently discarded and removed from the configuration file. |
|||
* |
|||
* @return true if at least one valid path was found |
|||
*/ |
|||
bool createPathList( void ); |
|||
|
|||
/** |
|||
* Check that \a aPath is valid and adds it to the search list. |
|||
* |
|||
* @param aPath is the alias set to be checked and added. |
|||
* @return true if \a aPath is valid. |
|||
*/ |
|||
bool addPath( const SEARCH_PATH& aPath ); |
|||
|
|||
/** |
|||
* Read a list of path names from a configuration file. |
|||
* |
|||
* @return true if a file was found and contained at least one valid path. |
|||
*/ |
|||
bool readPathList( void ); |
|||
|
|||
/** |
|||
* 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. |
|||
*/ |
|||
void checkEnvVarPath( const wxString& aPath ); |
|||
|
|||
wxString expandVars( const wxString& aPath ); |
|||
|
|||
std::list< SEARCH_PATH > m_Paths; ///< List of base search paths. |
|||
|
|||
///< Mapping of (short) file names to resolved names. |
|||
std::map< wxString, wxString, S3D::rsort_wxString > m_NameMap; |
|||
int m_errflags; |
|||
wxString m_curProjDir; |
|||
|
|||
///< Environment variables. |
|||
std::map< wxString, wxString > m_EnvVars; |
|||
}; |
|||
|
|||
#endif // RESOLVER_3D_H |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue