diff options
author | usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-12-14 10:38:12 +0000 |
---|---|---|
committer | usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-12-14 10:38:12 +0000 |
commit | 0c6103cf50fd3d7c43ff264c9cc9d62c1f723067 (patch) | |
tree | ed198b94b0166fd7a7d30b745ba5446307d2e94c | |
parent | 4474c83a9d2baca7c263009e722c02d105ae9336 (diff) | |
download | ruby-0c6103cf50fd3d7c43ff264c9cc9d62c1f723067.tar.gz |
* 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]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@34043 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 24 | ||||
-rw-r--r-- | include/ruby/win32.h | 1 | ||||
-rw-r--r-- | io.c | 86 | ||||
-rw-r--r-- | test/ruby/test_io_m17n.rb | 92 | ||||
-rw-r--r-- | win32/win32.c | 8 |
5 files changed, 194 insertions, 17 deletions
@@ -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 *); @@ -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; +} |