static int keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr, rb_thread_t *th) { *rest_hash_ptr = rb_check_hash_type(*kw_hash_ptr); if (!NIL_P(*rest_hash_ptr)) { VALUE hash = rb_extract_keywords(rest_hash_ptr); if (!hash) hash = Qnil; *kw_hash_ptr = hash; return TRUE; } else { *kw_hash_ptr = Qnil; return FALSE; } }
int rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...) { int i; const char *p = fmt; VALUE *var; va_list vargs; int f_var = 0, f_hash = 0, f_block = 0; int n_lead = 0, n_opt = 0, n_trail = 0, n_mand; int argi = 0; VALUE hash = Qnil; if (ISDIGIT(*p)) { n_lead = *p - '0'; p++; if (ISDIGIT(*p)) { n_opt = *p - '0'; p++; if (ISDIGIT(*p)) { n_trail = *p - '0'; p++; goto block_arg; } } } if (*p == '*') { f_var = 1; p++; if (ISDIGIT(*p)) { n_trail = *p - '0'; p++; } } block_arg: if (*p == ':') { f_hash = 1; p++; } if (*p == '&') { f_block = 1; p++; } if (*p != '\0') { rb_fatal("bad scan arg format: %s", fmt); } n_mand = n_lead + n_trail; if (argc < n_mand) goto argc_error; va_start(vargs, fmt); /* capture an option hash - phase 1: pop */ if (f_hash && n_mand < argc) { VALUE last = argv[argc - 1]; if (NIL_P(last)) { /* nil is taken as an empty option hash only if it is not ambiguous; i.e. '*' is not specified and arguments are given more than sufficient */ if (!f_var && n_mand + n_opt < argc) argc--; } else { hash = rb_check_hash_type(last); if (!NIL_P(hash)) { VALUE opts = rb_extract_keywords(&hash); if (!hash) argc--; hash = opts ? opts : Qnil; } } } /* capture leading mandatory arguments */ for (i = n_lead; i-- > 0; ) { var = va_arg(vargs, VALUE *); if (var) *var = argv[argi]; argi++; } /* capture optional arguments */ for (i = n_opt; i-- > 0; ) { var = va_arg(vargs, VALUE *); if (argi < argc - n_trail) { if (var) *var = argv[argi]; argi++; } else { if (var) *var = Qnil; } } /* capture variable length arguments */ if (f_var) { int n_var = argc - argi - n_trail; var = va_arg(vargs, VALUE *); if (0 < n_var) { if (var) *var = rb_ary_new4(n_var, &argv[argi]); argi += n_var; } else { if (var) *var = rb_ary_new(); } } /* capture trailing mandatory arguments */ for (i = n_trail; i-- > 0; ) { var = va_arg(vargs, VALUE *); if (var) *var = argv[argi]; argi++; } /* capture an option hash - phase 2: assignment */ if (f_hash) { var = va_arg(vargs, VALUE *); if (var) *var = hash; } /* capture iterator block */ if (f_block) { var = va_arg(vargs, VALUE *); if (rb_block_given_p()) { *var = rb_block_proc(); } else { *var = Qnil; } } va_end(vargs); if (argi < argc) { argc_error: rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt); } return argc; }