summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/io.c40
-rw-r--r--test/io.rb1
2 files changed, 31 insertions, 10 deletions
diff --git a/src/io.c b/src/io.c
index f842ceb85..264ded8ad 100644
--- a/src/io.c
+++ b/src/io.c
@@ -264,13 +264,32 @@ 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)
{
mrb_value cmd, io, result;
mrb_value mode = mrb_str_new_cstr(mrb, "r");
mrb_value opt = mrb_hash_new(mrb);
- mrb_value opt_in, opt_out, opt_err;
struct mrb_io *fptr;
const char *pname;
@@ -279,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));
@@ -287,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) {
@@ -316,17 +339,14 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
result = mrb_nil_value();
switch (pid = fork()) {
case 0: /* child */
- opt_in = mrb_funcall(mrb, opt, "[]", 1, mrb_symbol_value(mrb_intern_lit(mrb, "in")));
- opt_out = mrb_funcall(mrb, opt, "[]", 1, mrb_symbol_value(mrb_intern_lit(mrb, "out")));
- opt_err = mrb_funcall(mrb, opt, "[]", 1, mrb_symbol_value(mrb_intern_lit(mrb, "err")));
- if (!mrb_nil_p(opt_in)) {
- dup2(mrb_fixnum(mrb_io_fileno(mrb, opt_in)), 0);
+ if (opt_in != -1) {
+ dup2(opt_in, 0);
}
- if (!mrb_nil_p(opt_out)) {
- dup2(mrb_fixnum(mrb_io_fileno(mrb, opt_out)), 1);
+ if (opt_out != -1) {
+ dup2(opt_out, 1);
}
- if (!mrb_nil_p(opt_err)) {
- dup2(mrb_fixnum(mrb_io_fileno(mrb, opt_err)), 2);
+ if (opt_err != -1) {
+ dup2(opt_err, 2);
}
if (flags & FMODE_READABLE) {
close(pr[0]);
diff --git a/test/io.rb b/test/io.rb
index 6e1bcb247..1b0a2d52e 100644
--- a/test/io.rb
+++ b/test/io.rb
@@ -401,6 +401,7 @@ assert('IO.popen with in option') do
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