[ruby/io-console] Check rawmode option names strictly

https://github.com/ruby/io-console/commit/aa8fc7e947
This commit is contained in:
Nobuyoshi Nakada 2022-12-02 17:39:30 +09:00 committed by git
parent 7390eb43fe
commit 678bcfcaa6
2 changed files with 38 additions and 10 deletions

View File

@ -75,7 +75,7 @@ getattr(int fd, conmode *t)
#define SET_LAST_ERROR (0) #define SET_LAST_ERROR (0)
#endif #endif
static ID id_getc, id_console, id_close, id_min, id_time, id_intr; static ID id_getc, id_console, id_close;
#if ENABLE_IO_GETPASS #if ENABLE_IO_GETPASS
static ID id_gets, id_chomp_bang; static ID id_gets, id_chomp_bang;
#endif #endif
@ -112,18 +112,34 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
} }
#endif #endif
enum rawmode_opt_ids {
kwd_min,
kwd_time,
kwd_intr,
rawmode_opt_id_count
};
static ID rawmode_opt_ids[rawmode_opt_id_count];
typedef struct { typedef struct {
int vmin; int vmin;
int vtime; int vtime;
int intr; int intr;
} rawmode_arg_t; } rawmode_arg_t;
#ifndef UNDEF_P
# define UNDEF_P(obj) ((obj) == Qundef)
#endif
#ifndef NIL_OR_UNDEF_P
# define NIL_OR_UNDEF_P(obj) (NIL_P(obj) || UNDEF_P(obj))
#endif
static rawmode_arg_t * static rawmode_arg_t *
rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts) rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts)
{ {
int argc = *argcp; int argc = *argcp;
rawmode_arg_t *optp = NULL; rawmode_arg_t *optp = NULL;
VALUE vopts = Qnil; VALUE vopts = Qnil;
VALUE optvals[rawmode_opt_id_count];
#ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS #ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS
argc = rb_scan_args(argc, argv, "*:", NULL, &vopts); argc = rb_scan_args(argc, argv, "*:", NULL, &vopts);
#else #else
@ -138,19 +154,20 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
} }
#endif #endif
rb_check_arity(argc, min_argc, max_argc); rb_check_arity(argc, min_argc, max_argc);
if (!NIL_P(vopts)) { if (rb_get_kwargs(vopts, rawmode_opt_ids,
VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min)); 0, rawmode_opt_id_count, optvals)) {
VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time)); VALUE vmin = optvals[kwd_min];
VALUE intr = rb_hash_aref(vopts, ID2SYM(id_intr)); VALUE vtime = optvals[kwd_time];
VALUE intr = optvals[kwd_intr];
/* default values by `stty raw` */ /* default values by `stty raw` */
opts->vmin = 1; opts->vmin = 1;
opts->vtime = 0; opts->vtime = 0;
opts->intr = 0; opts->intr = 0;
if (!NIL_P(vmin)) { if (!NIL_OR_UNDEF_P(vmin)) {
opts->vmin = NUM2INT(vmin); opts->vmin = NUM2INT(vmin);
optp = opts; optp = opts;
} }
if (!NIL_P(vtime)) { if (!NIL_OR_UNDEF_P(vtime)) {
VALUE v10 = INT2FIX(10); VALUE v10 = INT2FIX(10);
vtime = rb_funcall3(vtime, '*', 1, &v10); vtime = rb_funcall3(vtime, '*', 1, &v10);
opts->vtime = NUM2INT(vtime); opts->vtime = NUM2INT(vtime);
@ -165,6 +182,7 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
opts->intr = 0; opts->intr = 0;
optp = opts; optp = opts;
break; break;
case Qundef:
case Qnil: case Qnil:
break; break;
default: default:
@ -1633,9 +1651,11 @@ Init_console(void)
#endif #endif
id_console = rb_intern("console"); id_console = rb_intern("console");
id_close = rb_intern("close"); id_close = rb_intern("close");
id_min = rb_intern("min"); #define init_rawmode_opt_id(name) \
id_time = rb_intern("time"); rawmode_opt_ids[kwd_##name] = rb_intern(#name)
id_intr = rb_intern("intr"); init_rawmode_opt_id(min);
init_rawmode_opt_id(time);
init_rawmode_opt_id(intr);
#ifndef HAVE_RB_F_SEND #ifndef HAVE_RB_F_SEND
id___send__ = rb_intern("__send__"); id___send__ = rb_intern("__send__");
#endif #endif

View File

@ -49,6 +49,14 @@ class TestIO_Console < Test::Unit::TestCase
assert_include(e.message, IO::NULL) assert_include(e.message, IO::NULL)
end end
end end
def test_bad_keyword
assert_raise_with_message(ArgumentError, /unknown keyword:.*bad/) do
File.open(IO::NULL) do |f|
f.raw(bad: 0)
end
end
end
end end
defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do