io-console 0.6.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,10 @@
2
2
  /*
3
3
  * console IO module
4
4
  */
5
+
6
+ static const char *const
7
+ IO_CONSOLE_VERSION = "0.8.1";
8
+
5
9
  #include "ruby.h"
6
10
  #include "ruby/io.h"
7
11
  #include "ruby/thread.h"
@@ -75,9 +79,14 @@ getattr(int fd, conmode *t)
75
79
  #define SET_LAST_ERROR (0)
76
80
  #endif
77
81
 
78
- static ID id_getc, id_console, id_close;
79
- #if ENABLE_IO_GETPASS
80
- static ID id_gets, id_chomp_bang;
82
+ #define CSI "\x1b\x5b"
83
+
84
+ static ID id_getc, id_close;
85
+ static ID id_gets, id_flush, id_chomp_bang;
86
+
87
+ #ifndef HAVE_RB_INTERNED_STR_CSTR
88
+ # define rb_str_to_interned_str(str) rb_str_freeze(str)
89
+ # define rb_interned_str_cstr(str) rb_str_freeze(rb_usascii_str_new_cstr(str))
81
90
  #endif
82
91
 
83
92
  #if defined HAVE_RUBY_FIBER_SCHEDULER_H
@@ -87,7 +96,48 @@ extern VALUE rb_scheduler_timeout(struct timeval *timeout);
87
96
  # define rb_fiber_scheduler_make_timeout rb_scheduler_timeout
88
97
  #endif
89
98
 
90
- #define sys_fail_fptr(fptr) rb_sys_fail_str((fptr)->pathv)
99
+ #ifndef HAVE_RB_IO_DESCRIPTOR
100
+ static int
101
+ io_descriptor_fallback(VALUE io)
102
+ {
103
+ rb_io_t *fptr;
104
+ GetOpenFile(io, fptr);
105
+ return fptr->fd;
106
+ }
107
+ #define rb_io_descriptor io_descriptor_fallback
108
+ #endif
109
+
110
+ #ifndef HAVE_RB_IO_PATH
111
+ static VALUE
112
+ io_path_fallback(VALUE io)
113
+ {
114
+ rb_io_t *fptr;
115
+ GetOpenFile(io, fptr);
116
+ return fptr->pathv;
117
+ }
118
+ #define rb_io_path io_path_fallback
119
+ #endif
120
+
121
+ #ifndef HAVE_RB_IO_GET_WRITE_IO
122
+ static VALUE
123
+ io_get_write_io_fallback(VALUE io)
124
+ {
125
+ rb_io_t *fptr;
126
+ GetOpenFile(io, fptr);
127
+ VALUE wio = fptr->tied_io_for_writing;
128
+ return wio ? wio : io;
129
+ }
130
+ #define rb_io_get_write_io io_get_write_io_fallback
131
+ #endif
132
+
133
+ #ifndef DHAVE_RB_SYSERR_FAIL_STR
134
+ # define rb_syserr_fail_str(e, mesg) rb_exc_raise(rb_syserr_new_str(e, mesg))
135
+ #endif
136
+
137
+ #define sys_fail(io) do { \
138
+ int err = errno; \
139
+ rb_syserr_fail_str(err, rb_io_path(io)); \
140
+ } while (0)
91
141
 
92
142
  #ifndef HAVE_RB_F_SEND
93
143
  #ifndef RB_PASS_CALLED_KEYWORDS
@@ -293,33 +343,21 @@ set_ttymode(int fd, conmode *t, void (*setter)(conmode *, void *), void *arg)
293
343
  return setattr(fd, &r);
294
344
  }
295
345
 
296
- #define GetReadFD(fptr) ((fptr)->fd)
297
-
298
- static inline int
299
- get_write_fd(const rb_io_t *fptr)
300
- {
301
- VALUE wio = fptr->tied_io_for_writing;
302
- rb_io_t *ofptr;
303
- if (!wio) return fptr->fd;
304
- GetOpenFile(wio, ofptr);
305
- return ofptr->fd;
306
- }
307
- #define GetWriteFD(fptr) get_write_fd(fptr)
346
+ #define GetReadFD(io) rb_io_descriptor(io)
347
+ #define GetWriteFD(io) rb_io_descriptor(rb_io_get_write_io(io))
308
348
 
309
349
  #define FD_PER_IO 2
310
350
 
311
351
  static VALUE
