about summary refs log tree commit diff
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);