#include #include #include #include #include #include #include #include #include #include #include static const struct { #define itsdefined(name, sym) { sym, name }, mrb_sym sym; int eno; } e2c[] = { #define itsnotdefined(name, sym) #include "known_errors_def.cstub" #undef itsdefined #undef itsnotdefined }; static const struct { #define itsnotdefined(name, sym) { sym }, mrb_sym sym; } noe2c[] = { #define itsdefined(name, sym) #include "known_errors_def.cstub" #undef itsdefined #undef itsnotdefined }; #define ENTRY_SYM(e) (e).sym #define E2C_LEN (sizeof(e2c) / sizeof(e2c[0])) #define NOE2C_LEN (sizeof(noe2c) / sizeof(noe2c[0])) static struct RClass* mrb_errno_define_exxx(mrb_state *mrb, mrb_sym name, int eno) { struct RClass *errno_module = mrb_module_get_id(mrb, MRB_SYM(Errno)); if (mrb_const_defined_at(mrb, mrb_obj_value(errno_module), name)) { mrb_value v = mrb_const_get(mrb, mrb_obj_value(errno_module), name); if (mrb_class_p(v)) { return mrb_class_ptr(v); } } struct RClass *sce_class = mrb_exc_get_id(mrb, MRB_SYM(SystemCallError)); struct RClass *e = mrb_define_class_under_id(mrb, errno_module, name, sce_class); mrb_define_const_id(mrb, e, MRB_SYM(Errno), mrb_fixnum_value(eno)); return e; } typedef mrb_sym sym_ref; #define sym_ref_init(mrb, id) (id) #define errno_name_matched_p(errentry, ref) ((errentry).sym == *(ref)) static mrb_bool ary_included_in_head(mrb_state *mrb, mrb_value ary, mrb_value obj, mrb_ssize head) { const mrb_value *p = RARRAY_PTR(ary); for (; head > 0; head--, p++) { if (mrb_obj_eq(mrb, obj, *p)) { return TRUE; } } return FALSE; } /* * Internal method used by the Errno module to check if a specific * error constant exists on this platform. */ static mrb_value mrb_errno_defined_p(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); const sym_ref ref = sym_ref_init(mrb, name); for (size_t i = 0; i < E2C_LEN; i++) { if (errno_name_matched_p(e2c[i], &ref)) { return mrb_true_value(); } } for (size_t i = 0; i < NOE2C_LEN; i++) { if (errno_name_matched_p(noe2c[i], &ref)) { return mrb_true_value(); } } return mrb_false_value(); } /* * Internal method used by the Errno module to define errno classes * for error constants that exist on this platform. */ static mrb_value mrb_errno_define(mrb_state *mrb, mrb_value self) { mrb_sym name; mrb_get_args(mrb, "n", &name); const sym_ref ref = sym_ref_init(mrb, name); for (size_t i = 0; i < E2C_LEN; i++) { if (errno_name_matched_p(e2c[i], &ref)) { return mrb_obj_value(mrb_errno_define_exxx(mrb, name, e2c[i].eno)); } } for (size_t i = 0; i < NOE2C_LEN; i++) { if (errno_name_matched_p(noe2c[i], &ref)) { struct RClass *errno_module = mrb_module_get_id(mrb, MRB_SYM(Errno)); return mrb_obj_value(mrb_class_get_under_id(mrb, errno_module, MRB_SYM(NOERROR))); } } return mrb_nil_value(); } /* * Internal method used by the Errno module to populate an array * with all errno symbols available on this platform. */ static mrb_value mrb_errno_list(mrb_state *mrb, mrb_value self) { mrb_value list; mrb_get_args(mrb, "A", &list); mrb_ary_modify(mrb, mrb_ary_ptr(list)); mrb_ssize head = RARRAY_LEN(list); for (size_t i = 0; i < E2C_LEN; i++) { mrb_value id = mrb_symbol_value(ENTRY_SYM(e2c[i])); if (!ary_included_in_head(mrb, list, id, head)) { mrb_ary_push(mrb, list, id); } } for (size_t i = 0; i < NOE2C_LEN; i++) { mrb_value id = mrb_symbol_value(ENTRY_SYM(noe2c[i])); if (!ary_included_in_head(mrb, list, id, head)) { mrb_ary_push(mrb, list, id); } } return list; } static void mrb_sce_init(mrb_state *mrb, mrb_value self, mrb_value m, mrb_value no) { mrb_value str; char buf[20]; if (!mrb_nil_p(no)) { size_t i; int n = (int)mrb_as_int(mrb, no); for (i=0; i < E2C_LEN; i++) { if (e2c[i].eno == n) { mrb_basic_ptr(self)->c = mrb_errno_define_exxx(mrb, ENTRY_SYM(e2c[i]), e2c[i].eno); str = mrb_str_new_cstr(mrb, strerror(n)); break; } } if (i == E2C_LEN) { mrb_iv_set(mrb, self, MRB_SYM(errno), mrb_fixnum_value(n)); str = mrb_str_new_cstr(mrb, "Unknown error: "); char *bp = mrb_int_to_cstr(buf, sizeof(buf), n, 10); mrb_str_cat2(mrb, str, bp); } } else { str = mrb_str_new_cstr(mrb, "unknown error"); } if (!mrb_nil_p(m)) { mrb_str_cat2(mrb, str, " - "); mrb_str_append(mrb, str, m); } mrb_exc_mesg_set(mrb, mrb_exc_ptr(self), str); } /* * call-seq: * errno_class.new(message = nil) -> errno_exception * * Creates a new instance of a specific errno exception class. * The optional message parameter provides additional context. * * Errno::ENOENT.new #=> # * Errno::ENOENT.new("custom message") #=> # */ static mrb_value mrb_exxx_init(mrb_state *mrb, mrb_value self) { mrb_value m = mrb_nil_value(); mrb_get_args(mrb, "|S", &m); mrb_sce_init(mrb, self, m, mrb_nil_value()); return self; } /* * call-seq: * SystemCallError.new(message) -> system_call_error * SystemCallError.new(errno) -> system_call_error * SystemCallError.new(message, errno) -> system_call_error * * Creates a new SystemCallError exception. Can be called with: * - A message string only * - An errno number only * - Both a message string and errno number * * SystemCallError.new("custom error") #=> # * SystemCallError.new(2) #=> # * SystemCallError.new("failed", 2) #=> # */ static mrb_value mrb_sce_init_m(mrb_state *mrb, mrb_value self) { if (mrb_class(mrb, self) != mrb_exc_get_id(mrb, MRB_SYM(SystemCallError))) { return mrb_exxx_init(mrb, self); } mrb_value m, n; if (mrb_get_args(mrb, "o|o", &m, &n) == 1) { if (mrb_fixnum_p(m)) { n = m; m = mrb_nil_value(); } else { n = mrb_nil_value(); } } mrb_sce_init(mrb, self, m, n); return self; } /* * call-seq: * system_call_error.errno -> integer or nil * * Returns the errno number associated with this SystemCallError. * Returns nil if no errno was set. * * begin * File.open("/nonexistent") * rescue SystemCallError => e * e.errno #=> 2 (ENOENT) * end */ static mrb_value mrb_sce_errno(mrb_state *mrb, mrb_value self) { struct RClass *c; mrb_sym sym; c = mrb_class(mrb, self); sym = MRB_SYM(Errno); if (mrb_const_defined_at(mrb, mrb_obj_value(c), sym)) { return mrb_const_get(mrb, mrb_obj_value(c), sym); } else { sym = MRB_SYM(errno); return mrb_attr_get(mrb, self, sym); } } /* * call-seq: * SystemCallError._sys_fail(errno, message = nil) * * Internal method that raises a SystemCallError with the given errno * and optional message. This method does not return as it raises an exception. * * SystemCallError._sys_fail(2) # raises Errno::ENOENT * SystemCallError._sys_fail(2, "failed") # raises Errno::ENOENT with message */ static mrb_value mrb_sce_sys_fail(mrb_state *mrb, mrb_value cls) { mrb_value msg, no; mrb->c->ci->mid = 0; struct RClass *sce = mrb_class_ptr(cls); mrb_int argc = mrb_get_args(mrb, "o|S", &no, &msg); struct RBasic* e = mrb_obj_alloc(mrb, MRB_TT_EXCEPTION, sce); mrb_value exc = mrb_obj_value(e); if (argc == 1) { msg = mrb_nil_value(); } mrb_sce_init(mrb, exc, msg, no); mrb_exc_raise(mrb, exc); return mrb_nil_value(); /* NOTREACHED */ } void mrb_mruby_errno_gem_init(mrb_state *mrb) { struct RClass *sce = mrb_define_class_id(mrb, MRB_SYM(SystemCallError), E_STANDARD_ERROR); mrb_define_class_method_id(mrb, sce, MRB_SYM(_sys_fail), mrb_sce_sys_fail, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, sce, MRB_SYM(errno), mrb_sce_errno, MRB_ARGS_NONE()); mrb_define_method_id(mrb, sce, MRB_SYM(initialize), mrb_sce_init_m, MRB_ARGS_OPT(2)); struct RClass *eno = mrb_define_module_id(mrb, MRB_SYM(Errno)); mrb_define_class_method_id(mrb, eno, MRB_SYM_Q(__errno_defined), mrb_errno_defined_p, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, eno, MRB_SYM(__errno_define), mrb_errno_define, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, eno, MRB_SYM(__errno_list), mrb_errno_list, MRB_ARGS_REQ(1)); struct RClass *e = mrb_define_class_under_id(mrb, eno, MRB_SYM(NOERROR), sce); mrb_define_const_id(mrb, e, MRB_SYM(Errno), mrb_fixnum_value(0)); //mrb_define_method_id(mrb, e, MRB_SYM(eqq), mrb_exxx_cmp, MRB_ARGS_REQ(1)); // Pre-allocation for Errno::ENOMEM only mrb_errno_define_exxx(mrb, MRB_SYM(ENOMEM), ENOMEM); } void mrb_mruby_errno_gem_final(mrb_state *mrb) { }