io-console 0.9.1-java → 0.9.3-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e86817ae95f9a6fc3fa3301ea895706a3071f8e500c3c2dec9ef669dae510609
4
- data.tar.gz: 433bb663068ef08dbc2a076c0d037d94872f2906652bd198ee950fff5c3a1bc7
3
+ metadata.gz: 81030a79e741f9fbea14484f4849da9e6efc1daf0784d6d9b7fc067cc3a95357
4
+ data.tar.gz: f3c862f8495b89a129d0547177530875456cb44fcdf4f7b76d9ddb81c110b08b
5
5
  SHA512:
6
- metadata.gz: ec7274caecc4e434b7a1e7afe30251d1b07f677c54d3865f3b341c008f96ca6a7d95faa7c87faacbbf65734ee7a0e23bbdfb469088349a03c1cacc5f82be3bee
7
- data.tar.gz: a9b72f79b80a65809217ed7cdab1edeb01daa53ffe9647d42900960569f4db3e83bb869c80f8da8ee9b2c76d23a66b08cc5d879a373a9ac9d9f6bdd05c0c8a66
6
+ metadata.gz: 6441ea13e62add5a11937381834077352406e0fd96a4cd7ded00e6fbcdc08a4c0e1376aa9b42ec35a3aba9b68ca7a6e5b0b8eddd4086cec960cabc1d6a1565f4
7
+ data.tar.gz: 28f42664fef6544ae6cffcf2ebc09e921155c302c8cf8da85e3ff584697d5a98bf1893bccddf76cfe68fa391a3b7f86ebb97ffb2ab6eb6dd299ecdf19b055c3b
@@ -1,8 +1,72 @@
1
+ require 'ffi'
2
+
3
+ case RbConfig::CONFIG['host_os']
4
+ when /darwin/i
5
+ tested_platforms = /i386|x86_64|aarch64/
6
+ unless tested_platforms.match?(FFI::Platform::ARCH)
7
+ raise LoadError, "native console on MacOS only supported on #{tested_platforms.source.gsub('|', ', ')}"
8
+ end
9
+ when /linux/i
10
+ tested_platforms = /i386|x86_64|powerpc64|aarch64|s390x/
11
+ unless tested_platforms.match?(FFI::Platform::ARCH)
12
+ warn "native console only tested on #{tested_platforms.source.gsub('|', ', ')}"
13
+ end
14
+ end
15
+
16
+ module IO::Console::LibC
17
+ include IO::Console::Constants
18
+ extend FFI::Library
19
+ ffi_lib FFI::Library::LIBC
20
+
21
+ typedef TCFLAG_TYPE, :tcflag_t
22
+ typedef SPEED_TYPE, :speed_t
23
+
24
+ class Termios < FFI::Struct
25
+ constants = IO::Console::Constants
26
+ fields = [
27
+ :c_iflag, :tcflag_t,
28
+ :c_oflag, :tcflag_t,
29
+ :c_cflag, :tcflag_t,
30
+ :c_lflag, :tcflag_t,
31
+ ]
32
+ fields.concat([:c_line, :uchar]) if constants::TERMIOS_HAS_LINE
33
+ fields.concat([
34
+ :c_cc, [:uchar, constants::NCCS],
35
+ :c_ispeed, :speed_t,
36
+ :c_ospeed, :speed_t,
37
+ ])
38
+ layout(*fields)
39
+ end
40
+
41
+ class Winsize < FFI::Struct
42
+ layout \
43
+ :ws_row, :ushort,
44
+ :ws_col, :ushort,
45
+ :ws_xpixel, :ushort,
46
+ :ws_ypixel, :ushort
47
+ end
48
+
49
+ attach_function :tcsetattr, [:int, :int, :pointer], :int
50
+ attach_function :tcgetattr, [:int, :pointer], :int
51
+ attach_function :cfgetispeed, [:pointer], :speed_t
52
+ attach_function :cfgetospeed, [:pointer], :speed_t
53
+ attach_function :cfsetispeed, [:pointer, :speed_t], :int
54
+ attach_function :cfsetospeed, [:pointer, :speed_t], :int
55
+ attach_function :cfmakeraw, [:pointer], :void
56
+ attach_function :tcflush, [:int, :int], :int
57
+ attach_function :ioctl, [:int, :ulong, :varargs], :int
58
+ end
59
+
1
60
  # Common logic that uses native calls for console
2
61
  module IO::Console
3
62
  def ttymode
4
63
  termios = LibC::Termios.new
