ruby/lib/reline/terminfo.rb

175 строки
5.4 KiB
Ruby

begin
require 'fiddle'
require 'fiddle/import'
rescue LoadError
module Reline::Terminfo
def self.curses_dl
false
end
end
end
module Reline::Terminfo
extend Fiddle::Importer
class TerminfoError < StandardError; end
def self.curses_dl_files
case RUBY_PLATFORM
when /mingw/, /mswin/
# aren't supported
[]
when /cygwin/
%w[cygncursesw-10.dll cygncurses-10.dll]
when /darwin/
%w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib]
else
%w[libncursesw.so libcursesw.so libncurses.so libcurses.so]
end
end
@curses_dl = false
def self.curses_dl
return @curses_dl unless @curses_dl == false
if RUBY_VERSION >= '3.0.0'
# Gem module isn't defined in test-all of the Ruby repository, and
# Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC.
fiddle_supports_variadic = true
elsif Fiddle.const_defined?(:VERSION,false) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1')
# Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1.
fiddle_supports_variadic = true
else
fiddle_supports_variadic = false
end
if fiddle_supports_variadic and not Fiddle.const_defined?(:TYPE_VARIADIC)
# If the libffi version is not 3.0.5 or higher, there isn't TYPE_VARIADIC.
fiddle_supports_variadic = false
end
if fiddle_supports_variadic
curses_dl_files.each do |curses_name|
result = Fiddle::Handle.new(curses_name)
rescue Fiddle::DLError
next
else
@curses_dl = result
break
end
end
@curses_dl = nil if @curses_dl == false
@curses_dl
end
end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl)
module Reline::Terminfo
dlload curses_dl
#extern 'int setupterm(char *term, int fildes, int *errret)'
@setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
#extern 'char *tigetstr(char *capname)'
@tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP)
begin
#extern 'char *tiparm(const char *str, ...)'
@tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
rescue Fiddle::DLError
# OpenBSD lacks tiparm
#extern 'char *tparm(const char *str, ...)'
@tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
end
begin
#extern 'int tigetflag(char *str)'
@tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
rescue Fiddle::DLError
# OpenBSD lacks tigetflag
#extern 'int tgetflag(char *str)'
@tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
end
begin
#extern 'int tigetnum(char *str)'
@tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
rescue Fiddle::DLError
# OpenBSD lacks tigetnum
#extern 'int tgetnum(char *str)'
@tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
end
def self.setupterm(term, fildes)
errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT)
ret = @setupterm.(term, fildes, errret_int)
errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i')
case ret
when 0 # OK
0
when -1 # ERR
case errret
when 1
raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.')
when 0
raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.')
when -1
raise TerminfoError.new('The terminfo database could not be found.')
else # unknown
-1
end
else # unknown
-2
end
end
class StringWithTiparm < String
def tiparm(*args) # for method chain
Reline::Terminfo.tiparm(self, *args)
end
end
def self.tigetstr(capname)
raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
capability = @tigetstr.(capname)
case capability.to_i
when 0, -1
raise TerminfoError, "can't find capability: #{capname}"
end
StringWithTiparm.new(capability.to_s)
end
def self.tiparm(str, *args)
new_args = []
args.each do |a|
new_args << Fiddle::TYPE_INT << a
end
@tiparm.(str, *new_args).to_s
end
def self.tigetflag(capname)
raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
flag = @tigetflag.(capname).to_i
case flag
when -1
raise TerminfoError, "not boolean capability: #{capname}"
when 0
raise TerminfoError, "can't find capability: #{capname}"
end
flag
end
def self.tigetnum(capname)
raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String)
num = @tigetnum.(capname).to_i
case num
when -2
raise TerminfoError, "not numeric capability: #{capname}"
when -1
raise TerminfoError, "can't find capability: #{capname}"
end
num
end
def self.enabled?
true
end
end if Reline::Terminfo.curses_dl
module Reline::Terminfo
def self.enabled?
false
end
end unless Reline::Terminfo.curses_dl