diff --git a/core/src/utility.cpp b/core/src/utility.cpp index b79e4c5..3dac96c 100644 --- a/core/src/utility.cpp +++ b/core/src/utility.cpp @@ -19,9 +19,52 @@ #include #include #include +#include #include "SourcetrailException.h" +namespace +{ +std::istream& safeGetline(std::istream& is, std::string& t) +{ + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. + // Code that uses streambuf this way must be guarded by a sentry object. + // The sentry object performs various tasks, + // such as thread synchronization and updating the stream state. + + std::istream::sentry se(is, true); + std::streambuf* sb = is.rdbuf(); + + while (true) + { + int c = sb->sbumpc(); + switch (c) + { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') + { + sb->sbumpc(); + } + return is; + case std::streambuf::traits_type::eof(): + // Also handle the case when the last line has no line ending + if (t.empty()) + { + is.setstate(std::ios::eofbit); + } + return is; + default: + t += (char)c; + } + } +} +} // namespace + namespace sourcetrail { namespace utility @@ -34,23 +77,53 @@ bool getFileExists(const std::string& filePath) std::string getFileContent(const std::string& filePath) { + std::vector lines; + + try + { + std::ifstream srcFile; + srcFile.open(filePath, std::ios::binary | std::ios::in); + + if (srcFile.fail()) + { + throw SourcetrailException("Could not open file " + filePath); + } + + while (!srcFile.eof()) + { + std::string line; + safeGetline(srcFile, line); + lines.push_back(line + '\n'); + } + + srcFile.close(); + } + catch (std::exception& e) + { + throw SourcetrailException("Exception thrown while reading file \"" + filePath + "\": " + e.what()); + } + catch (...) + { + throw SourcetrailException("Unknown exception thrown while reading file \"" + filePath + "\""); + } + + if (!lines.empty()) + { + std::string last = lines.back().substr(0, lines.back().size() - 1); + lines.pop_back(); + if (!last.empty()) + { + lines.push_back(last); + } + } + std::string content; - std::ifstream file; - file.open(filePath); - - if (file.fail()) + for (const std::string& line: lines) { - throw SourcetrailException("Could not open file " + filePath); + content += line; } - for (std::string line; std::getline(file, line);) - { - content += line + "\n"; - } - - file.close(); - return content; }