From c381b6d024ba00628eadc5511883a684a7e7a683 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 24 Apr 2023 11:58:03 +0100 Subject: [PATCH] More robust file reading for simulator files. --- common/richio.cpp | 41 +++++++++++++++++++++++++++ eeschema/sim/sim_lib_mgr.cpp | 10 +++---- eeschema/sim/sim_lib_mgr.h | 4 +-- eeschema/sim/sim_library.cpp | 9 +++--- eeschema/sim/sim_library.h | 18 ++++-------- eeschema/sim/sim_library_kibis.cpp | 4 +-- eeschema/sim/sim_library_kibis.h | 5 +--- eeschema/sim/sim_library_spice.cpp | 7 +---- eeschema/sim/sim_library_spice.h | 5 +--- eeschema/sim/spice_library_parser.cpp | 25 ++++++++-------- eeschema/sim/spice_library_parser.h | 4 +-- include/richio.h | 12 ++++++++ 12 files changed, 88 insertions(+), 56 deletions(-) diff --git a/common/richio.cpp b/common/richio.cpp index eb1fabe597..501d80d4ce 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -106,6 +106,47 @@ std::string StrPrintf( const char* format, ... ) } +wxString SafeReadFile( const wxString& aFilePath, const wxString& aReadType ) +{ + auto FROM_UTF8_WINE = + []( const char* cstring ) + { + wxString line = wxString::FromUTF8( cstring ); + + if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence + line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion + + // We have trouble here *probably* because Wine-hosted LTSpice writes out MSW + // encoded text on a macOS, where it isn't expected. In any case, wxWidgets' + // wxSafeConvert() appears to get around it. + + if( line.IsEmpty() ) + line = wxSafeConvertMB2WX( cstring ); + + // I'm not sure what the source of this style of line-endings is, but it can be + // found in some Fairchild Semiconductor SPICE files. + line.Replace( wxS( "\r\r\n" ), wxS( "\n" ) ); + + return line; + }; + + // Open file + FILE* fp = wxFopen( aFilePath, aReadType ); + + if( !fp ) + THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFilePath ) ); + + FILE_LINE_READER fileReader( fp, aFilePath ); + + wxString contents; + + while( fileReader.ReadLine() ) + contents += FROM_UTF8_WINE( fileReader.Line() ); + + return contents; +} + + //----------------------------------------------------------- LINE_READER::LINE_READER( unsigned aMaxLineLength ) : diff --git a/eeschema/sim/sim_lib_mgr.cpp b/eeschema/sim/sim_lib_mgr.cpp index 7ebf6d2a87..a3eed6ff01 100644 --- a/eeschema/sim/sim_lib_mgr.cpp +++ b/eeschema/sim/sim_lib_mgr.cpp @@ -88,8 +88,8 @@ wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, const PR } -std::string SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const std::string& aLibPath, - const std::string& aRelativeLib ) +wxString SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const wxString& aLibPath, + const wxString& aRelativeLib ) { wxFileName testPath( aLibPath ); wxString fullPath( aLibPath ); @@ -123,7 +123,7 @@ std::string SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const std::string& aLibPath catch( ... ) {} - return fullPath.ToStdString(); + return fullPath; } @@ -133,7 +133,7 @@ void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath ) { wxString path = ResolveLibraryPath( aLibraryPath, m_project ); - std::function f2 = + std::function f2 = std::bind( &SIM_LIB_MGR::ResolveEmbeddedLibraryPath, this, _1, _2 ); std::unique_ptr library = SIM_LIBRARY::Create( path, m_reporter, &f2 ); @@ -287,7 +287,7 @@ SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath, { path = ResolveLibraryPath( aLibraryPath, m_project ); - std::function f2 = + std::function f2 = std::bind( &SIM_LIB_MGR::ResolveEmbeddedLibraryPath, this, _1, _2 ); auto it = m_libraries.try_emplace( path, SIM_LIBRARY::Create( path, m_reporter, &f2 ) ).first; diff --git a/eeschema/sim/sim_lib_mgr.h b/eeschema/sim/sim_lib_mgr.h index 4b8964b7b5..fb1460ff91 100644 --- a/eeschema/sim/sim_lib_mgr.h +++ b/eeschema/sim/sim_lib_mgr.h @@ -76,9 +76,7 @@ public: std::vector> GetModels() const; static wxString ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT* aProject ); - - std::string ResolveEmbeddedLibraryPath( const std::string& aLibPath, - const std::string& aRelativeLib ); + wxString ResolveEmbeddedLibraryPath( const wxString& aLibPath, const wxString& aRelativeLib ); private: const PROJECT* m_project; diff --git a/eeschema/sim/sim_library.cpp b/eeschema/sim/sim_library.cpp index ae3b4e1469..eab347a86e 100644 --- a/eeschema/sim/sim_library.cpp +++ b/eeschema/sim/sim_library.cpp @@ -29,8 +29,9 @@ #include -std::unique_ptr SIM_LIBRARY::Create( const wxString& aFilePath, REPORTER* aReporter, - std::function *aResolver ) +std::unique_ptr +SIM_LIBRARY::Create( const wxString& aFilePath, REPORTER* aReporter, + std::function* aResolver ) { std::unique_ptr library; @@ -41,13 +42,13 @@ std::unique_ptr SIM_LIBRARY::Create( const wxString& aFilePath, REP library->m_reporter = aReporter; library->m_pathResolver = aResolver; - library->ReadFile( std::string( TO_UTF8( aFilePath ) ), aReporter ); + library->ReadFile( aFilePath, aReporter ); return library; } -void SIM_LIBRARY::ReadFile( const std::string& aFilePath, REPORTER* aReporter ) +void SIM_LIBRARY::ReadFile( const wxString& aFilePath, REPORTER* aReporter ) { m_filePath = aFilePath; } diff --git a/eeschema/sim/sim_library.h b/eeschema/sim/sim_library.h index 7ce2a92eea..49df8ff05c 100644 --- a/eeschema/sim/sim_library.h +++ b/eeschema/sim/sim_library.h @@ -52,9 +52,9 @@ public: * @param aReporter The reporter the library reports to * @return The library loaded in a newly constructed object. */ - static std::unique_ptr Create( const wxString& aFilePath, - REPORTER* aReporter = nullptr, - std::function* aResolver = nullptr); + static std::unique_ptr + Create( const wxString& aFilePath, REPORTER* aReporter, + std::function* aResolver ); /** * Read library from a source file. Must be in the format appropriate to the subclass, e.g. @@ -63,15 +63,7 @@ public: * @param aFilePath Path to the file. * @throw IO_ERROR on read or parsing error. */ - virtual void ReadFile( const std::string& aFilePath, REPORTER* aReporter ) = 0; - - /** - * Write library to a source file (e.g. in Spice format). - * - * @param aFilePath Path to the file. - * @throw IO_ERROR on write error. - */ - virtual void WriteFile( const std::string& aFilePath ) = 0; + virtual void ReadFile( const wxString& aFilePath, REPORTER* aReporter ) = 0; SIM_MODEL* FindModel( const std::string& aModelName ) const; @@ -83,7 +75,7 @@ protected: std::vector m_modelNames; std::vector> m_models; - std::function* m_pathResolver; + std::function* m_pathResolver; std::string m_filePath; diff --git a/eeschema/sim/sim_library_kibis.cpp b/eeschema/sim/sim_library_kibis.cpp index 3c9b4b7155..ab0f67a785 100644 --- a/eeschema/sim/sim_library_kibis.cpp +++ b/eeschema/sim/sim_library_kibis.cpp @@ -29,10 +29,10 @@ #include -void SIM_LIBRARY_KIBIS::ReadFile( const std::string& aFilePath, REPORTER* aReporter ) +void SIM_LIBRARY_KIBIS::ReadFile( const wxString& aFilePath, REPORTER* aReporter ) { SIM_LIBRARY::ReadFile( aFilePath, aReporter ); - m_kibis = KIBIS( aFilePath, m_reporter ); + m_kibis = KIBIS( aFilePath.ToStdString(), m_reporter ); if( !m_kibis.m_valid ) { diff --git a/eeschema/sim/sim_library_kibis.h b/eeschema/sim/sim_library_kibis.h index 12ae08f9ea..17dec0ad9c 100644 --- a/eeschema/sim/sim_library_kibis.h +++ b/eeschema/sim/sim_library_kibis.h @@ -38,10 +38,7 @@ public: static constexpr auto DIFF_FIELD = "Sim.Ibis.Diff"; // @copydoc SIM_LIBRARY::ReadFile() - void ReadFile( const std::string& aFilePath, REPORTER* aReporter ) override; - - // @copydoc SIM_LIBRARY::WriteFile() - void WriteFile( const std::string& aFilePath ) override{}; + void ReadFile( const wxString& aFilePath, REPORTER* aReporter ) override; bool InitModel( SIM_MODEL_KIBIS& aModel, wxString aCompName ); bool isPinDiff( const std::string& aComp, const std::string& aPinNumber ) const; diff --git a/eeschema/sim/sim_library_spice.cpp b/eeschema/sim/sim_library_spice.cpp index 3d29313f5e..b408a63070 100644 --- a/eeschema/sim/sim_library_spice.cpp +++ b/eeschema/sim/sim_library_spice.cpp @@ -33,14 +33,9 @@ SIM_LIBRARY_SPICE::SIM_LIBRARY_SPICE() : } -void SIM_LIBRARY_SPICE::ReadFile( const std::string& aFilePath, REPORTER* aReporter ) +void SIM_LIBRARY_SPICE::ReadFile( const wxString& aFilePath, REPORTER* aReporter ) { SIM_LIBRARY::ReadFile( aFilePath, aReporter ); m_spiceLibraryParser->ReadFile( aFilePath, aReporter ); } - -void SIM_LIBRARY_SPICE::WriteFile( const std::string& aFilePath ) -{ - // Not implemented yet. -} diff --git a/eeschema/sim/sim_library_spice.h b/eeschema/sim/sim_library_spice.h index a97144b3e3..96fb3bc20b 100644 --- a/eeschema/sim/sim_library_spice.h +++ b/eeschema/sim/sim_library_spice.h @@ -37,10 +37,7 @@ public: SIM_LIBRARY_SPICE(); // @copydoc SIM_LIBRARY::ReadFile() - void ReadFile( const std::string& aFilePath, REPORTER* aReporter ) override; - - // @copydoc SIM_LIBRARY::WriteFile() - void WriteFile( const std::string& aFilePath ) override; + void ReadFile( const wxString& aFilePath, REPORTER* aReporter ) override; private: std::unique_ptr m_spiceLibraryParser; diff --git a/eeschema/sim/spice_library_parser.cpp b/eeschema/sim/spice_library_parser.cpp index 7e3aa0c4cb..155762867a 100644 --- a/eeschema/sim/spice_library_parser.cpp +++ b/eeschema/sim/spice_library_parser.cpp @@ -57,17 +57,16 @@ namespace SIM_LIBRARY_SPICE_PARSER }; -void SPICE_LIBRARY_PARSER::readElement( const std::string &aFilePath, REPORTER& aReporter ) +void SPICE_LIBRARY_PARSER::parseFile( const wxString &aFilePath, REPORTER& aReporter ) { try { - tao::pegtl::file_input in( aFilePath ); - std::unique_ptr root = - tao::pegtl::parse_tree::parse in( SafeReadFile( aFilePath, wxS( "r" ) ).ToStdString(), + aFilePath.ToStdString() ); + auto root = tao::pegtl::parse_tree::parse - ( in ); + SIM_LIBRARY_SPICE_PARSER::control> ( in ); for( const auto& node : root->children ) { @@ -88,14 +87,14 @@ void SPICE_LIBRARY_PARSER::readElement( const std::string &aFilePath, REPORTER& } else if( node->is_type() ) { - std::string lib = node->children.at( 0 )->string(); + wxString lib = node->children.at( 0 )->string(); try { if( m_library.m_pathResolver ) lib = ( *m_library.m_pathResolver )( lib, aFilePath ); - readElement( lib, aReporter ); + parseFile( lib, aReporter ); } catch( const IO_ERROR& e ) { @@ -112,9 +111,9 @@ void SPICE_LIBRARY_PARSER::readElement( const std::string &aFilePath, REPORTER& } } } - catch( const std::filesystem::filesystem_error& e ) + catch( const IO_ERROR& e ) { - aReporter.Report( e.what(), RPT_SEVERITY_ERROR ); + aReporter.Report( e.What(), RPT_SEVERITY_ERROR ); } catch( const tao::pegtl::parse_error& e ) { @@ -123,21 +122,21 @@ void SPICE_LIBRARY_PARSER::readElement( const std::string &aFilePath, REPORTER& } -void SPICE_LIBRARY_PARSER::ReadFile( const std::string& aFilePath, REPORTER* aReporter ) +void SPICE_LIBRARY_PARSER::ReadFile( const wxString& aFilePath, REPORTER* aReporter ) { m_library.m_models.clear(); m_library.m_modelNames.clear(); if( aReporter ) { - readElement( aFilePath, *aReporter ); + parseFile( aFilePath, *aReporter ); } else { wxString msg; WX_STRING_REPORTER reporter( &msg ); - readElement( aFilePath, reporter ); + parseFile( aFilePath, reporter ); if( reporter.HasMessage() ) THROW_IO_ERROR( msg ); diff --git a/eeschema/sim/spice_library_parser.h b/eeschema/sim/spice_library_parser.h index e364e810bf..d7975eec55 100644 --- a/eeschema/sim/spice_library_parser.h +++ b/eeschema/sim/spice_library_parser.h @@ -40,10 +40,10 @@ public: virtual ~SPICE_LIBRARY_PARSER() {}; - virtual void ReadFile( const std::string& aFilePath, REPORTER* aReporter ); + virtual void ReadFile( const wxString& aFilePath, REPORTER* aReporter ); protected: - void readElement( const std::string& aFilePath, REPORTER& aReporter ); + void parseFile( const wxString& aFilePath, REPORTER& aReporter ); private: SIM_LIBRARY_SPICE& m_library; diff --git a/include/richio.h b/include/richio.h index cb1d0a7c36..be6a97f953 100644 --- a/include/richio.h +++ b/include/richio.h @@ -70,6 +70,18 @@ std::string StrPrintf( const char* format, ... ); +/** + * Nominally opens a file and reads it into a string. But unlike other facilities, this handles + * mis-encded Wine-written files on macOS. + * + * @param aFilePath + * @param aReadType + * @throw IO_ERROR if the file can't be opened + * @return the file contents + */ +wxString SafeReadFile( const wxString& aFilePath, const wxString& aReadType ); + + #define LINE_READER_LINE_DEFAULT_MAX 1000000 #define LINE_READER_LINE_INITIAL_SIZE 5000