5
- if LibC.tcgetattr(self.fileno, termios) != 0
64
+ result = if LibC.const_defined?(:TIOCGETA)
65
+ LibC.ioctl(fileno, LibC::TIOCGETA, :pointer, termios.pointer)
66
+ else
67
+ LibC.tcgetattr(fileno, termios.pointer)
68
+ end
69
+ if result != 0
6
70
  raise SystemCallError.new(respond_to?(:path) ? path : "tcgetattr", FFI.errno)
7
71
  end
8
72
 
@@ -15,7 +79,12 @@ module IO::Console
15
79
  private :ttymode
16
80
 
17
81
  def ttymode_set(termios)
18
- if LibC.tcsetattr(self.fileno, LibC::TCSANOW, termios) != 0
82
+ result = if LibC.const_defined?(:TIOCSETA)
83
+ LibC.ioctl(fileno, LibC::TIOCSETA, :pointer, termios.pointer)
84
+ else
85
+ LibC.tcsetattr(fileno, LibC::TCSANOW, termios.pointer)
86
+ end
87
+ if result != 0
19
88
  raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno)
20
89
  end
21
90
  end
@@ -33,13 +102,19 @@ module IO::Console
33
102
  end
34
103
  private :ttymode_yield
35
104
 
36
- TTY_RAW = Proc.new do |t, min: 1, time: nil, intr: nil|
37
- LibC.cfmakeraw(t)
105
+ TTY_SET_MIN = Proc.new do |t, min|
106
+ t[:c_cc][LibC::VMIN] = min ? min.to_i.clamp(0, 255) : 1
107
+ end
108
+
109
+ TTY_SET_TIME = Proc.new do |t, time|
110
+ t[:c_cc][LibC::VTIME] = time ? (time * 10).to_i.clamp(0, 255) : 0
111
+ end
112
+
113
+ TTY_RAW = Proc.new do |t, min: nil, time: nil, intr: nil|
114
+ LibC.cfmakeraw(t.pointer)
38
115
  t[:c_lflag] &= ~(LibC::ECHOE|LibC::ECHOK)
39
- if min >= 0
40
- t[:c_cc][LibC::VMIN] = min
41
- end
42
- t[:c_cc][LibC::VTIME] = (time&.to_i || 0) * 10
116
+ TTY_SET_MIN[t, min]
117
+ TTY_SET_TIME[t, time]
43
118
  if intr
44
119
  t[:c_iflag] |= LibC::BRKINT
45
120
  t[:c_lflag] |= LibC::ISIG
@@ -47,18 +122,19 @@ module IO::Console
47
122
  end
48
123
  end
49
124
 
50
- def raw(*, min: 1, time: nil, intr: nil, &block)
125
+ def raw(*, min: nil, time: nil, intr: nil, &block)
51
126
  ttymode_yield(block, min:, time:, intr:, &TTY_RAW)
52
127
  end
53
128
 
54
- def raw!(*)
55
- ttymode(&TTY_RAW)
129
+ def raw!(*, min: 1, time: nil, intr: nil)
130
+ ttymode { |t| TTY_RAW.call(t, min:, time:, intr:) }
131
+ self
56
132
  end
57
133
 
58
134
  TTY_COOKED = Proc.new do |t|
59
135
  t[:c_iflag] |= (LibC::BRKINT|LibC::ISTRIP|LibC::ICRNL|LibC::IXON)
60
136
  t[:c_oflag] |= LibC::OPOST
61
- t[:c_lflag] |= (LibC::ECHO|LibC::ECHOE|LibC::ECHOK|LibC::ECHONL|LibC::ICANON|LibC::ISIG|LibC::IEXTEN)
137
+ t[:c_lflag] |= (TTY_ECHO|LibC::ICANON|LibC::ISIG|LibC::IEXTEN)
62
138
  end
63
139
 
64
140
  def cooked(*, &block)
@@ -88,6 +164,8 @@ module IO::Console
88
164
  ttymode_yield(block) { |t| t[:c_lflag] &= ~(TTY_ECHO) }
89
165
  end
90
166
 
167
+ private_constant :TTY_SET_TIME, :TTY_SET_MIN, :TTY_RAW, :TTY_COOKED, :TTY_ECHO
168
+
91
169
  class Mode
92
170
  attr_reader :termios
93
171
 
@@ -99,6 +177,11 @@ module IO::Console
99
177
  @termios = m.termios.dup
100
178
  end
101
179
 
180
+ def echo?
181
+ !(@termios[:c_lflag] & TTY_ECHO).zero?
182
+ end
183
+ alias echo echo?
184
+
102
185
  def echo=(echo)
103
186
  if echo
104
187
  @termios[:c_lflag] |= TTY_ECHO
