/*
* call-seq:
* POSIX_MQ.new(name [, flags [, mode [, mq_attr]]) => mq
*
* Opens a POSIX message queue given by +name+. +name+ should start
* with a slash ("/") for portable applications.
*
* If a Symbol is given in place of integer +flags+, then:
*
* * +:r+ is equivalent to IO::RDONLY
* * +:w+ is equivalent to IO::CREAT|IO::WRONLY
* * +:rw+ is equivalent to IO::CREAT|IO::RDWR
*
* +mode+ is an integer and only used when IO::CREAT is used.
* +mq_attr+ is a POSIX_MQ::Attr and only used if IO::CREAT is used.
* If +mq_attr+ is not specified when creating a queue, then the
* system defaults will be used.
*
* See the manpage for mq_open(3) for more details on this function.
*/
static VALUE init(int argc, VALUE *argv, VALUE self)
{
struct posix_mq *mq = get(self, 0);
struct open_args x;
VALUE name, oflags, mode, attr;
rb_scan_args(argc, argv, "13", &name, &oflags, &mode, &attr);
switch (TYPE(oflags)) {
case T_NIL:
x.oflags = O_RDONLY;
break;
case T_SYMBOL:
if (oflags == sym_r)
x.oflags = O_RDONLY;
else if (oflags == sym_w)
x.oflags = O_CREAT|O_WRONLY;
else if (oflags == sym_rw)
x.oflags = O_CREAT|O_RDWR;
else {
oflags = rb_inspect(oflags);
rb_raise(rb_eArgError,
"symbol must be :r, :w, or :rw: %s",
StringValuePtr(oflags));
}
break;
case T_BIGNUM:
case T_FIXNUM:
x.oflags = NUM2INT(oflags);
break;
default:
rb_raise(rb_eArgError, "flags must be an int, :r, :w, or :wr");
}
x.name = StringValueCStr(name);
x.argc = 2;
switch (TYPE(mode)) {
case T_FIXNUM:
x.argc = 3;
x.mode = NUM2UINT(mode);
break;
case T_NIL:
if (x.oflags & O_CREAT) {
x.argc = 3;
x.mode = 0666;
}
break;
default:
rb_raise(rb_eArgError, "mode not an integer");
}
switch (TYPE(attr)) {
case T_STRUCT:
x.argc = 4;
rstruct2mqattr(&x.attr, attr, 1);
/* principle of least surprise */
if (x.attr.mq_flags & O_NONBLOCK)
x.oflags |= O_NONBLOCK;
break;
case T_NIL:
break;
default:
check_struct_type(attr);
}
(void)xopen(&x);
mq->des = x.des;
if (mq->des == MQD_INVALID) {
switch (errno) {
case ENOMEM:
case EMFILE:
case ENFILE:
case ENOSPC:
rb_gc();
(void)xopen(&x);
mq->des = x.des;
}
if (mq->des == MQD_INVALID)
rb_sys_fail("mq_open");
}
mq->name = rb_str_dup(name);
if (x.oflags & O_NONBLOCK)
mq->attr.mq_flags = O_NONBLOCK;
return self;
}