/*
* call-seq:
*
* Kgio::File.tryopen(filename, [, mode [, perm]]) -> Kgio::File or Symbol
*
* Returns a Kgio::File object on a successful open. +filename+ is a
* path to any file on the filesystem. If specified, +mode+ is a bitmask
* of flags (see IO.sysopen) and +perm+ should be an octal number.
*
* This does not raise errors for most failures, but installs returns a
* Ruby symbol for the constant in the Errno::* namespace.
*
* Common error symbols are:
*
* - :ENOENT
* - :EACCES
*
* See your open(2) manpage for more information on open(2) errors.
*/
static VALUE s_tryopen(int argc, VALUE *argv, VALUE klass)
{
int fd;
VALUE pathname, flags, mode;
struct open_args o;
int retried = 0;
VALUE rv;
rb_scan_args(argc, argv, "12", &pathname, &flags, &mode);
if (rb_respond_to(pathname, id_to_path))
pathname = rb_funcall(pathname, id_to_path, 0);
o.pathname = StringValueCStr(pathname);
switch (TYPE(flags)) {
case T_NIL: o.flags = O_RDONLY; break;
case T_FIXNUM: o.flags = FIX2INT(flags); break;
case T_BIGNUM: o.flags = NUM2INT(flags); break;
default: rb_raise(rb_eArgError, "flags must be an Integer");
}
switch (TYPE(mode)) {
case T_NIL: o.mode = 0666; break;
case T_FIXNUM: o.mode = FIX2INT(mode); break;
case T_BIGNUM: o.mode = NUM2INT(mode); break;
default: rb_raise(rb_eArgError, "mode must be an Integer");
}
retry:
fd = (int)rb_thread_blocking_region(nogvl_open, &o, RUBY_UBF_IO, 0);
if (fd == -1) {
if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
rb_gc();
if (retried)
rb_sys_fail(o.pathname);
retried = 1;
goto retry;
}
if (fd == -1) {
int saved_errno = errno;
if (!st_lookup(errno2sym, (st_data_t)errno, &rv)) {
errno = saved_errno;
rb_sys_fail(o.pathname);
}
return rv;
}
}
rv = rb_funcall(klass, id_for_fd, 1, INT2FIX(fd));
set_file_path(rv, pathname);
return rv;
}