From 1f52cb087aeb1eef7082884df6423b749e7f50c1 Mon Sep 17 00:00:00 2001 From: KWSys Upstream Date: Fri, 18 May 2018 09:51:35 -0400 Subject: KWSys 2018-05-18 (5357cfc4) Code extracted from: https://gitlab.kitware.com/utils/kwsys.git at commit 5357cfc4af0cbef4c78df5834ff0440cee3148d1 (master). Upstream Shortlog ----------------- Peter Wu (1): 898dc6b9 SystemTools: Fix GetLineFromStream to avoid libc++ bug on OS X 10.7 --- SystemTools.cxx | 48 ++++++++-------------------------- SystemTools.hxx.in | 5 ++-- testSystemTools.cxx | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 41 deletions(-) diff --git a/SystemTools.cxx b/SystemTools.cxx index 7167527..7743e4e 100644 --- a/SystemTools.cxx +++ b/SystemTools.cxx @@ -4032,23 +4032,16 @@ std::string SystemTools::MakeCidentifier(const std::string& s) return str; } -// Due to a buggy stream library on the HP and another on Mac OS X, we -// need this very carefully written version of getline. Returns true +// Convenience function around std::getline which removes a trailing carriage +// return and can truncate the buffer as needed. Returns true // if any data were read before the end-of-file was reached. bool SystemTools::GetLineFromStream(std::istream& is, std::string& line, bool* has_newline /* = 0 */, long sizeLimit /* = -1 */) { - const int bufferSize = 1024; - char buffer[bufferSize]; - bool haveData = false; - bool haveNewline = false; - // Start with an empty line. line = ""; - long leftToRead = sizeLimit; - // Early short circuit return if stream is no good. Just return // false and the empty line. (Probably means caller tried to // create a file stream with a non-existent file name...) @@ -4060,44 +4053,23 @@ bool SystemTools::GetLineFromStream(std::istream& is, std::string& line, return false; } - // If no characters are read from the stream, the end of file has - // been reached. Clear the fail bit just before reading. - while (!haveNewline && leftToRead != 0 && - (static_cast(is.clear(is.rdstate() & ~std::ios::failbit)), - static_cast(is.getline(buffer, bufferSize)), - is.gcount() > 0)) { - // We have read at least one byte. - haveData = true; - - // If newline character was read the gcount includes the character - // but the buffer does not: the end of line has been reached. - size_t length = strlen(buffer); - if (length < static_cast(is.gcount())) { - haveNewline = true; - } - + std::getline(is, line); + bool haveData = !line.empty() || !is.eof(); + if (!line.empty()) { // Avoid storing a carriage return character. - if (length > 0 && buffer[length - 1] == '\r') { - buffer[length - 1] = 0; + if (*line.rbegin() == '\r') { + line.resize(line.size() - 1); } // if we read too much then truncate the buffer - if (leftToRead > 0) { - if (static_cast(length) > leftToRead) { - buffer[leftToRead] = 0; - leftToRead = 0; - } else { - leftToRead -= static_cast(length); - } + if (sizeLimit >= 0 && line.size() >= static_cast(sizeLimit)) { + line.resize(sizeLimit); } - - // Append the data read to the line. - line.append(buffer); } // Return the results. if (has_newline) { - *has_newline = haveNewline; + *has_newline = !is.eof(); } return haveData; } diff --git a/SystemTools.hxx.in b/SystemTools.hxx.in index e79e3fc..3898e3a 100644 --- a/SystemTools.hxx.in +++ b/SystemTools.hxx.in @@ -523,9 +523,8 @@ public: static bool GetShortPath(const std::string& path, std::string& result); /** - * Read line from file. Make sure to get everything. Due to a buggy stream - * library on the HP and another on Mac OS X, we need this very carefully - * written version of getline. Returns true if any data were read before the + * Read line from file. Make sure to read a full line and truncates it if + * requested via sizeLimit. Returns true if any data were read before the * end-of-file was reached. If the has_newline argument is specified, it will * be true when the line read had a newline character. */ diff --git a/testSystemTools.cxx b/testSystemTools.cxx index a6d934b..8c928d4 100644 --- a/testSystemTools.cxx +++ b/testSystemTools.cxx @@ -912,6 +912,78 @@ static bool CheckGetLineFromStream() return ret; } +static bool CheckGetLineFromStreamLongLine() +{ + const std::string fileWithLongLine("longlines.txt"); + std::string firstLine, secondLine; + // First line: large buffer, containing a carriage return for some reason. + firstLine.assign(2050, ' '); + firstLine += "\rfirst"; + secondLine.assign(2050, 'y'); + secondLine += "second"; + + // Create file with long lines. + { + kwsys::ofstream out(fileWithLongLine.c_str(), std::ios::binary); + if (!out) { + std::cerr << "Problem opening for write: " << fileWithLongLine + << std::endl; + return false; + } + out << firstLine << "\r\n\n" << secondLine << "\n"; + } + + kwsys::ifstream file(fileWithLongLine.c_str(), std::ios::binary); + if (!file) { + std::cerr << "Problem opening: " << fileWithLongLine << std::endl; + return false; + } + + std::string line; + bool has_newline = false; + bool result; + + // Read first line. + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line != firstLine) { + std::cerr << "First line does not match, expected " << firstLine.size() + << " characters, got " << line.size() << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read from first line" << std::endl; + return false; + } + + // Read empty line. + has_newline = false; + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || !line.empty()) { + std::cerr << "Expected successful read with an empty line, got " + << line.size() << " characters" << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read for an empty line" << std::endl; + return false; + } + + // Read second line. + has_newline = false; + result = kwsys::SystemTools::GetLineFromStream(file, line, &has_newline, -1); + if (!result || line != secondLine) { + std::cerr << "Second line does not match, expected " << secondLine.size() + << " characters, got " << line.size() << std::endl; + return false; + } + if (!has_newline) { + std::cerr << "Expected new line to be read from second line" << std::endl; + return false; + } + + return true; +} + int testSystemTools(int, char* []) { bool res = true; @@ -951,6 +1023,8 @@ int testSystemTools(int, char* []) res &= CheckGetLineFromStream(); + res &= CheckGetLineFromStreamLongLine(); + res &= CheckGetFilenameName(); return res ? 0 : 1; -- cgit v0.12