Replace file reading code in wxTextFile with wxFile::ReadAll()
There is nothing special about wxTextFile justifying having code for
dealing with non-seekable files in it, so put this code into wxFile
itself and just call its ReadAll() method from here.
This is a better fix than 41f6f17d01
originally applied (and which is going to be reverted next) as it
doesn't break wxFile::Length() for these files and also avoids
triggering an assert if the file we're trying to read was truncated by
another process in the meanwhile -- which can happen and doesn't
indicate a programming error and so shouldn't result in an assert.
Also add a unit test checking that this really works.
See #3802, #8354, #9965.
This commit is contained in:
@@ -93,122 +93,13 @@ bool wxTextFile::OnRead(const wxMBConv& conv)
|
|||||||
// file should be opened
|
// file should be opened
|
||||||
wxASSERT_MSG( m_file.IsOpened(), wxT("can't read closed file") );
|
wxASSERT_MSG( m_file.IsOpened(), wxT("can't read closed file") );
|
||||||
|
|
||||||
// read the entire file in memory: this is not the most efficient thing to
|
wxString str;
|
||||||
// do it but there is no good way to avoid it in Unicode build because if
|
if ( !m_file.ReadAll(&str, conv) )
|
||||||
// we read the file block by block we can't convert each block to Unicode
|
|
||||||
// separately (the last multibyte char in the block might be only partially
|
|
||||||
// read and so the conversion would fail) and, as the file contents is kept
|
|
||||||
// in memory by wxTextFile anyhow, it shouldn't be a big problem to read
|
|
||||||
// the file entirely
|
|
||||||
size_t bufSize = 0;
|
|
||||||
|
|
||||||
// number of bytes to (try to) read from disk at once
|
|
||||||
static const size_t BLOCK_SIZE = 4096;
|
|
||||||
|
|
||||||
wxCharBuffer buf;
|
|
||||||
|
|
||||||
// first determine if the file is seekable or not and so whether we can
|
|
||||||
// determine its length in advance
|
|
||||||
wxFileOffset fileLength;
|
|
||||||
{
|
{
|
||||||
wxLogNull logNull;
|
wxLogError(_("Failed to read text file \"%s\"."), GetName());
|
||||||
fileLength = m_file.Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// some non-seekable files under /proc under Linux pretend that they're
|
|
||||||
// seekable but always return 0; others do return an error
|
|
||||||
const bool seekable = fileLength != wxInvalidOffset && fileLength != 0;
|
|
||||||
if ( seekable )
|
|
||||||
{
|
|
||||||
// we know the required length, so set the buffer size in advance
|
|
||||||
bufSize = fileLength;
|
|
||||||
if ( !buf.extend(bufSize) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// if the file is seekable, also check that we're at its beginning
|
|
||||||
wxASSERT_MSG( m_file.Tell() == 0, wxT("should be at start of file") );
|
|
||||||
|
|
||||||
char *dst = buf.data();
|
|
||||||
for ( size_t nRemaining = bufSize; nRemaining > 0; )
|
|
||||||
{
|
|
||||||
size_t nToRead = BLOCK_SIZE;
|
|
||||||
|
|
||||||
// the file size could have changed, avoid overflowing the buffer
|
|
||||||
// even if it did
|
|
||||||
if ( nToRead > nRemaining )
|
|
||||||
nToRead = nRemaining;
|
|
||||||
|
|
||||||
ssize_t nRead = m_file.Read(dst, nToRead);
|
|
||||||
|
|
||||||
if ( nRead == wxInvalidOffset )
|
|
||||||
{
|
|
||||||
// read error (error message already given in wxFile::Read)
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( nRead == 0 )
|
|
||||||
{
|
|
||||||
// this file can't be empty because we checked for this above
|
|
||||||
// so this must be the end of file
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dst += nRead;
|
|
||||||
nRemaining -= nRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxASSERT_MSG( dst - buf.data() == (wxFileOffset)bufSize,
|
|
||||||
wxT("logic error") );
|
|
||||||
}
|
|
||||||
else // file is not seekable
|
|
||||||
{
|
|
||||||
char block[BLOCK_SIZE];
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
ssize_t nRead = m_file.Read(block, WXSIZEOF(block));
|
|
||||||
|
|
||||||
if ( nRead == wxInvalidOffset )
|
|
||||||
{
|
|
||||||
// read error (error message already given in wxFile::Read)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( nRead == 0 )
|
|
||||||
{
|
|
||||||
// if no bytes have been read, presumably this is a
|
|
||||||
// valid-but-empty file
|
|
||||||
if ( bufSize == 0 )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// otherwise we've finished reading the file
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extend the buffer for new data
|
|
||||||
if ( !buf.extend(bufSize + nRead) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// and append it to the buffer
|
|
||||||
memcpy(buf.data() + bufSize, block, nRead);
|
|
||||||
bufSize += nRead;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const wxString str(buf, conv, bufSize);
|
|
||||||
|
|
||||||
// there's no risk of this happening in ANSI build
|
|
||||||
#if wxUSE_UNICODE
|
|
||||||
if ( bufSize > 4 && str.empty() )
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to convert file \"%s\" to Unicode."), GetName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // wxUSE_UNICODE
|
|
||||||
|
|
||||||
// we don't need this memory any more
|
|
||||||
buf.reset();
|
|
||||||
|
|
||||||
|
|
||||||
// now break the buffer in lines
|
// now break the buffer in lines
|
||||||
|
|
||||||
// the beginning of the current line, changes inside the loop
|
// the beginning of the current line, changes inside the loop
|
||||||
|
@@ -338,5 +338,29 @@ void TextFileTestCase::ReadBig()
|
|||||||
f[NUM_LINES - 1] );
|
f[NUM_LINES - 1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // wxUSE_TEXTFILE
|
#ifdef __LINUX__
|
||||||
|
|
||||||
|
// Check if using wxTextFile with special files, whose reported size doesn't
|
||||||
|
// correspond to the real amount of data in them, works.
|
||||||
|
TEST_CASE("wxTextFile::Special", "[textfile][linux][special-file]")
|
||||||
|
{
|
||||||
|
SECTION("/proc")
|
||||||
|
{
|
||||||
|
wxTextFile f;
|
||||||
|
CHECK( f.Open("/proc/diskstats") );
|
||||||
|
CHECK( f.GetLineCount() > 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("/sys")
|
||||||
|
{
|
||||||
|
wxTextFile f;
|
||||||
|
CHECK( f.Open("/sys/power/state") );
|
||||||
|
REQUIRE( f.GetLineCount() == 1 );
|
||||||
|
INFO( "/sys/power/state contains \"" << f[0] << "\"" );
|
||||||
|
CHECK( f[0].find("mem") != wxString::npos );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __LINUX__
|
||||||
|
|
||||||
|
#endif // wxUSE_TEXTFILE
|
||||||
|
Reference in New Issue
Block a user