In article <1101888165.570792.4192.nullmailer / x31.priv.netlab.jp>,
  Yukihiro Matsumoto <matz / ruby-lang.org> writes:

> |というわけで、stdio を捨てれば解決できるように思います。たぶん。
>
> そうかあ。手間がなあ。

どのくらいの手間がかかるか疑問に思って、ちょっとやってみました。

いくつかさぼっているところもあるのですが、こんなかんじでだいたい捨てら
れるようです。面白いことに、(さぼっているところがあるのもあるでしょう
が)、行数を測ってみると少し減っています。

まぁ、text mode は考えていませんし、この話の発端である signal もちゃん
と扱っているわけではありませんが。

signal についてはいろいろ考えたのですが、TRAP_BEG, TRAP_END 間では一時
的に signal handler から SA_RESTART を落とすのが適切だろうと考えるよう
になりました。そうやって system call の再起動をしないようにしておいて、
system call が EINTR もしくは partial read/write で終わってから trap
を起動するのがいいと思います。signal handler から直接実行させる方法は
検討したのですが、read/write が 1byte 以上行われた後に signal を受けて
partial read/write になるときには、読み書きした長さを signal handler
で知ることができないため、バッファの状態を trap を起動する前に更新でき
ず、適切に動作するには無理があると考えています。まぁ、そんな難しいこと
は考えずに IO をロックするほうがいいのかもしれませんが...

なお、EOF flag はなくしてしまいました。また、f2 もなくしてしまったので
読み書き両用な popen は socketpair に変えてあります。f も無くそうかと
思ったのですが、popen(3) のサポートを無くすのもなんだと思ったので残し
てあります。

あと、rb_read_pending など、FILE* を引数に取る関数は、バッファにアクセ
スできなくなるので困った所です。ext で使うぶんは rb_io_read_pending な
どを足してどうにかしましたが、拡張ライブラリに対する非互換性になること
はたしかですね。

Index: file.c
===================================================================
RCS file: /src/ruby/file.c,v
retrieving revision 1.192
diff -u -p -r1.192 file.c
--- file.c	22 Nov 2004 17:02:21 -0000	1.192
+++ file.c	5 Dec 2004 14:51:03 -0000
@@ -639,7 +639,7 @@ rb_stat(file, st)
 	OpenFile *fptr;
 
 	GetOpenFile(tmp, fptr);
-	return fstat(fileno(fptr->f), st);
+	return fstat(fptr->fd, st);
     }
     FilePathValue(file);
     return stat(StringValueCStr(file), st);
@@ -693,7 +693,7 @@ rb_io_stat(obj)
     struct stat st;
 
     GetOpenFile(obj, fptr);