@@ -117,6 +200,22 @@ module IO::Console
117
200
  TTY_RAW[new_mode.termios, min:, time:, intr:]
118
201
  new_mode
119
202
  end
203
+
204
+ def min
205
+ @termios[:c_cc][LibC::VMIN]
206
+ end
207
+
208
+ def min=(min)
209
+ TTY_SET_MIN[@termios, min]
210
+ end
211
+
212
+ def time
213
+ @termios[:c_cc][LibC::VTIME].quo(10)
214
+ end
215
+
216
+ def time=(time)
217
+ TTY_SET_TIME[@termios, time]
218
+ end
120
219
  end
121
220
 
122
221
  def console_mode
@@ -176,4 +275,5 @@ end
176
275
 
177
276
  class IO
178
277
  include Console
278
+ prepend PlatformTty if instance_method(:tty?).arity == 0
179
279
  end
@@ -1,13 +1,6 @@
1
1
  require 'ffi'
2
2
 
3
3
  module IO::Console::Windows
4
- STD_INPUT_HANDLE = -10
5
- STD_OUTPUT_HANDLE = -11
6
- WAIT_OBJECT_0 = 0
7
- WAIT_TIMEOUT = 258
8
- ENABLE_WRAP_AT_EOL_OUTPUT = 2
9
- ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
10
-
11
4
  module Native
12
5
  extend FFI::Library
13
6
  ffi_convention :stdcall
@@ -125,11 +118,30 @@ end
125
118
 
126
119
  IO.prepend(IO::Console::Windows::TTY)
127
120
 
128
- class IO::ConsoleMode
121
+ class IO::Console::Mode
129
122
  def initialize(mode)
130
123
  @mode = mode
131
124
  end
132
125
 
126
+ def echo?
127
+ end
128
+ alias echo echo?
129
+
130
+ def echo=(echo)
131
+ end
132
+
133
+ def min
134
+ end
135
+
136
+ def min=(min)
137
+ end
138
+
139
+ def time
140
+ end
141
+
142
+ def time=(time)
143
+ end
144
+
133
145
  def virtual_terminal_processing?
134
146
  @mode & IO::Console::Windows::ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0
135
147
  end
@@ -158,7 +170,7 @@ end
158
170
 
159
171
  class IO
160
172
  def console_mode
161
- IO::ConsoleMode.new(IO::Console::Windows.console_mode(self))
173
+ IO::Console::Mode.new(IO::Console::Windows.console_mode(self))
162
174
  end
163
175
 
164
176
  def console_mode=(mode)
