Skip to content

Commit 10554b9

Browse files
committed
Eof: Don't check ftell() in text-mode
ftell() and ftello64() cannot safely be used on text-mode streams in the way Eof() was using it: It assumed the returned value was a valid file offset corresponding to the current position, but ftell() isn't guaranteed to actually return that. It's only guaranteed to return something that can be used with fseek(). I ran into this issue on Windows when reading a file with LF line endings (instead of CRLF) in text mode. The ftell() value was different from the real file offset (probably due to the internal LF => CRLF conversions?), causing Eof() to return TRUE too early. I've added the test case - it failed for me on Windows. It seems that whether ftell() gives a useful result also depends on file size and the buffer size set with setvbuf(... _IOFBF ...) (FB uses that too). The point is that ftell() isn't guaranteed to give valid file offsets in text-mode. Instead, this patch changes Eof() to peek ahead via getc()/ungetc(), and check for EOF this way.
1 parent 344389a commit 10554b9

File tree

5 files changed

+3409
-14
lines changed

5 files changed

+3409
-14
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Version 1.05.0
1818
- Compiler crash during error recovery when there was an error when parsing the argument expression for a BYREF AS ANY parameter
1919
- 1.04.0 regression: Get# for WStrings was incorrectly changed to convert the loaded bytes to wstring characters, like Input# would do. Now it's changed back to just loading the raw bytes into the wstring, which is also how Get# works for other datatypes.
2020
- 1.04.0 regression: Due to the Get# wstring breakage, the compiler failed to read source files encoded in UTF16LE with BOM on Windows, and UTF32LE with BOM on Linux.
21+
- Eof() could incorrectly return TRUE too early on Windows, when reading a big text file with LF line endings, OPENed FOR INPUT (text mode)
2122

2223

2324
Version 1.04.0

src/rtlib/dev_file_eof.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
int fb_DevFileEof( FB_FILE *handle )
66
{
7-
int result;
87
FILE *fp;
98

109
FB_LOCK();
@@ -16,32 +15,35 @@ int fb_DevFileEof( FB_FILE *handle )
1615
return FB_TRUE;
1716
}
1817

19-
result = FB_FALSE;
20-
18+
int eof;
2119
switch( handle->mode ) {
2220
/* non-text mode? */
2321
case FB_FILE_MODE_BINARY:
2422
case FB_FILE_MODE_RANDOM:
2523
/* note: handle->putback_size will be checked by fb_FileEofEx() */
26-
result = (ftello( fp ) >= handle->size ? FB_TRUE : FB_FALSE);
24+
/* This detects both cases: a) last read reached EOF, b) next
25+
read will reach EOF */
26+
eof = (ftello( fp ) >= handle->size);
2727
break;
2828

2929
/* text-mode (INPUT, OUTPUT or APPEND) */
3030
default:
31-
/* try feof() first, because the EOF char (27) */
32-
if( feof( fp ) ) {
33-
result = FB_TRUE;
34-
/* if in input mode, feof() won't return TRUE if file_pos == file_size */
35-
} else if( handle->mode == FB_FILE_MODE_INPUT ) {
36-
/* Check the file size, if known
37-
(excluding pipes, for example, which re-use fb_DevFileEof()) */
38-
if( handle->hooks->pfnTell != NULL && handle->hooks->pfnSeek != NULL ) {
39-
result = (ftello( fp ) >= handle->size ? FB_TRUE : FB_FALSE);
31+
/* This also handles the EOF char (27). */
32+
/* We can't check ftell(), because it's not guaranteed to give
33+
a real file offset in text mode. */
34+
/* a) detect whether last read reached EOF */
35+
eof = feof( fp );
36+
if( !eof ) {
37+
/* b) peek ahead: will the next read reach EOF? */
38+
int c = getc( fp );
39+
eof = (c == EOF);
40+
if( !eof ) {
41+
ungetc( c, fp );
4042
}
4143
}
4244
break;
4345
}
4446

4547
FB_UNLOCK();
46-
return result;
48+
return eof ? FB_TRUE : FB_FALSE;
4749
}

tests/file/.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
big-list-eol-lf.txt eol=lf

0 commit comments

Comments
 (0)