diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | io.c | 126 | ||||
-rw-r--r-- | test/ruby/test_io.rb | 57 |
4 files changed, 197 insertions, 1 deletions
@@ -1,3 +1,11 @@ +Thu Jun 2 16:29:34 2011 Shota Fukumori <sorah@tubusu.net> + + * io.c: Add File.write, File.binwrite. [Feature #1081] [ruby-core:21701] + + * test/ruby/test_io.rb: Test for File.write, File.binwrite. + + * NEWS: News for above. + Thu Jun 2 12:33:09 2011 NAKAMURA Usaku <usa@ruby-lang.org> * io.c (io_flush, rb_io_flush): need to fsync() when ruby calls internal @@ -57,8 +57,13 @@ with all sufficient information, see the ChangeLog file. * IO * extended method: * IO#putc supports multibyte characters - * new method: + * new methods: * IO#advise + * IO.write(name, string, [offset] ) + Write `string` to file `name`. + Opposite with File.read. + * IO.binwrite(name, string, [offset] ) + binary virsion of IO.write. * Kernel * Kernel#respond_to? now returns false for protected methods. @@ -808,6 +808,12 @@ struct binwrite_arg { long length; }; +struct write_arg { + VALUE io; + VALUE str; + int nosync; +}; + static VALUE io_binwrite_string(VALUE arg) { @@ -8371,6 +8377,124 @@ rb_io_s_binread(int argc, VALUE *argv, VALUE io) return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io); } +static VALUE +io_s_write0(struct write_arg *arg) +{ + return io_write(arg->io,arg->str,arg->nosync); +} + +static VALUE +io_s_write(int argc, VALUE *argv, int binary) +{ + VALUE string, offset, opt; + struct foreach_arg arg; + struct write_arg warg; + + rb_scan_args(argc, argv, "21:", NULL, &string, &offset, &opt); + + if (NIL_P(opt)) opt = rb_hash_new(); + else opt = rb_hash_dup(opt); + + + if (NIL_P(rb_hash_aref(opt,sym_mode))) { + int mode = O_WRONLY|O_CREAT; +#ifdef O_BINARY + if (binary) mode |= O_BINARY; +#endif + if (NIL_P(offset)) mode |= O_TRUNC; + rb_hash_aset(opt,sym_mode,INT2NUM(mode)); + } + open_key_args(argc,argv,opt,&arg); + +#ifndef O_BINARY + if (binary) rb_io_binmode_m(arg.io); +#endif + + if (NIL_P(arg.io)) return Qnil; + if (!NIL_P(offset)) { + struct seek_arg sarg; + int state = 0; + sarg.io = arg.io; + sarg.offset = offset; + sarg.mode = SEEK_SET; + rb_protect(seek_before_access, (VALUE)&sarg, &state); + if (state) { + rb_io_close(arg.io); + rb_jump_tag(state); + } + } + + warg.io = arg.io; + warg.str = string; + warg.nosync = 0; + + return rb_ensure(io_s_write0, (VALUE)&warg, rb_io_close, arg.io); +} + +/* + * call-seq: + * IO.write(name, string, [offset] ) => fixnum + * IO.write(name, string, [offset], open_args ) => fixnum + * + * Opens the file, optionally seeks to the given <i>offset</i>, writes + * <i>string</i>, then returns the length written. + * <code>write</code> ensures the file is closed before returning. + * If <i>offset</i> is not given, the file is truncated. Otherwise, + * it is not truncated. + * + * If the last argument is a hash, it specifies option for internal + * open(). The key would be the following. open_args: is exclusive + * to others. + * + * encoding: string or encoding + * + * specifies encoding of the read string. encoding will be ignored + * if length is specified. + * + * mode: string + * + * specifies mode argument for open(). it should start with "w" or "a" or "r+" + * otherwise it would cause error. + * + * perm: fixnum + * + * specifies perm argument for open(). + * + * open_args: array of strings + * + * specifies arguments for open() as an array. + * + * IO.write("testfile", "0123456789") #=> "0123456789" + * IO.write("testfile", "0123456789", 20) #=> "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n" + */ + +static VALUE +rb_io_s_write(int argc, VALUE *argv, VALUE io) +{ + io_s_write(argc, argv, 0); +} + +/* + * call-seq: + * IO.binwrite(name, string, [offset] ) => fixnum + * + * Opens the file, optionally seeks to the given <i>offset</i>, write + * <i>string</i> then returns the length written. + * <code>binwrite</code> ensures the file is closed before returning. + * The open mode would be "wb:ASCII-8BIT". + * If <i>offset</i> is not given, the file is truncated. Otherwise, + * it is not truncated. + * + * IO.binwrite("testfile", "0123456789") #=> "0123456789" + * IO.binwrite("testfile", "0123456789", 20) #=> "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n" + */ + +static VALUE +rb_io_s_binwrite(int argc, VALUE *argv, VALUE io) +{ + io_s_write(argc, argv, 1); +} + struct copy_stream_struct { VALUE src; VALUE dst; @@ -10320,6 +10444,8 @@ Init_IO(void) rb_define_singleton_method(rb_cIO, "readlines", rb_io_s_readlines, -1); rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1); rb_define_singleton_method(rb_cIO, "binread", rb_io_s_binread, -1); + rb_define_singleton_method(rb_cIO, "write", rb_io_s_write, -1); + rb_define_singleton_method(rb_cIO, "binwrite", rb_io_s_binwrite, -1); rb_define_singleton_method(rb_cIO, "select", rb_f_select, -1); rb_define_singleton_method(rb_cIO, "pipe", rb_io_s_pipe, -1); rb_define_singleton_method(rb_cIO, "try_convert", rb_io_s_try_convert, 1); diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 73cef715e6..c4b4d3850a 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1865,4 +1865,61 @@ End end end + def test_s_write + t = Tempfile.new("foo") + path = t.path + t.close(false) + File.write(path, "foo\nbar\nbaz") + assert_equal("foo\nbar\nbaz", File.read(path)) + File.write(path, "FOO", 0) + assert_equal("FOO\nbar\nbaz", File.read(path)) + File.write(path, "BAR") + assert_equal("BAR", File.read(path)) + File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP") + assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP")) + File.delete t + assert_equal(6, File.write(path,'string',2)) + File.delete t + assert_raise(Errno::EINVAL) { File.write('/tmp/nonexisting','string',-2) } + assert_equal(6, File.write(path, 'string')) + assert_equal(3, File.write(path, 'sub', 1)) + assert_equal("ssubng", File.read(path)) + File.delete t + assert_equal(3, File.write(path, "foo", encoding: "UTF-8")) + File.delete t + assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8")) + assert_equal("foo", File.read(path)) + assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8")) + assert_equal("ffo", File.read(path)) + File.delete t + assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8")) + assert_equal("\00f", File.read(path)) + assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8")) + assert_equal("ff", File.read(path)) + t.unlink + end + + def test_s_binwrite + t = Tempfile.new("foo") + path = t.path + t.close(false) + File.binwrite(path, "foo\nbar\nbaz") + assert_equal("foo\nbar\nbaz", File.read(path)) + File.binwrite(path, "FOO", 0) + assert_equal("FOO\nbar\nbaz", File.read(path)) + File.binwrite(path, "BAR") + assert_equal("BAR", File.read(path)) + File.binwrite(path, "\u{3042}") + assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path)) + File.delete t + assert_equal(6, File.binwrite(path,'string',2)) + File.delete t + assert_equal(6, File.binwrite(path, 'string')) + assert_equal(3, File.binwrite(path, 'sub', 1)) + assert_equal("ssubng", File.binread(path)) + assert_equal(6, File.size(path)) + assert_raise(Errno::EINVAL) { File.binwrite('/tmp/nonexisting','string',-2) } + assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") } + t.unlink + end end |