summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog47
-rw-r--r--include/ruby/win32.h1
-rw-r--r--io.c96
-rw-r--r--test/ruby/test_io_m17n.rb103
-rw-r--r--version.h2
-rw-r--r--win32/win32.c8
6 files changed, 237 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 2599e1085a..081418eb52 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,50 @@
+Sat Feb 11 03:38:48 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com>
+
+ * io.c (rb_sys_fail_path): move the definition.
+ Move above for using it in set_binary_mode_with_seek_cur().
+
+ * io.c (set_binary_mode_with_seek_cur): fix improper seek cursor.
+ Seeking file cursor with setting binary mode has possibility to
+ cause infinite loop. Fixed the bug and refined error handling.
+ Introduced at r34043.
+
+ And cleanups as below.
+ Remove unnecessary parentheses of `fptr`.
+ Use return value of setmode().
+
+ * test/ruby/test_io_m17n.rb
+ (TestIO_M17N#test_seek_with_setting_binmode): add a test for abobe.
+ [ruby-core:41671] [Bug #5714]
+
+Sat Feb 11 03:38:48 2012 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * test/ruby/test_io_m17n.rb
+ (TestIO_M17N#test_{read_with_binmode_and_get[cs]}): only for Windows.
+
+Sat Feb 11 03:38:48 2012 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c, include/ruby/win32.h (rb_w32_fd_is_text): new function.
+
+ * win32/win32.c (init_stdhandle): set default mode of stdin as binmode.
+
+ * io.c (set_binary_mode_with_seek_cur): new function to replace
+ SET_BINARY_MODE_WITH_SEEK_CUR macro. now returns previous mode of the
+ fd and take care of LF in rbuf.
+
+ * io.c (do_writeconv): set text mode when needed.
+
+ * io.c (io_read): need to change the mode of the IO to binmode
+ temporally when the length for IO#read, because IO#read with length
+ must behave so.
+
+ * test/ruby/test_io_m17n.rb (TestIO_M17N#test_{read_with_length,
+ read_with_length_binmode,get[cs]_and_read_with_binmode,
+ read_with_binmode_and_get[cs],read_write_with_binmode}): tests for
+ above changes.
+
+ all patches are written by Hiroshi Shirosaki. [ruby-core:41496]
+ [Feature #5714]
+
Sat Feb 11 03:37:56 2012 NAKAMURA Usaku <usa@ruby-lang.org>
* test/rexml/test_order.rb (OrderTester#test_more_ordering): use
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index 4d8a66742f..7d3c1d0cef 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -303,6 +303,7 @@ extern int rb_w32_stati64(const char *, struct stati64 *);
extern int rb_w32_ustati64(const char *, struct stati64 *);
extern int rb_w32_access(const char *, int);
extern int rb_w32_uaccess(const char *, int);
+extern char rb_w32_fd_is_text(int);
#ifdef __BORLANDC__
extern int rb_w32_fstati64(int, struct stati64 *);
diff --git a/io.c b/io.c
index aa72d06a41..cbe2bff2c4 100644
--- a/io.c
+++ b/io.c
@@ -219,6 +219,10 @@ rb_update_max_fd(int fd)
# 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)
#define NEED_NEWLINE_DECORATOR_ON_WRITE(fptr) ((fptr)->mode & FMODE_TEXTMODE)
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
@@ -258,22 +262,65 @@ rb_update_max_fd(int fd)
* but stdin and pipe cannot seek back. Stdin and pipe read should use encoding
* conversion for working properly with mode change.
*/
-#define SET_BINARY_MODE_WITH_SEEK_CUR(fptr) do {\
- if ((fptr)->rbuf.len > 0 && !((fptr)->mode & FMODE_DUPLEX)) {\
- off_t r;\
- errno = 0;\
- r = io_seek((fptr), -(fptr)->rbuf.len, SEEK_CUR);\
- if (r < 0 && errno) {\
- if (errno == ESPIPE)\
- (fptr)->mode |= FMODE_DUPLEX;\
- }\
- else {\
- (fptr)->rbuf.off = 0;\
- (fptr)->rbuf.len = 0;\
- }\
- }\
- setmode((fptr)->fd, O_BINARY);\
-} while(0)
+/*
+ * Return previous translation mode.
+ */
+inline static int set_binary_mode_with_seek_cur(rb_io_t *fptr) {
+ off_t r, pos;
+ 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;
+
+ if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) {
+ return setmode(fptr->fd, O_BINARY);
+ }
+
+ if (io_fflush(fptr) < 0) {
+ rb_sys_fail(0);
+ }
+ errno = 0;
+ pos = lseek(fptr->fd, 0, SEEK_CUR);
+ if (pos < 0 && errno) {
+ if (errno == ESPIPE)
+ fptr->mode |= FMODE_DUPLEX;
+ return setmode(fptr->fd, O_BINARY);
+ }
+ /* 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 (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;
+ fptr->rbuf.len = 0;
+ return setmode(fptr->fd, O_BINARY);
+}
+#define SET_BINARY_MODE_WITH_SEEK_CUR(fptr) set_binary_mode_with_seek_cur(fptr)
#else
/* Unix */
@@ -290,8 +337,6 @@ rb_update_max_fd(int fd)
#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)
@@ -339,7 +384,6 @@ rb_io_check_closed(rb_io_t *fptr)
}
}
-static int io_fflush(rb_io_t *);
VALUE
rb_io_get_io(VALUE io)
@@ -987,6 +1031,9 @@ do_writeconv(VALUE str, rb_io_t *fptr)
!(fptr->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
setmode(fptr->fd, O_BINARY);
}
+ else {
+ setmode(fptr->fd, O_TEXT);
+ }
if (!rb_enc_asciicompat(rb_enc_get(str))) {
rb_raise(rb_eArgError, "ASCII incompatible string written for text mode IO without encoding conversion: %s",
rb_enc_name(rb_enc_get(str)));
@@ -2301,6 +2348,9 @@ io_read(int argc, VALUE *argv, VALUE io)
rb_io_t *fptr;
long n, len;
VALUE length, str;
+#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
+ int previous_mode;
+#endif
rb_scan_args(argc, argv, "02", &length, &str);
@@ -2321,7 +2371,15 @@ io_read(int argc, VALUE *argv, VALUE io)
if (len == 0) return str;
READ_CHECK(fptr);
+#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
+ previous_mode = set_binary_mode_with_seek_cur(fptr);
+#endif
n = io_fread(str, 0, fptr);
+#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
+ if (previous_mode == O_TEXT) {
+ setmode(fptr->fd, O_TEXT);
+ }
+#endif
if (n == 0) {
if (fptr->fd < 0) return Qnil;
rb_str_resize(str, 0);
diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb
index 6f823c85a3..b7460bf63e 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -2222,4 +2222,107 @@ EOT
end
end
end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_length
+ with_tmpdir {
+ str = "a\nb"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal(str, f.read(3))
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_length_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ # read with length should be binary mode
+ assert_equal("a\r\n", f.read(3)) # binary
+ assert_equal("b\nc\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_gets_and_read_with_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\n\r\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
+ assert_equal("c\r\n", f.read(3)) # binary
+ assert_equal("\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_getc_and_read_with_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\n\n\r\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\n\n\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_binmode_and_gets
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ open("tmp", "wb") { |f| f.write str }
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\n", f.gets) # text
+ assert_equal("\n", f.gets) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_binmode_and_getc
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ open("tmp", "wb") { |f| f.write str }
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_write_with_binmode
+ with_tmpdir {
+ str = "a\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r+") do |f|
+ assert_equal("a\r\n", f.read(3)) # binary
+ f.write("b\n\n"); # text
+ f.rewind
+ assert_equal("a\nb\n\n", f.read) # text
+ f.rewind
+ assert_equal("a\r\nb\r\n\r\n", f.binmode.read) # binary
+ 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
diff --git a/version.h b/version.h
index 9033e82256..33a48c3a88 100644
--- a/version.h
+++ b/version.h
@@ -1,5 +1,5 @@
#define RUBY_VERSION "1.9.3"
-#define RUBY_PATCHLEVEL 91
+#define RUBY_PATCHLEVEL 92
#define RUBY_RELEASE_DATE "2012-02-11"
#define RUBY_RELEASE_YEAR 2012
diff --git a/win32/win32.c b/win32/win32.c
index 8b7283ba6a..15832a8c0a 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -2135,6 +2135,9 @@ init_stdhandle(void)
if (fileno(stdin) < 0) {
stdin->_file = open_null(0);
}
+ else {
+ setmode(fileno(stdin), O_BINARY);
+ }
if (fileno(stdout) < 0) {
stdout->_file = open_null(1);
}
@@ -5817,3 +5820,8 @@ rb_w32_inet_ntop(int af, void *addr, char *numaddr, size_t numaddr_len)
}
return numaddr;
}
+
+char
+rb_w32_fd_is_text(int fd) {
+ return _osfile(fd) & FTEXT;
+}