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.

923 lines
25 KiB

14 years ago
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 1992-2019 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. /**
  26. * @file common.cpp
  27. */
  28. #include <fctsys.h>
  29. #include <eda_base_frame.h>
  30. #include <base_struct.h>
  31. #include <common.h>
  32. #include <macros.h>
  33. #include <base_units.h>
  34. #include <reporter.h>
  35. #include <mutex>
  36. #include <wx/process.h>
  37. #include <wx/config.h>
  38. #include <wx/utils.h>
  39. #include <wx/stdpaths.h>
  40. #include <wx/url.h>
  41. #include <pgm_base.h>
  42. using KIGFX::COLOR4D;
  43. /**
  44. * Global variables definitions.
  45. *
  46. * TODO: All of these variables should be moved into the class were they
  47. * are defined and used. Most of them probably belong in the
  48. * application class.
  49. */
  50. COLOR4D g_GhostColor;
  51. #if defined( _WIN32 ) && defined( DEBUG )
  52. // a wxAssertHandler_t function to filter wxWidgets alert messages when reading/writing a file
  53. // when switching the locale to LC_NUMERIC, "C"
  54. // It is used in class LOCALE_IO to hide a useless (in kicad) wxWidgets alert message
  55. void KiAssertFilter( const wxString &file, int line,
  56. const wxString &func, const wxString &cond,
  57. const wxString &msg)
  58. {
  59. if( !msg.Contains( "Decimal separator mismatch" ) )
  60. wxTheApp->OnAssertFailure( file, line, func, cond, msg );
  61. }
  62. #endif
  63. std::atomic<unsigned int> LOCALE_IO::m_c_count( 0 );
  64. // Note on Windows, setlocale( LC_NUMERIC, "C" ) works fine to read/write
  65. // files with floating point numbers, but generates a overzealous wx alert
  66. // in some cases (reading a bitmap for instance)
  67. // So we disable alerts during the time a file is read or written
  68. LOCALE_IO::LOCALE_IO()
  69. {
  70. // use thread safe, atomic operation
  71. if( m_c_count++ == 0 )
  72. {
  73. // Store the user locale name, to restore this locale later, in dtor
  74. m_user_locale = setlocale( LC_NUMERIC, nullptr );
  75. #if defined( _WIN32 ) && defined( DEBUG )
  76. // Disable wxWidgets alerts
  77. wxSetAssertHandler( KiAssertFilter );
  78. #endif
  79. // Switch the locale to C locale, to read/write files with fp numbers
  80. setlocale( LC_NUMERIC, "C" );
  81. }
  82. }
  83. LOCALE_IO::~LOCALE_IO()
  84. {
  85. // use thread safe, atomic operation
  86. if( --m_c_count == 0 )
  87. {
  88. // revert to the user locale
  89. setlocale( LC_NUMERIC, m_user_locale.c_str() );
  90. #if defined( _WIN32 ) && defined( DEBUG )
  91. // Enable wxWidgets alerts
  92. wxSetDefaultAssertHandler();
  93. #endif
  94. }
  95. }
  96. wxSize GetTextSize( const wxString& aSingleLine, wxWindow* aWindow )
  97. {
  98. wxCoord width;
  99. wxCoord height;
  100. {
  101. wxClientDC dc( aWindow );
  102. dc.SetFont( aWindow->GetFont() );
  103. dc.GetTextExtent( aSingleLine, &width, &height );
  104. }
  105. return wxSize( width, height );
  106. }
  107. bool EnsureTextCtrlWidth( wxTextCtrl* aCtrl, const wxString* aString )
  108. {
  109. wxWindow* window = aCtrl->GetParent();
  110. if( !window )
  111. window = aCtrl;
  112. wxString ctrlText;
  113. if( !aString )
  114. {
  115. ctrlText = aCtrl->GetValue();
  116. aString = &ctrlText;
  117. }
  118. wxSize textz = GetTextSize( *aString, window );
  119. wxSize ctrlz = aCtrl->GetSize();
  120. if( ctrlz.GetWidth() < textz.GetWidth() + 10 )
  121. {
  122. ctrlz.SetWidth( textz.GetWidth() + 10 );
  123. aCtrl->SetSizeHints( ctrlz );
  124. return true;
  125. }
  126. return false;
  127. }
  128. void SelectReferenceNumber( wxTextEntry* aTextEntry )
  129. {
  130. wxString ref = aTextEntry->GetValue();
  131. if( ref.find_first_of( '?' ) != ref.npos )
  132. {
  133. aTextEntry->SetSelection( ref.find_first_of( '?' ), ref.find_last_of( '?' ) + 1 );
  134. }
  135. else
  136. {
  137. wxString num = ref;
  138. while( !num.IsEmpty() && ( !isdigit( num.Last() ) || !isdigit( num.GetChar( 0 ) ) ) )
  139. {
  140. if( !isdigit( num.Last() ) )
  141. num.RemoveLast();
  142. if( !isdigit( num.GetChar ( 0 ) ) )
  143. num = num.Right( num.Length() - 1);
  144. }
  145. aTextEntry->SetSelection( ref.Find( num ), ref.Find( num ) + num.Length() );
  146. if( num.IsEmpty() )
  147. aTextEntry->SetSelection( -1, -1 );
  148. }
  149. }
  150. void wxStringSplit( const wxString& aText, wxArrayString& aStrings, wxChar aSplitter )
  151. {
  152. wxString tmp;
  153. for( unsigned ii = 0; ii < aText.Length(); ii++ )
  154. {
  155. if( aText[ii] == aSplitter )
  156. {
  157. aStrings.Add( tmp );
  158. tmp.Clear();
  159. }
  160. else
  161. tmp << aText[ii];
  162. }
  163. if( !tmp.IsEmpty() )
  164. {
  165. aStrings.Add( tmp );
  166. }
  167. }
  168. int ProcessExecute( const wxString& aCommandLine, int aFlags, wxProcess *callback )
  169. {
  170. return wxExecute( aCommandLine, aFlags, callback );
  171. }
  172. timestamp_t GetNewTimeStamp()
  173. {
  174. static timestamp_t oldTimeStamp;
  175. timestamp_t newTimeStamp;
  176. newTimeStamp = time( NULL );
  177. if( newTimeStamp <= oldTimeStamp )
  178. newTimeStamp = oldTimeStamp + 1;
  179. oldTimeStamp = newTimeStamp;
  180. return newTimeStamp;
  181. }
  182. double RoundTo0( double x, double precision )
  183. {
  184. assert( precision != 0 );
  185. long long ix = KiROUND( x * precision );
  186. if ( x < 0.0 )
  187. ix = -ix;
  188. int remainder = ix % 10; // remainder is in precision mm
  189. if( remainder <= 2 )
  190. ix -= remainder; // truncate to the near number
  191. else if( remainder >= 8 )
  192. ix += 10 - remainder; // round to near number
  193. if ( x < 0 )
  194. ix = -ix;
  195. return (double) ix / precision;
  196. }
  197. std::unique_ptr<wxConfigBase> GetNewConfig( const wxString& aProgName )
  198. {
  199. wxFileName configname;
  200. configname.AssignDir( GetKicadConfigPath() );
  201. configname.SetFullName( aProgName );
  202. // explicitly use wxFileConfig to prevent storing any settings in the system registry on Windows
  203. return std::make_unique<wxFileConfig>( wxT( "" ), wxT( "" ), configname.GetFullPath() );
  204. }
  205. wxString GetKicadConfigPath()
  206. {
  207. wxFileName cfgpath;
  208. // http://docs.wxwidgets.org/3.0/classwx_standard_paths.html#a7c7cf595d94d29147360d031647476b0
  209. cfgpath.AssignDir( wxStandardPaths::Get().GetUserConfigDir() );
  210. // GetUserConfigDir() does not default to ~/.config which is the current standard
  211. // configuration file location on Linux. This has been fixed in later versions of wxWidgets.
  212. #if !defined( __WXMSW__ ) && !defined( __WXMAC__ )
  213. wxArrayString dirs = cfgpath.GetDirs();
  214. if( dirs.Last() != ".config" )
  215. cfgpath.AppendDir( ".config" );
  216. #endif
  217. wxString envstr;
  218. // This shouldn't cause any issues on Windows or MacOS.
  219. if( wxGetEnv( wxT( "XDG_CONFIG_HOME" ), &envstr ) && !envstr.IsEmpty() )
  220. {
  221. // Override the assignment above with XDG_CONFIG_HOME
  222. cfgpath.AssignDir( envstr );
  223. }
  224. cfgpath.AppendDir( TO_STR( KICAD_CONFIG_DIR ) );
  225. // Use KICAD_CONFIG_HOME to allow the user to force a specific configuration path.
  226. if( wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), &envstr ) && !envstr.IsEmpty() )
  227. {
  228. // Override the assignment above with KICAD_CONFIG_HOME
  229. cfgpath.AssignDir( envstr );
  230. }
  231. if( !cfgpath.DirExists() )
  232. {
  233. cfgpath.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
  234. }
  235. return cfgpath.GetPath();
  236. }
  237. enum Bracket
  238. {
  239. Bracket_None,
  240. Bracket_Normal = ')',
  241. Bracket_Curly = '}',
  242. #ifdef __WINDOWS__
  243. Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
  244. #endif
  245. Bracket_Max
  246. };
  247. //
  248. // Stolen from wxExpandEnvVars and then heavily optimized
  249. //
  250. wxString KIwxExpandEnvVars(const wxString& str)
  251. {
  252. size_t strlen = str.length();
  253. wxString strResult;
  254. strResult.Alloc( strlen );
  255. for( size_t n = 0; n < strlen; n++ )
  256. {
  257. wxUniChar str_n = str[n];
  258. switch( str_n.GetValue() )
  259. {
  260. #ifdef __WINDOWS__
  261. case wxT( '%' ):
  262. #endif // __WINDOWS__
  263. case wxT( '$' ):
  264. {
  265. Bracket bracket;
  266. #ifdef __WINDOWS__
  267. if( str_n == wxT( '%' ) )
  268. bracket = Bracket_Windows;
  269. else
  270. #endif // __WINDOWS__
  271. if( n == strlen - 1 )
  272. {
  273. bracket = Bracket_None;
  274. }
  275. else
  276. {
  277. switch( str[n + 1].GetValue() )
  278. {
  279. case wxT( '(' ):
  280. bracket = Bracket_Normal;
  281. str_n = str[++n]; // skip the bracket
  282. break;
  283. case wxT( '{' ):
  284. bracket = Bracket_Curly;
  285. str_n = str[++n]; // skip the bracket
  286. break;
  287. default:
  288. bracket = Bracket_None;
  289. }
  290. }
  291. size_t m = n + 1;
  292. wxUniChar str_m = str[m];
  293. while( m < strlen && ( wxIsalnum( str_m ) || str_m == wxT( '_' ) ) )
  294. str_m = str[++m];
  295. wxString strVarName( str.c_str() + n + 1, m - n - 1 );
  296. #ifdef __WXWINCE__
  297. const bool expanded = false;
  298. #else
  299. // NB: use wxGetEnv instead of wxGetenv as otherwise variables
  300. // set through wxSetEnv may not be read correctly!
  301. bool expanded = false;
  302. wxString tmp;
  303. if( wxGetEnv( strVarName, &tmp ) )
  304. {
  305. strResult += tmp;
  306. expanded = true;
  307. }
  308. else
  309. #endif
  310. {
  311. // variable doesn't exist => don't change anything
  312. #ifdef __WINDOWS__
  313. if ( bracket != Bracket_Windows )
  314. #endif
  315. if ( bracket != Bracket_None )
  316. strResult << str[n - 1];
  317. strResult << str_n << strVarName;
  318. }
  319. // check the closing bracket
  320. if( bracket != Bracket_None )
  321. {
  322. if( m == strlen || str_m != (wxChar)bracket )
  323. {
  324. // under MSW it's common to have '%' characters in the registry
  325. // and it's annoying to have warnings about them each time, so
  326. // ignore them silently if they are not used for env vars
  327. //
  328. // under Unix, OTOH, this warning could be useful for the user to
  329. // understand why isn't the variable expanded as intended
  330. #ifndef __WINDOWS__
  331. wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
  332. "at position %u in '%s'." ),
  333. (char)bracket, (unsigned int) (m + 1), str.c_str() );
  334. #endif // __WINDOWS__
  335. }
  336. else
  337. {
  338. // skip closing bracket unless the variables wasn't expanded
  339. if( !expanded )
  340. strResult << (wxChar)bracket;
  341. str_m = str[++m];
  342. }
  343. }
  344. n = m - 1; // skip variable name
  345. str_n = str[n];
  346. }
  347. break;
  348. case wxT( '\\' ):
  349. // backslash can be used to suppress special meaning of % and $
  350. if( n != strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
  351. {
  352. str_n = str[++n];
  353. strResult += str_n;
  354. break;
  355. }
  356. //else: fall through
  357. default:
  358. strResult += str_n;
  359. }
  360. }
  361. return strResult;
  362. }
  363. const wxString ExpandEnvVarSubstitutions( const wxString& aString )
  364. {
  365. // wxGetenv( wchar_t* ) is not re-entrant on linux.
  366. // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
  367. static std::mutex getenv_mutex;
  368. std::lock_guard<std::mutex> lock( getenv_mutex );
  369. // We reserve the right to do this another way, by providing our own member function.
  370. return KIwxExpandEnvVars( aString );
  371. }
  372. const wxString ResolveUriByEnvVars( const wxString& aUri )
  373. {
  374. // URL-like URI: return as is.
  375. wxURL url( aUri );
  376. if( url.GetError() == wxURL_NOERR )
  377. return aUri;
  378. // Otherwise, the path points to a local file. Resolve environment
  379. // variables if any.
  380. return ExpandEnvVarSubstitutions( aUri );
  381. }
  382. bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName,
  383. const wxString& aBaseFilename,
  384. REPORTER* aReporter )
  385. {
  386. wxString msg;
  387. wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
  388. // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
  389. // already an absolute path) absolute:
  390. if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
  391. {
  392. if( aReporter )
  393. {
  394. msg.Printf( _( "Cannot make path \"%s\" absolute with respect to \"%s\"." ),
  395. GetChars( aTargetFullFileName->GetPath() ),
  396. GetChars( baseFilePath ) );
  397. aReporter->Report( msg, REPORTER::RPT_ERROR );
  398. }
  399. return false;
  400. }
  401. // Ensure the path of aTargetFullFileName exists, and create it if needed:
  402. wxString outputPath( aTargetFullFileName->GetPath() );
  403. if( !wxFileName::DirExists( outputPath ) )
  404. {
  405. if( wxMkdir( outputPath ) )
  406. {
  407. if( aReporter )
  408. {
  409. msg.Printf( _( "Output directory \"%s\" created.\n" ), GetChars( outputPath ) );
  410. aReporter->Report( msg, REPORTER::RPT_INFO );
  411. return true;
  412. }
  413. }
  414. else
  415. {
  416. if( aReporter )
  417. {
  418. msg.Printf( _( "Cannot create output directory \"%s\".\n" ),
  419. GetChars( outputPath ) );
  420. aReporter->Report( msg, REPORTER::RPT_ERROR );
  421. }
  422. return false;
  423. }
  424. }
  425. return true;
  426. }
  427. #ifdef __WXMAC__
  428. wxString GetOSXKicadUserDataDir()
  429. {
  430. // According to wxWidgets documentation for GetUserDataDir:
  431. // Mac: ~/Library/Application Support/appname
  432. wxFileName udir( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString );
  433. // Since appname is different if started via launcher or standalone binary
  434. // map all to "kicad" here
  435. udir.RemoveLastDir();
  436. udir.AppendDir( "kicad" );
  437. return udir.GetPath();
  438. }
  439. wxString GetOSXKicadMachineDataDir()
  440. {
  441. return wxT( "/Library/Application Support/kicad" );
  442. }
  443. wxString GetOSXKicadDataDir()
  444. {
  445. // According to wxWidgets documentation for GetDataDir:
  446. // Mac: appname.app/Contents/SharedSupport bundle subdirectory
  447. wxFileName ddir( wxStandardPaths::Get().GetDataDir(), wxEmptyString );
  448. // This must be mapped to main bundle for everything but kicad.app
  449. const wxArrayString dirs = ddir.GetDirs();
  450. if( dirs[dirs.GetCount() - 3] != wxT( "kicad.app" ) )
  451. {
  452. // Bundle structure resp. current path is
  453. // kicad.app/Contents/Applications/<standalone>.app/Contents/SharedSupport
  454. // and will be mapped to
  455. // kicad.app/Contents/SharedSupprt
  456. ddir.RemoveLastDir();
  457. ddir.RemoveLastDir();
  458. ddir.RemoveLastDir();
  459. ddir.RemoveLastDir();
  460. ddir.AppendDir( wxT( "SharedSupport" ) );
  461. }
  462. return ddir.GetPath();
  463. }
  464. #endif
  465. // add this only if it is not in wxWidgets (for instance before 3.1.0)
  466. #ifdef USE_KICAD_WXSTRING_HASH
  467. size_t std::hash<wxString>::operator()( const wxString& s ) const
  468. {
  469. return std::hash<std::wstring>{}( s.ToStdWstring() );
  470. }
  471. #endif
  472. #ifdef USE_KICAD_WXPOINT_LESS_AND_HASH
  473. size_t std::hash<wxPoint>::operator() ( const wxPoint& k ) const
  474. {
  475. auto xhash = std::hash<int>()( k.x );
  476. // 0x9e3779b9 is 2^33 / ( 1 + sqrt(5) )
  477. // Adding this value ensures that consecutive bits of y will not be close to each other
  478. // decreasing the likelihood of hash collision in similar values of x and y
  479. return xhash ^ ( std::hash<int>()( k.y ) + 0x9e3779b9 + ( xhash << 6 ) + ( xhash >> 2 ) );
  480. }
  481. bool std::less<wxPoint>::operator()( const wxPoint& aA, const wxPoint& aB ) const
  482. {
  483. if( aA.x == aB.x )
  484. return aA.y < aB.y;
  485. return aA.x < aB.x;
  486. }
  487. #endif
  488. std::ostream& operator<<( std::ostream& out, const wxSize& size )
  489. {
  490. out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\"";
  491. return out;
  492. }
  493. std::ostream& operator<<( std::ostream& out, const wxPoint& pt )
  494. {
  495. out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\"";
  496. return out;
  497. }
  498. /**
  499. * Performance enhancements to file and directory operations.
  500. *
  501. * Note: while it's annoying to have to make copies of wxWidgets stuff and then
  502. * add platform-specific performance optimizations, the following routines offer
  503. * SIGNIFICANT performance benefits.
  504. */
  505. /**
  506. * WX_FILENAME
  507. *
  508. * A wrapper around a wxFileName which avoids expensive calls to wxFileName::SplitPath()
  509. * and string concatenations by caching the path and filename locally and only resolving
  510. * the wxFileName when it has to.
  511. */
  512. WX_FILENAME::WX_FILENAME( const wxString& aPath, const wxString& aFilename ) :
  513. m_fn( aPath, aFilename ),
  514. m_path( aPath ),
  515. m_fullName( aFilename )
  516. { }
  517. void WX_FILENAME::SetFullName( const wxString& aFileNameAndExtension )
  518. {
  519. m_fullName = aFileNameAndExtension;
  520. }
  521. wxString WX_FILENAME::GetName() const
  522. {
  523. size_t dot = m_fullName.find_last_of( wxT( '.' ) );
  524. return m_fullName.substr( 0, dot );
  525. }
  526. wxString WX_FILENAME::GetFullName() const
  527. {
  528. return m_fullName;
  529. }
  530. wxString WX_FILENAME::GetPath() const
  531. {
  532. return m_path;
  533. }
  534. wxString WX_FILENAME::GetFullPath() const
  535. {
  536. return m_path + wxT( '/' ) + m_fullName;
  537. }
  538. // Write locally-cached values to the wxFileName. MUST be called before using m_fn.
  539. void WX_FILENAME::resolve()
  540. {
  541. size_t dot = m_fullName.find_last_of( wxT( '.' ) );
  542. m_fn.SetName( m_fullName.substr( 0, dot ) );
  543. m_fn.SetExt( m_fullName.substr( dot + 1 ) );
  544. }
  545. long long WX_FILENAME::GetTimestamp()
  546. {
  547. resolve();
  548. if( m_fn.FileExists() )
  549. return m_fn.GetModificationTime().GetValue().GetValue();
  550. return 0;
  551. }
  552. /**
  553. * A copy of wxMatchWild(), which wxWidgets attributes to Douglas A. Lewis
  554. * <dalewis@cs.Buffalo.EDU> and ircII's reg.c.
  555. *
  556. * This version is modified to skip any encoding conversions (for performance).
  557. */
  558. bool matchWild( const char* pat, const char* text, bool dot_special )
  559. {
  560. if( !*text )
  561. {
  562. /* Match if both are empty. */
  563. return !*pat;
  564. }
  565. const char *m = pat,
  566. *n = text,
  567. *ma = NULL,
  568. *na = NULL;
  569. int just = 0,
  570. acount = 0,
  571. count = 0;
  572. if( dot_special && (*n == '.') )
  573. {
  574. /* Never match so that hidden Unix files
  575. * are never found. */
  576. return false;
  577. }
  578. for(;;)
  579. {
  580. if( *m == '*' )
  581. {
  582. ma = ++m;
  583. na = n;
  584. just = 1;
  585. acount = count;
  586. }
  587. else if( *m == '?' )
  588. {
  589. m++;
  590. if( !*n++ )
  591. return false;
  592. }
  593. else
  594. {
  595. if( *m == '\\' )
  596. {
  597. m++;
  598. /* Quoting "nothing" is a bad thing */
  599. if( !*m )
  600. return false;
  601. }
  602. if( !*m )
  603. {
  604. /*
  605. * If we are out of both strings or we just
  606. * saw a wildcard, then we can say we have a
  607. * match
  608. */
  609. if( !*n )
  610. return true;
  611. if( just )
  612. return true;
  613. just = 0;
  614. goto not_matched;
  615. }
  616. /*
  617. * We could check for *n == NULL at this point, but
  618. * since it's more common to have a character there,
  619. * check to see if they match first (m and n) and
  620. * then if they don't match, THEN we can check for
  621. * the NULL of n
  622. */
  623. just = 0;
  624. if( *m == *n )
  625. {
  626. m++;
  627. count++;
  628. n++;
  629. }
  630. else
  631. {
  632. not_matched:
  633. /*
  634. * If there are no more characters in the
  635. * string, but we still need to find another
  636. * character (*m != NULL), then it will be
  637. * impossible to match it
  638. */
  639. if( !*n )
  640. return false;
  641. if( ma )
  642. {
  643. m = ma;
  644. n = ++na;
  645. count = acount;
  646. }
  647. else
  648. return false;
  649. }
  650. }
  651. }
  652. }
  653. /**
  654. * A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function
  655. * private to src/common/filename.cpp.
  656. */
  657. #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
  658. // Convert between wxDateTime and FILETIME which is a 64-bit value representing
  659. // the number of 100-nanosecond intervals since January 1, 1601 UTC.
  660. //
  661. // This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
  662. static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
  663. static void ConvertFileTimeToWx( wxDateTime *dt, const FILETIME &ft )
  664. {
  665. wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
  666. t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
  667. t -= EPOCH_OFFSET_IN_MSEC;
  668. *dt = wxDateTime( t );
  669. }
  670. #endif // wxUSE_DATETIME && __WIN32__
  671. /**
  672. * TimestampDir
  673. *
  674. * This routine offers SIGNIFICANT performance benefits over using wxWidgets to gather
  675. * timestamps from matching files in a directory.
  676. * @param aDirPath the directory to search
  677. * @param aFilespec a (wildcarded) file spec to match against
  678. * @return a hash of the last-mod-dates of all matching files in the directory
  679. */
  680. long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
  681. {
  682. long long timestamp = 0;
  683. #if defined( __WIN32__ )
  684. // Win32 version.
  685. // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
  686. // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
  687. // conversion by staying on the MSW side of things.
  688. std::wstring filespec( aDirPath.t_str() );
  689. filespec += '\\';
  690. filespec += aFilespec.t_str();
  691. WIN32_FIND_DATA findData;
  692. wxDateTime lastModDate;
  693. HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
  694. if( fileHandle != INVALID_HANDLE_VALUE )
  695. {
  696. do
  697. {
  698. ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
  699. timestamp += lastModDate.GetValue().GetValue();
  700. }
  701. while ( FindNextFile( fileHandle, &findData ) != 0 );
  702. }
  703. FindClose( fileHandle );
  704. #else
  705. // POSIX version.
  706. // Save time by not converting between encodings -- do everything on the file-system side.
  707. std::string filespec( aFilespec.fn_str() );
  708. std::string dir_path( aDirPath.fn_str() );
  709. DIR* dir = opendir( dir_path.c_str() );
  710. if( dir )
  711. {
  712. for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
  713. {
  714. if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
  715. continue;
  716. std::string entry_path = dir_path + '/' + dir_entry->d_name;
  717. struct stat entry_stat;
  718. wxCRT_Lstat( entry_path.c_str(), &entry_stat );
  719. // Timestamp the source file, not the symlink
  720. if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
  721. {
  722. char buffer[ PATH_MAX + 1 ];
  723. ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
  724. if( pathLen > 0 )
  725. {
  726. buffer[ pathLen ] = '\0';
  727. entry_path = dir_path + buffer;
  728. wxCRT_Lstat( entry_path.c_str(), &entry_stat );
  729. }
  730. }
  731. if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
  732. timestamp += entry_stat.st_mtime * 1000;
  733. }
  734. closedir( dir );
  735. }
  736. #endif
  737. return timestamp;
  738. }