Fix fread bug causing it to discard buffered data.

If you read from a file using fgetc (or another function that calls it internally), and then later read from it using fread, any data left in the buffer from fgetc would be skipped over. The pattern causing this to happen was as follows:

fread called ~SetFilePointer, which (if there was buffered data) called fseek(f, 0, SEEK_CUR). fseek would call fflush, which calls ~InitBuffer. That zeros out the buffer count. fseek then calls ftell, which gets the current mark from GS/OS and then subtracts the buffer count.  But the count was set to 0 in ~InitBuffer, so ftell reflects the position after any previously buffered data. fseek sets the mark to the position returned by ftell, i.e. after any data that was previously read and buffered, so that data would get skipped over. (Before commits 0047b755c9 and c95bfc19fb the behavior would be somewhat different due to the issue with ~InitBuffer that they addressed, but you could still get similar symptoms.)

The fix is simply to use the buffered data (if any), rather than discarding it.

Here is a test program illustrating the problem:

#include <stdio.h>
char buf[BUFSIZ+1];
#define RECSIZE 2
int main(void) {
        FILE *f = fopen("somefile","r"); // a file with some data
        if (!f) return 0;
        fgetc(f);
        size_t count = fread(buf, RECSIZE, BUFSIZ/RECSIZE, f);
        printf("read %zu records: %s\n", count, buf);
}
This commit is contained in:
Stephen Heumann 2022-07-02 18:24:19 -05:00
parent 84f471474a
commit 36808404ca
1 changed files with 56 additions and 47 deletions

103
stdio.asm
View File

@ -1574,15 +1574,17 @@ lb4 creturn 2:err
*
fread start
temp equ 1
p equ 5
csubroutine (4:ptr,4:element_size,4:count,4:stream),4
csubroutine (4:ptr,4:element_size,4:count,4:stream),8
phb
phk
plb
stz rdTransferCount set the # of elements read
stz rdTransferCount+2
stz pbkCount no putback characters read yet
stz extraCount no putback characters read yet
stz extraCount+2
ph4 <stream verify that stream exists
jsl ~VerifyStream
jcs lb6
@ -1599,7 +1601,7 @@ pb1 lda rdRequestCount quit if the request count is 0
long M
inc4 ptr adjust pointer and counts
dec4 rdRequestCount
inc pbkCount
inc4 extraCount
ldy #FILE_pbk+2 pop the putback buffer
lda [stream],Y
tax
@ -1610,9 +1612,7 @@ pb1 lda rdRequestCount quit if the request count is 0
sta [stream],Y
bra pb1 loop to check for another putback chr
lb0 ph4 <stream reset file pointer
jsl ~SetFilePointer
ldy #FILE_file set the file ID number
lb0 ldy #FILE_file set the file ID number
lda [stream],Y
bpl lb2 branch if it is a file
@ -1628,7 +1628,7 @@ lb1 jsl SYSKEYIN read the bytes
ora >stdin+4+FILE_flag
sta >stdin+4+FILE_flag
jsl SYSKEYIN read the closing cr
bra lb4
brl lb4
lb1a short M set character
sta [ptr]
long M
@ -1638,10 +1638,55 @@ lb1a short M set character
lda rdRequestCount
ora rdRequestCount+2
bne lb1
bra lb4
brl lb4
lb2 sta rdRefNum set the reference number
move4 ptr,rdDataBuffer set the start address
ldy #FILE_flag if the file is being read then
lda [stream],Y
bit #_IOREAD
beq lb2c
lb2a ldy #FILE_cnt while there is buffered data...
lda [stream],Y
iny
iny
ora [stream],Y
beq lb2c
lda rdRequestCount ...and the request count is not 0
ora rdRequestCount+2
beq lb4
ldy #FILE_ptr get the next character
lda [stream],Y
sta p
clc
adc #1
sta [stream],Y
iny
iny
lda [stream],Y
sta p+2
adc #0
sta [stream],Y
short M
lda [p]
sta [ptr]
long M
ldy #FILE_cnt dec the # chars in the buffer
sec
lda [stream],Y
sbc #1
sta [stream],Y
bcs lb2b
iny
iny
lda [stream],Y
dec A
sta [stream],Y
lb2b inc4 ptr adjust pointer and counts
dec4 rdRequestCount
inc4 extraCount
bra lb2a
lb2c move4 ptr,rdDataBuffer set the start address
OSRead rd read the bytes
bcc lb4
cmp #$4C if the error was $4C then
@ -1650,12 +1695,7 @@ lb2 sta rdRefNum set the reference number
bra lb4
lb3 ph4 <stream I/O error
jsr ~ioerror
lb4 clc rdTransferCount += pbkCount
lda rdTransferCount
adc pbkCount
sta rdTransferCount
bcc lb5
inc rdTransferCount+2
lb4 add4 rdTransferCount,extraCount
lb5 lda temp if there were too few elements read then
cmp rdTransferCount
bne lb5a
@ -1679,7 +1719,7 @@ rdRequestCount ds 4
rdTransferCount ds 4
dc i'1'
pbkCount ds 2 number of characters read from putback
extraCount ds 4 # characters read from putback or buffer
;
; Set the EOF flag
;
@ -6203,37 +6243,6 @@ smBase dc i'1' EOF-displacement mode
smDisplacement dc i4'0' displacement = 0
end
****************************************************************
*
* ~SetFilePointer - makes sure nothing is in the input buffer
*
* Inputs:
* stream - stream to check
*
****************************************************************
*
~SetFilePointer private
csubroutine (4:stream),0
ldy #FILE_pbk if stream->FILE_pbk != -1
lda [stream],Y
inc A
ldy #FILE_cnt or stream->FILE_cnt != 0 then
ora [stream],Y
iny
iny
ora [stream],Y
beq lb1
ph2 #SEEK_CUR fseek(stream, 0L, SEEK_CUR)
ph4 #0
ph4 <stream
jsl fseek
lb1 anop
creturn
end
****************************************************************
*
* ~VerifyStream - insures that a stream actually exists