diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index 9994fcc6..da136342 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -90,8 +91,9 @@ namespace QUtil QPDF_DLL int os_wrapper(std::string const& description, int status); - // If the open fails, throws std::runtime_error. Otherwise, the - // FILE* is returned. + // If the open fails, throws std::runtime_error. Otherwise, the + // FILE* is returned. The filename should be UTF-8 encoded, even + // on Windows. It will be converted as needed on Windows. QPDF_DLL FILE* safe_fopen(char const* filename, char const* mode); @@ -308,10 +310,29 @@ namespace QUtil QPDF_DLL RandomDataProvider* getRandomDataProvider(); + // Filename is UTF-8 encoded, even on Windows, as described in the + // comments for safe_fopen. QPDF_DLL std::list read_lines_from_file(char const* filename); + // ABI: make preserve_eol an optional arg and remove single-arg version + QPDF_DLL + std::list read_lines_from_file( + char const* filename, bool preserve_eol); QPDF_DLL std::list read_lines_from_file(std::istream&); + // ABI: make preserve_eol an optional arg and remove single-arg version + QPDF_DLL + std::list read_lines_from_file( + std::istream&, bool preserve_eol); + QPDF_DLL + std::list read_lines_from_file( + FILE*, bool preserve_eol = false); + QPDF_DLL + void read_lines_from_file( + std::function next_char, + std::list& lines, + bool preserve_eol = false); + QPDF_DLL void read_file_into_memory( char const* filename, PointerHolder& file_buf, size_t& size); diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index 5fda01c9..42647504 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -234,6 +234,23 @@ static unsigned short mac_roman_to_unicode[] = { 0x02c7, // 0xff }; +class FileCloser +{ + public: + FileCloser(FILE* f) : + f(f) + { + } + + ~FileCloser() + { + fclose(f); + } + + private: + FILE* f; +}; + template static std::string @@ -987,19 +1004,6 @@ QUtil::is_number(char const* p) return found_digit; } -std::list -QUtil::read_lines_from_file(char const* filename) -{ - std::ifstream in(filename, std::ios_base::binary); - if (! in.is_open()) - { - throw_system_error(std::string("open ") + filename); - } - std::list lines = read_lines_from_file(in); - in.close(); - return lines; -} - void QUtil::read_file_into_memory( char const* filename, @@ -1039,19 +1043,70 @@ QUtil::read_file_into_memory( fclose(f); } +std::list +QUtil::read_lines_from_file(char const* filename) +{ + // ABI: remove this method + std::list lines; + FILE* f = safe_fopen(filename, "rb"); + FileCloser fc(f); + auto next_char = [&f](char& ch) { return (fread(&ch, 1, 1, f) > 0); }; + read_lines_from_file(next_char, lines, false); + return lines; +} + +std::list +QUtil::read_lines_from_file(char const* filename, bool preserve_eol) +{ + std::list lines; + FILE* f = safe_fopen(filename, "rb"); + FileCloser fc(f); + auto next_char = [&f](char& ch) { return (fread(&ch, 1, 1, f) > 0); }; + read_lines_from_file(next_char, lines, preserve_eol); + return lines; +} + std::list QUtil::read_lines_from_file(std::istream& in) { - std::list result; - std::string* buf = 0; + // ABI: remove this method + std::list lines; + auto next_char = [&in](char& ch) { return (in.get(ch)) ? true: false; }; + read_lines_from_file(next_char, lines, false); + return lines; +} +std::list +QUtil::read_lines_from_file(std::istream& in, bool preserve_eol) +{ + std::list lines; + auto next_char = [&in](char& ch) { return (in.get(ch)) ? true: false; }; + read_lines_from_file(next_char, lines, preserve_eol); + return lines; +} + +std::list +QUtil::read_lines_from_file(FILE* f, bool preserve_eol) +{ + std::list lines; + auto next_char = [&f](char& ch) { return (fread(&ch, 1, 1, f) > 0); }; + read_lines_from_file(next_char, lines, preserve_eol); + return lines; +} + +void +QUtil::read_lines_from_file(std::function next_char, + std::list& lines, + bool preserve_eol) +{ + std::string* buf = 0; char c; - while (in.get(c)) + while (next_char(c)) { if (buf == 0) { - result.push_back(""); - buf = &(result.back()); + lines.push_back(""); + buf = &(lines.back()); buf->reserve(80); } @@ -1074,8 +1129,6 @@ QUtil::read_lines_from_file(std::istream& in) buf->append(1, c); } } - - return result; } int diff --git a/libtests/qutil.cc b/libtests/qutil.cc index 3c87cd31..0331d715 100644 --- a/libtests/qutil.cc +++ b/libtests/qutil.cc @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef _WIN32 # include @@ -408,6 +409,15 @@ void read_from_file_test() { std::cout << *iter << std::endl; } + // Test the other versions and make sure we get the same results + { + std::ifstream infs("other-file", std::ios_base::binary); + assert(QUtil::read_lines_from_file(infs) == lines); + FILE* fp = QUtil::safe_fopen("other-file", "rb"); + assert(QUtil::read_lines_from_file(fp) == lines); + fclose(fp); + } + PointerHolder buf; size_t size = 0; QUtil::read_file_into_memory("other-file", buf, size);