diff options
Diffstat (limited to 'ext/io')
| -rw-r--r-- | ext/io/console/.document | 2 | ||||
| -rw-r--r-- | ext/io/console/console.c | 1481 | ||||
| -rw-r--r-- | ext/io/console/depend | 168 | ||||
| -rw-r--r-- | ext/io/console/extconf.rb | 44 | ||||
| -rw-r--r-- | ext/io/console/io-console.gemspec | 49 | ||||
| -rw-r--r-- | ext/io/console/win32_vk.inc | 362 | ||||
| -rw-r--r-- | ext/io/console/win32_vk.list | 2 | ||||
| -rw-r--r-- | ext/io/nonblock/depend | 161 | ||||
| -rw-r--r-- | ext/io/nonblock/extconf.rb | 7 | ||||
| -rw-r--r-- | ext/io/nonblock/io-nonblock.gemspec | 25 | ||||
| -rw-r--r-- | ext/io/nonblock/nonblock.c | 136 | ||||
| -rw-r--r-- | ext/io/wait/depend | 161 | ||||
| -rw-r--r-- | ext/io/wait/extconf.rb | 17 | ||||
| -rw-r--r-- | ext/io/wait/io-wait.gemspec | 39 | ||||
| -rw-r--r-- | ext/io/wait/wait.c | 237 |
15 files changed, 2163 insertions, 728 deletions
diff --git a/ext/io/console/.document b/ext/io/console/.document new file mode 100644 index 0000000000..945a377256 --- /dev/null +++ b/ext/io/console/.document @@ -0,0 +1,2 @@ +console.c +lib/size.rb diff --git a/ext/io/console/console.c b/ext/io/console/console.c index 04cf9e3f9c..5f3c9478f7 100644 --- a/ext/io/console/console.c +++ b/ext/io/console/console.c @@ -1,9 +1,14 @@ -/* -*- c-file-style: "ruby" -*- */ +/* -*- c-file-style: "ruby"; indent-tabs-mode: t -*- */ /* * console IO module */ + +static const char *const +IO_CONSOLE_VERSION = "0.8.2"; + #include "ruby.h" #include "ruby/io.h" +#include "ruby/thread.h" #ifdef HAVE_UNISTD_H #include <unistd.h> @@ -14,12 +19,6 @@ #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif -#ifndef RARRAY_CONST_PTR -# define RARRAY_CONST_PTR(ary) RARRAY_PTR(ary) -#endif -#ifndef HAVE_RB_FUNCALLV -# define rb_funcallv rb_funcall2 -#endif #if defined HAVE_TERMIOS_H # include <termios.h> @@ -28,7 +27,7 @@ typedef struct termios conmode; static int setattr(int fd, conmode *t) { - while (tcsetattr(fd, TCSAFLUSH, t)) { + while (tcsetattr(fd, TCSANOW, t)) { if (errno != EINTR) return 0; } return 1; @@ -54,6 +53,7 @@ typedef struct sgttyb conmode; # endif #elif defined _WIN32 #include <winioctl.h> +#include <conio.h> typedef DWORD conmode; #define LAST_ERROR rb_w32_map_errno(GetLastError()) @@ -79,12 +79,71 @@ getattr(int fd, conmode *t) #define SET_LAST_ERROR (0) #endif -static ID id_getc, id_console, id_close, id_min, id_time; -#if ENABLE_IO_GETPASS -static ID id_gets; +#define CSI "\x1b\x5b" + +static ID id_getc, id_close; +static ID id_gets, id_flush, id_chomp_bang; + +#ifndef HAVE_RB_INTERNED_STR_CSTR +# define rb_str_to_interned_str(str) rb_str_freeze(str) +# define rb_interned_str_cstr(str) rb_str_freeze(rb_usascii_str_new_cstr(str)) +#endif + +#if defined HAVE_RUBY_FIBER_SCHEDULER_H +# include "ruby/fiber/scheduler.h" +#elif defined HAVE_RB_SCHEDULER_TIMEOUT +extern VALUE rb_scheduler_timeout(struct timeval *timeout); +# define rb_fiber_scheduler_make_timeout rb_scheduler_timeout +#endif + +#ifndef HAVE_RB_IO_DESCRIPTOR +static int +io_descriptor_fallback(VALUE io) +{ + rb_io_t *fptr; + GetOpenFile(io, fptr); + return fptr->fd; +} +#define rb_io_descriptor io_descriptor_fallback +#endif + +#ifndef HAVE_RB_IO_PATH +static VALUE +io_path_fallback(VALUE io) +{ + rb_io_t *fptr; + GetOpenFile(io, fptr); + return fptr->pathv; +} +#define rb_io_path io_path_fallback +#endif + +#ifndef HAVE_RB_IO_GET_WRITE_IO +static VALUE +io_get_write_io_fallback(VALUE io) +{ + rb_io_t *fptr; + GetOpenFile(io, fptr); + VALUE wio = fptr->tied_io_for_writing; + return wio ? wio : io; +} +#define rb_io_get_write_io io_get_write_io_fallback +#endif + +#ifndef DHAVE_RB_SYSERR_FAIL_STR +# define rb_syserr_fail_str(e, mesg) rb_exc_raise(rb_syserr_new_str(e, mesg)) #endif +#define sys_fail(io) do { \ + int err = errno; \ + rb_syserr_fail_str(err, rb_io_path(io)); \ +} while (0) + #ifndef HAVE_RB_F_SEND +#ifndef RB_PASS_CALLED_KEYWORDS +# define rb_funcallv_kw(recv, mid, arg, argv, kw_splat) rb_funcallv(recv, mid, arg, argv) +#endif + static ID id___send__; static VALUE @@ -99,41 +158,87 @@ rb_f_send(int argc, VALUE *argv, VALUE recv) else { vid = id___send__; } - return rb_funcallv(recv, vid, argc, argv); + return rb_funcallv_kw(recv, vid, argc, argv, RB_PASS_CALLED_KEYWORDS); } #endif -#ifndef HAVE_RB_SYM2STR -# define rb_sym2str(sym) rb_id2str(SYM2ID(sym)) -#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 { int vmin; int vtime; + int intr; } 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 * -rawmode_opt(int argc, VALUE *argv, rawmode_arg_t *opts) +rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts) { + int argc = *argcp; rawmode_arg_t *optp = NULL; - VALUE vopts; - rb_scan_args(argc, argv, "0:", &vopts); - if (!NIL_P(vopts)) { - VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min)); - VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time)); + VALUE vopts = Qnil; + VALUE optvals[rawmode_opt_id_count]; +#ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS + argc = rb_scan_args(argc, argv, "*:", NULL, &vopts); +#else + if (argc > min_argc) { + vopts = rb_check_hash_type(argv[argc-1]); + if (!NIL_P(vopts)) { + argv[argc-1] = vopts; + vopts = rb_extract_keywords(&argv[argc-1]); + if (!argv[argc-1]) *argcp = --argc; + if (!vopts) vopts = Qnil; + } + } +#endif + rb_check_arity(argc, min_argc, max_argc); + if (rb_get_kwargs(vopts, rawmode_opt_ids, + 0, rawmode_opt_id_count, optvals)) { + VALUE vmin = optvals[kwd_min]; + VALUE vtime = optvals[kwd_time]; + VALUE intr = optvals[kwd_intr]; /* default values by `stty raw` */ opts->vmin = 1; opts->vtime = 0; - if (!NIL_P(vmin)) { + opts->intr = 0; + if (!NIL_OR_UNDEF_P(vmin)) { opts->vmin = NUM2INT(vmin); optp = opts; } - if (!NIL_P(vtime)) { + if (!NIL_OR_UNDEF_P(vtime)) { VALUE v10 = INT2FIX(10); vtime = rb_funcall3(vtime, '*', 1, &v10); opts->vtime = NUM2INT(vtime); optp = opts; } + switch (intr) { + case Qtrue: + opts->intr = 1; + optp = opts; + break; + case Qfalse: + opts->intr = 0; + optp = opts; + break; + case Qundef: + case Qnil: + break; + default: + rb_raise(rb_eArgError, "true or false expected as intr: %"PRIsVALUE, + intr); + } } return optp; } @@ -145,24 +250,36 @@ set_rawmode(conmode *t, void *arg) cfmakeraw(t); t->c_lflag &= ~(ECHOE|ECHOK); #elif defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H - t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + t->c_iflag &= ~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL); t->c_oflag &= ~OPOST; - t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN); + t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN|XCASE); t->c_cflag &= ~(CSIZE|PARENB); t->c_cflag |= CS8; + t->c_cc[VMIN] = 1; + t->c_cc[VTIME] = 0; #elif defined HAVE_SGTTY_H t->sg_flags &= ~ECHO; t->sg_flags |= RAW; #elif defined _WIN32 *t = 0; #endif -#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H if (arg) { const rawmode_arg_t *r = arg; +#ifdef VMIN if (r->vmin >= 0) t->c_cc[VMIN] = r->vmin; +#endif +#ifdef VTIME if (r->vtime >= 0) t->c_cc[VTIME] = r->vtime; - } #endif +#ifdef ISIG + if (r->intr) { + t->c_iflag |= BRKINT; + t->c_lflag |= ISIG; + t->c_oflag |= OPOST; + } +#endif + (void)r; + } } static void @@ -226,33 +343,21 @@ set_ttymode(int fd, conmode *t, void (*setter)(conmode *, void *), void *arg) return setattr(fd, &r); } -#define GetReadFD(fptr) ((fptr)->fd) - -static inline int -get_write_fd(const rb_io_t *fptr) -{ - VALUE wio = fptr->tied_io_for_writing; - rb_io_t *ofptr; - if (!wio) return fptr->fd; - GetOpenFile(wio, ofptr); - return ofptr->fd; -} -#define GetWriteFD(fptr) get_write_fd(fptr) +#define GetReadFD(io) rb_io_descriptor(io) +#define GetWriteFD(io) rb_io_descriptor(rb_io_get_write_io(io)) #define FD_PER_IO 2 static VALUE -ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void *arg) +ttymode(VALUE io, VALUE (*func)(VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg) { - rb_io_t *fptr; int status = -1; int error = 0; int fd[FD_PER_IO]; conmode t[FD_PER_IO]; VALUE result = Qnil; - GetOpenFile(io, fptr); - fd[0] = GetReadFD(fptr); + fd[0] = GetReadFD(io); if (fd[0] != -1) { if (set_ttymode(fd[0], t+0, setter, arg)) { status = 0; @@ -262,7 +367,7 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void fd[0] = -1; } } - fd[1] = GetWriteFD(fptr); + fd[1] = GetWriteFD(io); if (fd[1] != -1 && fd[1] != fd[0]) { if (set_ttymode(fd[1], t+1, setter, arg)) { status = 0; @@ -273,16 +378,15 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void } } if (status == 0) { - result = rb_protect(func, io, &status); + result = rb_protect(func, farg, &status); } - GetOpenFile(io, fptr); - if (fd[0] != -1 && fd[0] == GetReadFD(fptr)) { + if (fd[0] != -1 && fd[0] == GetReadFD(io)) { if (!setattr(fd[0], t+0)) { error = errno; status = -1; } } - if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(fptr)) { + if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(io)) { if (!setattr(fd[1], t+1)) { error = errno; status = -1; @@ -297,11 +401,36 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void return result; } +#if !defined _WIN32 +struct ttymode_callback_args { + VALUE (*func)(VALUE, VALUE); + VALUE io; + VALUE farg; +}; + +static VALUE +ttymode_callback(VALUE args) +{ + struct ttymode_callback_args *argp = (struct ttymode_callback_args *)args; + return argp->func(argp->io, argp->farg); +} + +static VALUE +ttymode_with_io(VALUE io, VALUE (*func)(VALUE, VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg) +{ + struct ttymode_callback_args cargs; + cargs.func = func; + cargs.io = io; + cargs.farg = farg; + return ttymode(io, ttymode_callback, (VALUE)&cargs, setter, arg); +} +#endif + /* * call-seq: - * io.raw(min: nil, time: nil) {|io| } + * io.raw(min: nil, time: nil, intr: nil) {|io| } * - * Yields +self+ within raw mode. + * Yields +self+ within raw mode, and returns the result of the block. * * STDIN.raw(&:gets) * @@ -313,6 +442,9 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void * The parameter +time+ specifies the timeout in _seconds_ with a * precision of 1/10 of a second. (default: 0) * + * If the parameter +intr+ is +true+, enables break, interrupt, quit, + * and suspend special characters. + * * Refer to the manual page of termios for further details. * * You must require 'io/console' to use this method. @@ -320,17 +452,17 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void static VALUE console_raw(int argc, VALUE *argv, VALUE io) { - rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts); - return ttymode(io, rb_yield, set_rawmode, optp); + rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); + return ttymode(io, rb_yield, io, set_rawmode, optp); } /* * call-seq: - * io.raw!(min: nil, time: nil) + * io.raw!(min: nil, time: nil, intr: nil) -> io * - * Enables raw mode. + * Enables raw mode, and returns +io+. * - * If the terminal mode needs to be back, use io.raw { ... }. + * If the terminal mode needs to be back, use <code>io.raw { ... }</code>. * * See IO#raw for details on the parameters. * @@ -340,15 +472,11 @@ static VALUE console_set_raw(int argc, VALUE *argv, VALUE io) { conmode t; - rb_io_t *fptr; - int fd; - rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts); - - GetOpenFile(io, fptr); - fd = GetReadFD(fptr); - if (!getattr(fd, &t)) rb_sys_fail(0); + rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); + int fd = GetReadFD(io); + if (!getattr(fd, &t)) sys_fail(io); set_rawmode(&t, optp); - if (!setattr(fd, &t)) rb_sys_fail(0); + if (!setattr(fd, &t)) sys_fail(io); return io; } @@ -367,7 +495,7 @@ console_set_raw(int argc, VALUE *argv, VALUE io) static VALUE console_cooked(VALUE io) { - return ttymode(io, rb_yield, set_cookedmode, NULL); + return ttymode(io, rb_yield, io, set_cookedmode, NULL); } /* @@ -384,26 +512,45 @@ static VALUE console_set_cooked(VALUE io) { conmode t; - rb_io_t *fptr; - int fd; - - GetOpenFile(io, fptr); - fd = GetReadFD(fptr); - if (!getattr(fd, &t)) rb_sys_fail(0); + int fd = GetReadFD(io); + if (!getattr(fd, &t)) sys_fail(io); set_cookedmode(&t, NULL); - if (!setattr(fd, &t)) rb_sys_fail(0); + if (!setattr(fd, &t)) sys_fail(io); return io; } +#ifndef _WIN32 static VALUE getc_call(VALUE io) { return rb_funcallv(io, id_getc, 0, 0); } +#else +static void * +nogvl_getch(void *p) +{ + int len = 0; + wint_t *buf = p, c = _getwch(); + + switch (c) { + case WEOF: + break; + case 0x00: + case 0xe0: + buf[len++] = c; + c = _getwch(); + /* fall through */ + default: + buf[len++] = c; + break; + } + return (void *)(VALUE)len; +} +#endif /* * call-seq: - * io.getch(min: nil, time: nil) -> char + * io.getch(min: nil, time: nil, intr: nil) -> char * * Reads and returns a character in raw mode. * @@ -414,8 +561,78 @@ getc_call(VALUE io) static VALUE console_getch(int argc, VALUE *argv, VALUE io) { - rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts); - return ttymode(io, getc_call, set_rawmode, optp); + rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); +#ifndef _WIN32 + return ttymode(io, getc_call, io, set_rawmode, optp); +#else + rb_io_t *fptr; + VALUE str; + wint_t c; + int len; + char buf[8]; + wint_t wbuf[2]; +# ifndef HAVE_RB_IO_WAIT + struct timeval *to = NULL, tv; +# else + VALUE timeout = Qnil; +# endif + + GetOpenFile(io, fptr); + if (optp) { + if (optp->vtime) { +# ifndef HAVE_RB_IO_WAIT + to = &tv; +# else + struct timeval tv; +# endif + tv.tv_sec = optp->vtime / 10; + tv.tv_usec = (optp->vtime % 10) * 100000; +# ifdef HAVE_RB_IO_WAIT + timeout = rb_fiber_scheduler_make_timeout(&tv); +# endif + } + switch (optp->vmin) { + case 1: /* default */ + break; + case 0: /* return nil when timed out */ + if (optp->vtime) break; + /* fallthru */ + default: + rb_warning("min option larger than 1 ignored"); + } + if (optp->intr) { +# ifndef HAVE_RB_IO_WAIT + int w = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, to); + if (w < 0) rb_eof_error(); + if (!(w & RB_WAITFD_IN)) return Qnil; +# else + VALUE result = rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), timeout); + if (!RTEST(result)) return Qnil; +# endif + } + else if (optp->vtime) { + rb_warning("Non-zero vtime option ignored if intr flag is unset"); + } + } + len = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getch, wbuf, RUBY_UBF_IO, 0); + switch (len) { + case 0: + return Qnil; + case 2: + buf[0] = (char)wbuf[0]; + c = wbuf[1]; + len = 1; + do { + buf[len++] = (unsigned char)c; + } while ((c >>= CHAR_BIT) && len < (int)sizeof(buf)); + return rb_str_new(buf, len); + default: + c = wbuf[0]; + len = rb_uv_to_utf8(buf, c); + str = rb_utf8_str_new(buf, len); + return rb_str_conv_enc(str, NULL, rb_default_external_encoding()); + } +#endif } /* @@ -433,7 +650,7 @@ console_getch(int argc, VALUE *argv, VALUE io) static VALUE console_noecho(VALUE io) { - return ttymode(io, rb_yield, set_noecho, NULL); + return ttymode(io, rb_yield, io, set_noecho, NULL); } /* @@ -450,17 +667,17 @@ static VALUE console_set_echo(VALUE io, VALUE f) { conmode t; - rb_io_t *fptr; - int fd; + int fd = GetReadFD(io); + + if (!getattr(fd, &t)) sys_fail(io); - GetOpenFile(io, fptr); - fd = GetReadFD(fptr); - if (!getattr(fd, &t)) rb_sys_fail(0); if (RTEST(f)) - set_echo(&t, NULL); + set_echo(&t, NULL); else - set_noecho(&t, NULL); - if (!setattr(fd, &t)) rb_sys_fail(0); + set_noecho(&t, NULL); + + if (!setattr(fd, &t)) sys_fail(io); + return io; } @@ -476,15 +693,116 @@ static VALUE console_echo_p(VALUE io) { conmode t; - rb_io_t *fptr; - int fd; + int fd = GetReadFD(io); - GetOpenFile(io, fptr); - fd = GetReadFD(fptr); - if (!getattr(fd, &t)) rb_sys_fail(0); + if (!getattr(fd, &t)) sys_fail(io); return echo_p(&t) ? Qtrue : Qfalse; } +static const rb_data_type_t conmode_type = { + "console-mode", + {0, RUBY_TYPED_DEFAULT_FREE,}, + 0, 0, + RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, +}; +static VALUE cConmode; + +static VALUE +conmode_alloc(VALUE klass) +{ + return rb_data_typed_object_zalloc(klass, sizeof(conmode), &conmode_type); +} + +static VALUE +conmode_new(VALUE klass, const conmode *t) +{ + VALUE obj = conmode_alloc(klass); + *(conmode *)DATA_PTR(obj) = *t; + return obj; +} + +static VALUE +conmode_init_copy(VALUE obj, VALUE obj2) +{ + conmode *t = rb_check_typeddata(obj, &conmode_type); + conmode *t2 = rb_check_typeddata(obj2, &conmode_type); + *t = *t2; + return obj; +} + +static VALUE +conmode_set_echo(VALUE obj, VALUE f) +{ + conmode *t = rb_check_typeddata(obj, &conmode_type); + if (RTEST(f)) + set_echo(t, NULL); + else + set_noecho(t, NULL); + return obj; +} + +static VALUE +conmode_set_raw(int argc, VALUE *argv, VALUE obj) +{ + conmode *t = rb_check_typeddata(obj, &conmode_type); + rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); + + set_rawmode(t, optp); + return obj; +} + +static VALUE +conmode_raw_new(int argc, VALUE *argv, VALUE obj) +{ + conmode *r = rb_check_typeddata(obj, &conmode_type); + conmode t = *r; + rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); + + set_rawmode(&t, optp); + return conmode_new(rb_obj_class(obj), &t); +} + +/* + * call-seq: + * io.console_mode -> mode + * + * Returns a data represents the current console mode. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_conmode_get(VALUE io) +{ + conmode t; + int fd = GetReadFD(io); + + if (!getattr(fd, &t)) sys_fail(io); + + return conmode_new(cConmode, &t); +} + +/* + * call-seq: + * io.console_mode = mode + * + * Sets the console mode to +mode+. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_conmode_set(VALUE io, VALUE mode) +{ + conmode *t, r; + int fd = GetReadFD(io); + + TypedData_Get_Struct(mode, conmode, &conmode_type, t); + r = *t; + + if (!setattr(fd, &r)) sys_fail(io); + + return mode; +} + #if defined TIOCGWINSZ typedef struct winsize rb_console_size_t; #define getwinsize(fd, buf) (ioctl((fd), TIOCGWINSZ, (buf)) == 0) @@ -516,16 +834,15 @@ typedef CONSOLE_SCREEN_BUFFER_INFO rb_console_size_t; static VALUE console_winsize(VALUE io) { - rb_io_t *fptr; - int fd; rb_console_size_t ws; - - GetOpenFile(io, fptr); - fd = GetWriteFD(fptr); - if (!getwinsize(fd, &ws)) rb_sys_fail(0); + int fd = GetWriteFD(io); + if (!getwinsize(fd, &ws)) sys_fail(io); return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws))); } +static VALUE console_scroll(VALUE io, int line); +static VALUE console_goto(VALUE io, VALUE y, VALUE x); + /* * call-seq: * io.winsize = [rows, columns] @@ -538,29 +855,26 @@ console_winsize(VALUE io) static VALUE console_set_winsize(VALUE io, VALUE size) { - rb_io_t *fptr; rb_console_size_t ws; #if defined _WIN32 HANDLE wh; int newrow, newcol; - BOOL ret; + COORD oldsize; + SMALL_RECT oldwindow; #endif VALUE row, col, xpixel, ypixel; const VALUE *sz; - int fd; long sizelen; + int fd; - GetOpenFile(io, fptr); size = rb_Array(size); if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) { - rb_raise(rb_eArgError, - "wrong number of arguments (given %ld, expected 2 or 4)", - sizelen); + rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen); } sz = RARRAY_CONST_PTR(size); row = sz[0], col = sz[1], xpixel = ypixel = Qnil; if (sizelen == 4) xpixel = sz[2], ypixel = sz[3]; - fd = GetWriteFD(fptr); + fd = GetWriteFD(io); #if defined TIOCSWINSZ ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0; #define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m) @@ -569,7 +883,7 @@ console_set_winsize(VALUE io, VALUE size) SET(xpixel); SET(ypixel); #undef SET - if (!setwinsize(fd, &ws)) rb_sys_fail(0); + if (!setwinsize(fd, &ws)) sys_fail(io); #elif defined _WIN32 wh = (HANDLE)rb_w32_get_osfhandle(fd); #define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m) @@ -581,18 +895,62 @@ console_set_winsize(VALUE io, VALUE size) if (!GetConsoleScreenBufferInfo(wh, &ws)) { rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo"); } + oldsize = ws.dwSize; + oldwindow = ws.srWindow; + if (ws.srWindow.Right + 1 < newcol) { + ws.dwSize.X = newcol; + } + if (ws.dwSize.Y < newrow) { + ws.dwSize.Y = newrow; + } + /* expand screen buffer first if needed */ + if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) { + rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); + } + /* refresh ws for new dwMaximumWindowSize */ + if (!GetConsoleScreenBufferInfo(wh, &ws)) { + rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo"); + } + /* check new size before modifying buffer content */ + if (newrow <= 0 || newcol <= 0 || + newrow > ws.dwMaximumWindowSize.Y || + newcol > ws.dwMaximumWindowSize.X) { + SetConsoleScreenBufferSize(wh, oldsize); + /* remove scrollbar if possible */ + SetConsoleWindowInfo(wh, TRUE, &oldwindow); + rb_raise(rb_eArgError, "out of range winsize: [%d, %d]", newrow, newcol); + } + /* shrink screen buffer width */ ws.dwSize.X = newcol; - ret = SetConsoleScreenBufferSize(wh, ws.dwSize); + /* shrink screen buffer height if window height were the same */ + if (oldsize.Y == ws.srWindow.Bottom - ws.srWindow.Top + 1) { + ws.dwSize.Y = newrow; + } ws.srWindow.Left = 0; - ws.srWindow.Top = 0; - ws.srWindow.Right = newcol-1; - ws.srWindow.Bottom = newrow-1; + ws.srWindow.Right = newcol - 1; + ws.srWindow.Bottom = ws.srWindow.Top + newrow -1; + if (ws.dwCursorPosition.Y > ws.srWindow.Bottom) { + console_scroll(io, ws.dwCursorPosition.Y - ws.srWindow.Bottom); + ws.dwCursorPosition.Y = ws.srWindow.Bottom; + console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X)); + } + if (ws.srWindow.Bottom > ws.dwSize.Y - 1) { + console_scroll(io, ws.srWindow.Bottom - (ws.dwSize.Y - 1)); + ws.dwCursorPosition.Y -= ws.srWindow.Bottom - (ws.dwSize.Y - 1); + console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X)); + ws.srWindow.Bottom = ws.dwSize.Y - 1; + } + ws.srWindow.Top = ws.srWindow.Bottom - (newrow - 1); + /* perform changes to winsize */ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) { - rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo"); + int last_error = LAST_ERROR; + SetConsoleScreenBufferSize(wh, oldsize); + rb_syserr_fail(last_error, "SetConsoleWindowInfo"); } - /* retry when shrinking buffer after shrunk window */ - if (!ret && !SetConsoleScreenBufferSize(wh, ws.dwSize)) { - rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); + /* perform screen buffer shrinking if necessary */ + if ((ws.dwSize.Y < oldsize.Y || ws.dwSize.X < oldsize.X) && + !SetConsoleScreenBufferSize(wh, ws.dwSize)) { + rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); } /* remove scrollbar if possible */ if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) { @@ -603,6 +961,38 @@ console_set_winsize(VALUE io, VALUE size) } #endif +#ifdef _WIN32 +/* + * call-seq: + * io.check_winsize_changed { ... } -> io + * + * Yields while console input events are queued. + * + * This method is Windows only. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_check_winsize_changed(VALUE io) +{ + HANDLE h; + DWORD num; + + h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io)); + while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) { + INPUT_RECORD rec; + if (ReadConsoleInput(h, &rec, 1, &num)) { + if (rec.EventType == WINDOW_BUFFER_SIZE_EVENT) { + rb_yield(Qnil); + } + } + } + return io; +} +#else +#define console_check_winsize_changed rb_f_notimplement +#endif + /* * call-seq: * io.iflush @@ -614,15 +1004,11 @@ console_set_winsize(VALUE io, VALUE size) static VALUE console_iflush(VALUE io) { - rb_io_t *fptr; - int fd; - - GetOpenFile(io, fptr); - fd = GetReadFD(fptr); #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H - if (tcflush(fd, TCIFLUSH)) rb_sys_fail(0); + int fd = GetReadFD(io); + if (tcflush(fd, TCIFLUSH)) sys_fail(io); #endif - (void)fd; + return io; } @@ -637,13 +1023,9 @@ console_iflush(VALUE io) static VALUE console_oflush(VALUE io) { - rb_io_t *fptr; - int fd; - - GetOpenFile(io, fptr); - fd = GetWriteFD(fptr); + int fd = GetWriteFD(io); #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H - if (tcflush(fd, TCOFLUSH)) rb_sys_fail(0); + if (tcflush(fd, TCOFLUSH)) sys_fail(io); #endif (void)fd; return io; @@ -660,87 +1042,111 @@ console_oflush(VALUE io) static VALUE console_ioflush(VALUE io) { - rb_io_t *fptr; #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H - int fd1, fd2; -#endif + int fd1 = GetReadFD(io); + int fd2 = GetWriteFD(io); - GetOpenFile(io, fptr); -#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H - fd1 = GetReadFD(fptr); - fd2 = GetWriteFD(fptr); if (fd2 != -1 && fd1 != fd2) { - if (tcflush(fd1, TCIFLUSH)) rb_sys_fail(0); - if (tcflush(fd2, TCOFLUSH)) rb_sys_fail(0); + if (tcflush(fd1, TCIFLUSH)) sys_fail(io); + if (tcflush(fd2, TCOFLUSH)) sys_fail(io); } else { - if (tcflush(fd1, TCIOFLUSH)) rb_sys_fail(0); + if (tcflush(fd1, TCIOFLUSH)) sys_fail(io); } #endif + return io; } +/* + * call-seq: + * io.beep + * + * Beeps on the output console. + * + * You must require 'io/console' to use this method. + */ static VALUE console_beep(VALUE io) { - rb_io_t *fptr; - int fd; - - GetOpenFile(io, fptr); - fd = GetWriteFD(fptr); #ifdef _WIN32 - (void)fd; MessageBeep(0); #else - if (write(fd, "\a", 1) < 0) - rb_sys_fail(0); + int fd = GetWriteFD(io); + if (write(fd, "\a", 1) < 0) sys_fail(io); #endif return io; } +static int +mode_in_range(VALUE val, int high, const char *modename) +{ + int mode; + if (NIL_P(val)) return 0; + if (!RB_INTEGER_TYPE_P(val)) { + wrong_value: + rb_raise(rb_eArgError, "wrong %s mode: %"PRIsVALUE, modename, val); + } + if ((mode = NUM2INT(val)) < 0 || mode > high) { + goto wrong_value; + } + return mode; +} + #if defined _WIN32 -static VALUE -console_goto(VALUE io, VALUE x, VALUE y) +static void +constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos) { - rb_io_t *fptr; - int fd; - COORD pos; + DWORD written; - GetOpenFile(io, fptr); - fd = GetWriteFD(fptr); - pos.X = NUM2UINT(x); - pos.Y = NUM2UINT(y); - if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) { - rb_syserr_fail(LAST_ERROR, 0); - } - return io; + FillConsoleOutputAttribute(handle, attr, len, pos, &written); + FillConsoleOutputCharacterW(handle, L' ', len, pos, &written); } static VALUE -console_cursor_pos(VALUE io) +console_scroll(VALUE io, int line) { - rb_io_t *fptr; - int fd; + HANDLE h; rb_console_size_t ws; - GetOpenFile(io, fptr); - fd = GetWriteFD(fptr); - if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) { + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { rb_syserr_fail(LAST_ERROR, 0); } - return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.X), UINT2NUM(ws.dwCursorPosition.Y)); -} - -static VALUE -console_cursor_set(VALUE io, VALUE cpos) -{ - cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary"); - if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate"); - return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1)); + if (line) { + SMALL_RECT scroll; + COORD destination; + CHAR_INFO fill; + scroll.Left = 0; + scroll.Top = line > 0 ? line : 0; + scroll.Right = winsize_col(&ws) - 1; + scroll.Bottom = winsize_row(&ws) - 1 + (line < 0 ? line : 0); + destination.X = 0; + destination.Y = line < 0 ? -line : 0; + fill.Char.UnicodeChar = L' '; + fill.Attributes = ws.wAttributes; + + ScrollConsoleScreenBuffer(h, &scroll, NULL, destination, &fill); + } + return io; } +#define GPERF_DOWNCASE 1 +#define GPERF_CASE_STRCMP 1 +#define gperf_case_strcmp STRCASECMP #include "win32_vk.inc" +/* + * call-seq: + * io.pressed?(key) -> bool + * + * Returns +true+ if +key+ is pressed. +key+ may be a virtual key + * code or its name (String or Symbol) with out "VK_" prefix. + * + * This method is Windows only. + * + * You must require 'io/console' to use this method. + */ static VALUE console_key_pressed_p(VALUE io, VALUE k) { @@ -767,14 +1173,521 @@ console_key_pressed_p(VALUE io, VALUE k) return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse; } #else -# define console_goto rb_f_notimplement -# define console_cursor_pos rb_f_notimplement -# define console_cursor_set rb_f_notimplement +struct query_args { + char qstr[6]; + unsigned char opt; +}; + +static int +direct_query(VALUE io, const struct query_args *query) +{ + if (RB_TYPE_P(io, T_FILE)) { + VALUE wio = rb_io_get_write_io(io); + VALUE s = rb_str_new_cstr(query->qstr); + rb_io_write(wio, s); + rb_io_flush(wio); + return 1; + } + return 0; +} + +static VALUE +read_vt_response(VALUE io, VALUE query) +{ + struct query_args *qargs = (struct query_args *)query; + VALUE result, b; + int opt = 0; + int num = 0; + if (qargs) { + opt = qargs->opt; + if (!direct_query(io, qargs)) return Qnil; + } + if (rb_io_getbyte(io) != INT2FIX(0x1b)) return Qnil; + if (rb_io_getbyte(io) != INT2FIX('[')) return Qnil; + result = rb_ary_new(); + while (!NIL_P(b = rb_io_getbyte(io))) { + int c = NUM2UINT(b); + if (c == ';') { + rb_ary_push(result, INT2NUM(num)); + num = 0; + } + else if (ISDIGIT(c)) { + num = num * 10 + c - '0'; + } + else if (opt && c == opt) { + opt = 0; + } + else { + char last = (char)c; + rb_ary_push(result, INT2NUM(num)); + b = rb_str_new(&last, 1); + break; + } + } + return rb_ary_push(result, b); +} + +static VALUE +console_vt_response(int argc, VALUE *argv, VALUE io, const struct query_args *qargs) +{ + rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 1, &opts); + VALUE query = (VALUE)qargs; + VALUE ret = ttymode_with_io(io, read_vt_response, query, set_rawmode, optp); + return ret; +} + +static VALUE +console_scroll(VALUE io, int line) +{ + if (line) { + VALUE s = rb_sprintf(CSI "%d%c", line < 0 ? -line : line, + line < 0 ? 'T' : 'S'); + rb_io_write(io, s); + } + return io; +} + # define console_key_pressed_p rb_f_notimplement #endif /* * call-seq: + * io.cursor -> [row, column] + * + * Returns the current cursor position as a two-element array of integers (row, column) + * + * io.cursor # => [3, 5] + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_cursor_pos(VALUE io) +{ +#ifdef _WIN32 + rb_console_size_t ws; + int fd = GetWriteFD(io); + if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y - ws.srWindow.Top), UINT2NUM(ws.dwCursorPosition.X)); +#else + static const struct query_args query = {"\033[6n", 0}; + VALUE resp = console_vt_response(0, 0, io, &query); + VALUE row, column, term; + unsigned int r, c; + if (!RB_TYPE_P(resp, T_ARRAY) || RARRAY_LEN(resp) != 3) return Qnil; + term = RARRAY_AREF(resp, 2); + if (!RB_TYPE_P(term, T_STRING) || RSTRING_LEN(term) != 1) return Qnil; + if (RSTRING_PTR(term)[0] != 'R') return Qnil; + row = RARRAY_AREF(resp, 0); + column = RARRAY_AREF(resp, 1); + rb_ary_resize(resp, 2); + r = NUM2UINT(row) - 1; + c = NUM2UINT(column) - 1; + RARRAY_ASET(resp, 0, INT2NUM(r)); + RARRAY_ASET(resp, 1, INT2NUM(c)); + return resp; +#endif +} + +/* + * call-seq: + * io.goto(line, column) -> io + * + * Set the cursor position at +line+ and +column+. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_goto(VALUE io, VALUE y, VALUE x) +{ +#ifdef _WIN32 + HANDLE h; + rb_console_size_t ws; + COORD *pos = &ws.dwCursorPosition; + + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + pos->X = NUM2UINT(x); + pos->Y = ws.srWindow.Top + NUM2UINT(y); + if (!SetConsoleCursorPosition(h, *pos)) { + rb_syserr_fail(LAST_ERROR, 0); + } +#else + rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1)); +#endif + return io; +} + +static VALUE +console_move(VALUE io, int y, int x) +{ +#ifdef _WIN32 + HANDLE h; + rb_console_size_t ws; + COORD *pos = &ws.dwCursorPosition; + + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + pos->X += x; + pos->Y += y; + if (!SetConsoleCursorPosition(h, *pos)) { + rb_syserr_fail(LAST_ERROR, 0); + } +#else + if (x || y) { + VALUE s = rb_str_new_cstr(""); + if (y) rb_str_catf(s, CSI "%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B'); + if (x) rb_str_catf(s, CSI "%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C'); + rb_io_write(io, s); + rb_io_flush(io); + } +#endif + return io; +} + +/* + * call-seq: + * io.goto_column(column) -> io + * + * Set the cursor position at +column+ in the same line of the current + * position. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_goto_column(VALUE io, VALUE val) +{ +#ifdef _WIN32 + HANDLE h; + rb_console_size_t ws; + COORD *pos = &ws.dwCursorPosition; + + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + pos->X = NUM2INT(val); + if (!SetConsoleCursorPosition(h, *pos)) { + rb_syserr_fail(LAST_ERROR, 0); + } +#else + rb_io_write(io, rb_sprintf(CSI "%dG", NUM2UINT(val)+1)); +#endif + return io; +} + +/* + * call-seq: + * io.erase_line(mode) -> io + * + * Erases the line at the cursor corresponding to +mode+. + * +mode+ may be either: + * 0: after cursor + * 1: before and cursor + * 2: entire line + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_erase_line(VALUE io, VALUE val) +{ + int mode = mode_in_range(val, 2, "line erase"); +#ifdef _WIN32 + HANDLE h; + rb_console_size_t ws; + COORD *pos = &ws.dwCursorPosition; + DWORD w; + + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + w = winsize_col(&ws); + switch (mode) { + case 0: /* after cursor */ + w -= pos->X; + break; + case 1: /* before *and* cursor */ + w = pos->X + 1; + pos->X = 0; + break; + case 2: /* entire line */ + pos->X = 0; + break; + } + constat_clear(h, ws.wAttributes, w, *pos); + return io; +#else + rb_io_write(io, rb_sprintf(CSI "%dK", mode)); +#endif + return io; +} + +/* + * call-seq: + * io.erase_screen(mode) -> io + * + * Erases the screen at the cursor corresponding to +mode+. + * +mode+ may be either: + * 0: after cursor + * 1: before and cursor + * 2: entire screen + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_erase_screen(VALUE io, VALUE val) +{ + int mode = mode_in_range(val, 3, "screen erase"); +#ifdef _WIN32 + HANDLE h; + rb_console_size_t ws; + COORD *pos = &ws.dwCursorPosition; + DWORD w; + + h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); + if (!GetConsoleScreenBufferInfo(h, &ws)) { + rb_syserr_fail(LAST_ERROR, 0); + } + w = winsize_col(&ws); + switch (mode) { + case 0: /* erase after cursor */ + w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X); + break; + case 1: /* erase before *and* cursor */ + w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1); + pos->X = 0; + pos->Y = ws.srWindow.Top; + break; + case 2: /* erase entire screen */ + w = (w * winsize_row(&ws)); + pos->X = 0; + pos->Y = ws.srWindow.Top; + break; + case 3: /* erase entire screen */ + w = (w * ws.dwSize.Y); + pos->X = 0; + pos->Y = 0; + break; + } + constat_clear(h, ws.wAttributes, w, *pos); +#else + rb_io_write(io, rb_sprintf(CSI "%dJ", mode)); +#endif + return io; +} + +/* + * call-seq: + * io.cursor = [line, column] -> io + * + * Same as <tt>io.goto(line, column)</tt> + * + * See IO#goto. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_cursor_set(VALUE io, VALUE cpos) +{ + cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary"); + if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate"); + return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1)); +} + +/* + * call-seq: + * io.cursor_up(n) -> io + * + * Moves the cursor up +n+ lines. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_cursor_up(VALUE io, VALUE val) +{ + return console_move(io, -NUM2INT(val), 0); +} + +/* + * call-seq: + * io.cursor_down(n) -> io + * + * Moves the cursor down +n+ lines. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_cursor_down(VALUE io, VALUE val) +{ + return console_move(io, +NUM2INT(val), 0); +} + +/* + * call-seq: + * io.cursor_left(n) -> io + * + * Moves the cursor left +n+ columns. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_cursor_left(VALUE io, VALUE val) +{ + return console_move(io, 0, -NUM2INT(val)); +} + +/* + * call-seq: + * io.cursor_right(n) -> io + * + * Moves the cursor right +n+ columns. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_cursor_right(VALUE io, VALUE val) +{ + return console_move(io, 0, +NUM2INT(val)); +} + +/* + * call-seq: + * io.scroll_forward(n) -> io + * + * Scrolls the entire scrolls forward +n+ lines. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_scroll_forward(VALUE io, VALUE val) +{ + return console_scroll(io, +NUM2INT(val)); +} + +/* + * call-seq: + * io.scroll_backward(n) -> io + * + * Scrolls the entire scrolls backward +n+ lines. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_scroll_backward(VALUE io, VALUE val) +{ + return console_scroll(io, -NUM2INT(val)); +} + +/* + * call-seq: + * io.clear_screen -> io + * + * Clears the entire screen and moves the cursor top-left corner. + * + * You must require 'io/console' to use this method. + */ +static VALUE +console_clear_screen(VALUE io) +{ + console_erase_screen(io, INT2FIX(2)); + console_goto(io, INT2FIX(0), INT2FIX(0)); + return io; +} + +#ifndef HAVE_RB_IO_OPEN_DESCRIPTOR +static VALUE +io_open_descriptor_fallback(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, void *encoding) +{ + VALUE arguments[2] = { + (rb_update_max_fd(descriptor), INT2NUM(descriptor)), + INT2FIX(mode), + }; + + VALUE self = rb_class_new_instance(2, arguments, klass); + + rb_io_t *fptr; + GetOpenFile(self, fptr); + fptr->pathv = path; + fptr->mode |= mode; + + return self; +} +#define rb_io_open_descriptor io_open_descriptor_fallback +#endif + +#ifndef HAVE_RB_IO_CLOSED_P +static VALUE +rb_io_closed_p(VALUE io) +{ + rb_io_t *fptr = RFILE(io)->fptr; + return fptr->fd == -1 ? Qtrue : Qfalse; +} +#endif + +#if defined(RB_EXT_RACTOR_SAFE) && defined(HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY) +# define USE_RACTOR_STORAGE 1 +#else +# define USE_RACTOR_STORAGE 0 +#endif + +#if USE_RACTOR_STORAGE +#include "ruby/ractor.h" +static rb_ractor_local_key_t key_console_dev; + +static bool +console_dev_get(VALUE klass, VALUE *dev) +{ + return rb_ractor_local_storage_value_lookup(key_console_dev, dev); +} + +static void +console_dev_set(VALUE klass, VALUE value) +{ + rb_ractor_local_storage_value_set(key_console_dev, value); +} + +static void +console_dev_remove(VALUE klass) +{ + console_dev_set(klass, Qnil); +} + +#else + +static ID id_console; + +static int +console_dev_get(VALUE klass, VALUE *dev) +{ + if (rb_const_defined(klass, id_console)) { + *dev = rb_const_get(klass, id_console); + return 1; + } + return 0; +} + +static void +console_dev_set(VALUE klass, VALUE value) +{ + rb_const_set(klass, id_console, value); +} + +static void +console_dev_remove(VALUE klass) +{ + rb_const_remove(klass, id_console); +} + +#endif + +/* + * call-seq: * IO.console -> #<File:/dev/tty> * IO.console(sym, *args) * @@ -790,34 +1703,34 @@ static VALUE console_dev(int argc, VALUE *argv, VALUE klass) { VALUE con = 0; - rb_io_t *fptr; VALUE sym = 0; - rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS); if (argc) { - Check_Type(sym = argv[0], T_SYMBOL); + Check_Type(sym = argv[0], T_SYMBOL); } + + /* Force the class to be File. */ if (klass == rb_cIO) klass = rb_cFile; - if (rb_const_defined(klass, id_console)) { - con = rb_const_get(klass, id_console); - if (!RB_TYPE_P(con, T_FILE) || - (!(fptr = RFILE(con)->fptr) || GetReadFD(fptr) == -1)) { - rb_const_remove(klass, id_console); - con = 0; - } + + if (console_dev_get(klass, &con)) { + if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) { + console_dev_remove(klass); + con = 0; + } } + if (sym) { - if (sym == ID2SYM(id_close) && argc == 1) { - if (con) { - rb_io_close(con); - rb_const_remove(klass, id_console); - con = 0; - } - return Qnil; - } + if (sym == ID2SYM(id_close) && argc == 1) { + if (con) { + rb_io_close(con); + console_dev_remove(klass); + con = 0; + } + return Qnil; + } } + if (!con) { - VALUE args[2]; #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H # define CONSOLE_DEVICE "/dev/tty" #elif defined _WIN32 @@ -829,50 +1742,41 @@ console_dev(int argc, VALUE *argv, VALUE klass) # define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE #endif #ifdef CONSOLE_DEVICE_FOR_WRITING - VALUE out; - rb_io_t *ofptr; + VALUE out; #endif - int fd; + int fd; + VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE)); #ifdef CONSOLE_DEVICE_FOR_WRITING - fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0); - if (fd < 0) return Qnil; - rb_update_max_fd(fd); - args[1] = INT2FIX(O_WRONLY); - args[0] = INT2NUM(fd); - out = rb_class_new_instance(2, args, klass); -#endif - fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0); - if (fd < 0) { + fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0); + if (fd < 0) return Qnil; + out = rb_io_open_descriptor(klass, fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL); +#endif + fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0); + if (fd < 0) { #ifdef CONSOLE_DEVICE_FOR_WRITING - rb_io_close(out); + rb_io_close(out); #endif - return Qnil; - } - rb_update_max_fd(fd); - args[1] = INT2FIX(O_RDWR); - args[0] = INT2NUM(fd); - con = rb_class_new_instance(2, args, klass); - GetOpenFile(con, fptr); - fptr->pathv = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE)); + return Qnil; + } + + con = rb_io_open_descriptor(klass, fd, FMODE_READWRITE | FMODE_SYNC, path, Qnil, NULL); #ifdef CONSOLE_DEVICE_FOR_WRITING - GetOpenFile(out, ofptr); - ofptr->pathv = fptr->pathv; - fptr->tied_io_for_writing = out; - ofptr->mode |= FMODE_SYNC; + rb_io_set_write_io(con, out); #endif - fptr->mode |= FMODE_SYNC; - rb_const_set(klass, id_console, con); + console_dev_set(klass, con); } + if (sym) { - return rb_f_send(argc, argv, con); + return rb_f_send(argc, argv, con); } + return con; } /* * call-seq: - * io.getch(min: nil, time: nil) -> char + * io.getch(min: nil, time: nil, intr: nil) -> char * * See IO#getch. */ @@ -882,7 +1786,6 @@ io_getch(int argc, VALUE *argv, VALUE io) return rb_funcallv(io, id_getc, argc, argv); } -#if ENABLE_IO_GETPASS static VALUE puts_call(VALUE io) { @@ -890,9 +1793,15 @@ puts_call(VALUE io) } static VALUE +gets_call(VALUE io) +{ + return rb_funcallv(io, id_gets, 0, 0); +} + +static VALUE getpass_call(VALUE io) { - return ttymode(io, rb_io_gets, set_noecho, NULL); + return ttymode(io, rb_io_gets, io, set_noecho, NULL); } static void @@ -901,7 +1810,6 @@ prompt(int argc, VALUE *argv, VALUE io) if (argc > 0 && !NIL_P(argv[0])) { VALUE str = argv[0]; StringValueCStr(str); - rb_check_safe_obj(str); rb_io_write(io, str); } } @@ -910,7 +1818,8 @@ static VALUE str_chomp(VALUE str) { if (!NIL_P(str)) { - str = rb_funcallv(str, rb_intern("chomp!"), 0, 0); + const VALUE rs = rb_default_rs; /* rvalue in TruffleRuby */ + rb_funcallv(str, id_chomp_bang, 1, &rs); } return str; } @@ -922,7 +1831,17 @@ str_chomp(VALUE str) * Reads and returns a line without echo back. * Prints +prompt+ unless it is +nil+. * + * The newline character that terminates the + * read line is removed from the returned string, + * see String#chomp!. + * * You must require 'io/console' to use this method. + * + * require 'io/console' + * IO::console.getpass("Enter password:") + * Enter password: + * # => "mypassword" + * */ static VALUE console_getpass(int argc, VALUE *argv, VALUE io) @@ -933,6 +1852,7 @@ console_getpass(int argc, VALUE *argv, VALUE io) wio = rb_io_get_write_io(io); if (wio == io && io == rb_stdin) wio = rb_stderr; prompt(argc, argv, wio); + rb_io_flush(wio); str = rb_ensure(getpass_call, io, puts_call, wio); return str_chomp(str); } @@ -950,10 +1870,64 @@ io_getpass(int argc, VALUE *argv, VALUE io) rb_check_arity(argc, 0, 1); prompt(argc, argv, io); - str = str_chomp(rb_funcallv(io, id_gets, 0, 0)); - puts_call(io); - return str; + rb_check_funcall(io, id_flush, 0, 0); + str = rb_ensure(gets_call, io, puts_call, io); + return str_chomp(str); } + +#if defined(_WIN32) || defined(HAVE_TTYNAME_R) || defined(HAVE_TTYNAME) +/* + * call-seq: + * io.ttyname -> string or nil + * + * Returns name of associated terminal (tty) if +io+ is not a tty. + * Returns +nil+ otherwise. + */ +static VALUE +console_ttyname(VALUE io) +{ + int fd = rb_io_descriptor(io); + if (!isatty(fd)) return Qnil; +# if defined _WIN32 + return rb_usascii_str_new_lit("con"); +# elif defined HAVE_TTYNAME_R + { + char termname[1024], *tn = termname; + size_t size = sizeof(termname); + int e; + if (ttyname_r(fd, tn, size) == 0) + return rb_interned_str_cstr(tn); + if ((e = errno) == ERANGE) { + VALUE s = rb_str_new(0, size); + while (1) { + tn = RSTRING_PTR(s); + size = rb_str_capacity(s); + if (ttyname_r(fd, tn, size) == 0) { + return rb_str_to_interned_str(rb_str_resize(s, strlen(tn))); + } + if ((e = errno) != ERANGE) break; + if ((size *= 2) >= INT_MAX/2) break; + rb_str_resize(s, size); + } + } + rb_syserr_fail_str(e, rb_sprintf("ttyname_r(%d)", fd)); + UNREACHABLE_RETURN(Qnil); + } +# elif defined HAVE_TTYNAME + { + const char *tn = ttyname(fd); + if (!tn) { + int e = errno; + rb_syserr_fail_str(e, rb_sprintf("ttyname(%d)", fd)); + } + return rb_interned_str_cstr(tn); + } +# else +# error No ttyname function +# endif +} +#else +# define console_ttyname rb_f_notimplement #endif /* @@ -962,15 +1936,26 @@ io_getpass(int argc, VALUE *argv, VALUE io) void Init_console(void) { +#if USE_RACTOR_STORAGE + RB_EXT_RACTOR_SAFE(true); +#endif + #undef rb_intern +#if USE_RACTOR_STORAGE + key_console_dev = rb_ractor_local_storage_value_newkey(); +#else + id_console = rb_intern("console"); +#endif id_getc = rb_intern("getc"); -#if ENABLE_IO_GETPASS id_gets = rb_intern("gets"); -#endif - id_console = rb_intern("console"); + id_flush = rb_intern("flush"); + id_chomp_bang = rb_intern("chomp!"); id_close = rb_intern("close"); - id_min = rb_intern("min"); - id_time = rb_intern("time"); +#define init_rawmode_opt_id(name) \ + rawmode_opt_ids[kwd_##name] = rb_intern(#name) + init_rawmode_opt_id(min); + init_rawmode_opt_id(time); + init_rawmode_opt_id(intr); #ifndef HAVE_RB_F_SEND id___send__ = rb_intern("__send__"); #endif @@ -987,6 +1972,8 @@ InitVM_console(void) rb_define_method(rb_cIO, "getch", console_getch, -1); rb_define_method(rb_cIO, "echo=", console_set_echo, 1); rb_define_method(rb_cIO, "echo?", console_echo_p, 0); + rb_define_method(rb_cIO, "console_mode", console_conmode_get, 0); + rb_define_method(rb_cIO, "console_mode=", console_conmode_set, 1); rb_define_method(rb_cIO, "noecho", console_noecho, 0); rb_define_method(rb_cIO, "winsize", console_winsize, 0); rb_define_method(rb_cIO, "winsize=", console_set_winsize, 1); @@ -997,16 +1984,36 @@ InitVM_console(void) rb_define_method(rb_cIO, "goto", console_goto, 2); rb_define_method(rb_cIO, "cursor", console_cursor_pos, 0); rb_define_method(rb_cIO, "cursor=", console_cursor_set, 1); + rb_define_method(rb_cIO, "cursor_up", console_cursor_up, 1); + rb_define_method(rb_cIO, "cursor_down", console_cursor_down, 1); + rb_define_method(rb_cIO, "cursor_left", console_cursor_left, 1); + rb_define_method(rb_cIO, "cursor_right", console_cursor_right, 1); + rb_define_method(rb_cIO, "goto_column", console_goto_column, 1); + rb_define_method(rb_cIO, "erase_line", console_erase_line, 1); + rb_define_method(rb_cIO, "erase_screen", console_erase_screen, 1); + rb_define_method(rb_cIO, "scroll_forward", console_scroll_forward, 1); + rb_define_method(rb_cIO, "scroll_backward", console_scroll_backward, 1); + rb_define_method(rb_cIO, "clear_screen", console_clear_screen, 0); rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1); -#if ENABLE_IO_GETPASS + rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0); rb_define_method(rb_cIO, "getpass", console_getpass, -1); -#endif + rb_define_method(rb_cIO, "ttyname", console_ttyname, 0); rb_define_singleton_method(rb_cIO, "console", console_dev, -1); { + /* :nodoc: */ VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable"); rb_define_method(mReadable, "getch", io_getch, -1); -#if ENABLE_IO_GETPASS rb_define_method(mReadable, "getpass", io_getpass, -1); -#endif + } + { + /* :nodoc: */ + cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject); + rb_define_const(cConmode, "VERSION", rb_obj_freeze(rb_str_new_cstr(IO_CONSOLE_VERSION))); + rb_define_alloc_func(cConmode, conmode_alloc); + rb_undef_method(cConmode, "initialize"); + rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1); + rb_define_method(cConmode, "echo=", conmode_set_echo, 1); + rb_define_method(cConmode, "raw!", conmode_set_raw, -1); + rb_define_method(cConmode, "raw", conmode_raw_new, -1); } } diff --git a/ext/io/console/depend b/ext/io/console/depend index d69a0b3aef..150a138d4d 100644 --- a/ext/io/console/depend +++ b/ext/io/console/depend @@ -1,18 +1,180 @@ # AUTOGENERATED DEPENDENCIES START console.o: $(RUBY_EXTCONF_H) console.o: $(arch_hdrdir)/ruby/config.h +console.o: $(hdrdir)/ruby.h +console.o: $(hdrdir)/ruby/assert.h console.o: $(hdrdir)/ruby/backward.h +console.o: $(hdrdir)/ruby/backward/2/assume.h +console.o: $(hdrdir)/ruby/backward/2/attributes.h +console.o: $(hdrdir)/ruby/backward/2/bool.h +console.o: $(hdrdir)/ruby/backward/2/inttypes.h +console.o: $(hdrdir)/ruby/backward/2/limits.h +console.o: $(hdrdir)/ruby/backward/2/long_long.h +console.o: $(hdrdir)/ruby/backward/2/stdalign.h +console.o: $(hdrdir)/ruby/backward/2/stdarg.h console.o: $(hdrdir)/ruby/defines.h console.o: $(hdrdir)/ruby/encoding.h +console.o: $(hdrdir)/ruby/fiber/scheduler.h console.o: $(hdrdir)/ruby/intern.h +console.o: $(hdrdir)/ruby/internal/abi.h +console.o: $(hdrdir)/ruby/internal/anyargs.h +console.o: $(hdrdir)/ruby/internal/arithmetic.h +console.o: $(hdrdir)/ruby/internal/arithmetic/char.h +console.o: $(hdrdir)/ruby/internal/arithmetic/double.h +console.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +console.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/int.h +console.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/long.h +console.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +console.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/short.h +console.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +console.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +console.o: $(hdrdir)/ruby/internal/assume.h +console.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +console.o: $(hdrdir)/ruby/internal/attr/artificial.h +console.o: $(hdrdir)/ruby/internal/attr/cold.h +console.o: $(hdrdir)/ruby/internal/attr/const.h +console.o: $(hdrdir)/ruby/internal/attr/constexpr.h +console.o: $(hdrdir)/ruby/internal/attr/deprecated.h +console.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +console.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +console.o: $(hdrdir)/ruby/internal/attr/error.h +console.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +console.o: $(hdrdir)/ruby/internal/attr/forceinline.h +console.o: $(hdrdir)/ruby/internal/attr/format.h +console.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +console.o: $(hdrdir)/ruby/internal/attr/noalias.h +console.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +console.o: $(hdrdir)/ruby/internal/attr/noexcept.h +console.o: $(hdrdir)/ruby/internal/attr/noinline.h +console.o: $(hdrdir)/ruby/internal/attr/nonnull.h +console.o: $(hdrdir)/ruby/internal/attr/noreturn.h +console.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +console.o: $(hdrdir)/ruby/internal/attr/pure.h +console.o: $(hdrdir)/ruby/internal/attr/restrict.h +console.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +console.o: $(hdrdir)/ruby/internal/attr/warning.h +console.o: $(hdrdir)/ruby/internal/attr/weakref.h +console.o: $(hdrdir)/ruby/internal/cast.h +console.o: $(hdrdir)/ruby/internal/compiler_is.h +console.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +console.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +console.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +console.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +console.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +console.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +console.o: $(hdrdir)/ruby/internal/compiler_since.h +console.o: $(hdrdir)/ruby/internal/config.h +console.o: $(hdrdir)/ruby/internal/constant_p.h +console.o: $(hdrdir)/ruby/internal/core.h +console.o: $(hdrdir)/ruby/internal/core/rarray.h +console.o: $(hdrdir)/ruby/internal/core/rbasic.h +console.o: $(hdrdir)/ruby/internal/core/rbignum.h +console.o: $(hdrdir)/ruby/internal/core/rclass.h +console.o: $(hdrdir)/ruby/internal/core/rdata.h +console.o: $(hdrdir)/ruby/internal/core/rfile.h +console.o: $(hdrdir)/ruby/internal/core/rhash.h +console.o: $(hdrdir)/ruby/internal/core/robject.h +console.o: $(hdrdir)/ruby/internal/core/rregexp.h +console.o: $(hdrdir)/ruby/internal/core/rstring.h +console.o: $(hdrdir)/ruby/internal/core/rstruct.h +console.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +console.o: $(hdrdir)/ruby/internal/ctype.h +console.o: $(hdrdir)/ruby/internal/dllexport.h +console.o: $(hdrdir)/ruby/internal/dosish.h +console.o: $(hdrdir)/ruby/internal/encoding/coderange.h +console.o: $(hdrdir)/ruby/internal/encoding/ctype.h +console.o: $(hdrdir)/ruby/internal/encoding/encoding.h +console.o: $(hdrdir)/ruby/internal/encoding/pathname.h +console.o: $(hdrdir)/ruby/internal/encoding/re.h +console.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +console.o: $(hdrdir)/ruby/internal/encoding/string.h +console.o: $(hdrdir)/ruby/internal/encoding/symbol.h +console.o: $(hdrdir)/ruby/internal/encoding/transcode.h +console.o: $(hdrdir)/ruby/internal/error.h +console.o: $(hdrdir)/ruby/internal/eval.h +console.o: $(hdrdir)/ruby/internal/event.h +console.o: $(hdrdir)/ruby/internal/fl_type.h +console.o: $(hdrdir)/ruby/internal/gc.h +console.o: $(hdrdir)/ruby/internal/glob.h +console.o: $(hdrdir)/ruby/internal/globals.h +console.o: $(hdrdir)/ruby/internal/has/attribute.h +console.o: $(hdrdir)/ruby/internal/has/builtin.h +console.o: $(hdrdir)/ruby/internal/has/c_attribute.h +console.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +console.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +console.o: $(hdrdir)/ruby/internal/has/extension.h +console.o: $(hdrdir)/ruby/internal/has/feature.h +console.o: $(hdrdir)/ruby/internal/has/warning.h +console.o: $(hdrdir)/ruby/internal/intern/array.h +console.o: $(hdrdir)/ruby/internal/intern/bignum.h +console.o: $(hdrdir)/ruby/internal/intern/class.h +console.o: $(hdrdir)/ruby/internal/intern/compar.h +console.o: $(hdrdir)/ruby/internal/intern/complex.h +console.o: $(hdrdir)/ruby/internal/intern/cont.h +console.o: $(hdrdir)/ruby/internal/intern/dir.h +console.o: $(hdrdir)/ruby/internal/intern/enum.h +console.o: $(hdrdir)/ruby/internal/intern/enumerator.h +console.o: $(hdrdir)/ruby/internal/intern/error.h +console.o: $(hdrdir)/ruby/internal/intern/eval.h +console.o: $(hdrdir)/ruby/internal/intern/file.h +console.o: $(hdrdir)/ruby/internal/intern/hash.h +console.o: $(hdrdir)/ruby/internal/intern/io.h +console.o: $(hdrdir)/ruby/internal/intern/load.h +console.o: $(hdrdir)/ruby/internal/intern/marshal.h +console.o: $(hdrdir)/ruby/internal/intern/numeric.h +console.o: $(hdrdir)/ruby/internal/intern/object.h +console.o: $(hdrdir)/ruby/internal/intern/parse.h +console.o: $(hdrdir)/ruby/internal/intern/proc.h +console.o: $(hdrdir)/ruby/internal/intern/process.h +console.o: $(hdrdir)/ruby/internal/intern/random.h +console.o: $(hdrdir)/ruby/internal/intern/range.h +console.o: $(hdrdir)/ruby/internal/intern/rational.h +console.o: $(hdrdir)/ruby/internal/intern/re.h +console.o: $(hdrdir)/ruby/internal/intern/ruby.h +console.o: $(hdrdir)/ruby/internal/intern/select.h +console.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +console.o: $(hdrdir)/ruby/internal/intern/set.h +console.o: $(hdrdir)/ruby/internal/intern/signal.h +console.o: $(hdrdir)/ruby/internal/intern/sprintf.h +console.o: $(hdrdir)/ruby/internal/intern/string.h +console.o: $(hdrdir)/ruby/internal/intern/struct.h +console.o: $(hdrdir)/ruby/internal/intern/thread.h +console.o: $(hdrdir)/ruby/internal/intern/time.h +console.o: $(hdrdir)/ruby/internal/intern/variable.h +console.o: $(hdrdir)/ruby/internal/intern/vm.h +console.o: $(hdrdir)/ruby/internal/interpreter.h +console.o: $(hdrdir)/ruby/internal/iterator.h +console.o: $(hdrdir)/ruby/internal/memory.h +console.o: $(hdrdir)/ruby/internal/method.h +console.o: $(hdrdir)/ruby/internal/module.h +console.o: $(hdrdir)/ruby/internal/newobj.h +console.o: $(hdrdir)/ruby/internal/scan_args.h +console.o: $(hdrdir)/ruby/internal/special_consts.h +console.o: $(hdrdir)/ruby/internal/static_assert.h +console.o: $(hdrdir)/ruby/internal/stdalign.h +console.o: $(hdrdir)/ruby/internal/stdbool.h +console.o: $(hdrdir)/ruby/internal/stdckdint.h +console.o: $(hdrdir)/ruby/internal/symbol.h +console.o: $(hdrdir)/ruby/internal/value.h +console.o: $(hdrdir)/ruby/internal/value_type.h +console.o: $(hdrdir)/ruby/internal/variable.h +console.o: $(hdrdir)/ruby/internal/warning_push.h +console.o: $(hdrdir)/ruby/internal/xmalloc.h console.o: $(hdrdir)/ruby/io.h console.o: $(hdrdir)/ruby/missing.h console.o: $(hdrdir)/ruby/onigmo.h console.o: $(hdrdir)/ruby/oniguruma.h +console.o: $(hdrdir)/ruby/ractor.h console.o: $(hdrdir)/ruby/ruby.h console.o: $(hdrdir)/ruby/st.h console.o: $(hdrdir)/ruby/subst.h -console.o: $(top_srcdir)/include/ruby.h +console.o: $(hdrdir)/ruby/thread.h console.o: console.c # AUTOGENERATED DEPENDENCIES END @@ -25,8 +187,8 @@ win32_vk.inc: win32_vk.list -e 'n=$$F[1] and (n.strip!; /\AVK_/=~n) and' \ -e 'puts(%[#ifndef #{n}\n# define #{n} UNDEFINED_VK\n#endif])' \ $< && \ - gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ - | sed 's/(int)(long)&((\(struct stringpool_t\) *\*)0)->\(stringpool_[a-z0-9]*\)/offsetof(\1, \2)/g' \ + gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ + | sed -f $(top_srcdir)/tool/gperf.sed \ ) > $(@F) .SUFFIXES: .chksum .list .inc diff --git a/ext/io/console/extconf.rb b/ext/io/console/extconf.rb index be536dff9f..dd3d221ae5 100644 --- a/ext/io/console/extconf.rb +++ b/ext/io/console/extconf.rb @@ -1,7 +1,30 @@ # frozen_string_literal: false require 'mkmf' -ok = true +# `--target-rbconfig` compatibility for Ruby 3.3 or earlier +# See https://bugs.ruby-lang.org/issues/20345 +MakeMakefile::RbConfig ||= ::RbConfig + +have_func("rb_syserr_fail_str(0, Qnil)") or +have_func("rb_syserr_new_str(0, Qnil)") or + abort + +have_func("rb_interned_str_cstr") +have_func("rb_io_path", "ruby/io.h") +have_func("rb_io_descriptor", "ruby/io.h") +have_func("rb_io_get_write_io", "ruby/io.h") +have_func("rb_io_closed_p", "ruby/io.h") +have_func("rb_io_open_descriptor", "ruby/io.h") +have_func("rb_ractor_local_storage_value_newkey") + +is_wasi = /wasi/ =~ MakeMakefile::RbConfig::CONFIG["platform"] +# `ok` can be `true`, `false`, or `nil`: +# * `true` : Required headers and functions available, proceed regular build. +# * `false`: Required headers or functions not available, abort build. +# * `nil` : Unsupported compilation target, generate dummy Makefile. +# +# Skip building io/console on WASI, as it does not support termios.h. +ok = true if (RUBY_ENGINE == "ruby" && !is_wasi) || RUBY_ENGINE == "truffleruby" hdr = nil case when macro_defined?("_WIN32", "") @@ -14,16 +37,25 @@ when have_header(hdr = "sgtty.h") %w"stty gtty".each {|f| have_func(f, hdr)} else ok = false -end -if ok +end if ok +case ok +when true have_header("sys/ioctl.h") if hdr - have_func("rb_funcallv") - have_func("rb_sym2str") # rb_check_hash_type: 1.9.3 # rb_io_get_write_io: 1.9.1 # rb_cloexec_open: 2.0.0 - $defs << "-D""ENABLE_IO_GETPASS=1" + # rb_funcallv: 2.1.0 + # RARRAY_CONST_PTR: 2.1.0 + # rb_sym2str: 2.2.0 + if have_macro("HAVE_RUBY_FIBER_SCHEDULER_H") + $defs << "-D""HAVE_RB_IO_WAIT=1" + elsif have_func("rb_scheduler_timeout") # 3.0 + have_func("rb_io_wait") + end + have_func("ttyname_r") or have_func("ttyname") create_makefile("io/console") {|conf| conf << "\n""VK_HEADER = #{vk_header}\n" } +when nil + File.write("Makefile", dummy_makefile($srcdir).join("")) end diff --git a/ext/io/console/io-console.gemspec b/ext/io/console/io-console.gemspec index 1256162468..0a19992734 100644 --- a/ext/io/console/io-console.gemspec +++ b/ext/io/console/io-console.gemspec @@ -1,24 +1,53 @@ # -*- ruby -*- -_VERSION = "0.4.6" -date = %w$Date:: $[1] +_VERSION = ["", "ext/io/console/"].find do |dir| + begin + break File.open(File.join(__dir__, "#{dir}console.c")) {|f| + f.gets("\nIO_CONSOLE_VERSION ") + f.gets[/"(.+)"/, 1] + } + rescue Errno::ENOENT + end +end Gem::Specification.new do |s| s.name = "io-console" s.version = _VERSION - s.date = date s.summary = "Console interface" s.email = "nobu@ruby-lang.org" s.description = "add console capabilities to IO instances." - s.required_ruby_version = ">= 2.0.0" + s.required_ruby_version = ">= 2.6.0" s.homepage = "https://github.com/ruby/io-console" + s.metadata["source_code_url"] = s.homepage + s.metadata["changelog_uri"] = s.homepage + "/releases" s.authors = ["Nobu Nakada"] s.require_path = %[lib] - s.files = %w[ext/io/console/console.c ext/io/console/extconf.rb lib/console/size.rb ext/io/console/win32_vk.inc] + s.files = %w[ + .document + BSDL + COPYING + README.md + ext/io/console/console.c + ext/io/console/extconf.rb + ext/io/console/win32_vk.inc + lib/io/console/size.rb + ] s.extensions = %w[ext/io/console/extconf.rb] - s.license = "BSD-2-Clause" - s.cert_chain = %w[certs/nobu.pem] - s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/ - s.add_development_dependency 'rake-compiler' - s.add_development_dependency 'rake-compiler-dock', ">= 0.6.1" + if Gem::Platform === s.platform and s.platform =~ 'java' + s.files.delete_if {|f| f.start_with?("ext/")} + s.extensions.clear + s.require_paths.unshift('lib/ffi') + s.files.concat(%w[ + lib/ffi/io/console.rb + lib/ffi/io/console/bsd_console.rb + lib/ffi/io/console/common.rb + lib/ffi/io/console/linux_console.rb + lib/ffi/io/console/native_console.rb + lib/ffi/io/console/stty_console.rb + lib/ffi/io/console/stub_console.rb + lib/ffi/io/console/version.rb + ]) + end + + s.licenses = ["Ruby", "BSD-2-Clause"] end diff --git a/ext/io/console/win32_vk.inc b/ext/io/console/win32_vk.inc index a098158e27..348e6be5ed 100644 --- a/ext/io/console/win32_vk.inc +++ b/ext/io/console/win32_vk.inc @@ -479,8 +479,8 @@ #ifndef VK_OEM_CLEAR # define VK_OEM_CLEAR UNDEFINED_VK #endif -/* C code produced by gperf version 3.0.4 */ -/* Command-line: gperf --ignore-case -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ @@ -506,13 +506,13 @@ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ -error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." #endif #line 1 "win32_vk.list" struct vktable {short ofs; unsigned short vk;}; -static const struct vktable *console_win32_vk(const char *, unsigned int); +static const struct vktable *console_win32_vk(const char *, size_t); #line 5 "win32_vk.list" struct vktable; /* maximum key range = 245, duplicates = 0 */ @@ -545,9 +545,7 @@ static unsigned char gperf_downcase[256] = #ifndef GPERF_CASE_STRCMP #define GPERF_CASE_STRCMP 1 static int -gperf_case_strcmp (s1, s2) - register const char *s1; - register const char *s2; +gperf_case_strcmp (register const char *s1, register const char *s2) { for (;;) { @@ -560,15 +558,15 @@ gperf_case_strcmp (s1, s2) } #endif -#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) || defined(__GNUC_STDC_INLINE__) -inline -#elif defined(__GNUC__) +#ifdef __GNUC__ __inline +#else +#ifdef __cplusplus +inline +#endif #endif static unsigned int -hash (str, len) - register const char *str; - register unsigned int len; +hash (register const char *str, register size_t len) { static const unsigned short asso_values[] = { @@ -599,7 +597,7 @@ hash (str, len) 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257 }; - register int hval = len; + register unsigned int hval = (unsigned int)len; switch (hval) { @@ -661,7 +659,7 @@ hash (str, len) hval += asso_values[(unsigned char)str[0]]; break; } - return hval; + return (unsigned int)hval; } struct stringpool_t @@ -991,16 +989,8 @@ static const struct stringpool_t stringpool_contents = "DIVIDE" }; #define stringpool ((const char *) &stringpool_contents) -#ifdef __GNUC__ -__inline -#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ -__attribute__ ((__gnu_inline__)) -#endif -#endif const struct vktable * -console_win32_vk (str, len) - register const char *str; - register unsigned int len; +console_win32_vk (register const char *str, register size_t len) { enum { @@ -1016,375 +1006,375 @@ console_win32_vk (str, len) {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 40 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str12), VK_UP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, VK_UP}, #line 52 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str13), VK_APPS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, VK_APPS}, #line 159 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str14), VK_CRSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, VK_CRSEL}, #line 34 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str15), VK_SPACE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, VK_SPACE}, #line 95 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str16), VK_SCROLL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16, VK_SCROLL}, #line 29 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str17), VK_ESCAPE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, VK_ESCAPE}, #line 9 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str18), VK_CANCEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18, VK_CANCEL}, #line 32 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str19), VK_ACCEPT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, VK_ACCEPT}, #line 66 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str20), VK_SEPARATOR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, VK_SEPARATOR}, #line 43 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str21), VK_SELECT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, VK_SELECT}, #line 18 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str22), VK_CONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, VK_CONTROL}, #line 166 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str23), VK_OEM_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, VK_OEM_CLEAR}, #line 145 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str24), VK_OEM_RESET}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, VK_OEM_RESET}, #line 155 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str25), VK_OEM_AUTO}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, VK_OEM_AUTO}, #line 151 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str26), VK_OEM_CUSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, VK_OEM_CUSEL}, {-1}, #line 22 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str28), VK_KANA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, VK_KANA}, #line 127 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str29), VK_OEM_PLUS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, VK_OEM_PLUS}, #line 35 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str30), VK_PRIOR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, VK_PRIOR}, #line 152 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str31), VK_OEM_ATTN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, VK_OEM_ATTN}, #line 20 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str32), VK_PAUSE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, VK_PAUSE}, #line 13 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str33), VK_BACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, VK_BACK}, #line 144 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str34), VK_PACKET}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, VK_PACKET}, #line 105 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str35), VK_RCONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, VK_RCONTROL}, #line 104 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str36), VK_LCONTROL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, VK_LCONTROL}, #line 37 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str37), VK_END}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VK_END}, #line 38 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str38), VK_HOME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, VK_HOME}, #line 44 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str39), VK_PRINT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39, VK_PRINT}, #line 94 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str40), VK_NUMLOCK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str40, VK_NUMLOCK}, #line 39 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str41), VK_LEFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41, VK_LEFT}, #line 25 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str42), VK_JUNJA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, VK_JUNJA}, #line 19 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str43), VK_MENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, VK_MENU}, #line 150 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str44), VK_OEM_WSCTRL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, VK_OEM_WSCTRL}, #line 156 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str45), VK_OEM_ENLW}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, VK_OEM_ENLW}, #line 36 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str46), VK_NEXT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, VK_NEXT}, #line 51 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str47), VK_RWIN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VK_RWIN}, #line 50 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str48), VK_LWIN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48, VK_LWIN}, #line 21 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str49), VK_CAPITAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, VK_CAPITAL}, #line 49 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str50), VK_HELP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str50, VK_HELP}, #line 164 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str51), VK_NONAME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str51, VK_NONAME}, #line 8 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str52), VK_RBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, VK_RBUTTON}, #line 7 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str53), VK_LBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, VK_LBUTTON}, #line 96 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str54), VK_OEM_NEC_EQUAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54, VK_OEM_NEC_EQUAL}, {-1}, #line 47 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str56), VK_INSERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str56, VK_INSERT}, #line 27 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str57), VK_HANJA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, VK_HANJA}, {-1}, {-1}, #line 46 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str60), VK_SNAPSHOT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, VK_SNAPSHOT}, #line 158 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str61), VK_ATTN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61, VK_ATTN}, #line 14 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str62), VK_TAB}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, VK_TAB}, #line 157 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str63), VK_OEM_BACKTAB}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, VK_OEM_BACKTAB}, #line 143 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str64), VK_ICO_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, VK_ICO_CLEAR}, #line 30 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str65), VK_CONVERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, VK_CONVERT}, #line 16 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str66), VK_RETURN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, VK_RETURN}, #line 146 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str67), VK_OEM_JUMP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, VK_OEM_JUMP}, {-1}, {-1}, {-1}, #line 111 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str71), VK_BROWSER_STOP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, VK_BROWSER_STOP}, #line 26 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str72), VK_FINAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, VK_FINAL}, #line 163 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str73), VK_ZOOM}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str73, VK_ZOOM}, #line 28 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str74), VK_KANJI}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74, VK_KANJI}, #line 48 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str75), VK_DELETE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75, VK_DELETE}, #line 128 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str76), VK_OEM_COMMA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, VK_OEM_COMMA}, #line 67 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str77), VK_SUBTRACT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77, VK_SUBTRACT}, {-1}, #line 10 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str79), VK_MBUTTON}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79, VK_MBUTTON}, #line 78 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str80), VK_F9}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, VK_F9}, #line 17 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str81), VK_SHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81, VK_SHIFT}, #line 103 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str82), VK_RSHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, VK_RSHIFT}, #line 102 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str83), VK_LSHIFT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str83, VK_LSHIFT}, #line 65 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str84), VK_ADD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84, VK_ADD}, #line 31 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str85), VK_NONCONVERT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85, VK_NONCONVERT}, #line 160 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str86), VK_EXSEL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str86, VK_EXSEL}, #line 126 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str87), VK_OEM_1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str87, VK_OEM_1}, #line 138 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str88), VK_OEM_AX}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str88, VK_OEM_AX}, #line 108 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str89), VK_BROWSER_BACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str89, VK_BROWSER_BACK}, #line 137 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str90), VK_OEM_8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str90, VK_OEM_8}, #line 129 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str91), VK_OEM_MINUS}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str91, VK_OEM_MINUS}, #line 162 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str92), VK_PLAY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str92, VK_PLAY}, #line 131 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str93), VK_OEM_2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str93, VK_OEM_2}, #line 15 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str94), VK_CLEAR}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str94, VK_CLEAR}, #line 99 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str95), VK_OEM_FJ_TOUROKU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str95, VK_OEM_FJ_TOUROKU}, #line 147 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str96), VK_OEM_PA1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str96, VK_OEM_PA1}, #line 140 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str97), VK_ICO_HELP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str97, VK_ICO_HELP}, #line 112 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str98), VK_BROWSER_SEARCH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str98, VK_BROWSER_SEARCH}, #line 53 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str99), VK_SLEEP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str99, VK_SLEEP}, {-1}, #line 70 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str101), VK_F1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str101, VK_F1}, #line 148 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str102), VK_OEM_PA2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str102, VK_OEM_PA2}, #line 154 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str103), VK_OEM_COPY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str103, VK_OEM_COPY}, #line 77 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str104), VK_F8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str104, VK_F8}, #line 88 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str105), VK_F19}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str105, VK_F19}, #line 41 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str106), VK_RIGHT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str106, VK_RIGHT}, #line 71 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str107), VK_F2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str107, VK_F2}, #line 135 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str108), VK_OEM_6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str108, VK_OEM_6}, #line 87 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str109), VK_F18}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str109, VK_F18}, {-1}, #line 117 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str111), VK_VOLUME_UP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str111, VK_VOLUME_UP}, {-1}, {-1}, #line 120 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str114), VK_MEDIA_STOP}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str114, VK_MEDIA_STOP}, #line 130 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str115), VK_OEM_PERIOD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str115, VK_OEM_PERIOD}, {-1}, #line 161 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str117), VK_EREOF}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str117, VK_EREOF}, {-1}, {-1}, {-1}, #line 114 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str121), VK_BROWSER_HOME}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str121, VK_BROWSER_HOME}, #line 75 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str122), VK_F6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str122, VK_F6}, {-1}, #line 110 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str124), VK_BROWSER_REFRESH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str124, VK_BROWSER_REFRESH}, {-1}, #line 165 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str126), VK_PA1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str126, VK_PA1}, #line 142 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str127), VK_PROCESSKEY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str127, VK_PROCESSKEY}, #line 68 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str128), VK_DECIMAL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str128, VK_DECIMAL}, #line 132 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str129), VK_OEM_3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str129, VK_OEM_3}, #line 107 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str130), VK_RMENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str130, VK_RMENU}, #line 106 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str131), VK_LMENU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str131, VK_LMENU}, #line 98 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str132), VK_OEM_FJ_MASSHOU}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str132, VK_OEM_FJ_MASSHOU}, #line 54 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str133), VK_NUMPAD0}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str133, VK_NUMPAD0}, #line 24 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str134), VK_HANGUL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str134, VK_HANGUL}, #line 63 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str135), VK_NUMPAD9}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str135, VK_NUMPAD9}, #line 23 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str136), VK_HANGEUL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str136, VK_HANGEUL}, #line 134 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str137), VK_OEM_5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str137, VK_OEM_5}, #line 149 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str138), VK_OEM_PA3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str138, VK_OEM_PA3}, #line 115 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str139), VK_VOLUME_MUTE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str139, VK_VOLUME_MUTE}, #line 133 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str140), VK_OEM_4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str140, VK_OEM_4}, #line 122 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str141), VK_LAUNCH_MAIL}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str141, VK_LAUNCH_MAIL}, #line 97 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str142), VK_OEM_FJ_JISHO}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str142, VK_OEM_FJ_JISHO}, #line 72 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str143), VK_F3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str143, VK_F3}, #line 101 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str144), VK_OEM_FJ_ROYA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str144, VK_OEM_FJ_ROYA}, #line 100 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str145), VK_OEM_FJ_LOYA}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str145, VK_OEM_FJ_LOYA}, {-1}, #line 42 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str147), VK_DOWN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str147, VK_DOWN}, {-1}, #line 153 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str149), VK_OEM_FINISH}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str149, VK_OEM_FINISH}, {-1}, #line 74 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str151), VK_F5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str151, VK_F5}, {-1}, #line 136 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str153), VK_OEM_7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str153, VK_OEM_7}, #line 73 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str154), VK_F4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str154, VK_F4}, #line 86 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str155), VK_F17}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str155, VK_F17}, #line 55 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str156), VK_NUMPAD1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str156, VK_NUMPAD1}, #line 141 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str157), VK_ICO_00}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str157, VK_ICO_00}, {-1}, #line 62 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str159), VK_NUMPAD8}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str159, VK_NUMPAD8}, {-1}, {-1}, #line 56 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str162), VK_NUMPAD2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str162, VK_NUMPAD2}, {-1}, #line 124 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str164), VK_LAUNCH_APP1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str164, VK_LAUNCH_APP1}, #line 109 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str165), VK_BROWSER_FORWARD}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str165, VK_BROWSER_FORWARD}, {-1}, #line 76 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str167), VK_F7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str167, VK_F7}, {-1}, {-1}, #line 125 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str170), VK_LAUNCH_APP2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str170, VK_LAUNCH_APP2}, #line 64 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str171), VK_MULTIPLY}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str171, VK_MULTIPLY}, {-1}, {-1}, #line 45 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str174), VK_EXECUTE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174, VK_EXECUTE}, {-1}, #line 113 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str176), VK_BROWSER_FAVORITES}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str176, VK_BROWSER_FAVORITES}, #line 60 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str177), VK_NUMPAD6}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177, VK_NUMPAD6}, {-1}, #line 85 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str179), VK_F16}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str179, VK_F16}, {-1}, {-1}, #line 79 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str182), VK_F10}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str182, VK_F10}, {-1}, {-1}, #line 116 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str185), VK_VOLUME_DOWN}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str185, VK_VOLUME_DOWN}, {-1}, {-1}, #line 89 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str188), VK_F20}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str188, VK_F20}, #line 119 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str189), VK_MEDIA_PREV_TRACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str189, VK_MEDIA_PREV_TRACK}, {-1}, #line 33 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str191), VK_MODECHANGE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str191, VK_MODECHANGE}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 83 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str197), VK_F14}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str197, VK_F14}, #line 57 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str198), VK_NUMPAD3}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str198, VK_NUMPAD3}, #line 11 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str199), VK_XBUTTON1}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str199, VK_XBUTTON1}, {-1}, {-1}, {-1}, #line 93 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str203), VK_F24}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str203, VK_F24}, {-1}, #line 12 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str205), VK_XBUTTON2}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str205, VK_XBUTTON2}, #line 59 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str206), VK_NUMPAD5}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str206, VK_NUMPAD5}, {-1}, {-1}, #line 58 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str209), VK_NUMPAD4}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str209, VK_NUMPAD4}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 121 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str215), VK_MEDIA_PLAY_PAUSE}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str215, VK_MEDIA_PLAY_PAUSE}, {-1}, #line 123 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str217), VK_LAUNCH_MEDIA_SELECT}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str217, VK_LAUNCH_MEDIA_SELECT}, #line 80 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str218), VK_F11}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str218, VK_F11}, {-1}, #line 139 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str220), VK_OEM_102}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str220, VK_OEM_102}, #line 118 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str221), VK_MEDIA_NEXT_TRACK}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str221, VK_MEDIA_NEXT_TRACK}, #line 61 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str222), VK_NUMPAD7}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str222, VK_NUMPAD7}, {-1}, #line 90 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str224), VK_F21}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str224, VK_F21}, {-1}, #line 82 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str226), VK_F13}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str226, VK_F13}, {-1}, {-1}, #line 81 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str229), VK_F12}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229, VK_F12}, {-1}, {-1}, #line 92 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str232), VK_F23}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str232, VK_F23}, {-1}, {-1}, #line 91 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str235), VK_F22}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str235, VK_F22}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 84 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str242), VK_F15}, + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str242, VK_F15}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, #line 69 "win32_vk.list" - {offsetof(struct stringpool_t, stringpool_str256), VK_DIVIDE} + {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, VK_DIVIDE} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { - register int key = hash (str, len); + register unsigned int key = hash (str, len); - if (key <= MAX_HASH_VALUE && key >= 0) + if (key <= MAX_HASH_VALUE) { register int o = wordlist[key].ofs; if (o >= 0) diff --git a/ext/io/console/win32_vk.list b/ext/io/console/win32_vk.list index 28bc9545ec..5df3d6da57 100644 --- a/ext/io/console/win32_vk.list +++ b/ext/io/console/win32_vk.list @@ -1,6 +1,6 @@ %{ struct vktable {short ofs; unsigned short vk;}; -static const struct vktable *console_win32_vk(const char *, unsigned int); +static const struct vktable *console_win32_vk(const char *, size_t); %} struct vktable %% diff --git a/ext/io/nonblock/depend b/ext/io/nonblock/depend index 4402898de6..e78a05c316 100644 --- a/ext/io/nonblock/depend +++ b/ext/io/nonblock/depend @@ -1,10 +1,170 @@ # AUTOGENERATED DEPENDENCIES START nonblock.o: $(RUBY_EXTCONF_H) nonblock.o: $(arch_hdrdir)/ruby/config.h +nonblock.o: $(hdrdir)/ruby.h +nonblock.o: $(hdrdir)/ruby/assert.h nonblock.o: $(hdrdir)/ruby/backward.h +nonblock.o: $(hdrdir)/ruby/backward/2/assume.h +nonblock.o: $(hdrdir)/ruby/backward/2/attributes.h +nonblock.o: $(hdrdir)/ruby/backward/2/bool.h +nonblock.o: $(hdrdir)/ruby/backward/2/inttypes.h +nonblock.o: $(hdrdir)/ruby/backward/2/limits.h +nonblock.o: $(hdrdir)/ruby/backward/2/long_long.h +nonblock.o: $(hdrdir)/ruby/backward/2/stdalign.h +nonblock.o: $(hdrdir)/ruby/backward/2/stdarg.h nonblock.o: $(hdrdir)/ruby/defines.h nonblock.o: $(hdrdir)/ruby/encoding.h nonblock.o: $(hdrdir)/ruby/intern.h +nonblock.o: $(hdrdir)/ruby/internal/abi.h +nonblock.o: $(hdrdir)/ruby/internal/anyargs.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/char.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/double.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/int.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/long.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/short.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +nonblock.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +nonblock.o: $(hdrdir)/ruby/internal/assume.h +nonblock.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +nonblock.o: $(hdrdir)/ruby/internal/attr/artificial.h +nonblock.o: $(hdrdir)/ruby/internal/attr/cold.h +nonblock.o: $(hdrdir)/ruby/internal/attr/const.h +nonblock.o: $(hdrdir)/ruby/internal/attr/constexpr.h +nonblock.o: $(hdrdir)/ruby/internal/attr/deprecated.h +nonblock.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +nonblock.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +nonblock.o: $(hdrdir)/ruby/internal/attr/error.h +nonblock.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +nonblock.o: $(hdrdir)/ruby/internal/attr/forceinline.h +nonblock.o: $(hdrdir)/ruby/internal/attr/format.h +nonblock.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +nonblock.o: $(hdrdir)/ruby/internal/attr/noalias.h +nonblock.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +nonblock.o: $(hdrdir)/ruby/internal/attr/noexcept.h +nonblock.o: $(hdrdir)/ruby/internal/attr/noinline.h +nonblock.o: $(hdrdir)/ruby/internal/attr/nonnull.h +nonblock.o: $(hdrdir)/ruby/internal/attr/noreturn.h +nonblock.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +nonblock.o: $(hdrdir)/ruby/internal/attr/pure.h +nonblock.o: $(hdrdir)/ruby/internal/attr/restrict.h +nonblock.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +nonblock.o: $(hdrdir)/ruby/internal/attr/warning.h +nonblock.o: $(hdrdir)/ruby/internal/attr/weakref.h +nonblock.o: $(hdrdir)/ruby/internal/cast.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +nonblock.o: $(hdrdir)/ruby/internal/compiler_since.h +nonblock.o: $(hdrdir)/ruby/internal/config.h +nonblock.o: $(hdrdir)/ruby/internal/constant_p.h +nonblock.o: $(hdrdir)/ruby/internal/core.h +nonblock.o: $(hdrdir)/ruby/internal/core/rarray.h +nonblock.o: $(hdrdir)/ruby/internal/core/rbasic.h +nonblock.o: $(hdrdir)/ruby/internal/core/rbignum.h +nonblock.o: $(hdrdir)/ruby/internal/core/rclass.h +nonblock.o: $(hdrdir)/ruby/internal/core/rdata.h +nonblock.o: $(hdrdir)/ruby/internal/core/rfile.h +nonblock.o: $(hdrdir)/ruby/internal/core/rhash.h +nonblock.o: $(hdrdir)/ruby/internal/core/robject.h +nonblock.o: $(hdrdir)/ruby/internal/core/rregexp.h +nonblock.o: $(hdrdir)/ruby/internal/core/rstring.h +nonblock.o: $(hdrdir)/ruby/internal/core/rstruct.h +nonblock.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +nonblock.o: $(hdrdir)/ruby/internal/ctype.h +nonblock.o: $(hdrdir)/ruby/internal/dllexport.h +nonblock.o: $(hdrdir)/ruby/internal/dosish.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/coderange.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/ctype.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/encoding.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/pathname.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/re.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/string.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/symbol.h +nonblock.o: $(hdrdir)/ruby/internal/encoding/transcode.h +nonblock.o: $(hdrdir)/ruby/internal/error.h +nonblock.o: $(hdrdir)/ruby/internal/eval.h +nonblock.o: $(hdrdir)/ruby/internal/event.h +nonblock.o: $(hdrdir)/ruby/internal/fl_type.h +nonblock.o: $(hdrdir)/ruby/internal/gc.h +nonblock.o: $(hdrdir)/ruby/internal/glob.h +nonblock.o: $(hdrdir)/ruby/internal/globals.h +nonblock.o: $(hdrdir)/ruby/internal/has/attribute.h +nonblock.o: $(hdrdir)/ruby/internal/has/builtin.h +nonblock.o: $(hdrdir)/ruby/internal/has/c_attribute.h +nonblock.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +nonblock.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +nonblock.o: $(hdrdir)/ruby/internal/has/extension.h +nonblock.o: $(hdrdir)/ruby/internal/has/feature.h +nonblock.o: $(hdrdir)/ruby/internal/has/warning.h +nonblock.o: $(hdrdir)/ruby/internal/intern/array.h +nonblock.o: $(hdrdir)/ruby/internal/intern/bignum.h +nonblock.o: $(hdrdir)/ruby/internal/intern/class.h +nonblock.o: $(hdrdir)/ruby/internal/intern/compar.h +nonblock.o: $(hdrdir)/ruby/internal/intern/complex.h +nonblock.o: $(hdrdir)/ruby/internal/intern/cont.h +nonblock.o: $(hdrdir)/ruby/internal/intern/dir.h +nonblock.o: $(hdrdir)/ruby/internal/intern/enum.h +nonblock.o: $(hdrdir)/ruby/internal/intern/enumerator.h +nonblock.o: $(hdrdir)/ruby/internal/intern/error.h +nonblock.o: $(hdrdir)/ruby/internal/intern/eval.h +nonblock.o: $(hdrdir)/ruby/internal/intern/file.h +nonblock.o: $(hdrdir)/ruby/internal/intern/hash.h +nonblock.o: $(hdrdir)/ruby/internal/intern/io.h +nonblock.o: $(hdrdir)/ruby/internal/intern/load.h +nonblock.o: $(hdrdir)/ruby/internal/intern/marshal.h +nonblock.o: $(hdrdir)/ruby/internal/intern/numeric.h +nonblock.o: $(hdrdir)/ruby/internal/intern/object.h +nonblock.o: $(hdrdir)/ruby/internal/intern/parse.h +nonblock.o: $(hdrdir)/ruby/internal/intern/proc.h +nonblock.o: $(hdrdir)/ruby/internal/intern/process.h +nonblock.o: $(hdrdir)/ruby/internal/intern/random.h +nonblock.o: $(hdrdir)/ruby/internal/intern/range.h +nonblock.o: $(hdrdir)/ruby/internal/intern/rational.h +nonblock.o: $(hdrdir)/ruby/internal/intern/re.h +nonblock.o: $(hdrdir)/ruby/internal/intern/ruby.h +nonblock.o: $(hdrdir)/ruby/internal/intern/select.h +nonblock.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +nonblock.o: $(hdrdir)/ruby/internal/intern/set.h +nonblock.o: $(hdrdir)/ruby/internal/intern/signal.h +nonblock.o: $(hdrdir)/ruby/internal/intern/sprintf.h +nonblock.o: $(hdrdir)/ruby/internal/intern/string.h +nonblock.o: $(hdrdir)/ruby/internal/intern/struct.h +nonblock.o: $(hdrdir)/ruby/internal/intern/thread.h +nonblock.o: $(hdrdir)/ruby/internal/intern/time.h +nonblock.o: $(hdrdir)/ruby/internal/intern/variable.h +nonblock.o: $(hdrdir)/ruby/internal/intern/vm.h +nonblock.o: $(hdrdir)/ruby/internal/interpreter.h +nonblock.o: $(hdrdir)/ruby/internal/iterator.h +nonblock.o: $(hdrdir)/ruby/internal/memory.h +nonblock.o: $(hdrdir)/ruby/internal/method.h +nonblock.o: $(hdrdir)/ruby/internal/module.h +nonblock.o: $(hdrdir)/ruby/internal/newobj.h +nonblock.o: $(hdrdir)/ruby/internal/scan_args.h +nonblock.o: $(hdrdir)/ruby/internal/special_consts.h +nonblock.o: $(hdrdir)/ruby/internal/static_assert.h +nonblock.o: $(hdrdir)/ruby/internal/stdalign.h +nonblock.o: $(hdrdir)/ruby/internal/stdbool.h +nonblock.o: $(hdrdir)/ruby/internal/stdckdint.h +nonblock.o: $(hdrdir)/ruby/internal/symbol.h +nonblock.o: $(hdrdir)/ruby/internal/value.h +nonblock.o: $(hdrdir)/ruby/internal/value_type.h +nonblock.o: $(hdrdir)/ruby/internal/variable.h +nonblock.o: $(hdrdir)/ruby/internal/warning_push.h +nonblock.o: $(hdrdir)/ruby/internal/xmalloc.h nonblock.o: $(hdrdir)/ruby/io.h nonblock.o: $(hdrdir)/ruby/missing.h nonblock.o: $(hdrdir)/ruby/onigmo.h @@ -12,6 +172,5 @@ nonblock.o: $(hdrdir)/ruby/oniguruma.h nonblock.o: $(hdrdir)/ruby/ruby.h nonblock.o: $(hdrdir)/ruby/st.h nonblock.o: $(hdrdir)/ruby/subst.h -nonblock.o: $(top_srcdir)/include/ruby.h nonblock.o: nonblock.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/io/nonblock/extconf.rb b/ext/io/nonblock/extconf.rb index d813a01e7c..505c9e6252 100644 --- a/ext/io/nonblock/extconf.rb +++ b/ext/io/nonblock/extconf.rb @@ -2,6 +2,13 @@ require 'mkmf' target = "io/nonblock" +unless RUBY_ENGINE == 'ruby' + File.write("Makefile", dummy_makefile($srcdir).join("")) + return +end + +have_func("rb_io_descriptor", "ruby/io.h") + hdr = %w"fcntl.h" if have_macro("O_NONBLOCK", hdr) and (have_macro("F_GETFL", hdr) or have_macro("F_SETFL", hdr)) diff --git a/ext/io/nonblock/io-nonblock.gemspec b/ext/io/nonblock/io-nonblock.gemspec new file mode 100644 index 0000000000..4f5b8cef6f --- /dev/null +++ b/ext/io/nonblock/io-nonblock.gemspec @@ -0,0 +1,25 @@ +Gem::Specification.new do |spec| + spec.name = "io-nonblock" + spec.version = "0.3.2" + spec.authors = ["Nobu Nakada"] + spec.email = ["nobu@ruby-lang.org"] + + spec.summary = %q{Enables non-blocking mode with IO class} + spec.description = %q{Enables non-blocking mode with IO class} + spec.homepage = "https://github.com/ruby/io-nonblock" + spec.licenses = ["Ruby", "BSD-2-Clause"] + spec.required_ruby_version = Gem::Requirement.new(">= 3.0") + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = spec.homepage + + spec.files = %w[ + COPYING + README.md + ext/io/nonblock/depend + ext/io/nonblock/extconf.rb + ext/io/nonblock/nonblock.c + ] + spec.extensions = %w[ext/io/nonblock/extconf.rb] + spec.require_paths = ["lib"] +end diff --git a/ext/io/nonblock/nonblock.c b/ext/io/nonblock/nonblock.c index 1c0bdc68e7..cd40ea3335 100644 --- a/ext/io/nonblock/nonblock.c +++ b/ext/io/nonblock/nonblock.c @@ -17,16 +17,27 @@ #endif #include <fcntl.h> +#ifndef HAVE_RB_IO_DESCRIPTOR +static int +io_descriptor_fallback(VALUE io) +{ + rb_io_t *fptr; + GetOpenFile(io, fptr); + return fptr->fd; +} +#define rb_io_descriptor io_descriptor_fallback +#endif + #ifdef F_GETFL static int -io_nonblock_mode(int fd) +get_fcntl_flags(int fd) { int f = fcntl(fd, F_GETFL); if (f == -1) rb_sys_fail(0); return f; } #else -#define io_nonblock_mode(fd) ((void)(fd), 0) +#define get_fcntl_flags(fd) ((void)(fd), 0) #endif #ifdef F_GETFL @@ -39,10 +50,8 @@ io_nonblock_mode(int fd) static VALUE rb_io_nonblock_p(VALUE io) { - rb_io_t *fptr; - GetOpenFile(io, fptr); - if (io_nonblock_mode(fptr->fd) & O_NONBLOCK) - return Qtrue; + if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK) + return Qtrue; return Qfalse; } #else @@ -50,6 +59,15 @@ rb_io_nonblock_p(VALUE io) #endif #ifdef F_SETFL +static void +set_fcntl_flags(int fd, int f) +{ + if (fcntl(fd, F_SETFL, f) == -1) + rb_sys_fail(0); +} + +#ifndef RUBY_IO_NONBLOCK_METHODS + static int io_nonblock_set(int fd, int f, int nb) { @@ -63,8 +81,7 @@ io_nonblock_set(int fd, int f, int nb) return 0; f &= ~O_NONBLOCK; } - if (fcntl(fd, F_SETFL, f) == -1) - rb_sys_fail(0); + set_fcntl_flags(fd, f); return 1; } @@ -74,32 +91,77 @@ io_nonblock_set(int fd, int f, int nb) * * Enables non-blocking mode on a stream when set to * +true+, and blocking mode when set to +false+. + * + * This method set or clear O_NONBLOCK flag for the file descriptor + * in <em>ios</em>. + * + * The behavior of most IO methods is not affected by this flag + * because they retry system calls to complete their task + * after EAGAIN and partial read/write. + * (An exception is IO#syswrite which doesn't retry.) + * + * This method can be used to clear non-blocking mode of standard I/O. + * Since nonblocking methods (read_nonblock, etc.) set non-blocking mode but + * they doesn't clear it, this method is usable as follows. + * + * END { STDOUT.nonblock = false } + * STDOUT.write_nonblock("foo") + * + * Since the flag is shared across processes and + * many non-Ruby commands doesn't expect standard I/O with non-blocking mode, + * it would be safe to clear the flag before Ruby program exits. + * + * For example following Ruby program leaves STDIN/STDOUT/STDER non-blocking mode. + * (STDIN, STDOUT and STDERR are connected to a terminal. + * So making one of them nonblocking-mode effects other two.) + * Thus cat command try to read from standard input and + * it causes "Resource temporarily unavailable" error (EAGAIN). + * + * % ruby -e ' + * STDOUT.write_nonblock("foo\n")'; cat + * foo + * cat: -: Resource temporarily unavailable + * + * Clearing the flag makes the behavior of cat command normal. + * (cat command waits input from standard input.) + * + * % ruby -rio/nonblock -e ' + * END { STDOUT.nonblock = false } + * STDOUT.write_nonblock("foo") + * '; cat + * foo + * */ static VALUE -rb_io_nonblock_set(VALUE io, VALUE nb) +rb_io_nonblock_set(VALUE self, VALUE value) { - rb_io_t *fptr; - GetOpenFile(io, fptr); - if (RTEST(nb)) - rb_io_set_nonblock(fptr); - else - io_nonblock_set(fptr->fd, io_nonblock_mode(fptr->fd), RTEST(nb)); - return io; + if (RTEST(value)) { + rb_io_t *fptr; + GetOpenFile(self, fptr); + rb_io_set_nonblock(fptr); + } + else { + int descriptor = rb_io_descriptor(self); + io_nonblock_set(descriptor, get_fcntl_flags(descriptor), RTEST(value)); + } + + return self; } +#endif /* RUBY_IO_NONBLOCK_METHODS */ + static VALUE io_nonblock_restore(VALUE arg) { int *restore = (int *)arg; - if (fcntl(restore[0], F_SETFL, restore[1]) == -1) - rb_sys_fail(0); + set_fcntl_flags(restore[0], restore[1]); return Qnil; } /* * call-seq: - * io.nonblock {|io| } -> io - * io.nonblock(boolean) {|io| } -> io + * io.nonblock {|io| } -> object + * io.nonblock(boolean) {|io| } -> object * * Yields +self+ in non-blocking mode. * @@ -107,24 +169,25 @@ io_nonblock_restore(VALUE arg) * The original mode is restored after the block is executed. */ static VALUE -rb_io_nonblock_block(int argc, VALUE *argv, VALUE io) +rb_io_nonblock_block(int argc, VALUE *argv, VALUE self) { int nb = 1; - rb_io_t *fptr; - int f, restore[2]; - GetOpenFile(io, fptr); + int descriptor = rb_io_descriptor(self); + if (argc > 0) { - VALUE v; - rb_scan_args(argc, argv, "01", &v); - nb = RTEST(v); + VALUE v; + rb_scan_args(argc, argv, "01", &v); + nb = RTEST(v); } - f = io_nonblock_mode(fptr->fd); - restore[0] = fptr->fd; - restore[1] = f; - if (!io_nonblock_set(fptr->fd, f, nb)) - return rb_yield(io); - return rb_ensure(rb_yield, io, io_nonblock_restore, (VALUE)restore); + + int current_flags = get_fcntl_flags(descriptor); + int restore[2] = {descriptor, current_flags}; + + if (!io_nonblock_set(descriptor, current_flags, nb)) + return rb_yield(self); + + return rb_ensure(rb_yield, self, io_nonblock_restore, (VALUE)restore); } #else #define rb_io_nonblock_set rb_f_notimplement @@ -134,7 +197,14 @@ rb_io_nonblock_block(int argc, VALUE *argv, VALUE io) void Init_nonblock(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + +#ifndef RUBY_IO_NONBLOCK_METHODS rb_define_method(rb_cIO, "nonblock?", rb_io_nonblock_p, 0); rb_define_method(rb_cIO, "nonblock=", rb_io_nonblock_set, 1); +#endif + rb_define_method(rb_cIO, "nonblock", rb_io_nonblock_block, -1); } diff --git a/ext/io/wait/depend b/ext/io/wait/depend index f509dcd8a4..41d53c1400 100644 --- a/ext/io/wait/depend +++ b/ext/io/wait/depend @@ -1,10 +1,170 @@ # AUTOGENERATED DEPENDENCIES START wait.o: $(RUBY_EXTCONF_H) wait.o: $(arch_hdrdir)/ruby/config.h +wait.o: $(hdrdir)/ruby.h +wait.o: $(hdrdir)/ruby/assert.h wait.o: $(hdrdir)/ruby/backward.h +wait.o: $(hdrdir)/ruby/backward/2/assume.h +wait.o: $(hdrdir)/ruby/backward/2/attributes.h +wait.o: $(hdrdir)/ruby/backward/2/bool.h +wait.o: $(hdrdir)/ruby/backward/2/inttypes.h +wait.o: $(hdrdir)/ruby/backward/2/limits.h +wait.o: $(hdrdir)/ruby/backward/2/long_long.h +wait.o: $(hdrdir)/ruby/backward/2/stdalign.h +wait.o: $(hdrdir)/ruby/backward/2/stdarg.h wait.o: $(hdrdir)/ruby/defines.h wait.o: $(hdrdir)/ruby/encoding.h wait.o: $(hdrdir)/ruby/intern.h +wait.o: $(hdrdir)/ruby/internal/abi.h +wait.o: $(hdrdir)/ruby/internal/anyargs.h +wait.o: $(hdrdir)/ruby/internal/arithmetic.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/char.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/double.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/int.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/long.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/short.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +wait.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +wait.o: $(hdrdir)/ruby/internal/assume.h +wait.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +wait.o: $(hdrdir)/ruby/internal/attr/artificial.h +wait.o: $(hdrdir)/ruby/internal/attr/cold.h +wait.o: $(hdrdir)/ruby/internal/attr/const.h +wait.o: $(hdrdir)/ruby/internal/attr/constexpr.h +wait.o: $(hdrdir)/ruby/internal/attr/deprecated.h +wait.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +wait.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +wait.o: $(hdrdir)/ruby/internal/attr/error.h +wait.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +wait.o: $(hdrdir)/ruby/internal/attr/forceinline.h +wait.o: $(hdrdir)/ruby/internal/attr/format.h +wait.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +wait.o: $(hdrdir)/ruby/internal/attr/noalias.h +wait.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +wait.o: $(hdrdir)/ruby/internal/attr/noexcept.h +wait.o: $(hdrdir)/ruby/internal/attr/noinline.h +wait.o: $(hdrdir)/ruby/internal/attr/nonnull.h +wait.o: $(hdrdir)/ruby/internal/attr/noreturn.h +wait.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +wait.o: $(hdrdir)/ruby/internal/attr/pure.h +wait.o: $(hdrdir)/ruby/internal/attr/restrict.h +wait.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +wait.o: $(hdrdir)/ruby/internal/attr/warning.h +wait.o: $(hdrdir)/ruby/internal/attr/weakref.h +wait.o: $(hdrdir)/ruby/internal/cast.h +wait.o: $(hdrdir)/ruby/internal/compiler_is.h +wait.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +wait.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +wait.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +wait.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +wait.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +wait.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +wait.o: $(hdrdir)/ruby/internal/compiler_since.h +wait.o: $(hdrdir)/ruby/internal/config.h +wait.o: $(hdrdir)/ruby/internal/constant_p.h +wait.o: $(hdrdir)/ruby/internal/core.h +wait.o: $(hdrdir)/ruby/internal/core/rarray.h +wait.o: $(hdrdir)/ruby/internal/core/rbasic.h +wait.o: $(hdrdir)/ruby/internal/core/rbignum.h +wait.o: $(hdrdir)/ruby/internal/core/rclass.h +wait.o: $(hdrdir)/ruby/internal/core/rdata.h +wait.o: $(hdrdir)/ruby/internal/core/rfile.h +wait.o: $(hdrdir)/ruby/internal/core/rhash.h +wait.o: $(hdrdir)/ruby/internal/core/robject.h +wait.o: $(hdrdir)/ruby/internal/core/rregexp.h +wait.o: $(hdrdir)/ruby/internal/core/rstring.h +wait.o: $(hdrdir)/ruby/internal/core/rstruct.h +wait.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +wait.o: $(hdrdir)/ruby/internal/ctype.h +wait.o: $(hdrdir)/ruby/internal/dllexport.h +wait.o: $(hdrdir)/ruby/internal/dosish.h +wait.o: $(hdrdir)/ruby/internal/encoding/coderange.h +wait.o: $(hdrdir)/ruby/internal/encoding/ctype.h +wait.o: $(hdrdir)/ruby/internal/encoding/encoding.h +wait.o: $(hdrdir)/ruby/internal/encoding/pathname.h +wait.o: $(hdrdir)/ruby/internal/encoding/re.h +wait.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +wait.o: $(hdrdir)/ruby/internal/encoding/string.h +wait.o: $(hdrdir)/ruby/internal/encoding/symbol.h +wait.o: $(hdrdir)/ruby/internal/encoding/transcode.h +wait.o: $(hdrdir)/ruby/internal/error.h +wait.o: $(hdrdir)/ruby/internal/eval.h +wait.o: $(hdrdir)/ruby/internal/event.h +wait.o: $(hdrdir)/ruby/internal/fl_type.h +wait.o: $(hdrdir)/ruby/internal/gc.h +wait.o: $(hdrdir)/ruby/internal/glob.h +wait.o: $(hdrdir)/ruby/internal/globals.h +wait.o: $(hdrdir)/ruby/internal/has/attribute.h +wait.o: $(hdrdir)/ruby/internal/has/builtin.h +wait.o: $(hdrdir)/ruby/internal/has/c_attribute.h +wait.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +wait.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +wait.o: $(hdrdir)/ruby/internal/has/extension.h +wait.o: $(hdrdir)/ruby/internal/has/feature.h +wait.o: $(hdrdir)/ruby/internal/has/warning.h +wait.o: $(hdrdir)/ruby/internal/intern/array.h +wait.o: $(hdrdir)/ruby/internal/intern/bignum.h +wait.o: $(hdrdir)/ruby/internal/intern/class.h +wait.o: $(hdrdir)/ruby/internal/intern/compar.h +wait.o: $(hdrdir)/ruby/internal/intern/complex.h +wait.o: $(hdrdir)/ruby/internal/intern/cont.h +wait.o: $(hdrdir)/ruby/internal/intern/dir.h +wait.o: $(hdrdir)/ruby/internal/intern/enum.h +wait.o: $(hdrdir)/ruby/internal/intern/enumerator.h +wait.o: $(hdrdir)/ruby/internal/intern/error.h +wait.o: $(hdrdir)/ruby/internal/intern/eval.h +wait.o: $(hdrdir)/ruby/internal/intern/file.h +wait.o: $(hdrdir)/ruby/internal/intern/hash.h +wait.o: $(hdrdir)/ruby/internal/intern/io.h +wait.o: $(hdrdir)/ruby/internal/intern/load.h +wait.o: $(hdrdir)/ruby/internal/intern/marshal.h +wait.o: $(hdrdir)/ruby/internal/intern/numeric.h +wait.o: $(hdrdir)/ruby/internal/intern/object.h +wait.o: $(hdrdir)/ruby/internal/intern/parse.h +wait.o: $(hdrdir)/ruby/internal/intern/proc.h +wait.o: $(hdrdir)/ruby/internal/intern/process.h +wait.o: $(hdrdir)/ruby/internal/intern/random.h +wait.o: $(hdrdir)/ruby/internal/intern/range.h +wait.o: $(hdrdir)/ruby/internal/intern/rational.h +wait.o: $(hdrdir)/ruby/internal/intern/re.h +wait.o: $(hdrdir)/ruby/internal/intern/ruby.h +wait.o: $(hdrdir)/ruby/internal/intern/select.h +wait.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +wait.o: $(hdrdir)/ruby/internal/intern/set.h +wait.o: $(hdrdir)/ruby/internal/intern/signal.h +wait.o: $(hdrdir)/ruby/internal/intern/sprintf.h +wait.o: $(hdrdir)/ruby/internal/intern/string.h +wait.o: $(hdrdir)/ruby/internal/intern/struct.h +wait.o: $(hdrdir)/ruby/internal/intern/thread.h +wait.o: $(hdrdir)/ruby/internal/intern/time.h +wait.o: $(hdrdir)/ruby/internal/intern/variable.h +wait.o: $(hdrdir)/ruby/internal/intern/vm.h +wait.o: $(hdrdir)/ruby/internal/interpreter.h +wait.o: $(hdrdir)/ruby/internal/iterator.h +wait.o: $(hdrdir)/ruby/internal/memory.h +wait.o: $(hdrdir)/ruby/internal/method.h +wait.o: $(hdrdir)/ruby/internal/module.h +wait.o: $(hdrdir)/ruby/internal/newobj.h +wait.o: $(hdrdir)/ruby/internal/scan_args.h +wait.o: $(hdrdir)/ruby/internal/special_consts.h +wait.o: $(hdrdir)/ruby/internal/static_assert.h +wait.o: $(hdrdir)/ruby/internal/stdalign.h +wait.o: $(hdrdir)/ruby/internal/stdbool.h +wait.o: $(hdrdir)/ruby/internal/stdckdint.h +wait.o: $(hdrdir)/ruby/internal/symbol.h +wait.o: $(hdrdir)/ruby/internal/value.h +wait.o: $(hdrdir)/ruby/internal/value_type.h +wait.o: $(hdrdir)/ruby/internal/variable.h +wait.o: $(hdrdir)/ruby/internal/warning_push.h +wait.o: $(hdrdir)/ruby/internal/xmalloc.h wait.o: $(hdrdir)/ruby/io.h wait.o: $(hdrdir)/ruby/missing.h wait.o: $(hdrdir)/ruby/onigmo.h @@ -12,6 +172,5 @@ wait.o: $(hdrdir)/ruby/oniguruma.h wait.o: $(hdrdir)/ruby/ruby.h wait.o: $(hdrdir)/ruby/st.h wait.o: $(hdrdir)/ruby/subst.h -wait.o: $(top_srcdir)/include/ruby.h wait.o: wait.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb index b5d36c3fe3..00c455a45c 100644 --- a/ext/io/wait/extconf.rb +++ b/ext/io/wait/extconf.rb @@ -1,19 +1,4 @@ # frozen_string_literal: false require 'mkmf' -target = "io/wait" -unless macro_defined?("DOSISH", "#include <ruby.h>") - have_header(ioctl_h = "sys/ioctl.h") or ioctl_h = nil - fionread = %w[sys/ioctl.h sys/filio.h sys/socket.h].find do |h| - have_macro("FIONREAD", [h, ioctl_h].compact) - end - if fionread - $defs << "-DFIONREAD_HEADER=\"<#{fionread}>\"" - create_makefile(target) - end -else - if have_func("rb_w32_ioctlsocket", "ruby.h") - have_func("rb_w32_is_socket", "ruby.h") - create_makefile(target) - end -end +create_makefile("io/wait") diff --git a/ext/io/wait/io-wait.gemspec b/ext/io/wait/io-wait.gemspec new file mode 100644 index 0000000000..c1c6172589 --- /dev/null +++ b/ext/io/wait/io-wait.gemspec @@ -0,0 +1,39 @@ +_VERSION = "0.4.0" + +Gem::Specification.new do |spec| + spec.name = "io-wait" + spec.version = _VERSION + spec.authors = ["Nobu Nakada", "Charles Oliver Nutter"] + spec.email = ["nobu@ruby-lang.org", "headius@headius.com"] + + spec.summary = %q{Waits until IO is readable or writable without blocking.} + spec.description = %q{Waits until IO is readable or writable without blocking.} + spec.homepage = "https://github.com/ruby/io-wait" + spec.licenses = ["Ruby", "BSD-2-Clause"] + spec.required_ruby_version = Gem::Requirement.new(">= 3.2") + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = spec.homepage + + jruby = true if Gem::Platform.new('java') =~ spec.platform or RUBY_ENGINE == 'jruby' + dir, gemspec = File.split(__FILE__) + excludes = [ + *%w[:^/.git* :^/Gemfile* :^/Rakefile* :^/bin/ :^/test/ :^/rakelib/ :^*.java], + *(jruby ? %w[:^/ext/io] : %w[:^/ext/java]), + ":(exclude,literal,top)#{gemspec}" + ] + files = IO.popen(%w[git ls-files -z --] + excludes, chdir: dir, &:read).split("\x0") + + spec.files = files + spec.bindir = "exe" + spec.executables = [] + spec.require_paths = ["lib"] + + if jruby + spec.platform = 'java' + spec.files << "lib/io/wait.jar" + spec.require_paths += ["ext/java/lib"] + else + spec.extensions = %w[ext/io/wait/extconf.rb] + end +end diff --git a/ext/io/wait/wait.c b/ext/io/wait/wait.c index f7a7508eeb..f7575191fe 100644 --- a/ext/io/wait/wait.c +++ b/ext/io/wait/wait.c @@ -1,3 +1,4 @@ +/* -*- c-file-style: "ruby"; indent-tabs-mode: t -*- */ /********************************************************************** io/wait.c - @@ -10,245 +11,13 @@ **********************************************************************/ -#include "ruby.h" -#include "ruby/io.h" - -#include <sys/types.h> -#if defined(HAVE_UNISTD_H) && (defined(__sun)) -#include <unistd.h> -#endif -#if defined(HAVE_SYS_IOCTL_H) -#include <sys/ioctl.h> -#endif -#if defined(FIONREAD_HEADER) -#include FIONREAD_HEADER -#endif - -#ifdef HAVE_RB_W32_IOCTLSOCKET -#define ioctl ioctlsocket -#define ioctl_arg u_long -#define ioctl_arg2num(i) ULONG2NUM(i) -#else -#define ioctl_arg int -#define ioctl_arg2num(i) INT2NUM(i) -#endif - -#ifdef HAVE_RB_W32_IS_SOCKET -#define FIONREAD_POSSIBLE_P(fd) rb_w32_is_socket(fd) -#else -#define FIONREAD_POSSIBLE_P(fd) ((void)(fd),Qtrue) -#endif - -static VALUE io_ready_p _((VALUE io)); -static VALUE io_wait_readable _((int argc, VALUE *argv, VALUE io)); -static VALUE io_wait_writable _((int argc, VALUE *argv, VALUE io)); -void Init_wait _((void)); - -static struct timeval * -get_timeout(int argc, VALUE *argv, struct timeval *timerec) -{ - VALUE timeout = Qnil; - rb_check_arity(argc, 0, 1); - if (!argc || NIL_P(timeout = argv[0])) { - return NULL; - } - else { - *timerec = rb_time_interval(timeout); - return timerec; - } -} - -static int -wait_for_single_fd(rb_io_t *fptr, int events, struct timeval *tv) -{ - int i = rb_wait_for_single_fd(fptr->fd, events, tv); - if (i < 0) - rb_sys_fail(0); - rb_io_check_closed(fptr); - return (i & events); -} - -/* - * call-seq: - * io.nread -> int - * - * Returns number of bytes that can be read without blocking. - * Returns zero if no information available. - */ - -static VALUE -io_nread(VALUE io) -{ - rb_io_t *fptr; - int len; - ioctl_arg n; - - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - len = rb_io_read_pending(fptr); - if (len > 0) return INT2FIX(len); - if (!FIONREAD_POSSIBLE_P(fptr->fd)) return INT2FIX(0); - if (ioctl(fptr->fd, FIONREAD, &n)) return INT2FIX(0); - if (n > 0) return ioctl_arg2num(n); - return INT2FIX(0); -} - -/* - * call-seq: - * io.ready? -> true, false or nil - * - * Returns true if input available without blocking, or false. - * Returns nil if no information available. - */ - -static VALUE -io_ready_p(VALUE io) -{ - rb_io_t *fptr; - struct timeval tv = {0, 0}; - - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - if (rb_io_read_pending(fptr)) return Qtrue; - if (wait_for_single_fd(fptr, RB_WAITFD_IN, &tv)) - return Qtrue; - return Qfalse; -} - -/* - * call-seq: - * io.wait_readable -> IO, true or nil - * io.wait_readable(timeout) -> IO, true or nil - * - * Waits until IO is readable without blocking and returns +self+, or - * +nil+ when times out. - * Returns +true+ immediately when buffered data is available. - */ - -static VALUE -io_wait_readable(int argc, VALUE *argv, VALUE io) -{ - rb_io_t *fptr; - struct timeval timerec; - struct timeval *tv; - - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - tv = get_timeout(argc, argv, &timerec); - if (rb_io_read_pending(fptr)) return Qtrue; - if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) { - return io; - } - return Qnil; -} - -/* - * call-seq: - * io.wait_writable -> IO - * io.wait_writable(timeout) -> IO or nil - * - * Waits until IO is writable without blocking and returns +self+ or - * +nil+ when times out. - */ -static VALUE -io_wait_writable(int argc, VALUE *argv, VALUE io) -{ - rb_io_t *fptr; - struct timeval timerec; - struct timeval *tv; - - GetOpenFile(io, fptr); - rb_io_check_writable(fptr); - tv = get_timeout(argc, argv, &timerec); - if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) { - return io; - } - return Qnil; -} - -static int -wait_mode_sym(VALUE mode) -{ - if (mode == ID2SYM(rb_intern("r"))) { - return RB_WAITFD_IN; - } - if (mode == ID2SYM(rb_intern("read"))) { - return RB_WAITFD_IN; - } - if (mode == ID2SYM(rb_intern("readable"))) { - return RB_WAITFD_IN; - } - if (mode == ID2SYM(rb_intern("w"))) { - return RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("write"))) { - return RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("writable"))) { - return RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("rw"))) { - return RB_WAITFD_IN|RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("read_write"))) { - return RB_WAITFD_IN|RB_WAITFD_OUT; - } - if (mode == ID2SYM(rb_intern("readable_writable"))) { - return RB_WAITFD_IN|RB_WAITFD_OUT; - } - rb_raise(rb_eArgError, "unsupported mode: %"PRIsVALUE, mode); - return 0; -} - -/* - * call-seq: - * io.wait(timeout = nil, mode = :read) -> IO, true or nil - * - * Waits until IO is readable or writable without blocking and returns - * +self+, or +nil+ when times out. - * Returns +true+ immediately when buffered data is available. - * Optional parameter +mode+ is one of +:read+, +:write+, or - * +:read_write+. - */ - -static VALUE -io_wait_readwrite(int argc, VALUE *argv, VALUE io) -{ - rb_io_t *fptr; - struct timeval timerec; - struct timeval *tv = NULL; - int event = 0; - int i; - - GetOpenFile(io, fptr); - for (i = 0; i < argc; ++i) { - if (SYMBOL_P(argv[i])) { - event |= wait_mode_sym(argv[i]); - } - else { - *(tv = &timerec) = rb_time_interval(argv[i]); - } - } - /* rb_time_interval() and might_mode() might convert the argument */ - rb_io_check_closed(fptr); - if (!event) event = RB_WAITFD_IN; - if ((event & RB_WAITFD_IN) && rb_io_read_pending(fptr)) - return Qtrue; - if (wait_for_single_fd(fptr, event, tv)) - return io; - return Qnil; -} +#include "ruby.h" /* abi_version */ /* - * IO wait methods + * IO wait methods are built in ruby now, just for backward compatibility. */ void Init_wait(void) { - rb_define_method(rb_cIO, "nread", io_nread, 0); - rb_define_method(rb_cIO, "ready?", io_ready_p, 0); - rb_define_method(rb_cIO, "wait", io_wait_readwrite, -1); - rb_define_method(rb_cIO, "wait_readable", io_wait_readable, -1); - rb_define_method(rb_cIO, "wait_writable", io_wait_writable, -1); } |
