diff --git a/io.c b/io.c
index b87b43e1d7..e65620ad3a 100644
--- a/io.c
+++ b/io.c
@@ -7366,67 +7366,130 @@ static VALUE popen_finish(VALUE port, VALUE klass);
/*
* call-seq:
- * IO.popen([env,] cmd, mode="r" [, opt]) -> io
- * IO.popen([env,] cmd, mode="r" [, opt]) {|io| block } -> obj
+ * IO.popen(env = {}, cmd, mode = 'r', **opts) -> io
+ * IO.popen(env = {}, cmd, mode = 'r', **opts) {|io| ... } -> object
*
- * Runs the specified command as a subprocess; the subprocess's
- * standard input and output will be connected to the returned
- * IO object.
+ * Executes the given command +cmd+ as a subprocess
+ * whose $stdin and $stdout are connected to a new stream +io+.
*
- * The PID of the started process can be obtained by IO#pid method.
+ * If no block is given, returns the new stream,
+ * which depending on given +mode+ may be open for reading, writing, or both.
+ * The stream should be explicitly closed (eventually) to avoid resource leaks.
*
- * _cmd_ is a string or an array as follows.
+ * If a block is given, the stream is passed to the block
+ * (again, open for reading, writing, or both);
+ * when the block exits, the stream is closed,
+ * and the block's value is assigned to global variable $? and returned.
*
- * cmd:
- * "-" : fork
- * commandline : command line string which is passed to a shell
- * [env, cmdname, arg1, ..., opts] : command name and zero or more arguments (no shell)
- * [env, [cmdname, argv0], arg1, ..., opts] : command name, argv[0] and zero or more arguments (no shell)
- * (env and opts are optional.)
+ * Optional argument +mode+ may be any valid \IO mode.
+ * See {Modes}[#class-IO-label-Modes].
*
- * If _cmd_ is a +String+ ``-
'',
- * then a new instance of Ruby is started as the subprocess.
+ * Required argument +cmd+ determines which of the following occurs:
*
- * If cmd is an +Array+ of +String+,
- * then it will be used as the subprocess's +argv+ bypassing a shell.
- * The array can contain a hash at first for environments and
- * a hash at last for options similar to #spawn.
+ * - The process forks.
+ * - A specified program runs in a shell.
+ * - A specified program runs with specified arguments.
+ * - A specified program runs with specified arguments and a specified +argv0+.
*
- * The default mode for the new file object is ``r'',
- * but mode may be set to any of the modes listed in the description for class IO.
- * The last argument opt qualifies mode.
+ * Each of these is detailed below.
*
- * # set IO encoding
+ * The optional hash argument +env+ specifies name/value pairs that are to be added
+ * to the environment variables for the subprocess:
+ *
+ * IO.popen({'FOO' => 'bar'}, 'ruby', 'r+') do |pipe|
+ * pipe.puts 'puts ENV["FOO"]'
+ * pipe.close_write
+ * pipe.gets
+ * end => "bar\n"
+ *
+ * The optional keyword arguments +opts+ may be {\IO open options}[#class-IO-label-Open+Options]
+ * and options for Kernel#spawn.
+ *
+ * Forked \Process
+ *
+ * When argument +cmd+ is the 1-character string '-', causes the process to fork:
+ * IO.popen('-') do |pipe|
+ * if pipe
+ * $stderr.puts "In parent, child pid is #{pipe.pid}\n"
+ * else
+ * $stderr.puts "In child, pid is #{$$}\n"
+ * end
+ * end
+ *
+ * Output:
+ *
+ * In parent, child pid is 26253
+ * In child, pid is 26253
+ *
+ * Note that this is not supported on all platforms.
+ *
+ * Shell Subprocess
+ *
+ * When argument +cmd+ is a single string (but not '-'),
+ * the program named +cmd+ is run as a shell command:
+ *
+ * IO.popen('uname') do |pipe|
+ * pipe.readlines
+ * end
+ *
+ * Output:
+ *
+ * ["Linux\n"]
+ *
+ * Another example:
+ *
+ * IO.popen('/bin/sh', 'r+') do |pipe|
+ * pipe.puts('ls')
+ * pipe.close_write
+ * $stderr.puts pipe.readlines.size
+ * end
+ *
+ * Output:
+ *
+ * 213
+ *
+ * Program Subprocess
+ *
+ * When argument +cmd+ is an array of strings,
+ * the program named cmd[0] is run with all elements of +cmd+ as its arguments:
+ *
+ * IO.popen(['du', '..', '.']) do |pipe|
+ * $stderr.puts pipe.readlines.size
+ * end
+ *
+ * Output:
+ *
+ * 1111
+ *
+ * Program Subprocess with argv0
+ *
+ * When argument +cmd+ is an array whose first element is a 2-element string array
+ * and whose remaining elements (if any) are strings:
+ *
+ * - cmd[0][0] (the first string in the nested array) is the name of a program that is run.
+ * - cmd[0][1] (the second string in the nested array) is set as the program's argv[0].
+ * - cmd[1..-1] (the strings in the outer array) are the program's arguments.
+ *
+ * Example (sets $0 to 'foo'):
+ *
+ * IO.popen([['/bin/sh', 'foo'], '-c', 'echo $0']).read # => "foo\n"
+ *
+ * Some Special Examples
+ *
+ * # Set IO encoding.
* IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io|
* euc_jp_string = nkf_io.read
* }
*
- * # merge standard output and standard error using
- * # spawn option. See the document of Kernel.spawn.
- * IO.popen(["ls", "/", :err=>[:child, :out]]) {|ls_io|
- * ls_result_with_error = ls_io.read
- * }
+ * # Merge standard output and standard error using Kernel#spawn option. See Kernel#spawn.
+ * IO.popen(["ls", "/", :err=>[:child, :out]]) do |io|
+ * ls_result_with_error = io.read
+ * end
*
- * # spawn options can be mixed with IO options
- * IO.popen(["ls", "/"], :err=>[:child, :out]) {|ls_io|
- * ls_result_with_error = ls_io.read
- * }
- *
- * Raises exceptions which IO.pipe and Kernel.spawn raise.
- *
- * If a block is given, Ruby will run the command as a child connected
- * to Ruby with a pipe. Ruby's end of the pipe will be passed as a
- * parameter to the block.
- * At the end of block, Ruby closes the pipe and sets $?
.
- * In this case IO.popen returns the value of the block.
- *
- * If a block is given with a _cmd_ of ``-
'',
- * the block will be run in two separate processes: once in the parent,
- * and once in a child. The parent process will be passed the pipe
- * object as a parameter to the block, the child version of the block
- * will be passed +nil+, and the child's standard in and
- * standard out will be connected to the parent through the pipe. Not
- * available on all platforms.
+ * # Use mixture of spawn options and IO options.
+ * IO.popen(["ls", "/"], :err=>[:child, :out]) do |io|
+ * ls_result_with_error = io.read
+ * end
*
* f = IO.popen("uname")
* p f.readlines
@@ -7439,7 +7502,7 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* f.puts "bar"; f.close_write; puts f.gets
* }
*
- * produces:
+ * Output (from last section):
*
* ["Linux\n"]
* Parent is 21346
@@ -7448,6 +7511,9 @@ static VALUE popen_finish(VALUE port, VALUE klass);
* 21352 is here, f is nil
* #
* bar;zot;
+ *
+ * Raises exceptions that IO.pipe and Kernel.spawn raise.
+ *
*/
static VALUE