53 lines
1.3 KiB
C
53 lines
1.3 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "fileutil.h"
|
|
|
|
size_t fread(void *data, size_t membsize, size_t nmemb, FILE *fp)
|
|
{
|
|
if(!fp->readable) {
|
|
fp->error = 1;
|
|
return 0;
|
|
}
|
|
|
|
size_t request_size;
|
|
if(__builtin_umul_overflow(membsize, nmemb, &request_size)) {
|
|
fp->error = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* If the stream if unbuffered, we might have no buffer for the reads.
|
|
If it's buffered, we always have one. It's also possible that fp is
|
|
unbuffered (_IONBF) but has a buffer temporarily because ungetc()
|
|
has been used, in which case we have to transition from buffered
|
|
reads into direct reads midway. We use __fp_buffered_read() to
|
|
handle this. */
|
|
|
|
size_t read_size = 0;
|
|
__fp_buffer_mode_read(fp);
|
|
|
|
while(read_size < request_size) {
|
|
int remaining = request_size - read_size;
|
|
int chunk = __fp_buffered_read(fp, data+read_size, remaining);
|
|
|
|
/* Stream is not/no longer buffered, finish unbuffered */
|
|
if(chunk < 0) {
|
|
ssize_t rc = __fp_read(fp, data+read_size, remaining);
|
|
return read_size + (rc == EOF ? 0 : rc);
|
|
}
|
|
|
|
read_size += chunk;
|
|
if(read_size >= request_size)
|
|
break;
|
|
|
|
/* Get more data from the file descriptor into the buffer */
|
|
if(fp->buf) {
|
|
ssize_t rc = __fp_read(fp, fp->buf, fp->bufsize);
|
|
if(rc <= 0) /* EOF or error */
|
|
break;
|
|
fp->bufread = rc;
|
|
}
|
|
}
|
|
|
|
return read_size;
|
|
}
|