diff --git a/opm/parser/eclipse/Deck/Deck.hpp b/opm/parser/eclipse/Deck/Deck.hpp index e5f7eb360..8febffe46 100644 --- a/opm/parser/eclipse/Deck/Deck.hpp +++ b/opm/parser/eclipse/Deck/Deck.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -146,7 +147,7 @@ namespace Opm { void selectActiveUnitSystem( UnitSystem::UnitType unit_type ); const std::string& getInputPath() const; - const std::string& getDataFile() const; + std::string getDataFile() const; void setDataFile(const std::string& dataFile); std::string makeDeckPath(const std::string& path) const; @@ -175,7 +176,7 @@ namespace Opm { UnitSystem defaultUnits; std::unique_ptr activeUnits; - std::string m_dataFile; + std::optional m_dataFile; std::string input_path; mutable std::size_t unit_system_access_count = 0; }; diff --git a/src/opm/parser/eclipse/Deck/Deck.cpp b/src/opm/parser/eclipse/Deck/Deck.cpp index d9eac5fd3..c3a64e54a 100644 --- a/src/opm/parser/eclipse/Deck/Deck.cpp +++ b/src/opm/parser/eclipse/Deck/Deck.cpp @@ -245,8 +245,8 @@ namespace Opm { return this->defaultUnits; } - const std::string& Deck::getDataFile() const { - return m_dataFile; + std::string Deck::getDataFile() const { + return m_dataFile.value_or(""); } const std::string& Deck::getInputPath() const { @@ -265,6 +265,9 @@ namespace Opm { void Deck::setDataFile(const std::string& dataFile) { + if (this->m_dataFile.has_value()) + throw std::logic_error("Can not reassign deck datafile"); + this->m_dataFile = dataFile; auto slash_pos = dataFile.find_last_of("/\\"); diff --git a/src/opm/parser/eclipse/Parser/Parser.cpp b/src/opm/parser/eclipse/Parser/Parser.cpp index c63c1eef4..030b1c2a0 100644 --- a/src/opm/parser/eclipse/Parser/Parser.cpp +++ b/src/opm/parser/eclipse/Parser/Parser.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -374,7 +375,7 @@ class ParserState { void openRootFile( const Opm::filesystem::path& ); void handleRandomText(const std::string_view& ) const; - Opm::filesystem::path getIncludeFilePath( std::string ) const; + std::optional getIncludeFilePath( std::string ) const; void addPathAlias( const std::string& alias, const std::string& path ); const Opm::filesystem::path& current_path() const; @@ -516,18 +517,9 @@ void ParserState::loadString(const std::string& input) { void ParserState::loadFile(const Opm::filesystem::path& inputFile) { - Opm::filesystem::path inputFileCanonical; - try { - inputFileCanonical = Opm::filesystem::canonical(inputFile); - } catch (const Opm::filesystem::filesystem_error& fs_error) { - std::string msg = "Could not open file: " + inputFile.string(); - parseContext.handleError( ParseContext::PARSE_MISSING_INCLUDE , msg, {}, errors); - return; - } - const auto closer = []( std::FILE* f ) { std::fclose( f ); }; std::unique_ptr< std::FILE, decltype( closer ) > ufp( - std::fopen( inputFileCanonical.string().c_str(), "rb" ), + std::fopen( inputFile.c_str(), "rb" ), closer ); @@ -553,9 +545,9 @@ void ParserState::loadFile(const Opm::filesystem::path& inputFile) { if( std::ferror( fp ) || readc != buffer.size() - 1 ) throw std::runtime_error( "Error when reading input file '" - + inputFileCanonical.string() + "'" ); + + inputFile.string() + "'" ); - this->input_stack.push( str::clean( this->code_keywords, buffer ), inputFileCanonical ); + this->input_stack.push( str::clean( this->code_keywords, buffer ), inputFile ); } /* @@ -596,7 +588,7 @@ void ParserState::openRootFile( const Opm::filesystem::path& inputFile) { this->rootPath = inputFileCanonical.parent_path(); } -Opm::filesystem::path ParserState::getIncludeFilePath( std::string path ) const { +std::optional ParserState::getIncludeFilePath( std::string path ) const { static const std::string pathKeywordPrefix("$"); static const std::string validPathNameCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); @@ -620,7 +612,14 @@ Opm::filesystem::path ParserState::getIncludeFilePath( std::string path ) const Opm::filesystem::path includeFilePath(path); if (includeFilePath.is_relative()) - return this->rootPath / includeFilePath; + includeFilePath = this->rootPath / includeFilePath; + + try { + includeFilePath = Opm::filesystem::canonical(includeFilePath); + } catch (const Opm::filesystem::filesystem_error& fs_error) { + parseContext.handleError( ParseContext::PARSE_MISSING_INCLUDE , fmt::format("No such file: {}", includeFilePath.string()), {}, errors); + return {}; + } return includeFilePath; } @@ -1010,9 +1009,10 @@ bool parseState( ParserState& parserState, const Parser& parser ) { if (rawKeyword->getKeywordName() == Opm::RawConsts::include) { auto& firstRecord = rawKeyword->getFirstRecord( ); std::string includeFileAsString = readValueToken(firstRecord.getItem(0)); - Opm::filesystem::path includeFile = parserState.getIncludeFilePath( includeFileAsString ); + const auto& includeFile = parserState.getIncludeFilePath( includeFileAsString ); - parserState.loadFile( includeFile ); + if (includeFile.has_value()) + parserState.loadFile( includeFile.value() ); continue; } @@ -1042,9 +1042,9 @@ bool parseState( ParserState& parserState, const Parser& parser ) { if (deck_keyword.name() == ParserKeywords::IMPORT::keywordName) { bool formatted = deck_keyword.getRecord(0).getItem(1).get(0)[0] == 'F'; - Opm::filesystem::path import_file = parserState.getIncludeFilePath(deck_keyword.getRecord(0).getItem(0).get(0)); + const auto& import_file = parserState.getIncludeFilePath(deck_keyword.getRecord(0).getItem(0).get(0)); - ImportContainer import(parser, parserState.deck.getActiveUnitSystem(), import_file.string(), formatted, parserState.deck.size()); + ImportContainer import(parser, parserState.deck.getActiveUnitSystem(), import_file.value().string(), formatted, parserState.deck.size()); for (auto kw : import) parserState.deck.addKeyword(std::move(kw)); } else @@ -1171,7 +1171,22 @@ bool parseState( ParserState& parserState, const Parser& parser ) { std::inserter(ignore_sections, ignore_sections.end())); } - ParserState parserState( this->codeKeywords(), parseContext, errors, dataFileName, ignore_sections); + /* + The following rules apply to the .DATA file argument which is + internalized in the deck: + + 1. It is normalized by removing uneccessary '.' characters and + resolving symlinks. + + 2. The relative/abolute status of the path is retained. + */ + std::string data_file; + if (dataFileName[0] == '/') + data_file = Opm::filesystem::canonical(dataFileName).string(); + else + data_file = Opm::filesystem::relative( Opm::filesystem::canonical(dataFileName) ); + + ParserState parserState( this->codeKeywords(), parseContext, errors, data_file, ignore_sections); parseState( parserState, *this ); return std::move( parserState.deck ); } diff --git a/tests/parser/DeckTests.cpp b/tests/parser/DeckTests.cpp index 53166db50..e7efce5a2 100644 --- a/tests/parser/DeckTests.cpp +++ b/tests/parser/DeckTests.cpp @@ -146,7 +146,6 @@ BOOST_AUTO_TEST_CASE(keywordList_getbyindex_correctkeywordreturned) { BOOST_AUTO_TEST_CASE(set_and_get_data_file) { Deck deck; - BOOST_CHECK_EQUAL("", deck.getDataFile()); BOOST_CHECK_EQUAL("", deck.getInputPath()); BOOST_CHECK_EQUAL("some/path", deck.makeDeckPath("some/path")); BOOST_CHECK_EQUAL("/abs/path", deck.makeDeckPath("/abs/path")); @@ -157,10 +156,6 @@ BOOST_AUTO_TEST_CASE(set_and_get_data_file) { BOOST_CHECK_EQUAL("/path/to", deck.getInputPath()); BOOST_CHECK_EQUAL("/path/to/some/path", deck.makeDeckPath("some/path")); BOOST_CHECK_EQUAL("/abs/path", deck.makeDeckPath("/abs/path")); - - deck.setDataFile("FILE"); - BOOST_CHECK_EQUAL("FILE", deck.getDataFile()); - BOOST_CHECK_EQUAL("", deck.getInputPath()); } BOOST_AUTO_TEST_CASE(DummyDefaultsString) {