@@ -0,0 +1,213 @@
1
+ # attempt to call stty; if failure, raise error
2
+ module IO::Console
3
+ STTY = %w[/usr/bin/stty /bin/stty].find {|path| File.executable?(path)}
4
+ end
5
+
6
+ raise LoadError, "stty command not found" unless IO::Console::STTY
7
+
8
+ warn "io/console on JRuby shells out to stty for most operations" if $VERBOSE
9
+
10
+ class IO::Console::Mode
11
+ STTY_PATTERNS = /
12
+ (?<a>[a-z]\w*)\s(?:=\s(?:(?<n>\d+)|(?<c>\^.|.)|<undef>)|(?<n>\d+)(?:\s+\w+)?); |
13
+ (?<n>\d+)\s+(?<a>[a-z]\w*); |
14
+ (?<f>-)?(?<a>[a-z]\w*)(?=\s|$)
15
+ /x
16
+ private_constant :STTY_PATTERNS
17
+
18
+ def initialize(saved, attrs)
19
+ @saved = saved.chomp
20
+ @attrs = {}
21
+ attrs.scan(STTY_PATTERNS) do
22
+ m = $~
23
+ case attr = m[:a]
24
+ when "echo"
25
+ @attrs[attr.to_sym] = !m[:f]
26
+ when "min", "time"
27
+ @attrs[attr.to_sym] = m[:n]&.to_i
28
+ when *%w[brkint icanon isig opost]
29
+ @attrs[attr.to_sym] = !m[:f]
30
+ end
31
+ end
32
+ @args = []
33
+ @changes = {}
34
+ end
35
+
36
+ def initialize_copy(mode)
37
+ super
38
+ @attrs = @attrs.dup
39
+ @saved = @saved.dup
40
+ @args = @args.dup
41
+ @changes = @changes.dup
42
+ end
43
+
44
+ def arguments
45
+ [
46
+ @saved,
47
+ *@args,
48
+ *@changes.flat_map {|a, v|
49
+ case v
50
+ when true
51
+ a.to_s
52
+ when false
53
+ "-#{a}"
54
+ when nil
55
+ else
56
+ [a.to_s, v.to_s]
57
+ end
58
+ },
59
+ ]
60
+ end
61
+
62
+ def echo?
63
+ @changes.fetch(:echo) {@attrs[:echo]}
64
+ end
65
+ alias echo echo?
66
+
67
+ def echo=(echo)
68
+ @changes[:echo] = echo
69
+ end
70
+
71
+ def raw(min: nil, time: nil, intr: nil)
72
+ dup.raw!(min:, time:, intr:)
73
+ end
74
+
75
+ def raw!(min: nil, time: nil, intr: nil)
76
+ @args << 'raw'
77
+ self.min = min
78
+ self.time = time
79
+ %i[brkint isig opost].each {|a| @changes[a] = true} if intr
80
+ self
81
+ end
82
+
83
+ def min
84
+ @changes.fetch(:min) {@attrs[:min]}
85
+ end
86
+
87
+ def min=(min)
88
+ @changes[:min] = (min || 1).to_i.clamp(0, 255)
89
+ end
90
+
91
+ def time
92
+ @changes.fetch(:time) {@attrs[:time]}&.quo(10)
93
+ end
94
+
95
+ def time=(time)
96
+ @changes[:time] = ((time || 0) * 10).to_i.clamp(0, 255)
97
+ end
98
+
99
+ private
100
+
101
+ attr_reader :attrs, :saved, :args, :changes
102
+ end
103
+
104
+ # Non-Windows assumes stty command is available
105
+ class IO
106
+ private def _io_console_stty(*args)
107
+ # pre-check to catch non-tty filenos we can't stty against anyway
108
+ raise Errno::ENOTTY, inspect if !tty?
109
+
110
+ IO.pipe do |re, we|
111
+ input = dup # workaround for JRuby
112
+ IO.popen([Console::STTY, *args, in: input, err: we], &:read)
113
+ ensure
114
+ input&.close
115
+ unless $?.success?
116
+ we.close
117
+ error = re.read
118
+ case error
119
+ when /Inappropriate ioctl for device/
120
+ raise Errno::ENOTTY, inspect
121
+ end
122
+ raise "stty command failed: #{error.chomp}"
123
+ end
124
+ end
125
+ end
126
+
127
+ def raw(*, min: 1, time: nil, intr: nil)
128
+ saved = console_mode
129
+ self.console_mode = saved.raw(min:, time:, intr:)
130
+ yield self
131
+ ensure
132
+ self.console_mode = saved if saved
133
+ end
134
+
135
+ def raw!(*, min: 1, time: nil, intr: nil)
136
+ self.console_mode = console_mode.raw(min:, time:, intr:)
137
+ self
138
+ end
139
+
140
+ def cooked(*)
141
+ saved = _io_console_stty('-g')
142
+ _io_console_stty('-raw')
143
+ yield self
144
+ ensure
145
+ _io_console_stty(saved) if saved
146
+ end
147
+
148
+ def cooked!(*)
149
+ _io_console_stty('-raw')
150
+ end
151
+
152
+ def echo=(echo)
153
+ _io_console_stty(echo ? 'echo' : '-echo')
154
+ end
155
+
156
+ def echo?
157
+ (_io_console_stty('-a') =~ / -echo /) ? false : true
158
+ end
159
+
160
+ def noecho
161
+ saved = _io_console_stty('-g')
162
+ _io_console_stty('-echo')
163
+ yield self
164
+ ensure
165
+ _io_console_stty(saved) if saved
166
+ end
167
+
168
+ def console_mode
169
+ Console::Mode.new(_io_console_stty('-g'), _io_console_stty('-a'))
170
+ end
171
+
172
+ def console_mode=(mode)
173
+ _io_console_stty(*mode.arguments)
174
+ mode
175
+ end
176
+
177
+ def winsize
178
+ # Pattern for rows/columns; all systems may not return in these formats.
179
+ match = /
180
+ (?<rows>\d+)\s+rows;\s*(?<columns>\d+)\s+columns |
181
+ rows\s+(?<rows>\d+);\s*columns\s+(?<columns>\d+)
182
+ /x.match(_io_console_stty('-a'))
183
+ [match[:rows].to_i, match[:columns].to_i]
184
+ end
185
+
186
+ def winsize=(size)
187
+ size = size.to_ary unless size.kind_of?(Array)
188
+ sizelen = size.size
189
+
190
+ if sizelen != 2 && sizelen != 4
191
+ raise ArgumentError, "wrong number of arguments (given #{sizelen}, expected 2 or 4)"
192
+ end
193
+
194
+ if sizelen == 4
195
+ warn "stty io/console does not support pixel winsize"
196
+ end
197
+
198
+ row, col, _, _ = size
199
+
200
+ _io_console_stty('rows', row.to_s, 'cols', col.to_s)
201
+ end
202
+
203
+ def iflush
204
+ end
205
+
206
+ def oflush
207
+ end
208
+
209
+ def ioflush
210
+ end
211
+
212
+ prepend PlatformTty
213
+ end
@@ -27,6 +27,18 @@ class IO
27
27
  yield self
