kicad/utils/kicad2step/pcb/3d_resolver.cpp

798 lines
22 KiB
C++
Raw Normal View History

2016-09-02 10:08:40 +00:00
/*
* This program source code file is part kicad2mcad
*
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
2022-07-06 21:32:52 +00:00
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
2016-09-02 10:08:40 +00:00
*
* 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>
2016-09-02 10:08:40 +00:00
#include <sstream>
2016-09-02 10:08:40 +00:00
#include <wx/fileconf.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/thread.h>
#include <wx/msgdlg.h>
#include <wx/stdpaths.h>
2016-09-02 10:08:40 +00:00
#include "3d_resolver.h"
// configuration file version
#define CFGFILE_VERSION 1
#define S3D_RESOLVER_CONFIG "ExportPaths.cfg"
2016-09-02 10:08:40 +00:00
// flag bits used to track different one-off messages to users
#define ERRFLG_ALIAS (1)
#define ERRFLG_RELPATH (2)
#define ERRFLG_ENVPATH (4)
2021-07-29 11:10:58 +00:00
/**
* Flag to enable plugin loader trace output.
*
* @ingroup trace_env_vars
*/
const wxChar* const trace3dResolver = wxT( "KICAD_3D_RESOLVER" );
2016-09-02 10:08:40 +00:00
static std::mutex mutex3D_resolver;
2016-09-02 10:08:40 +00:00
2021-07-29 11:10:58 +00:00
2016-09-02 10:08:40 +00:00
static bool getHollerith( const std::string& aString, size_t& aIndex, wxString& aResult );
2021-07-29 11:10:58 +00:00
2016-09-02 10:08:40 +00:00
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();
2016-09-02 10:08:40 +00:00
if( m_Paths.empty() )
return false;
#ifdef DEBUG
2022-02-08 21:47:43 +00:00
wxLogTrace( trace3dResolver, wxT( " * [3D model] search paths:\n" ) );
2016-09-02 10:08:40 +00:00
for( const SEARCH_PATH& searchPath : m_Paths )
2022-02-08 21:47:43 +00:00
wxLogTrace( trace3dResolver, wxT( " + '%s'\n" ), searchPath.m_Pathexp );
2016-09-02 10:08:40 +00:00
#endif
return true;
}
wxString S3D_RESOLVER::ResolvePath( const wxString& aFileName,
std::vector<wxString>& aSearchedPaths )
2016-09-02 10:08:40 +00:00
{
std::lock_guard<std::mutex> lock( mutex3D_resolver );
2016-09-02 10:08:40 +00:00
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;
2016-09-02 10:08:40 +00:00
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
2016-09-02 10:08:40 +00:00
// translate from KiCad's internal UNIX-like path to MSWin paths
2022-02-08 21:47:43 +00:00
tname.Replace( wxT( "/" ), wxT( "\\" ) );
#endif
2016-09-02 10:08:40 +00:00
// 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().
2022-02-08 21:47:43 +00:00
if( tname.StartsWith( wxT( "${" ) ) || tname.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
tname = expandVars( tname );
wxFileName tmpFN( tname );
// in the case of absolute filenames we don't store a map item
2022-02-08 21:47:43 +00:00
if( !aFileName.StartsWith( wxT( "${" ) ) && !aFileName.StartsWith( wxT( "$(" ) )
&& tmpFN.IsAbsolute() )
2016-09-02 10:08:40 +00:00
{
if( tmpFN.FileExists() )
{
tmpFN.Normalize();
return tmpFN.GetFullPath();
}
else
{
aSearchedPaths.push_back( tmpFN.GetFullPath() );
}
2016-09-02 10:08:40 +00:00
}
// 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();
2016-09-02 10:08:40 +00:00
if( tmpFN.FileExists() )
{
tname = tmpFN.GetFullPath();
m_NameMap[ aFileName ] = tname;
2016-09-02 10:08:40 +00:00
// special case: if a path begins with ${ENV_VAR} but is not in the resolver's path list
// then add it
2022-02-08 21:47:43 +00:00
if( aFileName.StartsWith( wxT( "${" ) ) || aFileName.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
checkEnvVarPath( aFileName );
return tname;
}
else if( tmpFN.GetFullPath() != aFileName )
{
aSearchedPaths.push_back( tmpFN.GetFullPath() );
}
2016-09-02 10:08:40 +00:00
// 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
2022-02-08 21:47:43 +00:00
if( aFileName.StartsWith( wxT( "${" ) ) || aFileName.StartsWith( wxT( "$(" ) ) )
{
m_errflags |= ERRFLG_ENVPATH;
return aFileName;
}
2016-09-02 10:08:40 +00:00
// 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}.
2022-02-08 21:47:43 +00:00
if( !m_Paths.empty() && !m_Paths.begin()->m_Pathexp.empty() && !tname.StartsWith( wxT( ":" ) ) )
2016-09-02 10:08:40 +00:00
{
2022-02-08 21:47:43 +00:00
tmpFN.Assign( m_Paths.begin()->m_Pathexp, wxT( "" ) );
2016-09-02 10:08:40 +00:00
wxString fullPath = tmpFN.GetPathWithSep() + tname;
2022-02-08 21:47:43 +00:00
if( fullPath.StartsWith( wxT( "${" ) ) || fullPath.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
fullPath = expandVars( fullPath );
tmpFN.Assign( fullPath );
tmpFN.Normalize();
if( tmpFN.FileExists() )
2016-09-02 10:08:40 +00:00
{
tname = tmpFN.GetFullPath();
m_NameMap[ aFileName ] = tname;
2016-09-02 10:08:40 +00:00
return tname;
}
else if( tmpFN.GetFullPath() != aFileName )
{
aSearchedPaths.push_back( tmpFN.GetFullPath() );
}
2016-09-02 10:08:40 +00:00
}
// check the partial path relative to ${KICAD6_3DMODEL_DIR} (legacy behavior)
2022-02-08 21:47:43 +00:00
if( !tname.Contains( wxT( ":" ) ) )
2016-09-02 10:08:40 +00:00
{
wxFileName fpath;
2022-02-08 21:47:43 +00:00
wxString fullPath( wxT( "${KICAD6_3DMODEL_DIR}" ) );
2016-09-02 10:08:40 +00:00
fullPath.Append( fpath.GetPathSeparator() );
fullPath.Append( tname );
fullPath = expandVars( fullPath );
fpath.Assign( fullPath );
fpath.Normalize();
2016-09-02 10:08:40 +00:00
if( fpath.FileExists() )
2016-09-02 10:08:40 +00:00
{
tname = fpath.GetFullPath();
m_NameMap[ aFileName ] = tname;
2016-09-02 10:08:40 +00:00
return tname;
}
else
{
aSearchedPaths.push_back( fpath.GetFullPath() );
}
2016-09-02 10:08:40 +00:00
}
// 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;
2016-09-02 10:08:40 +00:00
}
for( const SEARCH_PATH& path : m_Paths )
2016-09-02 10:08:40 +00:00
{
// ${ENV_VAR} paths have already been checked; skip them
2022-02-08 21:47:43 +00:00
if( path.m_Alias.StartsWith( wxT( "${" ) ) || path.m_Alias.StartsWith( wxT( "$(" ) ) )
continue;
if( path.m_Alias == alias && !path.m_Pathexp.empty() )
2016-09-02 10:08:40 +00:00
{
wxFileName fpath( wxFileName::DirName( path.m_Pathexp ) );
2016-09-02 10:08:40 +00:00
wxString fullPath = fpath.GetPathWithSep() + relpath;
2022-02-08 21:47:43 +00:00
if( fullPath.StartsWith( wxT( "${" ) ) || fullPath.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
fullPath = expandVars( fullPath );
wxFileName tmp( fullPath );
tmp.Normalize();
2016-09-02 10:08:40 +00:00
if( tmp.FileExists() )
{
tname = tmp.GetFullPath();
m_NameMap[ aFileName ] = tname;
2016-09-02 10:08:40 +00:00
return tname;
}
else
{
aSearchedPaths.push_back( tmp.GetFullPath() );
}
2016-09-02 10:08:40 +00:00
}
}
m_errflags |= ERRFLG_ALIAS;
return aFileName;
2016-09-02 10:08:40 +00:00
}
bool S3D_RESOLVER::addPath( const SEARCH_PATH& aPath )
2016-09-02 10:08:40 +00:00
{
2020-11-16 11:16:44 +00:00
if( aPath.m_Alias.empty() || aPath.m_Pathvar.empty() )
2016-09-02 10:08:40 +00:00
return false;
std::lock_guard<std::mutex> lock( mutex3D_resolver );
2016-09-02 10:08:40 +00:00
SEARCH_PATH tpath = aPath;
2016-09-02 10:08:40 +00:00
#ifdef _WIN32
2022-02-08 21:47:43 +00:00
while( tpath.m_Pathvar.EndsWith( wxT( "\\" ) ) )
2020-11-16 11:16:44 +00:00
tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
#else
2022-02-08 21:47:43 +00:00
while( tpath.m_Pathvar.EndsWith( wxT( "/" ) ) && tpath.m_Pathvar.length() > 1 )
2020-11-16 11:16:44 +00:00
tpath.m_Pathvar.erase( tpath.m_Pathvar.length() - 1 );
#endif
2016-09-02 10:08:40 +00:00
2022-02-08 21:47:43 +00:00
wxFileName path( tpath.m_Pathvar, wxT( "" ) );
2016-09-02 10:08:40 +00:00
path.Normalize();
if( !path.DirExists() )
2016-09-02 10:08:40 +00:00
{
// Show a message only in debug mode
#ifdef DEBUG
2022-02-08 21:47:43 +00:00
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
2016-09-02 10:08:40 +00:00
{
wxString msg = _( "The given path does not exist" );
2022-02-08 21:47:43 +00:00
msg.append( wxT( "\n" ) );
2020-11-16 11:16:44 +00:00
msg.append( tpath.m_Pathvar );
2022-02-08 21:47:43 +00:00
wxLogMessage( wxT( "%s\n" ), msg.ToUTF8() );
2016-09-02 10:08:40 +00:00
}
#endif
2016-09-02 10:08:40 +00:00
2020-11-16 11:16:44 +00:00
tpath.m_Pathexp.clear();
2016-09-02 10:08:40 +00:00
}
else
{
2020-11-16 11:16:44 +00:00
tpath.m_Pathexp = path.GetFullPath();
2016-09-02 10:08:40 +00:00
#ifdef _WIN32
2022-02-08 21:47:43 +00:00
while( tpath.m_Pathexp.EndsWith( wxT( "\\" ) ) )
2022-07-06 21:32:52 +00:00
tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
#else
2022-02-08 21:47:43 +00:00
while( tpath.m_Pathexp.EndsWith( wxT( "/" ) ) && tpath.m_Pathexp.length() > 1 )
2020-11-16 11:16:44 +00:00
tpath.m_Pathexp.erase( tpath.m_Pathexp.length() - 1 );
#endif
2016-09-02 10:08:40 +00:00
}
wxString pname = path.GetPath();
std::list< SEARCH_PATH >::iterator sPL = m_Paths.begin();
std::list< SEARCH_PATH >::iterator ePL = m_Paths.end();
2016-09-02 10:08:40 +00:00
while( sPL != ePL )
{
if( tpath.m_Alias == sPL->m_Alias )
2016-09-02 10:08:40 +00:00
{
wxString msg = _( "Alias:" ) + wxS( " " );
2020-11-16 11:16:44 +00:00
msg.append( tpath.m_Alias );
2022-02-08 21:47:43 +00:00
msg.append( wxS( "\n" ) );
msg.append( _( "This path:" ) + wxS( " " ) );
2020-11-16 11:16:44 +00:00
msg.append( tpath.m_Pathvar );
2022-02-08 21:47:43 +00:00
msg.append( wxS( "\n" ) );
msg.append( _( "Existing path:" ) + wxS( " " ) );
2020-11-16 11:16:44 +00:00
msg.append( sPL->m_Pathvar );
2016-09-02 10:08:40 +00:00
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 );
2016-09-02 10:08:40 +00:00
cfgpath.Normalize();
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'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, cfgname );
2016-09-02 10:08:40 +00:00
return false;
}
cfgFile.open( cfgname.ToUTF8() );
if( !cfgFile.is_open() )
{
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * Could not open configuration file '%s'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, cfgname );
2016-09-02 10:08:40 +00:00
return false;
}
int lineno = 0;
SEARCH_PATH al;
2016-09-02 10:08:40 +00:00
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;
2020-11-16 11:16:44 +00:00
if( !getHollerith( cfgLine, idx, al.m_Alias ) )
2016-09-02 10:08:40 +00:00
continue;
2020-11-16 11:16:44 +00:00
if( !getHollerith( cfgLine, idx, al.m_Pathvar ) )
2016-09-02 10:08:40 +00:00
continue;
2020-11-16 11:16:44 +00:00
if( !getHollerith( cfgLine, idx, al.m_Description ) )
2016-09-02 10:08:40 +00:00
continue;
addPath( al );
}
cfgFile.close();
if( m_Paths.size() != nitems )
return true;
return false;
}
void S3D_RESOLVER::checkEnvVarPath( const wxString& aPath )
{
bool useParen = false;
2022-02-08 21:47:43 +00:00
if( aPath.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
useParen = true;
2022-02-08 21:47:43 +00:00
else if( !aPath.StartsWith( wxT( "${" ) ) )
2016-09-02 10:08:40 +00:00
return;
size_t pEnd;
if( useParen )
2022-02-08 21:47:43 +00:00
pEnd = aPath.find( wxT( ")" ) );
2016-09-02 10:08:40 +00:00
else
2022-02-08 21:47:43 +00:00
pEnd = aPath.find( wxT( "}" ) );
2016-09-02 10:08:40 +00:00
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();
2016-09-02 10:08:40 +00:00
while( sPL != ePL )
{
2020-11-16 11:16:44 +00:00
if( sPL->m_Alias == envar )
2016-09-02 10:08:40 +00:00
return;
2022-02-08 21:47:43 +00:00
if( !sPL->m_Alias.StartsWith( wxT( "${" ) ) )
2016-09-02 10:08:40 +00:00
break;
++sPL;
}
SEARCH_PATH lpath;
2020-11-16 11:16:44 +00:00
lpath.m_Alias = envar;
lpath.m_Pathvar = lpath.m_Alias;
2022-02-08 21:47:43 +00:00
wxFileName tmpFN( lpath.m_Alias, wxT( "" ) );
2016-09-02 10:08:40 +00:00
wxUniChar psep = tmpFN.GetPathSeparator();
tmpFN.Normalize();
if( !tmpFN.DirExists() )
return;
2020-11-16 11:16:44 +00:00
lpath.m_Pathexp = tmpFN.GetFullPath();
2016-09-02 10:08:40 +00:00
2020-11-16 11:16:44 +00:00
if( !lpath.m_Pathexp.empty() && psep == *lpath.m_Pathexp.rbegin() )
lpath.m_Pathexp.erase( --lpath.m_Pathexp.end() );
2016-09-02 10:08:40 +00:00
2020-11-16 11:16:44 +00:00
if( lpath.m_Pathexp.empty() )
2016-09-02 10:08:40 +00:00
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 )
2016-09-02 10:08:40 +00:00
{
if( !aPath.compare( 2, i.first.length(), i.first ) )
{
result = i.second;
result.append( aPath.substr( 3 + i.first.length() ) );
2022-02-08 21:47:43 +00:00
if( result.StartsWith( wxT( "${" ) ) || result.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
result = expandVars( result );
return result;
}
}
result = wxExpandEnvVars( aPath );
if( result == aPath )
return wxEmptyString;
2022-02-08 21:47:43 +00:00
if( result.StartsWith( wxT( "${" ) ) || result.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
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();
2016-09-02 10:08:40 +00:00
size_t idx;
while( sL != eL )
{
2021-07-29 11:10:58 +00:00
// undefined paths do not participate in the file name shortening procedure.
2020-11-16 11:16:44 +00:00
if( sL->m_Pathexp.empty() )
2016-09-02 10:08:40 +00:00
{
++sL;
continue;
}
2022-02-08 21:47:43 +00:00
wxFileName fpath( sL->m_Pathexp, wxT( "" ) );
2016-09-02 10:08:40 +00:00
wxString fps = fpath.GetPathWithSep();
wxString tname;
idx = fname.find( fps );
2022-07-06 21:32:52 +00:00
if( std::string::npos != idx && 0 == idx )
2016-09-02 10:08:40 +00:00
{
fname = fname.substr( fps.size() );
#ifdef _WIN32
2016-09-02 10:08:40 +00:00
// ensure only the '/' separator is used in the internal name
2022-02-08 21:47:43 +00:00
fname.Replace( wxT( "\\" ), wxT( "/" ) );
#endif
2016-09-02 10:08:40 +00:00
2022-02-08 21:47:43 +00:00
if( sL->m_Alias.StartsWith( wxT( "${" ) ) || sL->m_Alias.StartsWith( wxT( "$(" ) ) )
2016-09-02 10:08:40 +00:00
{
// old style ENV_VAR
2020-11-16 11:16:44 +00:00
tname = sL->m_Alias;
2022-02-08 21:47:43 +00:00
tname.Append( wxT( "/" ) );
2016-09-02 10:08:40 +00:00
tname.append( fname );
}
else
{
// new style alias
2022-02-08 21:47:43 +00:00
tname = wxT( ":" );
2020-11-16 11:16:44 +00:00
tname.append( sL->m_Alias );
2022-02-08 21:47:43 +00:00
tname.append( wxT( ":" ) );
2016-09-02 10:08:40 +00:00
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.
2022-02-08 21:47:43 +00:00
fname.Replace( wxT( "\\" ), wxT( "/" ) );
2016-09-02 10:08:40 +00:00
#endif
return fname;
}
const std::list< SEARCH_PATH >* S3D_RESOLVER::GetPaths( void )
2016-09-02 10:08:40 +00:00
{
return &m_Paths;
}
2021-07-29 11:10:58 +00:00
bool S3D_RESOLVER::SplitAlias( const wxString& aFileName, wxString& anAlias, wxString& aRelPath )
2016-09-02 10:08:40 +00:00
{
anAlias.clear();
aRelPath.clear();
size_t searchStart = 0;
if( aFileName.StartsWith( wxT( ":" ) ) )
searchStart = 1;
2016-09-02 10:08:40 +00:00
size_t tagpos = aFileName.find( wxT( ":" ), searchStart );
2016-09-02 10:08:40 +00:00
if( tagpos == wxString::npos || tagpos == searchStart )
2016-09-02 10:08:40 +00:00
return false;
if( tagpos + 1 >= aFileName.length() )
return false;
anAlias = aFileName.substr( searchStart, tagpos - searchStart );
2016-09-02 10:08:40 +00:00
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'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, aString );
2016-09-02 10:08:40 +00:00
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'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, aString );
2016-09-02 10:08:40 +00:00
return false;
}
++i2;
if( i2 >= aString.size() )
{
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * unexpected end of line in line '%s'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, aString );
2016-09-02 10:08:40 +00:00
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'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, aString );
2016-09-02 10:08:40 +00:00
return false;
}
std::istringstream istr;
istr.str( tnum );
size_t nchars;
istr >> nchars;
2022-07-06 21:32:52 +00:00
if( ( i2 + nchars ) >= aString.size() )
2016-09-02 10:08:40 +00:00
{
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * unexpected end of line in line '%s'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, aString );
2016-09-02 10:08:40 +00:00
return false;
}
if( nchars > 0 )
{
aResult = wxString::FromUTF8( aString.substr( i2, nchars ).c_str() );
2016-09-02 10:08:40 +00:00
i2 += nchars;
}
if( i2 >= aString.size() || aString[i2] != '"' )
2016-09-02 10:08:40 +00:00
{
wxLogTrace( trace3dResolver, wxT( "%s:%s:%d\n * missing closing quote mark in line '%s'" ),
2021-07-29 11:10:58 +00:00
__FILE__, __FUNCTION__, __LINE__, aString );
2016-09-02 10:08:40 +00:00
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 "{}[]()%~<>\"='`;:.,&?/\\|$"
2016-09-02 10:08:40 +00:00
// 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;
2021-09-06 14:01:55 +00:00
size_t aliasStart = aFileName.StartsWith( ':' ) ? 1 : 0;
size_t aliasEnd = aFileName.find( ':' );
2016-09-02 10:08:40 +00:00
// ensure that the file separators suit the current platform
#ifdef __WINDOWS__
2022-02-08 21:47:43 +00:00
filename.Replace( wxT( "/" ), wxT( "\\" ) );
2016-09-02 10:08:40 +00:00
// if we see the :\ pattern then it must be a drive designator
if( aliasEnd != wxString::npos )
2016-09-02 10:08:40 +00:00
{
2022-02-08 21:47:43 +00:00
size_t pos1 = aFileName.find( wxT( ":\\" ) );
2016-09-02 10:08:40 +00:00
if( pos1 != wxString::npos && ( pos1 != aliasEnd || pos1 != 1 ) )
2016-09-02 10:08:40 +00:00
return false;
// if we have a drive designator then we have no alias
if( pos1 != wxString::npos )
aliasEnd = wxString::npos;
2016-09-02 10:08:40 +00:00
}
#else
2022-02-08 21:47:43 +00:00
filename.Replace( wxT( "\\" ), wxT( "/" ) );
#endif
2016-09-02 10:08:40 +00:00
// names may not end with ':'
if( aliasEnd == aFileName.length() - 1 )
2016-09-02 10:08:40 +00:00
return false;
if( aliasEnd != wxString::npos )
2016-09-02 10:08:40 +00:00
{
// ensure the alias component is not empty
if( aliasEnd == aliasStart )
2016-09-02 10:08:40 +00:00
return false;
lpath = filename.substr( aliasStart, aliasEnd );
2016-09-02 10:08:40 +00:00
// check the alias for restricted characters
if( wxString::npos != lpath.find_first_of( "{}[]()%~<>\"='`;:.,&?/\\|$" ) )
2016-09-02 10:08:40 +00:00
return false;
hasAlias = true;
lpath = aFileName.substr( aliasEnd + 1 );
2016-09-02 10:08:40 +00:00
}
else
{
lpath = aFileName;
// in the case of ${ENV_VAR}|$(ENV_VAR)/path, strip the
// environment string before testing
aliasEnd = wxString::npos;
2016-09-02 10:08:40 +00:00
2022-02-08 21:47:43 +00:00
if( aFileName.StartsWith( wxT( "${" ) ) )
aliasEnd = aFileName.find( '}' );
2022-02-08 21:47:43 +00:00
else if( aFileName.StartsWith( wxT( "$(" ) ) )
aliasEnd = aFileName.find( ')' );
2016-09-02 10:08:40 +00:00
if( aliasEnd != wxString::npos )
lpath = aFileName.substr( aliasEnd + 1 );
2016-09-02 10:08:40 +00:00
}
if( wxString::npos != lpath.find_first_of( wxFileName::GetForbiddenChars() ) )
return false;
return true;
}