KiCad PCB EDA Suite
common.cpp
Go to the documentation of this file.
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 
26 #include <eda_base_frame.h>
27 #include <project.h>
28 #include <common.h>
29 #include <reporter.h>
30 #include <macros.h>
31 #include <mutex>
32 #include <wx/process.h>
33 #include <wx/config.h>
34 #include <wx/utils.h>
35 #include <wx/stdpaths.h>
36 #include <wx/url.h>
37 #include <wx/wx.h>
38 #include <boost/uuid/uuid_generators.hpp>
39 #include <boost/uuid/uuid_io.hpp>
40 #include <boost/functional/hash.hpp>
41 
42 
43 // Create only once, as seeding is *very* expensive
44 static boost::uuids::random_generator randomGenerator;
45 
46 // These don't have the same performance penalty, but might as well be consistent
47 static boost::uuids::string_generator stringGenerator;
48 static boost::uuids::nil_generator nilGenerator;
49 
50 // Global nil reference
51 KIID niluuid( 0 );
52 
53 
54 // For static initialization
56 {
57  static KIID nil( 0 );
58  return nil;
59 }
60 
61 
63  m_uuid( randomGenerator() ),
64  m_cached_timestamp( 0 )
65 {
66 }
67 
68 
69 KIID::KIID( int null ) :
70  m_uuid( nilGenerator() ),
71  m_cached_timestamp( 0 )
72 {
73  wxASSERT( null == 0 );
74 }
75 
76 
77 KIID::KIID( const wxString& aString ) :
78  m_uuid(),
79  m_cached_timestamp( 0 )
80 {
81  if( aString.length() == 8 )
82  {
83  // A legacy-timestamp-based UUID has only the last 4 octets filled in.
84  // Convert them individually to avoid stepping in the little-endian/big-endian
85  // doo-doo.
86  for( int i = 0; i < 4; ++i )
87  {
88  wxString octet = aString.substr( i * 2, 2 );
89  m_uuid.data[ i + 12 ] = strtol( octet.data(), NULL, 16 );
90  }
91 
92  m_cached_timestamp = strtol( aString.c_str(), NULL, 16 );
93  }
94  else
95  {
96  try
97  {
98  m_uuid = stringGenerator( aString.wc_str() );
99 
100  if( IsLegacyTimestamp() )
101  m_cached_timestamp = strtol( aString.substr( 28 ).c_str(), NULL, 16 );
102  }
103  catch( ... )
104  {
105  // Failed to parse string representation; best we can do is assign a new
106  // random one.
108  }
109  }
110 }
111 
112 
113 KIID::KIID( timestamp_t aTimestamp )
114 {
115  m_cached_timestamp = aTimestamp;
116 
117  // A legacy-timestamp-based UUID has only the last 4 octets filled in.
118  // Convert them individually to avoid stepping in the little-endian/big-endian
119  // doo-doo.
120  wxString str = AsLegacyTimestampString();
121 
122  for( int i = 0; i < 4; ++i )
123  {
124  wxString octet = str.substr( i * 2, 2 );
125  m_uuid.data[ i + 12 ] = strtol( octet.data(), NULL, 16 );
126  }
127 }
128 
129 
131 {
132  return !m_uuid.data[8] && !m_uuid.data[9] && !m_uuid.data[10] && !m_uuid.data[11];
133 }
134 
135 
137 {
138  return m_cached_timestamp;
139 }
140 
141 
142 size_t KIID::Hash() const
143 {
144  size_t hash = 0;
145 
146  // Note: this is NOT little-endian/big-endian safe, but as long as it's just used
147  // at runtime it won't matter.
148 
149  for( int i = 0; i < 4; ++i )
150  boost::hash_combine( hash, reinterpret_cast<const uint32_t*>( m_uuid.data )[i] );
151 
152  return hash;
153 }
154 
155 
156 void KIID::Clone( const KIID& aUUID )
157 {
158  m_uuid = aUUID.m_uuid;
160 }
161 
162 
163 wxString KIID::AsString() const
164 {
165  return boost::uuids::to_string( m_uuid );
166 }
167 
168 
170 {
171  return wxString::Format( "%8.8lX", (unsigned long) AsLegacyTimestamp() );
172 }
173 
174 
176 {
177  if( !IsLegacyTimestamp() )
178  return;
179 
180  m_cached_timestamp = 0;
182 }
183 
184 
186 {
187  switch( aUnit )
188  {
189  case EDA_UNITS::INCHES:
190  case EDA_UNITS::MILS:
191  return true;
192 
193  default:
194  return false;
195  }
196 
197  return false;
198 }
199 
200 
201 bool IsMetricUnit( EDA_UNITS aUnit )
202 {
203  switch( aUnit )
204  {
206  return true;
207 
208  default:
209  return false;
210  }
211 
212  return false;
213 }
214 
215 
224 // When reading/writing files, we need to swtich to setlocale( LC_NUMERIC, "C" ).
225 // Works fine to read/write files with floating point numbers.
226 // We can call setlocale( LC_NUMERIC, "C" ) of wxLocale( "C", "C", "C", false )
227 // wxWidgets discourage a direct call to setlocale
228 // However, for us, calling wxLocale( "C", "C", "C", false ) has a unwanted effect:
229 // The I18N translations are no longer active, because the English dixtionary is selected.
230 // To read files, this is not a major issues, but the resul can differ
231 // from using setlocale(xx, "C").
232 // Previouly, we called setlocale( LC_NUMERIC, "C" )
233 // The old code will be removed when calling wxLocale( "C", "C", "C", false )
234 // is fully tested, and all issues fixed
235 #define USE_WXLOCALE 1 /* 0 to call setlocale, 1 to call wxLocale */
236 
237 // On Windows, when using setlocale, a wx alert is generated
238 // in some cases (reading a bitmap for instance)
239 // So we disable alerts during the time a file is read or written
240 #if !USE_WXLOCALE
241 #if defined( _WIN32 ) && defined( DEBUG )
242 // a wxAssertHandler_t function to filter wxWidgets alert messages when reading/writing a file
243 // when switching the locale to LC_NUMERIC, "C"
244 // It is used in class LOCALE_IO to hide a useless (in kicad) wxWidgets alert message
245 void KiAssertFilter( const wxString &file, int line,
246  const wxString &func, const wxString &cond,
247  const wxString &msg)
248 {
249  if( !msg.Contains( "Decimal separator mismatch" ) )
250  wxTheApp->OnAssertFailure( file.c_str(), line, func.c_str(), cond.c_str(), msg.c_str() );
251 }
252 #endif
253 #endif
254 
255 std::atomic<unsigned int> LOCALE_IO::m_c_count( 0 );
256 LOCALE_IO::LOCALE_IO() : m_wxLocale( nullptr )
257 {
258  // use thread safe, atomic operation
259  if( m_c_count++ == 0 )
260  {
261 #if USE_WXLOCALE
262  m_wxLocale = new wxLocale( "C", "C", "C", false );
263 #else
264  // Store the user locale name, to restore this locale later, in dtor
265  m_user_locale = setlocale( LC_NUMERIC, nullptr );
266 #if defined( _WIN32 ) && defined( DEBUG )
267  // Disable wxWidgets alerts
268  wxSetAssertHandler( KiAssertFilter );
269 #endif
270  // Switch the locale to C locale, to read/write files with fp numbers
271  setlocale( LC_NUMERIC, "C" );
272 #endif
273  }
274 }
275 
276 
278 {
279  // use thread safe, atomic operation
280  if( --m_c_count == 0 )
281  {
282  // revert to the user locale
283 #if USE_WXLOCALE
284  delete m_wxLocale; // Deleting m_wxLocale restored previous locale
285  m_wxLocale = nullptr;
286 #else
287  setlocale( LC_NUMERIC, m_user_locale.c_str() );
288 #if defined( _WIN32 ) && defined( DEBUG )
289  // Enable wxWidgets alerts
290  wxSetDefaultAssertHandler();
291 #endif
292 #endif
293  }
294 }
295 
296 
297 wxSize GetTextSize( const wxString& aSingleLine, wxWindow* aWindow )
298 {
299  wxCoord width;
300  wxCoord height;
301 
302  {
303  wxClientDC dc( aWindow );
304  dc.SetFont( aWindow->GetFont() );
305  dc.GetTextExtent( aSingleLine, &width, &height );
306  }
307 
308  return wxSize( width, height );
309 }
310 
311 
312 bool EnsureTextCtrlWidth( wxTextCtrl* aCtrl, const wxString* aString )
313 {
314  wxWindow* window = aCtrl->GetParent();
315 
316  if( !window )
317  window = aCtrl;
318 
319  wxString ctrlText;
320 
321  if( !aString )
322  {
323  ctrlText = aCtrl->GetValue();
324  aString = &ctrlText;
325  }
326 
327  wxSize textz = GetTextSize( *aString, window );
328  wxSize ctrlz = aCtrl->GetSize();
329 
330  if( ctrlz.GetWidth() < textz.GetWidth() + 10 )
331  {
332  ctrlz.SetWidth( textz.GetWidth() + 10 );
333  aCtrl->SetSizeHints( ctrlz );
334  return true;
335  }
336 
337  return false;
338 }
339 
340 
341 void SelectReferenceNumber( wxTextEntry* aTextEntry )
342 {
343  wxString ref = aTextEntry->GetValue();
344 
345  if( ref.find_first_of( '?' ) != ref.npos )
346  {
347  aTextEntry->SetSelection( ref.find_first_of( '?' ), ref.find_last_of( '?' ) + 1 );
348  }
349  else
350  {
351  wxString num = ref;
352 
353  while( !num.IsEmpty() && ( !isdigit( num.Last() ) || !isdigit( num.GetChar( 0 ) ) ) )
354  {
355  // Trim non-digit from end
356  if( !isdigit( num.Last() ) )
357  num.RemoveLast();
358 
359  // Trim non-digit from the start
360  if( !num.IsEmpty() && !isdigit( num.GetChar( 0 ) ) )
361  num = num.Right( num.Length() - 1 );
362  }
363 
364  aTextEntry->SetSelection( ref.Find( num ), ref.Find( num ) + num.Length() );
365 
366  if( num.IsEmpty() )
367  aTextEntry->SetSelection( -1, -1 );
368  }
369 }
370 
371 
372 void wxStringSplit( const wxString& aText, wxArrayString& aStrings, wxChar aSplitter )
373 {
374  wxString tmp;
375 
376  for( unsigned ii = 0; ii < aText.Length(); ii++ )
377  {
378  if( aText[ii] == aSplitter )
379  {
380  aStrings.Add( tmp );
381  tmp.Clear();
382  }
383 
384  else
385  tmp << aText[ii];
386  }
387 
388  if( !tmp.IsEmpty() )
389  {
390  aStrings.Add( tmp );
391  }
392 }
393 
394 
395 int ProcessExecute( const wxString& aCommandLine, int aFlags, wxProcess *callback )
396 {
397  return (int) wxExecute( aCommandLine, aFlags, callback );
398 }
399 
400 
402 {
406 #ifdef __WINDOWS__
407  Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
408 #endif
410 };
411 
412 
413 wxString ExpandTextVars( const wxString& aSource,
414  const std::function<bool( wxString* )>* aLocalResolver,
415  const PROJECT* aProject,
416  const std::function<bool( wxString* )>* aFallbackResolver )
417 {
418  wxString newbuf;
419  size_t sourceLen = aSource.length();
420 
421  newbuf.Alloc( sourceLen ); // best guess (improves performance)
422 
423  for( size_t i = 0; i < sourceLen; ++i )
424  {
425  if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
426  {
427  wxString token;
428 
429  for( i = i + 2; i < sourceLen; ++i )
430  {
431  if( aSource[i] == '}' )
432  break;
433  else
434  token.append( aSource[i] );
435  }
436 
437  if( token.IsEmpty() )
438  continue;
439 
440  if( aLocalResolver && (*aLocalResolver)( &token ) )
441  {
442  newbuf.append( token );
443  }
444  else if( aProject && aProject->TextVarResolver( &token ) )
445  {
446  newbuf.append( token );
447  }
448  else if( aFallbackResolver && (*aFallbackResolver)( &token ) )
449  {
450  newbuf.append( token );
451  }
452  else
453  {
454  // Token not resolved: leave the reference unchanged
455  newbuf.append( "${" + token + "}" );
456  }
457  }
458  else
459  {
460  newbuf.append( aSource[i] );
461  }
462  }
463 
464  return newbuf;
465 }
466 
467 
468 //
469 // Stolen from wxExpandEnvVars and then heavily optimized
470 //
471 wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject )
472 {
473  size_t strlen = str.length();
474 
475  wxString strResult;
476  strResult.Alloc( strlen ); // best guess (improves performance)
477 
478  for( size_t n = 0; n < strlen; n++ )
479  {
480  wxUniChar str_n = str[n];
481 
482  switch( str_n.GetValue() )
483  {
484 #ifdef __WINDOWS__
485  case wxT( '%' ):
486 #endif // __WINDOWS__
487  case wxT( '$' ):
488  {
489  Bracket bracket;
490 #ifdef __WINDOWS__
491  if( str_n == wxT( '%' ) )
492  bracket = Bracket_Windows;
493  else
494 #endif // __WINDOWS__
495  if( n == strlen - 1 )
496  {
497  bracket = Bracket_None;
498  }
499  else
500  {
501  switch( str[n + 1].GetValue() )
502  {
503  case wxT( '(' ):
504  bracket = Bracket_Normal;
505  str_n = str[++n]; // skip the bracket
506  break;
507 
508  case wxT( '{' ):
509  bracket = Bracket_Curly;
510  str_n = str[++n]; // skip the bracket
511  break;
512 
513  default:
514  bracket = Bracket_None;
515  }
516  }
517 
518  size_t m = n + 1;
519  wxUniChar str_m = str[m];
520 
521  while( m < strlen && ( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) ) )
522  str_m = str[++m];
523 
524  wxString strVarName( str.c_str() + n + 1, m - n - 1 );
525 
526  // NB: use wxGetEnv instead of wxGetenv as otherwise variables
527  // set through wxSetEnv may not be read correctly!
528  bool expanded = false;
529  wxString tmp = strVarName;
530 
531  if( aProject && aProject->TextVarResolver( &tmp ) )
532  {
533  strResult += tmp;
534  expanded = true;
535  }
536  else if( wxGetEnv( strVarName, &tmp ) )
537  {
538  strResult += tmp;
539  expanded = true;
540  }
541  else
542  {
543  // variable doesn't exist => don't change anything
544 #ifdef __WINDOWS__
545  if ( bracket != Bracket_Windows )
546 #endif
547  if ( bracket != Bracket_None )
548  strResult << str[n - 1];
549 
550  strResult << str_n << strVarName;
551  }
552 
553  // check the closing bracket
554  if( bracket != Bracket_None )
555  {
556  if( m == strlen || str_m != (wxChar)bracket )
557  {
558  // under MSW it's common to have '%' characters in the registry
559  // and it's annoying to have warnings about them each time, so
560  // ignore them silently if they are not used for env vars
561  //
562  // under Unix, OTOH, this warning could be useful for the user to
563  // understand why isn't the variable expanded as intended
564 #ifndef __WINDOWS__
565  wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
566  "at position %u in '%s'." ),
567  (char)bracket, (unsigned int) (m + 1), str.c_str() );
568 #endif // __WINDOWS__
569  }
570  else
571  {
572  // skip closing bracket unless the variables wasn't expanded
573  if( !expanded )
574  strResult << (wxChar)bracket;
575 
576  m++;
577  }
578  }
579 
580  n = m - 1; // skip variable name
581  str_n = str[n];
582  }
583  break;
584 
585  case wxT( '\\' ):
586  // backslash can be used to suppress special meaning of % and $
587  if( n != strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
588  {
589  str_n = str[++n];
590  strResult += str_n;
591 
592  break;
593  }
595 
596  default:
597  strResult += str_n;
598  }
599  }
600 
601 #ifndef __WINDOWS__
602  if( strResult.StartsWith( "~" ) )
603  strResult.Replace( "~", wxGetHomeDir(), false );
604 #endif // __WINDOWS__
605 
606  return strResult;
607 }
608 
609 
610 const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aProject )
611 {
612  // wxGetenv( wchar_t* ) is not re-entrant on linux.
613  // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
614  static std::mutex getenv_mutex;
615 
616  std::lock_guard<std::mutex> lock( getenv_mutex );
617 
618  // We reserve the right to do this another way, by providing our own member function.
619  return KIwxExpandEnvVars( aString, aProject );
620 }
621 
622 
623 const wxString ResolveUriByEnvVars( const wxString& aUri, PROJECT* aProject )
624 {
625  wxString uri = ExpandTextVars( aUri, nullptr, aProject );
626 
627  // URL-like URI: return as is.
628  wxURL url( uri );
629 
630  if( url.GetError() == wxURL_NOERR )
631  return uri;
632 
633  // Otherwise, the path points to a local file. Resolve environment variables if any.
634  return ExpandEnvVarSubstitutions( aUri, aProject );
635 }
636 
637 
638 bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName,
639  const wxString& aBaseFilename,
640  REPORTER* aReporter )
641 {
642  wxString msg;
643  wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
644 
645  // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
646  // already an absolute path) absolute:
647  if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
648  {
649  if( aReporter )
650  {
651  msg.Printf( _( "Cannot make path \"%s\" absolute with respect to \"%s\"." ),
652  aTargetFullFileName->GetPath(),
653  baseFilePath );
654  aReporter->Report( msg, RPT_SEVERITY_ERROR );
655  }
656 
657  return false;
658  }
659 
660  // Ensure the path of aTargetFullFileName exists, and create it if needed:
661  wxString outputPath( aTargetFullFileName->GetPath() );
662 
663  if( !wxFileName::DirExists( outputPath ) )
664  {
665  if( wxMkdir( outputPath ) )
666  {
667  if( aReporter )
668  {
669  msg.Printf( _( "Output directory \"%s\" created.\n" ), outputPath );
670  aReporter->Report( msg, RPT_SEVERITY_INFO );
671  return true;
672  }
673  }
674  else
675  {
676  if( aReporter )
677  {
678  msg.Printf( _( "Cannot create output directory \"%s\".\n" ), outputPath );
679  aReporter->Report( msg, RPT_SEVERITY_ERROR );
680  }
681 
682  return false;
683  }
684  }
685 
686  return true;
687 }
688 
689 
690 #ifdef __WXMAC__
691 wxString GetOSXKicadUserDataDir()
692 {
693  // According to wxWidgets documentation for GetUserDataDir:
694  // Mac: ~/Library/Application Support/appname
695  wxFileName udir( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString );
696 
697  // Since appname is different if started via launcher or standalone binary
698  // map all to "kicad" here
699  udir.RemoveLastDir();
700  udir.AppendDir( "kicad" );
701 
702  return udir.GetPath();
703 }
704 
705 
706 wxString GetOSXKicadMachineDataDir()
707 {
708  return wxT( "/Library/Application Support/kicad" );
709 }
710 
711 
712 wxString GetOSXKicadDataDir()
713 {
714  // According to wxWidgets documentation for GetDataDir:
715  // Mac: appname.app/Contents/SharedSupport bundle subdirectory
716  wxFileName ddir( wxStandardPaths::Get().GetDataDir(), wxEmptyString );
717 
718  // This must be mapped to main bundle for everything but kicad.app
719  const wxArrayString dirs = ddir.GetDirs();
720  if( dirs[dirs.GetCount() - 3] != wxT( "kicad.app" ) )
721  {
722  // Bundle structure resp. current path is
723  // kicad.app/Contents/Applications/<standalone>.app/Contents/SharedSupport
724  // and will be mapped to
725  // kicad.app/Contents/SharedSupprt
726  ddir.RemoveLastDir();
727  ddir.RemoveLastDir();
728  ddir.RemoveLastDir();
729  ddir.RemoveLastDir();
730  ddir.AppendDir( wxT( "SharedSupport" ) );
731  }
732 
733  return ddir.GetPath();
734 }
735 #endif
736 
737 
738 // add this only if it is not in wxWidgets (for instance before 3.1.0)
739 #ifdef USE_KICAD_WXSTRING_HASH
740 size_t std::hash<wxString>::operator()( const wxString& s ) const
741 {
742  return std::hash<std::wstring>{}( s.ToStdWstring() );
743 }
744 #endif
745 
746 #ifdef USE_KICAD_WXPOINT_LESS_AND_HASH
747 size_t std::hash<wxPoint>::operator() ( const wxPoint& k ) const
748 {
749  auto xhash = std::hash<int>()( k.x );
750 
751  // 0x9e3779b9 is 2^33 / ( 1 + sqrt(5) )
752  // Adding this value ensures that consecutive bits of y will not be close to each other
753  // decreasing the likelihood of hash collision in similar values of x and y
754  return xhash ^ ( std::hash<int>()( k.y ) + 0x9e3779b9 + ( xhash << 6 ) + ( xhash >> 2 ) );
755 }
756 
757 bool std::less<wxPoint>::operator()( const wxPoint& aA, const wxPoint& aB ) const
758 {
759  if( aA.x == aB.x )
760  return aA.y < aB.y;
761 
762  return aA.x < aB.x;
763 }
764 #endif
765 
766 
767 std::ostream& operator<<( std::ostream& out, const wxSize& size )
768 {
769  out << " width=\"" << size.GetWidth() << "\" height=\"" << size.GetHeight() << "\"";
770  return out;
771 }
772 
773 
774 std::ostream& operator<<( std::ostream& out, const wxPoint& pt )
775 {
776  out << " x=\"" << pt.x << "\" y=\"" << pt.y << "\"";
777  return out;
778 }
779 
780 
796 WX_FILENAME::WX_FILENAME( const wxString& aPath, const wxString& aFilename ) :
797  m_fn( aPath, aFilename ),
798  m_path( aPath ),
799  m_fullName( aFilename )
800 { }
801 
802 
803 void WX_FILENAME::SetFullName( const wxString& aFileNameAndExtension )
804 {
805  m_fullName = aFileNameAndExtension;
806 }
807 
808 
809 wxString WX_FILENAME::GetName() const
810 {
811  size_t dot = m_fullName.find_last_of( wxT( '.' ) );
812  return m_fullName.substr( 0, dot );
813 }
814 
815 
816 wxString WX_FILENAME::GetFullName() const
817 {
818  return m_fullName;
819 }
820 
821 
822 wxString WX_FILENAME::GetPath() const
823 {
824  return m_path;
825 }
826 
827 
828 wxString WX_FILENAME::GetFullPath() const
829 {
830  return m_path + wxT( '/' ) + m_fullName;
831 }
832 
833 
834 // Write locally-cached values to the wxFileName. MUST be called before using m_fn.
836 {
837  size_t dot = m_fullName.find_last_of( wxT( '.' ) );
838  m_fn.SetName( m_fullName.substr( 0, dot ) );
839  m_fn.SetExt( m_fullName.substr( dot + 1 ) );
840 }
841 
842 
844 {
845  resolve();
846 
847  if( m_fn.FileExists() )
848  return m_fn.GetModificationTime().GetValue().GetValue();
849 
850  return 0;
851 }
852 
853 
860 bool matchWild( const char* pat, const char* text, bool dot_special )
861 {
862  if( !*text )
863  {
864  /* Match if both are empty. */
865  return !*pat;
866  }
867 
868  const char *m = pat,
869  *n = text,
870  *ma = NULL,
871  *na = NULL;
872  int just = 0,
873  acount = 0,
874  count = 0;
875 
876  if( dot_special && (*n == '.') )
877  {
878  /* Never match so that hidden Unix files
879  * are never found. */
880  return false;
881  }
882 
883  for(;;)
884  {
885  if( *m == '*' )
886  {
887  ma = ++m;
888  na = n;
889  just = 1;
890  acount = count;
891  }
892  else if( *m == '?' )
893  {
894  m++;
895 
896  if( !*n++ )
897  return false;
898  }
899  else
900  {
901  if( *m == '\\' )
902  {
903  m++;
904 
905  /* Quoting "nothing" is a bad thing */
906  if( !*m )
907  return false;
908  }
909  if( !*m )
910  {
911  /*
912  * If we are out of both strings or we just
913  * saw a wildcard, then we can say we have a
914  * match
915  */
916  if( !*n )
917  return true;
918 
919  if( just )
920  return true;
921 
922  just = 0;
923  goto not_matched;
924  }
925 
926  /*
927  * We could check for *n == NULL at this point, but
928  * since it's more common to have a character there,
929  * check to see if they match first (m and n) and
930  * then if they don't match, THEN we can check for
931  * the NULL of n
932  */
933  just = 0;
934 
935  if( *m == *n )
936  {
937  m++;
938  count++;
939  n++;
940  }
941  else
942  {
943  not_matched:
944 
945  /*
946  * If there are no more characters in the
947  * string, but we still need to find another
948  * character (*m != NULL), then it will be
949  * impossible to match it
950  */
951  if( !*n )
952  return false;
953 
954  if( ma )
955  {
956  m = ma;
957  n = ++na;
958  count = acount;
959  }
960  else
961  return false;
962  }
963  }
964  }
965 }
966 
967 
972 #if wxUSE_DATETIME && defined(__WIN32__) && !defined(__WXMICROWIN__)
973 
974 // Convert between wxDateTime and FILETIME which is a 64-bit value representing
975 // the number of 100-nanosecond intervals since January 1, 1601 UTC.
976 //
977 // This is the offset between FILETIME epoch and the Unix/wxDateTime Epoch.
978 static wxInt64 EPOCH_OFFSET_IN_MSEC = wxLL(11644473600000);
979 
980 
981 static void ConvertFileTimeToWx( wxDateTime *dt, const FILETIME &ft )
982 {
983  wxLongLong t( ft.dwHighDateTime, ft.dwLowDateTime );
984  t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
985  t -= EPOCH_OFFSET_IN_MSEC;
986 
987  *dt = wxDateTime( t );
988 }
989 
990 #endif // wxUSE_DATETIME && __WIN32__
991 
992 
1002 long long TimestampDir( const wxString& aDirPath, const wxString& aFilespec )
1003 {
1004  long long timestamp = 0;
1005 
1006 #if defined( __WIN32__ )
1007  // Win32 version.
1008  // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
1009  // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
1010  // conversion by staying on the MSW side of things.
1011  std::wstring filespec( aDirPath.t_str() );
1012  filespec += '\\';
1013  filespec += aFilespec.t_str();
1014 
1015  WIN32_FIND_DATA findData;
1016  wxDateTime lastModDate;
1017 
1018  HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
1019 
1020  if( fileHandle != INVALID_HANDLE_VALUE )
1021  {
1022  do
1023  {
1024  ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
1025  timestamp += lastModDate.GetValue().GetValue();
1026  }
1027  while ( FindNextFile( fileHandle, &findData ) != 0 );
1028  }
1029 
1030  FindClose( fileHandle );
1031 #else
1032  // POSIX version.
1033  // Save time by not converting between encodings -- do everything on the file-system side.
1034  std::string filespec( aFilespec.fn_str() );
1035  std::string dir_path( aDirPath.fn_str() );
1036 
1037  DIR* dir = opendir( dir_path.c_str() );
1038 
1039  if( dir )
1040  {
1041  for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
1042  {
1043  if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
1044  continue;
1045 
1046  std::string entry_path = dir_path + '/' + dir_entry->d_name;
1047  struct stat entry_stat;
1048 
1049  if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
1050  {
1051  // Timestamp the source file, not the symlink
1052  if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
1053  {
1054  char buffer[ PATH_MAX + 1 ];
1055  ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
1056 
1057  if( pathLen > 0 )
1058  {
1059  struct stat linked_stat;
1060  buffer[ pathLen ] = '\0';
1061  entry_path = dir_path + buffer;
1062 
1063  if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
1064  {
1065  entry_stat = linked_stat;
1066  }
1067  else
1068  {
1069  // if we couldn't lstat the linked file we'll have to just use
1070  // the symbolic link info
1071  }
1072  }
1073  }
1074 
1075  if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
1076  timestamp += entry_stat.st_mtime * 1000;
1077  }
1078  else
1079  {
1080  // if we couldn't lstat the file itself all we can do is use the name
1081  timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
1082  }
1083  }
1084 
1085  closedir( dir );
1086  }
1087 #endif
1088 
1089  return timestamp;
1090 }
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
Definition: common.cpp:372
EDA_UNITS
Definition: common.h:200
bool IsMetricUnit(EDA_UNITS aUnit)
Definition: common.cpp:201
WX_FILENAME(const wxString &aPath, const wxString &aFilename)
Performance enhancements to file and directory operations.
Definition: common.cpp:796
int ProcessExecute(const wxString &aCommandLine, int aFlags, wxProcess *callback)
Run a command in a child process.
Definition: common.cpp:395
~LOCALE_IO()
Definition: common.cpp:277
PROJECT holds project specific data.
Definition: project.h:61
KIID()
Definition: common.cpp:62
KIID & NilUuid()
Definition: common.cpp:55
wxLocale * m_wxLocale
Definition: common.h:235
wxString GetName() const
Definition: common.cpp:809
static boost::uuids::string_generator stringGenerator
Definition: common.cpp:47
wxString AsString() const
Definition: common.cpp:163
wxString GetFullName() const
Definition: common.cpp:816
wxString m_path
Definition: common.h:438
boost::uuids::uuid m_uuid
Definition: common.h:111
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:89
size_t operator()(const wxPoint &k) const
Definition: common.cpp:747
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:610
REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:64
void SetFullName(const wxString &aFileNameAndExtension)
Definition: common.cpp:803
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
static boost::uuids::nil_generator nilGenerator
Definition: common.cpp:48
static void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
Definition: hash_eda.h:64
bool IsLegacyTimestamp() const
Definition: common.cpp:130
timestamp_t m_cached_timestamp
Definition: common.h:113
This file contains miscellaneous commonly used macros and functions.
wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition: common.cpp:297
wxFileName m_fn
Definition: common.h:437
static std::atomic< unsigned int > m_c_count
Definition: common.h:230
void SelectReferenceNumber(wxTextEntry *aTextEntry)
Select the number (or "?") in a reference for ease of editing.
Definition: common.cpp:341
Definition: common.h:70
long long GetTimestamp()
Definition: common.cpp:843
void resolve()
Definition: common.cpp:835
size_t Hash() const
Definition: common.cpp:142
bool EnsureTextCtrlWidth(wxTextCtrl *aCtrl, const wxString *aString)
Set the minimum pixel width on a text control in order to make a text string be fully visible within ...
Definition: common.cpp:312
#define NULL
timestamp_t AsLegacyTimestamp() const
Definition: common.cpp:136
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition: common.cpp:638
Bracket
Definition: common.cpp:401
bool operator()(const wxPoint &aA, const wxPoint &aB) const
Definition: common.cpp:757
KIID niluuid(0)
static boost::uuids::random_generator randomGenerator
Definition: common.cpp:44
const wxString ResolveUriByEnvVars(const wxString &aUri, PROJECT *aProject)
Replace any environment and/or text variables in file-path uris (leaving network-path URIs alone).
Definition: common.cpp:623
Base window classes and related definitions.
VTBL_ENTRY bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:65
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function private to src/common/...
Definition: common.cpp:1002
LOCALE_IO()
Definition: common.cpp:256
void ConvertTimestampToUuid()
Change an existing time stamp based UUID into a true UUID.
Definition: common.cpp:175
wxString ExpandTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aLocalResolver, const PROJECT *aProject, const std::function< bool(wxString *)> *aFallbackResolver)
Expand '${var-name}' templates in text.
Definition: common.cpp:413
bool matchWild(const char *pat, const char *text, bool dot_special)
A copy of wxMatchWild(), which wxWidgets attributes to Douglas A.
Definition: common.cpp:860
wxString GetPath() const
Definition: common.cpp:822
void Clone(const KIID &aUUID)
Definition: common.cpp:156
wxString GetFullPath() const
Definition: common.cpp:828
bool IsImperialUnit(EDA_UNITS aUnit)
Definition: common.cpp:185
wxString AsLegacyTimestampString() const
Definition: common.cpp:169
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
#define _(s)
Definition: 3d_actions.cpp:33
The common library.
uint32_t timestamp_t
timestamp_t is our type to represent unique IDs for all kinds of elements; historically simply the ti...
Definition: common.h:58
wxString m_fullName
Definition: common.h:439
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject)
Definition: common.cpp:471
std::string m_user_locale
Definition: common.h:234
std::ostream & operator<<(std::ostream &out, const wxSize &size)
Helper function to print the given wxSize to a stream.
Definition: common.cpp:767
size_t operator()(const wxString &s) const
Definition: common.cpp:740