28
28
  end
29
29
 
30
+ def min
31
+ end
32
+
33
+ def min=(n)
34
+ end
35
+
36
+ def time
37
+ end
38
+
39
+ def time=(t)
40
+ end
41
+
30
42
  def winsize
31
43
  [25, 80]
32
44
  end
@@ -6,7 +6,7 @@ end
6
6
 
7
7
  class IO
8
8
  # TODO: Windows version uses "conin$" and "conout$" instead of /dev/tty
9
- def self.console(sym = nil, *args)
9
+ def self.console(sym = nil, *args, **opts)
10
10
  raise TypeError, "expected Symbol, got #{sym.class}" unless sym.nil? || sym.kind_of?(Symbol)
11
11
 
12
12
  # klass = self == IO ? File : self
@@ -43,7 +43,8 @@ class IO
43
43
  @console = con
44
44
  end
45
45
 
46
- return con.send(sym, *args) if sym
46
+ return nil unless con
47
+ return con.send(sym, *args, **opts) if sym
47
48
  return con
48
49
  end
49
50
 
@@ -57,14 +58,11 @@ class IO
57
58
  wio = self == $stdin ? $stderr : self
58
59
  wio.write(prompt) if prompt
59
60
  begin
60
- str = nil
61
- noecho do
62
- str = gets
63
- end
61
+ str = noecho {gets("\n")}
64
62
  ensure
65
- puts($/)
63
+ puts
66
64
  end
67
- str.chomp
65
+ str&.chomp("\n")
68
66
  end
69
67
 
70
68
  def input_pending?
@@ -108,6 +106,7 @@ class IO
108
106
  end
109
107
  end
110
108
 
109
+ return nil unless b && result.length == 2
111
110
  result.map(&:pred)
112
111
  end
113
112
  end
@@ -162,9 +161,26 @@ class IO
162
161
 
163
162
  def getpass(prompt = nil)
164
163
  write(prompt) if prompt
165
- str = gets.chomp
166
- puts($/)
164
+ str = gets("\n")&.chomp("\n")
165
+ puts
167
166
  str
168
167
  end
169
168
  end
169
+
170
+ module PlatformTty # :nodoc:
171
+ def tty?(*modes)
172
+ modes.each do |mode|
173
+ case mode
174
+ when nil
175
+ when :any
176
+ when Symbol
177
+ raise ArgumentError, "unknown tty type: #{mode.inspect}"
178
+ else
179
+ raise TypeError, "wrong argument type #{mode.class} (expected Symbol)"
180
+ end
181
+ end
182
+ super()
183
+ end
184
+ end
185
+ private_constant :PlatformTty
170
186
  end
@@ -1,22 +1,14 @@
1
- require 'ffi'
2
-
3
- tested_platforms = %w[i386 x86_64]
4
-
5
- if RbConfig::CONFIG['host_os'].downcase =~ /darwin/ && FFI::Platform::ARCH !~ /#{tested_platforms.join('|')}/
6
- raise LoadError.new("native console on MacOS only supported on #{tested_platforms.join(', ')}")
7
- end
8
-
9
- module IO::Console::LibC
10
- extend FFI::Library
11
- ffi_lib FFI::Library::LIBC
12
-
13
- if RbConfig::CONFIG['host_os'].downcase =~ /darwin/
14
- typedef :ulong, :tcflag_t
15
- typedef :ulong, :speed_t
1
+ module IO::Console::Constants
2
+ if /darwin/i.match?(RbConfig::CONFIG['host_os'])
3
+ TCFLAG_TYPE = :ulong
4
+ SPEED_TYPE = :ulong
5
+ TIOCGETA = 0x40487413
6
+ TIOCSETA = 0x80487414
16
7
  else
17
- typedef :uint, :tcflag_t
18
- typedef :uint, :speed_t
8
+ TCFLAG_TYPE = :uint
9
+ SPEED_TYPE = :uint
19
10
  end
11
+ TERMIOS_HAS_LINE = false
20
12
 
21
13
  # Special Control Characters
22
14
  VEOF = 0 # ICANON