-    if (fstat(fileno(fptr->f), &st) == -1) {
+    if (fstat(fptr->fd, &st) == -1) {
 	rb_sys_fail(fptr->path);
     }
     return stat_new(&st);
@@ -1531,7 +1531,7 @@ rb_file_atime(obj)
     struct stat st;
 
     GetOpenFile(obj, fptr);
-    if (fstat(fileno(fptr->f), &st) == -1) {
+    if (fstat(fptr->fd, &st) == -1) {
 	rb_sys_fail(fptr->path);
     }
     return rb_time_new(st.st_atime, 0);
@@ -1576,7 +1576,7 @@ rb_file_mtime(obj)
     struct stat st;
 
     GetOpenFile(obj, fptr);
-    if (fstat(fileno(fptr->f), &st) == -1) {
+    if (fstat(fptr->fd, &st) == -1) {
 	rb_sys_fail(fptr->path);
     }
     return rb_time_new(st.st_mtime, 0);
@@ -1624,7 +1624,7 @@ rb_file_ctime(obj)
     struct stat st;
 
     GetOpenFile(obj, fptr);
-    if (fstat(fileno(fptr->f), &st) == -1) {
+    if (fstat(fptr->fd, &st) == -1) {
 	rb_sys_fail(fptr->path);
     }
     return rb_time_new(st.st_ctime, 0);
@@ -1695,7 +1695,7 @@ rb_file_chmod(obj, vmode)
 
     GetOpenFile(obj, fptr);
 #ifdef HAVE_FCHMOD
-    if (fchmod(fileno(fptr->f), mode) == -1)
+    if (fchmod(fptr->fd, mode) == -1)
 	rb_sys_fail(fptr->path);
 #else
     if (!fptr->path) return Qnil;
@@ -1840,7 +1840,7 @@ rb_file_chown(obj, owner, group)
     if (chown(fptr->path, o, g) == -1)
 	rb_sys_fail(fptr->path);
 #else
-    if (fchown(fileno(fptr->f), o, g) == -1)
+    if (fchown(fptr->fd, o, g) == -1)
 	rb_sys_fail(fptr->path);
 #endif
 
@@ -2973,7 +2973,6 @@ rb_file_truncate(obj, len)
     VALUE obj, len;
 {
     OpenFile *fptr;
-    FILE *f;
     off_t pos;
 
     rb_secure(2);
@@ -2982,15 +2981,13 @@ rb_file_truncate(obj, len)
     if (!(fptr->mode & FMODE_WRITABLE)) {
 	rb_raise(rb_eIOError, "not opened for writing");
     }
-    f = GetWriteFile(fptr);
-    fflush(f);
-    fseeko(f, (off_t)0, SEEK_CUR);
+    rb_io_flush(obj);
 #ifdef HAVE_TRUNCATE
-    if (ftruncate(fileno(f), pos) < 0)
+    if (ftruncate(fptr->fd, pos) < 0)
 	rb_sys_fail(fptr->path);
 #else
 # ifdef HAVE_CHSIZE
-    if (chsize(fileno(f), pos) < 0)
+    if (chsize(fptr->fd, pos) < 0)
 	rb_sys_fail(fptr->path);
 # else
     rb_notimplement();
@@ -3084,10 +3081,10 @@ rb_file_flock(obj, operation)
     GetOpenFile(obj, fptr);
 
     if (fptr->mode & FMODE_WRITABLE) {
-	fflush(GetWriteFile(fptr));
+        rb_io_flush(obj);
     }
   retry:
-    if (flock(fileno(fptr->f), op) < 0) {
+    if (flock(fptr->fd, op) < 0) {
         switch (errno) {
           case EAGAIN:
           case EACCES:
Index: intern.h
===================================================================
RCS file: /src/ruby/intern.h,v
retrieving revision 1.160
diff -u -p -r1.160 intern.h
--- intern.h	3 Dec 2004 03:25:29 -0000	1.160
+++ intern.h	5 Dec 2004 14:51:03 -0000
@@ -278,6 +278,7 @@ VALUE rb_io_gets _((VALUE));
 VALUE rb_io_getc _((VALUE));
 VALUE rb_io_ungetc _((VALUE, VALUE));
 VALUE rb_io_close _((VALUE));
+VALUE rb_io_flush _((VALUE));
 VALUE rb_io_eof _((VALUE));
 VALUE rb_io_binmode _((VALUE));
 VALUE rb_io_addstr _((VALUE, VALUE));
Index: io.c
===================================================================
RCS file: /src/ruby/io.c,v
retrieving revision 1.342
diff -u -p -r1.342 io.c
--- io.c	2 Dec 2004 15:17:35 -0000	1.342
+++ io.c	5 Dec 2004 14:51:03 -0000
@@ -19,6 +19,8 @@
 #include <ctype.h>
 #include <errno.h>
 
+#include <sys/types.h>
+#include <sys/socket.h>
 
 #if defined(MSDOS) || defined(__BOW__) || defined(__CYGWIN__) || defined(_WIN32) || defined(__human68k__) || defined(__EMX__) || defined(__BEOS__)
 # define NO_SAFE_RENAME
@@ -117,50 +119,14 @@ static VALUE lineno = INT2FIX(0);
 #define open(file_spec, flags, mode)  open(file_spec, flags, mode, "rfm=stmlf")
 #endif
 
-#ifdef _STDIO_USES_IOSTREAM  /* GNU libc */
-#  ifdef _IO_fpos_t
-#    define READ_DATA_PENDING(fp) ((fp)->_IO_read_ptr != (fp)->_IO_read_end)
-#    define READ_DATA_PENDING_COUNT(fp) ((fp)->_IO_read_end - (fp)->_IO_read_ptr)
-#    define READ_DATA_PENDING_PTR(fp) ((fp)->_IO_read_ptr)
-#  else
-#    define READ_DATA_PENDING(fp) ((fp)->_gptr < (fp)->_egptr)
-#    define READ_DATA_PENDING_COUNT(fp) ((fp)->_egptr - (fp)->_gptr)
-#    define READ_DATA_PENDING_PTR(fp) ((fp)->_gptr)
-#  endif
-#elif defined(FILE_COUNT)
-#  define READ_DATA_PENDING(fp) ((fp)->FILE_COUNT > 0)
-#  define READ_DATA_PENDING_COUNT(fp) ((fp)->FILE_COUNT)
-#elif defined(FILE_READEND)
-#  define READ_DATA_PENDING(fp) ((fp)->FILE_READPTR < (fp)->FILE_READEND)
-#  define READ_DATA_PENDING_COUNT(fp) ((fp)->FILE_READEND - (fp)->FILE_READPTR)
-#elif defined(__BEOS__)
-#  define READ_DATA_PENDING(fp) (fp->_state._eof == 0)
-#elif defined(__VMS)
-#  define READ_DATA_PENDING(fp) (((unsigned int)((*(fp))->_flag) & _IOEOF) == 0)
-#else
-/* requires systems own version of the ReadDataPending() */
-extern int ReadDataPending();
-#  define READ_DATA_PENDING(fp) (!feof(fp))
-#  define READ_DATA_BUFFERED(fp) 0
-#endif
-#ifndef READ_DATA_BUFFERED
-#  define READ_DATA_BUFFERED(fp) READ_DATA_PENDING(fp)
-#endif
-
-#ifndef READ_DATA_PENDING_PTR
-# ifdef FILE_READPTR
-#  define READ_DATA_PENDING_PTR(fp) ((char *)(fp)->FILE_READPTR)
-# endif
-#endif
-
-#if defined __DJGPP__
-# undef READ_DATA_PENDING_COUNT
-# undef READ_DATA_PENDING_PTR
-#endif
-
-#define READ_CHECK(fp) do {\
-    if (!READ_DATA_PENDING(fp)) {\
-	rb_thread_wait_fd(fileno(fp));\
+#define READ_DATA_PENDING(fptr) ((fptr)->rbuf_len)
+#define READ_DATA_PENDING_COUNT(fptr) ((fptr)->rbuf_len)
+#define READ_DATA_PENDING_PTR(fptr) ((fptr)->rbuf+(fptr)->rbuf_off)
+#define READ_DATA_BUFFERED(fptr) READ_DATA_PENDING(fptr)
+
+#define READ_CHECK(fptr) do {\
+    if (!READ_DATA_PENDING(fptr)) {\
+	rb_thread_wait_fd((fptr)->fd);\
 	rb_io_check_closed(fptr);\
      }\
 } while(0)
@@ -195,12 +161,12 @@ rb_io_check_closed(fptr)
     OpenFile *fptr;
 {
     rb_io_check_initialized(fptr);
-    if (!fptr->f && !fptr->f2) {
+    if (fptr->fd < 0 || !fptr->f) {
 	rb_raise(rb_eIOError, "closed stream");
     }
 }
 
-static void io_fflush _((FILE *, OpenFile *));
+static int io_fflush _((OpenFile *));
 
 static VALUE
 rb_io_get_io(io)
@@ -216,18 +182,59 @@ rb_io_check_io(io)
     return rb_check_convert_type(io, T_FILE, "IO", "to_io");
 }
 
+static void
+io_unread(OpenFile *fptr)
+{
+    off_t r;
+    rb_io_check_closed(fptr);
+    if (fptr->rbuf_len == 0 || fptr->mode & FMODE_UNSEEKABLE)
+        return;
+    /* xxx: target position may be negative if buffer is filled by ungetc */
+    r = lseek(fptr->fd, -fptr->rbuf_len, SEEK_CUR);
+    if (r < 0) {
+        if (errno == ESPIPE)
+            fptr->mode |= FMODE_UNSEEKABLE;
+        return;
+    }
+    fptr->rbuf_off = 0;
+    fptr->rbuf_len = 0;
+    return;
+}
+
+static int
+io_ungetc(int c, OpenFile *fptr)
+{
+    if (fptr->rbuf == NULL) {
+        fptr->rbuf_off = 0;
+        fptr->rbuf_len = 0;
+        fptr->rbuf_capa = 8192;
+        fptr->rbuf = ALLOC_N(char, fptr->rbuf_capa);
+    }
+    if (c < 0 || fptr->rbuf_len == fptr->rbuf_capa) {
+        return -1;
+    }
+    if (fptr->rbuf_off == 0) {
+        if (fptr->rbuf_len)
+            MEMMOVE(fptr->rbuf+1, fptr->rbuf, char, fptr->rbuf_len);
+        fptr->rbuf_off = 1;
+    }
+    fptr->rbuf_off--;
+    fptr->rbuf_len++;
+    fptr->rbuf[fptr->rbuf_off] = c;
+    return c;
+}
+
 static OpenFile *
 flush_before_seek(fptr)
     OpenFile *fptr;
 {
-    if (fptr->mode & FMODE_WBUF) {
-	io_fflush(GetWriteFile(fptr), fptr);
-    }
+    io_fflush(fptr);
+    io_unread(fptr);
     return fptr;
 }
 
-#define io_seek(fptr, ofs, whence) fseeko(flush_before_seek(fptr)->f, ofs, whence)
-#define io_tell(fptr) ftello(flush_before_seek(fptr)->f)
+#define io_seek(fptr, ofs, whence) lseek(flush_before_seek(fptr)->fd, ofs, whence)
+#define io_tell(fptr) lseek(flush_before_seek(fptr)->fd, 0, SEEK_CUR)
 
 #ifndef SEEK_CUR
 # define SEEK_SET 0
@@ -242,15 +249,12 @@ rb_io_check_readable(fptr)
     OpenFile *fptr;
 {
     rb_io_check_closed(fptr);
-#if NEED_IO_SEEK_BETWEEN_RW
-    if (((fptr->mode & FMODE_WBUF) ||
-	 (fptr->mode & (FMODE_SYNCWRITE|FMODE_RBUF)) == FMODE_SYNCWRITE) &&
-	!feof(fptr->f) &&
-	!fptr->f2) {
-	io_seek(fptr, 0, SEEK_CUR);
+    if (!(fptr->mode & FMODE_READABLE)) {
+	rb_raise(rb_eIOError, "not opened for reading");
+    }
+    if (fptr->wbuf_len) {
+        io_fflush(fptr);
     }
-#endif
-    fptr->mode |= FMODE_RBUF;
 }
 
 void
@@ -261,11 +265,8 @@ rb_io_check_writable(fptr)
     if (!(fptr->mode & FMODE_WRITABLE)) {
 	rb_raise(rb_eIOError, "not opened for writing");
     }
-    if ((fptr->mode & FMODE_RBUF) && !feof(fptr->f) && !fptr->f2) {
-	io_seek(fptr, 0, SEEK_CUR);
-    }
-    if (!fptr->f2) {
-	fptr->mode &= ~FMODE_RBUF;
+    if (fptr->rbuf_len) {
+        io_unread(fptr);
     }
 }
 
@@ -273,16 +274,35 @@ int
 rb_read_pending(fp)
     FILE *fp;
 {
-    return READ_DATA_PENDING(fp);
+    /* xxx: return READ_DATA_PENDING(fp); */
+    return 1;
+}
+
+int
+rb_io_read_pending(OpenFile *fptr)
+{
+    return READ_DATA_PENDING(fptr);
 }
 
 void
 rb_read_check(fp)
     FILE *fp;
 {
+    /* xxx:
     if (!READ_DATA_PENDING(fp)) {
 	rb_thread_wait_fd(fileno(fp));
     }
+    */
+    return;
+}
+
+void
+rb_io_read_check(OpenFile *fptr)
+{
+    if (!READ_DATA_PENDING(fptr)) {
+	rb_thread_wait_fd(fptr->fd);
+    }
+    return;
 }
 
 static int
@@ -317,26 +337,39 @@ io_alloc(klass)
     return (VALUE)io;
 }
 
-static void
-io_fflush(f, fptr)
-    FILE *f;
+static int
+io_fflush(fptr)
     OpenFile *fptr;
 {
-    int n;
+    int r;
 
-    if (!rb_thread_fd_writable(fileno(f))) {
+    rb_io_check_closed(fptr);
+    if (fptr->wbuf_len == 0)
+        return 0;
+    if (!rb_thread_fd_writable(fptr->fd)) {
         rb_io_check_closed(fptr);
     }
-    for (;;) {
-	TRAP_BEG;
-	n = fflush(f);
-	TRAP_END;
-	if (n != EOF) break;
-	if (!rb_io_wait_writable(fileno(f)))
-	    rb_sys_fail(fptr->path);
+  retry:
+    if (fptr->wbuf_len == 0)
+        return 0;
+    TRAP_BEG;
+    r = write(fptr->fd, fptr->wbuf+fptr->wbuf_off, fptr->wbuf_len);
+    TRAP_END; /* xxx: signal handler may change wbuf. */
+    if (r == fptr->wbuf_len) {
+        fptr->wbuf_off = 0;
+        fptr->wbuf_len = 0;
+        return 0;
+    }
+    if (0 <= r) {
+        fptr->wbuf_off += r;
+        fptr->wbuf_len -= r;
+        errno = EAGAIN;
+    }
+    if (rb_io_wait_writable(fptr->fd)) {
         rb_io_check_closed(fptr);
+        goto retry;
     }
-    fptr->mode &= ~FMODE_WBUF;
+    return -1;
 }
 
 int
@@ -403,55 +436,50 @@ io_fwrite(ptr, len, fptr)
     OpenFile *fptr;
 {
     long n, r;
-    FILE *f = GetWriteFile(fptr);
 
     if ((n = len) <= 0) return n;
-    if (fptr->mode & FMODE_SYNC) {
-	io_fflush(f, fptr);
-	if (!rb_thread_fd_writable(fileno(f))) {
+    if (fptr->wbuf == NULL && !(fptr->mode & FMODE_SYNC)) {
+        fptr->wbuf_off = 0;
+        fptr->wbuf_len = 0;
+        fptr->wbuf_capa = 8192;
+        fptr->wbuf = ALLOC_N(char, fptr->wbuf_capa);
+    }
+    if ((fptr->mode & FMODE_SYNC) ||
+        ((fptr->mode & FMODE_LINEBUF) && memchr(ptr, '\n', len)) ||
+        (fptr->wbuf && fptr->wbuf_capa <= fptr->wbuf_len + len)) {
+        /* xxx: avoid double write:
+         1. use writev if available
+         2. copy given data to the buffer if buffer has enough space. */
+        if (io_fflush(fptr) < 0)
+            return -1L;
+	if (!rb_thread_fd_writable(fptr->fd)) {
 	    rb_io_check_closed(fptr);
 	}
       retry:
-	r = write(fileno(f), ptr, n);
+        TRAP_BEG;
+	r = write(fptr->fd, ptr, n);
+        TRAP_END; /* xxx: signal handler may change wbuf. */
         if (r == n) return len;
         if (0 <= r) {
             ptr += r;
             n -= r;
             errno = EAGAIN;
         }
-        if (rb_io_wait_writable(fileno(f))) {
+        if (rb_io_wait_writable(fptr->fd)) {
             rb_io_check_closed(fptr);
             goto retry;
         }
         return -1L;
     }
-#if defined __human68k__
-    do {
-	if (fputc(*ptr++, f) == EOF) {
-	    if (ferror(f)) return -1L;
-	    break;
-	}
-    } while (--n > 0);
-#else
-    while (errno = 0, ptr += (r = fwrite(ptr, 1, n, f)), (n -= r) > 0) {
-	if (ferror(f)
-#if defined __BORLANDC__
-	    || errno
-#endif
-	) {
-#ifdef __hpux
-	    if (!errno) errno = EAGAIN;
-#endif
-	    if (rb_io_wait_writable(fileno(f))) {
-		rb_io_check_closed(fptr);
-		clearerr(f);
-		continue;
-	    }
-	    return -1L;
-	}
-    }
-#endif
-    return len - n;
+
+    if (fptr->wbuf_off) {
+        if (fptr->wbuf_len)
+            MEMMOVE(fptr->wbuf, fptr->wbuf+fptr->wbuf_off, char, fptr->wbuf_len);
+        fptr->wbuf_off = 0;
+    }
+    MEMMOVE(fptr->wbuf+fptr->wbuf_off+fptr->wbuf_len, ptr, char, len);
+    fptr->wbuf_len += len;
+    return len;
 }
 
 long
@@ -462,8 +490,8 @@ rb_io_fwrite(ptr, len, f)
 {
     OpenFile of;
 
+    of.fd = fileno(f);
     of.f = f;
-    of.f2 = NULL;
     of.mode = FMODE_WRITABLE;
     of.path = NULL;
     return io_fwrite(ptr, len, &of);
@@ -512,9 +540,6 @@ io_write(io, str)
     n = io_fwrite(RSTRING(str)->ptr, RSTRING(str)->len, fptr);
     rb_str_unlocktmp(str);
     if (n == -1L) rb_sys_fail(fptr->path);
-    if (!(fptr->mode & FMODE_SYNC)) {
-	fptr->mode |= FMODE_WBUF;
-    }
 
     return LONG2FIX(n);
 }
@@ -566,23 +591,24 @@ rb_io_addstr(io, str)
  *     no newline
  */
 
-static VALUE
+VALUE
 rb_io_flush(io)
     VALUE io;
 {
     OpenFile *fptr;
-    FILE *f;
 
     GetOpenFile(io, fptr);
-    rb_io_check_writable(fptr);
-    f = GetWriteFile(fptr);
 
-    io_fflush(f, fptr);
+    if (fptr->mode & FMODE_WRITABLE) {
+        io_fflush(fptr);
+    }
+    if (fptr->mode & FMODE_READABLE) {
+        io_unread(fptr);
+    }
 
     return io;
 }
 
-
 /*
  *  call-seq:
  *     ios.pos     => integer
@@ -621,7 +647,6 @@ rb_io_seek(io, offset, whence)
     GetOpenFile(io, fptr);
     pos = io_seek(fptr, pos, whence);
     if (pos < 0) rb_sys_fail(fptr->path);
-    clearerr(fptr->f);
 
     return INT2FIX(0);
 }
@@ -685,7 +710,6 @@ rb_io_set_pos(io, offset)
     GetOpenFile(io, fptr);
     pos = io_seek(fptr, pos, SEEK_SET);
     if (pos < 0) rb_sys_fail(fptr->path);
-    clearerr(fptr->f);
 
     return OFFT2NUM(pos);
 }
@@ -712,7 +736,6 @@ rb_io_rewind(io)
 
     GetOpenFile(io, fptr);
     if (io_seek(fptr, 0L, 0) < 0) rb_sys_fail(fptr->path);
-    clearerr(fptr->f);
     if (io == current_file) {
 	gets_lineno -= fptr->lineno;
     }
@@ -721,6 +744,36 @@ rb_io_rewind(io)
     return INT2FIX(0);
 }
 
+static int
+io_getc(OpenFile *fptr)
+{
+    int r;
+    if (fptr->rbuf == NULL) {
+        fptr->rbuf_off = 0;
+        fptr->rbuf_len = 0;
+        fptr->rbuf_capa = 8192;
+        fptr->rbuf = ALLOC_N(char, fptr->rbuf_capa);
+    }
+    if (fptr->rbuf_len == 0) {
+        fptr->rbuf_off = 0;
+      retry:
+        TRAP_BEG;
+        r = read(fptr->fd, fptr->rbuf, fptr->rbuf_capa);
+        TRAP_END; /* xxx: signal handler may change rbuf */
+        if (r < 0) {
+            if (rb_io_wait_readable(fptr->fd))
+                goto retry;
+            rb_sys_fail(fptr->path);
+        }
+        fptr->rbuf_len = r;
+        if (r == 0)
+            return -1; /* EOF */
+    }
+    fptr->rbuf_off++;
+    fptr->rbuf_len--;
+    return (unsigned char)fptr->rbuf[fptr->rbuf_off-1];
+}
+
 /*
  *  call-seq:
  *     ios.eof     => true or false
@@ -744,18 +797,16 @@ rb_io_eof(io)
     GetOpenFile(io, fptr);
     rb_io_check_readable(fptr);
 
-    if (feof(fptr->f)) return Qtrue;
-    if (READ_DATA_PENDING(fptr->f)) return Qfalse;
-    READ_CHECK(fptr->f);
+    if (READ_DATA_PENDING(fptr)) return Qfalse;
+    READ_CHECK(fptr);
     TRAP_BEG;
-    ch = getc(fptr->f);
+    ch = io_getc(fptr);
     TRAP_END;
 
     if (ch != EOF) {
-	ungetc(ch, fptr->f);
+	io_ungetc(ch, fptr);
 	return Qfalse;
     }
-    clearerr(fptr->f);
     return Qtrue;
 }
 
@@ -831,13 +882,11 @@ rb_io_fsync(io)
 {
 #ifdef HAVE_FSYNC
     OpenFile *fptr;
-    FILE *f;
 
     GetOpenFile(io, fptr);
-    f = GetWriteFile(fptr);
 
-    io_fflush(f, fptr);
-    if (fsync(fileno(f)) < 0)
+    io_fflush(fptr);
+    if (fsync(fptr->fd) < 0)
 	rb_sys_fail(fptr->path);
     return INT2FIX(0);
 #else
@@ -866,7 +915,7 @@ rb_io_fileno(io)
     int fd;
 
     GetOpenFile(io, fptr);
-    fd = fileno(fptr->f);
+    fd = fptr->fd;
     return INT2FIX(fd);
 }
 
@@ -923,7 +972,7 @@ rb_io_inspect(obj)
     if (!fptr || !fptr->path) return rb_any_to_s(obj);
     cname = rb_obj_classname(obj);
     len = strlen(cname) + strlen(fptr->path) + 5;
-    if (!(fptr->f || fptr->f2)) {
+    if (!fptr->f) {
 	st = " (closed)";
 	len += 9;
     }
@@ -948,24 +997,17 @@ rb_io_to_io(io)
 
 /* reading functions */
 static long
-read_buffered_data(ptr, len, f)
-    char *ptr;
-    long len;
-    FILE *f;
+read_buffered_data(char *ptr, long len, OpenFile *fptr)
 {
     long n;
 
-#ifdef READ_DATA_PENDING_COUNT
-    n = READ_DATA_PENDING_COUNT(f);
+    n = READ_DATA_PENDING_COUNT(fptr);
     if (n <= 0) return 0;
     if (n > len) n = len;
-    return fread(ptr, 1, n, f);
-#else
-    for (n = 0; n < len && READ_DATA_PENDING(f); ++n) {
-	*ptr++ = getc(f);
-    }
+    MEMMOVE(ptr, fptr->rbuf+fptr->rbuf_off, char, n);
+    fptr->rbuf_off += n;
+    fptr->rbuf_len -= n;
     return n;
-#endif
 }
 
 long
@@ -978,37 +1020,17 @@ io_fread(ptr, len, fptr)
     int c;
 
     while (n > 0) {
-	c = read_buffered_data(ptr, n, fptr->f);
-	if (c < 0) goto eof;
+	c = read_buffered_data(ptr, n, fptr);
 	if (c > 0) {
 	    ptr += c;
 	    if ((n -= c) <= 0) break;
 	}
-	rb_thread_wait_fd(fileno(fptr->f));
+	rb_thread_wait_fd(fptr->fd);
 	rb_io_check_closed(fptr);
 	TRAP_BEG;
-	c = getc(fptr->f);
+	c = io_getc(fptr);
 	TRAP_END;
-	if (c == EOF) {
-	  eof:
-	    if (ferror(fptr->f)) {
-		switch (errno) {
-		  case EINTR:
-#if defined(ERESTART)
-		  case ERESTART:
-#endif
-		    clearerr(fptr->f);
-		    continue;
-		  case EAGAIN:
-#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
-		  case EWOULDBLOCK:
-#endif
-		    if (len > n) {
-			clearerr(fptr->f);
-		    }
-		}
-		if (len == n) return 0;
-	    }
+	if (c < 0) {
 	    break;
 	}
 	*ptr++ = c;
@@ -1025,8 +1047,8 @@ rb_io_fread(ptr, len, f)
 {
     OpenFile of;
 
+    of.fd = fileno(f);
     of.f = f;
-    of.f2 = NULL;
     of.mode = FMODE_READABLE;
     return io_fread(ptr, len, &of);
 }
@@ -1045,8 +1067,7 @@ remain_size(fptr)
     off_t siz = BUFSIZ;
     off_t pos;
 
-    if (feof(fptr->f)) return 0;
-    if (fstat(fileno(fptr->f), &st) == 0  && S_ISREG(st.st_mode)
+    if (fstat(fptr->fd, &st) == 0  && S_ISREG(st.st_mode)
 #ifdef __BEOS__
 	&& (st.st_dev > 3)
 #endif
@@ -1081,14 +1102,11 @@ read_all(fptr, siz, str)
     }
     for (;;) {
 	rb_str_locktmp(str);
-	READ_CHECK(fptr->f);
+	READ_CHECK(fptr);
 	n = io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr);
 	rb_str_unlocktmp(str);
 	if (n == 0 && bytes == 0) {
-	    if (!fptr->f) break;
-	    if (feof(fptr->f)) break;
-	    if (!ferror(fptr->f)) break;
-	    rb_sys_fail(fptr->path);
+            break;
 	}
 	bytes += n;
 	if (bytes < siz) break;
@@ -1186,20 +1204,20 @@ io_readpartial(argc, argv, io)
     if (len == 0)
 	return str;
 
-    READ_CHECK(fptr->f);
+    READ_CHECK(fptr);
     if (RSTRING(str)->len != len) {
       modified:
 	rb_raise(rb_eRuntimeError, "buffer string modified");
     }
-    n = read_buffered_data(RSTRING(str)->ptr, len, fptr->f);
+    n = read_buffered_data(RSTRING(str)->ptr, len, fptr);
     if (n <= 0) {
       again:
 	if (RSTRING(str)->len != len) goto modified;
         TRAP_BEG;
-        n = read(fileno(fptr->f), RSTRING(str)->ptr, len);
+        n = read(fptr->fd, RSTRING(str)->ptr, len);
         TRAP_END;
         if (n < 0) {
-            if (rb_io_wait_readable(fileno(fptr->f)))
+            if (rb_io_wait_readable(fptr->fd))
                 goto again;
             rb_sys_fail(fptr->path);
         }
@@ -1260,11 +1278,10 @@ io_read(argc, argv, io)
 
     GetOpenFile(io, fptr);
     rb_io_check_readable(fptr);
-    if (feof(fptr->f)) return Qnil;
     if (len == 0) return str;
 
     rb_str_locktmp(str);
-    READ_CHECK(fptr->f);
+    READ_CHECK(fptr);
     if (RSTRING(str)->len != len) {
 	rb_raise(rb_eRuntimeError, "buffer string modified");
     }
@@ -1272,11 +1289,8 @@ io_read(argc, argv, io)
     rb_str_unlocktmp(str);
     if (n == 0) {
 	if (!fptr->f) return Qnil;
-	if (feof(fptr->f)) {
-	    rb_str_resize(str, 0);
-	    return Qnil;
-	}
-	if (len > 0) rb_sys_fail(fptr->path);
+        rb_str_resize(str, 0);
+        return Qnil;
     }
     rb_str_resize(str, n);
     RSTRING(str)->len = n;
@@ -1292,20 +1306,13 @@ appendline(fptr, delim, strp)
     int delim;
     VALUE *strp;
 {
-    FILE *f = fptr->f;
     VALUE str = *strp;
     int c = EOF;
-#ifndef READ_DATA_PENDING_PTR
-    char buf[8192];
-    char *bp = buf, *bpe = buf + sizeof buf - 3;
-    int update = Qfalse;
-#endif
 
     do {
-#ifdef READ_DATA_PENDING_PTR
-	long pending = READ_DATA_PENDING_COUNT(f);
+	long pending = READ_DATA_PENDING_COUNT(fptr);
 	if (pending > 0) {
-	    const char *p = READ_DATA_PENDING_PTR(f);
+	    const char *p = READ_DATA_PENDING_PTR(fptr);
 	    const char *e = memchr(p, delim, pending);
 	    long last = 0, len = (c != EOF);
 	    if (e) pending = e - p + 1;
@@ -1322,7 +1329,7 @@ appendline(fptr, delim, strp)
 	    if (c != EOF) {
 		RSTRING(str)->ptr[last++] = c;
 	    }
-	    fread(RSTRING(str)->ptr + last, 1, pending, f); /* must not fail */
+	    read_buffered_data(RSTRING(str)->ptr + last, pending, fptr); /* must not fail */
 	    if (e) return delim;
 	}
 	else if (c != EOF) {
@@ -1335,47 +1342,16 @@ appendline(fptr, delim, strp)
 		RSTRING(str)->ptr[RSTRING(str)->len++] = c;
 	    }
 	}
-	rb_thread_wait_fd(fileno(f));
+	rb_thread_wait_fd(fptr->fd);
 	rb_io_check_closed(fptr);
-#else
-	READ_CHECK(f);
-#endif
 	TRAP_BEG;
-	c = getc(f);
+	c = io_getc(fptr);
 	TRAP_END;
-	if (c == EOF) {
-	    if (ferror(f)) {
-		clearerr(f);
-		if (!rb_io_wait_readable(fileno(f)))
-		    rb_sys_fail(fptr->path);
-		continue;
-	    }
-#ifdef READ_DATA_PENDING_PTR
+	if (c < 0) {
 	    return c;
-#endif
 	}
-#ifndef READ_DATA_PENDING_PTR
-	if (c == EOF || (*bp++ = c) == delim || bp == bpe) {
-	    int cnt = bp - buf;
-
-	    if (cnt > 0) {
-		if (!NIL_P(str))
-		    rb_str_cat(str, buf, cnt);
-		else
-		    *strp = str = rb_str_new(buf, cnt);
-	    }
-	    if (c == EOF) {
-		if (update)
-		    return (int)RSTRING(str)->ptr[RSTRING(str)->len-1];
-		return c;
-	    }
-	    bp = buf;
-	}
-	update = Qtrue;
-#endif
     } while (c != delim);
 
-#ifdef READ_DATA_PENDING_PTR
     {
 	char ch = c;
 	if (!NIL_P(str)) {
@@ -1385,7 +1361,6 @@ appendline(fptr, delim, strp)
 	    *strp = str = rb_str_new(&ch, 1);
 	}
     }
-#endif
 
     return c;
 }
@@ -1395,33 +1370,28 @@ swallow(fptr, term)
     OpenFile *fptr;
     int term;
 {
-    FILE *f = fptr->f;
     int c;
 
     do {
-#ifdef READ_DATA_PENDING_PTR
 	long cnt;
-	while ((cnt = READ_DATA_PENDING_COUNT(f)) > 0) {
+	while ((cnt = READ_DATA_PENDING_COUNT(fptr)) > 0) {
 	    char buf[1024];
-	    const char *p = READ_DATA_PENDING_PTR(f);
+	    const char *p = READ_DATA_PENDING_PTR(fptr);
 	    int i;
 	    if (cnt > sizeof buf) cnt = sizeof buf;
 	    if (*p != term) return Qtrue;
 	    i = cnt;
 	    while (--i && *++p == term);
-	    if (!fread(buf, 1, cnt - i, f)) /* must not fail */
+	    if (!read_buffered_data(buf, cnt - i, fptr)) /* must not fail */
 		rb_sys_fail(fptr->path);
 	}
-	rb_thread_wait_fd(fileno(f));
+	rb_thread_wait_fd(fptr->fd);
 	rb_io_check_closed(fptr);
-#else
-	READ_CHECK(f);
-#endif
 	TRAP_BEG;
-	c = getc(f);
+	c = io_getc(fptr);
 	TRAP_END;
 	if (c != term) {
-	    ungetc(c, f);
+	    io_ungetc(c, fptr);
 	    return Qtrue;
 	}
     } while (c != EOF);
@@ -1782,22 +1752,15 @@ rb_io_each_byte(io)
     for (;;) {
 	rb_io_check_readable(fptr);
 	f = fptr->f;
-	READ_CHECK(f);
+	READ_CHECK(fptr);
 	TRAP_BEG;
-	c = getc(f);
+	c = io_getc(fptr);
 	TRAP_END;
-	if (c == EOF) {
-	    if (ferror(f)) {
-		clearerr(f);
-		if (!rb_io_wait_readable(fileno(f)))
-		    rb_sys_fail(fptr->path);
-		continue;
-	    }
+	if (c < 0) {
 	    break;
 	}
 	rb_yield(INT2FIX(c & 0xff));
     }
-    if (ferror(f)) rb_sys_fail(fptr->path);
     return io;
 }
 
@@ -1825,19 +1788,12 @@ rb_io_getc(io)
     rb_io_check_readable(fptr);
     f = fptr->f;
 
-  retry:
-    READ_CHECK(f);
+    READ_CHECK(fptr);
     TRAP_BEG;
-    c = getc(f);
+    c = io_getc(fptr);
     TRAP_END;
 
-    if (c == EOF) {
-	if (ferror(f)) {
-	    clearerr(f);
-	    if (!rb_io_wait_readable(fileno(f)))
-		rb_sys_fail(fptr->path);
-	    goto retry;
-	}
+    if (c < 0) {
 	return Qnil;
     }
     return INT2FIX(c & 0xff);
@@ -1847,6 +1803,7 @@ int
 rb_getc(f)
     FILE *f;
 {
+    /*xxx
     int c;
 
     if (!READ_DATA_PENDING(f)) {
@@ -1857,6 +1814,8 @@ rb_getc(f)
     TRAP_END;
 
     return c;
+    */
+    return -1;
 }
 
 /*
@@ -1903,11 +1862,9 @@ rb_io_ungetc(io, c)
     int cc = NUM2INT(c);
 
     GetOpenFile(io, fptr);
-    if (!(fptr->mode & FMODE_RBUF))
-	rb_raise(rb_eIOError, "unread stream");
     rb_io_check_readable(fptr);
 
-    if (ungetc(cc, fptr->f) == EOF && cc != EOF) {
+    if (io_ungetc(cc, fptr) == EOF && cc != EOF) {
 	rb_raise(rb_eIOError, "ungetc failed");
     }
     return Qnil;
@@ -1932,7 +1889,7 @@ rb_io_isatty(io)
     OpenFile *fptr;
 
     GetOpenFile(io, fptr);
-    if (isatty(fileno(fptr->f)) == 0)
+    if (isatty(fptr->fd) == 0)
 	return Qfalse;
     return Qtrue;
 }
@@ -1942,43 +1899,15 @@ fptr_finalize(fptr, noraise)
     OpenFile *fptr;
     int noraise;
 {
-    int n1 = 0, n2 = 0, f1, f2 = -1;
-
-    if (fptr->f2) {
-	f2 = fileno(fptr->f2);
-	while (n2 = 0, fflush(fptr->f2) < 0) {
-	    n2 = errno;
-	    if (!rb_io_wait_writable(f2)) {
-		break;
-	    }
-	    if (!fptr->f2) break;
-	}
-	if (fclose(fptr->f2) < 0 && n2 == 0) {
-	    n2 = errno;
-	}
-	fptr->f2 = 0;
-    }
-    if (fptr->f) {
-	f1 = fileno(fptr->f);
-	if ((f2 == -1) && (fptr->mode & FMODE_WBUF)) {
-	    while (n1 = 0, fflush(fptr->f) < 0) {
-		n1 = errno;
-		if (!rb_io_wait_writable(f1)) break;
-		if (!fptr->f) break;
-	    }
-	}
-	if (fclose(fptr->f) < 0 && n1 == 0) {
-	    n1 = errno;
-	}
-	fptr->f = 0;
-	if (n1 == EBADF && f1 == f2) {
-	    n1 = 0;
-	}
+    if (fptr->wbuf_len) {
+        io_fflush(fptr);
     }
-    if (!noraise && (n1 || n2)) {
-	errno = (n1 ? n1 : n2);
+    if (fptr->f && fclose(fptr->f) < 0 && !noraise) {
 	rb_sys_fail(fptr->path);
     }
+    fptr->fd = -1;
+    fptr->f = 0;
+    fptr->mode &= ~(FMODE_READABLE|FMODE_WRITABLE);
 }
 
 static void
@@ -1999,16 +1928,24 @@ rb_io_fptr_finalize(fptr)
     OpenFile *fptr;
 {
     if (!fptr) return 0;
+    /*
     if (fptr->f == stdin || fptr->f == stdout || fptr->f == stderr) {
 	return 0;
     }
+    */
     if (fptr->refcnt <= 0 || --fptr->refcnt) return 0;
     if (fptr->path) {
 	free(fptr->path);
 	fptr->path = 0;
     }
-    if ((fptr->f && fileno(fptr->f) > 2) || fptr->f2) {
-	rb_io_fptr_cleanup(fptr, Qtrue);
+    rb_io_fptr_cleanup(fptr, Qtrue);
+    if (fptr->rbuf) {
+        free(fptr->rbuf);
+        fptr->rbuf = 0;
+    }
+    if (fptr->wbuf) {
+        free(fptr->wbuf);
+        fptr->wbuf = 0;
     }
     free(fptr);
     return 1;
@@ -2019,22 +1956,15 @@ rb_io_close(io)
     VALUE io;
 {
     OpenFile *fptr;
-    int fd, fd2;
+    int fd;
 
     fptr = RFILE(io)->fptr;
     if (!fptr) return Qnil;
-    if (fptr->f2) {
-	fd2 = fileno(fptr->f2);
-    }
-    else {
-	if (!fptr->f) return Qnil;
-	fd2 = -1;
-    }
+    if (!fptr->f) return Qnil;
 
-    fd = fileno(fptr->f);
+    fd = fptr->fd;
     rb_io_fptr_cleanup(fptr, Qfalse);
     rb_thread_fd_close(fd);
-    if (fd2 >= 0) rb_thread_fd_close(fd2);
 
     if (fptr->pid) {
 	rb_syswait(fptr->pid);
@@ -2101,7 +2031,7 @@ rb_io_closed(io)
 
     fptr = RFILE(io)->fptr;
     rb_io_check_initialized(fptr);
-    return (fptr->f || fptr->f2)?Qfalse:Qtrue;
+    return fptr->f ? Qfalse : Qtrue;
 }
 
 /*
@@ -2127,25 +2057,26 @@ rb_io_close_read(io)
     VALUE io;
 {
     OpenFile *fptr;
-    int n;
+    struct stat sbuf;
 
     if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
 	rb_raise(rb_eSecurityError, "Insecure: can't close");
     }
     GetOpenFile(io, fptr);
-    if (fptr->f2 == 0 && (fptr->mode & FMODE_WRITABLE)) {
-	rb_raise(rb_eIOError, "closing non-duplex IO for reading");
+    if (fstat(fptr->fd, &sbuf) < 0)
+        rb_sys_fail(fptr->path);
+    if (S_ISSOCK(sbuf.st_mode)) {
+        if (shutdown(fptr->fd, 0) < 0)
+            rb_sys_fail(fptr->path);
+        fptr->mode &= ~FMODE_READABLE;
+        if (!(fptr->mode & FMODE_WRITABLE))
+            return rb_io_close(io);
+        return Qnil;
     }
-    if (fptr->f2 == 0) {
-	return rb_io_close(io);
+    if (fptr->mode & FMODE_WRITABLE) {
+	rb_raise(rb_eIOError, "closing non-duplex IO for reading");
     }
-    n = fclose(fptr->f);
-    fptr->mode &= ~FMODE_READABLE;
-    fptr->f = fptr->f2;
-    fptr->f2 = 0;
-    if (n < 0) rb_sys_fail(fptr->path);
-
-    return Qnil;
+    return rb_io_close(io);
 }
 
 /*
@@ -2172,24 +2103,27 @@ rb_io_close_write(io)
     VALUE io;
 {
     OpenFile *fptr;
-    int n;
+    struct stat sbuf;
 
     if (rb_safe_level() >= 4 && !OBJ_TAINTED(io)) {
 	rb_raise(rb_eSecurityError, "Insecure: can't close");
     }
     GetOpenFile(io, fptr);
-    if (fptr->f2 == 0 && (fptr->mode & FMODE_READABLE)) {
-	rb_raise(rb_eIOError, "closing non-duplex IO for writing");
-    }
-    if (fptr->f2 == 0) {
-	return rb_io_close(io);
+    if (fstat(fptr->fd, &sbuf) < 0)
+        rb_sys_fail(fptr->path);
+    if (S_ISSOCK(sbuf.st_mode)) {
+        if (shutdown(fptr->fd, 1) < 0)
+            rb_sys_fail(fptr->path);
+        fptr->mode &= ~FMODE_WRITABLE;
+        if (!(fptr->mode & FMODE_READABLE))
+            return rb_io_close(io);
+        return Qnil;
     }
-    n = fclose(fptr->f2);
-    fptr->f2 = 0;
-    fptr->mode &= ~FMODE_WRITABLE;
-    if (n < 0) rb_sys_fail(fptr->path);
 
-    return Qnil;
+    if (fptr->mode & FMODE_READABLE) {
+	rb_raise(rb_eIOError, "closing non-duplex IO for writing");
+    }
+    return rb_io_close(io);
 }
 
 /*
@@ -2221,15 +2155,14 @@ rb_io_sysseek(argc, argv, io)
     }
     pos = NUM2OFFT(offset);
     GetOpenFile(io, fptr);
-    if ((fptr->mode & FMODE_READABLE) && READ_DATA_BUFFERED(fptr->f)) {
+    if ((fptr->mode & FMODE_READABLE) && READ_DATA_BUFFERED(fptr)) {
 	rb_raise(rb_eIOError, "sysseek for buffered IO");
     }
-    if ((fptr->mode & FMODE_WRITABLE) && (fptr->mode & FMODE_WBUF)) {
+    if ((fptr->mode & FMODE_WRITABLE) && fptr->wbuf_len) {
 	rb_warn("sysseek for buffered IO");
     }
-    pos = lseek(fileno(fptr->f), pos, whence);
+    pos = lseek(fptr->fd, pos, whence);
     if (pos == -1) rb_sys_fail(fptr->path);
-    clearerr(fptr->f);
 
     return OFFT2NUM(pos);
 }
@@ -2252,7 +2185,6 @@ rb_io_syswrite(io, str)
     VALUE io, str;
 {
     OpenFile *fptr;
-    FILE *f;
     long n;
 
     rb_secure(4);
@@ -2261,15 +2193,14 @@ rb_io_syswrite(io, str)
 
     GetOpenFile(io, fptr);
     rb_io_check_writable(fptr);
-    f = GetWriteFile(fptr);
 
-    if (fptr->mode & FMODE_WBUF) {
+    if (fptr->wbuf_len) {
 	rb_warn("syswrite for buffered IO");
     }
-    if (!rb_thread_fd_writable(fileno(f))) {
+    if (!rb_thread_fd_writable(fptr->fd)) {
         rb_io_check_closed(fptr);
     }
-    n = write(fileno(f), RSTRING(str)->ptr, RSTRING(str)->len);
+    n = write(fptr->fd, RSTRING(str)->ptr, RSTRING(str)->len);
 
     if (n == -1) rb_sys_fail(fptr->path);
 
@@ -2318,19 +2249,19 @@ rb_io_sysread(argc, argv, io)
     GetOpenFile(io, fptr);
     rb_io_check_readable(fptr);
 
-    if (READ_DATA_BUFFERED(fptr->f)) {
+    if (READ_DATA_BUFFERED(fptr)) {
 	rb_raise(rb_eIOError, "sysread for buffered IO");
     }
     rb_str_locktmp(str);
 
-    n = fileno(fptr->f);
-    rb_thread_wait_fd(fileno(fptr->f));
+    n = fptr->fd;
+    rb_thread_wait_fd(fptr->fd);
     rb_io_check_closed(fptr);
     if (RSTRING(str)->len != ilen) {
 	rb_raise(rb_eRuntimeError, "buffer string modified");
     }
     TRAP_BEG;
-    n = read(fileno(fptr->f), RSTRING(str)->ptr, ilen);
+    n = read(fptr->fd, RSTRING(str)->ptr, ilen);
     TRAP_END;
 
     rb_str_unlocktmp(str);
@@ -2365,18 +2296,14 @@ rb_io_binmode(io)
     OpenFile *fptr;
 
     GetOpenFile(io, fptr);
-    if (!(fptr->mode & FMODE_BINMODE) && READ_DATA_BUFFERED(fptr->f)) {
+    if (!(fptr->mode & FMODE_BINMODE) && READ_DATA_BUFFERED(fptr)) {
 	rb_raise(rb_eIOError, "buffer already filled with text-mode content");
     }
 #ifdef __human68k__
     if (fptr->f)
 	fmode(fptr->f, _IOBIN);
-    if (fptr->f2)
-	fmode(fptr->f2, _IOBIN);
 #else
-    if (fptr->f && setmode(fileno(fptr->f), O_BINARY) == -1)
-	rb_sys_fail(fptr->path);
-    if (fptr->f2 && setmode(fileno(fptr->f2), O_BINARY) == -1)
+    if (fptr->f && setmode(fptr->fd, O_BINARY) == -1)
 	rb_sys_fail(fptr->path);
 #endif
 
@@ -2636,6 +2563,7 @@ rb_fdopen(fd, mode)
 	}
     }
 
+    /* xxx: should be _IONBF?  A buffer in FILE may have trouble. */
 #ifdef USE_SETVBUF
     if (setvbuf(file, NULL, _IOFBF, 0) != 0)
 	rb_warn("setvbuf() can't be honoured (fd=%d)", fd);
@@ -2653,7 +2581,10 @@ rb_file_open_internal(io, fname, mode)
     MakeOpenFile(io, fptr);
     fptr->mode = rb_io_mode_flags(mode);
     fptr->path = strdup(fname);
-    fptr->f = rb_fopen(fptr->path, rb_io_flags_mode(fptr->mode));
+    fptr->fd = rb_sysopen(fptr->path, rb_io_mode_modenum(rb_io_flags_mode(fptr->mode)), 0666);
+    fptr->f = rb_fdopen(fptr->fd, rb_io_flags_mode(fptr->mode));
+    if ((fptr->mode & FMODE_WRITABLE) && isatty(fptr->fd))
+        fptr->mode |= FMODE_LINEBUF;
 
     return io;
 }
@@ -2672,7 +2603,6 @@ rb_file_sysopen_internal(io, fname, flag
     int flags, mode;
 {
     OpenFile *fptr;
-    int fd;
     char *m;
 
     MakeOpenFile(io, fptr);
@@ -2680,8 +2610,10 @@ rb_file_sysopen_internal(io, fname, flag
     fptr->path = strdup(fname);
     m = rb_io_modenum_mode(flags);
     fptr->mode = rb_io_modenum_flags(flags);
-    fd = rb_sysopen(fptr->path, flags, mode);
-    fptr->f = rb_fdopen(fd, m);
+    fptr->fd = rb_sysopen(fptr->path, flags, mode);
+    fptr->f = rb_fdopen(fptr->fd, m);
+    if ((fptr->mode & FMODE_WRITABLE) && isatty(fptr->fd))
+        fptr->mode |= FMODE_LINEBUF;
 
     return io;
 }
@@ -2762,10 +2694,8 @@ pipe_finalize(fptr, noraise)
     if (fptr->f) {
 	status = pclose(fptr->f);
     }
-    if (fptr->f2) {
-	status = pclose(fptr->f2);
-    }
-    fptr->f = fptr->f2 = 0;
+    fptr->fd = -1;
+    fptr->f = 0;
 #if defined DJGPP
     status <<= 8;
 #endif
@@ -2793,26 +2723,34 @@ rb_io_unbuffered(fptr)
 
 struct popen_arg {
     struct rb_exec_arg exec;
-    int pr[2], pw[2];
+    int modef;
+    int pair[2];
 };
 
 static void
 popen_redirect(p)
     struct popen_arg *p;
 {
-    if (p->pr[1] != -1) {
-	close(p->pr[0]);
-	if (p->pr[1] != 1) {
-	    dup2(p->pr[1], 1);
-	    close(p->pr[1]);
-	}
+    if ((p->modef & FMODE_READABLE) && (p->modef & FMODE_WRITABLE)) {
+        close(p->pair[0]);
+        dup2(p->pair[1], 0);
+        dup2(p->pair[1], 1);
+        if (2 <= p->pair[1])
+            close(p->pair[1]);
+    }
+    else if (p->modef & FMODE_READABLE) {
+        close(p->pair[0]);
+        if (p->pair[1] != 1) {
+            dup2(p->pair[1], 1);
+            close(p->pair[1]);
+        }
     }
-    if (p->pw[0] != -1) {
-	close(p->pw[1]);
-	if (p->pw[0] != 0) {
-	    dup2(p->pw[0], 0);
-	    close(p->pw[0]);
-	}
+    else {
+        close(p->pair[1]);
+        if (p->pair[0] != 0) {
+            dup2(p->pair[0], 0);
+            close(p->pair[0]);
+        }
     }
 }
 
@@ -2855,6 +2793,7 @@ pipe_open(argc, argv, mode)
     char *exename = NULL;
 #endif
     char *cmd;
+    int fd;
 
     prog = rb_check_argv(argc, argv);
     if (!prog) {
@@ -2866,23 +2805,28 @@ pipe_open(argc, argv, mode)
     cmd = StringValueCStr(prog);
     doexec = (strcmp("-", cmd) != 0);
     if (!doexec) {
+        /* xxx: flush fptr->wbuf */
 	fflush(stdin);		/* is it really needed? */
 	fflush(stdout);
 	fflush(stderr);
     }
-    arg.pr[0] = arg.pr[1] = arg.pw[0] = arg.pw[1] = -1;
-    if ((modef & FMODE_READABLE) && pipe(arg.pr) == -1) {
-	rb_sys_fail(cmd);
+    arg.modef = modef;
+    arg.pair[0] = arg.pair[1] = -1;
+    if ((modef & FMODE_READABLE) && (modef & FMODE_WRITABLE)) {
+        if (socketpair(AF_UNIX, SOCK_STREAM, 0, arg.pair) < 0)
+            rb_sys_fail(cmd);
+    }
+    else if (modef & FMODE_READABLE) {
+        if (pipe(arg.pair) < 0)
+            rb_sys_fail(cmd);
+    }
+    else if (modef & FMODE_WRITABLE) {
+        if (pipe(arg.pair) < 0)
+            rb_sys_fail(cmd);
     }
-    if ((modef & FMODE_WRITABLE) && pipe(arg.pw) == -1) {
-	if (modef & FMODE_READABLE) {
-	    int e = errno;
-	    close(arg.pr[0]); close(arg.pr[1]);
-	    errno = e;
-	}
-	rb_sys_fail(cmd);
+    else {
+        rb_sys_fail(cmd);
     }
-
     if (doexec) {
 	arg.exec.argc = argc;
 	arg.exec.argv = argv;
@@ -2900,16 +2844,26 @@ pipe_open(argc, argv, mode)
     }
 
     /* parent */
-    if (modef & FMODE_READABLE) close(arg.pr[1]);
-    if (modef & FMODE_WRITABLE) close(arg.pw[0]);
     if (pid == -1) {
 	int e = errno;
-	if (modef & FMODE_READABLE) close(arg.pr[0]);
-	if (modef & FMODE_WRITABLE) close(arg.pw[1]);
+	close(arg.pair[0]);
+	close(arg.pair[1]);
 	errno = e;
 	rb_sys_fail(cmd);
     }
-#define PIPE_FDOPEN(i) (rb_fdopen((i?arg.pw:arg.pr)[i], i?"w":"r"))
+    if ((modef & FMODE_READABLE) && (modef & FMODE_WRITABLE)) {
+        close(arg.pair[1]);
+        fd = arg.pair[0];
+    }
+    else if (modef & FMODE_READABLE) {
+        close(arg.pair[1]);
+        fd = arg.pair[0];
+    }
+    else {
+        close(arg.pair[0]);
+        fd = arg.pair[1];
+    }
+#define PIPE_FDOPEN(i) (rb_fdopen(arg.pair[i], i?"w":"r"))
 #elif defined(_WIN32)
     if (argc) {
 	char **args = ALLOCA_N(char *, argc+1);
@@ -2946,21 +2900,24 @@ pipe_open(argc, argv, mode)
     fpr = popen(StringValueCStr(prog), mode);
 
     if (!fpr) rb_sys_fail(RSTRING(prog)->ptr);
+    fd = fileno(fpr);
 #define PIPE_FDOPEN(i) (fpr)
 #endif
 
     port = io_alloc(rb_cIO);
     MakeOpenFile(port, fptr);
+    fptr->fd = fd;
     fptr->mode = modef | FMODE_SYNC;
     fptr->pid = pid;
 
-    if (modef & FMODE_READABLE) {
-	fptr->f = PIPE_FDOPEN(0);
+    if ((modef & FMODE_READABLE) && (modef & FMODE_WRITABLE)) {
+	fptr->f = rb_fdopen(fd, "r+");
+    }
+    else if (modef & FMODE_READABLE) {
+	fptr->f = rb_fdopen(fd, "r");
     }
-    if (modef & FMODE_WRITABLE) {
-	FILE *fpwt = PIPE_FDOPEN(1);
-	if (fptr->f) fptr->f2 = fpwt;
-	else fptr->f = fpwt;
+    else {
+	fptr->f = rb_fdopen(fd, "w");
     }
 #if defined (__CYGWIN__) || !defined(HAVE_FORK)
     fptr->finalize = pipe_finalize;
@@ -3052,6 +3009,7 @@ rb_io_s_popen(argc, argv, klass)
 	/* child */
 	if (rb_block_given_p()) {
 	    rb_yield(Qnil);
+            /* xxx: flush wbuf */
 	    fflush(stdout);
 	    fflush(stderr);
 	    _exit(0);
@@ -3323,14 +3281,11 @@ io_reopen(io, nfile)
     if (orig->mode & FMODE_READABLE) {
 	pos = io_tell(orig);
     }
-    if (orig->f2) {
-	io_fflush(orig->f2, orig);
-    }
-    else if (orig->mode & FMODE_WRITABLE) {
-	io_fflush(orig->f, orig);
+    if (orig->mode & FMODE_WRITABLE) {
+	io_fflush(orig);
     }
     if (fptr->mode & FMODE_WRITABLE) {
-	io_fflush(GetWriteFile(fptr), fptr);
+	io_fflush(fptr);
     }
 
     /* copy OpenFile structure */
@@ -3343,12 +3298,11 @@ io_reopen(io, nfile)
     fptr->finalize = orig->finalize;
 
     mode = rb_io_mode_string(fptr);
-    fd = fileno(fptr->f);
-    fd2 = fileno(orig->f);
+    fd = fptr->fd;
+    fd2 = orig->fd;
     if (fd != fd2) {
 #if !defined __CYGWIN__
 	if (fptr->f == stdin || fptr->f == stdout || fptr->f == stderr) {
-	    clearerr(fptr->f);
 	    /* need to keep stdio objects */
 	    if (dup2(fd2, fd) < 0)
 		rb_sys_fail(orig->path);
@@ -3373,22 +3327,6 @@ io_reopen(io, nfile)
 	}
     }
 
-    if (fptr->f2 && fd != fileno(fptr->f2)) {
-	fd = fileno(fptr->f2);
-	if (!orig->f2) {
-	    fclose(fptr->f2);
-	    rb_thread_fd_close(fd);
-	    fptr->f2 = 0;
-	}
-	else if (fd != (fd2 = fileno(orig->f2))) {
-	    fclose(fptr->f2);
-	    rb_thread_fd_close(fd);
-	    if (dup2(fd2, fd) < 0)
-		rb_sys_fail(orig->path);
-	    fptr->f2 = rb_fdopen(fd, "w");
-	}
-    }
-
     if (fptr->mode & FMODE_BINMODE) {
 	rb_io_binmode(io);
     }
@@ -3451,11 +3389,8 @@ rb_io_reopen(argc, argv, file)
     fptr->path = strdup(RSTRING(fname)->ptr);
     mode = rb_io_flags_mode(fptr->mode);
     if (!fptr->f) {
-	fptr->f = rb_fopen(fptr->path, mode);
-	if (fptr->f2) {
-	    fclose(fptr->f2);
-	    fptr->f2 = 0;
-	}
+        fptr->fd = rb_sysopen(fptr->path, rb_io_mode_modenum(mode), 0666);
+	fptr->f = rb_fdopen(fptr->fd, mode);
 	return file;
     }
 
@@ -3467,12 +3402,6 @@ rb_io_reopen(argc, argv, file)
 	rb_warn("setvbuf() can't be honoured for %s", RSTRING(fname)->ptr);
 #endif
 
-    if (fptr->f2) {
-	if (freopen(RSTRING(fname)->ptr, "w", fptr->f2) == 0) {
-	    rb_sys_fail(fptr->path);
-	}
-    }
-
     return file;
 }
 
@@ -3490,16 +3419,7 @@ rb_io_init_copy(dest, io)
     GetOpenFile(io, orig);
     MakeOpenFile(dest, fptr);
 
-    if (orig->f2) {
-	io_fflush(orig->f2, orig);
-	fseeko(orig->f, 0L, SEEK_CUR);
-    }
-    else if (orig->mode & FMODE_WRITABLE) {
-	io_fflush(orig->f, orig);
-    }
-    else {
-	fseeko(orig->f, 0L, SEEK_CUR);
-    }
+    rb_io_flush(io);
 
     /* copy OpenFile structure */
     fptr->mode = orig->mode;
@@ -3515,20 +3435,12 @@ rb_io_init_copy(dest, io)
       case FMODE_WRITABLE:
 	mode = "w"; break;
       case FMODE_READWRITE:
-	if (orig->f2) mode = "r";
-	else          mode = "r+";
-	break;
+	mode = "r+"; break;
     }
-    fd = ruby_dup(fileno(orig->f));
+    fd = ruby_dup(orig->fd);
+    fptr->fd = fd;
     fptr->f = rb_fdopen(fd, mode);
-    fseeko(fptr->f, ftello(orig->f), SEEK_SET);
-    if (orig->f2) {
-	if (fileno(orig->f) != fileno(orig->f2)) {
-	    fd = ruby_dup(fileno(orig->f2));
-	}
-	fptr->f2 = rb_fdopen(fd, "w");
-	fseeko(fptr->f2, ftello(orig->f2), SEEK_SET);
-    }
+    io_seek(fptr, io_tell(orig), SEEK_SET);
     if (fptr->mode & FMODE_BINMODE) {
 	rb_io_binmode(dest);
     }
@@ -3957,14 +3869,23 @@ prep_stdio(f, mode, klass)
     VALUE io = io_alloc(klass);
 
     MakeOpenFile(io, fp);
+    fp->fd = fileno(f);
 #ifdef __CYGWIN__
-    if (!isatty(fileno(f))) {
+    if (!isatty(fp->fd)) {
 	mode |= O_BINARY;
-	setmode(fileno(f), O_BINARY);
+	setmode(fp->fd, O_BINARY);
     }
 #endif
     fp->f = f;
     fp->mode = mode;
+    if (fp->mode & FMODE_WRITABLE) {
+        if (fp->fd == 2) { /* stderr must be unbuffered */
+            fp->mode |= FMODE_SYNC;
+        }
+        else if (isatty(fp->fd)) {
+            fp->mode |= FMODE_LINEBUF;
+        }
+    }
 
     return io;
 }
@@ -4035,8 +3956,11 @@ rb_io_initialize(argc, argv, io)
 #endif
 	}
 	MakeOpenFile(io, fp);
+        fp->fd = fd;
 	fp->mode = rb_io_modenum_flags(flags);
 	fp->f = rb_fdopen(fd, rb_io_modenum_mode(flags));
+        if ((fp->mode & FMODE_WRITABLE) && isatty(fp->fd))
+            fp->mode |= FMODE_LINEBUF;
     }
     else if (RFILE(io)->fptr) {
 	rb_raise(rb_eRuntimeError, "reinitializing IO");
@@ -4559,12 +4483,12 @@ rb_f_select(argc, argv, obj)
 	FD_ZERO(rp);
 	for (i=0; i<RARRAY(read)->len; i++) {
 	    GetOpenFile(rb_io_get_io(RARRAY(read)->ptr[i]), fptr);
-	    FD_SET(fileno(fptr->f), rp);
-	    if (READ_DATA_PENDING(fptr->f)) { /* check for buffered data */
+	    FD_SET(fptr->fd, rp);
+	    if (READ_DATA_PENDING(fptr)) { /* check for buffered data */
 		pending++;
-		FD_SET(fileno(fptr->f), &pset);
+		FD_SET(fptr->fd, &pset);
 	    }
-	    if (max < fileno(fptr->f)) max = fileno(fptr->f);
+	    if (max < fptr->fd) max = fptr->fd;
 	}
 	if (pending) {		/* no blocking if there's buffered data */
 	    timerec.tv_sec = timerec.tv_usec = 0;
@@ -4580,12 +4504,8 @@ rb_f_select(argc, argv, obj)
 	FD_ZERO(wp);
 	for (i=0; i<RARRAY(write)->len; i++) {
 	    GetOpenFile(rb_io_get_io(RARRAY(write)->ptr[i]), fptr);
-	    FD_SET(fileno(fptr->f), wp);
-	    if (max < fileno(fptr->f)) max = fileno(fptr->f);
-	    if (fptr->f2) {
-		FD_SET(fileno(fptr->f2), wp);
-		if (max < fileno(fptr->f2)) max = fileno(fptr->f2);
-	    }
+	    FD_SET(fptr->fd, wp);
+	    if (max < fptr->fd) max = fptr->fd;
 	}
     }
     else
@@ -4597,12 +4517,8 @@ rb_f_select(argc, argv, obj)
 	FD_ZERO(ep);
 	for (i=0; i<RARRAY(except)->len; i++) {
 	    GetOpenFile(rb_io_get_io(RARRAY(except)->ptr[i]), fptr);
-	    FD_SET(fileno(fptr->f), ep);
-	    if (max < fileno(fptr->f)) max = fileno(fptr->f);
-	    if (fptr->f2) {
-		FD_SET(fileno(fptr->f2), ep);
-		if (max < fileno(fptr->f2)) max = fileno(fptr->f2);
-	    }
+	    FD_SET(fptr->fd, ep);
+	    if (max < fptr->fd) max = fptr->fd;
 	}
     }
     else {
@@ -4627,8 +4543,8 @@ rb_f_select(argc, argv, obj)
 	    list = RARRAY(res)->ptr[0];
 	    for (i=0; i< RARRAY(read)->len; i++) {
 		GetOpenFile(rb_io_get_io(RARRAY(read)->ptr[i]), fptr);
-		if (FD_ISSET(fileno(fptr->f), rp)
-		    || FD_ISSET(fileno(fptr->f), &pset)) {
+		if (FD_ISSET(fptr->fd, rp)
+		    || FD_ISSET(fptr->fd, &pset)) {
 		    rb_ary_push(list, RARRAY(read)->ptr[i]);
 		}
 	    }
@@ -4638,10 +4554,7 @@ rb_f_select(argc, argv, obj)
 	    list = RARRAY(res)->ptr[1];
 	    for (i=0; i< RARRAY(write)->len; i++) {
 		GetOpenFile(rb_io_get_io(RARRAY(write)->ptr[i]), fptr);
-		if (FD_ISSET(fileno(fptr->f), wp)) {
-		    rb_ary_push(list, RARRAY(write)->ptr[i]);
-		}
-		else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), wp)) {
+		if (FD_ISSET(fptr->fd, wp)) {
 		    rb_ary_push(list, RARRAY(write)->ptr[i]);
 		}
 	    }
@@ -4651,10 +4564,7 @@ rb_f_select(argc, argv, obj)
 	    list = RARRAY(res)->ptr[2];
 	    for (i=0; i< RARRAY(except)->len; i++) {
 		GetOpenFile(rb_io_get_io(RARRAY(except)->ptr[i]), fptr);
-		if (FD_ISSET(fileno(fptr->f), ep)) {
-		    rb_ary_push(list, RARRAY(except)->ptr[i]);
-		}
-		else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), ep)) {
+		if (FD_ISSET(fptr->fd, ep)) {
 		    rb_ary_push(list, RARRAY(except)->ptr[i]);
 		}
 	    }
@@ -4746,17 +4656,12 @@ rb_io_ctl(io, req, arg, io_p)
 	}
     }
     GetOpenFile(io, fptr);
-    retval = io_cntl(fileno(fptr->f), cmd, narg, io_p);
+    retval = io_cntl(fptr->fd, cmd, narg, io_p);
     if (retval < 0) rb_sys_fail(fptr->path);
     if (TYPE(arg) == T_STRING && RSTRING(arg)->ptr[len] != 17) {
 	rb_raise(rb_eArgError, "return value overflowed string");
     }
 
-    if (fptr->f2 && fileno(fptr->f) != fileno(fptr->f2)) {
-	/* call on f2 too; ignore result */
-	io_cntl(fileno(fptr->f2), cmd, narg, io_p);
-    }
-
     return INT2NUM(retval);
 #else
     rb_notimplement();
@@ -5429,6 +5334,17 @@ opt_i_set(val)
     ruby_inplace_mode = strdup(RSTRING(val)->ptr);
 }
 
+static VALUE
+rb_io_bufreport(VALUE obj)
+{
+    OpenFile *fptr;
+    fptr = RFILE(rb_io_taint_check(obj))->fptr;
+    fprintf(stderr, "fd:%d rbuf:%d wbuf:%d\n",
+            fptr->fd,
+            fptr->rbuf_len, fptr->wbuf_len);
+    return Qnil;
+}
+
 /*
  *  Class <code>IO</code> is the basis for all input and output in Ruby.
  *  An I/O stream may be <em>duplexed</em> (that is, bidirectional), and
@@ -5641,6 +5557,8 @@ Init_IO()
     rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
     rb_define_method(rb_cIO, "pid", rb_io_pid, 0);
     rb_define_method(rb_cIO, "inspect",  rb_io_inspect, 0);
+
+    rb_define_method(rb_cIO, "bufreport",  rb_io_bufreport, 0);
 
     rb_stdin = prep_stdio(stdin, FMODE_READABLE, rb_cIO);
     rb_define_variable("$stdin", &rb_stdin);
Index: rubyio.h
===================================================================
RCS file: /src/ruby/rubyio.h,v
retrieving revision 1.31
diff -u -p -r1.31 rubyio.h
--- rubyio.h	19 Nov 2004 16:59:11 -0000	1.31
+++ rubyio.h	5 Dec 2004 14:51:04 -0000
@@ -21,14 +21,22 @@
 #endif
 
 typedef struct OpenFile {
+    int fd;
     FILE *f;			/* stdio ptr for read/write */
-    FILE *f2;			/* additional ptr for rw pipes */
     int mode;			/* mode flags */
     int pid;			/* child's pid (for pipes) */
     int lineno;			/* number of lines read */
     char *path;			/* pathname for file */
     void (*finalize) _((struct OpenFile*,int)); /* finalize proc */
     long refcnt;
+    char *wbuf;
+    int wbuf_off;
+    int wbuf_len;
+    int wbuf_capa;
+    char *rbuf;
+    int rbuf_off;
+    int rbuf_len;
+    int rbuf_capa;
 } OpenFile;
 
 #define FMODE_READABLE  1
@@ -38,8 +46,8 @@ typedef struct OpenFile {
 #define FMODE_CREATE  128
 #define FMODE_BINMODE   4
 #define FMODE_SYNC      8
-#define FMODE_WBUF     16
-#define FMODE_RBUF     32
+#define FMODE_LINEBUF  16
+#define FMODE_UNSEEKABLE 32
 
 #define GetOpenFile(obj,fp) rb_io_check_closed((fp) = RFILE(rb_io_taint_check(obj))->fptr)
 
@@ -51,23 +59,29 @@ typedef struct OpenFile {
     }\
     fp = 0;\
     fp = RFILE(obj)->fptr = ALLOC(OpenFile);\
-    fp->f = fp->f2 = NULL;\
+    fp->fd = -1;\
+    fp->f = NULL;\
     fp->mode = 0;\
     fp->pid = 0;\
     fp->lineno = 0;\
     fp->path = NULL;\
     fp->finalize = 0;\
     fp->refcnt = 1;\
+    fp->wbuf = NULL;\
+    fp->wbuf_off = 0;\
+    fp->wbuf_len = 0;\
+    fp->wbuf_capa = 0;\
+    fp->rbuf = NULL;\
+    fp->rbuf_off = 0;\
+    fp->rbuf_len = 0;\
+    fp->rbuf_capa = 0;\
 } while (0)
 
 #define GetReadFile(fptr) ((fptr)->f)
-#define GetWriteFile(fptr) (((fptr)->f2) ? (fptr)->f2 : (fptr)->f)
+#define GetWriteFile(fptr) ((fptr)->f)
 
 FILE *rb_fopen _((const char*, const char*));
 FILE *rb_fdopen _((int, const char*));
-int rb_getc _((FILE*));
-long rb_io_fread _((char *, long, FILE *));
-long rb_io_fwrite _((const char *, long, FILE *));
 int  rb_io_mode_flags _((const char*));
 int  rb_io_modenum_flags _((int));
 void rb_io_check_writable _((OpenFile*));
@@ -82,6 +96,32 @@ int rb_io_wait_writable _((int));
 VALUE rb_io_taint_check _((VALUE));
 NORETURN(void rb_eof_error _((void)));
 
-void rb_read_check _((FILE*));
-int rb_read_pending _((FILE*));
+void rb_io_read_check _((OpenFile*));
+int rb_io_read_pending _((OpenFile*));
+
+int rb_getc _((FILE*))
+#ifdef __GNUC__
+  __attribute__ ((deprecated))
+#endif
+  ;
+long rb_io_fread _((char *, long, FILE *))
+#ifdef __GNUC__
+  __attribute__ ((deprecated))
+#endif
+  ;
+long rb_io_fwrite _((const char *, long, FILE *))
+#ifdef __GNUC__
+  __attribute__ ((deprecated))
+#endif
+  ;
+void rb_read_check _((FILE*))
+#ifdef __GNUC__
+  __attribute__ ((deprecated))
+#endif
+  ;
+int rb_read_pending _((FILE*))
+#ifdef __GNUC__
+  __attribute__ ((deprecated))
+#endif
+  ;
 #endif
Index: ext/io/wait/wait.c
===================================================================
RCS file: /src/ruby/ext/io/wait/wait.c,v
retrieving revision 1.3
diff -u -p -r1.3 wait.c
--- ext/io/wait/wait.c	1 Nov 2003 11:24:40 -0000	1.3
+++ ext/io/wait/wait.c	5 Dec 2004 14:51:04 -0000
@@ -40,15 +40,12 @@ io_ready_p(io)
     VALUE io;
 {
     OpenFile *fptr;
-    FILE *fp;
     int n;
 
     GetOpenFile(io, fptr);
     rb_io_check_readable(fptr);
-    fp = fptr->f;
-    if (feof(fp)) return Qfalse;
-    if (rb_read_pending(fp)) return Qtrue;
-    if (ioctl(fileno(fp), FIONREAD, &n)) rb_sys_fail(0);
+    if (rb_io_read_pending(fptr)) return Qtrue;
+    if (ioctl(fptr->fd, FIONREAD, &n)) rb_sys_fail(0);
     if (n > 0) return INT2NUM(n);
     return Qnil;
 }
@@ -68,7 +65,6 @@ io_wait(argc, argv, io)
 {
     OpenFile *fptr;
     fd_set rd;
-    FILE *fp;
     int fd, n;
     VALUE timeout;
     struct timeval *tp, timerec;
@@ -84,16 +80,14 @@ io_wait(argc, argv, io)
 	tp = &timerec;
     }
 
-    fp = fptr->f;
-    if (feof(fp)) return Qfalse;
-    if (rb_read_pending(fp)) return Qtrue;
-    fd = fileno(fp);
+    if (rb_io_read_pending(fptr)) return Qtrue;
+    fd = fptr->fd;
     FD_ZERO(&rd);
     FD_SET(fd, &rd);
     if (rb_thread_select(fd + 1, &rd, NULL, NULL, tp) < 0)
 	rb_sys_fail(0);
     rb_io_check_closed(fptr);
-    if (ioctl(fileno(fp), FIONREAD, &n)) rb_sys_fail(0);
+    if (ioctl(fptr->fd, FIONREAD, &n)) rb_sys_fail(0);
     if (n > 0) return io;
     return Qnil;
 }
Index: ext/openssl/ossl_ssl.c
===================================================================
RCS file: /src/ruby/ext/openssl/ossl_ssl.c,v
retrieving revision 1.16
diff -u -p -r1.16 ossl_ssl.c
--- ext/openssl/ossl_ssl.c	27 Oct 2004 09:29:25 -0000	1.16
+++ ext/openssl/ossl_ssl.c	5 Dec 2004 14:51:04 -0000
@@ -429,7 +429,7 @@ ossl_ssl_setup(VALUE self)
         GetOpenFile(io, fptr);
         rb_io_check_readable(fptr);
         rb_io_check_writable(fptr);
-        SSL_set_fd(ssl, TO_SOCKET(fileno(fptr->f)));
+        SSL_set_fd(ssl, TO_SOCKET(fptr->fd));
     }
 
     return Qtrue;
@@ -505,7 +505,7 @@ ossl_ssl_read(int argc, VALUE *argv, VAL
 
     if (ssl) {
 	if(SSL_pending(ssl) <= 0)
-	    rb_thread_wait_fd(fileno(fptr->f));
+	    rb_thread_wait_fd(fptr->fd);
 	for (;;){
 	    nread = SSL_read(ssl, RSTRING(str)->ptr, RSTRING(str)->len);
 	    switch(SSL_get_error(ssl, nread)){
Index: ext/pty/pty.c
===================================================================
RCS file: /src/ruby/ext/pty/pty.c,v
retrieving revision 1.19
diff -u -p -r1.19 pty.c
--- ext/pty/pty.c	11 Dec 2003 02:39:59 -0000	1.19
+++ ext/pty/pty.c	5 Dec 2004 14:51:04 -0000
@@ -427,11 +427,13 @@ pty_getpty(argc, argv, self)
     establishShell(argc, argv, &info);
 
     rfptr->mode = rb_io_mode_flags("r");
-    rfptr->f = fdopen(info.fd, "r");
+    rfptr->fd = info.fd;
+    rfptr->f = rb_fdopen(info.fd, "r");
     rfptr->path = strdup(SlaveName);
 
     wfptr->mode = rb_io_mode_flags("w") | FMODE_SYNC;
-    wfptr->f = fdopen(dup(info.fd), "w");
+    wfptr->fd = dup(info.fd);
+    wfptr->f = rb_fdopen(wfptr->fd, "w");
     wfptr->path = strdup(SlaveName);
 
     res = rb_ary_new2(3);
Index: ext/socket/socket.c
===================================================================
RCS file: /src/ruby/ext/socket/socket.c,v
retrieving revision 1.134
diff -u -p -r1.134 socket.c
--- ext/socket/socket.c	10 Nov 2004 07:16:24 -0000	1.134
+++ ext/socket/socket.c	5 Dec 2004 14:51:04 -0000
@@ -182,8 +182,8 @@ init_sock(sock, fd)
     OpenFile *fp;
 
     MakeOpenFile(sock, fp);
-    fp->f = rb_fdopen(fd, "r");
-    fp->f2 = rb_fdopen(fd, "w");
+    fp->fd = fd;
+    fp->f = rb_fdopen(fd, "r+");
     fp->mode = FMODE_READWRITE;
     if (do_not_reverse_lookup) {
 	fp->mode |= FMODE_NOREVLOOKUP;
@@ -228,7 +228,7 @@ bsock_shutdown(argc, argv, sock)
 	}
     }
     GetOpenFile(sock, fptr);
-    if (shutdown(fileno(fptr->f), how) == -1)
+    if (shutdown(fptr->fd, how) == -1)
 	rb_sys_fail(0);
 
     return INT2FIX(0);
@@ -244,7 +244,7 @@ bsock_close_read(sock)
 	rb_raise(rb_eSecurityError, "Insecure: can't close socket");
     }
     GetOpenFile(sock, fptr);
-    shutdown(fileno(fptr->f), 0);
+    shutdown(fptr->fd, 0);
     if (!(fptr->mode & FMODE_WRITABLE)) {
 	return rb_io_close(sock);
     }
@@ -266,7 +266,7 @@ bsock_close_write(sock)
     if (!(fptr->mode & FMODE_READABLE)) {
 	return rb_io_close(sock);
     }
-    shutdown(fileno(fptr->f2), 1);
+    shutdown(fptr->fd, 1);
     fptr->mode &= ~FMODE_WRITABLE;
 
     return Qnil;
@@ -306,7 +306,7 @@ bsock_setsockopt(sock, lev, optname, val
 	break;
     }
 
-    if (setsockopt(fileno(fptr->f), level, option, v, vlen) < 0)
+    if (setsockopt(fptr->fd, level, option, v, vlen) < 0)
 	rb_sys_fail(fptr->path);
 
     return INT2FIX(0);
@@ -328,7 +328,7 @@ bsock_getsockopt(sock, lev, optname)
     buf = ALLOCA_N(char,len);
 
     GetOpenFile(sock, fptr);
-    if (getsockopt(fileno(fptr->f), level, option, buf, &len) < 0)
+    if (getsockopt(fptr->fd, level, option, buf, &len) < 0)
 	rb_sys_fail(fptr->path);
 
     return rb_str_new(buf, len);
@@ -346,7 +346,7 @@ bsock_getsockname(sock)
     OpenFile *fptr;
 
     GetOpenFile(sock, fptr);
-    if (getsockname(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0)
+    if (getsockname(fptr->fd, (struct sockaddr*)buf, &len) < 0)
 	rb_sys_fail("getsockname(2)");
     return rb_str_new(buf, len);
 }
@@ -360,7 +360,7 @@ bsock_getpeername(sock)
     OpenFile *fptr;
 
     GetOpenFile(sock, fptr);
-    if (getpeername(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0)
+    if (getpeername(fptr->fd, (struct sockaddr*)buf, &len) < 0)
 	rb_sys_fail("getpeername(2)");
     return rb_str_new(buf, len);
 }
@@ -374,15 +374,13 @@ bsock_send(argc, argv, sock)
     VALUE mesg, to;
     VALUE flags;
     OpenFile *fptr;
-    FILE *f;
     int fd, n;
 
     rb_secure(4);
     rb_scan_args(argc, argv, "21", &mesg, &flags, &to);
 
     GetOpenFile(sock, fptr);
-    f = GetWriteFile(fptr);
-    fd = fileno(f);
+    fd = fptr->fd;
     rb_thread_fd_writable(fd);
     StringValue(mesg);
   retry:
@@ -465,10 +463,10 @@ s_recvfrom(sock, argc, argv, from)
     else             flags = NUM2INT(flg);
 
     GetOpenFile(sock, fptr);
-    if (rb_read_pending(fptr->f)) {
+    if (rb_io_read_pending(fptr)) {
 	rb_raise(rb_eIOError, "recv for buffered IO");
     }
-    fd = fileno(fptr->f);
+    fd = fptr->fd;
 
     buflen = NUM2INT(len);
     str = rb_tainted_str_new(0, buflen);
@@ -1123,8 +1121,7 @@ socks_s_close(sock)
 	rb_raise(rb_eSecurityError, "Insecure: can't close socket");
     }
     GetOpenFile(sock, fptr);
-    shutdown(fileno(fptr->f), 2);
-    shutdown(fileno(fptr->f2), 2);
+    shutdown(fptr->fd, 2);
     return rb_io_close(sock);
 }
 #endif
@@ -1276,7 +1273,7 @@ tcp_accept(sock)
 
     GetOpenFile(sock, fptr);
     fromlen = sizeof(from);
-    return s_accept(rb_cTCPSocket, fileno(fptr->f),
+    return s_accept(rb_cTCPSocket, fptr->fd,
 		    (struct sockaddr*)&from, &fromlen);
 }
 
@@ -1290,7 +1287,7 @@ tcp_sysaccept(sock)
 
     GetOpenFile(sock, fptr);
     fromlen = sizeof(from);
-    return s_accept(0, fileno(fptr->f), (struct sockaddr*)&from, &fromlen);
+    return s_accept(0, fptr->fd, (struct sockaddr*)&from, &fromlen);
 }
 
 #ifdef HAVE_SYS_UN_H
@@ -1367,7 +1364,7 @@ ip_addr(sock)
 
     GetOpenFile(sock, fptr);
 
-    if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+    if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
 	rb_sys_fail("getsockname(2)");
     return ipaddr((struct sockaddr*)&addr, fptr->mode & FMODE_NOREVLOOKUP);
 }
@@ -1382,7 +1379,7 @@ ip_peeraddr(sock)
 
     GetOpenFile(sock, fptr);
 
-    if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+    if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
 	rb_sys_fail("getpeername(2)");
     return ipaddr((struct sockaddr*)&addr, fptr->mode & FMODE_NOREVLOOKUP);
 }
@@ -1464,7 +1461,7 @@ udp_connect(sock, host, port)
     rb_secure(3);
     GetOpenFile(sock, fptr);
     arg.res = sock_addrinfo(host, port, SOCK_DGRAM, 0);
-    arg.fd = fileno(fptr->f);
+    arg.fd = fptr->fd;
     ret = rb_ensure(udp_connect_internal, (VALUE)&arg,
 		    RUBY_METHOD_FUNC(freeaddrinfo), (VALUE)arg.res);
     if (!ret) rb_sys_fail("connect(2)");
@@ -1482,7 +1479,7 @@ udp_bind(sock, host, port)
     GetOpenFile(sock, fptr);
     res0 = sock_addrinfo(host, port, SOCK_DGRAM, 0);
     for (res = res0; res; res = res->ai_next) {
-	if (bind(fileno(fptr->f), res->ai_addr, res->ai_addrlen) < 0) {
+	if (bind(fptr->fd, res->ai_addr, res->ai_addrlen) < 0) {
 	    continue;
 	}
 	freeaddrinfo(res0);
@@ -1501,7 +1498,6 @@ udp_send(argc, argv, sock)
 {
     VALUE mesg, flags, host, port;
     OpenFile *fptr;
-    FILE *f;
     int n;
     struct addrinfo *res0, *res;
 
@@ -1513,17 +1509,16 @@ udp_send(argc, argv, sock)
 
     GetOpenFile(sock, fptr);
     res0 = sock_addrinfo(host, port, SOCK_DGRAM, 0);
-    f = GetWriteFile(fptr);
     StringValue(mesg);
     for (res = res0; res; res = res->ai_next) {
       retry:
-	n = sendto(fileno(f), RSTRING(mesg)->ptr, RSTRING(mesg)->len, NUM2INT(flags),
+	n = sendto(fptr->fd, RSTRING(mesg)->ptr, RSTRING(mesg)->len, NUM2INT(flags),
 		   res->ai_addr, res->ai_addrlen);
 	if (n >= 0) {
 	    freeaddrinfo(res0);
 	    return INT2FIX(n);
 	}
-	if (rb_io_wait_writable(fileno(f))) {
+	if (rb_io_wait_writable(fptr->fd)) {
 	    goto retry;
 	}
     }
@@ -1550,7 +1545,7 @@ unix_path(sock)
     if (fptr->path == 0) {
 	struct sockaddr_un addr;
 	socklen_t len = sizeof(addr);
-	if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+	if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
 	    rb_sys_fail(0);
 	fptr->path = strdup(addr.sun_path);
     }
@@ -1606,7 +1601,7 @@ unix_send_io(sock, val)
     if (rb_obj_is_kind_of(val, rb_cIO)) {
         OpenFile *valfptr;
 	GetOpenFile(val, valfptr);
-	fd = fileno(valfptr->f);
+	fd = valfptr->fd;
     }
     else if (FIXNUM_P(val)) {
         fd = FIX2INT(val);
@@ -1640,7 +1635,7 @@ unix_send_io(sock, val)
     msg.msg_accrightslen = sizeof(fd);
 #endif
 
-    if (sendmsg(fileno(fptr->f), &msg, 0) == -1)
+    if (sendmsg(fptr->fd, &msg, 0) == -1)
 	rb_sys_fail("sendmsg(2)");
 
     return Qnil;
@@ -1692,7 +1687,7 @@ unix_recv_io(argc, argv, sock)
 
     GetOpenFile(sock, fptr);
 
-    thread_read_select(fileno(fptr->f));
+    thread_read_select(fptr->fd);
 
     msg.msg_name = NULL;
     msg.msg_namelen = 0;
@@ -1716,7 +1711,7 @@ unix_recv_io(argc, argv, sock)
     fd = -1;
 #endif
 
-    if (recvmsg(fileno(fptr->f), &msg, 0) == -1)
+    if (recvmsg(fptr->fd, &msg, 0) == -1)
 	rb_sys_fail("recvmsg(2)");
 
     if (
@@ -1765,7 +1760,7 @@ unix_accept(sock)
 
     GetOpenFile(sock, fptr);
     fromlen = sizeof(struct sockaddr_un);
-    return s_accept(rb_cUNIXSocket, fileno(fptr->f),
+    return s_accept(rb_cUNIXSocket, fptr->fd,
 		    (struct sockaddr*)&from, &fromlen);
 }
 
@@ -1779,7 +1774,7 @@ unix_sysaccept(sock)
 
     GetOpenFile(sock, fptr);
     fromlen = sizeof(struct sockaddr_un);
-    return s_accept(0, fileno(fptr->f), (struct sockaddr*)&from, &fromlen);
+    return s_accept(0, fptr->fd, (struct sockaddr*)&from, &fromlen);
 }
 
 #ifdef HAVE_SYS_UN_H
@@ -1802,7 +1797,7 @@ unix_addr(sock)
 
     GetOpenFile(sock, fptr);
 
-    if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+    if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
 	rb_sys_fail("getsockname(2)");
     if (len == 0)
         addr.sun_path[0] = '\0';
@@ -1819,7 +1814,7 @@ unix_peeraddr(sock)
 
     GetOpenFile(sock, fptr);
 
-    if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
+    if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
 	rb_sys_fail("getsockname(2)");
     if (len == 0)
         addr.sun_path[0] = '\0';
@@ -1987,7 +1982,7 @@ sock_connect(sock, addr)
 
     StringValue(addr);
     GetOpenFile(sock, fptr);
-    fd = fileno(fptr->f);
+    fd = fptr->fd;
     rb_str_locktmp(addr);
     n = ruby_connect(fd, (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len, 0);
     rb_str_unlocktmp(addr);
@@ -2006,7 +2001,7 @@ sock_bind(sock, addr)
 
     StringValue(addr);
     GetOpenFile(sock, fptr);
-    if (bind(fileno(fptr->f), (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len) < 0)
+    if (bind(fptr->fd, (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len) < 0)
 	rb_sys_fail("bind(2)");
 
     return INT2FIX(0);
@@ -2020,7 +2015,7 @@ sock_listen(sock, log)
 
     rb_secure(4);
     GetOpenFile(sock, fptr);
-    if (listen(fileno(fptr->f), NUM2INT(log)) < 0)
+    if (listen(fptr->fd, NUM2INT(log)) < 0)
 	rb_sys_fail("listen(2)");
 
     return INT2FIX(0);
@@ -2045,7 +2040,7 @@ sock_accept(sock)
     socklen_t len = sizeof buf;
 
     GetOpenFile(sock, fptr);
-    sock2 = s_accept(rb_cSocket,fileno(fptr->f),(struct sockaddr*)buf,&len);
+    sock2 = s_accept(rb_cSocket,fptr->fd,(struct sockaddr*)buf,&len);
 
     return rb_assoc_new(sock2, rb_str_new(buf, len));
 }
@@ -2060,7 +2055,7 @@ sock_sysaccept(sock)
     socklen_t len = sizeof buf;
 
     GetOpenFile(sock, fptr);
-    sock2 = s_accept(0,fileno(fptr->f),(struct sockaddr*)buf,&len);
+    sock2 = s_accept(0,fptr->fd,(struct sockaddr*)buf,&len);
 
     return rb_assoc_new(sock2, rb_str_new(buf, len));
 }
Index: ext/stringio/stringio.c
===================================================================
RCS file: /src/ruby/ext/stringio/stringio.c,v
retrieving revision 1.36
diff -u -p -r1.36 stringio.c
--- ext/stringio/stringio.c	29 Nov 2004 07:06:18 -0000	1.36
+++ ext/stringio/stringio.c	5 Dec 2004 14:51:04 -0000
@@ -20,8 +20,6 @@
 #include <sys/fcntl.h>
 #endif
 
-#define STRIO_EOF FMODE_SYNC
-
 struct StringIO {
     VALUE string;
     long pos;
@@ -490,7 +488,6 @@ strio_rewind(self)
     struct StringIO *ptr = StringIO(self);
     ptr->pos = 0;
     ptr->lineno = 0;
-    ptr->flags &= ~STRIO_EOF;
     return INT2FIX(0);
 }
 
@@ -522,7 +519,6 @@ strio_seek(argc, argv, self)
 	error_inval(0);
     }
     ptr->pos = offset;
-    ptr->flags &= ~STRIO_EOF;
     return INT2FIX(0);
 }
 
@@ -557,7 +553,6 @@ strio_getc(self)
     struct StringIO *ptr = readable(StringIO(self));
     int c;
     if (ptr->pos >= RSTRING(ptr->string)->len) {
-	ptr->flags |= STRIO_EOF;
 	return Qnil;
     }
     c = RSTRING(ptr->string)->ptr[ptr->pos++];
@@ -588,7 +583,6 @@ strio_ungetc(self, ch)
 	    OBJ_INFECT(ptr->string, self);
 	}
 	--ptr->pos;
-	ptr->flags &= ~STRIO_EOF;
     }
     return Qnil;
 }
@@ -661,7 +655,6 @@ strio_getline(argc, argv, ptr)
     }
 
     if (ptr->pos >= (n = RSTRING(ptr->string)->len)) {
-	ptr->flags |= STRIO_EOF;
 	return Qnil;
     }
     s = RSTRING(ptr->string)->ptr;
@@ -674,7 +667,6 @@ strio_getline(argc, argv, ptr)
 	p = s;
 	while (*p == '\n') {
 	    if (++p == e) {
-		ptr->flags |= STRIO_EOF;
 		return Qnil;
 	    }
 	}
@@ -858,11 +850,6 @@ strio_read(argc, argv, self)
 		rb_raise(rb_eArgError, "negative length %ld given", len);
 	    }
 	    if (len > 0 && ptr->pos >= RSTRING(ptr->string)->len) {
-		ptr->flags |= STRIO_EOF;
-		if (!NIL_P(str)) rb_str_resize(str, 0);
-		return Qnil;
-	    }
-	    else if (ptr->flags & STRIO_EOF) {
 		if (!NIL_P(str)) rb_str_resize(str, 0);
 		return Qnil;
 	    }
@@ -873,7 +860,6 @@ strio_read(argc, argv, self)
 	olen = -1;
 	len = RSTRING(ptr->string)->len;
 	if (len <= ptr->pos) {
-	    ptr->flags |= STRIO_EOF;
 	    if (NIL_P(str)) {
 		str = rb_str_new(0, 0);
 	    }
@@ -899,13 +885,12 @@ strio_read(argc, argv, self)
 	MEMCPY(RSTRING(str)->ptr, RSTRING(ptr->string)->ptr + ptr->pos, char, len);
     }
     if (NIL_P(str)) {
-	if (!(ptr->flags & STRIO_EOF)) str = rb_str_new(0, 0);
+	str = rb_str_new(0, 0);
 	len = 0;
     }
     else {
 	ptr->pos += len = RSTRING(str)->len;
     }
-    if (olen < 0 || olen > len) ptr->flags |= STRIO_EOF;
     return str;
 }
 
Index: test/ruby/ut_eof.rb
===================================================================
RCS file: /src/ruby/test/ruby/ut_eof.rb,v
retrieving revision 1.7
diff -u -p -r1.7 ut_eof.rb
--- test/ruby/ut_eof.rb	24 Dec 2003 05:23:32 -0000	1.7
+++ test/ruby/ut_eof.rb	5 Dec 2004 14:51:04 -0000
@@ -6,8 +6,8 @@ module TestEOF
       assert_equal("", f.read(0))
       assert_equal("", f.read(0))
       assert_equal("", f.read)
-      assert_nil(f.read(0))
-      assert_nil(f.read(0))
+      assert_equal("", f.read(0))
+      assert_equal("", f.read(0))
     }
     open_file("") {|f|
       assert_nil(f.read(1))
@@ -43,8 +43,8 @@ module TestEOF
       assert_equal("" , f.read(0))
       assert_equal("" , f.read(0))
       assert_equal("", f.read)
-      assert_nil(f.read(0))
-      assert_nil(f.read(0))
+      assert_equal("", f.read(0))
+      assert_equal("", f.read(0))
     }
     open_file("a") {|f|
       assert_equal("a", f.read(1))
@@ -69,7 +69,7 @@ module TestEOF
     }
     open_file("a") {|f|
       assert_equal("a", f.read)
-      assert_nil(f.read(0))
+      assert_equal("", f.read(0))
     }
     open_file("a") {|f|
       s = "x"
@@ -96,7 +96,7 @@ module TestEOF
         assert_equal(10, f.pos)
         assert_equal("", f.read(0))
         assert_equal("", f.read)
-        assert_nil(f.read(0))
+        assert_equal("", f.read(0))
         assert_equal("", f.read)
       }
     end
-- 
[田中 哲][たなか あきら][Tanaka Akira]