summaryrefslogtreecommitdiff
path: root/src/stdio/fread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stdio/fread.c')
-rw-r--r--src/stdio/fread.c49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/stdio/fread.c b/src/stdio/fread.c
new file mode 100644
index 00000000..0fa0b2aa
--- /dev/null
+++ b/src/stdio/fread.c
@@ -0,0 +1,49 @@
+#include "stdio_impl.h"
+
+#define MIN(a,b) ((a)<(b) ? (a) : (b))
+
+size_t fread(void *destv, size_t size, size_t nmemb, FILE *f)
+{
+ unsigned char *dest = destv;
+ size_t len = size*nmemb, l = len, k;
+
+ /* Never touch the file if length is zero.. */
+ if (!l) return 0;
+
+ FLOCK(f);
+
+ for (;;) {
+ /* First exhaust the buffer. */
+ k = MIN(f->rend - f->rpos, l);
+ memcpy(dest, f->rpos, k);
+ f->rpos += k;
+ dest += k;
+ l -= k;
+
+ /* Stop on EOF or errors */
+ if (f->flags & (F_EOF|F_ERR|F_NORD)) goto eof;
+
+ /* Done? Or going unbuffered? */
+ if (!l || l > f->buf_size/2) break;
+
+ /* Otherwise, refill & read thru buffer. */
+ __underflow(f);
+ }
+
+ /* Read the remainder directly */
+ for (; l; l-=k, dest+=k) {
+ k = f->read(f, dest, l);
+ if (k+1<=1) {
+ f->flags |= F_EOF | (F_ERR & k);
+ goto eof;
+ }
+ }
+
+ FUNLOCK(f);
+ return nmemb;
+eof:
+ FUNLOCK(f);
+ return (len-l)/size;
+}
+
+weak_alias(fread, fread_unlocked);