@@ -124,51 +116,6 @@ module IO::Console::LibC
124
116
  IOCPARM_MASK = 0x1fff
125
117
  IOC_OUT = 0x40000000
126
118
  IOC_IN = 0x80000000
127
-
128
- def self._IOC(inout,group,num,len)
129
- inout | ((len & IOCPARM_MASK) << 16) | ((group.ord << 8) | num)
130
- end
131
-
132
- def self._IOR(g,n,t)
133
- self._IOC(IOC_OUT, g, n, find_type(t).size)
134
- end
135
-
136
- def self._IOW(g,n,t)
137
- self._IOC(IOC_IN, g, n, find_type(t).size)
138
- end
139
-
140
-
141
- class Termios < FFI::Struct
142
- layout \
143
- :c_iflag, :tcflag_t,
144
- :c_oflag, :tcflag_t,
145
- :c_cflag, :tcflag_t,
146
- :c_lflag, :tcflag_t,
147
- :c_cc, [ :uchar, NCCS ],
148
- :c_ispeed, :speed_t,
149
- :c_ospeed, :speed_t
150
- end
151
-
152
- class Winsize < FFI::Struct
153
- layout \
154
- :ws_row, :ushort,
155
- :ws_col, :ushort,
156
- :ws_xpixel, :ushort,
157
- :ws_ypixel, :ushort
158
- end
159
-
160
- TIOCGWINSZ = _IOR('t', 104, Winsize) # get window size
161
- TIOCSWINSZ = _IOW('t', 103, Winsize) # set window size
162
-
163
- attach_function :tcsetattr, [ :int, :int, Termios ], :int
164
- attach_function :tcgetattr, [ :int, Termios ], :int
165
- attach_function :cfgetispeed, [ Termios ], :speed_t
166
- attach_function :cfgetospeed, [ Termios ], :speed_t
167
- attach_function :cfsetispeed, [ Termios, :speed_t ], :int
168
- attach_function :cfsetospeed, [ Termios, :speed_t ], :int
169
- attach_function :cfmakeraw, [ Termios ], :int
170
- attach_function :tcflush, [ :int, :int ], :int
171
- attach_function :ioctl, [ :int, :ulong, :varargs ], :int
119
+ TIOCGWINSZ = 0x40087468
120
+ TIOCSWINSZ = 0x80087467
172
121
  end
173
-
174
- require_relative 'native_console'
@@ -1,17 +1,7 @@
1
- require 'ffi'
2
-
3
- tested_platforms = %w[i386 x86_64 powerpc64 aarch64 s390x]
4
-
5
- unless FFI::Platform::ARCH =~ /#{tested_platforms.join('|')}/
6
- warn "native console only tested on #{tested_platforms.join(', ')}"
7
- end
8
-
9
- module IO::Console::LibC
10
- extend FFI::Library
11
- ffi_lib FFI::Library::LIBC
12
-
13
- typedef :uint, :tcflag_t
14
- typedef :uint, :speed_t
1
+ module IO::Console::Constants
2
+ TCFLAG_TYPE = :uint
3
+ SPEED_TYPE = :uint
4
+ TERMIOS_HAS_LINE = true
15
5
 
16
6
  VINTR = 0
17
7
  VQUIT = 1
@@ -168,39 +158,6 @@ module IO::Console::LibC
168
158
  TCSADRAIN = 1
169
159
  TCSAFLUSH = 2
170
160
  NCCS = 32
171
- class Termios < FFI::Struct
172
- layout \
173
- :c_iflag, :tcflag_t,
174
- :c_oflag, :tcflag_t,
175
- :c_cflag, :tcflag_t,
176
- :c_lflag, :tcflag_t,
177
- :c_line, :uchar,
178
- :c_cc, [ :uchar, NCCS ],
179
- :c_ispeed, :speed_t,
180
- :c_ospeed, :speed_t
181
- end
182
-
183
- class Winsize < FFI::Struct
184
- layout \
185
- :ws_row, :ushort,
186
- :ws_col, :ushort,
187
- :ws_xpixel, :ushort,
188
- :ws_ypixel, :ushort
189
- end
190
-
191
-
192
161
  TIOCGWINSZ = 0x5413
193
162
  TIOCSWINSZ = 0x5414
194
-
195
- attach_function :tcsetattr, [ :int, :int, Termios ], :int
196
- attach_function :tcgetattr, [ :int, Termios ], :int
197
- attach_function :cfgetispeed, [ Termios ], :speed_t
198
- attach_function :cfgetospeed, [ Termios ], :speed_t
199
- attach_function :cfsetispeed, [ Termios, :speed_t ], :int
200
- attach_function :cfsetospeed, [ Termios, :speed_t ], :int
201
- attach_function :cfmakeraw, [ Termios ], :int
202
- attach_function :tcflush, [ :int, :int ], :int
203
- attach_function :ioctl, [ :int, :ulong, :varargs ], :int
204
163
  end
