diff --git a/docs/changes.txt b/docs/changes.txt index 2b982c304a..5ac1eb417a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -106,6 +106,7 @@ All: - Add UTF-8 support to wxZipOutputStream (Tobias Taschner). - Update all bundled 3rd party libraries to their latest versions. - Use unique prefix for all zlib symbols to avoid link conflicts. +- Make wxFile::ReadAll() work for unseekable files too. All (GUI): diff --git a/interface/wx/file.h b/interface/wx/file.h index 31ea6bdd4f..78fb12aa3c 100644 --- a/interface/wx/file.h +++ b/interface/wx/file.h @@ -377,6 +377,8 @@ public: /** Reads the entire contents of the file into a string. + Since wxWidgets 3.1.1 this method also works for unseekable files. + @param str Non-@NULL pointer to a string to read data into. @param conv diff --git a/src/common/file.cpp b/src/common/file.cpp index 0e976273dc..84cf9f78fc 100644 --- a/src/common/file.cpp +++ b/src/common/file.cpp @@ -261,24 +261,71 @@ bool wxFile::ReadAll(wxString *str, const wxMBConv& conv) { wxCHECK_MSG( str, false, wxS("Output string must be non-NULL") ); + static const ssize_t READSIZE = 4096; + + wxCharBuffer buf; + ssize_t length = Length(); - wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") ); - - wxCharBuffer buf(length); - char* p = buf.data(); - for ( ;; ) + if ( length != -1 ) { - static const ssize_t READSIZE = 4096; + wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") ); - ssize_t nread = Read(p, length > READSIZE ? READSIZE : length); - if ( nread == wxInvalidOffset ) + if ( !buf.extend(length) ) return false; - p += nread; - if ( length <= nread ) - break; + char* p = buf.data(); + for ( ;; ) + { + ssize_t nread = Read(p, length > READSIZE ? READSIZE : length); + if ( nread == wxInvalidOffset ) + return false; - length -= nread; + if ( nread == 0 ) + { + // We have reached EOF before reading the entire length of the + // file. This can happen for some special files (e.g. those + // under /sys on Linux systems) or even for regular files if + // another process has truncated the file since we started + // reading it, so deal with it gracefully. + buf.shrink(p - buf.data()); + break; + } + + p += nread; + length -= nread; + + if ( !length ) + { + // Notice that we don't keep reading after getting the expected + // number of bytes, even though in principle a situation + // similar to the one described above, with another process + // extending the file since we started to read it, is possible. + // But returning just the data that was in the file when we + // originally started reading it isn't really wrong in this + // case, so keep things simple and just do it like this. + break; + } + } + } + else // File is not seekable + { + for ( ;; ) + { + const size_t len = buf.length(); + if ( !buf.extend(len + READSIZE) ) + return false; + + ssize_t nread = Read(buf.data() + len, READSIZE); + if ( nread == wxInvalidOffset ) + return false; + + if ( nread < READSIZE ) + { + // We have reached EOF. + buf.shrink(len + nread); + break; + } + } } str->assign(buf, conv); diff --git a/tests/file/filetest.cpp b/tests/file/filetest.cpp index fdad2d8e4a..faeed69dbf 100644 --- a/tests/file/filetest.cpp +++ b/tests/file/filetest.cpp @@ -139,4 +139,28 @@ void FileTestCase::TempFile() CPPUNIT_ASSERT( wxRemoveFile(wxT("test2")) ); } +#ifdef __LINUX__ + +// Check that GetSize() works correctly for special files. +TEST_CASE("wxFile::Special", "[file][linux][special-file]") +{ + // This file is not seekable and has 0 size, but can still be read. + wxFile fileProc("/proc/diskstats"); + CHECK( fileProc.IsOpened() ); + + wxString s; + CHECK( fileProc.ReadAll(&s) ); + CHECK( !s.empty() ); + + // All files in /sys seem to have size of 4KiB currently, even if they + // don't have that much data in them. + wxFile fileSys("/sys/power/state"); + CHECK( fileSys.IsOpened() ); + CHECK( fileSys.ReadAll(&s) ); + CHECK( !s.empty() ); + CHECK( s.length() < 4096 ); +} + +#endif // __LINUX__ + #endif // wxUSE_FILE