# Use two pipes for duplex IO.popen

**URL:** <https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818>\
**Category:** Development\
**Created:** [November 10, 2007, 9:05pm UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818 "2007-11-10T21:05:41Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 10, 2007, 9:05pm UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/1 "2007-11-10T21:05:41Z")

</div>

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);

- 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 = 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;

- 
- 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;

- 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;

- 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);

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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 11, 2007, 4:00am UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/2 "2007-11-11T04:00:55Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 11, 2007, 4:25am UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/3 "2007-11-11T04:25:27Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Nobuyoshi N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 12, 2007, 3:47am UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/4 "2007-11-12T03:47:55Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 20, 2007, 4:22am UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/5 "2007-11-20T04:22:11Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Tanaka A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 12, 2007, 7:19am UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/6 "2007-11-12T07:19:28Z")

</div>

In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
Nobuyoshi N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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);

- 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 = 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;

- 
- 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;

- 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);

- {$(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;

- gc\_mark(obj-\>as.file.fptr-\>tied\_io\_for\_writing, lev);

- 

```
   break;

```

- 

```
 case T_REGEXP:
 case T_FLOAT:
 case T_BIGNUM:

```

---

<div class="post-metadata">

**Author:** ![Tanaka\_A](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c67d28/32.png) [@Tanaka\_A](https://www.ruby-forum.com/u/Tanaka_A)\
**Post date:** [November 20, 2007, 9:08am UTC](https://www.ruby-forum.com/t/use-two-pipes-for-duplex-io-popen/119818/7 "2007-11-20T09:08:35Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
writes:

|In article [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid),  
| Tanaka A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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
