summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-02-10 18:39:05 +0000
committernaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-02-10 18:39:05 +0000
commita208a7507244c3819beff06ddf422066437ed5b5 (patch)
tree7007ac87dab416d2687a7b27422e91884c74e434
parent877469197274d5ee7559e154cfac21dbdcec3854 (diff)
merge revision(s) 34043,34045,34132: [Backport #5791]
* 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#est_{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] * test/ruby/test_io_m17n.rb (TestIO_M17N#test_{read_with_binmode_and_get[cs]}): only for Windows. * test/ruby/test_io_m17n.rb (TestIO_M17N#test_{read_with_length, * 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] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_9_3@34546 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-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;
+}