diff --git a/io.c b/io.c index 3e184f2..66e399b 100644 --- a/io.c +++ b/io.c @@ -374,6 +374,8 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd) # endif #endif +#define rb_sys_fail_path(path) rb_sys_fail(NIL_P(path) ? 0 : RSTRING_PTR(path)) + static int io_fflush(rb_io_t *); #define NEED_NEWLINE_DECORATOR_ON_READ(fptr) ((fptr)->mode & FMODE_TEXTMODE) @@ -423,6 +425,7 @@ inline static int set_binary_mode_with_seek_cur(rb_io_t *fptr) { ssize_t read_size; long i; long newlines = 0; + long extra_max; char *p; if (!rb_w32_fd_is_text((fptr)->fd)) return O_BINARY; @@ -443,23 +446,31 @@ inline static int set_binary_mode_with_seek_cur(rb_io_t *fptr) { setmode((fptr)->fd, O_BINARY); return O_TEXT; } - /* add extra offset for '\r' */ - p = (fptr)->rbuf.ptr+(fptr)->rbuf.off; + /* add extra offset for removed '\r' in rbuf */ + extra_max = pos - (fptr)->rbuf.len; + p = (fptr)->rbuf.ptr + (fptr)->rbuf.off; for (i = 0; i < (fptr)->rbuf.len; i++) { if (*p == '\n') newlines++; + if (extra_max == newlines) break; p++; } while (newlines >= 0) { r = lseek((fptr)->fd, pos - (fptr)->rbuf.len - newlines, SEEK_SET); if (newlines == 0) break; - if (read_size = _read((fptr)->fd, (fptr)->rbuf.ptr, (fptr)->rbuf.len + newlines)) { - if (read_size == (fptr)->rbuf.len) { - lseek((fptr)->fd, r, SEEK_SET); - break; - } - else { - newlines--; - } + if (r < 0) { + newlines--; + continue; + } + read_size = _read((fptr)->fd, (fptr)->rbuf.ptr, (fptr)->rbuf.len + newlines); + if (read_size < 0) { + rb_sys_fail_path(fptr->pathv); + } + if (read_size == (fptr)->rbuf.len) { + lseek((fptr)->fd, r, SEEK_SET); + break; + } + else { + newlines--; } } (fptr)->rbuf.off = 0; @@ -484,8 +495,6 @@ inline static int set_binary_mode_with_seek_cur(rb_io_t *fptr) { #define shutdown(a,b) 0 #endif -#define rb_sys_fail_path(path) rb_sys_fail(NIL_P(path) ? 0 : RSTRING_PTR(path)) - #if defined(_WIN32) #define is_socket(fd, path) rb_w32_is_socket(fd) #elif !defined(S_ISSOCK) diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index 9adf7c8..b7460bf 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -2314,4 +2314,15 @@ EOT end } end if /mswin|mingw/ =~ RUBY_PLATFORM + + def test_seek_with_setting_binmode + with_tmpdir { + str = "a\r\nb\r\nc\r\n\r\n\n\n\n\n\n\n\n" + generate_file("tmp", str) + open("tmp", "r") do |f| + assert_equal("a\n", f.gets) # text + assert_equal("b\r\n", f.read(3)) # binary + end + } + end if /mswin|mingw/ =~ RUBY_PLATFORM end