summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--NEWS7
-rw-r--r--io.c126
-rw-r--r--test/ruby/test_io.rb57
4 files changed, 197 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index dbecb72df2..400516e582 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/NEWS b/NEWS
index fe63757539..73e8e909db 100644
--- a/NEWS
+++ b/NEWS
@@ -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.
diff --git a/io.c b/io.c
index e8f44dcd43..51f839cebd 100644
--- a/io.c
+++ b/io.c
@@ -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