diff libinterp/corefcn/oct-stream.cc @ 18512:fdd27f68b011 stable

handle fread skip parameter correctly (bug #41648) * oct-stream.cc (octave_stream::read): Decide whether to skip based on current number of elements read, not total. Correctly handle case of reading partial blocks when there is a repeat cound. Skip to EOF if the full skip is beyond EOF. * io.tst: New test.
author Rik <rik@octave.org> and John W. Eaton <jwe@octave.org>
date Fri, 21 Feb 2014 15:28:02 -0500
parents a0abcf377ec5
children bc31d9359cf9 0bdecd41b2dd
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc	Thu Jan 16 16:26:56 2014 -0500
+++ b/libinterp/corefcn/oct-stream.cc	Fri Feb 21 15:28:02 2014 -0500
@@ -3260,6 +3260,14 @@
               while (is && ! is.eof ()
                      && (read_to_eof || count < elts_to_read))
                 {
+                  if (! read_to_eof)
+                    {
+                      octave_idx_type remaining_elts = elts_to_read - count;
+
+                      if (remaining_elts < input_buf_elts)
+                        input_buf_size = remaining_elts * input_elt_size;
+                    }
+
                   char *input_buf = new char [input_buf_size];
 
                   is.read (input_buf, input_buf_size);
@@ -3268,15 +3276,35 @@
 
                   char_count += gcount;
 
-                  count += gcount / input_elt_size;
+                  size_t nel = gcount / input_elt_size;
+
+                  count += nel;
 
                   input_buf_list.push_back (input_buf);
 
-                  if (is && skip != 0 && count == block_size)
+                  if (is && skip != 0 && nel == block_size)
                     {
-                      int seek_status = seek (skip, SEEK_CUR);
-
-                      if (seek_status < 0)
+                      // Seek to skip.  If skip would move past EOF,
+                      // position at EOF.
+
+                      off_t orig_pos = tell ();
+
+                      seek (0, SEEK_END);
+
+                      off_t eof_pos = tell ();
+
+                      // Is it possible for this to fail to return us to
+                      // the original position?
+                      seek (orig_pos, SEEK_SET);
+
+                      size_t remaining = eof_pos - orig_pos;
+
+                      if (remaining < skip)
+                        seek (0, SEEK_END);
+                      else
+                        seek (skip, SEEK_CUR);
+
+                      if (! is)
                         break;
                     }
                 }