summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--include/ruby/win32.h1
-rw-r--r--io.c86
-rw-r--r--test/ruby/test_io_m17n.rb92
-rw-r--r--win32/win32.c8
5 files changed, 194 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index 6d60af0e7e..17bb0ade9f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+Wed Dec 14 19:22:33 2011 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#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]
+
Wed Dec 14 15:28:31 2011 Nobuyoshi Nakada <nobu@ruby-lang.org>
* transcode.c (str_encode): about the extension of :fallback
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
index 83da87a45a..d49a9b4f71 100644
--- a/include/ruby/win32.h
+++ b/include/ruby/win32.h
@@ -304,6 +304,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 0713f35fcb..3e184f2995 100644
--- a/io.c
+++ b/io.c
@@ -374,6 +374,8 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
# endif
#endif
+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)
@@ -413,22 +415,59 @@ rb_cloexec_fcntl_dupfd(int fd, int minfd)
* 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;
+ char *p;
+
+ if (!rb_w32_fd_is_text((fptr)->fd)) return O_BINARY;
+
+ if ((fptr)->rbuf.len == 0 || (fptr)->mode & FMODE_DUPLEX) {
+ setmode((fptr)->fd, O_BINARY);
+ return O_TEXT;
+ }
+
+ 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;
+ setmode((fptr)->fd, O_BINARY);
+ return O_TEXT;
+ }
+ /* add extra offset for '\r' */
+ p = (fptr)->rbuf.ptr+(fptr)->rbuf.off;
+ for (i = 0; i < (fptr)->rbuf.len; i++) {
+ if (*p == '\n') newlines++;
+ 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--;
+ }
+ }
+ }
+ (fptr)->rbuf.off = 0;
+ (fptr)->rbuf.len = 0;
+ setmode((fptr)->fd, O_BINARY);
+ return O_TEXT;
+}
+#define SET_BINARY_MODE_WITH_SEEK_CUR(fptr) set_binary_mode_with_seek_cur(fptr)
#else
/* Unix */
@@ -494,7 +533,6 @@ rb_io_check_closed(rb_io_t *fptr)
}
}
-static int io_fflush(rb_io_t *);
VALUE
rb_io_get_io(VALUE io)
@@ -1142,6 +1180,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)));
@@ -2462,6 +2503,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);
@@ -2482,7 +2526,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..b9c88ab647 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -2222,4 +2222,96 @@ 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
+
+ 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
+
+ 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
end
diff --git a/win32/win32.c b/win32/win32.c
index 67a392e863..79df655e43 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -2259,6 +2259,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);
}
@@ -6137,3 +6140,8 @@ rb_w32_inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len)
}
return numaddr;
}
+
+char
+rb_w32_fd_is_text(int fd) {
+ return _osfile(fd) & FTEXT;
+}