Use two pipes for duplex IO.popen

e$B2~?4$7$Fe(B duplex e$B$Je(B IO.popen e$B$Ge(B socketpair
e$B$r;H$&$N$O$d$a$h$&e(B
e$B$H;W$&$N$G$9$,!"$I$&$G$7$g$&$+!#e(B

e$B$H$$$&$N$O!"5/F0$7$?%W%m%0%i%$,I8==PNO$r%/%m!<%:$7$?$3$H$re(B
e$B?F$,8!CN$G$-$J$$$+$i$G$9!#e(B

% cat close-stdout.c
#include <unistd.h>

int main(int argc, char **argv)
{
close(1);
sleep(2);
return 0;
}
% gcc -o close-stdout close-stdout.c

e$B$H$$$&$h$&$J!“I8==PNO$rB(:B$K%/%m!<%:$7!"$=$N8e$Ge(B 2e$BIC?2$k!"e(B e$B$H$$$&%W%m%0%i%$,$”$C$?$H$7$^$9!#e(B

e$B$3$l$Oe(B 1.8 e$B$G$O<!$N$h$&$KB(:B$K=*$o$j$^$9!#e(B

% time ruby-1.8 -ve ‘p IO.popen("./close-stdout", “r+”).read’
ruby 1.8.6 (2007-11-09 patchlevel 5000) [i686-linux]
“”
ruby-1.8 -ve ‘p IO.popen("./close-stdout", “r+”).read’ 0.00s user 0.00s
system 71% cpu 0.006 total

e$B$7$+$7e(B 1.9 e$B$G$Oe(B 2e$BIC$+$+$j$^$9!#e(B

% time ./ruby -ve ‘p IO.popen("./close-stdout", “r+”).read’
ruby 1.9.0 (2007-11-10 patchlevel 0) [i686-linux]
“”
./ruby -ve ‘p IO.popen("./close-stdout", “r+”).read’ 0.01s user 0.00s
system 0% cpu 2.020 total

e$B$3$l$Oe(B socketpair e$B$Ge(B EOF e$B$rEA$($k$?$a$K$Oe(B half close
e$B$7$J$$e(B
e$B$H$$$1$J$/$F!"$=$N$?$a$K$Oe(B shutdown e$B$9$kI,MW$,$"$k$1$l$I5/F0e(B
e$B$5$l$?B&$N%W%m%0%i%`$O$=$s$J$3$H$OCN$C$?$3$H$G$OL5$$!"$H$$$&e(B
e$B$N$,LdBj$G$9!#e(B

e$B$3$l$Oe(B socketpair e$B$r;H$&8B$j$O2r7h$G$-$J$$$N$G!"e(B1.8
e$B$N$h$&$Ke(B
e$B%Q%$%W$re(B 2e$BK;H$C$F!“IaDL$Ke(B close
e$B$9$l$P?F$,$=$l$r8!CN$G$-$ke(B
e$B$N$,E,@Z$G$”$k$H$$$&7kO@$K;j$j$^$7$?!#e(B

e$B$J$*!“e(BIO e$B$R$H$D$Ke(B 2e$B$D$Ne(B fd e$B$,F~$C$F$$$Fe(B fcntl
e$BEy$Ne(B fd e$B$NA:ne(B e$B$K;Y>c$rMh$9$H$$$&LdBj$O!"=q$-9~$_B&$Ne(B fd e$B$K$bFHN)$7$?e(B IO e$B%*e(B e$B%V%8%'%/%H$r:n$j!"$=$l$re(B @write_io e$B$H$$$&%$%s%9%?%s%9JQ?t$Ge(B e$B;2>H2DG=$K$9$k$3$H$K$h$j!"A:nIT2DG=$G$O$J$$$h$&$K$7$F$”$j$^e(B
e$B$9!#e(B

Index: include/ruby/intern.h

— include/ruby/intern.h (e$B%j%S%8%g%se(B 13873)
+++ include/ruby/intern.h (e$B:n6H%3%T!<e(B)
@@ -353,6 +353,7 @@
VALUE rb_io_print(int, VALUE*, VALUE);
VALUE rb_io_puts(int, VALUE*, VALUE);
VALUE rb_io_fdopen(int, int, const char*);
+VALUE rb_io_get_write_io(VALUE);
VALUE rb_file_open(const char*, const char*);
VALUE rb_gets(void);
void rb_write_error(const char*);
Index: include/ruby/io.h

— include/ruby/io.h (e$B%j%S%8%g%se(B 13873)
+++ include/ruby/io.h (e$B:n6H%3%T!<e(B)
@@ -45,6 +45,7 @@
int rbuf_off;
int rbuf_len;
int rbuf_capa;

  • int has_write_io;
    rb_encoding *enc;
    } rb_io_t;

@@ -88,8 +89,12 @@
fp->rbuf_off = 0;
fp->rbuf_len = 0;
fp->rbuf_capa = 0;\

  • fp->has_write_io = 0;
    } while (0)

+#define GetReadIO(io) (io)
+#define GetWriteIO(io) rb_io_get_write_io(io)
+
FILE *rb_io_stdio_file(rb_io_t *fptr);

FILE rb_fopen(const char, const char*);
Index: io.c

— io.c (e$B%j%S%8%g%se(B 13873)
+++ io.c (e$B:n6H%3%T!<e(B)
@@ -246,6 +246,21 @@
return rb_check_convert_type(io, T_FILE, “IO”, “to_io”);
}

+VALUE
+rb_io_get_write_io(VALUE io)
+{

  • VALUE write_io;
  • if (RFILE(io)->fptr->has_write_io) {
  •    write_io = rb_ivar_get(io, rb_intern("@write_io"));
    
  •    if (RTEST(write_io)) {
    
  •        write_io = rb_io_get_io(write_io);
    
  •        if (!RFILE(write_io)->fptr->has_write_io)
    
  •            return write_io;
    
  •    }
    
  • }
  • return io;
    +}

/*

  • call-seq:
  • IO.try_convert(obj) -> io or nil
    

@@ -676,6 +691,7 @@
VALUE tmp;

 rb_secure(4);
  • io = GetWriteIO(io);
    str = rb_obj_as_string(str);
    tmp = rb_io_check_io(io);
    if (NIL_P(tmp)) {
    @@ -748,6 +764,7 @@
    return rb_funcall(io, id_flush, 0);
    }

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    if (fptr->mode & FMODE_WRITABLE) {
    @@ -982,6 +999,7 @@
    {
    rb_io_t *fptr;

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    return (fptr->mode & FMODE_SYNC) ? Qtrue : Qfalse;
    }
    @@ -1006,6 +1024,7 @@
    {
    rb_io_t *fptr;

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    if (RTEST(mode)) {
    fptr->mode |= FMODE_SYNC;
    @@ -1034,6 +1053,7 @@
    #ifdef HAVE_FSYNC
    rb_io_t *fptr;

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    io_fflush(fptr);
    @@ -1464,6 +1484,7 @@
    if (TYPE(str) != T_STRING)
    str = rb_obj_as_string(str);

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);

@@ -2348,7 +2369,17 @@
{
rb_io_t *fptr;
int fd;

  • VALUE write_io;

  • rb_io_t *write_fptr;

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_fptr = RFILE(write_io)->fptr;
    
  •    if (write_fptr && 0 <= write_fptr->fd) {
    
  •        rb_io_fptr_cleanup(write_fptr, Qtrue);
    
  •    }
    
  • }

  • fptr = RFILE(io)->fptr;
    if (!fptr) return Qnil;
    if (fptr->fd < 0) return Qnil;
    @@ -2425,7 +2456,17 @@
    rb_io_closed(VALUE io)
    {
    rb_io_t *fptr;

  • VALUE write_io;

  • rb_io_t *write_fptr;

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_fptr = RFILE(write_io)->fptr;
    
  •    if (write_fptr && 0 <= write_fptr->fd) {
    
  •        return Qfalse;
    
  •    }
    
  • }

  • fptr = RFILE(io)->fptr;
    rb_io_check_initialized(fptr);
    return 0 <= fptr->fd ? Qfalse : Qtrue;
    @@ -2453,6 +2494,7 @@
    rb_io_close_read(VALUE io)
    {
    rb_io_t *fptr;

  • VALUE write_io;

    if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
    rb_raise(rb_eSecurityError, “Insecure: can’t close”);
    @@ -2469,6 +2511,13 @@
    return rb_io_close(io);
    return Qnil;
    }

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    fptr_finalize(fptr, Qfalse);
    
  •    return Qnil;
    
  • }

  • if (fptr->mode & FMODE_WRITABLE) {
    rb_raise(rb_eIOError, “closing non-duplex IO for reading”);
    }
    @@ -2498,6 +2547,8 @@
    rb_io_close_write(VALUE io)
    {
    rb_io_t *fptr;

  • VALUE write_io;

  • rb_io_t *write_fptr;

    if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
    rb_raise(rb_eSecurityError, “Insecure: can’t close”);
    @@ -2515,6 +2566,13 @@
    return Qnil;
    }

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_fptr = RFILE(write_io)->fptr;
    
  •    fptr_finalize(write_fptr, Qfalse);
    
  •    return Qnil;
    
  • }

  • if (fptr->mode & FMODE_READABLE) {
    rb_raise(rb_eIOError, “closing non-duplex IO for writing”);
    }
    @@ -2582,6 +2640,7 @@
    if (TYPE(str) != T_STRING)
    str = rb_obj_as_string(str);

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);

@@ -2666,15 +2725,6 @@
return str;
}

-/*

    • call-seq:
    • ios.binmode    => ios
      
    • Puts ios into binary mode. This is useful only in
    • MS-DOS/Windows environments. Once a stream is in binary mode, it
    • cannot be reset to nonbinary mode.
  • */

VALUE
rb_io_binmode(VALUE io)
{
@@ -2693,6 +2743,30 @@
return io;
}

+/*

    • call-seq:
    • ios.binmode    => ios
      
    • Puts ios into binary mode. This is useful only in
    • MS-DOS/Windows environments. Once a stream is in binary mode, it
    • cannot be reset to nonbinary mode.
  • */

+static VALUE
+rb_io_binmode_m(VALUE io)
+{
+#if defined(_WIN32) || defined(DJGPP) || defined(CYGWIN) ||
defined(human68k) || defined(EMX)

  • VALUE write_io;
  • rb_io_binmode(io);
  • write_io = GetWriteIO(io);
  • if (write_io != io)
  •    rb_io_binmode(write_io);
    

+#endif

  • return io;
    +}

static const char*
rb_io_flags_mode(int flags)
{
@@ -3078,17 +3152,23 @@
struct rb_exec_arg exec;
int modef;
int pair[2];

  • int write_pair[2];
    };

static void
popen_redirect(struct popen_arg *p)
{
if ((p->modef & FMODE_READABLE) && (p->modef & FMODE_WRITABLE)) {

  •    close(p->write_pair[1]);
    
  •    if (p->write_pair[0] != 0) {
    
  •        dup2(p->write_pair[0], 0);
    
  •        close(p->write_pair[0]);
    
  •    }
       close(p->pair[0]);
    
  •    dup2(p->pair[1], 0);
    
  •    dup2(p->pair[1], 1);
    
  •    if (2 <= p->pair[1])
    
  •    if (p->pair[1] != 1) {
    
  •        dup2(p->pair[1], 1);
           close(p->pair[1]);
    
  •    }
    
    }
    else if (p->modef & FMODE_READABLE) {
    close(p->pair[0]);
    @@ -3132,6 +3212,8 @@
    int pid = 0;
    rb_io_t *fptr;
    VALUE port;
  • rb_io_t *write_fptr;
  • VALUE write_port;
    #if defined(HAVE_FORK)
    int status;
    struct popen_arg arg;
    @@ -3141,17 +3223,24 @@
    #endif
    FILE *fp = 0;
    int fd = -1;
  • int write_fd = -1;

#if defined(HAVE_FORK)
arg.modef = modef;
arg.pair[0] = arg.pair[1] = -1;

  • arg.write_pair[0] = arg.write_pair[1] = -1;
    switch (modef & (FMODE_READABLE|FMODE_WRITABLE)) {
    -#if defined(HAVE_SOCKETPAIR)
    case FMODE_READABLE|FMODE_WRITABLE:
  •    if (socketpair(AF_UNIX, SOCK_STREAM, 0, arg.pair) < 0)
    
  •    if (pipe(arg.write_pair) < 0)
           rb_sys_fail(cmd);
    
  •    if (pipe(arg.pair) < 0) {
    
  •        int e = errno;
    
  •        close(arg.write_pair[0]);
    
  •        close(arg.write_pair[1]);
    
  •        errno = e;
    
  •        rb_sys_fail(cmd);
    
  •    }
    
    break;
    -#endif
    case FMODE_READABLE:
    if (pipe(arg.pair) < 0)
    rb_sys_fail(cmd);
    @@ -3187,12 +3276,18 @@
    int e = errno;
    close(arg.pair[0]);
    close(arg.pair[1]);
  •    if ((modef & (FMODE_READABLE|FMODE_WRITABLE)) == 
    

(FMODE_READABLE|FMODE_WRITABLE)) {

  •        close(arg.write_pair[0]);
    
  •        close(arg.write_pair[1]);
    
  •    }
    

    errno = e;
    rb_sys_fail(cmd);
    }
    if ((modef & FMODE_READABLE) && (modef & FMODE_WRITABLE)) {
    close(arg.pair[1]);
    fd = arg.pair[0];

  •    close(arg.write_pair[0]);
    
  •    write_fd = arg.write_pair[1];
    

    }
    else if (modef & FMODE_READABLE) {
    close(arg.pair[1]);
    @@ -3245,6 +3340,16 @@
    fptr->mode = modef | FMODE_SYNC|FMODE_DUPLEX;
    fptr->pid = pid;

  • if (0 <= write_fd) {

  •    write_port = io_alloc(rb_cIO);
    
  •    MakeOpenFile(write_port, write_fptr);
    
  •    write_fptr->fd = write_fd;
    
  •    write_fptr->mode = (modef & ~FMODE_READABLE)| 
    

FMODE_SYNC|FMODE_DUPLEX;

  •    fptr->mode &= ~FMODE_WRITABLE;
    
  •    rb_ivar_set(port, rb_intern("@write_io"), write_port);
    
  •    fptr->has_write_io = 1;
    
  • }

#if defined (CYGWIN) || !defined(HAVE_FORK)
fptr->finalize = pipe_finalize;
pipe_add_fptr(fptr);
@@ -3770,6 +3875,7 @@
{
rb_io_t *fptr, *orig;
int fd;

  • VALUE write_io;

    io = rb_io_get_io(io);
    if (dest == io) return dest;
    @@ -3792,6 +3898,13 @@
    rb_io_binmode(dest);
    }

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_io = rb_obj_dup(write_io);
    
  •    rb_ivar_set(dest, rb_intern("@write_io"), write_io);
    
  •    fptr->has_write_io = 1;
    
  • }

  • return dest;
    }

@@ -4732,7 +4845,8 @@
if (!NIL_P(write)) {
Check_Type(write, T_ARRAY);
for (i=0; i<RARRAY_LEN(write); i++) {

  •  GetOpenFile(rb_io_get_io(RARRAY_PTR(write)[i]), fptr);
    
  •        VALUE write_io = 
    

GetWriteIO(rb_io_get_io(RARRAY_PTR(write)[i]));

  •  GetOpenFile(write_io, fptr);
     rb_fd_set(fptr->fd, &fds[1]);
     if (max < fptr->fd) max = fptr->fd;
    
    }
    @@ -4744,9 +4858,16 @@
    if (!NIL_P(except)) {
    Check_Type(except, T_ARRAY);
    for (i=0; i<RARRAY_LEN(except); i++) {
  •  GetOpenFile(rb_io_get_io(RARRAY_PTR(except)[i]), fptr);
    
  •        VALUE io = rb_io_get_io(RARRAY_PTR(except)[i]);
    
  •        VALUE write_io = GetWriteIO(io);
    
  •  GetOpenFile(io, fptr);
     rb_fd_set(fptr->fd, &fds[2]);
     if (max < fptr->fd) max = fptr->fd;
    
  •        if (io != write_io) {
    
  •            GetOpenFile(write_io, fptr);
    
  •            rb_fd_set(fptr->fd, &fds[2]);
    
  •            if (max < fptr->fd) max = fptr->fd;
    
  •        }
    
    }
    ep = rb_fd_ptr(&fds[2]);
    }
    @@ -4771,10 +4892,11 @@
    if (rp) {
    list = RARRAY_PTR(res)[0];
    for (i=0; i< RARRAY_LEN(read); i++) {
  • GetOpenFile(rb_io_get_io(RARRAY_PTR(read)[i]), fptr);
  •            VALUE io = rb_io_get_io(rb_ary_entry(read, i));
    
  • GetOpenFile(io, fptr);
    if (rb_fd_isset(fptr->fd, &fds[0]) ||
    rb_fd_isset(fptr->fd, &fds[3])) {
  •    rb_ary_push(list, rb_ary_entry(read, i));
    
  •    rb_ary_push(list, io);
    
    }
    }
    }
    @@ -4782,9 +4904,11 @@
    if (wp) {
    list = RARRAY_PTR(res)[1];
    for (i=0; i< RARRAY_LEN(write); i++) {
  • GetOpenFile(rb_io_get_io(RARRAY_PTR(write)[i]), fptr);
  •            VALUE io = rb_io_get_io(rb_ary_entry(write, i));
    
  •            VALUE write_io = GetWriteIO(io);
    
  • GetOpenFile(write_io, fptr);
    if (rb_fd_isset(fptr->fd, &fds[1])) {
  •    rb_ary_push(list, rb_ary_entry(write, i));
    
  •    rb_ary_push(list, io);
    
    }
    }
    }
    @@ -4792,10 +4916,18 @@
    if (ep) {
    list = RARRAY_PTR(res)[2];
    for (i=0; i< RARRAY_LEN(except); i++) {
  • GetOpenFile(rb_io_get_io(RARRAY_PTR(except)[i]), fptr);
  •            VALUE io = rb_io_get_io(rb_ary_entry(write, i));
    
  •            VALUE write_io = GetWriteIO(io);
    
  • GetOpenFile(io, fptr);
    if (rb_fd_isset(fptr->fd, &fds[2])) {
  •    rb_ary_push(list, rb_ary_entry(except, i));
    
  •    rb_ary_push(list, io);
    

    }

  •            else if (io != write_io) {
    
  •                GetOpenFile(write_io, fptr);
    
  •                if (rb_fd_isset(fptr->fd, &fds[2])) {
    
  •                    rb_ary_push(list, io);
    
  •                }
    
  •            }
     }
    

    }
    }
    @@ -5906,7 +6038,7 @@

    rb_define_method(rb_cIO, “isatty”, rb_io_isatty, 0);
    rb_define_method(rb_cIO, “tty?”, rb_io_isatty, 0);

  • rb_define_method(rb_cIO, “binmode”, rb_io_binmode, 0);
  • rb_define_method(rb_cIO, “binmode”, rb_io_binmode_m, 0);
    rb_define_method(rb_cIO, “sysseek”, rb_io_sysseek, -1);

    rb_define_method(rb_cIO, “ioctl”, rb_io_ioctl, -1);

e$B$J$!"e(BWindows e$B$K$D$$$F$O$$$A$$&e(B binmode
e$B$@$1$O=hCV$7$?$D$be(B
e$B$j$G$9$,!"$&$^$/$$$C$F$$$J$$$+$b$7$l$^$;$s!#e(B

e$B$J$+$@$G$9!#e(B

At Sun, 11 Nov 2007 05:04:30 +0900,
Tanaka A. wrote in [ruby-dev:32205]:

e$B$J$*!“e(BIO e$B$R$H$D$Ke(B 2e$B$D$Ne(B fd e$B$,F~$C$F$$$Fe(B fcntl e$BEy$Ne(B fd e$B$NA:ne(B e$B$K;Y>c$rMh$9$H$$$&LdBj$O!"=q$-9~$_B&$Ne(B fd e$B$K$bFHN)$7$?e(B IO e$B%*e(B e$B%V%8%'%/%H$r:n$j!"$=$l$re(B @write_io e$B$H$$$&%$%s%9%?%s%9JQ?t$Ge(B e$B;2>H2DG=$K$9$k$3$H$K$h$j!"A:nIT2DG=$G$O$J$$$h$&$K$7$F$”$j$^e(B
e$B$9!#e(B

e$BIaDL$N%$%s%9%?%s%9JQ?t$@$H%9%/%j%W%H$+$i=q$-49$($?$j$G$-$A$c$&$oe(B
e$B$1$G$9$,!"$=$l$r5v$9M}M3$O2?$G$7$g$&$+!#<h$j=P$9$H$-$K%A%’%C%/$,e(B
e$BF~$C$F$k$H$O$$$(!"$3$&$$$&$N$O%"%/%;%C%5$rDj5A$7$?$[$&$,$$$$$se(B
e$B$8$c$J$+$m$&$+$H;W$$$^$9$,!#e(B

In article [email protected],
Nobuyoshi N. [email protected] writes:

e$BIaDL$N%$%s%9%?%s%9JQ?t$@$H%9%/%j%W%H$+$i=q$-49$($?$j$G$-$A$c$&$oe(B
e$B$1$G$9$,!"$=$l$r5v$9M}M3$O2?$G$7$g$&$+!#e(B

e$B%a%=%C%I$NL>A0$r9M$($FN;2r$r<h$l$k$^$G$d$k$N$,LLE]$@$C$?$N$G!#e(B

e$B<h$j=P$9$H$-$K%A%'%C%/$,e(B
e$BF~$C$F$k$H$O$$$(!“$3$&$$$&$N$O%”%/%;%C%5$rDj5A$7$?$[$&$,$$$$$se(B
e$B$8$c$J$+$m$&$+$H;W$$$^$9$,!#e(B

e$B$I$s$JL>A0$,$$$$$G$7$g$&$+!#e(B

e$B$J$+$@$G$9!#e(B

At Sun, 11 Nov 2007 12:24:42 +0900,
Tanaka A. wrote in [ruby-dev:32209]:

e$B<h$j=P$9$H$-$K%A%’%C%/$,e(B
e$BF~$C$F$k$H$O$$$(!"$3$&$$$&$N$O%"%/%;%C%5$rDj5A$7$?$[$&$,$$$$$se(B
e$B$8$c$J$+$m$&$+$H;W$$$^$9$,!#e(B

e$B$I$s$JL>A0$,$$$$$G$7$g$&$+!#e(B

e$B$=$N$^$^e(Bwrite_ioe$B$H$+!"D9$$$N$,$h$1$l$Pe(Btied_io_for_writinge$B$H$+$Ge(B
e$B$O$I$&$G$7$g$&$+!#e(B

In article [email protected],
Tanaka A. [email protected] writes:

tied_io_for_writing e$B$J$i$3$&$G$9$+$M!#e(B

e$BN;2r$,F@$i$l$J$$$N$G!“8x3+%$%s%?!<%U%'!<%9e(B
(IO.tied_io_for_writing) e$B$OB-$;$^$;$s$,!”%P%0$OD>$9$H$$$&$3e(B
e$B$H$G%3%_%C%H$7$^$7$?!#e(B

e$B%$%s%9%?%s%9JQ?t$O8x3+%$%s%?!<%U%'!<%9$G$O$J$$$N$GB-$7$F$“$je(B
e$B$^$9$,Hs>oMQ$J$N$GIaCJ$O;H$o$J$$$H$$$&$3$H$G!#Hs>o;~$G$J$/$Fe(B
e$B$bI,MW$K$J$k>l9g$K$O!”$=$N>l9g$N$?$a$N%a%=%C%I$r:n$k$J$j$J$se(B
e$B$J$j$NBP1~$r$9$k$N$,E,@Z$G$7$g$&!#e(B

In article [email protected],
Nobuyoshi N. [email protected] writes:

e$B$=$N$^$^e(Bwrite_ioe$B$H$+!"D9$$$N$,$h$1$l$Pe(Btied_io_for_writinge$B$H$+$Ge(B
e$B$O$I$&$G$7$g$&$+!#e(B

tied_io_for_writing e$B$J$i$3$&$G$9$+$M!#e(B

Index: include/ruby/intern.h

— include/ruby/intern.h (e$B%j%S%8%g%se(B 13873)
+++ include/ruby/intern.h (e$B:n6H%3%T!<e(B)
@@ -353,6 +353,7 @@
VALUE rb_io_print(int, VALUE*, VALUE);
VALUE rb_io_puts(int, VALUE*, VALUE);
VALUE rb_io_fdopen(int, int, const char*);
+VALUE rb_io_get_write_io(VALUE);
VALUE rb_file_open(const char*, const char*);
VALUE rb_gets(void);
void rb_write_error(const char*);
Index: include/ruby/io.h

— include/ruby/io.h (e$B%j%S%8%g%se(B 13873)
+++ include/ruby/io.h (e$B:n6H%3%T!<e(B)
@@ -45,6 +45,7 @@
int rbuf_off;
int rbuf_len;
int rbuf_capa;

  • VALUE tied_io_for_writing;
    rb_encoding *enc;
    } rb_io_t;

@@ -88,8 +89,12 @@
fp->rbuf_off = 0;
fp->rbuf_len = 0;
fp->rbuf_capa = 0;\

  • fp->tied_io_for_writing = 0;
    } while (0)

+#define GetReadIO(io) (io)
+#define GetWriteIO(io) rb_io_get_write_io(io)
+
FILE *rb_io_stdio_file(rb_io_t *fptr);

FILE rb_fopen(const char, const char*);
Index: io.c

— io.c (e$B%j%S%8%g%se(B 13873)
+++ io.c (e$B:n6H%3%T!<e(B)
@@ -246,6 +246,17 @@
return rb_check_convert_type(io, T_FILE, “IO”, “to_io”);
}

+VALUE
+rb_io_get_write_io(VALUE io)
+{

  • VALUE write_io;
  • write_io = RFILE(io)->fptr->tied_io_for_writing;
  • if (write_io) {
  •    return write_io;
    
  • }
  • return io;
    +}

/*

  • call-seq:
  • IO.try_convert(obj) -> io or nil
    

@@ -676,6 +687,7 @@
VALUE tmp;

 rb_secure(4);
  • io = GetWriteIO(io);
    str = rb_obj_as_string(str);
    tmp = rb_io_check_io(io);
    if (NIL_P(tmp)) {
    @@ -748,6 +760,7 @@
    return rb_funcall(io, id_flush, 0);
    }

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    if (fptr->mode & FMODE_WRITABLE) {
    @@ -982,6 +995,7 @@
    {
    rb_io_t *fptr;

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    return (fptr->mode & FMODE_SYNC) ? Qtrue : Qfalse;
    }
    @@ -1006,6 +1020,7 @@
    {
    rb_io_t *fptr;

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    if (RTEST(mode)) {
    fptr->mode |= FMODE_SYNC;
    @@ -1034,6 +1049,7 @@
    #ifdef HAVE_FSYNC
    rb_io_t *fptr;

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);

    io_fflush(fptr);
    @@ -1464,6 +1480,7 @@
    if (TYPE(str) != T_STRING)
    str = rb_obj_as_string(str);

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);

@@ -2348,7 +2365,17 @@
{
rb_io_t *fptr;
int fd;

  • VALUE write_io;

  • rb_io_t *write_fptr;

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_fptr = RFILE(write_io)->fptr;
    
  •    if (write_fptr && 0 <= write_fptr->fd) {
    
  •        rb_io_fptr_cleanup(write_fptr, Qtrue);
    
  •    }
    
  • }

  • fptr = RFILE(io)->fptr;
    if (!fptr) return Qnil;
    if (fptr->fd < 0) return Qnil;
    @@ -2425,7 +2452,17 @@
    rb_io_closed(VALUE io)
    {
    rb_io_t *fptr;

  • VALUE write_io;

  • rb_io_t *write_fptr;

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_fptr = RFILE(write_io)->fptr;
    
  •    if (write_fptr && 0 <= write_fptr->fd) {
    
  •        return Qfalse;
    
  •    }
    
  • }

  • fptr = RFILE(io)->fptr;
    rb_io_check_initialized(fptr);
    return 0 <= fptr->fd ? Qfalse : Qtrue;
    @@ -2453,6 +2490,7 @@
    rb_io_close_read(VALUE io)
    {
    rb_io_t *fptr;

  • VALUE write_io;

    if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
    rb_raise(rb_eSecurityError, “Insecure: can’t close”);
    @@ -2469,6 +2507,13 @@
    return rb_io_close(io);
    return Qnil;
    }

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    fptr_finalize(fptr, Qfalse);
    
  •    return Qnil;
    
  • }

  • if (fptr->mode & FMODE_WRITABLE) {
    rb_raise(rb_eIOError, “closing non-duplex IO for reading”);
    }
    @@ -2502,6 +2547,7 @@
    if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
    rb_raise(rb_eSecurityError, “Insecure: can’t close”);
    }

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    if (is_socket(fptr->fd, fptr->path)) {
    #ifndef SHUT_WR
    @@ -2582,6 +2628,7 @@
    if (TYPE(str) != T_STRING)
    str = rb_obj_as_string(str);

  • io = GetWriteIO(io);
    GetOpenFile(io, fptr);
    rb_io_check_writable(fptr);

@@ -2666,15 +2713,6 @@
return str;
}

-/*

    • call-seq:
    • ios.binmode    => ios
      
    • Puts ios into binary mode. This is useful only in
    • MS-DOS/Windows environments. Once a stream is in binary mode, it
    • cannot be reset to nonbinary mode.
  • */

VALUE
rb_io_binmode(VALUE io)
{
@@ -2693,6 +2731,30 @@
return io;
}

+/*

    • call-seq:
    • ios.binmode    => ios
      
    • Puts ios into binary mode. This is useful only in
    • MS-DOS/Windows environments. Once a stream is in binary mode, it
    • cannot be reset to nonbinary mode.
  • */

+static VALUE
+rb_io_binmode_m(VALUE io)
+{
+#if defined(_WIN32) || defined(DJGPP) || defined(CYGWIN) ||
defined(human68k) || defined(EMX)

  • VALUE write_io;
  • rb_io_binmode(io);
  • write_io = GetWriteIO(io);
  • if (write_io != io)
  •    rb_io_binmode(write_io);
    

+#endif

  • return io;
    +}

static const char*
rb_io_flags_mode(int flags)
{
@@ -3078,17 +3140,23 @@
struct rb_exec_arg exec;
int modef;
int pair[2];

  • int write_pair[2];
    };

static void
popen_redirect(struct popen_arg *p)
{
if ((p->modef & FMODE_READABLE) && (p->modef & FMODE_WRITABLE)) {

  •    close(p->write_pair[1]);
    
  •    if (p->write_pair[0] != 0) {
    
  •        dup2(p->write_pair[0], 0);
    
  •        close(p->write_pair[0]);
    
  •    }
       close(p->pair[0]);
    
  •    dup2(p->pair[1], 0);
    
  •    dup2(p->pair[1], 1);
    
  •    if (2 <= p->pair[1])
    
  •    if (p->pair[1] != 1) {
    
  •        dup2(p->pair[1], 1);
           close(p->pair[1]);
    
  •    }
    
    }
    else if (p->modef & FMODE_READABLE) {
    close(p->pair[0]);
    @@ -3132,6 +3200,8 @@
    int pid = 0;
    rb_io_t *fptr;
    VALUE port;
  • rb_io_t *write_fptr;
  • VALUE write_port;
    #if defined(HAVE_FORK)
    int status;
    struct popen_arg arg;
    @@ -3141,17 +3211,24 @@
    #endif
    FILE *fp = 0;
    int fd = -1;
  • int write_fd = -1;

#if defined(HAVE_FORK)
arg.modef = modef;
arg.pair[0] = arg.pair[1] = -1;

  • arg.write_pair[0] = arg.write_pair[1] = -1;
    switch (modef & (FMODE_READABLE|FMODE_WRITABLE)) {
    -#if defined(HAVE_SOCKETPAIR)
    case FMODE_READABLE|FMODE_WRITABLE:
  •    if (socketpair(AF_UNIX, SOCK_STREAM, 0, arg.pair) < 0)
    
  •    if (pipe(arg.write_pair) < 0)
           rb_sys_fail(cmd);
    
  •    if (pipe(arg.pair) < 0) {
    
  •        int e = errno;
    
  •        close(arg.write_pair[0]);
    
  •        close(arg.write_pair[1]);
    
  •        errno = e;
    
  •        rb_sys_fail(cmd);
    
  •    }
    
    break;
    -#endif
    case FMODE_READABLE:
    if (pipe(arg.pair) < 0)
    rb_sys_fail(cmd);
    @@ -3187,12 +3264,18 @@
    int e = errno;
    close(arg.pair[0]);
    close(arg.pair[1]);
  •    if ((modef & (FMODE_READABLE|FMODE_WRITABLE)) == 
    

(FMODE_READABLE|FMODE_WRITABLE)) {

  •        close(arg.write_pair[0]);
    
  •        close(arg.write_pair[1]);
    
  •    }
    

    errno = e;
    rb_sys_fail(cmd);
    }
    if ((modef & FMODE_READABLE) && (modef & FMODE_WRITABLE)) {
    close(arg.pair[1]);
    fd = arg.pair[0];

  •    close(arg.write_pair[0]);
    
  •    write_fd = arg.write_pair[1];
    

    }
    else if (modef & FMODE_READABLE) {
    close(arg.pair[1]);
    @@ -3245,6 +3328,15 @@
    fptr->mode = modef | FMODE_SYNC|FMODE_DUPLEX;
    fptr->pid = pid;

  • if (0 <= write_fd) {

  •    write_port = io_alloc(rb_cIO);
    
  •    MakeOpenFile(write_port, write_fptr);
    
  •    write_fptr->fd = write_fd;
    
  •    write_fptr->mode = (modef & ~FMODE_READABLE)| 
    

FMODE_SYNC|FMODE_DUPLEX;

  •    fptr->mode &= ~FMODE_WRITABLE;
    
  •    fptr->tied_io_for_writing = write_port;
    
  • }

#if defined (CYGWIN) || !defined(HAVE_FORK)
fptr->finalize = pipe_finalize;
pipe_add_fptr(fptr);
@@ -3375,7 +3467,23 @@
return port;
}

+/*

    • call-seq:
    • io.tied_io_for_writing -> io or nil
      
    • Return the tied IO for writing from the IO object
    • opened by duplex popen such as IO.popen(“…”, “r+”).
  • */
    static VALUE
    +rb_io_tied_io_for_writing(VALUE io)
    +{
  • VALUE write_io = GetWriteIO(io);
  • if (write_io != io)
  •    return write_io;
    
  • return Qnil;
    +}

+static VALUE
rb_open_file(int argc, VALUE *argv, VALUE io)
{
VALUE fname, vmode, perm;
@@ -3770,6 +3878,7 @@
{
rb_io_t *fptr, *orig;
int fd;

  • VALUE write_io;

    io = rb_io_get_io(io);
    if (dest == io) return dest;
    @@ -3792,6 +3901,12 @@
    rb_io_binmode(dest);
    }

  • write_io = GetWriteIO(io);

  • if (io != write_io) {

  •    write_io = rb_obj_dup(write_io);
    
  •    fptr->tied_io_for_writing = write_io;
    
  • }

  • return dest;
    }

@@ -4732,7 +4847,8 @@
if (!NIL_P(write)) {
Check_Type(write, T_ARRAY);
for (i=0; i<RARRAY_LEN(write); i++) {

  •  GetOpenFile(rb_io_get_io(RARRAY_PTR(write)[i]), fptr);
    
  •        VALUE write_io = 
    

GetWriteIO(rb_io_get_io(RARRAY_PTR(write)[i]));

  •  GetOpenFile(write_io, fptr);
     rb_fd_set(fptr->fd, &fds[1]);
     if (max < fptr->fd) max = fptr->fd;
    
    }
    @@ -4744,9 +4860,16 @@
    if (!NIL_P(except)) {
    Check_Type(except, T_ARRAY);
    for (i=0; i<RARRAY_LEN(except); i++) {
  •  GetOpenFile(rb_io_get_io(RARRAY_PTR(except)[i]), fptr);
    
  •        VALUE io = rb_io_get_io(RARRAY_PTR(except)[i]);
    
  •        VALUE write_io = GetWriteIO(io);
    
  •  GetOpenFile(io, fptr);
     rb_fd_set(fptr->fd, &fds[2]);
     if (max < fptr->fd) max = fptr->fd;
    
  •        if (io != write_io) {
    
  •            GetOpenFile(write_io, fptr);
    
  •            rb_fd_set(fptr->fd, &fds[2]);
    
  •            if (max < fptr->fd) max = fptr->fd;
    
  •        }
    
    }
    ep = rb_fd_ptr(&fds[2]);
    }
    @@ -4771,10 +4894,11 @@
    if (rp) {
    list = RARRAY_PTR(res)[0];
    for (i=0; i< RARRAY_LEN(read); i++) {
  • GetOpenFile(rb_io_get_io(RARRAY_PTR(read)[i]), fptr);
  •            VALUE io = rb_io_get_io(rb_ary_entry(read, i));
    
  • GetOpenFile(io, fptr);
    if (rb_fd_isset(fptr->fd, &fds[0]) ||
    rb_fd_isset(fptr->fd, &fds[3])) {
  •    rb_ary_push(list, rb_ary_entry(read, i));
    
  •    rb_ary_push(list, io);
    
    }
    }
    }
    @@ -4782,9 +4906,11 @@
    if (wp) {
    list = RARRAY_PTR(res)[1];
    for (i=0; i< RARRAY_LEN(write); i++) {
  • GetOpenFile(rb_io_get_io(RARRAY_PTR(write)[i]), fptr);
  •            VALUE io = rb_io_get_io(rb_ary_entry(write, i));
    
  •            VALUE write_io = GetWriteIO(io);
    
  • GetOpenFile(write_io, fptr);
    if (rb_fd_isset(fptr->fd, &fds[1])) {
  •    rb_ary_push(list, rb_ary_entry(write, i));
    
  •    rb_ary_push(list, io);
    
    }
    }
    }
    @@ -4792,10 +4918,18 @@
    if (ep) {
    list = RARRAY_PTR(res)[2];
    for (i=0; i< RARRAY_LEN(except); i++) {
  • GetOpenFile(rb_io_get_io(RARRAY_PTR(except)[i]), fptr);
  •            VALUE io = rb_io_get_io(rb_ary_entry(write, i));
    
  •            VALUE write_io = GetWriteIO(io);
    
  • GetOpenFile(io, fptr);
    if (rb_fd_isset(fptr->fd, &fds[2])) {
  •    rb_ary_push(list, rb_ary_entry(except, i));
    
  •    rb_ary_push(list, io);
    

    }

  •            else if (io != write_io) {
    
  •                GetOpenFile(write_io, fptr);
    
  •                if (rb_fd_isset(fptr->fd, &fds[2])) {
    
  •                    rb_ary_push(list, io);
    
  •                }
    
  •            }
     }
    

    }
    }
    @@ -5830,6 +5964,8 @@

    rb_define_method(rb_cIO, “initialize”, rb_io_initialize, -1);

  • rb_define_method(rb_cIO, “tied_io_for_writing”,
    rb_io_tied_io_for_writing, 0);

  • rb_output_fs = Qnil;
    rb_define_hooked_variable(“$,”, &rb_output_fs, 0, rb_str_setter);

@@ -5906,7 +6042,7 @@

 rb_define_method(rb_cIO, "isatty", rb_io_isatty, 0);
 rb_define_method(rb_cIO, "tty?", rb_io_isatty, 0);
  • rb_define_method(rb_cIO, “binmode”, rb_io_binmode, 0);
  • rb_define_method(rb_cIO, “binmode”, rb_io_binmode_m, 0);
    rb_define_method(rb_cIO, “sysseek”, rb_io_sysseek, -1);

    rb_define_method(rb_cIO, “ioctl”, rb_io_ioctl, -1);
    Index: common.mk
    ===================================================================
    — common.mk (e$B%j%S%8%g%se(B 13873)
    +++ common.mk (e$B:n6H%3%T!<e(B)
    @@ -434,7 +434,7 @@
    gc.$(OBJEXT): {$(VPATH)}gc.c {$(VPATH)}ruby.h {$(VPATH)}config.h
    {$(VPATH)}defines.h {$(VPATH)}intern.h {$(VPATH)}missing.h
    {$(VPATH)}signal.h {$(VPATH)}st.h {$(VPATH)}node.h \

  • {$(VPATH)}re.h {$(VPATH)}regex.h {$(VPATH)}oniguruma.h \
  • {$(VPATH)}re.h {$(VPATH)}io.h {$(VPATH)}regex.h {$(VPATH)}oniguruma.h

    {$(VPATH)}vm_core.h {$(VPATH)}id.h {$(VPATH)}debug.h
    {$(VPATH)}vm_opts.h
    {$(VPATH)}thread_$(THREAD_MODEL).h
    hash.$(OBJEXT): {$(VPATH)}hash.c {$(VPATH)}ruby.h {$(VPATH)}config.h
    Index: gc.c
    ===================================================================
    — gc.c (e$B%j%S%8%g%se(B 13873)
    +++ gc.c (e$B:n6H%3%T!<e(B)
    @@ -17,6 +17,7 @@
    #include “ruby/st.h”
    #include “ruby/node.h”
    #include “ruby/re.h”
    +#include “ruby/io.h”
    #include “vm_core.h”
    #include “gc.h”
    #include <stdio.h>
    @@ -1080,6 +1081,9 @@
    break;

    case T_FILE:
    
  • gc_mark(obj->as.file.fptr->tied_io_for_writing, lev);

  •    break;
    
  •  case T_REGEXP:
     case T_FLOAT:
     case T_BIGNUM:
    

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:32322] Re: Use two pipes for duplex IO.popen”
on Tue, 20 Nov 2007 12:21:33 +0900, Tanaka A. [email protected]
writes:

|In article [email protected],
| Tanaka A. [email protected] writes:
|
|> tied_io_for_writing e$B$J$i$3$&$G$9$+$M!#e(B
|
|e$BN;2r$,F@$i$l$J$$$N$G!“8x3+%$%s%?!<%U%'!<%9e(B
|(IO.tied_io_for_writing) e$B$OB-$;$^$;$s$,!”%P%0$OD>$9$H$$$&$3e(B
|e$B$H$G%3%_%C%H$7$^$7$?!#e(B

e$B$"$j$,$H$&$4$6$$$^$9!#e(B

|e$B%$%s%9%?%s%9JQ?t$O8x3+%$%s%?!<%U%'!<%9$G$O$J$$$N$GB-$7$F$“$je(B
|e$B$^$9$,Hs>oMQ$J$N$GIaCJ$O;H$o$J$$$H$$$&$3$H$G!#Hs>o;~$G$J$/$Fe(B
|e$B$bI,MW$K$J$k>l9g$K$O!”$=$N>l9g$N$?$a$N%a%=%C%I$r:n$k$J$j$J$se(B
|e$B$J$j$NBP1~$r$9$k$N$,E,@Z$G$7$g$&!#e(B

e$BI,MW$J6ILL$,$h$/$o$+$i$J$$$N$GH=CG$G$-$^$;$s$G$7$?!#$3$NJU$Oe(B
e$BMWK>$,=P$F$+$i9M$($^$9!#e(B