Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up structure of io/console and avoid stty on Windows. #4590

Merged
merged 1 commit into from May 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
306 changes: 27 additions & 279 deletions lib/ruby/1.9/io/console.rb
Expand Up @@ -19,301 +19,49 @@
# we don't actually disable echo and the password is shown...we will try to
# do a better version of this in 1.7.1.

# attempt to call stty; if failure, fall back on stubbed version
require 'rbconfig'

if RbConfig::CONFIG['host_os'].downcase =~ /darwin|openbsd|freebsd|netbsd|linux/
require 'java'
require 'io/console/common'

result = begin
if RbConfig::CONFIG['host_os'].downcase =~ /darwin|openbsd|freebsd|netbsd/
require File.join(File.dirname(__FILE__), 'bsd_console')

elsif RbConfig::CONFIG['host_os'].downcase =~ /linux/
require File.join(File.dirname(__FILE__), 'linux_console')

else
raise LoadError.new("no native io/console support")
end

class IO
module LibC
begin
FD_FIELD = java.io.FileDescriptor.java_class.declared_field("fd")
FD_FIELD.accessible = true

def self.fd(io)
FD_FIELD.value io.to_java.open_file_checked.main_stream_safe.descriptor.file_descriptor
end
rescue
def self.fd(io)
io.fileno
end
end
end

def ttymode
termios = LibC::Termios.new
if LibC.tcgetattr(LibC.fd(self), termios) != 0
raise SystemCallError.new("tcgetattr", FFI.errno)
end

if block_given?
yield tmp = termios.dup
if LibC.tcsetattr(LibC.fd(self), LibC::TCSADRAIN, tmp) != 0
raise SystemCallError.new("tcsetattr", FFI.errno)
end
end
termios
end

def ttymode_yield(block, &setup)
begin
orig_termios = ttymode { |t| setup.call(t) }
block.call(self)
ensure
if orig_termios && LibC.tcsetattr(LibC.fd(self), LibC::TCSADRAIN, orig_termios) != 0
raise SystemCallError.new("tcsetattr", FFI.errno)
end
end
end

TTY_RAW = Proc.new do |t|
LibC.cfmakeraw(t)
t[:c_lflag] &= ~(LibC::ECHOE|LibC::ECHOK)
end

def raw(*, &block)
ttymode_yield(block, &TTY_RAW)
end

def raw!(*)
ttymode(&TTY_RAW)
end

TTY_COOKED = Proc.new do |t|
t[:c_iflag] |= (LibC::BRKINT|LibC::ISTRIP|LibC::ICRNL|LibC::IXON)
t[:c_oflag] |= LibC::OPOST
t[:c_lflag] |= (LibC::ECHO|LibC::ECHOE|LibC::ECHOK|LibC::ECHONL|LibC::ICANON|LibC::ISIG|LibC::IEXTEN)
end

def cooked(*, &block)
ttymode_yield(block, &TTY_COOKED)
end

def cooked!(*)
ttymode(&TTY_COOKED)
end

TTY_ECHO = LibC::ECHO | LibC::ECHOE | LibC::ECHOK | LibC::ECHONL
def echo=(echo)
ttymode do |t|
if echo
t[:c_lflag] |= TTY_ECHO
else
t[:c_lflag] &= ~TTY_ECHO
end
end
end

def echo?
(ttymode[:c_lflag] & (LibC::ECHO | LibC::ECHONL)) != 0
end

def noecho(&block)
ttymode_yield(block) { |t| t[:c_lflag] &= ~(TTY_ECHO) }
end

def getch(*)
raw do
getc
end
end

def winsize
ws = LibC::Winsize.new
if LibC.ioctl(LibC.fd(self), LibC::TIOCGWINSZ, :pointer, ws.pointer) != 0
raise SystemCallError.new("ioctl(TIOCGWINSZ)", FFI.errno)
end
[ ws[:ws_row], ws[:ws_col] ]
end

def winsize=(size)
ws = LibC::Winsize.new
if LibC.ioctl(LibC.fd(self), LibC::TIOCGWINSZ, :pointer, ws.pointer) != 0
raise SystemCallError.new("ioctl(TIOCGWINSZ)", FFI.errno)
end

ws[:ws_row] = size[0]
ws[:ws_col] = size[1]
if LibC.ioctl(LibC.fd(self), LibC::TIOCSWINSZ, :pointer, ws.pointer) != 0
raise SystemCallError.new("ioctl(TIOCSWINSZ)", FFI.errno)
end
end