205
-
206
- require_relative 'native_console'
@@ -1,4 +1,11 @@
1
1
  module IO::Console::Windows
2
+ STD_INPUT_HANDLE = -10
3
+ STD_OUTPUT_HANDLE = -11
4
+ WAIT_OBJECT_0 = 0
5
+ WAIT_TIMEOUT = 258
6
+ ENABLE_WRAP_AT_EOL_OUTPUT = 2
7
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
8
+
2
9
  VK_TAB = 0x09
3
10
  VK_RETURN = 0x0d
4
11
  VK_SHIFT = 0x10
@@ -1,5 +1,5 @@
1
1
  class IO
2
2
  module Console
3
- VERSION = "0.9.1"
3
+ VERSION = "0.9.3"
4
4
  end
5
5
  end
@@ -36,28 +36,30 @@ class IO
36
36
  deprecate_constant :ConsoleMode
37
37
  end
38
38
 
39
- libs = []
39
+ backends = []
40
+ ENV["IO_CONSOLE_BACKEND"]&.then do |be|
41
+ backends.concat(be.split(",").map {|b| b.split(":")})
42
+ end&.first ||
40
43
  # If Linux or BSD, try to load the native version
41
- case RbConfig::CONFIG['host_os'].downcase
42
- when /darwin|openbsd|freebsd|netbsd/
43
- libs << 'bsd' << 'stty'
44
- when /linux/
45
- libs << 'linux' << 'stty'
44
+ case RbConfig::CONFIG['host_os']
45
+ when /darwin|openbsd|freebsd|netbsd/i
46
+ backends << %w[ffi/termios bsd] << %w[stty]
47
+ when /linux/i
48
+ backends << %w[ffi/termios linux] << %w[stty]
46
49
  when /mswin|win32|ming/i
47
- require_relative 'console/windows_constants'
48
- require_relative 'console/windows_console'
49
- return
50
+ backends << %w[ffi/windows windows]
50
51
  else
51
- libs << 'stty'
52
+ backends << %w[stty]
52
53
  end
53
54
 
54
- return if libs.any? do |lib|
55
- require_relative "console/#{lib}_console"
55
+ return if backends.any? do |backend, constants|
56
+ require_relative "console/constants/#{constants}" if constants
57
+ require_relative "console/backend/#{backend}"
56
58
  rescue Exception => ex
57
- warn "failed to load #{lib} console support: #{ex}" if $VERBOSE
59
+ warn "failed to load #{backend.split('/', 2).first} console support: #{ex}" if $VERBOSE
58
60
  else
59
61
  true
60
62
  end
61
63
 
62
64
  # If still not ready, just use stubbed version
63
- require_relative "console/stub_console"
65
+ require_relative 'console/backend/stub'
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: false
2
2
  # fallback to console window size
3
3
  def IO.default_console_size
4
+ lines = ENV["LINES"].to_i
5
+ columns = ENV["COLUMNS"].to_i
4
6
  [
5
- ENV["LINES"].to_i.nonzero? || 25,
6
- ENV["COLUMNS"].to_i.nonzero? || 80,
7
+ lines.positive? ? lines : 25,
8
+ columns.positive? ? columns : 80,
7
9
  ]
8
10
  end
9
11
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-console
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.3
5
5
  platform: java
6
6
  authors:
7
7
  - Nobu Nakada
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-08-08 00:00:00.000000000 Z
10
+ date: 2026-09-14 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: add console capabilities to IO instances.
13
13
  email: nobu@ruby-lang.org
@@ -19,16 +19,16 @@ files:
19
19
  - BSDL
20
20
  - COPYING
21
21
  - README.md
22
- - lib/ffi/io/console.rb
23
- - lib/ffi/io/console/bsd_console.rb
24
- - lib/ffi/io/console/common.rb
25
- - lib/ffi/io/console/linux_console.rb
26
- - lib/ffi/io/console/native_console.rb
27
- - lib/ffi/io/console/stty_console.rb
28
- - lib/ffi/io/console/stub_console.rb
29
- - lib/ffi/io/console/version.rb
30
- - lib/ffi/io/console/windows_console.rb
31
- - lib/ffi/io/console/windows_constants.rb
22
+ - jruby/lib/io/console.rb
23
+ - jruby/lib/io/console/backend/ffi/termios.rb
24
+ - jruby/lib/io/console/backend/ffi/windows.rb
25
+ - jruby/lib/io/console/backend/stty.rb
26
+ - jruby/lib/io/console/backend/stub.rb
27
+ - jruby/lib/io/console/common.rb
28
+ - jruby/lib/io/console/constants/bsd.rb
29
+ - jruby/lib/io/console/constants/linux.rb
30
+ - jruby/lib/io/console/constants/windows.rb
31
+ - jruby/lib/io/console/version.rb
32
32
  - lib/io/console/size.rb
