You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

966 lines
26 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <iostream>
  24. #include <cstdlib>
  25. #include <cstring>
  26. #include <fstream>
  27. #include <sstream>
  28. #include <wx/filename.h>
  29. #include <wx/utils.h>
  30. #include <wx/msgdlg.h>
  31. #include "3d_filename_resolver.h"
  32. // configuration file version
  33. #define CFGFILE_VERSION 1
  34. #define S3D_RESOLVER_CONFIG wxT( "3Dresolver.cfg" )
  35. // flag bits used to track different one-off messages to users
  36. #define ERRFLG_ALIAS (1)
  37. #define ERRFLG_RELPATH (2)
  38. #define ERRFLG_ENVPATH (4)
  39. static bool getHollerith( const std::string& aString, size_t& aIndex, wxString& aResult );
  40. S3D_FILENAME_RESOLVER::S3D_FILENAME_RESOLVER()
  41. {
  42. m_errflags = 0;
  43. }
  44. bool S3D_FILENAME_RESOLVER::Set3DConfigDir( const wxString& aConfigDir )
  45. {
  46. if( aConfigDir.empty() )
  47. return false;
  48. wxFileName cfgdir( aConfigDir, wxT( "" ) );
  49. cfgdir.Normalize();
  50. if( false == cfgdir.DirExists() )
  51. return false;
  52. m_ConfigDir = cfgdir.GetPath();
  53. createPathList();
  54. return true;
  55. }
  56. bool S3D_FILENAME_RESOLVER::SetProjectDir( const wxString& aProjDir, bool* flgChanged )
  57. {
  58. if( aProjDir.empty() )
  59. return false;
  60. wxFileName projdir( aProjDir, wxT( "" ) );
  61. projdir.Normalize();
  62. if( false == projdir.DirExists() )
  63. return false;
  64. wxString path = projdir.GetPath();
  65. if( flgChanged )
  66. *flgChanged = false;
  67. if( m_Paths.empty() )
  68. {
  69. S3D_ALIAS al;
  70. al.m_alias = _( "(DEFAULT)" );
  71. al.m_pathvar = _( "${PROJDIR}" );
  72. al.m_pathexp = path;
  73. al.m_description = _( "Current project directory" );
  74. m_Paths.push_back( al );
  75. m_NameMap.clear();
  76. if( flgChanged )
  77. *flgChanged = true;
  78. }
  79. else
  80. {
  81. if( m_Paths.front().m_pathexp.Cmp( path ) )
  82. {
  83. m_Paths.front().m_pathexp = path;
  84. m_NameMap.clear();
  85. if( flgChanged )
  86. *flgChanged = true;
  87. }
  88. else
  89. {
  90. return true;
  91. }
  92. }
  93. #ifdef DEBUG
  94. std::cout << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  95. std::cout << " * [INFO] changed project dir to ";
  96. std::cout << m_Paths.front().m_pathexp.ToUTF8() << "\n";
  97. #endif
  98. return true;
  99. }
  100. wxString S3D_FILENAME_RESOLVER::GetProjectDir( void )
  101. {
  102. if( m_Paths.empty() )
  103. return wxEmptyString;
  104. return m_Paths.front().m_pathexp;
  105. }
  106. bool S3D_FILENAME_RESOLVER::createPathList( void )
  107. {
  108. if( !m_Paths.empty() )
  109. return true;
  110. wxString kmod;
  111. // add the current working directory as the first entry by
  112. // default; since CWD is not necessarily what we really want,
  113. // the user may change this later with a call to SetProjectDir()
  114. S3D_ALIAS lpath;
  115. lpath.m_alias = _( "(DEFAULT)" );
  116. lpath.m_pathvar = _( "(PROJECT DIR)" );
  117. lpath.m_pathexp = wxFileName::GetCwd();
  118. lpath.m_description = _( "Current project directory" );
  119. m_Paths.push_back( lpath );
  120. lpath.m_alias = wxT( "KISYS3DMOD" );
  121. lpath.m_pathvar = wxT( "${KISYS3DMOD}" );
  122. lpath.m_description = _( "Legacy 3D environment path" );
  123. addPath( lpath );
  124. if( !m_ConfigDir.empty() )
  125. readPathList();
  126. if( m_Paths.empty() )
  127. return false;
  128. #ifdef DEBUG
  129. std::cout << " * [3D model] search paths:\n";
  130. std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
  131. std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
  132. while( sPL != ePL )
  133. {
  134. std::cout << " + '" << (*sPL).m_pathexp.ToUTF8() << "'\n";
  135. ++sPL;
  136. }
  137. #endif
  138. return true;
  139. }
  140. bool S3D_FILENAME_RESOLVER::UpdatePathList( std::vector< S3D_ALIAS >& aPathList )
  141. {
  142. while( m_Paths.size() > 2 )
  143. m_Paths.pop_back();
  144. size_t nI = aPathList.size();
  145. for( size_t i = 0; i < nI; ++i )
  146. addPath( aPathList[i] );
  147. return writePathList();
  148. }
  149. wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
  150. {
  151. if( aFileName.empty() )
  152. return wxEmptyString;
  153. if( m_Paths.empty() )
  154. createPathList();
  155. // look up the filename in the internal filename map
  156. std::map< wxString, wxString, S3D::rsort_wxString >::iterator mi;
  157. mi = m_NameMap.find( aFileName );
  158. if( mi != m_NameMap.end() )
  159. return mi->second;
  160. // first attempt to use the name as specified:
  161. wxString tname = aFileName;
  162. #ifdef _WIN32
  163. // translate from KiCad's internal UNIX-like path to MSWin paths
  164. tname.Replace( wxT( "/" ), wxT( "\\" ) );
  165. #endif
  166. // this case covers full paths and paths relative to
  167. // the current working directory (which is not necessarily
  168. // the current project directory)
  169. if( wxFileName::FileExists( tname ) )
  170. {
  171. wxFileName tmp( tname );
  172. if( tmp.Normalize() )
  173. tname = tmp.GetFullPath();
  174. m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
  175. return tname;
  176. }
  177. // at this point aFileName:
  178. // a. is a legacy ${} shortened name
  179. // b. an aliased shortened name
  180. // c. cannot be determined
  181. if( aFileName.StartsWith( wxT( "${" ) ) )
  182. {
  183. wxFileName tmp( aFileName );
  184. if( tmp.Normalize() )
  185. {
  186. tname = tmp.GetFullPath();
  187. m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
  188. return tname;
  189. }
  190. if( !( m_errflags & ERRFLG_ENVPATH ) )
  191. {
  192. m_errflags |= ERRFLG_ENVPATH;
  193. wxString errmsg = _( "No such path; ensure the environment var is defined" );
  194. errmsg.append( "\n" );
  195. errmsg.append( tname );
  196. wxMessageBox( errmsg, _( "3D file resolver" ) );
  197. }
  198. return wxEmptyString;
  199. }
  200. std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
  201. std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
  202. // check the path relative to the current project directory;
  203. // note: this is not necessarily the same as the current working
  204. // directory, which has already been checked
  205. do
  206. {
  207. wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
  208. wxString fullPath = fpath.GetPathWithSep() + tname;
  209. if( wxFileName::FileExists( fullPath ) )
  210. {
  211. wxFileName tmp( fullPath );
  212. if( tmp.Normalize() )
  213. tname = tmp.GetFullPath();
  214. m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
  215. return tname;
  216. }
  217. } while( 0 );
  218. ++sPL; // skip to item 2: KISYS3DMOD
  219. // check if the path is relative to KISYS3DMOD but lacking
  220. // the "KISYS3DMOD:" alias tag
  221. if( !sPL->m_pathexp.empty() )
  222. {
  223. wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
  224. wxString fullPath = fpath.GetPathWithSep() + tname;
  225. if( wxFileName::FileExists( fullPath ) )
  226. {
  227. wxFileName tmp( fullPath );
  228. if( tmp.Normalize() )
  229. tname = tmp.GetFullPath();
  230. m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
  231. return tname;
  232. }
  233. }
  234. wxString alias; // the alias portion of the short filename
  235. wxString relpath; // the path relative to the alias
  236. if( !SplitAlias( tname, alias, relpath ) )
  237. {
  238. if( !( m_errflags & ERRFLG_RELPATH ) )
  239. {
  240. m_errflags |= ERRFLG_RELPATH;
  241. wxString errmsg = _( "No such path; ensure KISYS3DMOD is correctly defined" );
  242. errmsg.append( "\n" );
  243. errmsg.append( tname );
  244. wxMessageBox( errmsg, _( "3D file resolver" ) );
  245. }
  246. return wxEmptyString;
  247. }
  248. while( sPL != ePL )
  249. {
  250. if( !sPL->m_alias.Cmp( alias ) )
  251. {
  252. wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
  253. wxString fullPath = fpath.GetPathWithSep() + relpath;
  254. if( wxFileName::FileExists( fullPath ) )
  255. {
  256. wxFileName tmp( fullPath );
  257. if( tmp.Normalize() )
  258. tname = tmp.GetFullPath();
  259. m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
  260. return tname;
  261. }
  262. }
  263. ++sPL;
  264. }
  265. if( !( m_errflags & ERRFLG_ALIAS ) )
  266. {
  267. m_errflags |= ERRFLG_ALIAS;
  268. wxString errmsg = _( "No such path; ensure the path alias is defined" );
  269. errmsg.append( "\n" );
  270. errmsg.append( tname.substr( 1 ) );
  271. wxMessageBox( errmsg, _( "3D file resolver" ) );
  272. }
  273. return wxEmptyString;
  274. }
  275. bool S3D_FILENAME_RESOLVER::addPath( const S3D_ALIAS& aPath )
  276. {
  277. if( aPath.m_alias.empty() || aPath.m_pathvar.empty() )
  278. return false;
  279. S3D_ALIAS tpath = aPath;
  280. tpath.m_duplicate = false;
  281. #ifdef _WIN32
  282. while( tpath.m_pathvar.EndsWith( wxT( "\\" ) ) )
  283. tpath.m_pathvar.erase( tpath.m_pathvar.length() - 1 );
  284. #else
  285. while( tpath.m_pathvar.EndsWith( wxT( "/" ) ) && tpath.m_pathvar.length() > 1 )
  286. tpath.m_pathvar.erase( tpath.m_pathvar.length() - 1 );
  287. #endif
  288. wxFileName path( tpath.m_pathvar, wxT( "" ) );
  289. path.Normalize();
  290. if( !path.DirExists() )
  291. {
  292. wxString msg = _T( "The given path does not exist" );
  293. msg.append( wxT( "\n" ) );
  294. msg.append( tpath.m_pathvar );
  295. tpath.m_pathexp.clear();
  296. wxMessageBox( msg, _T( "3D model search path" ) );
  297. }
  298. else
  299. {
  300. tpath.m_pathexp = path.GetFullPath();
  301. #ifdef _WIN32
  302. while( tpath.m_pathexp.EndsWith( wxT( "\\" ) ) )
  303. tpath.m_pathexp.erase( tpath.m_pathexp.length() - 1 );
  304. #else
  305. while( tpath.m_pathexp.EndsWith( wxT( "/" ) ) && tpath.m_pathexp.length() > 1 )
  306. tpath.m_pathexp.erase( tpath.m_pathexp.length() - 1 );
  307. #endif
  308. }
  309. wxString pname = path.GetPath();
  310. std::list< S3D_ALIAS >::iterator sPL = m_Paths.begin();
  311. std::list< S3D_ALIAS >::iterator ePL = m_Paths.end();
  312. while( sPL != ePL )
  313. {
  314. // aliases with the same m_pathvar are forbidden and the
  315. // user must be forced to fix the problem in order to
  316. // obtain good filename resolution
  317. if( !sPL->m_pathvar.empty() && !tpath.m_pathvar.empty()
  318. && !tpath.m_pathvar.Cmp( sPL->m_pathvar ) )
  319. {
  320. wxString msg = _( "This alias: " );
  321. msg.append( tpath.m_alias );
  322. msg.append( wxT( "\n" ) );
  323. msg.append( _( "This path: " ) );
  324. msg.append( tpath.m_pathvar );
  325. msg.append( wxT( "\n" ) );
  326. msg.append( _( "Existing alias: " ) );
  327. msg.append( sPL->m_alias );
  328. msg.append( wxT( "\n" ) );
  329. msg.append( _( "Existing path: " ) );
  330. msg.append( sPL->m_pathvar );
  331. wxMessageBox( msg, _( "Bad alias (duplicate path)" ) );
  332. return false;
  333. }
  334. // aliases with the same m_pathexp are acceptable (one or both
  335. // aliases being testes may be expanded variables) but when shortening
  336. // names the preference is for (a) a fully specified path in m_pathvar
  337. // then (b) the more senior alias in the list
  338. if( !sPL->m_pathexp.empty() && !tpath.m_pathexp.empty() )
  339. {
  340. if( !tpath.m_pathexp.Cmp( sPL->m_pathexp ) )
  341. {
  342. wxString msg = _( "This alias: " );
  343. msg.append( tpath.m_alias );
  344. msg.append( wxT( "\n" ) );
  345. msg.append( _( "Existing alias: " ) );
  346. msg.append( sPL->m_alias );
  347. msg.append( wxT( "\n" ) );
  348. msg.append( _( "This path: " ) );
  349. msg.append( tpath.m_pathexp );
  350. msg.append( wxT( "\n" ) );
  351. msg.append( _( "Existing path: " ) );
  352. msg.append( sPL->m_pathexp );
  353. msg.append( wxT( "\n" ) );
  354. msg.append( _( "This full path: " ) );
  355. msg.append( tpath.m_pathexp );
  356. msg.append( wxT( "\n" ) );
  357. msg.append( _( "Existing full path: " ) );
  358. msg.append( sPL->m_pathexp );
  359. wxMessageBox( msg, _( "Bad alias (duplicate path)" ) );
  360. if( tpath.m_pathvar.StartsWith( wxT( "${" ) ) )
  361. tpath.m_duplicate = true;
  362. else if( sPL->m_pathvar.StartsWith( wxT( "${" ) ) )
  363. sPL->m_duplicate = true;
  364. }
  365. if( ( tpath.m_pathexp.find( sPL->m_pathexp ) != wxString::npos
  366. || sPL->m_pathexp.find( tpath.m_pathexp ) != wxString::npos )
  367. && tpath.m_pathexp.Cmp( sPL->m_pathexp ) )
  368. {
  369. wxString msg = _( "This alias: " );
  370. msg.append( tpath.m_alias );
  371. msg.append( wxT( "\n" ) );
  372. msg.append( _( "This path: " ) );
  373. msg.append( tpath.m_pathexp );
  374. msg.append( wxT( "\n" ) );
  375. msg.append( _( "Existing alias: " ) );
  376. msg.append( sPL->m_alias );
  377. msg.append( wxT( "\n" ) );
  378. msg.append( _( "Existing path: " ) );
  379. msg.append( sPL->m_pathexp );
  380. wxMessageBox( msg, _( "Bad alias (common path)" ) );
  381. return false;
  382. }
  383. }
  384. if( !tpath.m_alias.Cmp( sPL->m_alias ) )
  385. {
  386. wxString msg = _( "Alias: " );
  387. msg.append( tpath.m_alias );
  388. msg.append( wxT( "\n" ) );
  389. msg.append( _( "This path: " ) );
  390. msg.append( tpath.m_pathvar );
  391. msg.append( wxT( "\n" ) );
  392. msg.append( _( "Existing path: " ) );
  393. msg.append( sPL->m_pathvar );
  394. wxMessageBox( msg, _( "Bad alias (duplicate name)" ) );
  395. return false;
  396. }
  397. ++sPL;
  398. }
  399. // Note: at this point we may still have duplicated paths
  400. m_Paths.push_back( tpath );
  401. return true;
  402. }
  403. bool S3D_FILENAME_RESOLVER::readPathList( void )
  404. {
  405. if( m_ConfigDir.empty() )
  406. {
  407. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  408. wxString errmsg = _( "3D configuration directory is unknown" );
  409. std::cerr << " * " << errmsg.ToUTF8() << "\n";
  410. return false;
  411. }
  412. wxFileName cfgpath( m_ConfigDir, S3D_RESOLVER_CONFIG );
  413. cfgpath.Normalize();
  414. wxString cfgname = cfgpath.GetFullPath();
  415. size_t nitems = m_Paths.size();
  416. std::ifstream cfgFile;
  417. std::string cfgLine;
  418. if( !wxFileName::Exists( cfgname ) )
  419. {
  420. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  421. wxString errmsg = _( "no 3D configuration file" );
  422. std::cerr << " * " << errmsg.ToUTF8() << " '";
  423. std::cerr << cfgname.ToUTF8() << "'\n";
  424. return false;
  425. }
  426. cfgFile.open( cfgname.ToUTF8() );
  427. if( !cfgFile.is_open() )
  428. {
  429. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  430. wxString errmsg = _( "could not open configuration file" );
  431. std::cerr << " * " << errmsg.ToUTF8() << " '" << cfgname.ToUTF8() << "'\n";
  432. return false;
  433. }
  434. int lineno = 0;
  435. S3D_ALIAS al;
  436. size_t idx;
  437. int vnum = 0; // version number
  438. while( cfgFile.good() )
  439. {
  440. cfgLine.clear();
  441. std::getline( cfgFile, cfgLine );
  442. ++lineno;
  443. if( cfgLine.empty() )
  444. {
  445. if( cfgFile.eof() )
  446. break;
  447. continue;
  448. }
  449. if( 1 == lineno && cfgLine.find( "#V" ) == 0 )
  450. {
  451. // extract the version number and parse accordingly
  452. if( cfgLine.size() > 2 )
  453. {
  454. std::istringstream istr;
  455. istr.str( cfgLine.substr( 2 ) );
  456. istr >> vnum;
  457. }
  458. continue;
  459. }
  460. idx = 0;
  461. if( !getHollerith( cfgLine, idx, al.m_alias ) )
  462. continue;
  463. // never add on KISYS3DMOD from a config file
  464. if( !al.m_alias.Cmp( wxT( "KISYS3DMOD" ) ) )
  465. continue;
  466. if( !getHollerith( cfgLine, idx, al.m_pathvar ) )
  467. continue;
  468. if( !getHollerith( cfgLine, idx, al.m_description ) )
  469. continue;
  470. addPath( al );
  471. }
  472. cfgFile.close();
  473. if( vnum < CFGFILE_VERSION )
  474. writePathList();
  475. if( m_Paths.size() != nitems )
  476. return true;
  477. return false;
  478. }
  479. bool S3D_FILENAME_RESOLVER::writePathList( void )
  480. {
  481. if( m_ConfigDir.empty() )
  482. {
  483. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  484. wxString errmsg = _( "3D configuration directory is unknown" );
  485. std::cerr << " * " << errmsg.ToUTF8() << "\n";
  486. wxMessageBox( errmsg, _( "Write 3D search path list" ) );
  487. return false;
  488. }
  489. wxFileName cfgpath( m_ConfigDir, S3D_RESOLVER_CONFIG );
  490. wxString cfgname = cfgpath.GetFullPath();
  491. std::ofstream cfgFile;
  492. if( m_Paths.empty() || 1 == m_Paths.size() )
  493. {
  494. wxMessageDialog md( NULL,
  495. _( "3D search path list is empty;\ncontinue to write empty file?" ),
  496. _( "Write 3D search path list" ), wxYES_NO );
  497. if( md.ShowModal() == wxID_YES )
  498. {
  499. cfgFile.open( cfgname.ToUTF8(), std::ios_base::trunc );
  500. if( !cfgFile.is_open() )
  501. {
  502. wxMessageBox( _( "Could not open configuration file" ),
  503. _( "Write 3D search path list" ) );
  504. return false;
  505. }
  506. cfgFile << "#V" << CFGFILE_VERSION << "\n";
  507. cfgFile.close();
  508. return true;
  509. }
  510. return false;
  511. }
  512. cfgFile.open( cfgname.ToUTF8(), std::ios_base::trunc );
  513. if( !cfgFile.is_open() )
  514. {
  515. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  516. wxString errmsg = _( "could not open configuration file " );
  517. std::cerr << " * " << errmsg.ToUTF8() << " '" << cfgname.ToUTF8() << "'\n";
  518. wxMessageBox( _( "Could not open configuration file" ),
  519. _( "Write 3D search path list" ) );
  520. return false;
  521. }
  522. cfgFile << "#V" << CFGFILE_VERSION << "\n";
  523. std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
  524. std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
  525. // the first entry is the current project dir; we never add the implicit
  526. // project dir to the path list in the configuration file
  527. ++sPL;
  528. std::string tstr;
  529. while( sPL != ePL )
  530. {
  531. // never write the KISYS3DMOD entry
  532. if( !sPL->m_alias.Cmp( wxT( "KISYS3DMOD") ) )
  533. {
  534. ++sPL;
  535. continue;
  536. }
  537. tstr = sPL->m_alias.ToUTF8();
  538. cfgFile << "\"" << tstr.size() << ":" << tstr << "\",";
  539. tstr = sPL->m_pathvar.ToUTF8();
  540. cfgFile << "\"" << tstr.size() << ":" << tstr << "\",";
  541. tstr = sPL->m_description.ToUTF8();
  542. cfgFile << "\"" << tstr.size() << ":" << tstr << "\"\n";
  543. ++sPL;
  544. }
  545. bool bad = cfgFile.bad();
  546. cfgFile.close();
  547. if( bad )
  548. {
  549. wxMessageBox( _( "Problems writing configuration file" ),
  550. _( "Write 3D search path list" ) );
  551. return false;
  552. }
  553. return true;
  554. }
  555. wxString S3D_FILENAME_RESOLVER::ShortenPath( const wxString& aFullPathName )
  556. {
  557. wxString fname = aFullPathName;
  558. if( m_Paths.empty() )
  559. createPathList();
  560. std::list< S3D_ALIAS >::const_iterator sL = m_Paths.begin();
  561. std::list< S3D_ALIAS >::const_iterator eL = m_Paths.end();
  562. size_t idx;
  563. // test for files within the current project directory
  564. // and KISYS3DMOD directory
  565. for( int i = 0; i < 2 && sL != eL; ++i )
  566. {
  567. if( !sL->m_pathexp.empty() )
  568. {
  569. wxFileName fpath( sL->m_pathexp, wxT( "" ) );
  570. wxString fps = fpath.GetPathWithSep();
  571. idx = fname.find( fps );
  572. if( std::string::npos != idx && 0 == idx )
  573. {
  574. fname = fname.substr( fps.size() );
  575. return fname;
  576. }
  577. }
  578. ++sL;
  579. }
  580. while( sL != eL )
  581. {
  582. // undefined paths and duplicates do not participate
  583. // in the file name shortening procedure
  584. if( sL->m_pathexp.empty() || sL->m_duplicate )
  585. {
  586. ++sL;
  587. continue;
  588. }
  589. wxFileName fpath( sL->m_pathexp, wxT( "" ) );
  590. wxString fps = fpath.GetPathWithSep();
  591. wxString tname;
  592. idx = fname.find( fps );
  593. if( std::string::npos != idx && 0 == idx )
  594. {
  595. fname = fname.substr( fps.size() );
  596. #ifdef _WIN32
  597. // ensure only the '/' separator is used in the internal name
  598. fname.Replace( wxT( "\\" ), wxT( "/" ) );
  599. #endif
  600. tname = ":";
  601. tname.append( sL->m_alias );
  602. tname.append( ":" );
  603. tname.append( fname );
  604. return tname;
  605. }
  606. ++sL;
  607. }
  608. #ifdef _WIN32
  609. // it is strange to convert an MSWin full path to use the
  610. // UNIX separator but this is done for consistency and can
  611. // be helpful even when transferring project files from
  612. // MSWin to *NIX.
  613. fname.Replace( wxT( "\\" ), wxT( "/" ) );
  614. #endif
  615. return fname;
  616. }
  617. const std::list< S3D_ALIAS >* S3D_FILENAME_RESOLVER::GetPaths( void )
  618. {
  619. return &m_Paths;
  620. }
  621. bool S3D_FILENAME_RESOLVER::SplitAlias( const wxString& aFileName,
  622. wxString& anAlias, wxString& aRelPath )
  623. {
  624. anAlias.clear();
  625. aRelPath.clear();
  626. if( !aFileName.StartsWith( wxT( ":" ) ) )
  627. return false;
  628. size_t tagpos = aFileName.find( wxT( ":" ), 1 );
  629. if( wxString::npos == tagpos || 1 == tagpos )
  630. return false;
  631. if( tagpos + 1 >= aFileName.length() )
  632. return false;
  633. anAlias = aFileName.substr( 1, tagpos - 1 );
  634. aRelPath = aFileName.substr( tagpos + 1 );
  635. return true;
  636. }
  637. static bool getHollerith( const std::string& aString, size_t& aIndex, wxString& aResult )
  638. {
  639. aResult.clear();
  640. if( aIndex < 0 || aIndex >= aString.size() )
  641. {
  642. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  643. wxString errmsg = _( "bad Hollerith string on line" );
  644. std::cerr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'\n";
  645. return false;
  646. }
  647. size_t i2 = aString.find( '"', aIndex );
  648. if( std::string::npos == i2 )
  649. {
  650. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  651. wxString errmsg = _( "missing opening quote mark in config file" );
  652. std::cerr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'\n";
  653. return false;
  654. }
  655. ++i2;
  656. if( i2 >= aString.size() )
  657. {
  658. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  659. wxString errmsg = _( "invalid entry (unexpected end of line)" );
  660. std::cerr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'\n";
  661. return false;
  662. }
  663. std::string tnum;
  664. while( aString[i2] >= '0' && aString[i2] <= '9' )
  665. tnum.append( 1, aString[i2++] );
  666. if( tnum.empty() || aString[i2++] != ':' )
  667. {
  668. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  669. wxString errmsg = _( "bad Hollerith string on line" );
  670. std::cerr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'\n";
  671. return false;
  672. }
  673. std::istringstream istr;
  674. istr.str( tnum );
  675. size_t nchars;
  676. istr >> nchars;
  677. if( (i2 + nchars) >= aString.size() )
  678. {
  679. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  680. wxString errmsg = _( "invalid entry (unexpected end of line)" );
  681. std::cerr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'\n";
  682. return false;
  683. }
  684. if( nchars > 0 )
  685. {
  686. aResult = aString.substr( i2, nchars );
  687. i2 += nchars;
  688. }
  689. if( aString[i2] != '"' )
  690. {
  691. std::cerr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  692. wxString errmsg = _( "missing closing quote mark in config file" );
  693. std::cerr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'\n";
  694. return false;
  695. }
  696. aIndex = i2 + 1;
  697. return true;
  698. }
  699. bool S3D_FILENAME_RESOLVER::ValidateFileName( const wxString& aFileName, bool& hasAlias )
  700. {
  701. // Rules:
  702. // 1. The generic form of an aliased 3D relative path is:
  703. // ALIAS:relative/path
  704. // 2. ALIAS is a UTF string excluding wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" )
  705. // 3. The relative path must be a valid relative path for the platform
  706. hasAlias = false;
  707. if( aFileName.empty() )
  708. return false;
  709. wxString filename = aFileName;
  710. wxString lpath;
  711. size_t pos0 = aFileName.find( ':' );
  712. // ensure that the file separators suit the current platform
  713. #ifdef __WINDOWS__
  714. filename.Replace( wxT( "/" ), wxT( "\\" ) );
  715. // if we see the :\ pattern then it must be a drive designator
  716. if( pos0 != wxString::npos )
  717. {
  718. size_t pos1 = aFileName.find( wxT( ":\\" ) );
  719. if( pos1 != wxString::npos && ( pos1 != pos0 || pos1 != 1 ) )
  720. return false;
  721. // if we have a drive designator then we have no alias
  722. if( pos1 != wxString::npos )
  723. pos0 = wxString::npos;
  724. }
  725. #else
  726. filename.Replace( wxT( "\\" ), wxT( "/" ) );
  727. #endif
  728. // names may not end with ':'
  729. if( pos0 == aFileName.length() -1 )
  730. return false;
  731. if( pos0 != wxString::npos )
  732. {
  733. // ensure the alias component is not empty
  734. if( pos0 == 0 )
  735. return false;
  736. lpath = filename.substr( 0, pos0 );
  737. if( wxString::npos != lpath.find_first_of( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
  738. return false;
  739. hasAlias = true;
  740. lpath = aFileName.substr( pos0 + 1 );
  741. }
  742. else
  743. {
  744. lpath = aFileName;
  745. }
  746. if( wxString::npos != lpath.find_first_of( wxFileName::GetForbiddenChars() ) )
  747. return false;
  748. return true;
  749. }