diff options
| -rw-r--r-- | mrblib/io.rb | 4 | ||||
| -rw-r--r-- | src/io.c | 33 | ||||
| -rw-r--r-- | test/io.rb | 38 |
3 files changed, 73 insertions, 2 deletions
diff --git a/mrblib/io.rb b/mrblib/io.rb index e688a32fe..4bcf4f800 100644 --- a/mrblib/io.rb +++ b/mrblib/io.rb @@ -26,11 +26,11 @@ class IO end end - def self.popen(command, mode = 'r', &block) + def self.popen(command, mode = 'r', opts={}, &block) if !self.respond_to?(:_popen) raise NotImplementedError, "popen is not supported on this platform" end - io = self._popen(command, mode) + io = self._popen(command, mode, opts) return io unless block begin @@ -264,6 +264,26 @@ mrb_io_alloc(mrb_state *mrb) #endif #ifndef _WIN32 +static int +option_to_fd(mrb_state *mrb, mrb_value obj, const char *key) +{ + mrb_value opt = mrb_funcall(mrb, obj, "[]", 1, mrb_symbol_value(mrb_intern_static(mrb, key, strlen(key)))); + if (mrb_nil_p(opt)) { + return -1; + } + + switch (mrb_type(opt)) { + case MRB_TT_DATA: /* IO */ + return mrb_fixnum(mrb_io_fileno(mrb, opt)); + case MRB_TT_FIXNUM: + return mrb_fixnum(opt); + default: + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action"); + break; + } + return -1; /* never reached */ +} + mrb_value mrb_io_s_popen(mrb_state *mrb, mrb_value klass) { @@ -278,6 +298,7 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) int pw[2] = { -1, -1 }; int doexec; int saved_errno; + int opt_in, opt_out, opt_err; mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt); io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type)); @@ -286,6 +307,9 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode)); doexec = (strcmp("-", pname) != 0); + opt_in = option_to_fd(mrb, opt, "in"); + opt_out = option_to_fd(mrb, opt, "out"); + opt_err = option_to_fd(mrb, opt, "err"); if (flags & FMODE_READABLE) { if (pipe(pr) == -1) { @@ -315,6 +339,15 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass) result = mrb_nil_value(); switch (pid = fork()) { case 0: /* child */ + if (opt_in != -1) { + dup2(opt_in, 0); + } + if (opt_out != -1) { + dup2(opt_out, 1); + } + if (opt_err != -1) { + dup2(opt_err, 2); + } if (flags & FMODE_READABLE) { close(pr[0]); if (pr[1] != 1) { diff --git a/test/io.rb b/test/io.rb index f0f30e3ce..1b0a2d52e 100644 --- a/test/io.rb +++ b/test/io.rb @@ -393,6 +393,44 @@ assert('IO.popen') do end end +assert('IO.popen with in option') do + begin + IO.pipe do |r, w| + w.write 'hello' + w.close + assert_equal "hello", IO.popen("cat", "r", in: r) { |i| i.read } + assert_equal "", r.read + end + assert_raise(ArgumentError) { IO.popen("hello", "r", in: Object.new) } + rescue NotImplementedError => e + skip e.message + end +end + +assert('IO.popen with out option') do + begin + IO.pipe do |r, w| + IO.popen("echo 'hello'", "w", out: w) {} + w.close + assert_equal "hello\n", r.read + end + rescue NotImplementedError => e + skip e.message + end +end + +assert('IO.popen with err option') do + begin + IO.pipe do |r, w| + assert_equal "", IO.popen("echo 'hello' 1>&2", "r", err: w) { |i| i.read } + w.close + assert_equal "hello\n", r.read + end + rescue NotImplementedError => e + skip e.message + end +end + assert('IO.read') do # empty file fd = IO.sysopen $mrbtest_io_wfname, "w" |