33
33
  homepage: https://github.com/ruby/io-console
34
34
  licenses:
@@ -39,7 +39,7 @@ metadata:
39
39
  changelog_uri: https://github.com/ruby/io-console/releases
40
40
  rdoc_options: []
41
41
  require_paths:
42
- - lib/ffi
42
+ - jruby/lib
43
43
  - lib
44
44
  required_ruby_version: !ruby/object:Gem::Requirement
45
45
  requirements:
@@ -1,110 +0,0 @@
1
- # attempt to call stty; if failure, raise error
2
- module IO::Console
3
- STTY = %w[/usr/bin/stty /bin/stty].find {|path| File.executable?(path)}
4
- end
5
-
6
- unless IO::Console::STTY &&
7
- system(IO::Console::STTY, out: File::NULL, err: %i[child out])
8
- raise "stty command returned nonzero exit status"
9
- end
10
-
11
- warn "io/console on JRuby shells out to stty for most operations" if $VERBOSE
12
-
13
- # Non-Windows assumes stty command is available
14
- class IO
15
- private def _io_console_stty(*args)
16
- # pre-check to catch non-tty filenos we can't stty against anyway
17
- raise Errno::ENOTTY, inspect if !tty?
18
-
19
- IO.pipe do |re, we|
20
- input = dup # workaround for JRuby
21
- IO.popen([Console::STTY, *args, in: input, err: we], &:read)
22
- ensure
23
- input&.close
24
- unless $?.success?
25
- we.close
26
- error = re.read
27
- case error
28
- when /Inappropriate ioctl for device/
29
- raise Errno::ENOTTY, inspect
30
- end
31
- raise "stty command failed: #{error.chomp}"
32
- end
33
- end
34
- end
35
-
36
- def raw(*, min: 1, time: nil, intr: nil)
37
- saved = _io_console_stty('-g')
38
- _io_console_stty('raw')
39
- yield self
40
- ensure
41
- _io_console_stty(saved) if saved
42
- end
43
-
44
- def raw!(*)
45
- _io_console_stty('raw')
46
- end
47
-
48
- def cooked(*)
49
- saved = _io_console_stty('-g')
50
- _io_console_stty('-raw')
51
- yield self
52
- ensure
53
- _io_console_stty(saved) if saved
54
- end
55
-
56
- def cooked!(*)
57
- _io_console_stty('-raw')
58
- end
59
-
60
- def echo=(echo)
61
- _io_console_stty(echo ? 'echo' : '-echo')
62
- end
63
-
64
- def echo?
65
- (_io_console_stty('-a') =~ / -echo /) ? false : true
66
- end
67
-
68
- def noecho
69
- saved = _io_console_stty('-g')
70
- _io_console_stty('-echo')
71
- yield self
72
- ensure
73
- _io_console_stty(saved) if saved
74
- end
75
-
76
- # Not all systems return same format of stty -a output
77
- IEEE_STD_1003_2 = '(?<rows>\d+) rows; (?<columns>\d+) columns'
78
- UBUNTU = 'rows (?<rows>\d+); columns (?<columns>\d+)'
79
-
80
- def winsize
81
- match = _io_console_stty('-a').match(/#{IEEE_STD_1003_2}|#{UBUNTU}/o)
82
- [match[:rows].to_i, match[:columns].to_i]
83
- end
84
-
85
- def winsize=(size)
86
- size = size.to_ary unless size.kind_of?(Array)
87
- sizelen = size.size
88
-
89
- if sizelen != 2 && sizelen != 4
90
- raise ArgumentError.new("wrong number of arguments (given #{sizelen}, expected 2 or 4)")
91
- end
92
-
93
- if sizelen == 4
94
- warn "stty io/console does not support pixel winsize"
95
- end
96
-
97
- row, col, _, _ = size
98
-
99
- _io_console_stty("rows #{row} cols #{col}")
100
- end
101
-
102
- def iflush
103
- end
104
-
105
- def oflush
106
- end
107
-
108
- def ioflush
109
- end
110
- end