/*
* 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;
{
rb_io_t *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;
}