diff --git a/ChangeLog b/ChangeLog index 93f90ddca3..d72b6d2aa4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +Tue Oct 28 21:11:58 2008 NAKAMURA Usaku + + * io.c (extract_binmode): new function to extract binmode/textmode + options from hash. + + * io.c (rb_io_extract_modeenc): use above function. + + * io.c (rb_io_s_pipe): recognize binmode/textmode options. + + * io.c (make_readconv): now can specify the size of cbuf. + + * io.c (read_all, appendline, io_getc, rb_io_ungetc): follow above + change. + + * win32/win32.c (rb_w32_pipe_exec): internal fds should be always + binmode. + Tue Oct 28 17:22:35 2008 Yuki Sonoda (Yugui) * tool/make-snapshot.rb: merged back from trunk. diff --git a/io.c b/io.c index 6e76f23c8b..772d9e4d71 100644 --- a/io.c +++ b/io.c @@ -1430,7 +1430,7 @@ io_enc_str(VALUE str, rb_io_t *fptr) } static void -make_readconv(rb_io_t *fptr) +make_readconv(rb_io_t *fptr, int size) { if (!fptr->readconv) { int ecflags; @@ -1452,7 +1452,7 @@ make_readconv(rb_io_t *fptr) rb_exc_raise(rb_econv_open_exc(sname, dname, ecflags)); fptr->cbuf_off = 0; fptr->cbuf_len = 0; - fptr->cbuf_capa = 1024; + fptr->cbuf_capa = size < 1024 ? 1024 : size; fptr->cbuf = ALLOC_N(char, fptr->cbuf_capa); } } @@ -1558,7 +1558,7 @@ read_all(rb_io_t *fptr, long siz, VALUE str) if (NEED_READCONV(fptr)) { if (NIL_P(str)) str = rb_str_new(NULL, 0); else rb_str_set_len(str, 0); - make_readconv(fptr); + make_readconv(fptr, 0); while (1) { if (fptr->cbuf_len) { io_shift_cbuf(fptr, fptr->cbuf_len, &str); @@ -1940,7 +1940,7 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp) long limit = *lp; if (NEED_READCONV(fptr)) { - make_readconv(fptr); + make_readconv(fptr, 0); while (1) { const char *p, *e; int searchlen; @@ -2473,7 +2473,7 @@ io_getc(rb_io_t *fptr, rb_encoding *enc) if (NEED_READCONV(fptr)) { VALUE str = Qnil; - make_readconv(fptr); + make_readconv(fptr, 0); while (1) { if (fptr->cbuf_len) { @@ -2825,8 +2825,8 @@ rb_io_ungetc(VALUE io, VALUE c) SafeStringValue(c); } if (NEED_READCONV(fptr)) { - make_readconv(fptr); len = RSTRING_LEN(c); + make_readconv(fptr, len); if (fptr->cbuf_capa - fptr->cbuf_len < len) rb_raise(rb_eIOError, "ungetc failed"); if (fptr->cbuf_off < len) { @@ -3922,6 +3922,23 @@ validate_enc_binmode(int fmode, rb_encoding *enc, rb_encoding *enc2) rb_raise(rb_eArgError, "ASCII incompatible encoding needs binmode"); } +static void +extract_binmode(VALUE opthash, int *fmode) +{ + if (!NIL_P(opthash)) { + VALUE v; + v = rb_hash_aref(opthash, sym_textmode); + if (!NIL_P(v) && RTEST(v)) + *fmode |= FMODE_TEXTMODE; + v = rb_hash_aref(opthash, sym_binmode); + if (!NIL_P(v) && RTEST(v)) + *fmode |= FMODE_BINMODE; + + if ((*fmode & FMODE_BINMODE) && (*fmode & FMODE_TEXTMODE)) + rb_raise(rb_eArgError, "both textmode and binmode specified"); + } +} + static void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, int *fmode_p, convconfig_t *convconfig_p) @@ -3975,16 +3992,11 @@ rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, } else { VALUE v; - v = rb_hash_aref(opthash, sym_textmode); - if (!NIL_P(v) && RTEST(v)) - fmode |= FMODE_TEXTMODE; - v = rb_hash_aref(opthash, sym_binmode); - if (!NIL_P(v) && RTEST(v)) { - fmode |= FMODE_BINMODE; + extract_binmode(opthash, &fmode); #ifdef O_BINARY + if (fmode & FMODE_BINMODE) oflags |= O_BINARY; #endif - } if (!has_vmode) { v = rb_hash_aref(opthash, sym_mode); if (!NIL_P(v)) { @@ -4017,9 +4029,6 @@ rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, } } - if ((fmode & FMODE_BINMODE) && (fmode & FMODE_TEXTMODE)) - rb_raise(rb_eArgError, "both textmode and binmode specified"); - validate_enc_binmode(fmode, enc, enc2); *vmode_p = vmode; @@ -6825,7 +6834,8 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass) int pipes[2], state; VALUE r, w, args[3], v1, v2; VALUE opt; - rb_io_t *fptr; + rb_io_t *fptr, *fptr2; + int fmode = 0; opt = pop_last_hash(&argc, argv); rb_scan_args(argc, argv, "02", &v1, &v2); @@ -6851,7 +6861,12 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass) if (!NIL_P(r)) rb_io_close(r); rb_jump_tag(state); } - rb_io_synchronized(RFILE(w)->fptr); + GetOpenFile(w, fptr2); + rb_io_synchronized(fptr2); + + extract_binmode(opt, &fmode); + fptr->mode |= fmode; + fptr2->mode |= fmode; return rb_assoc_new(r, w); } diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index f6fcf89a14..0732c76a82 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -65,40 +65,70 @@ class TestFile < Test::Unit::TestCase end def test_read_all_extended_file - f = Tempfile.new("test-extended-file") - assert_nil(f.getc) - open(f.path, "w") {|g| g.print "a" } - assert_equal("a", f.read) + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "w") {|g| g.print "a" } + assert_equal("a", f.read, "mode = <#{mode}>") + end end def test_gets_extended_file - f = Tempfile.new("test-extended-file") - assert_nil(f.getc) - open(f.path, "w") {|g| g.print "a" } - assert_equal("a", f.gets("a")) + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "w") {|g| g.print "a" } + assert_equal("a", f.gets("a"), "mode = <#{mode}>") + end end def test_gets_para_extended_file - f = Tempfile.new("test-extended-file") - assert_nil(f.getc) - open(f.path, "w") {|g| g.print "\na" } - assert_equal("a", f.gets("")) + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "wb") {|g| g.print "\na" } + assert_equal("a", f.gets(""), "mode = <#{mode}>") + end + end + + def test_each_char_extended_file + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "w") {|g| g.print "a" } + result = [] + f.each_char {|b| result << b } + assert_equal([?a], result, "mode = <#{mode}>") + end end def test_each_byte_extended_file - f = Tempfile.new("test-extended-file") - assert_nil(f.getc) - open(f.path, "w") {|g| g.print "a" } - result = [] - f.each_byte {|b| result << b.chr } - assert_equal([?a], result) + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "w") {|g| g.print "a" } + result = [] + f.each_byte {|b| result << b.chr } + assert_equal([?a], result, "mode = <#{mode}>") + end end def test_getc_extended_file - f = Tempfile.new("test-extended-file") - assert_nil(f.getc) - open(f.path, "w") {|g| g.print "a" } - assert_equal(?a, f.getc) + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "w") {|g| g.print "a" } + assert_equal(?a, f.getc, "mode = <#{mode}>") + end + end + + def test_getbyte_extended_file + [nil, {:textmode=>true}, {:binmode=>true}].each do |mode| + f = Tempfile.new("test-extended-file", mode) + assert_nil(f.getc) + open(f.path, "w") {|g| g.print "a" } + assert_equal(?a, f.getbyte.chr, "mode = <#{mode}>") + end end def test_s_chown diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 4305293a3d..34a8d017c0 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1,5 +1,6 @@ require 'test/unit' require 'tmpdir' +require "fcntl" require 'io/nonblock' require 'socket' require 'stringio' @@ -8,6 +9,19 @@ require 'tempfile' require_relative 'envutil' class TestIO < Test::Unit::TestCase + def have_close_on_exec? + begin + $stdin.close_on_exec? + true + rescue NotImplementedError + false + end + end + + def have_nonblock? + IO.instance_methods.index(:"nonblock=") + end + def test_gets_rs # default_rs r, w = IO.pipe @@ -236,18 +250,20 @@ class TestIO < Test::Unit::TestCase assert_equal(content[1,1], r.read) } - with_read_pipe("abc") {|r1| - assert_equal("a", r1.getc) - with_pipe {|r2, w2| - w2.nonblock = true - s = w2.syswrite("a" * 100000) - t = Thread.new { sleep 0.1; r2.read } - ret = IO.copy_stream(r1, w2) - w2.close - assert_equal(2, ret) - assert_equal("a" * s + "bc", t.value) + if have_nonblock? + with_read_pipe("abc") {|r1| + assert_equal("a", r1.getc) + with_pipe {|r2, w2| + w2.nonblock = true + s = w2.syswrite("a" * 100000) + t = Thread.new { sleep 0.1; r2.read } + ret = IO.copy_stream(r1, w2) + w2.close + assert_equal(2, ret) + assert_equal("a" * s + "bc", t.value) + } } - } + end bigcontent = "abc" * 123456 File.open("bigsrc", "w") {|f| f << bigcontent } @@ -266,15 +282,19 @@ class TestIO < Test::Unit::TestCase assert_equal(bigcontent[100, 30000], File.read("bigdst")) File.open("bigsrc") {|f| - assert_equal(0, f.pos) - ret = IO.copy_stream(f, "bigdst", nil, 10) - assert_equal(bigcontent.bytesize-10, ret) - assert_equal(bigcontent[10..-1], File.read("bigdst")) - assert_equal(0, f.pos) - ret = IO.copy_stream(f, "bigdst", 40, 30) - assert_equal(40, ret) - assert_equal(bigcontent[30, 40], File.read("bigdst")) - assert_equal(0, f.pos) + begin + assert_equal(0, f.pos) + ret = IO.copy_stream(f, "bigdst", nil, 10) + assert_equal(bigcontent.bytesize-10, ret) + assert_equal(bigcontent[10..-1], File.read("bigdst")) + assert_equal(0, f.pos) + ret = IO.copy_stream(f, "bigdst", 40, 30) + assert_equal(40, ret) + assert_equal(bigcontent[30, 40], File.read("bigdst")) + assert_equal(0, f.pos) + rescue NotImplementedError + #skip "pread(2) is not implemtented." + end } with_pipe {|r, w| @@ -285,19 +305,21 @@ class TestIO < Test::Unit::TestCase megacontent = "abc" * 1234567 File.open("megasrc", "w") {|f| f << megacontent } - with_pipe {|r1, w1| - with_pipe {|r2, w2| - t1 = Thread.new { w1 << megacontent; w1.close } - t2 = Thread.new { r2.read } - r1.nonblock = true - w2.nonblock = true - ret = IO.copy_stream(r1, w2) - assert_equal(megacontent.bytesize, ret) - w2.close - t1.join - assert_equal(megacontent, t2.value) + if have_nonblock? + with_pipe {|r1, w1| + with_pipe {|r2, w2| + t1 = Thread.new { w1 << megacontent; w1.close } + t2 = Thread.new { r2.read } + r1.nonblock = true + w2.nonblock = true + ret = IO.copy_stream(r1, w2) + assert_equal(megacontent.bytesize, ret) + w2.close + t1.join + assert_equal(megacontent, t2.value) + } } - } + end with_pipe {|r1, w1| with_pipe {|r2, w2| @@ -323,15 +345,19 @@ class TestIO < Test::Unit::TestCase def test_copy_stream_rbuf mkcdtmpdir { - with_pipe {|r, w| - File.open("foo", "w") {|f| f << "abcd" } - File.open("foo") {|f| - f.read(1) - assert_equal(3, IO.copy_stream(f, w, 10, 1)) + begin + with_pipe {|r, w| + File.open("foo", "w") {|f| f << "abcd" } + File.open("foo") {|f| + f.read(1) + assert_equal(3, IO.copy_stream(f, w, 10, 1)) + } + w.close + assert_equal("bcd", r.read) } - w.close - assert_equal("bcd", r.read) - } + rescue NotImplementedError + skip "pread(2) is not implemtented." + end } end @@ -410,15 +436,17 @@ class TestIO < Test::Unit::TestCase megacontent = "abc" * 1234567 File.open("megasrc", "w") {|f| f << megacontent } - with_socketpair {|s1, s2| - t = Thread.new { s2.read } - s1.nonblock = true - ret = IO.copy_stream("megasrc", s1) - assert_equal(megacontent.bytesize, ret) - s1.close - result = t.value - assert_equal(megacontent, result) - } + if have_nonblock? + with_socketpair {|s1, s2| + t = Thread.new { s2.read } + s1.nonblock = true + ret = IO.copy_stream("megasrc", s1) + assert_equal(megacontent.bytesize, ret) + s1.close + result = t.value + assert_equal(megacontent, result) + } + end } end @@ -695,6 +723,7 @@ class TestIO < Test::Unit::TestCase end def test_write_nonblock + skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM pipe(proc do |w| w.write_nonblock(1) w.close @@ -874,6 +903,7 @@ class TestIO < Test::Unit::TestCase def test_bytes pipe(proc do |w| + w.binmode w.puts "foo" w.puts "bar" w.puts "baz" @@ -904,11 +934,13 @@ class TestIO < Test::Unit::TestCase def test_readbyte pipe(proc do |w| + w.binmode w.puts "foo" w.puts "bar" w.puts "baz" w.close end, proc do |r| + r.binmode (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c| assert_equal(c.ord, r.readbyte) end @@ -931,7 +963,7 @@ class TestIO < Test::Unit::TestCase end def test_close_on_exec - # xxx + skip "IO\#close_on_exec is not implemented." unless have_close_on_exec? ruby do |f| assert_equal(false, f.close_on_exec?) f.close_on_exec = true @@ -1022,7 +1054,11 @@ class TestIO < Test::Unit::TestCase fd = IO.sysopen(t.path, "w", 0666) assert_kind_of(Integer, fd) - f = IO.for_fd(fd) + if defined?(Fcntl::F_GETFL) + f = IO.for_fd(fd) + else + f = IO.for_fd(fd, 0666) + end f.write("FOO\n") f.close diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index 8b7c3363d1..71299181ec 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -677,7 +677,7 @@ EOT end def test_getc_invalid3 - with_pipe("utf-16le:euc-jp") {|r, w| + with_pipe("utf-16le:euc-jp", binmode: true) {|r, w| before1 = "\x42\x30".force_encoding("utf-16le") before2 = "\x44\x30".force_encoding("utf-16le") invalid = "\x00\xd8".force_encoding("utf-16le") diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index a5f453a055..4048ba038d 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -27,11 +27,15 @@ class TestRequire < Test::Unit::TestCase end INPUT - assert_in_out_err(["-S", "foo/" * 10000 + "foo"], "") do |r, e| - assert_equal([], r) - assert_operator(2, :<=, e.size) - assert_equal("openpath: pathname too long (ignored)", e.first) - assert_match(/\(LoadError\)/, e.last) + begin + assert_in_out_err(["-S", "foo/" * 10000 + "foo"], "") do |r, e| + assert_equal([], r) + assert_operator(2, :<=, e.size) + assert_equal("openpath: pathname too long (ignored)", e.first) + assert_match(/\(LoadError\)/, e.last) + end + rescue Errno::EINVAL + # too long commandline may be blocked by OS. end end diff --git a/win32/win32.c b/win32/win32.c index c3a5c8c5dd..13642afa27 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -764,9 +764,8 @@ rb_w32_pipe_exec(const char *cmd, const char *prog, int mode, int *pipe, reading = TRUE; writing = FALSE; } - mode &= ~(O_RDWR|O_RDONLY|O_WRONLY); - if (!(mode & O_BINARY)) - mode |= O_TEXT; + mode &= ~(O_RDWR|O_RDONLY|O_WRONLY|O_TEXT); + mode |= O_BINARY; sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL;