diff --git a/libguile/ports.c b/libguile/ports.c
index b97c826..41d54c1 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>  /* for chsize on mingw */
+#include <assert.h>
 
 #include "libguile/_scm.h"
 #include "libguile/async.h"
@@ -987,6 +988,45 @@ scm_fill_input (SCM port)
   return scm_ptobs[SCM_PTOBNUM (port)].fill_input (port);
 }
 
+int
+scm_fill_input_buf (SCM port, unsigned char *buf, size_t buf_size, size_t *n_available)
+{
+  scm_t_port *pt = SCM_PTAB_ENTRY (port);
+  int ans;
+
+  /* There's no point calling this routine with any null args. */
+  assert (buf != NULL);
+  assert (buf_size != 0);
+  assert (n_available != NULL);
+
+  if (pt->read_buf == &pt->shortbuf)
+    {
+      /* Port is using the `shortbuf', which has space for 1 character
+         only.  For the next operation, use the caller's buffer
+         instead.  Then put back the shortbuf afterwards. */
+      pt->read_pos = pt->read_buf = pt->read_end = buf;
+      pt->read_buf_size = buf_size;
+
+      ans = scm_fill_input (port);
+      *n_available = pt->read_end - pt->read_pos;
+
+      pt->read_pos = pt->read_buf = pt->read_end = &pt->shortbuf;
+      pt->read_buf_size = 1;
+    }
+  else
+    {
+      /* Port is already using a proper buffer.  Use that, then copy
+         what was read into the caller's buffer. */
+      ans = scm_fill_input (port);
+      *n_available = min (buf_size, pt->read_end - pt->read_pos);
+
+      memcpy (buf, pt->read_pos, *n_available);
+      pt->read_pos += *n_available;
+    }
+
+  return ans;
+}
+
 
 /* scm_lfwrite
  *
@@ -1052,35 +1092,28 @@ scm_c_read (SCM port, void *buffer, size_t size)
   if (pt->rw_random)
     pt->rw_active = SCM_PORT_READ;
 
-  if (SCM_READ_BUFFER_EMPTY_P (pt))
-    {
-      if (scm_fill_input (port) == EOF)
-	return 0;
-    }
-  
-  n_available = pt->read_end - pt->read_pos;
-  
-  while (n_available < size)
+  /* Take bytes first from the port's read buffer. */
+  if (pt->read_pos < pt->read_end)
     {
+      n_available = min (size, pt->read_end - pt->read_pos);
       memcpy (buffer, pt->read_pos, n_available);
       buffer = (char *) buffer + n_available;
       pt->read_pos += n_available;
       n_read += n_available;
-      
-      if (SCM_READ_BUFFER_EMPTY_P (pt))
-	{
-	  if (scm_fill_input (port) == EOF)
-	    return n_read;
-	}
-
       size -= n_available;
-      n_available = pt->read_end - pt->read_pos;
     }
 
-  memcpy (buffer, pt->read_pos, size);
-  pt->read_pos += size;
+  /* For as long as we need more, refill the port's buffer and copy
+     into our own one.  (If the port doesn't have its own buffer, it
+     will read directly into our one.) */
+  while (size && (scm_fill_input_buf (port, buffer, size, &n_available) != EOF))
+    {
+      buffer = (char *) buffer + n_available;
+      n_read += n_available;
+      size -= n_available;
+    }
 
-  return n_read + size;
+  return n_read;
 }
 #undef FUNC_NAME
 
diff --git a/libguile/ports.h b/libguile/ports.h
index d1aafb4..d3a1240 100644
--- a/libguile/ports.h
+++ b/libguile/ports.h
@@ -270,6 +270,7 @@ SCM_API void scm_lfwrite (const char *ptr, size_t size, SCM port);
 SCM_API void scm_flush (SCM port);
 SCM_API void scm_end_input (SCM port);
 SCM_API int scm_fill_input (SCM port);
+SCM_API int scm_fill_input_buf (SCM port, unsigned char *buf, size_t buf_size, size_t *n_available);
 SCM_API void scm_ungetc (int c, SCM port);
 SCM_API void scm_ungets (const char *s, int n, SCM port);
 SCM_API SCM scm_peek_char (SCM port);
diff --git a/libguile/srfi-4.c b/libguile/srfi-4.c
index 7d22f8b..a01f86e 100644
--- a/libguile/srfi-4.c
+++ b/libguile/srfi-4.c
@@ -886,38 +886,11 @@ SCM_DEFINE (scm_uniform_vector_read_x, "uniform-vector-read!", 1, 3, 0,
 
   if (SCM_NIMP (port_or_fd))
     {
-      scm_t_port *pt = SCM_PTAB_ENTRY (port_or_fd);
-
-      if (pt->rw_active == SCM_PORT_WRITE)
-	scm_flush (port_or_fd);
-
       ans = cend - cstart;
-      while (remaining > 0)
-	{
-	  if (pt->read_pos < pt->read_end)
-	    {
-	      size_t to_copy = min (pt->read_end - pt->read_pos,
-				    remaining);
-	      
-	      memcpy (base + off, pt->read_pos, to_copy);
-	      pt->read_pos += to_copy;
-	      remaining -= to_copy;
-	      off += to_copy;
-	    }
-	  else
-	    {
-	      if (scm_fill_input (port_or_fd) == EOF)
-		{
-		  if (remaining % sz != 0)
-		    SCM_MISC_ERROR ("unexpected EOF", SCM_EOL);
-		  ans -= remaining / sz;
-		  break;
-		}
-	    }
-	}
-      
-      if (pt->rw_random)
-	pt->rw_active = SCM_PORT_READ;
+      remaining -= scm_c_read (port_or_fd, base + off, remaining);
+      if (remaining % sz != 0)
+        SCM_MISC_ERROR ("unexpected EOF", SCM_EOL);
+      ans -= remaining / sz;
     }
   else /* file descriptor.  */
     {
