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.

641 lines
19 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014-2020 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <eda_base_frame.h>
  26. #include <project.h>
  27. #include <common.h>
  28. #include <reporter.h>
  29. #include <macros.h>
  30. #include <mutex>
  31. #include <wx/process.h>
  32. #include <wx/config.h>
  33. #include <wx/utils.h>
  34. #include <wx/stdpaths.h>
  35. #include <wx/url.h>
  36. #include <wx/wx.h>
  37. int ProcessExecute( const wxString& aCommandLine, int aFlags, wxProcess *callback )
  38. {
  39. return (int) wxExecute( aCommandLine, aFlags, callback );
  40. }
  41. enum Bracket
  42. {
  43. Bracket_None,
  44. Bracket_Normal = ')',
  45. Bracket_Curly = '}',
  46. #ifdef __WINDOWS__
  47. Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
  48. #endif
  49. Bracket_Max
  50. };
  51. wxString ExpandTextVars( const wxString& aSource, const PROJECT* aProject )
  52. {
  53. return ExpandTextVars( aSource, nullptr, nullptr, aProject );
  54. }
  55. wxString ExpandTextVars( const wxString& aSource,
  56. const std::function<bool( wxString* )>* aLocalResolver,
  57. const std::function<bool( wxString* )>* aFallbackResolver,
  58. const PROJECT* aProject )
  59. {
  60. wxString newbuf;
  61. size_t sourceLen = aSource.length();
  62. newbuf.Alloc( sourceLen ); // best guess (improves performance)
  63. for( size_t i = 0; i < sourceLen; ++i )
  64. {
  65. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  66. {
  67. wxString token;
  68. for( i = i + 2; i < sourceLen; ++i )
  69. {
  70. if( aSource[i] == '}' )
  71. break;
  72. else
  73. token.append( aSource[i] );
  74. }
  75. if( token.IsEmpty() )
  76. continue;
  77. if( aLocalResolver && (*aLocalResolver)( &token ) )
  78. {
  79. newbuf.append( token );
  80. }
  81. else if( aProject && aProject->TextVarResolver( &token ) )
  82. {
  83. newbuf.append( token );
  84. }
  85. else if( aFallbackResolver && (*aFallbackResolver)( &token ) )
  86. {
  87. newbuf.append( token );
  88. }
  89. else
  90. {
  91. // Token not resolved: leave the reference unchanged
  92. newbuf.append( "${" + token + "}" );
  93. }
  94. }
  95. else
  96. {
  97. newbuf.append( aSource[i] );
  98. }
  99. }
  100. return newbuf;
  101. }
  102. //
  103. // Stolen from wxExpandEnvVars and then heavily optimized
  104. //
  105. wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject )
  106. {
  107. size_t strlen = str.length();
  108. wxString strResult;
  109. strResult.Alloc( strlen ); // best guess (improves performance)
  110. for( size_t n = 0; n < strlen; n++ )
  111. {
  112. wxUniChar str_n = str[n];
  113. switch( str_n.GetValue() )
  114. {
  115. #ifdef __WINDOWS__
  116. case wxT( '%' ):
  117. #endif // __WINDOWS__
  118. case wxT( '$' ):
  119. {
  120. Bracket bracket;
  121. #ifdef __WINDOWS__
  122. if( str_n == wxT( '%' ) )
  123. bracket = Bracket_Windows;
  124. else
  125. #endif // __WINDOWS__
  126. if( n == strlen - 1 )
  127. {
  128. bracket = Bracket_None;
  129. }
  130. else
  131. {
  132. switch( str[n + 1].GetValue() )
  133. {
  134. case wxT( '(' ):
  135. bracket = Bracket_Normal;
  136. str_n = str[++n]; // skip the bracket
  137. break;
  138. case wxT( '{' ):
  139. bracket = Bracket_Curly;
  140. str_n = str[++n]; // skip the bracket
  141. break;
  142. default:
  143. bracket = Bracket_None;
  144. }
  145. }
  146. size_t m = n + 1;
  147. wxUniChar str_m = str[m];
  148. while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
  149. {
  150. if( ++m == strlen )
  151. {
  152. str_m = 0;
  153. break;
  154. }
  155. str_m = str[m];
  156. }
  157. wxString strVarName( str.c_str() + n + 1, m - n - 1 );
  158. // NB: use wxGetEnv instead of wxGetenv as otherwise variables
  159. // set through wxSetEnv may not be read correctly!
  160. bool expanded = false;
  161. wxString tmp = strVarName;
  162. if( aProject && aProject->TextVarResolver( &tmp ) )
  163. {
  164. strResult += tmp;
  165. expanded = true;
  166. }
  167. else if( wxGetEnv( strVarName, &tmp ) )
  168. {
  169. strResult += tmp;
  170. expanded = true;
  171. }
  172. else
  173. {
  174. // variable doesn't exist => don't change anything
  175. #ifdef __WINDOWS__
  176. if ( bracket != Bracket_Windows )
  177. #endif
  178. if ( bracket != Bracket_None )
  179. strResult << str[n - 1];
  180. strResult << str_n << strVarName;
  181. }
  182. // check the closing bracket
  183. if( bracket != Bracket_None )
  184. {
  185. if( m == strlen || str_m != (wxChar)bracket )
  186. {
  187. // under MSW it's common to have '%' characters in the registry
  188. // and it's annoying to have warnings about them each time, so
  189. // ignore them silently if they are not used for env vars
  190. //
  191. // under Unix, OTOH, this warning could be useful for the user to
  192. // understand why isn't the variable expanded as intended
  193. #ifndef __WINDOWS__
  194. wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
  195. "at position %u in '%s'." ),
  196. (char)bracket, (unsigned int) (m + 1), str.c_str() );
  197. #endif // __WINDOWS__
  198. }
  199. else
  200. {
  201. // skip closing bracket unless the variables wasn't expanded
  202. if( !expanded )
  203. strResult << (wxChar)bracket;
  204. m++;
  205. }
  206. }
  207. n = m - 1; // skip variable name
  208. str_n = str[n];
  209. }
  210. break;
  211. case wxT( '\\' ):
  212. // backslash can be used to suppress special meaning of % and $
  213. if( n != strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
  214. {
  215. str_n = str[++n];
  216. strResult += str_n;
  217. break;
  218. }
  219. KI_FALLTHROUGH;
  220. default:
  221. strResult += str_n;
  222. }
  223. }
  224. #ifndef __WINDOWS__
  225. if( strResult.StartsWith( "~" ) )
  226. strResult.Replace( "~", wxGetHomeDir(), false );
  227. #endif // __WINDOWS__
  228. return strResult;
  229. }
  230. const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aProject )
  231. {
  232. // wxGetenv( wchar_t* ) is not re-entrant on linux.
  233. // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
  234. static std::mutex getenv_mutex;
  235. std::lock_guard<std::mutex> lock( getenv_mutex );
  236. // We reserve the right to do this another way, by providing our own member function.
  237. return KIwxExpandEnvVars( aString, aProject );
  238. }
  239. const wxString ResolveUriByEnvVars( const wxString& aUri, PROJECT* aProject )
  240. {
  241. wxString uri = ExpandTextVars( aUri, aProject );
  242. // URL-like URI: return as is.
  243. wxURL url( uri );
  244. if( url.GetError() == wxURL_NOERR )
  245. return uri;
  246. // Otherwise, the path points to a local file. Resolve environment variables if any.
  247. return ExpandEnvVarSubstitutions( aUri, aProject );
  248. }
  249. bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName,
  250. const wxString& aBaseFilename,
  251. REPORTER* aReporter )
  252. {
  253. wxString msg;
  254. wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
  255. // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
  256. // already an absolute path) absolute:
  257. if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
  258. {
  259. if( aReporter )
  260. {
  261. msg.Printf( _( "Cannot make path \"%s\" absolute with respect to \"%s\"." ),
  262. aTargetFullFileName->GetPath(),
  263. baseFilePath );
  264. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  265. }
  266. return false;
  267. }
  268. // Ensure the path of aTargetFullFileName exists, and create it if needed:
  269. wxString outputPath( aTargetFullFileName->GetPath() );
  270. if( !wxFileName::DirExists( outputPath ) )
  271. {
  272. // Make every directory provided when the provided path doesn't exist
  273. if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
  274. {
  275. if( aReporter )
  276. {
  277. msg.Printf( _( "Output directory \"%s\" created.\n" ), outputPath );
  278. aReporter->Report( msg, RPT_SEVERITY_INFO );
  279. return true;
  280. }
  281. }
  282. else
  283. {
  284. if( aReporter )
  285. {
  286. msg.Printf( _( "Cannot create output directory \"%s\".\n" ), outputPath );
  287. aReporter->Report( msg, RPT_SEVERITY_ERROR );
  288. }
  289. return false;
  290. }
  291. }
  292. return true;
  293. }
  294. // add this only if it is not in wxWidgets (for instance before 3.1.0)
  295. #ifdef USE_KICAD_WXSTRING_HASH
  296. size_t std::hash<wxString>::operator()( const wxString& s ) const
  297. {
  298. return std::hash<std::wstring>{}( s.ToStdWstring() );
  299. }
  300. #endif
  301. #ifdef USE_KICAD_WXPOINT_LESS_AND_HASH
  302. size_t std::hash<wxPoint>::operator() ( const wxPoint& k ) const
  303. {
  304. auto xhash = std::hash<int>()( k.x );
  305. // 0x9e3779b9 is 2^33 / ( 1 + sqrt(5) )
  306. // Adding this value ensures that consecutive bits of y will not be close to each other
  307. // decreasing the likelihood of hash collision in similar values of x and y
  308. return xhash ^ ( std::hash<int>()( k.y ) + 0x9e3779b9 + ( xhash << 6 ) + ( xhash >> 2 ) );
  309. }
  310. bool std::less<wxPoint>::operator()( const wxPoint& aA, const wxPoint& aB ) const
  311. {
  312. if( aA.x == aB.x )
  313. return aA.y < aB.y;
  314. return aA.x < aB.x;
  315. }
  316. #endif
  317. std::ostream& operator<<( std::ostream& out, const wxSize& size )
  318. {
  319. out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\"";
  320. return out;
  321. }
  322. std::ostream& operator<<( std::ostream& out, const wxPoint& pt )
  323. {
  324. out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\"";
  325. return out;
  326. }
  327. /**
  328. * Performance enhancements to file and directory operations.
  329. *
  330. * Note: while it's annoying to have to make copies of wxWidgets stuff and then
  331. * add platform-specific performance optimizations, the following routines offer
  332. * SIGNIFICANT performance benefits.
  333. */
  334. /**
  335. * A copy of wxMatchWild(), which wxWidgets attributes to Douglas A. Lewis
  336. * <dalewis@cs.Buffalo.EDU> and ircII's reg.c.
  337. *
  338. * This version is modified to skip any encoding conversions (for performance).
  339. */
  340. bool matchWild( const char* pat, const char* text, bool dot_special )
  341. {
  342. if( !*text )
  343. {
  344. /* Match if both are empty. */
  345. return !*pat;
  346. }
  347. const char *m = pat,
  348. *n = text,
  349. *ma = NULL,
  350. *na = NULL;
  351. int just = 0,
  352. acount = 0,
  353. count = 0;
  354. if( dot_special && (*n == '.') )
  355. {
  356. /* Never match so that hidden Unix files
  357. * are never found. */
  358. return false;
  359. }
  360. for(;;)
  361. {
  362. if( *m == '*' )
  363. {
  364. ma = ++m;
  365. na = n;
  366. just = 1;
  367. acount = count;
  368. }
  369. else if( *m == '?' )
  370. {
  371. m++;
  372. if( !*n++ )
  373. return false;
  374. }
  375. else
  376. {
  377. if( *m == '\\' )
  378. {
  379. m++;
  380. /* Quoting "nothing" is a bad thing */
  381. if( !*m )
  382. return false;
  383. }
  384. if( !*m )
  385. {
  386. /*
  387. * If we are out of both strings or we just
  388. * saw a wildcard, then we can say we have a
  389. * match
  390. */
  391. if( !*n )
  392. return true;
  393. if( just )
  394. return true;
  395. just = 0;
  396. goto not_matched;
  397. }
  398. /*
  399. * We could check for *n == NULL at this point, but
  400. * since it's more common to have a character there,
  401. * check to see if they match first (m and n) and
  402. * then if they don't match, THEN we can check for
  403. * the NULL of n
  404. */
  405. just = 0;
  406. if( *m == *n )
  407. {
  408. m++;
  409. count++;
  410. n++;
  411. }
  412. else
  413. {
  414. not_matched:
  415. /*
  416. * If there are no more characters in the
  417. * string, but we still need to find another
  418. * character (*m != NULL), then it will be
  419. * impossible to match it
  420. */
  421. if( !*n )
  422. return false;
  423. if( ma )
  424. {
  425. m = ma;
  426. n = ++na;
  427. count = acount;
  428. }
  429. else
  430. return false;
  431. }
  432. }
  433. }
  434. }
  435. /**
  436. * A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function
  437. * private to src/common/filename.cpp.
  438. */
  439. #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
  440. // Convert between wxDateTime and FILETIME which is a 64-bit value representing
  441. // the number of 100-nanosecond intervals since January 1, 1601 UTC.
  442. //
  443. // This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
  444. static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
  445. static void ConvertFileTimeToWx( wxDateTime *dt, const FILETIME &ft )
  446. {
  447. wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
  448. t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
  449. t -= EPOCH_OFFSET_IN_MSEC;
  450. *dt = wxDateTime( t );
  451. }
  452. #endif // wxUSE_DATETIME && __WIN32__
  453. /**
  454. * TimestampDir
  455. *
  456. * This routine offers SIGNIFICANT performance benefits over using wxWidgets to gather
  457. * timestamps from matching files in a directory.
  458. * @param aDirPath the directory to search
  459. * @param aFilespec a (wildcarded) file spec to match against
  460. * @return a hash of the last-mod-dates of all matching files in the directory
  461. */
  462. long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
  463. {
  464. long long timestamp = 0;
  465. #if defined( __WIN32__ )
  466. // Win32 version.
  467. // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
  468. // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
  469. // conversion by staying on the MSW side of things.
  470. std::wstring filespec( aDirPath.t_str() );
  471. filespec += '\\';
  472. filespec += aFilespec.t_str();
  473. WIN32_FIND_DATA findData;
  474. wxDateTime lastModDate;
  475. HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
  476. if( fileHandle != INVALID_HANDLE_VALUE )
  477. {
  478. do
  479. {
  480. ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
  481. timestamp += lastModDate.GetValue().GetValue();
  482. timestamp += findData.nFileSizeLow; // Get the file size (partial) as well to check for sneaky changes
  483. }
  484. while ( FindNextFile( fileHandle, &findData ) != 0 );
  485. }
  486. FindClose( fileHandle );
  487. #else
  488. // POSIX version.
  489. // Save time by not converting between encodings -- do everything on the file-system side.
  490. std::string filespec( aFilespec.fn_str() );
  491. std::string dir_path( aDirPath.fn_str() );
  492. DIR* dir = opendir( dir_path.c_str() );
  493. if( dir )
  494. {
  495. for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
  496. {
  497. if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
  498. continue;
  499. std::string entry_path = dir_path + '/' + dir_entry->d_name;
  500. struct stat entry_stat;
  501. if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
  502. {
  503. // Timestamp the source file, not the symlink
  504. if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
  505. {
  506. char buffer[ PATH_MAX + 1 ];
  507. ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
  508. if( pathLen > 0 )
  509. {
  510. struct stat linked_stat;
  511. buffer[ pathLen ] = '\0';
  512. entry_path = dir_path + buffer;
  513. if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
  514. {
  515. entry_stat = linked_stat;
  516. }
  517. else
  518. {
  519. // if we couldn't lstat the linked file we'll have to just use
  520. // the symbolic link info
  521. }
  522. }
  523. }
  524. if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
  525. {
  526. timestamp += entry_stat.st_mtime * 1000;
  527. timestamp += entry_stat.st_size; // Get the file size as well to check for sneaky changes
  528. }
  529. }
  530. else
  531. {
  532. // if we couldn't lstat the file itself all we can do is use the name
  533. timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
  534. }
  535. }
  536. closedir( dir );
  537. }
  538. #endif
  539. return timestamp;
  540. }