312
352
  ttymode(VALUE io, VALUE (*func)(VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg)
313
353
  {
314
- rb_io_t *fptr;
315
354
  int status = -1;
316
355
  int error = 0;
317
356
  int fd[FD_PER_IO];
318
357
  conmode t[FD_PER_IO];
319
358
  VALUE result = Qnil;
320
359
 
321
- GetOpenFile(io, fptr);
322
- fd[0] = GetReadFD(fptr);
360
+ fd[0] = GetReadFD(io);
323
361
  if (fd[0] != -1) {
324
362
  if (set_ttymode(fd[0], t+0, setter, arg)) {
325
363
  status = 0;
@@ -329,7 +367,7 @@ ttymode(VALUE io, VALUE (*func)(VALUE), VALUE farg, void (*setter)(conmode *, vo
329
367
  fd[0] = -1;
330
368
  }
331
369
  }
332
- fd[1] = GetWriteFD(fptr);
370
+ fd[1] = GetWriteFD(io);
333
371
  if (fd[1] != -1 && fd[1] != fd[0]) {
334
372
  if (set_ttymode(fd[1], t+1, setter, arg)) {
335
373
  status = 0;
@@ -342,14 +380,13 @@ ttymode(VALUE io, VALUE (*func)(VALUE), VALUE farg, void (*setter)(conmode *, vo
342
380
  if (status == 0) {
343
381
  result = rb_protect(func, farg, &status);
344
382
  }
345
- GetOpenFile(io, fptr);
346
- if (fd[0] != -1 && fd[0] == GetReadFD(fptr)) {
383
+ if (fd[0] != -1 && fd[0] == GetReadFD(io)) {
347
384
  if (!setattr(fd[0], t+0)) {
348
385
  error = errno;
349
386
  status = -1;
350
387
  }
351
388
  }
352
- if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(fptr)) {
389
+ if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(io)) {
353
390
  if (!setattr(fd[1], t+1)) {
354
391
  error = errno;
355
392
  status = -1;
@@ -435,15 +472,11 @@ static VALUE
435
472
  console_set_raw(int argc, VALUE *argv, VALUE io)
436
473
  {
437
474
  conmode t;
438
- rb_io_t *fptr;
439
- int fd;
440
475
  rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
441
-
442
- GetOpenFile(io, fptr);
443
- fd = GetReadFD(fptr);
444
- if (!getattr(fd, &t)) sys_fail_fptr(fptr);
476
+ int fd = GetReadFD(io);
477
+ if (!getattr(fd, &t)) sys_fail(io);
445
478
  set_rawmode(&t, optp);
446
- if (!setattr(fd, &t)) sys_fail_fptr(fptr);
479
+ if (!setattr(fd, &t)) sys_fail(io);
447
480
  return io;
448
481
  }
449
482
 
@@ -479,14 +512,10 @@ static VALUE
479
512
  console_set_cooked(VALUE io)
480
513
  {
481
514
  conmode t;
482
- rb_io_t *fptr;
483
- int fd;
484
-
485
- GetOpenFile(io, fptr);
486
- fd = GetReadFD(fptr);
487
- if (!getattr(fd, &t)) sys_fail_fptr(fptr);
515
+ int fd = GetReadFD(io);
516
+ if (!getattr(fd, &t)) sys_fail(io);
488
517
  set_cookedmode(&t, NULL);
489
- if (!setattr(fd, &t)) sys_fail_fptr(fptr);
518
+ if (!setattr(fd, &t)) sys_fail(io);
490
519
  return io;
491
520
  }
492
521
 
@@ -638,17 +667,17 @@ static VALUE
638
667
  console_set_echo(VALUE io, VALUE f)
639
668
  {
640
669
  conmode t;
641
- rb_io_t *fptr;
642
- int fd;
670
+ int fd = GetReadFD(io);
671
+
672
+ if (!getattr(fd, &t)) sys_fail(io);
643
673
 
644
- GetOpenFile(io, fptr);
645
- fd = GetReadFD(fptr);
646
- if (!getattr(fd, &t)) sys_fail_fptr(fptr);
647
674
  if (RTEST(f))
648
- set_echo(&t, NULL);
675
+ set_echo(&t, NULL);
649
676
  else
650
- set_noecho(&t, NULL);
651
- if (!setattr(fd, &t)) sys_fail_fptr(fptr);
677
+ set_noecho(&t, NULL);
678
+
679
+ if (!setattr(fd, &t)) sys_fail(io);
680
+
652
681
  return io;
653
682
  }
654
683
 
@@ -664,12 +693,9 @@ static VALUE
664
693
  console_echo_p(VALUE io)
665
694
  {
666
695
  conmode t;
667
- rb_io_t *fptr;
668
- int fd;
696
+ int fd = GetReadFD(io);
669
697
 
670
- GetOpenFile(io, fptr);
671
- fd = GetReadFD(fptr);
672
- if (!getattr(fd, &t)) sys_fail_fptr(fptr);
698
+ if (!getattr(fd, &t)) sys_fail(io);
673
699
  return echo_p(&t) ? Qtrue : Qfalse;
674
700
  }
675
701
 
@@ -748,12 +774,9 @@ static VALUE
748
774
  console_conmode_get(VALUE io)
749
775
  {
750
776
  conmode t;
751
- rb_io_t *fptr;
752
- int fd;
777
+ int fd = GetReadFD(io);
753
778
 
754
- GetOpenFile(io, fptr);
755
- fd = GetReadFD(fptr);
756
- if (!getattr(fd, &t)) sys_fail_fptr(fptr);
779
+ if (!getattr(fd, &t)) sys_fail(io);
757
780
 
758
781
  return conmode_new(cConmode, &t);
759
782
  }
@@ -770,14 +793,12 @@ static VALUE
770
793
  console_conmode_set(VALUE io, VALUE mode)
771
794
  {
772
795
  conmode *t, r;
773
- rb_io_t *fptr;
774
- int fd;
796
+ int fd = GetReadFD(io);
775
797
 
776
798
  TypedData_Get_Struct(mode, conmode, &conmode_type, t);
777
799
  r = *t;
778
- GetOpenFile(io, fptr);
779
- fd = GetReadFD(fptr);
780
- if (!setattr(fd, &r)) sys_fail_fptr(fptr);
800
+
801
+ if (!setattr(fd, &r)) sys_fail(io);
781
802
 
782
803
  return mode;
783
804
  }
@@ -813,13 +834,9 @@ typedef CONSOLE_SCREEN_BUFFER_INFO rb_console_size_t;
813
834
  static VALUE
814
835
  console_winsize(VALUE io)
815
836
  {
816
- rb_io_t *fptr;
817
- int fd;
818
837
  rb_console_size_t ws;
819
-
820
- GetOpenFile(io, fptr);
821
- fd = GetWriteFD(fptr);
822
- if (!getwinsize(fd, &ws)) sys_fail_fptr(fptr);
838
+ int fd = GetWriteFD(io);
839
+ if (!getwinsize(fd, &ws)) sys_fail(io);
823
840
  return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
824
841
  }
825
842
 
@@ -835,7 +852,6 @@ console_winsize(VALUE io)
835
852
  static VALUE
836
853
  console_set_winsize(VALUE io, VALUE size)
837
854
  {
838
- rb_io_t *fptr;
839
855
  rb_console_size_t ws;
840
856
  #if defined _WIN32
841
857
  HANDLE wh;
@@ -844,20 +860,17 @@ console_set_winsize(VALUE io, VALUE size)
844
860
  #endif
845
861
  VALUE row, col, xpixel, ypixel;
846
862
  const VALUE *sz;
847
- int fd;
848
863
  long sizelen;
864
+ int fd;
849
865
 
850
- GetOpenFile(io, fptr);
851
866
  size = rb_Array(size);
852
867
  if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
853
- rb_raise(rb_eArgError,
854
- "wrong number of arguments (given %ld, expected 2 or 4)",
855
- sizelen);
868
+ rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen);
856
869
  }
857
870
  sz = RARRAY_CONST_PTR(size);
858
871
  row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
859
872
  if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
860
- fd = GetWriteFD(fptr);
873
+ fd = GetWriteFD(io);
861
874
  #if defined TIOCSWINSZ
862
875
  ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
863
876
  #define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
@@ -866,7 +879,7 @@ console_set_winsize(VALUE io, VALUE size)
866
879
  SET(xpixel);
867
880
  SET(ypixel);
868
881
  #undef SET
869
- if (!setwinsize(fd, &ws)) sys_fail_fptr(fptr);
882
+ if (!setwinsize(fd, &ws)) sys_fail(io);
870
883
  #elif defined _WIN32
871
884
  wh = (HANDLE)rb_w32_get_osfhandle(fd);
872
885
  #define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
@@ -901,15 +914,23 @@ console_set_winsize(VALUE io, VALUE size)
901
914
  #endif
902
915
 
903
916
  #ifdef _WIN32
917
+ /*
918
+ * call-seq:
919
+ * io.check_winsize_changed { ... } -> io
920
+ *
921
+ * Yields while console input events are queued.
922
+ *
923
+ * This method is Windows only.
924
+ *
925
+ * You must require 'io/console' to use this method.
926
+ */
904
927
  static VALUE
905
928
  console_check_winsize_changed(VALUE io)
906
929
  {
907
- rb_io_t *fptr;
908
930
  HANDLE h;
909
931
  DWORD num;
910
932
 
911
- GetOpenFile(io, fptr);
912
- h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(fptr));
933
+ h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
913
934
  while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) {
914
935
  INPUT_RECORD rec;
915
936
  if (ReadConsoleInput(h, &rec, 1, &num)) {
@@ -935,15 +956,11 @@ console_check_winsize_changed(VALUE io)
935
956
  static VALUE
936
957
  console_iflush(VALUE io)
937
958
  {
938
- rb_io_t *fptr;
939
- int fd;
940
-
941
- GetOpenFile(io, fptr);
942
- fd = GetReadFD(fptr);
943
959
  #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
944
- if (tcflush(fd, TCIFLUSH)) sys_fail_fptr(fptr);
960
+ int fd = GetReadFD(io);
961
+ if (tcflush(fd, TCIFLUSH)) sys_fail(io);
945
962
  #endif
946
- (void)fd;
963
+
947
964
  return io;
948
965
  }
949
966
 
@@ -958,13 +975,9 @@ console_iflush(VALUE io)
958
975
  static VALUE
959
976
  console_oflush(VALUE io)
960
977
  {
961
- rb_io_t *fptr;
962
- int fd;
963
-
964
- GetOpenFile(io, fptr);
965
- fd = GetWriteFD(fptr);
978
+ int fd = GetWriteFD(io);
966
979
  #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
967
- if (tcflush(fd, TCOFLUSH)) sys_fail_fptr(fptr);
980
+ if (tcflush(fd, TCOFLUSH)) sys_fail(io);
968
981
  #endif
969
982
  (void)fd;
970
983
  return io;
@@ -981,40 +994,38 @@ console_oflush(VALUE io)
981
994
  static VALUE
982
995
  console_ioflush(VALUE io)
983
996
  {
984
- rb_io_t *fptr;
985
997
  #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
986
- int fd1, fd2;
987
- #endif
998
+ int fd1 = GetReadFD(io);
999
+ int fd2 = GetWriteFD(io);
988
1000
 
989
- GetOpenFile(io, fptr);
990
- #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
991
- fd1 = GetReadFD(fptr);
992
- fd2 = GetWriteFD(fptr);
993
1001
  if (fd2 != -1 && fd1 != fd2) {
994
- if (tcflush(fd1, TCIFLUSH)) sys_fail_fptr(fptr);
995
- if (tcflush(fd2, TCOFLUSH)) sys_fail_fptr(fptr);
1002
+ if (tcflush(fd1, TCIFLUSH)) sys_fail(io);
1003
+ if (tcflush(fd2, TCOFLUSH)) sys_fail(io);
996
1004
  }
997
1005
  else {
998
- if (tcflush(fd1, TCIOFLUSH)) sys_fail_fptr(fptr);
1006
+ if (tcflush(fd1, TCIOFLUSH)) sys_fail(io);
999
1007
  }
1000
1008
  #endif
1009
+
1001
1010
  return io;
1002
1011
  }
1003
1012
 
1013
+ /*
1014
+ * call-seq:
1015
+ * io.beep
1016
+ *
1017
+ * Beeps on the output console.
1018
+ *
1019
+ * You must require 'io/console' to use this method.
1020
+ */
1004
1021
  static VALUE
1005
1022
  console_beep(VALUE io)
1006
1023
  {
1007
- rb_io_t *fptr;
1008
- int fd;
1009
-
1010
- GetOpenFile(io, fptr);
1011
- fd = GetWriteFD(fptr);
1012
1024
  #ifdef _WIN32
1013
- (void)fd;
1014
1025
  MessageBeep(0);
1015
1026
  #else
1016
- if (write(fd, "\a", 1) < 0)
1017
- sys_fail_fptr(fptr);
1027
+ int fd = GetWriteFD(io);
1028
+ if (write(fd, "\a", 1) < 0) sys_fail(io);
1018
1029
  #endif
1019
1030
  return io;
1020
1031
  }
@@ -1035,79 +1046,6 @@ mode_in_range(VALUE val, int high, const char *modename)
1035
1046
  }
1036
1047
 
1037
1048
  #if defined _WIN32
1038
- static VALUE
1039
- console_goto(VALUE io, VALUE y, VALUE x)
1040
- {
1041
- rb_io_t *fptr;
1042
- int fd;
1043
- COORD pos;
1044
-
1045
- GetOpenFile(io, fptr);
1046
- fd = GetWriteFD(fptr);
1047
- pos.X = NUM2UINT(x);
1048
- pos.Y = NUM2UINT(y);
1049
- if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
1050
- rb_syserr_fail(LAST_ERROR, 0);
1051
- }
1052
- return io;
1053
- }
1054
-
1055
- static VALUE
1056
- console_cursor_pos(VALUE io)
1057
- {
1058
- rb_io_t *fptr;
1059
- int fd;
1060
- rb_console_size_t ws;
1061
-
1062
- GetOpenFile(io, fptr);
1063
- fd = GetWriteFD(fptr);
1064
- if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
1065
- rb_syserr_fail(LAST_ERROR, 0);
1066
- }
1067
- return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
1068
- }
1069
-
1070
- static VALUE
1071
- console_move(VALUE io, int y, int x)
1072
- {
1073
- rb_io_t *fptr;
1074
- HANDLE h;
1075
- rb_console_size_t ws;
1076
- COORD *pos = &ws.dwCursorPosition;
1077
-
1078
- GetOpenFile(io, fptr);
1079
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1080
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
1081
- rb_syserr_fail(LAST_ERROR, 0);
1082
- }
1083
- pos->X += x;
1084
- pos->Y += y;
1085
- if (!SetConsoleCursorPosition(h, *pos)) {
1086
- rb_syserr_fail(LAST_ERROR, 0);
1087
- }
1088
- return io;
1089
- }
1090
-
1091
- static VALUE
1092
- console_goto_column(VALUE io, VALUE val)
1093
- {
1094
- rb_io_t *fptr;
1095
- HANDLE h;
1096
- rb_console_size_t ws;
1097
- COORD *pos = &ws.dwCursorPosition;
1098
-
1099
- GetOpenFile(io, fptr);
1100
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1101
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
1102
- rb_syserr_fail(LAST_ERROR, 0);
1103
- }
1104
- pos->X = NUM2INT(val);
1105
- if (!SetConsoleCursorPosition(h, *pos)) {
1106
- rb_syserr_fail(LAST_ERROR, 0);
1107
- }
1108
- return io;
1109
- }
1110
-
1111
1049
  static void
1112
1050
  constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
1113
1051
  {
@@ -1117,87 +1055,13 @@ constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
1117
1055
  FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
1118
1056
  }
1119
1057
 
1120
- static VALUE
1121
- console_erase_line(VALUE io, VALUE val)
1122
- {
1123
- rb_io_t *fptr;
1124
- HANDLE h;
1125
- rb_console_size_t ws;
1126
- COORD *pos = &ws.dwCursorPosition;
1127
- DWORD w;
1128
- int mode = mode_in_range(val, 2, "line erase");
1129
-
1130
- GetOpenFile(io, fptr);
1131
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1132
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
1133
- rb_syserr_fail(LAST_ERROR, 0);
1134
- }
1135
- w = winsize_col(&ws);
1136
- switch (mode) {
1137
- case 0: /* after cursor */
1138
- w -= pos->X;
1139
- break;
1140
- case 1: /* before *and* cursor */
1141
- w = pos->X + 1;
1142
- pos->X = 0;
1143
- break;
1144
- case 2: /* entire line */
1145
- pos->X = 0;
1146
- break;
1147
- }
1148
- constat_clear(h, ws.wAttributes, w, *pos);
1149
- return io;
1150
- }
1151
-
1152
- static VALUE
1153
- console_erase_screen(VALUE io, VALUE val)
1154
- {
1155
- rb_io_t *fptr;
1156
- HANDLE h;
1157
- rb_console_size_t ws;
1158
- COORD *pos = &ws.dwCursorPosition;
1159
- DWORD w;
1160
- int mode = mode_in_range(val, 3, "screen erase");
1161
-
1162
- GetOpenFile(io, fptr);
1163
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1164
- if (!GetConsoleScreenBufferInfo(h, &ws)) {
1165
- rb_syserr_fail(LAST_ERROR, 0);
1166
- }
1167
- w = winsize_col(&ws);
1168
- switch (mode) {
1169
- case 0: /* erase after cursor */
1170
- w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
1171
- break;
1172
- case 1: /* erase before *and* cursor */
1173
- w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
1174
- pos->X = 0;
1175
- pos->Y = ws.srWindow.Top;
1176
- break;
1177
- case 2: /* erase entire screen */
1178
- w = (w * winsize_row(&ws));
1179
- pos->X = 0;
1180
- pos->Y = ws.srWindow.Top;
1181
- break;
1182
- case 3: /* erase entire screen */
1183
- w = (w * ws.dwSize.Y);
1184
- pos->X = 0;
1185
- pos->Y = 0;
1186
- break;
1187
- }
1188
- constat_clear(h, ws.wAttributes, w, *pos);
1189
- return io;
1190
- }
1191
-
1192
1058
  static VALUE
1193
1059
  console_scroll(VALUE io, int line)
1194
1060
  {
1195
- rb_io_t *fptr;
1196
1061
  HANDLE h;
1197
1062
  rb_console_size_t ws;
1198
1063
 
1199
- GetOpenFile(io, fptr);
1200
- h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1064
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
1201
1065
  if (!GetConsoleScreenBufferInfo(h, &ws)) {
1202
1066
  rb_syserr_fail(LAST_ERROR, 0);
1203
1067
  }
@@ -1219,8 +1083,22 @@ console_scroll(VALUE io, int line)
1219
1083
  return io;
1220
1084
  }
1221
1085
 
1086
+ #define GPERF_DOWNCASE 1
1087
+ #define GPERF_CASE_STRCMP 1
1088
+ #define gperf_case_strcmp STRCASECMP
1222
1089
  #include "win32_vk.inc"
1223
1090
 
1091
+ /*
1092
+ * call-seq:
1093
+ * io.pressed?(key) -> bool
1094
+ *
1095
+ * Returns +true+ if +key+ is pressed. +key+ may be a virtual key
1096
+ * code or its name (String or Symbol) with out "VK_" prefix.
1097
+ *
1098
+ * This method is Windows only.
1099
+ *
1100
+ * You must require 'io/console' to use this method.
1101
+ */
1224
1102
  static VALUE
1225
1103
  console_key_pressed_p(VALUE io, VALUE k)
1226
1104
  {
@@ -1256,23 +1134,11 @@ static int
1256
1134
  direct_query(VALUE io, const struct query_args *query)
1257
1135
  {
1258
1136
  if (RB_TYPE_P(io, T_FILE)) {
1259
- rb_io_t *fptr;
1260
- VALUE wio;
1261
- GetOpenFile(io, fptr);
1262
- wio = fptr->tied_io_for_writing;
1263
- if (wio) {
1264
- VALUE s = rb_str_new_cstr(query->qstr);
1265
- rb_io_write(wio, s);
1266
- rb_io_flush(wio);
1267
- return 1;
1268
- }
1269
- if (write(fptr->fd, query->qstr, strlen(query->qstr)) != -1) {
1270
- return 1;
1271
- }
1272
- if (fptr->fd == 0 &&
1273
- write(1, query->qstr, strlen(query->qstr)) != -1) {
1274
- return 1;
1275
- }
1137
+ VALUE wio = rb_io_get_write_io(io);
1138
+ VALUE s = rb_str_new_cstr(query->qstr);
1139
+ rb_io_write(wio, s);
1140
+ rb_io_flush(wio);
1141
+ return 1;
1276
1142
  }
1277
1143
  return 0;
1278
1144
  }
@@ -1323,9 +1189,41 @@ console_vt_response(int argc, VALUE *argv, VALUE io, const struct query_args *qa
1323
1189
  }
1324
1190
 
1325
1191
  static VALUE
1326
- console_cursor_pos(VALUE io)
1192
+ console_scroll(VALUE io, int line)
1327
1193
  {
1328
- static const struct query_args query = {"\033[6n", 0};
1194
+ if (line) {
1195
+ VALUE s = rb_sprintf(CSI "%d%c", line < 0 ? -line : line,
1196
+ line < 0 ? 'T' : 'S');
1197
+ rb_io_write(io, s);
1198
+ }
1199
+ return io;
1200
+ }
1201
+
1202
+ # define console_key_pressed_p rb_f_notimplement
1203
+ #endif
1204
+
1205
+ /*
1206
+ * call-seq:
1207
+ * io.cursor -> [row, column]
1208
+ *
1209
+ * Returns the current cursor position as a two-element array of integers (row, column)
1210
+ *
1211
+ * io.cursor # => [3, 5]
1212
+ *
1213
+ * You must require 'io/console' to use this method.
1214
+ */
1215
+ static VALUE
1216
+ console_cursor_pos(VALUE io)
1217
+ {
1218
+ #ifdef _WIN32
1219
+ rb_console_size_t ws;
1220
+ int fd = GetWriteFD(io);
1221
+ if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
1222
+ rb_syserr_fail(LAST_ERROR, 0);
1223
+ }
1224
+ return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
1225
+ #else
1226
+ static const struct query_args query = {"\033[6n", 0};
1329
1227
  VALUE resp = console_vt_response(0, 0, io, &query);
1330
1228
  VALUE row, column, term;
1331
1229
  unsigned int r, c;
@@ -1341,64 +1239,205 @@ console_cursor_pos(VALUE io)
1341
1239
  RARRAY_ASET(resp, 0, INT2NUM(r));
1342
1240
  RARRAY_ASET(resp, 1, INT2NUM(c));
1343
1241
  return resp;
1242
+ #endif
1344
1243
  }
1345
1244
 
1245
+ /*
1246
+ * call-seq:
1247
+ * io.goto(line, column) -> io
1248
+ *
1249
+ * Set the cursor position at +line+ and +column+.
1250
+ *
1251
+ * You must require 'io/console' to use this method.
1252
+ */
1346
1253
  static VALUE
1347
1254
  console_goto(VALUE io, VALUE y, VALUE x)
1348
1255
  {
1349
- rb_io_write(io, rb_sprintf("\x1b[%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
1256
+ #ifdef _WIN32
1257
+ COORD pos;
1258
+ int fd = GetWriteFD(io);
1259
+ pos.X = NUM2UINT(x);
1260
+ pos.Y = NUM2UINT(y);
1261
+ if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
1262
+ rb_syserr_fail(LAST_ERROR, 0);
1263
+ }
1264
+ #else
1265
+ rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
1266
+ #endif
1350
1267
  return io;
1351
1268
  }
1352
1269
 
1353
1270
  static VALUE
1354
1271
  console_move(VALUE io, int y, int x)
1355
1272
  {
1273
+ #ifdef _WIN32
1274
+ HANDLE h;
1275
+ rb_console_size_t ws;
1276
+ COORD *pos = &ws.dwCursorPosition;
1277
+
1278
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
1279
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1280
+ rb_syserr_fail(LAST_ERROR, 0);
1281
+ }
1282
+ pos->X += x;
1283
+ pos->Y += y;
1284
+ if (!SetConsoleCursorPosition(h, *pos)) {
1285
+ rb_syserr_fail(LAST_ERROR, 0);
1286
+ }
1287
+ #else
1356
1288
  if (x || y) {
1357
1289
  VALUE s = rb_str_new_cstr("");
1358
- if (y) rb_str_catf(s, "\x1b[%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B');
1359
- if (x) rb_str_catf(s, "\x1b[%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C');
1290
+ if (y) rb_str_catf(s, CSI "%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B');
1291
+ if (x) rb_str_catf(s, CSI "%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C');
1360
1292
  rb_io_write(io, s);
1361
1293
  rb_io_flush(io);
1362
1294
  }
1295
+ #endif
1363
1296
  return io;
1364
1297
  }
1365
1298
 
1299
+ /*
1300
+ * call-seq:
1301
+ * io.goto_column(column) -> io
1302
+ *
1303
+ * Set the cursor position at +column+ in the same line of the current
1304
+ * position.
1305
+ *
1306
+ * You must require 'io/console' to use this method.
1307
+ */
1366
1308
  static VALUE
1367
1309
  console_goto_column(VALUE io, VALUE val)
1368
1310
  {
1369
- rb_io_write(io, rb_sprintf("\x1b[%dG", NUM2UINT(val)+1));
1311
+ #ifdef _WIN32
1312
+ HANDLE h;
1313
+ rb_console_size_t ws;
1314
+ COORD *pos = &ws.dwCursorPosition;
1315
+
1316
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
1317
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1318
+ rb_syserr_fail(LAST_ERROR, 0);
1319
+ }
1320
+ pos->X = NUM2INT(val);
1321
+ if (!SetConsoleCursorPosition(h, *pos)) {
1322
+ rb_syserr_fail(LAST_ERROR, 0);
1323
+ }
1324
+ #else
1325
+ rb_io_write(io, rb_sprintf(CSI "%dG", NUM2UINT(val)+1));
1326
+ #endif
1370
1327
  return io;
1371
1328
  }
1372
1329
 
1330
+ /*
1331
+ * call-seq:
1332
+ * io.erase_line(mode) -> io
1333
+ *
1334
+ * Erases the line at the cursor corresponding to +mode+.
1335
+ * +mode+ may be either:
1336
+ * 0: after cursor
1337
+ * 1: before and cursor
1338
+ * 2: entire line
1339
+ *
1340
+ * You must require 'io/console' to use this method.
1341
+ */
1373
1342
  static VALUE
1374
1343
  console_erase_line(VALUE io, VALUE val)
1375
1344
  {
1376
1345
  int mode = mode_in_range(val, 2, "line erase");
1377
- rb_io_write(io, rb_sprintf("\x1b[%dK", mode));
1346
+ #ifdef _WIN32
1347
+ HANDLE h;
1348
+ rb_console_size_t ws;
1349
+ COORD *pos = &ws.dwCursorPosition;
1350
+ DWORD w;
1351
+
1352
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
1353
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1354
+ rb_syserr_fail(LAST_ERROR, 0);
1355
+ }
1356
+ w = winsize_col(&ws);
1357
+ switch (mode) {
1358
+ case 0: /* after cursor */
1359
+ w -= pos->X;
1360
+ break;
1361
+ case 1: /* before *and* cursor */
1362
+ w = pos->X + 1;
1363
+ pos->X = 0;
1364
+ break;
1365
+ case 2: /* entire line */
1366
+ pos->X = 0;
1367
+ break;
1368
+ }
1369
+ constat_clear(h, ws.wAttributes, w, *pos);
1370
+ return io;
1371
+ #else
1372
+ rb_io_write(io, rb_sprintf(CSI "%dK", mode));
1373
+ #endif
1378
1374
  return io;
1379
1375
  }
1380
1376
 
1377
+ /*
1378
+ * call-seq:
1379
+ * io.erase_screen(mode) -> io
1380
+ *
1381
+ * Erases the screen at the cursor corresponding to +mode+.
1382
+ * +mode+ may be either:
1383
+ * 0: after cursor
1384
+ * 1: before and cursor
1385
+ * 2: entire screen
1386
+ *
1387
+ * You must require 'io/console' to use this method.
1388
+ */
1381
1389
  static VALUE
1382
1390
  console_erase_screen(VALUE io, VALUE val)
1383
1391
  {
1384
1392
  int mode = mode_in_range(val, 3, "screen erase");
1385
- rb_io_write(io, rb_sprintf("\x1b[%dJ", mode));
1386
- return io;
1387
- }
1393
+ #ifdef _WIN32
1394
+ HANDLE h;
1395
+ rb_console_size_t ws;
1396
+ COORD *pos = &ws.dwCursorPosition;
1397
+ DWORD w;
1388
1398
 
1389
- static VALUE
1390
- console_scroll(VALUE io, int line)
1391
- {
1392
- if (line) {
1393
- VALUE s = rb_sprintf("\x1b[%d%c", line < 0 ? -line : line,
1394
- line < 0 ? 'T' : 'S');
1395
- rb_io_write(io, s);
1399
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
1400
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1401
+ rb_syserr_fail(LAST_ERROR, 0);
1402
+ }
1403
+ w = winsize_col(&ws);
1404
+ switch (mode) {
1405
+ case 0: /* erase after cursor */
1406
+ w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
1407
+ break;
1408
+ case 1: /* erase before *and* cursor */
1409
+ w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
1410
+ pos->X = 0;
1411
+ pos->Y = ws.srWindow.Top;
1412
+ break;
1413
+ case 2: /* erase entire screen */
1414
+ w = (w * winsize_row(&ws));
1415
+ pos->X = 0;
1416
+ pos->Y = ws.srWindow.Top;
1417
+ break;
1418
+ case 3: /* erase entire screen */
1419
+ w = (w * ws.dwSize.Y);
1420
+ pos->X = 0;
1421
+ pos->Y = 0;
1422
+ break;
1396
1423
  }
1424
+ constat_clear(h, ws.wAttributes, w, *pos);
1425
+ #else
1426
+ rb_io_write(io, rb_sprintf(CSI "%dJ", mode));
1427
+ #endif
1397
1428
  return io;
1398
1429
  }
1399
- # define console_key_pressed_p rb_f_notimplement
1400
- #endif
1401
1430
 
1431
+ /*
1432
+ * call-seq:
1433
+ * io.cursor = [line, column] -> io
1434
+ *
1435
+ * Same as <tt>io.goto(line, column)</tt>
1436
+ *
1437
+ * See IO#goto.
1438
+ *
1439
+ * You must require 'io/console' to use this method.
1440
+ */
1402
1441
  static VALUE
1403
1442
  console_cursor_set(VALUE io, VALUE cpos)
1404
1443
  {
@@ -1407,42 +1446,98 @@ console_cursor_set(VALUE io, VALUE cpos)
1407
1446
  return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
1408
1447
  }
1409
1448
 
1449
+ /*
1450
+ * call-seq:
1451
+ * io.cursor_up(n) -> io
1452
+ *
1453
+ * Moves the cursor up +n+ lines.
1454
+ *
1455
+ * You must require 'io/console' to use this method.
1456
+ */
1410
1457
  static VALUE
1411
1458
  console_cursor_up(VALUE io, VALUE val)
1412
1459
  {
1413
1460
  return console_move(io, -NUM2INT(val), 0);
1414
1461
  }
1415
1462
 
1463
+ /*
1464
+ * call-seq:
1465
+ * io.cursor_down(n) -> io
1466
+ *
1467
+ * Moves the cursor down +n+ lines.
1468
+ *
1469
+ * You must require 'io/console' to use this method.
1470
+ */
1416
1471
  static VALUE
1417
1472
  console_cursor_down(VALUE io, VALUE val)
1418
1473
  {
1419
1474
  return console_move(io, +NUM2INT(val), 0);
1420
1475
  }
1421
1476
 
1477
+ /*
1478
+ * call-seq:
1479
+ * io.cursor_left(n) -> io
1480
+ *
1481
+ * Moves the cursor left +n+ columns.
1482
+ *
1483
+ * You must require 'io/console' to use this method.
1484
+ */
1422
1485
  static VALUE
1423
1486
  console_cursor_left(VALUE io, VALUE val)
1424
1487
  {
1425
1488
  return console_move(io, 0, -NUM2INT(val));
1426
1489
  }
1427
1490
 
1491
+ /*
1492
+ * call-seq:
1493
+ * io.cursor_right(n) -> io
1494
+ *
1495
+ * Moves the cursor right +n+ columns.
1496
+ *
1497
+ * You must require 'io/console' to use this method.
1498
+ */
1428
1499
  static VALUE
1429
1500
  console_cursor_right(VALUE io, VALUE val)
1430
1501
  {
1431
1502
  return console_move(io, 0, +NUM2INT(val));
1432
1503
  }
1433
1504
 
1505
+ /*
1506
+ * call-seq:
1507
+ * io.scroll_forward(n) -> io
1508
+ *
1509
+ * Scrolls the entire scrolls forward +n+ lines.
1510
+ *
1511
+ * You must require 'io/console' to use this method.
1512
+ */
1434
1513
  static VALUE
1435
1514
  console_scroll_forward(VALUE io, VALUE val)
1436
1515
  {
1437
1516
  return console_scroll(io, +NUM2INT(val));
1438
1517
  }
1439
1518
 
1519
+ /*
1520
+ * call-seq:
1521
+ * io.scroll_backward(n) -> io
1522
+ *
1523
+ * Scrolls the entire scrolls backward +n+ lines.
1524
+ *
1525
+ * You must require 'io/console' to use this method.
1526
+ */
1440
1527
  static VALUE
1441
1528
  console_scroll_backward(VALUE io, VALUE val)
1442
1529
  {
1443
1530
  return console_scroll(io, -NUM2INT(val));
1444
1531
  }
1445
1532
 
1533
+ /*
1534
+ * call-seq:
1535
+ * io.clear_screen -> io
1536
+ *
1537
+ * Clears the entire screen and moves the cursor top-left corner.
1538
+ *
1539
+ * You must require 'io/console' to use this method.
1540
+ */
1446
1541
  static VALUE
1447
1542
  console_clear_screen(VALUE io)
1448
1543
  {
@@ -1451,6 +1546,92 @@ console_clear_screen(VALUE io)
1451
1546
  return io;
1452
1547
  }
1453
1548
 
1549
+ #ifndef HAVE_RB_IO_OPEN_DESCRIPTOR
1550
+ static VALUE
1551
+ io_open_descriptor_fallback(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, void *encoding)
1552
+ {
1553
+ VALUE arguments[2] = {
1554
+ (rb_update_max_fd(descriptor), INT2NUM(descriptor)),
1555
+ INT2FIX(mode),
1556
+ };
1557
+
1558
+ VALUE self = rb_class_new_instance(2, arguments, klass);
1559
+
1560
+ rb_io_t *fptr;
1561
+ GetOpenFile(self, fptr);
1562
+ fptr->pathv = path;
1563
+ fptr->mode |= mode;
1564
+
1565
+ return self;
1566
+ }
1567
+ #define rb_io_open_descriptor io_open_descriptor_fallback
1568
+ #endif
1569
+
1570
+ #ifndef HAVE_RB_IO_CLOSED_P
1571
+ static VALUE
1572
+ rb_io_closed_p(VALUE io)
1573
+ {
1574
+ rb_io_t *fptr = RFILE(io)->fptr;
1575
+ return fptr->fd == -1 ? Qtrue : Qfalse;
1576
+ }
1577
+ #endif
1578
+
1579
+ #if defined(RB_EXT_RACTOR_SAFE) && defined(HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY)
1580
+ # define USE_RACTOR_STORAGE 1
1581
+ #else
1582
+ # define USE_RACTOR_STORAGE 0
1583
+ #endif
1584
+
1585
+ #if USE_RACTOR_STORAGE
1586
+ #include "ruby/ractor.h"
1587
+ static rb_ractor_local_key_t key_console_dev;
1588
+
1589
+ static bool
1590
+ console_dev_get(VALUE klass, VALUE *dev)
1591
+ {
1592
+ return rb_ractor_local_storage_value_lookup(key_console_dev, dev);
1593
+ }
1594
+
1595
+ static void
1596
+ console_dev_set(VALUE klass, VALUE value)
1597
+ {
1598
+ rb_ractor_local_storage_value_set(key_console_dev, value);
1599
+ }
1600
+
1601
+ static void
1602
+ console_dev_remove(VALUE klass)
1603
+ {
1604
+ console_dev_set(klass, Qnil);
1605
+ }
1606
+
1607
+ #else
1608
+
1609
+ static ID id_console;
1610
+
1611
+ static int
1612
+ console_dev_get(VALUE klass, VALUE *dev)
1613
+ {
1614
+ if (rb_const_defined(klass, id_console)) {
1615
+ *dev = rb_const_get(klass, id_console);
1616
+ return 1;
1617
+ }
1618
+ return 0;
1619
+ }
1620
+
1621
+ static void
1622
+ console_dev_set(VALUE klass, VALUE value)
1623
+ {
1624
+ rb_const_set(klass, id_console, value);
1625
+ }
1626
+
1627
+ static void
1628
+ console_dev_remove(VALUE klass)
1629
+ {
1630
+ rb_const_remove(klass, id_console);
1631
+ }
1632
+
1633
+ #endif
1634
+
1454
1635
  /*
1455
1636
  * call-seq:
1456
1637
  * IO.console -> #<File:/dev/tty>
@@ -1468,34 +1649,36 @@ static VALUE
1468
1649
  console_dev(int argc, VALUE *argv, VALUE klass)
1469
1650
  {
1470
1651
  VALUE con = 0;
1471
- rb_io_t *fptr;
1472
1652
  VALUE sym = 0;
1473
1653
 
1474
1654
  rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);
1655
+
1475
1656
  if (argc) {
1476
- Check_Type(sym = argv[0], T_SYMBOL);
1657
+ Check_Type(sym = argv[0], T_SYMBOL);
1477
1658
  }
1659
+
1660
+ // Force the class to be File.
1478
1661
  if (klass == rb_cIO) klass = rb_cFile;
1479
- if (rb_const_defined(klass, id_console)) {
1480
- con = rb_const_get(klass, id_console);
1481
- if (!RB_TYPE_P(con, T_FILE) ||
1482
- (!(fptr = RFILE(con)->fptr) || GetReadFD(fptr) == -1)) {
1483
- rb_const_remove(klass, id_console);
1484
- con = 0;
1485
- }
1662
+
1663
+ if (console_dev_get(klass, &con)) {
1664
+ if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) {
1665
+ console_dev_remove(klass);
1666
+ con = 0;
1667
+ }
1486
1668
  }
1669
+
1487
1670
  if (sym) {
1488
- if (sym == ID2SYM(id_close) && argc == 1) {
1489
- if (con) {
1490
- rb_io_close(con);
1491
- rb_const_remove(klass, id_console);
1492
- con = 0;
1493
- }
1494
- return Qnil;
1495
- }
1671
+ if (sym == ID2SYM(id_close) && argc == 1) {
1672
+ if (con) {
1673
+ rb_io_close(con);
1674
+ console_dev_remove(klass);
1675
+ con = 0;
1676
+ }
1677
+ return Qnil;
1678
+ }
1496
1679
  }
1680
+
1497
1681
  if (!con) {
1498
- VALUE args[2];
1499
1682
  #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H
1500
1683
  # define CONSOLE_DEVICE "/dev/tty"
1501
1684
  #elif defined _WIN32
@@ -1507,44 +1690,35 @@ console_dev(int argc, VALUE *argv, VALUE klass)
1507
1690
  # define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE
1508
1691
  #endif
1509
1692
  #ifdef CONSOLE_DEVICE_FOR_WRITING
1510
- VALUE out;
1511
- rb_io_t *ofptr;
1693
+ VALUE out;
1512
1694
  #endif
1513
- int fd;
1695
+ int fd;
1696
+ VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
1514
1697
 
1515
1698
  #ifdef CONSOLE_DEVICE_FOR_WRITING
1516
- fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
1517
- if (fd < 0) return Qnil;
1518
- rb_update_max_fd(fd);
1519
- args[1] = INT2FIX(O_WRONLY);
1520
- args[0] = INT2NUM(fd);
1521
- out = rb_class_new_instance(2, args, klass);
1699
+ fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
1700
+ if (fd < 0) return Qnil;
1701
+ out = rb_io_open_descriptor(klass, fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL);
1522
1702
  #endif
1523
- fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
1524
- if (fd < 0) {
1703
+ fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
1704
+ if (fd < 0) {
1525
1705
  #ifdef CONSOLE_DEVICE_FOR_WRITING
1526
- rb_io_close(out);
1706
+ rb_io_close(out);
1527
1707
  #endif
1528
- return Qnil;
1529
- }
1530
- rb_update_max_fd(fd);
1531
- args[1] = INT2FIX(O_RDWR);
1532
- args[0] = INT2NUM(fd);
1533
- con = rb_class_new_instance(2, args, klass);
1534
- GetOpenFile(con, fptr);
1535
- fptr->pathv = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
1708
+ return Qnil;
1709
+ }
1710
+
1711
+ con = rb_io_open_descriptor(klass, fd, FMODE_READWRITE | FMODE_SYNC, path, Qnil, NULL);
1536
1712
  #ifdef CONSOLE_DEVICE_FOR_WRITING
1537
- GetOpenFile(out, ofptr);
1538
- ofptr->pathv = fptr->pathv;
1539
- fptr->tied_io_for_writing = out;
1540
- ofptr->mode |= FMODE_SYNC;
1713
+ rb_io_set_write_io(con, out);
1541
1714
  #endif
1542
- fptr->mode |= FMODE_SYNC;
1543
- rb_const_set(klass, id_console, con);
1715
+ console_dev_set(klass, con);
1544
1716
  }
1717
+
1545
1718
  if (sym) {
1546
- return rb_f_send(argc, argv, con);
1719
+ return rb_f_send(argc, argv, con);
1547
1720
  }
1721
+
1548
1722
  return con;
1549
1723
  }
1550
1724
 
@@ -1560,13 +1734,18 @@ io_getch(int argc, VALUE *argv, VALUE io)
1560
1734
  return rb_funcallv(io, id_getc, argc, argv);
1561
1735
  }
1562
1736
 
1563
- #if ENABLE_IO_GETPASS
1564
1737
  static VALUE
1565
1738
  puts_call(VALUE io)
1566
1739
  {
1567
1740
  return rb_io_write(io, rb_default_rs);
1568
1741
  }
1569
1742
 
1743
+ static VALUE
1744
+ gets_call(VALUE io)
1745
+ {
1746
+ return rb_funcallv(io, id_gets, 0, 0);
1747
+ }
1748
+
1570
1749
  static VALUE
1571
1750
  getpass_call(VALUE io)
1572
1751
  {
@@ -1587,7 +1766,8 @@ static VALUE
1587
1766
  str_chomp(VALUE str)
1588
1767
  {
1589
1768
  if (!NIL_P(str)) {
1590
- rb_funcallv(str, id_chomp_bang, 0, 0);
1769
+ const VALUE rs = rb_default_rs; /* rvalue in TruffleRuby */
1770
+ rb_funcallv(str, id_chomp_bang, 1, &rs);
1591
1771
  }
1592
1772
  return str;
1593
1773
  }
@@ -1604,6 +1784,12 @@ str_chomp(VALUE str)
1604
1784
  * see String#chomp!.
1605
1785
  *
1606
1786
  * You must require 'io/console' to use this method.
1787
+ *
1788
+ * require 'io/console'
1789
+ * IO::console.getpass("Enter password:")
1790
+ * Enter password:
1791
+ * # => "mypassword"
1792
+ *
1607
1793
  */
1608
1794
  static VALUE
1609
1795
  console_getpass(int argc, VALUE *argv, VALUE io)
@@ -1614,6 +1800,7 @@ console_getpass(int argc, VALUE *argv, VALUE io)
1614
1800
  wio = rb_io_get_write_io(io);
1615
1801
  if (wio == io && io == rb_stdin) wio = rb_stderr;
1616
1802
  prompt(argc, argv, wio);
1803
+ rb_io_flush(wio);
1617
1804
  str = rb_ensure(getpass_call, io, puts_call, wio);
1618
1805
  return str_chomp(str);
1619
1806
  }
@@ -1631,10 +1818,64 @@ io_getpass(int argc, VALUE *argv, VALUE io)
1631
1818
 
1632
1819
  rb_check_arity(argc, 0, 1);
1633
1820
  prompt(argc, argv, io);
1634
- str = str_chomp(rb_funcallv(io, id_gets, 0, 0));
1635
- puts_call(io);
1636
- return str;
1821
+ rb_check_funcall(io, id_flush, 0, 0);
1822
+ str = rb_ensure(gets_call, io, puts_call, io);
1823
+ return str_chomp(str);
1824
+ }
1825
+
1826
+ #if defined(_WIN32) || defined(HAVE_TTYNAME_R) || defined(HAVE_TTYNAME)
1827
+ /*
1828
+ * call-seq:
1829
+ * io.ttyname -> string or nil
1830
+ *
1831
+ * Returns name of associated terminal (tty) if +io+ is not a tty.
1832
+ * Returns +nil+ otherwise.
1833
+ */
1834
+ static VALUE
1835
+ console_ttyname(VALUE io)
1836
+ {
1837
+ int fd = rb_io_descriptor(io);
1838
+ if (!isatty(fd)) return Qnil;
1839
+ # if defined _WIN32
1840
+ return rb_usascii_str_new_lit("con");
1841
+ # elif defined HAVE_TTYNAME_R
1842
+ {
1843
+ char termname[1024], *tn = termname;
1844
+ size_t size = sizeof(termname);
1845
+ int e;
1846
+ if (ttyname_r(fd, tn, size) == 0)
1847
+ return rb_interned_str_cstr(tn);
1848
+ if ((e = errno) == ERANGE) {
1849
+ VALUE s = rb_str_new(0, size);
1850
+ while (1) {
1851
+ tn = RSTRING_PTR(s);
1852
+ size = rb_str_capacity(s);
1853
+ if (ttyname_r(fd, tn, size) == 0) {
1854
+ return rb_str_to_interned_str(rb_str_resize(s, strlen(tn)));
1855
+ }
1856
+ if ((e = errno) != ERANGE) break;
1857
+ if ((size *= 2) >= INT_MAX/2) break;
1858
+ rb_str_resize(s, size);
1859
+ }
1860
+ }
1861
+ rb_syserr_fail_str(e, rb_sprintf("ttyname_r(%d)", fd));
1862
+ UNREACHABLE_RETURN(Qnil);
1863
+ }
1864
+ # elif defined HAVE_TTYNAME
1865
+ {
1866
+ const char *tn = ttyname(fd);
1867
+ if (!tn) {
1868
+ int e = errno;
1869
+ rb_syserr_fail_str(e, rb_sprintf("ttyname(%d)", fd));
1870
+ }
1871
+ return rb_interned_str_cstr(tn);
1872
+ }
1873
+ # else
1874
+ # error No ttyname function
1875
+ # endif
1637
1876
  }
1877
+ #else
1878
+ # define console_ttyname rb_f_notimplement
1638
1879
  #endif
1639
1880
 
1640
1881
  /*
@@ -1643,13 +1884,20 @@ io_getpass(int argc, VALUE *argv, VALUE io)
1643
1884
  void
1644
1885
  Init_console(void)
1645
1886
  {
1887
+ #if USE_RACTOR_STORAGE
1888
+ RB_EXT_RACTOR_SAFE(true);
1889
+ #endif
1890
+
1646
1891
  #undef rb_intern
1892
+ #if USE_RACTOR_STORAGE
1893
+ key_console_dev = rb_ractor_local_storage_value_newkey();
1894
+ #else
1895
+ id_console = rb_intern("console");
1896
+ #endif
1647
1897
  id_getc = rb_intern("getc");
1648
- #if ENABLE_IO_GETPASS
1649
1898
  id_gets = rb_intern("gets");
1899
+ id_flush = rb_intern("flush");
1650
1900
  id_chomp_bang = rb_intern("chomp!");
1651
- #endif
1652
- id_console = rb_intern("console");
1653
1901
  id_close = rb_intern("close");
1654
1902
  #define init_rawmode_opt_id(name) \
1655
1903
  rawmode_opt_ids[kwd_##name] = rb_intern(#name)
@@ -1696,20 +1944,20 @@ InitVM_console(void)
1696
1944
  rb_define_method(rb_cIO, "clear_screen", console_clear_screen, 0);
1697
1945
  rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1);
1698
1946
  rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0);
1699
- #if ENABLE_IO_GETPASS
1700
1947
  rb_define_method(rb_cIO, "getpass", console_getpass, -1);
1701
- #endif
1948
+ rb_define_method(rb_cIO, "ttyname", console_ttyname, 0);
1702
1949
  rb_define_singleton_method(rb_cIO, "console", console_dev, -1);
1703
1950
  {
1951
+ /* :stopdoc: */
1704
1952
  VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
1953
+ /* :startdoc: */
1705
1954
  rb_define_method(mReadable, "getch", io_getch, -1);
1706
- #if ENABLE_IO_GETPASS
1707
1955
  rb_define_method(mReadable, "getpass", io_getpass, -1);
1708
- #endif
1709
1956
  }
1710
1957
  {
1711
1958
  /* :stopdoc: */
1712
1959
  cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject);
1960
+ rb_define_const(cConmode, "VERSION", rb_obj_freeze(rb_str_new_cstr(IO_CONSOLE_VERSION)));
1713
1961
  rb_define_alloc_func(cConmode, conmode_alloc);
1714
1962
  rb_undef_method(cConmode, "initialize");
1715
1963
  rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1);