/*
 *  call-seq:
 *     ios.read([length [, buffer]])    => string, buffer, or nil
 *
 *  Reads at most <i>length</i> bytes from the I/O stream, or to the
 *  end of file if <i>length</i> is omitted or is <code>nil</code>.
 *  <i>length</i> must be a non-negative integer or nil.
 *  If the optional <i>buffer</i> argument is present, it must reference
 *  a String, which will receive the data.
 *
 *  At end of file, it returns <code>nil</code> or <code>""</code>
 *  depend on <i>length</i>.
 *  <code><i>ios</i>.read()</code> and
 *  <code><i>ios</i>.read(nil)</code> returns <code>""</code>.
 *  <code><i>ios</i>.read(<i>positive-integer</i>)</code> returns nil.
 *
 *     f = File.new("testfile")
 *     f.read(16)   #=> "This is line one"
 */

static VALUE
io_read(argc, argv, io)
    int argc;
    VALUE *argv;
    VALUE io;
{
    OpenFile *fptr;
    long n, len;
    VALUE length, str;

    rb_scan_args(argc, argv, "02", &length, &str);

    if (NIL_P(length)) {
        if (!NIL_P(str)) StringValue(str);
        GetOpenFile(io, fptr);
        rb_io_check_readable(fptr);    
        return read_all(fptr, remain_size(fptr), str);
    }
    len = NUM2LONG(length);
    if (len < 0) {
        rb_raise(rb_eArgError, "negative length %ld given", len);
    }

    if (NIL_P(str)) {
        str = rb_tainted_str_new(0, len);
    }
    else {
        StringValue(str);
        rb_str_modify(str);
        rb_str_resize(str,len);
    }

    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);
    if (RSTRING(str)->len != len) {
        rb_raise(rb_eRuntimeError, "buffer string modified");
    }
    n = io_fread(RSTRING(str)->ptr, len, fptr);
    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, n);
    RSTRING(str)->len = n;
    RSTRING(str)->ptr[n] = '\0';
    OBJ_TAINT(str);

    return str;
}