Ruby  2.4.2p198(2017-09-14revision59899)
console.c
Go to the documentation of this file.
1 /* -*- c-file-style: "ruby" -*- */
2 /*
3  * console IO module
4  */
5 #include "ruby.h"
6 #include "ruby/io.h"
7 
8 #ifdef HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11 #ifdef HAVE_FCNTL_H
12 #include <fcntl.h>
13 #endif
14 #ifdef HAVE_SYS_IOCTL_H
15 #include <sys/ioctl.h>
16 #endif
17 #ifndef RARRAY_CONST_PTR
18 # define RARRAY_CONST_PTR(ary) RARRAY_PTR(ary)
19 #endif
20 #ifndef HAVE_RB_FUNCALLV
21 # define rb_funcallv rb_funcall2
22 #endif
23 
24 #if defined HAVE_TERMIOS_H
25 # include <termios.h>
26 typedef struct termios conmode;
27 
28 static int
29 setattr(int fd, conmode *t)
30 {
31  while (tcsetattr(fd, TCSAFLUSH, t)) {
32  if (errno != EINTR) return 0;
33  }
34  return 1;
35 }
36 # define getattr(fd, t) (tcgetattr(fd, t) == 0)
37 #elif defined HAVE_TERMIO_H
38 # include <termio.h>
39 typedef struct termio conmode;
40 # define setattr(fd, t) (ioctl(fd, TCSETAF, t) == 0)
41 # define getattr(fd, t) (ioctl(fd, TCGETA, t) == 0)
42 #elif defined HAVE_SGTTY_H
43 # include <sgtty.h>
44 typedef struct sgttyb conmode;
45 # ifdef HAVE_STTY
46 # define setattr(fd, t) (stty(fd, t) == 0)
47 # else
48 # define setattr(fd, t) (ioctl((fd), TIOCSETP, (t)) == 0)
49 # endif
50 # ifdef HAVE_GTTY
51 # define getattr(fd, t) (gtty(fd, t) == 0)
52 # else
53 # define getattr(fd, t) (ioctl((fd), TIOCGETP, (t)) == 0)
54 # endif
55 #elif defined _WIN32
56 #include <winioctl.h>
57 typedef DWORD conmode;
58 
59 #define LAST_ERROR rb_w32_map_errno(GetLastError())
60 #define SET_LAST_ERROR (errno = LAST_ERROR, 0)
61 
62 static int
63 setattr(int fd, conmode *t)
64 {
65  int x = SetConsoleMode((HANDLE)rb_w32_get_osfhandle(fd), *t);
66  if (!x) errno = LAST_ERROR;
67  return x;
68 }
69 
70 static int
71 getattr(int fd, conmode *t)
72 {
73  int x = GetConsoleMode((HANDLE)rb_w32_get_osfhandle(fd), t);
74  if (!x) errno = LAST_ERROR;
75  return x;
76 }
77 #endif
78 #ifndef SET_LAST_ERROR
79 #define SET_LAST_ERROR (0)
80 #endif
81 
83 #if ENABLE_IO_GETPASS
84 static ID id_gets;
85 #endif
86 
87 #ifndef HAVE_RB_F_SEND
88 static ID id___send__;
89 
90 static VALUE
92 {
93  VALUE sym = argv[0];
94  ID vid = rb_check_id(&sym);
95  if (vid) {
96  --argc;
97  ++argv;
98  }
99  else {
100  vid = id___send__;
101  }
102  return rb_funcallv(recv, vid, argc, argv);
103 }
104 #endif
105 
106 #ifndef HAVE_RB_SYM2STR
107 # define rb_sym2str(sym) rb_id2str(SYM2ID(sym))
108 #endif
109 
110 typedef struct {
111  int vmin;
112  int vtime;
113 } rawmode_arg_t;
114 
115 static rawmode_arg_t *
117 {
118  rawmode_arg_t *optp = NULL;
119  VALUE vopts;
120  rb_scan_args(argc, argv, "0:", &vopts);
121  if (!NIL_P(vopts)) {
122  VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min));
123  VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time));
124  /* default values by `stty raw` */
125  opts->vmin = 1;
126  opts->vtime = 0;
127  if (!NIL_P(vmin)) {
128  opts->vmin = NUM2INT(vmin);
129  optp = opts;
130  }
131  if (!NIL_P(vtime)) {
132  VALUE v10 = INT2FIX(10);
133  vtime = rb_funcall3(vtime, '*', 1, &v10);
134  opts->vtime = NUM2INT(vtime);
135  optp = opts;
136  }
137  }
138  return optp;
139 }
140 
141 static void
142 set_rawmode(conmode *t, void *arg)
143 {
144 #ifdef HAVE_CFMAKERAW
145  cfmakeraw(t);
146  t->c_lflag &= ~(ECHOE|ECHOK);
147 #elif defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
148  t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
149  t->c_oflag &= ~OPOST;
150  t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
151  t->c_cflag &= ~(CSIZE|PARENB);
152  t->c_cflag |= CS8;
153 #elif defined HAVE_SGTTY_H
154  t->sg_flags &= ~ECHO;
155  t->sg_flags |= RAW;
156 #elif defined _WIN32
157  *t = 0;
158 #endif
159 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
160  if (arg) {
161  const rawmode_arg_t *r = arg;
162  if (r->vmin >= 0) t->c_cc[VMIN] = r->vmin;
163  if (r->vtime >= 0) t->c_cc[VTIME] = r->vtime;
164  }
165 #endif
166 }
167 
168 static void
169 set_cookedmode(conmode *t, void *arg)
170 {
171 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
172  t->c_iflag |= (BRKINT|ISTRIP|ICRNL|IXON);
173  t->c_oflag |= OPOST;
174  t->c_lflag |= (ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN);
175 #elif defined HAVE_SGTTY_H
176  t->sg_flags |= ECHO;
177  t->sg_flags &= ~RAW;
178 #elif defined _WIN32
179  *t |= ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT;
180 #endif
181 }
182 
183 static void
184 set_noecho(conmode *t, void *arg)
185 {
186 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
187  t->c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
188 #elif defined HAVE_SGTTY_H
189  t->sg_flags &= ~ECHO;
190 #elif defined _WIN32
191  *t &= ~ENABLE_ECHO_INPUT;
192 #endif
193 }
194 
195 static void
196 set_echo(conmode *t, void *arg)
197 {
198 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
199  t->c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
200 #elif defined HAVE_SGTTY_H
201  t->sg_flags |= ECHO;
202 #elif defined _WIN32
203  *t |= ENABLE_ECHO_INPUT;
204 #endif
205 }
206 
207 static int
208 echo_p(conmode *t)
209 {
210 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
211  return (t->c_lflag & (ECHO | ECHONL)) != 0;
212 #elif defined HAVE_SGTTY_H
213  return (t->sg_flags & ECHO) != 0;
214 #elif defined _WIN32
215  return (*t & ENABLE_ECHO_INPUT) != 0;
216 #endif
217 }
218 
219 static int
220 set_ttymode(int fd, conmode *t, void (*setter)(conmode *, void *), void *arg)
221 {
222  conmode r;
223  if (!getattr(fd, t)) return 0;
224  r = *t;
225  setter(&r, arg);
226  return setattr(fd, &r);
227 }
228 
229 #define GetReadFD(fptr) ((fptr)->fd)
230 
231 static inline int
232 get_write_fd(const rb_io_t *fptr)
233 {
234  VALUE wio = fptr->tied_io_for_writing;
235  rb_io_t *ofptr;
236  if (!wio) return fptr->fd;
237  GetOpenFile(wio, ofptr);
238  return ofptr->fd;
239 }
240 #define GetWriteFD(fptr) get_write_fd(fptr)
241 
242 #define FD_PER_IO 2
243 
244 static VALUE
245 ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void *arg)
246 {
247  rb_io_t *fptr;
248  int status = -1;
249  int error = 0;
250  int fd[FD_PER_IO];
251  conmode t[FD_PER_IO];
252  VALUE result = Qnil;
253 
254  GetOpenFile(io, fptr);
255  fd[0] = GetReadFD(fptr);
256  if (fd[0] != -1) {
257  if (set_ttymode(fd[0], t+0, setter, arg)) {
258  status = 0;
259  }
260  else {
261  error = errno;
262  fd[0] = -1;
263  }
264  }
265  fd[1] = GetWriteFD(fptr);
266  if (fd[1] != -1 && fd[1] != fd[0]) {
267  if (set_ttymode(fd[1], t+1, setter, arg)) {
268  status = 0;
269  }
270  else {
271  error = errno;
272  fd[1] = -1;
273  }
274  }
275  if (status == 0) {
276  result = rb_protect(func, io, &status);
277  }
278  GetOpenFile(io, fptr);
279  if (fd[0] != -1 && fd[0] == GetReadFD(fptr)) {
280  if (!setattr(fd[0], t+0)) {
281  error = errno;
282  status = -1;
283  }
284  }
285  if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(fptr)) {
286  if (!setattr(fd[1], t+1)) {
287  error = errno;
288  status = -1;
289  }
290  }
291  if (status) {
292  if (status == -1) {
293  rb_syserr_fail(error, 0);
294  }
295  rb_jump_tag(status);
296  }
297  return result;
298 }
299 
300 /*
301  * call-seq:
302  * io.raw(min: nil, time: nil) {|io| }
303  *
304  * Yields +self+ within raw mode.
305  *
306  * STDIN.raw(&:gets)
307  *
308  * will read and return a line without echo back and line editing.
309  *
310  * You must require 'io/console' to use this method.
311  */
312 static VALUE
314 {
315  rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
316  return ttymode(io, rb_yield, set_rawmode, optp);
317 }
318 
319 /*
320  * call-seq:
321  * io.raw!(min: nil, time: nil)
322  *
323  * Enables raw mode.
324  *
325  * If the terminal mode needs to be back, use io.raw { ... }.
326  *
327  * You must require 'io/console' to use this method.
328  */
329 static VALUE
331 {
332  conmode t;
333  rb_io_t *fptr;
334  int fd;
335  rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
336 
337  GetOpenFile(io, fptr);
338  fd = GetReadFD(fptr);
339  if (!getattr(fd, &t)) rb_sys_fail(0);
340  set_rawmode(&t, optp);
341  if (!setattr(fd, &t)) rb_sys_fail(0);
342  return io;
343 }
344 
345 /*
346  * call-seq:
347  * io.cooked {|io| }
348  *
349  * Yields +self+ within cooked mode.
350  *
351  * STDIN.cooked(&:gets)
352  *
353  * will read and return a line with echo back and line editing.
354  *
355  * You must require 'io/console' to use this method.
356  */
357 static VALUE
359 {
360  return ttymode(io, rb_yield, set_cookedmode, NULL);
361 }
362 
363 /*
364  * call-seq:
365  * io.cooked!
366  *
367  * Enables cooked mode.
368  *
369  * If the terminal mode needs to be back, use io.cooked { ... }.
370  *
371  * You must require 'io/console' to use this method.
372  */
373 static VALUE
375 {
376  conmode t;
377  rb_io_t *fptr;
378  int fd;
379 
380  GetOpenFile(io, fptr);
381  fd = GetReadFD(fptr);
382  if (!getattr(fd, &t)) rb_sys_fail(0);
383  set_cookedmode(&t, NULL);
384  if (!setattr(fd, &t)) rb_sys_fail(0);
385  return io;
386 }
387 
388 static VALUE
390 {
391  return rb_funcallv(io, id_getc, 0, 0);
392 }
393 
394 /*
395  * call-seq:
396  * io.getch(min: nil, time: nil) -> char
397  *
398  * Reads and returns a character in raw mode.
399  *
400  * You must require 'io/console' to use this method.
401  */
402 static VALUE
404 {
405  rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
406  return ttymode(io, getc_call, set_rawmode, optp);
407 }
408 
409 /*
410  * call-seq:
411  * io.noecho {|io| }
412  *
413  * Yields +self+ with disabling echo back.
414  *
415  * STDIN.noecho(&:gets)
416  *
417  * will read and return a line without echo back.
418  *
419  * You must require 'io/console' to use this method.
420  */
421 static VALUE
423 {
424  return ttymode(io, rb_yield, set_noecho, NULL);
425 }
426 
427 /*
428  * call-seq:
429  * io.echo = flag
430  *
431  * Enables/disables echo back.
432  * On some platforms, all combinations of this flags and raw/cooked
433  * mode may not be valid.
434  *
435  * You must require 'io/console' to use this method.
436  */
437 static VALUE
439 {
440  conmode t;
441  rb_io_t *fptr;
442  int fd;
443 
444  GetOpenFile(io, fptr);
445  fd = GetReadFD(fptr);
446  if (!getattr(fd, &t)) rb_sys_fail(0);
447  if (RTEST(f))
448  set_echo(&t, NULL);
449  else
450  set_noecho(&t, NULL);
451  if (!setattr(fd, &t)) rb_sys_fail(0);
452  return io;
453 }
454 
455 /*
456  * call-seq:
457  * io.echo? -> true or false
458  *
459  * Returns +true+ if echo back is enabled.
460  *
461  * You must require 'io/console' to use this method.
462  */
463 static VALUE
465 {
466  conmode t;
467  rb_io_t *fptr;
468  int fd;
469 
470  GetOpenFile(io, fptr);
471  fd = GetReadFD(fptr);
472  if (!getattr(fd, &t)) rb_sys_fail(0);
473  return echo_p(&t) ? Qtrue : Qfalse;
474 }
475 
476 #if defined TIOCGWINSZ
477 typedef struct winsize rb_console_size_t;
478 #define getwinsize(fd, buf) (ioctl((fd), TIOCGWINSZ, (buf)) == 0)
479 #define setwinsize(fd, buf) (ioctl((fd), TIOCSWINSZ, (buf)) == 0)
480 #define winsize_row(buf) (buf)->ws_row
481 #define winsize_col(buf) (buf)->ws_col
482 #elif defined _WIN32
483 typedef CONSOLE_SCREEN_BUFFER_INFO rb_console_size_t;
484 #define getwinsize(fd, buf) ( \
485  GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), (buf)) || \
486  SET_LAST_ERROR)
487 #define winsize_row(buf) ((buf)->srWindow.Bottom - (buf)->srWindow.Top + 1)
488 #define winsize_col(buf) (buf)->dwSize.X
489 #endif
490 
491 #if defined TIOCGWINSZ || defined _WIN32
492 #define USE_CONSOLE_GETSIZE 1
493 #endif
494 
495 #ifdef USE_CONSOLE_GETSIZE
496 /*
497  * call-seq:
498  * io.winsize -> [rows, columns]
499  *
500  * Returns console size.
501  *
502  * You must require 'io/console' to use this method.
503  */
504 static VALUE
505 console_winsize(VALUE io)
506 {
507  rb_io_t *fptr;
508  int fd;
509  rb_console_size_t ws;
510 
511  GetOpenFile(io, fptr);
512  fd = GetWriteFD(fptr);
513  if (!getwinsize(fd, &ws)) rb_sys_fail(0);
514  return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
515 }
516 
517 /*
518  * call-seq:
519  * io.winsize = [rows, columns]
520  *
521  * Tries to set console size. The effect depends on the platform and
522  * the running environment.
523  *
524  * You must require 'io/console' to use this method.
525  */
526 static VALUE
527 console_set_winsize(VALUE io, VALUE size)
528 {
529  rb_io_t *fptr;
530  rb_console_size_t ws;
531 #if defined _WIN32
532  HANDLE wh;
533  int newrow, newcol;
534 #endif
535  VALUE row, col, xpixel, ypixel;
536  const VALUE *sz;
537  int fd;
538  long sizelen;
539 
540  GetOpenFile(io, fptr);
541  size = rb_Array(size);
542  if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
544  "wrong number of arguments (given %ld, expected 2 or 4)",
545  sizelen);
546  }
547  sz = RARRAY_CONST_PTR(size);
548  row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
549  if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
550  fd = GetWriteFD(fptr);
551 #if defined TIOCSWINSZ
552  ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
553 #define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
554  SET(row);
555  SET(col);
556  SET(xpixel);
557  SET(ypixel);
558 #undef SET
559  if (!setwinsize(fd, &ws)) rb_sys_fail(0);
560 #elif defined _WIN32
561  wh = (HANDLE)rb_w32_get_osfhandle(fd);
562 #define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
563  SET(row);
564  SET(col);
565 #undef SET
566  if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel);
567  if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel);
568  if (!GetConsoleScreenBufferInfo(wh, &ws)) {
569  rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
570  }
571  if ((ws.dwSize.X < newcol && (ws.dwSize.X = newcol, 1)) ||
572  (ws.dwSize.Y < newrow && (ws.dwSize.Y = newrow, 1))) {
573  if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) {
574  rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
575  }
576  }
577  ws.srWindow.Left = 0;
578  ws.srWindow.Top = 0;
579  ws.srWindow.Right = newcol;
580  ws.srWindow.Bottom = newrow;
581  if (!SetConsoleWindowInfo(wh, FALSE, &ws.srWindow)) {
582  rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
583  }
584 #endif
585  return io;
586 }
587 #endif
588 
589 /*
590  * call-seq:
591  * io.iflush
592  *
593  * Flushes input buffer in kernel.
594  *
595  * You must require 'io/console' to use this method.
596  */
597 static VALUE
599 {
600  rb_io_t *fptr;
601  int fd;
602 
603  GetOpenFile(io, fptr);
604  fd = GetReadFD(fptr);
605 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
606  if (tcflush(fd, TCIFLUSH)) rb_sys_fail(0);
607 #endif
608  (void)fd;
609  return io;
610 }
611 
612 /*
613  * call-seq:
614  * io.oflush
615  *
616  * Flushes output buffer in kernel.
617  *
618  * You must require 'io/console' to use this method.
619  */
620 static VALUE
622 {
623  rb_io_t *fptr;
624  int fd;
625 
626  GetOpenFile(io, fptr);
627  fd = GetWriteFD(fptr);
628 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
629  if (tcflush(fd, TCOFLUSH)) rb_sys_fail(0);
630 #endif
631  (void)fd;
632  return io;
633 }
634 
635 /*
636  * call-seq:
637  * io.ioflush
638  *
639  * Flushes input and output buffers in kernel.
640  *
641  * You must require 'io/console' to use this method.
642  */
643 static VALUE
645 {
646  rb_io_t *fptr;
647 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
648  int fd1, fd2;
649 #endif
650 
651  GetOpenFile(io, fptr);
652 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
653  fd1 = GetReadFD(fptr);
654  fd2 = GetWriteFD(fptr);
655  if (fd2 != -1 && fd1 != fd2) {
656  if (tcflush(fd1, TCIFLUSH)) rb_sys_fail(0);
657  if (tcflush(fd2, TCOFLUSH)) rb_sys_fail(0);
658  }
659  else {
660  if (tcflush(fd1, TCIOFLUSH)) rb_sys_fail(0);
661  }
662 #endif
663  return io;
664 }
665 
666 static VALUE
668 {
669  rb_io_t *fptr;
670  int fd;
671 
672  GetOpenFile(io, fptr);
673  fd = GetWriteFD(fptr);
674 #ifdef _WIN32
675  (void)fd;
676  MessageBeep(0);
677 #else
678  if (write(fd, "\a", 1) < 0)
679  rb_sys_fail(0);
680 #endif
681  return io;
682 }
683 
684 #if defined _WIN32
685 static VALUE
686 console_goto(VALUE io, VALUE x, VALUE y)
687 {
688  rb_io_t *fptr;
689  int fd;
690  COORD pos;
691 
692  GetOpenFile(io, fptr);
693  fd = GetWriteFD(fptr);
694  pos.X = NUM2UINT(x);
695  pos.Y = NUM2UINT(y);
696  if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
697  rb_syserr_fail(LAST_ERROR, 0);
698  }
699  return io;
700 }
701 
702 static VALUE
704 {
705  rb_io_t *fptr;
706  int fd;
707  rb_console_size_t ws;
708 
709  GetOpenFile(io, fptr);
710  fd = GetWriteFD(fptr);
711  if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
712  rb_syserr_fail(LAST_ERROR, 0);
713  }
714  return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.X), UINT2NUM(ws.dwCursorPosition.Y));
715 }
716 
717 static VALUE
719 {
720  cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary");
721  if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate");
722  return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
723 }
724 
725 #include "win32_vk.inc"
726 
727 static VALUE
729 {
730  int vk = -1;
731 
732  if (FIXNUM_P(k)) {
733  vk = NUM2UINT(k);
734  }
735  else {
736  const struct vktable *t;
737  const char *kn;
738  if (SYMBOL_P(k)) {
739  k = rb_sym2str(k);
740  kn = RSTRING_PTR(k);
741  }
742  else {
743  kn = StringValuePtr(k);
744  }
745  t = console_win32_vk(kn, RSTRING_LEN(k));
746  if (!t || (vk = (short)t->vk) == -1) {
747  rb_raise(rb_eArgError, "unknown virtual key code: % "PRIsVALUE, k);
748  }
749  }
750  return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse;
751 }
752 #else
753 # define console_goto rb_f_notimplement
754 # define console_cursor_pos rb_f_notimplement
755 # define console_cursor_set rb_f_notimplement
756 # define console_key_pressed_p rb_f_notimplement
757 #endif
758 
759 /*
760  * call-seq:
761  * IO.console -> #<File:/dev/tty>
762  * IO.console(sym, *args)
763  *
764  * Returns an File instance opened console.
765  *
766  * If +sym+ is given, it will be sent to the opened console with
767  * +args+ and the result will be returned instead of the console IO
768  * itself.
769  *
770  * You must require 'io/console' to use this method.
771  */
772 static VALUE
774 {
775  VALUE con = 0;
776  rb_io_t *fptr;
777  VALUE sym = 0;
778 
780  if (argc) {
781  Check_Type(sym = argv[0], T_SYMBOL);
782  }
783  if (klass == rb_cIO) klass = rb_cFile;
784  if (rb_const_defined(klass, id_console)) {
785  con = rb_const_get(klass, id_console);
786  if (!RB_TYPE_P(con, T_FILE) ||
787  (!(fptr = RFILE(con)->fptr) || GetReadFD(fptr) == -1)) {
788  rb_const_remove(klass, id_console);
789  con = 0;
790  }
791  }
792  if (sym) {
793  if (sym == ID2SYM(id_close) && argc == 1) {
794  if (con) {
795  rb_io_close(con);
796  rb_const_remove(klass, id_console);
797  con = 0;
798  }
799  return Qnil;
800  }
801  }
802  if (!con) {
803  VALUE args[2];
804 #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H
805 # define CONSOLE_DEVICE "/dev/tty"
806 #elif defined _WIN32
807 # define CONSOLE_DEVICE "con$"
808 # define CONSOLE_DEVICE_FOR_READING "conin$"
809 # define CONSOLE_DEVICE_FOR_WRITING "conout$"
810 #endif
811 #ifndef CONSOLE_DEVICE_FOR_READING
812 # define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE
813 #endif
814 #ifdef CONSOLE_DEVICE_FOR_WRITING
815  VALUE out;
816  rb_io_t *ofptr;
817 #endif
818  int fd;
819 
820 #ifdef CONSOLE_DEVICE_FOR_WRITING
821  fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
822  if (fd < 0) return Qnil;
823  rb_update_max_fd(fd);
824  args[1] = INT2FIX(O_WRONLY);
825  args[0] = INT2NUM(fd);
826  out = rb_class_new_instance(2, args, klass);
827 #endif
829  if (fd < 0) {
830 #ifdef CONSOLE_DEVICE_FOR_WRITING
831  rb_io_close(out);
832 #endif
833  return Qnil;
834  }
835  rb_update_max_fd(fd);
836  args[1] = INT2FIX(O_RDWR);
837  args[0] = INT2NUM(fd);
838  con = rb_class_new_instance(2, args, klass);
839  GetOpenFile(con, fptr);
840  fptr->pathv = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
841 #ifdef CONSOLE_DEVICE_FOR_WRITING
842  GetOpenFile(out, ofptr);
843  ofptr->pathv = fptr->pathv;
844  fptr->tied_io_for_writing = out;
845  ofptr->mode |= FMODE_SYNC;
846 #endif
847  fptr->mode |= FMODE_SYNC;
848  rb_const_set(klass, id_console, con);
849  }
850  if (sym) {
851  return rb_f_send(argc, argv, con);
852  }
853  return con;
854 }
855 
856 /*
857  * call-seq:
858  * io.getch(min: nil, time: nil) -> char
859  *
860  * See IO#getch.
861  */
862 static VALUE
864 {
865  return rb_funcallv(io, id_getc, argc, argv);
866 }
867 
868 #if ENABLE_IO_GETPASS
869 static VALUE
870 puts_call(VALUE io)
871 {
872  return rb_io_write(io, rb_default_rs);
873 }
874 
875 static VALUE
876 getpass_call(VALUE io)
877 {
878  return ttymode(io, rb_io_gets, set_noecho, NULL);
879 }
880 
881 static void
882 prompt(int argc, VALUE *argv, VALUE io)
883 {
884  if (argc > 0 && !NIL_P(argv[0])) {
885  VALUE str = argv[0];
886  StringValueCStr(str);
887  rb_check_safe_obj(str);
888  rb_io_write(io, str);
889  }
890 }
891 
892 static VALUE
893 str_chomp(VALUE str)
894 {
895  if (!NIL_P(str)) {
896  str = rb_funcallv(str, rb_intern("chomp!"), 0, 0);
897  }
898  return str;
899 }
900 
901 /*
902  * call-seq:
903  * io.getpass(prompt=nil) -> string
904  *
905  * Reads and returns a line without echo back.
906  * Prints +prompt+ unless it is +nil+.
907  *
908  * You must require 'io/console' to use this method.
909  */
910 static VALUE
911 console_getpass(int argc, VALUE *argv, VALUE io)
912 {
913  VALUE str, wio;
914 
915  rb_check_arity(argc, 0, 1);
916  wio = rb_io_get_write_io(io);
917  if (wio == io && io == rb_stdin) wio = rb_stderr;
918  prompt(argc, argv, wio);
919  str = rb_ensure(getpass_call, io, puts_call, wio);
920  return str_chomp(str);
921 }
922 
923 /*
924  * call-seq:
925  * io.getpass(prompt=nil) -> string
926  *
927  * See IO#getpass.
928  */
929 static VALUE
930 io_getpass(int argc, VALUE *argv, VALUE io)
931 {
932  VALUE str;
933 
934  rb_check_arity(argc, 0, 1);
935  prompt(argc, argv, io);
936  str = str_chomp(rb_funcallv(io, id_gets, 0, 0));
937  puts_call(io);
938  return str;
939 }
940 #endif
941 
942 /*
943  * IO console methods
944  */
945 void
947 {
948 #undef rb_intern
949  id_getc = rb_intern("getc");
950 #if ENABLE_IO_GETPASS
951  id_gets = rb_intern("gets");
952 #endif
953  id_console = rb_intern("console");
954  id_close = rb_intern("close");
955  id_min = rb_intern("min");
956  id_time = rb_intern("time");
957 #ifndef HAVE_RB_F_SEND
958  id___send__ = rb_intern("__send__");
959 #endif
960  InitVM(console);
961 }
962 
963 void
965 {
966  rb_define_method(rb_cIO, "raw", console_raw, -1);
968  rb_define_method(rb_cIO, "cooked", console_cooked, 0);
970  rb_define_method(rb_cIO, "getch", console_getch, -1);
972  rb_define_method(rb_cIO, "echo?", console_echo_p, 0);
973  rb_define_method(rb_cIO, "noecho", console_noecho, 0);
974  rb_define_method(rb_cIO, "winsize", console_winsize, 0);
975  rb_define_method(rb_cIO, "winsize=", console_set_winsize, 1);
976  rb_define_method(rb_cIO, "iflush", console_iflush, 0);
977  rb_define_method(rb_cIO, "oflush", console_oflush, 0);
978  rb_define_method(rb_cIO, "ioflush", console_ioflush, 0);
979  rb_define_method(rb_cIO, "beep", console_beep, 0);
980  rb_define_method(rb_cIO, "goto", console_goto, 2);
984 #if ENABLE_IO_GETPASS
985  rb_define_method(rb_cIO, "getpass", console_getpass, -1);
986 #endif
988  {
989  VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
990  rb_define_method(mReadable, "getch", io_getch, -1);
991 #if ENABLE_IO_GETPASS
992  rb_define_method(mReadable, "getpass", io_getpass, -1);
993 #endif
994  }
995 }
static int get_write_fd(const rb_io_t *fptr)
Definition: console.c:232
#define SET(a, b, c, d, k, s, Ti)
#define T_SYMBOL
Definition: ruby.h:508
ID rb_check_id(volatile VALUE *)
Returns ID for the given name if it is interned already, or 0.
Definition: symbol.c:923
#define GetReadFD(fptr)
Definition: console.c:229
#define RARRAY_LEN(a)
Definition: ruby.h:1026
#define FALSE
Definition: nkf.h:174
#define console_cursor_set
Definition: console.c:755
#define INT2NUM(x)
Definition: ruby.h:1538
void rb_update_max_fd(int fd)
Definition: io.c:189
void rb_syserr_fail(int e, const char *mesg)
Definition: error.c:2314
#define FD_PER_IO
Definition: console.c:242
#define NUM2INT(x)
Definition: ruby.h:684
static VALUE rb_f_send(int argc, VALUE *argv, VALUE recv)
Definition: console.c:91
void Init_console(void)
Definition: console.c:946
#define NUM2UINT(x)
Definition: ruby.h:685
static rawmode_arg_t * rawmode_opt(int argc, VALUE *argv, rawmode_arg_t *opts)
Definition: console.c:116
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
static void set_echo(conmode *t, void *arg)
Definition: console.c:196
static ID id_close
Definition: console.c:82
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Definition: io.c:255
#define InitVM(ext)
Definition: ruby.h:2143
static void set_cookedmode(conmode *t, void *arg)
Definition: console.c:169
#define Qtrue
Definition: ruby.h:437
Definition: io.h:62
static VALUE ttymode(VALUE io, VALUE(*func)(VALUE), void(*setter)(conmode *, void *), void *arg)
Definition: console.c:245
static VALUE console_getch(int argc, VALUE *argv, VALUE io)
Definition: console.c:403
SOCKET rb_w32_get_osfhandle(int)
Definition: win32.c:1064
RUBY_EXTERN VALUE rb_stdin
Definition: ruby.h:1950
#define rb_check_arity
Definition: intern.h:303
static ID id_min
Definition: console.c:82
VALUE rb_cFile
Definition: file.c:129
SSL_METHOD *(* func)(void)
Definition: ossl_ssl.c:54
#define GetWriteFD(fptr)
Definition: console.c:240
VALUE rb_protect(VALUE(*proc)(VALUE), VALUE data, int *state)
Definition: eval.c:891
static int echo_p(conmode *t)
Definition: console.c:208
#define Check_Type(v, t)
Definition: ruby.h:562
static ID id_getc
Definition: console.c:82
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2207
VALUE rb_convert_type(VALUE, int, const char *, const char *)
Definition: object.c:2630
int rb_const_defined(VALUE, ID)
Definition: variable.c:2580
static ID id_console
Definition: console.c:82
#define T_ARRAY
Definition: ruby.h:498
VALUE rb_io_write(VALUE, VALUE)
Definition: io.c:1508
#define RFILE(obj)
Definition: ruby.h:1213
#define FIXNUM_P(f)
Definition: ruby.h:365
#define GetOpenFile(obj, fp)
Definition: io.h:120
void InitVM_console(void)
Definition: console.c:964
#define sym(x)
Definition: date_core.c:3721
int mode
Definition: io.h:65
#define RB_TYPE_P(obj, type)
Definition: ruby.h:527
static ID id_time
Definition: console.c:82
VALUE rb_Array(VALUE)
Definition: object.c:3126
static VALUE console_ioflush(VALUE io)
Definition: console.c:644
VALUE rb_io_get_write_io(VALUE io)
Definition: io.c:668
IUnknown DWORD
Definition: win32ole.c:32
#define CONSOLE_DEVICE_FOR_READING
#define UINT2NUM(x)
Definition: ruby.h:1539
#define console_key_pressed_p
Definition: console.c:756
#define NIL_P(v)
Definition: ruby.h:451
int fd
Definition: io.h:64
static VALUE console_echo_p(VALUE io)
Definition: console.c:464
int argc
Definition: ruby.c:183
#define Qfalse
Definition: ruby.h:436
#define rb_str_new2
Definition: intern.h:857
VALUE rb_class_new_instance(int, const VALUE *, VALUE)
Definition: object.c:1891
#define console_goto
Definition: console.c:753
static VALUE console_set_echo(VALUE io, VALUE f)
Definition: console.c:438
static VALUE console_set_cooked(VALUE io)
Definition: console.c:374
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2335
RUBY_EXTERN VALUE rb_cIO
Definition: ruby.h:1892
#define RSTRING_LEN(str)
Definition: ruby.h:978
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1020
#define RARRAY_CONST_PTR(a)
Definition: ruby.h:1028
int errno
VALUE rb_io_gets(VALUE)
Definition: io.c:3261
static VALUE getc_call(VALUE io)
Definition: console.c:389
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1919
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:623
#define PRIsVALUE
Definition: ruby.h:135
unsigned long ID
Definition: ruby.h:86
VALUE tied_io_for_writing
Definition: io.h:73
#define Qnil
Definition: ruby.h:438
void rb_const_set(VALUE, ID, VALUE)
Definition: variable.c:2616
static int set_ttymode(int fd, conmode *t, void(*setter)(conmode *, void *), void *arg)
Definition: console.c:220
VALUE rb_io_close(VALUE)
Definition: io.c:4469
unsigned long VALUE
Definition: ruby.h:85
static VALUE result
Definition: nkf.c:40
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
Definition: eval.c:923
static VALUE console_cooked(VALUE io)
Definition: console.c:358
static VALUE console_noecho(VALUE io)
Definition: console.c:422
void rb_sys_fail(const char *mesg)
Definition: error.c:2326
void rb_jump_tag(int tag)
Definition: eval.c:788
static VALUE console_raw(int argc, VALUE *argv, VALUE io)
Definition: console.c:313
#define rb_funcallv
Definition: console.c:21
VALUE rb_define_module_under(VALUE outer, const char *name)
Definition: class.c:790
#define StringValueCStr(v)
Definition: ruby.h:571
RUBY_EXTERN VALUE rb_default_rs
Definition: intern.h:537
#define RSTRING_PTR(str)
Definition: ruby.h:982
RUBY_EXTERN VALUE rb_stderr
Definition: ruby.h:1950
int size
Definition: encoding.c:57
#define f
#define INT2FIX(i)
Definition: ruby.h:232
#define UNLIMITED_ARGUMENTS
Definition: intern.h:44
#define RARRAY_AREF(a, i)
Definition: ruby.h:1040
VALUE rb_hash_aref(VALUE hash, VALUE key)
Definition: hash.c:845
#define rb_funcall3
Definition: ruby.h:1771
VALUE pathv
Definition: io.h:68
#define RTEST(v)
Definition: ruby.h:450
void rb_check_safe_obj(VALUE)
Definition: safe.c:117
#define T_FILE
Definition: ruby.h:502
static VALUE console_iflush(VALUE io)
Definition: console.c:598
#define console_cursor_pos
Definition: console.c:754
static VALUE console_set_raw(int argc, VALUE *argv, VALUE io)
Definition: console.c:330
static VALUE console_dev(int argc, VALUE *argv, VALUE klass)
Definition: console.c:773
#define ID2SYM(x)
Definition: ruby.h:383
static VALUE console_beep(VALUE io)
Definition: console.c:667
#define FMODE_SYNC
Definition: io.h:106
#define StringValuePtr(v)
Definition: ruby.h:570
VALUE rb_obj_freeze(VALUE)
Definition: object.c:1111
#define rb_intern(str)
static VALUE io_getch(int argc, VALUE *argv, VALUE io)
Definition: console.c:863
#define SYMBOL_P(x)
Definition: ruby.h:382
VALUE rb_const_remove(VALUE, ID)
Definition: variable.c:2387
#define NULL
Definition: _sdbm.c:102
static VALUE console_oflush(VALUE io)
Definition: console.c:621
static void set_rawmode(conmode *t, void *arg)
Definition: console.c:142
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
VALUE rb_eArgError
Definition: error.c:763
static void set_noecho(conmode *t, void *arg)
Definition: console.c:184
static ID id___send__
Definition: console.c:88
char ** argv
Definition: ruby.c:184
#define rb_sym2str(sym)
Definition: console.c:107