def iflush
raise SystemCallError.new("tcflush(TCIFLUSH)", FFI.errno) unless LibC.tcflush(LibC.fd(self), LibC::TCIFLUSH) == 0
end

def oflush
raise SystemCallError.new("tcflush(TCOFLUSH)", FFI.errno) unless LibC.tcflush(LibC.fd(self), LibC::TCOFLUSH) == 0
end

def ioflush
raise SystemCallError.new("tcflush(TCIOFLUSH)", FFI.errno) unless LibC.tcflush(LibC.fd(self), LibC::TCIOFLUSH) == 0
end
end
true
rescue Exception => ex
warn "failed to load native console support: #{ex}" if $VERBOSE
begin
`stty 2> /dev/null`
$?.exitstatus != 0
rescue Exception
nil
end
end
# If Windows, always use the stub version
if RbConfig::CONFIG['host_os'] =~ /(mswin)|(win32)|(ming)/
require 'io/console/stub_console'
else
result = begin
old_stderr = $stderr.dup
$stderr.reopen('/dev/null')
`stty -a`
$?.exitstatus != 0
rescue Exception
nil
ensure
$stderr.reopen(old_stderr)
end
end

if !result || RbConfig::CONFIG['host_os'] =~ /(mswin)|(win32)|(ming)/
warn "io/console not supported; tty will not be manipulated" if $VERBOSE

# Windows version is always stubbed for now
class IO
def raw(*)
yield self
end

def raw!(*)
end

def cooked(*)
yield self
end

def cooked!(*)
end

def getch(*)
getc
end

def echo=(echo)
end

def echo?
true
end

def noecho
yield self
end
# If Linux or BSD, try to load the native version
if RbConfig::CONFIG['host_os'].downcase =~ /darwin|openbsd|freebsd|netbsd|linux/
begin

def winsize
[25, 80]
end
# Attempt to load the native Linux and BSD console logic
# require 'io/console/native_console'
# ready = true

def winsize=(size)
end
rescue Exception => ex

def iflush
end
warn "failed to load native console support: #{ex}" if $VERBOSE
ready = false

def oflush
end

def ioflush
end
end
elsif !IO.method_defined?:ttymode
warn "io/console on JRuby shells out to stty for most operations"

# Non-Windows assumes stty command is available
class IO
if RbConfig::CONFIG['host_os'].downcase =~ /linux/ && File.exists?("/proc/#{Process.pid}/fd")
def stty(*args)
`stty #{args.join(' ')} < /proc/#{Process.pid}/fd/#{fileno}`
end
else
def stty(*args)
`stty #{args.join(' ')}`
end
end

def raw(*)
saved = stty('-g')
stty('raw')
yield self
ensure
stty(saved)
end

def raw!(*)
stty('raw')
end

def cooked(*)
saved = stty('-g')
stty('-raw')
yield self
ensure
stty(saved)
end

def cooked!(*)
stty('-raw')
end

def getch(*)
getc
end

def echo=(echo)
stty(echo ? 'echo' : '-echo')
end

def echo?
(stty('-a') =~ / -echo /) ? false : true
end

def noecho
saved = stty('-g')
stty('-echo')
yield self
ensure
stty(saved)
end
# Native failed, try to use stty
if !ready
begin

# Not all systems return same format of stty -a output
IEEE_STD_1003_2 = '(?<rows>\d+) rows; (?<columns>\d+) columns'
UBUNTU = 'rows (?<rows>\d+); columns (?<columns>\d+)'
require 'io/console/stty_console'
ready = true

def winsize
match = stty('-a').match(/#{IEEE_STD_1003_2}|#{UBUNTU}/)
[match[:rows].to_i, match[:columns].to_i]
end
rescue Exception

def winsize=(size)
stty("rows #{size[0]} cols #{size[1]}")
end
warn "failed to load stty console support: #{ex}" if $VERBOSE
ready = false

def iflush
end

def oflush
end
end

def ioflush
end
# If still not ready, just use stubbed version
if !ready
require 'io/console/stub_console'
end

end
File renamed without changes.
35 changes: 35 additions & 0 deletions lib/ruby/1.9/io/console/common.rb
@@ -0,0 +1,35 @@
# Methods common to all backend impls
class IO
def getch(*)
raw do
getc
end
end

def getpass(prompt = nil)
wio = self == $stdin ? $stderr : self
wio.write(prompt) if prompt
begin
str = nil
noecho do
str = gets
end
ensure
puts($/)
end
str.chomp
end

module GenericReadable
def getch(*)
getc
end

def getpass(prompt = nil)
write(prompt) if prompt
str = gets.chomp
puts($/)
str
end
end
end