├── .rdoc_options ├── ext └── io │ └── console │ ├── .document │ ├── win32_vk.chksum │ ├── buildgem.sh │ ├── depend │ ├── extconf.rb │ ├── win32_vk.list │ ├── win32_vk.inc │ └── console.c ├── .document ├── docs └── io.rb ├── .github ├── release.yml ├── dependabot.yml └── workflows │ ├── sync-ruby.yml │ ├── push_gem.yml │ └── test.yml ├── tool └── gperf.sed ├── rakelib ├── epoch.rake ├── build_java.rake ├── changelogs.rake └── version.rake ├── .gitignore ├── test ├── lib │ └── helper.rb └── io │ └── console │ ├── test_ractor.rb │ └── test_io_console.rb ├── Gemfile ├── lib ├── io │ └── console │ │ └── size.rb └── ffi │ └── io │ ├── console │ ├── stub_console.rb │ ├── stty_console.rb │ ├── common.rb │ ├── native_console.rb │ ├── linux_console.rb │ └── bsd_console.rb │ └── console.rb ├── BSDL ├── README.md ├── Rakefile ├── io-console.gemspec └── COPYING /.rdoc_options: -------------------------------------------------------------------------------- 1 | --- 2 | main_page: README.md 3 | -------------------------------------------------------------------------------- /ext/io/console/.document: -------------------------------------------------------------------------------- 1 | console.c 2 | lib/size.rb 3 | -------------------------------------------------------------------------------- /ext/io/console/win32_vk.chksum: -------------------------------------------------------------------------------- 1 | src="win32_vk.list", len=3269, checksum=34076 2 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | LICENSE.txt 2 | README.md 3 | docs/ 4 | ext/ 5 | lib/io/console/size.rb 6 | -------------------------------------------------------------------------------- /docs/io.rb: -------------------------------------------------------------------------------- 1 | # Built-in class of the basis for input and output. 2 | class IO 3 | end 4 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - dependencies # Added by Dependabot 5 | -------------------------------------------------------------------------------- /tool/gperf.sed: -------------------------------------------------------------------------------- 1 | /^[a-zA-Z_0-9]*hash/,/^}/{ 2 | s/ hval = / hval = (unsigned int)/ 3 | s/ return / return (unsigned int)/ 4 | } 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /rakelib/epoch.rake: -------------------------------------------------------------------------------- 1 | task "build" => "date_epoch" 2 | 3 | task "date_epoch" do 4 | ENV["SOURCE_DATE_EPOCH"] = IO.popen(%W[git -C #{__dir__} log -1 --format=%ct], &:read).chomp 5 | end 6 | -------------------------------------------------------------------------------- /ext/io/console/buildgem.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | cd ${0%/*} 3 | trap "mv depend.$$ depend" 0 2 4 | ${RUBY-ruby} -i.$$ -pe 'exit if /^win32_vk/' depend 5 | ${GEM-gem} build io-console.gemspec 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | /doc/ 3 | /html/ 4 | /pkg/ 5 | /tmp/ 6 | /logs/ 7 | 8 | ChangeLog 9 | Gemfile.lock 10 | 11 | *.bundle 12 | *.dll 13 | *.so 14 | lib/ffi/io/console/version.rb 15 | -------------------------------------------------------------------------------- /test/lib/helper.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require "core_assertions" 3 | 4 | class Test::Unit::TestCase 5 | alias skip pend 6 | end 7 | 8 | Test::Unit::TestCase.include Test::Unit::CoreAssertions 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in io-console.gemspec 4 | gemspec 5 | 6 | group :development do 7 | gem "bundler" 8 | gem "rake" 9 | gem "rdoc" 10 | gem "test-unit" 11 | gem "test-unit-ruby-core" 12 | gem 'rake-compiler' 13 | end 14 | -------------------------------------------------------------------------------- /lib/io/console/size.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | # fallback to console window size 3 | def IO.default_console_size 4 | [ 5 | ENV["LINES"].to_i.nonzero? || 25, 6 | ENV["COLUMNS"].to_i.nonzero? || 80, 7 | ] 8 | end 9 | 10 | begin 11 | require 'io/console' 12 | rescue LoadError 13 | class << IO 14 | alias console_size default_console_size 15 | end 16 | else 17 | # returns console window size 18 | def IO.console_size 19 | console.winsize 20 | rescue NoMethodError 21 | default_console_size 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /ext/io/console/depend: -------------------------------------------------------------------------------- 1 | win32_vk.inc: win32_vk.list 2 | 3 | .list.inc: 4 | ( \ 5 | $(RUBY) -anF, \ 6 | -e 'BEGIN {puts "#define UNDEFINED_VK (unsigned short)-1"}' \ 7 | -e 'n=$$F[1] and (n.strip!; /\AVK_/=~n) and' \ 8 | -e 'puts(%[#ifndef #{n}\n# define #{n} UNDEFINED_VK\n#endif])' \ 9 | $< && \ 10 | gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k* $< \ 11 | | sed -f $(top_srcdir)/tool/gperf.sed \ 12 | ) > $(@F) 13 | 14 | .SUFFIXES: .chksum .list .inc 15 | 16 | .list.chksum: 17 | @$(RUBY) -I$(top_srcdir)/tool -rchecksum \ 18 | -e "Checksum.update(ARGV) {|k|k.copy(k.target) rescue k.make(k.target)}" \ 19 | -- --make=$(MAKE) -I$(srcdir) $( 'date_epoch' do |t| 5 | java_pkg = helper.build_java_gem 6 | end 7 | 8 | task 'release:rubygem_push' => 'release:rubygem_push:java' 9 | desc 'Push binary gems for Java platform' 10 | task 'release:rubygem_push:java' => 'build:java' do 11 | helper.push_gem(java_pkg) 12 | end 13 | 14 | def helper.build_java_gem 15 | file_name = nil 16 | sh([*gem_command, "build", "-V", "--platform=java", spec_path]) do 17 | file_name = built_gem_path 18 | pkg = File.join(base, "pkg") 19 | FileUtils.mkdir_p(pkg) 20 | FileUtils.mv(file_name, pkg) 21 | file_name = File.basename(file_name) 22 | Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}." 23 | file_name = File.join(pkg, file_name) 24 | end 25 | file_name 26 | end 27 | 28 | def helper.push_gem(path) 29 | if gem_push? 30 | Bundler.ui.confirm "Pushing #{path}" 31 | rubygem_push(path) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /.github/workflows/sync-ruby.yml: -------------------------------------------------------------------------------- 1 | name: Sync ruby 2 | on: 3 | push: 4 | branches: [master] 5 | jobs: 6 | sync: 7 | name: Sync ruby 8 | runs-on: ubuntu-latest 9 | if: ${{ github.repository_owner == 'ruby' }} 10 | steps: 11 | - uses: actions/checkout@v6 12 | 13 | - name: Create GitHub App token 14 | id: app-token 15 | uses: actions/create-github-app-token@v2 16 | with: 17 | app-id: 2060836 18 | private-key: ${{ secrets.RUBY_SYNC_DEFAULT_GEMS_PRIVATE_KEY }} 19 | owner: ruby 20 | repositories: ruby 21 | 22 | - name: Sync to ruby/ruby 23 | uses: convictional/trigger-workflow-and-wait@v1.6.5 24 | with: 25 | owner: ruby 26 | repo: ruby 27 | workflow_file_name: sync_default_gems.yml 28 | github_token: ${{ steps.app-token.outputs.token }} 29 | ref: master 30 | client_payload: | 31 | {"gem":"${{ github.event.repository.name }}","before":"${{ github.event.before }}","after":"${{ github.event.after }}"} 32 | propagate_failure: true 33 | wait_interval: 10 34 | -------------------------------------------------------------------------------- /rakelib/changelogs.rake: -------------------------------------------------------------------------------- 1 | task "build" => "changelogs" 2 | 3 | changelog = proc do |output, ver = nil, prev = nil| 4 | ver &&= Gem::Version.new(ver) 5 | range = [[prev], [ver, "HEAD"]].map {|ver, branch| ver ? "v#{ver.to_s}" : branch}.compact.join("..") 6 | IO.popen(%W[git log --format=fuller --topo-order --no-merges #{range}]) do |log| 7 | line = log.gets 8 | FileUtils.mkpath(File.dirname(output)) 9 | File.open(output, "wb") do |f| 10 | f.print "-*- coding: utf-8 -*-\n\n", line 11 | log.each_line do |line| 12 | line.sub!(/^(?!:)(?:Author|Commit)?(?:Date)?: /, ' \&') 13 | line.sub!(/ +$/, '') 14 | f.print(line) 15 | end 16 | end 17 | end 18 | end 19 | 20 | tags = IO.popen(%w[git tag -l v[0-9]*]).grep(/v(.*)/) {$1} 21 | tags.sort_by! {|tag| tag.scan(/\d+/).map(&:to_i)} 22 | tags.inject(nil) do |prev, tag| 23 | task("logs/ChangeLog-#{tag}") {|t| changelog[t.name, tag, prev]} 24 | tag 25 | end 26 | 27 | desc "Make ChangeLog" 28 | task "ChangeLog", [:ver, :prev] do |t, ver: nil, prev: tags.last| 29 | changelog[t.name, ver, prev] 30 | end 31 | 32 | changelogs = ["ChangeLog", *tags.map {|tag| "logs/ChangeLog-#{tag}"}] 33 | task "changelogs" => changelogs 34 | CLOBBER.concat(changelogs) << "logs" 35 | -------------------------------------------------------------------------------- /test/io/console/test_ractor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'test/unit' 3 | require 'rbconfig' 4 | 5 | class TestIOConsoleInRactor < Test::Unit::TestCase 6 | def test_ractor 7 | ext = "/io/console.#{RbConfig::CONFIG['DLEXT']}" 8 | path = $".find {|path| path.end_with?(ext)} 9 | assert_in_out_err(%W[-r#{path}], "#{<<~"begin;"}\n#{<<~'end;'}", ["true"], []) 10 | begin; 11 | class Ractor 12 | alias value take 13 | end unless Ractor.method_defined? :value # compat with Ruby 3.4 and olders 14 | 15 | $VERBOSE = nil 16 | r = Ractor.new do 17 | $stdout.console_mode 18 | rescue SystemCallError 19 | true 20 | rescue Ractor::UnsafeError 21 | false 22 | else 23 | true # should not success 24 | end 25 | puts r.value 26 | end; 27 | 28 | assert_in_out_err(%W[-r#{path}], "#{<<~"begin;"}\n#{<<~'end;'}", ["true"], []) 29 | begin; 30 | class Ractor 31 | alias value take 32 | end unless Ractor.method_defined? :value # compat with Ruby 3.4 and olders 33 | 34 | console = IO.console 35 | $VERBOSE = nil 36 | r = Ractor.new do 37 | IO.console 38 | end 39 | puts console.class == r.value.class 40 | end; 41 | end 42 | end if defined? Ractor 43 | -------------------------------------------------------------------------------- /BSDL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /.github/workflows/push_gem.yml: -------------------------------------------------------------------------------- 1 | name: Publish gem to rubygems.org 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | push: 13 | if: github.repository == 'ruby/io-console' 14 | runs-on: ubuntu-latest 15 | 16 | environment: 17 | name: rubygems.org 18 | url: https://rubygems.org/gems/io-console 19 | 20 | permissions: 21 | contents: write 22 | id-token: write 23 | 24 | steps: 25 | - name: Harden Runner 26 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 27 | with: 28 | egress-policy: audit 29 | 30 | - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 31 | 32 | - name: Set up Ruby 33 | uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0 34 | with: 35 | bundler-cache: true 36 | ruby-version: ruby 37 | 38 | - name: Publish to RubyGems 39 | uses: rubygems/release-gem@1c162a739e8b4cb21a676e97b087e8268d8fc40b # v1.1.2 40 | 41 | - name: Create GitHub release 42 | run: | 43 | tag_name="$(git describe --tags --abbrev=0)" 44 | gh release create "${tag_name}" --verify-tag --generate-notes 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IO.console 2 | 3 | Add console capabilities to IO instances. 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'io-console' 11 | ``` 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install io-console 20 | 21 | ## Usage 22 | 23 | ```ruby 24 | require 'io/console' 25 | 26 | IO.console -> # 27 | IO.console(sym, *args) 28 | ``` 29 | 30 | Returns a File instance opened console. 31 | 32 | If `sym` is given, it will be sent to the opened console with `args` and the result will be returned instead of the console IO itself. 33 | 34 | ## Development 35 | 36 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 37 | 38 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 39 | 40 | ## Contributing 41 | 42 | Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/io-console. 43 | 44 | ## License 45 | 46 | The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause). 47 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | require "rdoc/task" 4 | 5 | name = "io/console" 6 | 7 | if RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby" 8 | require 'rake/extensiontask' 9 | extask = Rake::ExtensionTask.new(name) do |x| 10 | x.lib_dir.sub!(%r[(?=/|\z)], "/#{RUBY_VERSION}/#{x.platform}") 11 | end 12 | task :test => :compile 13 | end 14 | 15 | ffi_version_file = "lib/ffi/#{name}/version.rb" 16 | task ffi_version_file => "#{name.tr('/', '-')}.gemspec" do |t| 17 | version = <<~RUBY 18 | class IO::ConsoleMode 19 | VERSION = "#{Bundler::GemHelper.instance.gemspec.version}" 20 | end 21 | RUBY 22 | unless (File.read(t.name) rescue nil) == version 23 | File.binwrite(t.name, version) 24 | end 25 | end 26 | 27 | task :build => ffi_version_file 28 | 29 | Rake::TestTask.new(:test) do |t| 30 | if extask 31 | t.libs = ["lib/#{RUBY_VERSION}/#{extask.platform}"] 32 | end 33 | t.libs << "test/lib" 34 | t.ruby_opts << "-rhelper" 35 | t.options = "--ignore-name=TestIO_Console#test_bad_keyword" if RUBY_ENGINE == "jruby" 36 | t.test_files = FileList["test/**/test_*.rb"] 37 | end 38 | 39 | RDoc::Task.new 40 | 41 | task :default => :test 42 | 43 | task "build" => "build:java" 44 | 45 | task "build:java" => "date_epoch" 46 | 47 | task "coverage" do 48 | cov = [] 49 | e = IO.popen([FileUtils::RUBY, "-S", "rdoc", "-C"], &:read) 50 | e.scan(/^ *# in file (?.*)\n *(?.*)|^ *(?.*\S) *# in file (?.*)/) do 51 | cov << "%s: %s\n" % $~.values_at(:loc, :code) 52 | end 53 | cov.sort! 54 | puts cov 55 | end 56 | -------------------------------------------------------------------------------- /io-console.gemspec: -------------------------------------------------------------------------------- 1 | # -*- ruby -*- 2 | _VERSION = ["", "ext/io/console/"].find do |dir| 3 | begin 4 | break File.open(File.join(__dir__, "#{dir}console.c")) {|f| 5 | f.gets("\nIO_CONSOLE_VERSION ") 6 | f.gets[/"(.+)"/, 1] 7 | } 8 | rescue Errno::ENOENT 9 | end 10 | end 11 | 12 | Gem::Specification.new do |s| 13 | s.name = "io-console" 14 | s.version = _VERSION 15 | s.summary = "Console interface" 16 | s.email = "nobu@ruby-lang.org" 17 | s.description = "add console capabilities to IO instances." 18 | s.required_ruby_version = ">= 2.6.0" 19 | s.homepage = "https://github.com/ruby/io-console" 20 | s.metadata["source_code_url"] = s.homepage 21 | s.metadata["changelog_uri"] = s.homepage + "/releases" 22 | s.authors = ["Nobu Nakada"] 23 | s.require_path = %[lib] 24 | s.files = %w[ 25 | .document 26 | BSDL 27 | COPYING 28 | README.md 29 | ext/io/console/console.c 30 | ext/io/console/extconf.rb 31 | ext/io/console/win32_vk.inc 32 | lib/io/console/size.rb 33 | ] 34 | s.extensions = %w[ext/io/console/extconf.rb] 35 | 36 | if Gem::Platform === s.platform and s.platform =~ 'java' 37 | s.files.delete_if {|f| f.start_with?("ext/")} 38 | s.extensions.clear 39 | s.require_paths.unshift('lib/ffi') 40 | s.files.concat(%w[ 41 | lib/ffi/io/console.rb 42 | lib/ffi/io/console/bsd_console.rb 43 | lib/ffi/io/console/common.rb 44 | lib/ffi/io/console/linux_console.rb 45 | lib/ffi/io/console/native_console.rb 46 | lib/ffi/io/console/stty_console.rb 47 | lib/ffi/io/console/stub_console.rb 48 | lib/ffi/io/console/version.rb 49 | ]) 50 | end 51 | 52 | s.licenses = ["Ruby", "BSD-2-Clause"] 53 | end 54 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '25 10 * * *' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | ruby-versions: 12 | if: ${{ startsWith(github.repository, 'ruby/') || github.event_name != 'schedule' }} 13 | uses: ruby/actions/.github/workflows/ruby_versions.yml@master 14 | with: 15 | engine: cruby 16 | min_version: 2.6 # from `required_ruby_version` in io-console.gemspec 17 | 18 | test: 19 | needs: ruby-versions 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} 24 | os: [ ubuntu-latest, macos-latest, windows-latest ] 25 | include: 26 | - ruby: jruby 27 | - ruby: jruby-head 28 | - ruby: truffleruby 29 | - ruby: truffleruby-head 30 | runs-on: ${{ matrix.os || 'ubuntu-latest' }} 31 | steps: 32 | - name: git config 33 | run: | 34 | git config --global core.autocrlf false 35 | git config --global core.eol lf 36 | git config --global advice.detachedHead 0 37 | - uses: actions/checkout@v6 38 | - name: Set up Ruby ${{ matrix.ruby }} 39 | uses: ruby/setup-ruby@v1 40 | with: 41 | ruby-version: ${{ matrix.ruby }} 42 | rubygems: latest 43 | - name: Experiment with a manual bundle install 44 | run: bundle install 45 | - name: Run test 46 | run: rake # Attempting without 'bundle exec' 47 | timeout-minutes: 3 48 | - id: build 49 | run: | 50 | rake build 51 | echo "pkg=${GITHUB_REPOSITORY#*/}-${PLATFORM:-${RUNNING_OS%-*}}" >> $GITHUB_OUTPUT 52 | env: 53 | RUNNING_OS: ${{matrix.os}} 54 | PLATFORM: ${{ startsWith(matrix.ruby, 'jruby') && 'java' || '' }} 55 | if: >- 56 | ${{ 57 | github.event_name == 'push' && 58 | (matrix.ruby == needs.ruby-versions.outputs.latest || matrix.ruby == 'jruby-head') 59 | }} 60 | shell: bash 61 | - name: Upload package 62 | uses: actions/upload-artifact@v6 63 | with: 64 | path: pkg/*.gem 65 | name: ${{steps.build.outputs.pkg}} 66 | if: steps.build.outputs.pkg 67 | -------------------------------------------------------------------------------- /rakelib/version.rake: -------------------------------------------------------------------------------- 1 | class << (helper = Bundler::GemHelper.instance) 2 | PATH = "ext/io/console/console.c" 3 | def update_gemspec 4 | File.open(PATH, "r+b") do |f| 5 | d = f.read 6 | if d.sub!(/^(IO_CONSOLE_VERSION\s*=\s*)".*"/) {$1 + gemspec.version.to_s.dump} 7 | f.rewind 8 | f.truncate(0) 9 | f.print(d) 10 | end 11 | end 12 | end 13 | 14 | def commit_bump 15 | sh(%W[git commit -m bump\ up\ to\ #{gemspec.version} #{PATH}]) 16 | end 17 | 18 | def version=(v) 19 | gemspec.version = v 20 | update_gemspec 21 | commit_bump 22 | end 23 | 24 | def next_dev 25 | v = gemspec.version.segments 26 | if v.size > 4 27 | v[-1] = v[-1].succ 28 | else 29 | v[2] = v[2].succ if v.size == 3 30 | v[3..-1] = "dev.1" 31 | end 32 | Gem::Version.new(v.join(".")) 33 | end 34 | 35 | def next_preview 36 | v = gemspec.version.segments 37 | if v[3] == "pre" 38 | v[-1] = v[-1].succ 39 | else 40 | v[3..-1] = "pre.1" 41 | end 42 | Gem::Version.new(v.join(".")) 43 | end 44 | 45 | def next_release 46 | if gemspec.version.prerelease? 47 | gemspec.version.release 48 | else 49 | v = gemspec.version.segments[0, 3] 50 | v[-1] = v[-1].succ 51 | Gem::Version.new(v.join(".")) 52 | end 53 | end 54 | 55 | def next_minor 56 | major, minor = gemspec.version.segments 57 | Gem::Version.new("#{major}.#{minor+1}.0") 58 | end 59 | 60 | def next_major 61 | major, = gemspec.version.segments 62 | Gem::Version.new("#{major+1}.0.0") 63 | end 64 | end 65 | 66 | desc "Bump to development" 67 | task "bump:dev" do 68 | helper.version = helper.next_dev 69 | end 70 | 71 | desc "Bump to prerelease" 72 | task "bump:pre" do 73 | helper.version = helper.next_preview 74 | end 75 | 76 | desc "Bump teeny version" 77 | task "bump:teeny" do 78 | helper.version = helper.next_release 79 | end 80 | 81 | desc "Bump minor version" 82 | task "bump:minor" do 83 | helper.version = helper.next_minor 84 | end 85 | 86 | desc "Bump major version" 87 | task "bump:major" do 88 | helper.version = helper.next_major 89 | end 90 | 91 | desc "Bump teeny version" 92 | task "bump" => "bump:teeny" 93 | 94 | task "tag" do 95 | helper.__send__(:tag_version) 96 | end 97 | -------------------------------------------------------------------------------- /ext/io/console/extconf.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | require 'mkmf' 3 | 4 | # `--target-rbconfig` compatibility for Ruby 3.3 or earlier 5 | # See https://bugs.ruby-lang.org/issues/20345 6 | MakeMakefile::RbConfig ||= ::RbConfig 7 | 8 | have_func("rb_syserr_fail_str(0, Qnil)") or 9 | have_func("rb_syserr_new_str(0, Qnil)") or 10 | abort 11 | 12 | have_func("rb_interned_str_cstr") 13 | have_func("rb_io_path", "ruby/io.h") 14 | have_func("rb_io_descriptor", "ruby/io.h") 15 | have_func("rb_io_get_write_io", "ruby/io.h") 16 | have_func("rb_io_closed_p", "ruby/io.h") 17 | have_func("rb_io_open_descriptor", "ruby/io.h") 18 | have_func("rb_ractor_local_storage_value_newkey") 19 | 20 | is_wasi = /wasi/ =~ MakeMakefile::RbConfig::CONFIG["platform"] 21 | # `ok` can be `true`, `false`, or `nil`: 22 | # * `true` : Required headers and functions available, proceed regular build. 23 | # * `false`: Required headers or functions not available, abort build. 24 | # * `nil` : Unsupported compilation target, generate dummy Makefile. 25 | # 26 | # Skip building io/console on WASI, as it does not support termios.h. 27 | ok = true if (RUBY_ENGINE == "ruby" && !is_wasi) || RUBY_ENGINE == "truffleruby" 28 | hdr = nil 29 | case 30 | when macro_defined?("_WIN32", "") 31 | # rb_w32_map_errno: 1.8.7 32 | vk_header = File.exist?("#$srcdir/win32_vk.list") ? "chksum" : "inc" 33 | vk_header = "#{'{$(srcdir)}' if $nmake == ?m}win32_vk.#{vk_header}" 34 | when hdr = %w"termios.h termio.h".find {|h| have_header(h)} 35 | have_func("cfmakeraw", hdr) 36 | when have_header(hdr = "sgtty.h") 37 | %w"stty gtty".each {|f| have_func(f, hdr)} 38 | else 39 | ok = false 40 | end if ok 41 | case ok 42 | when true 43 | have_header("sys/ioctl.h") if hdr 44 | # rb_check_hash_type: 1.9.3 45 | # rb_io_get_write_io: 1.9.1 46 | # rb_cloexec_open: 2.0.0 47 | # rb_funcallv: 2.1.0 48 | # RARRAY_CONST_PTR: 2.1.0 49 | # rb_sym2str: 2.2.0 50 | if have_macro("HAVE_RUBY_FIBER_SCHEDULER_H") 51 | $defs << "-D""HAVE_RB_IO_WAIT=1" 52 | elsif have_func("rb_scheduler_timeout") # 3.0 53 | have_func("rb_io_wait") 54 | end 55 | have_func("ttyname_r") or have_func("ttyname") 56 | create_makefile("io/console") {|conf| 57 | conf << "\n""VK_HEADER = #{vk_header}\n" 58 | } 59 | when nil 60 | File.write("Makefile", dummy_makefile($srcdir).join("")) 61 | end 62 | -------------------------------------------------------------------------------- /lib/ffi/io/console.rb: -------------------------------------------------------------------------------- 1 | # This implementation of io/console is a little hacky. It shells out to `stty` 2 | # for most operations, which does not work on Windows, in secured environments, 3 | # and so on. In addition, because on Java 6 we can't actually launch 4 | # subprocesses with tty control, stty will not actually manipulate the 5 | # controlling terminal. 6 | # 7 | # For platforms where shelling to stty does not work, most operations will 8 | # just be pass-throughs. This allows them to function, but does not actually 9 | # change any tty flags. 10 | # 11 | # Finally, since we're using stty to shell out, we can only manipulate stdin/ 12 | # stdout tty rather than manipulating whatever terminal is actually associated 13 | # with the IO we're calling against. This will produce surprising results if 14 | # anyone is actually using io/console against non-stdio ttys...but that case 15 | # seems like it would be pretty rare. 16 | # 17 | # Note: we are incorporating this into 1.7.0 since RubyGems uses io/console 18 | # when pushing gems, in order to mask the password entry. Worst case is that 19 | # we don't actually disable echo and the password is shown...we will try to 20 | # do a better version of this in 1.7.1. 21 | 22 | require 'rbconfig' 23 | 24 | require_relative 'console/version' 25 | require_relative 'console/common' 26 | 27 | case RbConfig::CONFIG['host_os'] 28 | when /darwin|openbsd|freebsd|netbsd|linux/i 29 | # If Linux or BSD, try to load the native version 30 | 31 | begin 32 | 33 | # Attempt to load the native Linux and BSD console logic 34 | require_relative 'console/native_console' 35 | 36 | rescue Exception => ex 37 | 38 | warn "failed to load native console support: #{ex}" if $VERBOSE 39 | 40 | else 41 | 42 | # Native ready. 43 | ready = true 44 | 45 | end 46 | 47 | when /mswin|win32|ming/i 48 | # If Windows, stty is not possible, always use the stub version 49 | 50 | ready = false 51 | 52 | end 53 | 54 | if ready.nil? 55 | # Native is not ready, try to use stty 56 | 57 | begin 58 | 59 | require_relative 'console/stty_console' 60 | ready = true 61 | 62 | rescue Exception => ex2 63 | 64 | warn "failed to load stty console support: #{ex2}" if $VERBOSE 65 | ready = false 66 | 67 | end 68 | end 69 | 70 | unless ready 71 | # If still not ready, just use stubbed version 72 | 73 | require_relative 'console/stub_console' 74 | end 75 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Ruby is copyrighted free software by Yukihiro Matsumoto . 2 | You can redistribute it and/or modify it under either the terms of the 3 | 2-clause BSDL (see the file BSDL), or the conditions below: 4 | 5 | 1. You may make and give away verbatim copies of the source form of the 6 | software without restriction, provided that you duplicate all of the 7 | original copyright notices and associated disclaimers. 8 | 9 | 2. You may modify your copy of the software in any way, provided that 10 | you do at least ONE of the following: 11 | 12 | a. place your modifications in the Public Domain or otherwise 13 | make them Freely Available, such as by posting said 14 | modifications to Usenet or an equivalent medium, or by allowing 15 | the author to include your modifications in the software. 16 | 17 | b. use the modified software only within your corporation or 18 | organization. 19 | 20 | c. give non-standard binaries non-standard names, with 21 | instructions on where to get the original software distribution. 22 | 23 | d. make other distribution arrangements with the author. 24 | 25 | 3. You may distribute the software in object code or binary form, 26 | provided that you do at least ONE of the following: 27 | 28 | a. distribute the binaries and library files of the software, 29 | together with instructions (in the manual page or equivalent) 30 | on where to get the original distribution. 31 | 32 | b. accompany the distribution with the machine-readable source of 33 | the software. 34 | 35 | c. give non-standard binaries non-standard names, with 36 | instructions on where to get the original software distribution. 37 | 38 | d. make other distribution arrangements with the author. 39 | 40 | 4. You may modify and include the part of the software into any other 41 | software (possibly commercial). But some files in the distribution 42 | are not written by the author, so that they are not under these terms. 43 | 44 | For the list of those files and their copying conditions, see the 45 | file LEGAL. 46 | 47 | 5. The scripts and library files supplied as input to or produced as 48 | output from the software do not automatically fall under the 49 | copyright of the software, but belong to whomever generated them, 50 | and may be sold commercially, and may be aggregated with this 51 | software. 52 | 53 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 54 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 55 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 56 | PURPOSE. 57 | -------------------------------------------------------------------------------- /lib/ffi/io/console/stty_console.rb: -------------------------------------------------------------------------------- 1 | # attempt to call stty; if failure, raise error 2 | `stty 2> /dev/null` 3 | if $?.exitstatus != 0 4 | raise "stty command returned nonzero exit status" 5 | end 6 | 7 | warn "io/console on JRuby shells out to stty for most operations" if $VERBOSE 8 | 9 | # Non-Windows assumes stty command is available 10 | class IO 11 | if RbConfig::CONFIG['host_os'].downcase =~ /linux/ && File.exists?("/proc/#{Process.pid}/fd") 12 | protected def _io_console_stty(*args) 13 | _io_console_stty_error { `stty #{args.join(' ')} < /proc/#{Process.pid}/fd/#{fileno}` } 14 | end 15 | else 16 | protected def _io_console_stty(*args) 17 | _io_console_stty_error { `stty #{args.join(' ')}` } 18 | end 19 | end 20 | 21 | protected def _io_console_stty_error 22 | # pre-check to catch non-tty filenos we can't stty against anyway 23 | raise Errno::ENOTTY, inspect if !tty? 24 | 25 | result = yield 26 | 27 | case result 28 | when /Inappropriate ioctl for device/ 29 | raise Errno.ENOTTY, inspect 30 | end 31 | 32 | result 33 | end 34 | 35 | def raw(*, min: 1, time: nil, intr: nil) 36 | saved = _io_console_stty('-g raw') 37 | yield self 38 | ensure 39 | _io_console_stty(saved) 40 | end 41 | 42 | def raw!(*) 43 | stty('raw') 44 | end 45 | 46 | def cooked(*) 47 | saved = _io_console_stty('-g', '-raw') 48 | yield self 49 | ensure 50 | _io_console_stty(saved) 51 | end 52 | 53 | def cooked!(*) 54 | _io_console_stty('-raw') 55 | end 56 | 57 | def echo=(echo) 58 | _io_console_stty(echo ? 'echo' : '-echo') 59 | end 60 | 61 | def echo? 62 | (_io_console_stty('-a') =~ / -echo /) ? false : true 63 | end 64 | 65 | def noecho 66 | saved = _io_console_stty('-g', '-echo') 67 | yield self 68 | ensure 69 | _io_console_stty(saved) 70 | end 71 | 72 | # Not all systems return same format of stty -a output 73 | IEEE_STD_1003_2 = '(?\d+) rows; (?\d+) columns' 74 | UBUNTU = 'rows (?\d+); columns (?\d+)' 75 | 76 | def winsize 77 | match = _io_console_stty('-a').match(/#{IEEE_STD_1003_2}|#{UBUNTU}/) 78 | [match[:rows].to_i, match[:columns].to_i] 79 | end 80 | 81 | def winsize=(size) 82 | size = size.to_ary unless size.kind_of?(Array) 83 | sizelen = size.size 84 | 85 | if sizelen != 2 && sizelen != 4 86 | raise ArgumentError.new("wrong number of arguments (given #{sizelen}, expected 2 or 4)") 87 | end 88 | 89 | if sizelen == 4 90 | warn "stty io/console does not support pixel winsize" 91 | end 92 | 93 | row, col, _, _ = size 94 | 95 | _io_console_stty("rows #{row} cols #{col}") 96 | end 97 | 98 | def iflush 99 | end 100 | 101 | def oflush 102 | end 103 | 104 | def ioflush 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/ffi/io/console/common.rb: -------------------------------------------------------------------------------- 1 | # Methods common to all backend impls 2 | class IO 3 | # TODO: Windows version uses "conin$" and "conout$" instead of /dev/tty 4 | def self.console(sym = nil, *args) 5 | raise TypeError, "expected Symbol, got #{sym.class}" unless sym.nil? || sym.kind_of?(Symbol) 6 | 7 | # klass = self == IO ? File : self 8 | if defined?(@console) # using ivar instead of hidden const as in MRI 9 | con = @console 10 | # MRI checks IO internals : (!RB_TYPE_P(con, T_FILE) || (!(fptr = RFILE(con)->fptr) || GetReadFD(fptr) == -1)) 11 | if !con.kind_of?(File) || (con.kind_of?(IO) && (con.closed? || !FileTest.readable?(con))) 12 | remove_instance_variable :@console 13 | con = nil 14 | end 15 | end 16 | 17 | if sym 18 | if sym == :close 19 | if con 20 | con.close 21 | remove_instance_variable :@console if defined?(@console) 22 | end 23 | return nil 24 | end 25 | end 26 | 27 | if !con 28 | if $stdin.isatty && $stdout.isatty 29 | begin 30 | con = File.open('/dev/tty', 'r+') 31 | rescue 32 | return nil 33 | end 34 | 35 | con.sync = true 36 | end 37 | 38 | @console = con 39 | end 40 | 41 | return con.send(sym, *args) if sym 42 | return con 43 | end 44 | 45 | def getch(*, **opts) 46 | raw(**opts) do 47 | getc 48 | end 49 | end 50 | 51 | def getpass(prompt = nil) 52 | wio = self == $stdin ? $stderr : self 53 | wio.write(prompt) if prompt 54 | begin 55 | str = nil 56 | noecho do 57 | str = gets 58 | end 59 | ensure 60 | puts($/) 61 | end 62 | str.chomp 63 | end 64 | 65 | def cursor 66 | raw do 67 | syswrite "\e[6n" 68 | 69 | return nil if getbyte != 0x1b 70 | return nil if getbyte != ?[.ord 71 | 72 | num = 0 73 | result = [] 74 | 75 | while b = getbyte 76 | c = b.to_i 77 | if c == ?;.ord 78 | result.push num 79 | num = 0 80 | elsif c >= ?0.ord && c <= ?9.ord 81 | num = num * 10 + c - ?0.ord 82 | #elsif opt && c == opt 83 | else 84 | last = c 85 | result.push num 86 | b = last.chr 87 | return nil unless b == ?R 88 | break 89 | end 90 | end 91 | 92 | result.map(&:pred) 93 | end 94 | end 95 | 96 | def cursor=(pos) 97 | pos = pos.to_ary if !pos.kind_of?(Array) 98 | 99 | raise "expected 2D coordinates" unless pos.size == 2 100 | 101 | x, y = pos 102 | syswrite(format("\x1b[%d;%dH", x + 1, y + 1)) 103 | 104 | self 105 | end 106 | 107 | def cursor_down(n) 108 | raw do 109 | syswrite "\x1b[#{n}B" 110 | end 111 | 112 | self 113 | end 114 | 115 | def cursor_right(n) 116 | raw do 117 | syswrite "\x1b[#{n}C" 118 | end 119 | 120 | self 121 | end 122 | 123 | def cursor_left(n) 124 | raw do 125 | syswrite "\x1b[#{n}D" 126 | end 127 | 128 | self 129 | end 130 | 131 | def cursor_up(n) 132 | raw do 133 | syswrite "\x1b[#{n}A" 134 | end 135 | 136 | self 137 | end 138 | 139 | module GenericReadable 140 | def getch(*) 141 | getc 142 | end 143 | 144 | def getpass(prompt = nil) 145 | write(prompt) if prompt 146 | str = gets.chomp 147 | puts($/) 148 | str 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /ext/io/console/win32_vk.list: -------------------------------------------------------------------------------- 1 | %{ 2 | struct vktable {short ofs; unsigned short vk;}; 3 | static const struct vktable *console_win32_vk(const char *, size_t); 4 | %} 5 | struct vktable 6 | %% 7 | LBUTTON, VK_LBUTTON 8 | RBUTTON, VK_RBUTTON 9 | CANCEL, VK_CANCEL 10 | MBUTTON, VK_MBUTTON 11 | XBUTTON1, VK_XBUTTON1 12 | XBUTTON2, VK_XBUTTON2 13 | BACK, VK_BACK 14 | TAB, VK_TAB 15 | CLEAR, VK_CLEAR 16 | RETURN, VK_RETURN 17 | SHIFT, VK_SHIFT 18 | CONTROL, VK_CONTROL 19 | MENU, VK_MENU 20 | PAUSE, VK_PAUSE 21 | CAPITAL, VK_CAPITAL 22 | KANA, VK_KANA 23 | HANGEUL, VK_HANGEUL 24 | HANGUL, VK_HANGUL 25 | JUNJA, VK_JUNJA 26 | FINAL, VK_FINAL 27 | HANJA, VK_HANJA 28 | KANJI, VK_KANJI 29 | ESCAPE, VK_ESCAPE 30 | CONVERT, VK_CONVERT 31 | NONCONVERT, VK_NONCONVERT 32 | ACCEPT, VK_ACCEPT 33 | MODECHANGE, VK_MODECHANGE 34 | SPACE, VK_SPACE 35 | PRIOR, VK_PRIOR 36 | NEXT, VK_NEXT 37 | END, VK_END 38 | HOME, VK_HOME 39 | LEFT, VK_LEFT 40 | UP, VK_UP 41 | RIGHT, VK_RIGHT 42 | DOWN, VK_DOWN 43 | SELECT, VK_SELECT 44 | PRINT, VK_PRINT 45 | EXECUTE, VK_EXECUTE 46 | SNAPSHOT, VK_SNAPSHOT 47 | INSERT, VK_INSERT 48 | DELETE, VK_DELETE 49 | HELP, VK_HELP 50 | LWIN, VK_LWIN 51 | RWIN, VK_RWIN 52 | APPS, VK_APPS 53 | SLEEP, VK_SLEEP 54 | NUMPAD0, VK_NUMPAD0 55 | NUMPAD1, VK_NUMPAD1 56 | NUMPAD2, VK_NUMPAD2 57 | NUMPAD3, VK_NUMPAD3 58 | NUMPAD4, VK_NUMPAD4 59 | NUMPAD5, VK_NUMPAD5 60 | NUMPAD6, VK_NUMPAD6 61 | NUMPAD7, VK_NUMPAD7 62 | NUMPAD8, VK_NUMPAD8 63 | NUMPAD9, VK_NUMPAD9 64 | MULTIPLY, VK_MULTIPLY 65 | ADD, VK_ADD 66 | SEPARATOR, VK_SEPARATOR 67 | SUBTRACT, VK_SUBTRACT 68 | DECIMAL, VK_DECIMAL 69 | DIVIDE, VK_DIVIDE 70 | F1, VK_F1 71 | F2, VK_F2 72 | F3, VK_F3 73 | F4, VK_F4 74 | F5, VK_F5 75 | F6, VK_F6 76 | F7, VK_F7 77 | F8, VK_F8 78 | F9, VK_F9 79 | F10, VK_F10 80 | F11, VK_F11 81 | F12, VK_F12 82 | F13, VK_F13 83 | F14, VK_F14 84 | F15, VK_F15 85 | F16, VK_F16 86 | F17, VK_F17 87 | F18, VK_F18 88 | F19, VK_F19 89 | F20, VK_F20 90 | F21, VK_F21 91 | F22, VK_F22 92 | F23, VK_F23 93 | F24, VK_F24 94 | NUMLOCK, VK_NUMLOCK 95 | SCROLL, VK_SCROLL 96 | OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL 97 | OEM_FJ_JISHO, VK_OEM_FJ_JISHO 98 | OEM_FJ_MASSHOU, VK_OEM_FJ_MASSHOU 99 | OEM_FJ_TOUROKU, VK_OEM_FJ_TOUROKU 100 | OEM_FJ_LOYA, VK_OEM_FJ_LOYA 101 | OEM_FJ_ROYA, VK_OEM_FJ_ROYA 102 | LSHIFT, VK_LSHIFT 103 | RSHIFT, VK_RSHIFT 104 | LCONTROL, VK_LCONTROL 105 | RCONTROL, VK_RCONTROL 106 | LMENU, VK_LMENU 107 | RMENU, VK_RMENU 108 | BROWSER_BACK, VK_BROWSER_BACK 109 | BROWSER_FORWARD, VK_BROWSER_FORWARD 110 | BROWSER_REFRESH, VK_BROWSER_REFRESH 111 | BROWSER_STOP, VK_BROWSER_STOP 112 | BROWSER_SEARCH, VK_BROWSER_SEARCH 113 | BROWSER_FAVORITES, VK_BROWSER_FAVORITES 114 | BROWSER_HOME, VK_BROWSER_HOME 115 | VOLUME_MUTE, VK_VOLUME_MUTE 116 | VOLUME_DOWN, VK_VOLUME_DOWN 117 | VOLUME_UP, VK_VOLUME_UP 118 | MEDIA_NEXT_TRACK, VK_MEDIA_NEXT_TRACK 119 | MEDIA_PREV_TRACK, VK_MEDIA_PREV_TRACK 120 | MEDIA_STOP, VK_MEDIA_STOP 121 | MEDIA_PLAY_PAUSE, VK_MEDIA_PLAY_PAUSE 122 | LAUNCH_MAIL, VK_LAUNCH_MAIL 123 | LAUNCH_MEDIA_SELECT, VK_LAUNCH_MEDIA_SELECT 124 | LAUNCH_APP1, VK_LAUNCH_APP1 125 | LAUNCH_APP2, VK_LAUNCH_APP2 126 | OEM_1, VK_OEM_1 127 | OEM_PLUS, VK_OEM_PLUS 128 | OEM_COMMA, VK_OEM_COMMA 129 | OEM_MINUS, VK_OEM_MINUS 130 | OEM_PERIOD, VK_OEM_PERIOD 131 | OEM_2, VK_OEM_2 132 | OEM_3, VK_OEM_3 133 | OEM_4, VK_OEM_4 134 | OEM_5, VK_OEM_5 135 | OEM_6, VK_OEM_6 136 | OEM_7, VK_OEM_7 137 | OEM_8, VK_OEM_8 138 | OEM_AX, VK_OEM_AX 139 | OEM_102, VK_OEM_102 140 | ICO_HELP, VK_ICO_HELP 141 | ICO_00, VK_ICO_00 142 | PROCESSKEY, VK_PROCESSKEY 143 | ICO_CLEAR, VK_ICO_CLEAR 144 | PACKET, VK_PACKET 145 | OEM_RESET, VK_OEM_RESET 146 | OEM_JUMP, VK_OEM_JUMP 147 | OEM_PA1, VK_OEM_PA1 148 | OEM_PA2, VK_OEM_PA2 149 | OEM_PA3, VK_OEM_PA3 150 | OEM_WSCTRL, VK_OEM_WSCTRL 151 | OEM_CUSEL, VK_OEM_CUSEL 152 | OEM_ATTN, VK_OEM_ATTN 153 | OEM_FINISH, VK_OEM_FINISH 154 | OEM_COPY, VK_OEM_COPY 155 | OEM_AUTO, VK_OEM_AUTO 156 | OEM_ENLW, VK_OEM_ENLW 157 | OEM_BACKTAB, VK_OEM_BACKTAB 158 | ATTN, VK_ATTN 159 | CRSEL, VK_CRSEL 160 | EXSEL, VK_EXSEL 161 | EREOF, VK_EREOF 162 | PLAY, VK_PLAY 163 | ZOOM, VK_ZOOM 164 | NONAME, VK_NONAME 165 | PA1, VK_PA1 166 | OEM_CLEAR, VK_OEM_CLEAR 167 | -------------------------------------------------------------------------------- /lib/ffi/io/console/native_console.rb: -------------------------------------------------------------------------------- 1 | # Load appropriate native bits for BSD or Linux 2 | case RbConfig::CONFIG['host_os'].downcase 3 | when /darwin|openbsd|freebsd|netbsd/ 4 | require_relative 'bsd_console' 5 | when /linux/ 6 | require_relative 'linux_console' 7 | else 8 | raise LoadError.new("no native io/console support") 9 | end 10 | 11 | # Common logic that uses native calls for console 12 | class IO 13 | def ttymode 14 | termios = LibC::Termios.new 15 | if LibC.tcgetattr(self.fileno, termios) != 0 16 | raise SystemCallError.new(respond_to?(:path) ? path : "tcgetattr", FFI.errno) 17 | end 18 | 19 | if block_given? 20 | yield tmp = termios.dup 21 | if LibC.tcsetattr(self.fileno, LibC::TCSANOW, tmp) != 0 22 | raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno) 23 | end 24 | end 25 | termios 26 | end 27 | private :ttymode 28 | 29 | def ttymode_yield(block, **opts, &setup) 30 | begin 31 | orig_termios = ttymode { |t| setup.call(t, **opts) } 32 | block.call(self) 33 | ensure 34 | if orig_termios && LibC.tcsetattr(self.fileno, LibC::TCSANOW, orig_termios) != 0 35 | raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno) 36 | end 37 | end 38 | end 39 | private :ttymode_yield 40 | 41 | TTY_RAW = Proc.new do |t, min: 1, time: nil, intr: nil| 42 | LibC.cfmakeraw(t) 43 | t[:c_lflag] &= ~(LibC::ECHOE|LibC::ECHOK) 44 | if min >= 0 45 | t[:c_cc][LibC::VMIN] = min 46 | end 47 | t[:c_cc][LibC::VTIME] = (time&.to_i || 0) * 10 48 | if intr 49 | t[:c_iflag] |= LibC::BRKINT 50 | t[:c_lflag] |= LibC::ISIG 51 | t[:c_oflag] |= LibC::OPOST 52 | end 53 | end 54 | 55 | def raw(*, min: 1, time: nil, intr: nil, &block) 56 | ttymode_yield(block, min:, time:, intr:, &TTY_RAW) 57 | end 58 | 59 | def raw!(*) 60 | ttymode(&TTY_RAW) 61 | end 62 | 63 | TTY_COOKED = Proc.new do |t| 64 | t[:c_iflag] |= (LibC::BRKINT|LibC::ISTRIP|LibC::ICRNL|LibC::IXON) 65 | t[:c_oflag] |= LibC::OPOST 66 | t[:c_lflag] |= (LibC::ECHO|LibC::ECHOE|LibC::ECHOK|LibC::ECHONL|LibC::ICANON|LibC::ISIG|LibC::IEXTEN) 67 | end 68 | 69 | def cooked(*, &block) 70 | ttymode_yield(block, &TTY_COOKED) 71 | end 72 | 73 | def cooked!(*) 74 | ttymode(&TTY_COOKED) 75 | end 76 | 77 | TTY_ECHO = LibC::ECHO | LibC::ECHOE | LibC::ECHOK | LibC::ECHONL 78 | def echo=(echo) 79 | ttymode do |t| 80 | if echo 81 | t[:c_lflag] |= TTY_ECHO 82 | else 83 | t[:c_lflag] &= ~TTY_ECHO 84 | end 85 | end 86 | end 87 | 88 | def echo? 89 | (ttymode[:c_lflag] & (LibC::ECHO | LibC::ECHONL)) != 0 90 | end 91 | 92 | def noecho(&block) 93 | ttymode_yield(block) { |t| t[:c_lflag] &= ~(TTY_ECHO) } 94 | end 95 | 96 | def winsize 97 | ws = LibC::Winsize.new 98 | if LibC.ioctl(self.fileno, LibC::TIOCGWINSZ, :pointer, ws.pointer) != 0 99 | raise SystemCallError.new("ioctl(TIOCGWINSZ)", FFI.errno) 100 | end 101 | [ ws[:ws_row], ws[:ws_col] ] 102 | end 103 | 104 | def winsize=(size) 105 | size = size.to_ary unless size.kind_of?(Array) 106 | sizelen = size.size 107 | 108 | if sizelen != 2 && sizelen != 4 109 | raise ArgumentError.new("wrong number of arguments (given #{sizelen}, expected 2 or 4)") 110 | end 111 | 112 | row, col, xpixel, ypixel = size 113 | 114 | ws = LibC::Winsize.new 115 | if LibC.ioctl(self.fileno, LibC::TIOCGWINSZ, :pointer, ws.pointer) != 0 116 | raise SystemCallError.new("ioctl(TIOCGWINSZ)", FFI.errno) 117 | end 118 | 119 | ws[:ws_row] = row 120 | ws[:ws_col] = col 121 | ws[:ws_xpixel] = xpixel&.to_i || 0 122 | ws[:ws_ypixel] = ypixel&.to_i || 0 123 | 124 | if LibC.ioctl(self.fileno, LibC::TIOCSWINSZ, :pointer, ws.pointer) != 0 125 | raise SystemCallError.new("ioctl(TIOCSWINSZ)", FFI.errno) 126 | end 127 | end 128 | 129 | def iflush 130 | raise SystemCallError.new("tcflush(TCIFLUSH)", FFI.errno) unless LibC.tcflush(self.fileno, LibC::TCIFLUSH) == 0 131 | end 132 | 133 | def oflush 134 | raise SystemCallError.new("tcflush(TCOFLUSH)", FFI.errno) unless LibC.tcflush(self.fileno, LibC::TCOFLUSH) == 0 135 | end 136 | 137 | def ioflush 138 | raise SystemCallError.new("tcflush(TCIOFLUSH)", FFI.errno) unless LibC.tcflush(self.fileno, LibC::TCIOFLUSH) == 0 139 | end 140 | end 141 | -------------------------------------------------------------------------------- /lib/ffi/io/console/linux_console.rb: -------------------------------------------------------------------------------- 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::LibC 10 | extend FFI::Library 11 | ffi_lib FFI::Library::LIBC 12 | 13 | typedef :uint, :tcflag_t 14 | typedef :uint, :speed_t 15 | 16 | VINTR = 0 17 | VQUIT = 1 18 | VERASE = 2 19 | VKILL = 3 20 | VEOF = 4 21 | VTIME = 5 22 | VMIN = 6 23 | VSWTC = 7 24 | VSTART = 8 25 | VSTOP = 9 26 | VSUSP = 10 27 | VEOL = 11 28 | VREPRINT = 12 29 | VDISCARD = 13 30 | VWERASE = 14 31 | VLNEXT = 15 32 | VEOL2 = 16 33 | 34 | # c_iflag bits 35 | IGNBRK = 0000001 36 | BRKINT = 0000002 37 | IGNPAR = 0000004 38 | PARMRK = 0000010 39 | INPCK = 0000020 40 | ISTRIP = 0000040 41 | INLCR = 0000100 42 | IGNCR = 0000200 43 | ICRNL = 0000400 44 | IUCLC = 0001000 45 | IXON = 0002000 46 | IXANY = 0004000 47 | IXOFF = 0010000 48 | IMAXBEL = 0020000 49 | IUTF8 = 0040000 50 | 51 | # c_oflag bits 52 | OPOST = 0000001 53 | OLCUC = 0000002 54 | ONLCR = 0000004 55 | OCRNL = 0000010 56 | ONOCR = 0000020 57 | ONLRET = 0000040 58 | OFILL = 0000100 59 | OFDEL = 0000200 60 | NLDLY = 0000400 61 | NL0 = 0000000 62 | NL1 = 0000400 63 | CRDLY = 0003000 64 | CR0 = 0000000 65 | CR1 = 0001000 66 | CR2 = 0002000 67 | CR3 = 0003000 68 | TABDLY = 0014000 69 | TAB0 = 0000000 70 | TAB1 = 0004000 71 | TAB2 = 0010000 72 | TAB3 = 0014000 73 | XTABS = 0014000 74 | BSDLY = 0020000 75 | BS0 = 0000000 76 | BS1 = 0020000 77 | VTDLY = 0040000 78 | VT0 = 0000000 79 | VT1 = 0040000 80 | FFDLY = 0100000 81 | FF0 = 0000000 82 | FF1 = 0100000 83 | 84 | # c_cflag bit meaning 85 | CBAUD = 0010017 86 | B0 = 0000000 87 | B50 = 0000001 88 | B75 = 0000002 89 | B110 = 0000003 90 | B134 = 0000004 91 | B150 = 0000005 92 | B200 = 0000006 93 | B300 = 0000007 94 | B600 = 0000010 95 | B1200 = 0000011 96 | B1800 = 0000012 97 | B2400 = 0000013 98 | B4800 = 0000014 99 | B9600 = 0000015 100 | B19200 = 0000016 101 | B38400 = 0000017 102 | EXTA = B19200 103 | EXTB = B38400 104 | CSIZE = 0000060 105 | CS5 = 0000000 106 | CS6 = 0000020 107 | CS7 = 0000040 108 | CS8 = 0000060 109 | CSTOPB = 0000100 110 | CREAD = 0000200 111 | PARENB = 0000400 112 | PARODD = 0001000 113 | HUPCL = 0002000 114 | CLOCAL = 0004000 115 | CBAUDEX = 0010000 116 | BOTHER = 0010000 117 | B57600 = 0010001 118 | B115200 = 0010002 119 | B230400 = 0010003 120 | B460800 = 0010004 121 | B500000 = 0010005 122 | B576000 = 0010006 123 | B921600 = 0010007 124 | B1000000 = 0010010 125 | B1152000 = 0010011 126 | B1500000 = 0010012 127 | B2000000 = 0010013 128 | B2500000 = 0010014 129 | B3000000 = 0010015 130 | B3500000 = 0010016 131 | B4000000 = 0010017 132 | CIBAUD = 002003600000 133 | CMSPAR = 010000000000 134 | CRTSCTS = 020000000000 135 | 136 | IBSHIFT = 16 137 | 138 | # c_lflag bits 139 | ISIG = 0000001 140 | ICANON = 0000002 141 | XCASE = 0000004 142 | ECHO = 0000010 143 | ECHOE = 0000020 144 | ECHOK = 0000040 145 | ECHONL = 0000100 146 | NOFLSH = 0000200 147 | TOSTOP = 0000400 148 | ECHOCTL = 0001000 149 | ECHOPRT = 0002000 150 | ECHOKE = 0004000 151 | FLUSHO = 0010000 152 | PENDIN = 0040000 153 | IEXTEN = 0100000 154 | 155 | # tcflow() and TCXONC use these 156 | TCOOFF = 0 157 | TCOON = 1 158 | TCIOFF = 2 159 | TCION = 3 160 | 161 | # tcflush() and TCFLSH use these 162 | TCIFLUSH = 0 163 | TCOFLUSH = 1 164 | TCIOFLUSH = 2 165 | 166 | # tcsetattr uses these 167 | TCSANOW = 0 168 | TCSADRAIN = 1 169 | TCSAFLUSH = 2 170 | 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 | TIOCGWINSZ = 0x5413 193 | 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 | end 205 | -------------------------------------------------------------------------------- /lib/ffi/io/console/bsd_console.rb: -------------------------------------------------------------------------------- 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::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 16 | else 17 | typedef :uint, :tcflag_t 18 | typedef :uint, :speed_t 19 | end 20 | 21 | # Special Control Characters 22 | VEOF = 0 # ICANON 23 | VEOL = 1 # ICANON 24 | VEOL2 = 2 # ICANON together with IEXTEN 25 | VERASE = 3 # ICANON 26 | VWERASE = 4 # ICANON together with IEXTEN 27 | VKILL = 5 # ICANON 28 | VREPRINT = 6 # ICANON together with IEXTEN 29 | VINTR = 8 # ISIG 30 | VQUIT = 9 # ISIG 31 | VSUSP = 10 # ISIG 32 | VDSUSP = 11 # ISIG together with IEXTEN 33 | VSTART = 12 # IXON, IXOFF 34 | VSTOP = 13 # IXON, IXOFF 35 | VLNEXT = 14 # IEXTEN 36 | VDISCARD = 15 # IEXTEN 37 | VMIN = 16 # !ICANON 38 | VTIME = 17 # !ICANON 39 | VSTATUS = 18 # ICANON together with IEXTEN 40 | NCCS = 20 41 | 42 | # Input flags - software input processing 43 | IGNBRK = 0x00000001 # ignore BREAK condition 44 | BRKINT = 0x00000002 # map BREAK to SIGINTR 45 | IGNPAR = 0x00000004 # ignore (discard) parity errors 46 | PARMRK = 0x00000008 # mark parity and framing errors 47 | INPCK = 0x00000010 # enable checking of parity errors 48 | ISTRIP = 0x00000020 # strip 8th bit off chars 49 | INLCR = 0x00000040 # map NL into CR 50 | IGNCR = 0x00000080 # ignore CR 51 | ICRNL = 0x00000100 # map CR to NL (ala CRMOD) 52 | IXON = 0x00000200 # enable output flow control 53 | IXOFF = 0x00000400 # enable input flow control 54 | IXANY = 0x00000800 # any char will restart after stop 55 | IMAXBEL = 0x00002000 # ring bell on input queue full 56 | IUTF8 = 0x00004000 # maintain state for UTF-8 VERASE 57 | 58 | # Output flags - software output processing 59 | OPOST = 0x00000001 # enable following output processing 60 | ONLCR = 0x00000002 # map NL to CR-NL (ala CRMOD) 61 | OXTABS = 0x00000004 # expand tabs to spaces 62 | ONOEOT = 0x00000008 # discard EOT's (^D) on output) 63 | OCRNL = 0x00000010 # map CR to NL on output 64 | ONOCR = 0x00000020 # no CR output at column 0 65 | ONLRET = 0x00000040 # NL performs CR function 66 | 67 | # Control flags - hardware control of terminal 68 | CIGNORE = 0x00000001 # ignore control flags 69 | CSIZE = 0x00000300 # character size mask 70 | CS5 = 0x00000000 # 5 bits (pseudo) 71 | CS6 = 0x00000100 # 6 bits 72 | CS7 = 0x00000200 # 7 bits 73 | CS8 = 0x00000300 # 8 bits 74 | CSTOPB = 0x00000400 # send 2 stop bits 75 | CREAD = 0x00000800 # enable receiver 76 | PARENB = 0x00001000 # parity enable 77 | PARODD = 0x00002000 # odd parity, else even 78 | HUPCL = 0x00004000 # hang up on last close 79 | CLOCAL = 0x00008000 # ignore modem status lines 80 | CCTS_OFLOW = 0x00010000 # CTS flow control of output 81 | CRTS_IFLOW = 0x00020000 # RTS flow control of input 82 | CDTR_IFLOW = 0x00040000 # DTR flow control of input 83 | CDSR_OFLOW = 0x00080000 # DSR flow control of output 84 | CCAR_OFLOW = 0x00100000 # DCD flow control of output 85 | CRTSCTS = CCTS_OFLOW | CRTS_IFLOW 86 | MDMBUF = 0x00100000 # old name for CCAR_OFLOW 87 | 88 | 89 | # "Local" flags - dumping ground for other state 90 | ECHOKE = 0x00000001 # visual erase for line kill 91 | ECHOE = 0x00000002 # visually erase chars 92 | ECHOK = 0x00000004 # echo NL after line kill 93 | ECHO = 0x00000008 # enable echoing 94 | ECHONL = 0x00000010 # echo NL even if ECHO is off 95 | ECHOPRT = 0x00000020 # visual erase mode for hardcopy 96 | ECHOCTL = 0x00000040 # echo control chars as ^(Char) 97 | ISIG = 0x00000080 # enable signals INTR, QUIT, [D]SUSP 98 | ICANON = 0x00000100 # canonicalize input lines 99 | ALTWERASE = 0x00000200 # use alternate WERASE algorithm 100 | IEXTEN = 0x00000400 # enable DISCARD and LNEXT 101 | EXTPROC = 0x00000800 # external processing 102 | TOSTOP = 0x00400000 # stop background jobs from output 103 | FLUSHO = 0x00800000 # output being flushed (state) 104 | NOKERNINFO = 0x02000000 # no kernel output from VSTATUS 105 | PENDIN = 0x20000000 # XXX retype pending input (state) 106 | NOFLSH = 0x80000000 # don't flush after interrupt 107 | 108 | 109 | # Commands passed to tcsetattr() for setting the termios structure. 110 | TCSANOW = 0 # make change immediate 111 | TCSADRAIN = 1 # drain output, then change 112 | TCSAFLUSH = 2 # drain output, flush input 113 | TCSASOFT = 0x10 # flag - don't alter h.w. state 114 | 115 | 116 | TCIFLUSH = 1 117 | TCOFLUSH = 2 118 | TCIOFLUSH = 3 119 | TCOOFF = 1 120 | TCOON = 2 121 | TCIOFF = 3 122 | TCION = 4 123 | 124 | IOCPARM_MASK = 0x1fff 125 | IOC_OUT = 0x40000000 126 | 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 172 | end 173 | -------------------------------------------------------------------------------- /test/io/console/test_io_console.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | begin 3 | require 'io/console' 4 | require 'test/unit' 5 | require 'pty' 6 | rescue LoadError 7 | end 8 | 9 | class TestIO_Console < Test::Unit::TestCase 10 | HOST_OS = RbConfig::CONFIG['host_os'] 11 | private def host_os?(os) 12 | HOST_OS =~ os 13 | end 14 | 15 | begin 16 | PATHS = $LOADED_FEATURES.grep(%r"/io/console(?:\.#{RbConfig::CONFIG['DLEXT']}|\.rb|/\w+\.rb)\z") {$`} 17 | rescue Encoding::CompatibilityError 18 | $stderr.puts "test_io_console.rb debug" 19 | $LOADED_FEATURES.each{|path| $stderr.puts [path, path.encoding].inspect} 20 | raise 21 | end 22 | PATHS.uniq! 23 | INCLUDE_OPTS = "-I#{PATHS.join(File::PATH_SEPARATOR)}" 24 | 25 | # FreeBSD seems to hang on TTOU when running parallel tests 26 | # tested on FreeBSD 11.x. 27 | # 28 | # I suspect that it occurs only when having no TTY. 29 | # (Parallel mode runs tests in child processes, so I guess 30 | # they has no TTY.) 31 | # But it does not occur in `make test-all > /dev/null`, so 32 | # there should be an additional factor, I guess. 33 | def set_winsize_setup 34 | @old_ttou = trap(:TTOU, 'IGNORE') if host_os?(/freebsd/) 35 | end 36 | 37 | def set_winsize_teardown 38 | trap(:TTOU, @old_ttou) if defined?(@old_ttou) and @old_ttou 39 | end 40 | 41 | exceptions = %w[ENODEV ENOTTY EBADF ENXIO].map {|e| 42 | Errno.const_get(e) if Errno.const_defined?(e) 43 | } 44 | exceptions.compact! 45 | FailedPathExceptions = (exceptions unless exceptions.empty?) 46 | 47 | def test_failed_path 48 | File.open(IO::NULL) do |f| 49 | e = assert_raise(*FailedPathExceptions) do 50 | f.echo? 51 | end 52 | assert_include(e.message, IO::NULL) 53 | end 54 | end if FailedPathExceptions 55 | 56 | def test_bad_keyword 57 | assert_raise_with_message(ArgumentError, /unknown keyword:.*bad/) do 58 | File.open(IO::NULL) do |f| 59 | f.raw(bad: 0) 60 | end 61 | end 62 | end 63 | end 64 | 65 | defined?(PTY) and defined?(IO.console) and TestIO_Console.class_eval do 66 | Bug6116 = '[ruby-dev:45309]' 67 | 68 | def test_raw 69 | helper {|m, s| 70 | s.print "abc\n" 71 | assert_equal("abc\r\n", m.gets) 72 | assert_send([s, :echo?]) 73 | s.raw { 74 | assert_not_send([s, :echo?], Bug6116) 75 | s.print "def\n" 76 | assert_equal("def\n", m.gets) 77 | } 78 | assert_send([s, :echo?]) 79 | s.print "ghi\n" 80 | assert_equal("ghi\r\n", m.gets) 81 | } 82 | end 83 | 84 | def test_raw_minchar 85 | q = Thread::Queue.new 86 | helper {|m, s| 87 | len = 0 88 | assert_equal([nil, 0], [s.getch(min: 0), len]) 89 | main = Thread.current 90 | go = false 91 | th = Thread.start { 92 | q.pop 93 | sleep 0.01 until main.stop? 94 | len += 1 95 | m.print("a") 96 | m.flush 97 | sleep 0.01 until go and main.stop? 98 | len += 10 99 | m.print("1234567890") 100 | m.flush 101 | } 102 | begin 103 | sleep 0.1 104 | q.push(1) 105 | assert_equal(["a", 1], [s.getch(min: 1), len]) 106 | go = true 107 | assert_equal(["1", 11], [s.getch, len]) 108 | ensure 109 | th.join 110 | end 111 | } 112 | end 113 | 114 | def test_raw_timeout 115 | helper {|m, s| 116 | len = 0 117 | assert_equal([nil, 0], [s.getch(min: 0, time: 0.1), len]) 118 | main = Thread.current 119 | th = Thread.start { 120 | sleep 0.01 until main.stop? 121 | len += 2 122 | m.print("ab") 123 | } 124 | begin 125 | assert_equal(["a", 2], [s.getch(min: 1, time: 1), len]) 126 | assert_equal(["b", 2], [s.getch(time: 1), len]) 127 | ensure 128 | th.join 129 | end 130 | } 131 | end 132 | 133 | def test_raw! 134 | helper {|m, s| 135 | s.raw! 136 | s.print "foo\n" 137 | assert_equal("foo\n", m.gets) 138 | } 139 | end 140 | 141 | def test_cooked 142 | helper {|m, s| 143 | assert_send([s, :echo?]) 144 | s.raw { 145 | s.print "abc\n" 146 | assert_equal("abc\n", m.gets) 147 | assert_not_send([s, :echo?], Bug6116) 148 | s.cooked { 149 | assert_send([s, :echo?]) 150 | s.print "def\n" 151 | assert_equal("def\r\n", m.gets) 152 | } 153 | assert_not_send([s, :echo?], Bug6116) 154 | } 155 | assert_send([s, :echo?]) 156 | s.print "ghi\n" 157 | assert_equal("ghi\r\n", m.gets) 158 | } 159 | end 160 | 161 | def test_echo 162 | helper {|m, s| 163 | assert_send([s, :echo?]) 164 | m.print "a" 165 | assert_equal("a", m.readpartial(10)) 166 | } 167 | end 168 | 169 | def test_noecho 170 | helper {|m, s| 171 | s.noecho { 172 | assert_not_send([s, :echo?]) 173 | m.print "a" 174 | sleep 0.1 175 | } 176 | m.print "b" 177 | assert_equal("b", m.readpartial(10)) 178 | } 179 | end 180 | 181 | def test_noecho2 182 | helper {|m, s| 183 | assert_send([s, :echo?]) 184 | m.print "a\n" 185 | sleep 0.1 186 | s.print "b\n" 187 | sleep 0.1 188 | assert_equal("a\r\nb\r\n", m.gets + m.gets) 189 | assert_equal("a\n", s.gets) 190 | s.noecho { 191 | assert_not_send([s, :echo?]) 192 | m.print "a\n" 193 | s.print "b\n" 194 | assert_equal("b\r\n", m.gets) 195 | assert_equal("a\n", s.gets) 196 | } 197 | assert_send([s, :echo?]) 198 | m.print "a\n" 199 | sleep 0.1 200 | s.print "b\n" 201 | sleep 0.1 202 | assert_equal("a\r\nb\r\n", m.gets + m.gets) 203 | assert_equal("a\n", s.gets) 204 | } 205 | end 206 | 207 | def test_setecho 208 | helper {|m, s| 209 | assert_send([s, :echo?]) 210 | s.echo = false 211 | m.print "a" 212 | sleep 0.1 213 | s.echo = true 214 | m.print "b" 215 | assert_equal("b", m.readpartial(10)) 216 | } 217 | end 218 | 219 | def test_setecho2 220 | helper {|m, s| 221 | assert_send([s, :echo?]) 222 | m.print "a\n" 223 | sleep 0.1 224 | s.print "b\n" 225 | sleep 0.1 226 | assert_equal("a\r\nb\r\n", m.gets + m.gets) 227 | assert_equal("a\n", s.gets) 228 | s.echo = false 229 | assert_not_send([s, :echo?]) 230 | m.print "a\n" 231 | s.print "b\n" 232 | assert_equal("b\r\n", m.gets) 233 | assert_equal("a\n", s.gets) 234 | s.echo = true 235 | assert_send([s, :echo?]) 236 | m.print "a\n" 237 | sleep 0.1 238 | s.print "b\n" 239 | sleep 0.1 240 | assert_equal("a\r\nb\r\n", m.gets + m.gets) 241 | assert_equal("a\n", s.gets) 242 | } 243 | end 244 | 245 | def test_getpass 246 | run_pty("p IO.console.getpass('> ')") do |r, w| 247 | assert_equal("> ", r.readpartial(10)) 248 | sleep 0.1 249 | w.print "asdf\n" 250 | sleep 0.1 251 | assert_equal("\r\n", r.gets) 252 | assert_equal("\"asdf\"", r.gets.chomp) 253 | end 254 | 255 | run_pty("p IO.console.getpass('> ')") do |r, w| 256 | assert_equal("> ", r.readpartial(10)) 257 | sleep 0.1 258 | w.print "asdf\C-D\C-D" 259 | sleep 0.1 260 | assert_equal("\r\n", r.gets) 261 | assert_equal("\"asdf\"", r.gets.chomp) 262 | end 263 | 264 | run_pty("$VERBOSE, $/ = nil, '.'; p IO.console.getpass('> ')") do |r, w| 265 | assert_equal("> ", r.readpartial(10)) 266 | sleep 0.1 267 | w.print "asdf\n" 268 | sleep 0.1 269 | assert_equal("\r\n", r.gets) 270 | assert_equal("\"asdf\"", r.gets.chomp) 271 | end 272 | end 273 | 274 | def test_iflush 275 | helper {|m, s| 276 | m.print "a" 277 | s.iflush 278 | m.print "b\n" 279 | m.flush 280 | assert_equal("b\n", s.gets) 281 | } 282 | end 283 | 284 | def test_oflush 285 | helper {|m, s| 286 | s.print "a" 287 | s.oflush # oflush may be issued after "a" is already sent. 288 | s.print "b" 289 | s.flush 290 | sleep 0.1 291 | assert_include(["b", "ab"], m.readpartial(10)) 292 | } 293 | end 294 | 295 | def test_ioflush 296 | helper {|m, s| 297 | m.print "a" 298 | s.ioflush 299 | m.print "b\n" 300 | m.flush 301 | assert_equal("b\n", s.gets) 302 | } 303 | end 304 | 305 | def test_ioflush2 306 | helper {|m, s| 307 | s.print "a" 308 | s.ioflush # ioflush may be issued after "a" is already sent. 309 | s.print "b" 310 | s.flush 311 | sleep 0.1 312 | assert_include(["b", "ab"], m.readpartial(10)) 313 | } 314 | end 315 | 316 | def test_winsize 317 | helper {|m, s| 318 | begin 319 | assert_equal([0, 0], s.winsize) 320 | rescue Errno::EINVAL # OpenSolaris 2009.06 TIOCGWINSZ causes Errno::EINVAL before TIOCSWINSZ. 321 | else 322 | assert_equal([80, 25], s.winsize = [80, 25]) 323 | assert_equal([80, 25], s.winsize) 324 | #assert_equal([80, 25], m.winsize) 325 | assert_equal([100, 40], m.winsize = [100, 40]) 326 | #assert_equal([100, 40], s.winsize) 327 | assert_equal([100, 40], m.winsize) 328 | end 329 | } 330 | end 331 | 332 | def test_set_winsize_invalid_dev 333 | set_winsize_setup 334 | [IO::NULL, __FILE__].each do |path| 335 | open(path) do |io| 336 | begin 337 | s = io.winsize 338 | rescue SystemCallError => e 339 | assert_raise(e.class) {io.winsize = [0, 0]} 340 | else 341 | assert(false, "winsize on #{path} succeed: #{s.inspect}") 342 | end 343 | assert_raise(ArgumentError) {io.winsize = [0, 0, 0]} 344 | end 345 | end 346 | ensure 347 | set_winsize_teardown 348 | end 349 | 350 | def test_cursor_position 351 | run_pty("#{<<~"begin;"}\n#{<<~'end;'}") do |r, w, _| 352 | begin; 353 | con = IO.console 354 | p con.cursor 355 | con.cursor_down(3); con.puts 356 | con.cursor_right(4); con.puts 357 | con.cursor_left(2); con.puts 358 | con.cursor_up(1); con.puts 359 | end; 360 | assert_equal("\e[6n", r.readpartial(5)) 361 | w.print("\e[12;34R"); w.flush 362 | assert_equal([11, 33], eval(r.gets)) 363 | assert_equal("\e[3B", r.gets.chomp) 364 | assert_equal("\e[4C", r.gets.chomp) 365 | assert_equal("\e[2D", r.gets.chomp) 366 | assert_equal("\e[1A", r.gets.chomp) 367 | end 368 | end 369 | 370 | def assert_ctrl(expect, cc, r, w) 371 | sleep 0.1 372 | w.print cc 373 | w.flush 374 | result = EnvUtil.timeout(3) {r.gets} 375 | if result 376 | case cc.chr 377 | when "\C-A".."\C-_" 378 | cc = "^" + (cc.ord | 0x40).chr 379 | when "\C-?" 380 | cc = "^?" 381 | end 382 | result.sub!(cc, "") 383 | end 384 | assert_equal(expect, result.chomp) 385 | end 386 | 387 | def test_intr 388 | # This test fails randomly on FreeBSD 13 389 | # http://rubyci.s3.amazonaws.com/freebsd13/ruby-master/log/20220304T163001Z.fail.html.gz 390 | # 391 | # 1) Failure: 392 | # TestIO_Console#test_intr [/usr/home/chkbuild/chkbuild/tmp/build/20220304T163001Z/ruby/test/io/console/test_io_console.rb:387]: 393 | # <"25"> expected but was 394 | # <"-e:12:in `p': \e[1mexecution expired (\e[1;4mTimeout::Error\e[m\e[1m)\e[m">. 395 | omit if host_os?(/freebsd/) 396 | 397 | run_pty("#{<<~"begin;"}\n#{<<~'end;'}") do |r, w, _| 398 | begin; 399 | require 'timeout' 400 | STDOUT.puts `stty -a`.scan(/\b\w+ *= *\^.;/), "" 401 | STDOUT.flush 402 | con = IO.console 403 | while c = con.getch 404 | p c.ord 405 | p con.getch(intr: false).ord 406 | begin 407 | p Timeout.timeout(1) {con.getch(intr: true)}.ord 408 | rescue Timeout::Error, Interrupt => e 409 | p e 410 | end 411 | end 412 | end; 413 | ctrl = {} 414 | r.each do |l| 415 | break unless /^(\w+) *= *\^(\\?.)/ =~ l 416 | ctrl[$1] = eval("?\\C-#$2") 417 | end 418 | if cc = ctrl["intr"] 419 | assert_ctrl("#{cc.ord}", cc, r, w) 420 | assert_ctrl("#{cc.ord}", cc, r, w) 421 | assert_ctrl("Interrupt", cc, r, w) unless host_os?(/linux/) 422 | end 423 | if cc = ctrl["dsusp"] 424 | assert_ctrl("#{cc.ord}", cc, r, w) 425 | assert_ctrl("#{cc.ord}", cc, r, w) 426 | assert_ctrl("#{cc.ord}", cc, r, w) 427 | end 428 | if cc = ctrl["lnext"] 429 | assert_ctrl("#{cc.ord}", cc, r, w) 430 | assert_ctrl("#{cc.ord}", cc, r, w) 431 | assert_ctrl("#{cc.ord}", cc, r, w) 432 | end 433 | if cc = ctrl["stop"] 434 | assert_ctrl("#{cc.ord}", cc, r, w) 435 | assert_ctrl("#{cc.ord}", cc, r, w) 436 | assert_ctrl("#{cc.ord}", cc, r, w) 437 | end 438 | end 439 | end 440 | 441 | unless IO.console 442 | def test_close 443 | assert_equal(["true"], run_pty("IO.console.close; p IO.console.fileno >= 0")) 444 | assert_equal(["true"], run_pty("IO.console(:close); p IO.console(:tty?)")) 445 | end 446 | 447 | def test_console_kw 448 | assert_equal(["File"], run_pty("IO.console.close; p IO.console(:clone, freeze: true).class")) 449 | end 450 | 451 | def test_sync 452 | assert_equal(["true"], run_pty("p IO.console.sync")) 453 | end 454 | 455 | def test_ttyname 456 | return unless IO.method_defined?(:ttyname) 457 | # [Bug #20682] 458 | # `sleep 0.1` is added to stabilize flaky failures on macOS. 459 | assert_equal(["true"], run_pty("p STDIN.ttyname == STDOUT.ttyname; sleep 0.1")) 460 | end 461 | end 462 | 463 | private 464 | def helper 465 | m, s = PTY.open 466 | rescue RuntimeError 467 | omit $! 468 | else 469 | yield m, s 470 | ensure 471 | m.close if m 472 | s.close if s 473 | end 474 | 475 | def run_pty(src, n = 1) 476 | pend("PTY.spawn cannot control terminal on JRuby") if RUBY_ENGINE == 'jruby' 477 | 478 | args = [TestIO_Console::INCLUDE_OPTS, "-rio/console", "-e", src] 479 | args.shift if args.first == "-I" # statically linked 480 | r, w, pid = PTY.spawn(EnvUtil.rubybin, *args) 481 | rescue RuntimeError 482 | omit $! 483 | else 484 | if block_given? 485 | yield r, w, pid 486 | else 487 | result = [] 488 | n.times {result << r.gets.chomp} 489 | result 490 | end 491 | ensure 492 | r.close if r 493 | w.close if w 494 | Process.wait(pid) if pid 495 | end 496 | end 497 | 498 | defined?(IO.console) and TestIO_Console.class_eval do 499 | if IO.console 500 | def test_get_winsize_console 501 | s = IO.console.winsize 502 | assert_kind_of(Array, s) 503 | assert_equal(2, s.size) 504 | assert_kind_of(Integer, s[0]) 505 | assert_kind_of(Integer, s[1]) 506 | end 507 | 508 | def test_set_winsize_console 509 | set_winsize_setup 510 | s = IO.console.winsize 511 | assert_nothing_raised(TypeError) {IO.console.winsize = s} 512 | bug = '[ruby-core:82741] [Bug #13888]' 513 | begin 514 | IO.console.winsize = [s[0], s[1]+1] 515 | assert_equal([s[0], s[1]+1], IO.console.winsize, bug) 516 | rescue Errno::EINVAL # Error if run on an actual console. 517 | else 518 | IO.console.winsize = s 519 | assert_equal(s, IO.console.winsize, bug) 520 | end 521 | ensure 522 | set_winsize_teardown 523 | end 524 | 525 | def test_close 526 | IO.console.close 527 | assert_kind_of(IO, IO.console) 528 | assert_nothing_raised(IOError) {IO.console.fileno} 529 | 530 | IO.console(:close) 531 | assert(IO.console(:tty?)) 532 | ensure 533 | IO.console(:close) 534 | end 535 | 536 | def test_console_kw 537 | io = IO.console(:clone, freeze: true) 538 | io.close 539 | assert_kind_of(IO, io) 540 | end 541 | 542 | def test_sync 543 | assert(IO.console.sync, "console should be unbuffered") 544 | ensure 545 | IO.console(:close) 546 | end 547 | 548 | def test_getch_timeout 549 | assert_nil(IO.console.getch(intr: true, time: 0.1, min: 0)) 550 | end 551 | 552 | def test_ttyname 553 | return unless IO.method_defined?(:ttyname) 554 | ttyname = IO.console.ttyname 555 | assert_not_nil(ttyname) 556 | File.open(ttyname) {|f| assert_predicate(f, :tty?)} 557 | end 558 | end 559 | 560 | case 561 | when Process.respond_to?(:daemon) 562 | noctty = [EnvUtil.rubybin, "-e", "Process.daemon(true)"] 563 | when !(rubyw = RbConfig::CONFIG["RUBYW_INSTALL_NAME"]).empty? 564 | dir, base = File.split(EnvUtil.rubybin) 565 | noctty = [File.join(dir, base.sub(RUBY_ENGINE, rubyw))] 566 | end 567 | 568 | if noctty 569 | require 'tempfile' 570 | NOCTTY = noctty 571 | def run_noctty(src) 572 | t = Tempfile.new("noctty_out") 573 | t.close 574 | t2 = Tempfile.new("noctty_run") 575 | t2.close 576 | cmd = [*NOCTTY[1..-1], 577 | TestIO_Console::INCLUDE_OPTS, 578 | '-e', 'open(ARGV[0], "w") {|f|', 579 | '-e', 'STDOUT.reopen(f)', 580 | '-e', 'STDERR.reopen(f)', 581 | '-e', 'require "io/console"', 582 | '-e', "f.puts (#{src}).inspect", 583 | '-e', 'f.flush', 584 | '-e', 'File.unlink(ARGV[1])', 585 | '-e', '}', 586 | '--', t.path, t2.path] 587 | assert_ruby_status(cmd, rubybin: NOCTTY[0]) 588 | 30.times do 589 | break unless File.exist?(t2.path) 590 | sleep 0.1 591 | end 592 | t.open 593 | t.gets.lines(chomp: true) 594 | ensure 595 | t.close! if t and !t.closed? 596 | t2.close! 597 | end 598 | 599 | def test_noctty 600 | assert_equal(["nil"], run_noctty("IO.console")) 601 | if IO.method_defined?(:ttyname) 602 | assert_equal(["nil"], run_noctty("STDIN.ttyname rescue $!")) 603 | end 604 | end 605 | end 606 | end 607 | 608 | defined?(IO.console) and IO.console and IO.console.respond_to?(:pressed?) and 609 | TestIO_Console.class_eval do 610 | def test_pressed_valid 611 | assert_include([true, false], IO.console.pressed?("HOME")) 612 | assert_include([true, false], IO.console.pressed?(:"HOME")) 613 | end 614 | 615 | def test_pressed_invalid 616 | e = assert_raise(ArgumentError) do 617 | IO.console.pressed?("HOME\0") 618 | end 619 | assert_match(/unknown virtual key code/, e.message) 620 | end 621 | end 622 | 623 | TestIO_Console.class_eval do 624 | def test_stringio_getch 625 | assert_ruby_status %w"--disable=gems -rstringio -rio/console", %q{ 626 | abort unless StringIO.method_defined?(:getch) 627 | } 628 | assert_ruby_status %w"--disable=gems -rio/console -rstringio", %q{ 629 | abort unless StringIO.method_defined?(:getch) 630 | } 631 | assert_ruby_status %w"--disable=gems -rstringio", %q{ 632 | abort if StringIO.method_defined?(:getch) 633 | } 634 | end 635 | end 636 | -------------------------------------------------------------------------------- /ext/io/console/win32_vk.inc: -------------------------------------------------------------------------------- 1 | #define UNDEFINED_VK (unsigned short)-1 2 | #ifndef VK_LBUTTON 3 | # define VK_LBUTTON UNDEFINED_VK 4 | #endif 5 | #ifndef VK_RBUTTON 6 | # define VK_RBUTTON UNDEFINED_VK 7 | #endif 8 | #ifndef VK_CANCEL 9 | # define VK_CANCEL UNDEFINED_VK 10 | #endif 11 | #ifndef VK_MBUTTON 12 | # define VK_MBUTTON UNDEFINED_VK 13 | #endif 14 | #ifndef VK_XBUTTON1 15 | # define VK_XBUTTON1 UNDEFINED_VK 16 | #endif 17 | #ifndef VK_XBUTTON2 18 | # define VK_XBUTTON2 UNDEFINED_VK 19 | #endif 20 | #ifndef VK_BACK 21 | # define VK_BACK UNDEFINED_VK 22 | #endif 23 | #ifndef VK_TAB 24 | # define VK_TAB UNDEFINED_VK 25 | #endif 26 | #ifndef VK_CLEAR 27 | # define VK_CLEAR UNDEFINED_VK 28 | #endif 29 | #ifndef VK_RETURN 30 | # define VK_RETURN UNDEFINED_VK 31 | #endif 32 | #ifndef VK_SHIFT 33 | # define VK_SHIFT UNDEFINED_VK 34 | #endif 35 | #ifndef VK_CONTROL 36 | # define VK_CONTROL UNDEFINED_VK 37 | #endif 38 | #ifndef VK_MENU 39 | # define VK_MENU UNDEFINED_VK 40 | #endif 41 | #ifndef VK_PAUSE 42 | # define VK_PAUSE UNDEFINED_VK 43 | #endif 44 | #ifndef VK_CAPITAL 45 | # define VK_CAPITAL UNDEFINED_VK 46 | #endif 47 | #ifndef VK_KANA 48 | # define VK_KANA UNDEFINED_VK 49 | #endif 50 | #ifndef VK_HANGEUL 51 | # define VK_HANGEUL UNDEFINED_VK 52 | #endif 53 | #ifndef VK_HANGUL 54 | # define VK_HANGUL UNDEFINED_VK 55 | #endif 56 | #ifndef VK_JUNJA 57 | # define VK_JUNJA UNDEFINED_VK 58 | #endif 59 | #ifndef VK_FINAL 60 | # define VK_FINAL UNDEFINED_VK 61 | #endif 62 | #ifndef VK_HANJA 63 | # define VK_HANJA UNDEFINED_VK 64 | #endif 65 | #ifndef VK_KANJI 66 | # define VK_KANJI UNDEFINED_VK 67 | #endif 68 | #ifndef VK_ESCAPE 69 | # define VK_ESCAPE UNDEFINED_VK 70 | #endif 71 | #ifndef VK_CONVERT 72 | # define VK_CONVERT UNDEFINED_VK 73 | #endif 74 | #ifndef VK_NONCONVERT 75 | # define VK_NONCONVERT UNDEFINED_VK 76 | #endif 77 | #ifndef VK_ACCEPT 78 | # define VK_ACCEPT UNDEFINED_VK 79 | #endif 80 | #ifndef VK_MODECHANGE 81 | # define VK_MODECHANGE UNDEFINED_VK 82 | #endif 83 | #ifndef VK_SPACE 84 | # define VK_SPACE UNDEFINED_VK 85 | #endif 86 | #ifndef VK_PRIOR 87 | # define VK_PRIOR UNDEFINED_VK 88 | #endif 89 | #ifndef VK_NEXT 90 | # define VK_NEXT UNDEFINED_VK 91 | #endif 92 | #ifndef VK_END 93 | # define VK_END UNDEFINED_VK 94 | #endif 95 | #ifndef VK_HOME 96 | # define VK_HOME UNDEFINED_VK 97 | #endif 98 | #ifndef VK_LEFT 99 | # define VK_LEFT UNDEFINED_VK 100 | #endif 101 | #ifndef VK_UP 102 | # define VK_UP UNDEFINED_VK 103 | #endif 104 | #ifndef VK_RIGHT 105 | # define VK_RIGHT UNDEFINED_VK 106 | #endif 107 | #ifndef VK_DOWN 108 | # define VK_DOWN UNDEFINED_VK 109 | #endif 110 | #ifndef VK_SELECT 111 | # define VK_SELECT UNDEFINED_VK 112 | #endif 113 | #ifndef VK_PRINT 114 | # define VK_PRINT UNDEFINED_VK 115 | #endif 116 | #ifndef VK_EXECUTE 117 | # define VK_EXECUTE UNDEFINED_VK 118 | #endif 119 | #ifndef VK_SNAPSHOT 120 | # define VK_SNAPSHOT UNDEFINED_VK 121 | #endif 122 | #ifndef VK_INSERT 123 | # define VK_INSERT UNDEFINED_VK 124 | #endif 125 | #ifndef VK_DELETE 126 | # define VK_DELETE UNDEFINED_VK 127 | #endif 128 | #ifndef VK_HELP 129 | # define VK_HELP UNDEFINED_VK 130 | #endif 131 | #ifndef VK_LWIN 132 | # define VK_LWIN UNDEFINED_VK 133 | #endif 134 | #ifndef VK_RWIN 135 | # define VK_RWIN UNDEFINED_VK 136 | #endif 137 | #ifndef VK_APPS 138 | # define VK_APPS UNDEFINED_VK 139 | #endif 140 | #ifndef VK_SLEEP 141 | # define VK_SLEEP UNDEFINED_VK 142 | #endif 143 | #ifndef VK_NUMPAD0 144 | # define VK_NUMPAD0 UNDEFINED_VK 145 | #endif 146 | #ifndef VK_NUMPAD1 147 | # define VK_NUMPAD1 UNDEFINED_VK 148 | #endif 149 | #ifndef VK_NUMPAD2 150 | # define VK_NUMPAD2 UNDEFINED_VK 151 | #endif 152 | #ifndef VK_NUMPAD3 153 | # define VK_NUMPAD3 UNDEFINED_VK 154 | #endif 155 | #ifndef VK_NUMPAD4 156 | # define VK_NUMPAD4 UNDEFINED_VK 157 | #endif 158 | #ifndef VK_NUMPAD5 159 | # define VK_NUMPAD5 UNDEFINED_VK 160 | #endif 161 | #ifndef VK_NUMPAD6 162 | # define VK_NUMPAD6 UNDEFINED_VK 163 | #endif 164 | #ifndef VK_NUMPAD7 165 | # define VK_NUMPAD7 UNDEFINED_VK 166 | #endif 167 | #ifndef VK_NUMPAD8 168 | # define VK_NUMPAD8 UNDEFINED_VK 169 | #endif 170 | #ifndef VK_NUMPAD9 171 | # define VK_NUMPAD9 UNDEFINED_VK 172 | #endif 173 | #ifndef VK_MULTIPLY 174 | # define VK_MULTIPLY UNDEFINED_VK 175 | #endif 176 | #ifndef VK_ADD 177 | # define VK_ADD UNDEFINED_VK 178 | #endif 179 | #ifndef VK_SEPARATOR 180 | # define VK_SEPARATOR UNDEFINED_VK 181 | #endif 182 | #ifndef VK_SUBTRACT 183 | # define VK_SUBTRACT UNDEFINED_VK 184 | #endif 185 | #ifndef VK_DECIMAL 186 | # define VK_DECIMAL UNDEFINED_VK 187 | #endif 188 | #ifndef VK_DIVIDE 189 | # define VK_DIVIDE UNDEFINED_VK 190 | #endif 191 | #ifndef VK_F1 192 | # define VK_F1 UNDEFINED_VK 193 | #endif 194 | #ifndef VK_F2 195 | # define VK_F2 UNDEFINED_VK 196 | #endif 197 | #ifndef VK_F3 198 | # define VK_F3 UNDEFINED_VK 199 | #endif 200 | #ifndef VK_F4 201 | # define VK_F4 UNDEFINED_VK 202 | #endif 203 | #ifndef VK_F5 204 | # define VK_F5 UNDEFINED_VK 205 | #endif 206 | #ifndef VK_F6 207 | # define VK_F6 UNDEFINED_VK 208 | #endif 209 | #ifndef VK_F7 210 | # define VK_F7 UNDEFINED_VK 211 | #endif 212 | #ifndef VK_F8 213 | # define VK_F8 UNDEFINED_VK 214 | #endif 215 | #ifndef VK_F9 216 | # define VK_F9 UNDEFINED_VK 217 | #endif 218 | #ifndef VK_F10 219 | # define VK_F10 UNDEFINED_VK 220 | #endif 221 | #ifndef VK_F11 222 | # define VK_F11 UNDEFINED_VK 223 | #endif 224 | #ifndef VK_F12 225 | # define VK_F12 UNDEFINED_VK 226 | #endif 227 | #ifndef VK_F13 228 | # define VK_F13 UNDEFINED_VK 229 | #endif 230 | #ifndef VK_F14 231 | # define VK_F14 UNDEFINED_VK 232 | #endif 233 | #ifndef VK_F15 234 | # define VK_F15 UNDEFINED_VK 235 | #endif 236 | #ifndef VK_F16 237 | # define VK_F16 UNDEFINED_VK 238 | #endif 239 | #ifndef VK_F17 240 | # define VK_F17 UNDEFINED_VK 241 | #endif 242 | #ifndef VK_F18 243 | # define VK_F18 UNDEFINED_VK 244 | #endif 245 | #ifndef VK_F19 246 | # define VK_F19 UNDEFINED_VK 247 | #endif 248 | #ifndef VK_F20 249 | # define VK_F20 UNDEFINED_VK 250 | #endif 251 | #ifndef VK_F21 252 | # define VK_F21 UNDEFINED_VK 253 | #endif 254 | #ifndef VK_F22 255 | # define VK_F22 UNDEFINED_VK 256 | #endif 257 | #ifndef VK_F23 258 | # define VK_F23 UNDEFINED_VK 259 | #endif 260 | #ifndef VK_F24 261 | # define VK_F24 UNDEFINED_VK 262 | #endif 263 | #ifndef VK_NUMLOCK 264 | # define VK_NUMLOCK UNDEFINED_VK 265 | #endif 266 | #ifndef VK_SCROLL 267 | # define VK_SCROLL UNDEFINED_VK 268 | #endif 269 | #ifndef VK_OEM_NEC_EQUAL 270 | # define VK_OEM_NEC_EQUAL UNDEFINED_VK 271 | #endif 272 | #ifndef VK_OEM_FJ_JISHO 273 | # define VK_OEM_FJ_JISHO UNDEFINED_VK 274 | #endif 275 | #ifndef VK_OEM_FJ_MASSHOU 276 | # define VK_OEM_FJ_MASSHOU UNDEFINED_VK 277 | #endif 278 | #ifndef VK_OEM_FJ_TOUROKU 279 | # define VK_OEM_FJ_TOUROKU UNDEFINED_VK 280 | #endif 281 | #ifndef VK_OEM_FJ_LOYA 282 | # define VK_OEM_FJ_LOYA UNDEFINED_VK 283 | #endif 284 | #ifndef VK_OEM_FJ_ROYA 285 | # define VK_OEM_FJ_ROYA UNDEFINED_VK 286 | #endif 287 | #ifndef VK_LSHIFT 288 | # define VK_LSHIFT UNDEFINED_VK 289 | #endif 290 | #ifndef VK_RSHIFT 291 | # define VK_RSHIFT UNDEFINED_VK 292 | #endif 293 | #ifndef VK_LCONTROL 294 | # define VK_LCONTROL UNDEFINED_VK 295 | #endif 296 | #ifndef VK_RCONTROL 297 | # define VK_RCONTROL UNDEFINED_VK 298 | #endif 299 | #ifndef VK_LMENU 300 | # define VK_LMENU UNDEFINED_VK 301 | #endif 302 | #ifndef VK_RMENU 303 | # define VK_RMENU UNDEFINED_VK 304 | #endif 305 | #ifndef VK_BROWSER_BACK 306 | # define VK_BROWSER_BACK UNDEFINED_VK 307 | #endif 308 | #ifndef VK_BROWSER_FORWARD 309 | # define VK_BROWSER_FORWARD UNDEFINED_VK 310 | #endif 311 | #ifndef VK_BROWSER_REFRESH 312 | # define VK_BROWSER_REFRESH UNDEFINED_VK 313 | #endif 314 | #ifndef VK_BROWSER_STOP 315 | # define VK_BROWSER_STOP UNDEFINED_VK 316 | #endif 317 | #ifndef VK_BROWSER_SEARCH 318 | # define VK_BROWSER_SEARCH UNDEFINED_VK 319 | #endif 320 | #ifndef VK_BROWSER_FAVORITES 321 | # define VK_BROWSER_FAVORITES UNDEFINED_VK 322 | #endif 323 | #ifndef VK_BROWSER_HOME 324 | # define VK_BROWSER_HOME UNDEFINED_VK 325 | #endif 326 | #ifndef VK_VOLUME_MUTE 327 | # define VK_VOLUME_MUTE UNDEFINED_VK 328 | #endif 329 | #ifndef VK_VOLUME_DOWN 330 | # define VK_VOLUME_DOWN UNDEFINED_VK 331 | #endif 332 | #ifndef VK_VOLUME_UP 333 | # define VK_VOLUME_UP UNDEFINED_VK 334 | #endif 335 | #ifndef VK_MEDIA_NEXT_TRACK 336 | # define VK_MEDIA_NEXT_TRACK UNDEFINED_VK 337 | #endif 338 | #ifndef VK_MEDIA_PREV_TRACK 339 | # define VK_MEDIA_PREV_TRACK UNDEFINED_VK 340 | #endif 341 | #ifndef VK_MEDIA_STOP 342 | # define VK_MEDIA_STOP UNDEFINED_VK 343 | #endif 344 | #ifndef VK_MEDIA_PLAY_PAUSE 345 | # define VK_MEDIA_PLAY_PAUSE UNDEFINED_VK 346 | #endif 347 | #ifndef VK_LAUNCH_MAIL 348 | # define VK_LAUNCH_MAIL UNDEFINED_VK 349 | #endif 350 | #ifndef VK_LAUNCH_MEDIA_SELECT 351 | # define VK_LAUNCH_MEDIA_SELECT UNDEFINED_VK 352 | #endif 353 | #ifndef VK_LAUNCH_APP1 354 | # define VK_LAUNCH_APP1 UNDEFINED_VK 355 | #endif 356 | #ifndef VK_LAUNCH_APP2 357 | # define VK_LAUNCH_APP2 UNDEFINED_VK 358 | #endif 359 | #ifndef VK_OEM_1 360 | # define VK_OEM_1 UNDEFINED_VK 361 | #endif 362 | #ifndef VK_OEM_PLUS 363 | # define VK_OEM_PLUS UNDEFINED_VK 364 | #endif 365 | #ifndef VK_OEM_COMMA 366 | # define VK_OEM_COMMA UNDEFINED_VK 367 | #endif 368 | #ifndef VK_OEM_MINUS 369 | # define VK_OEM_MINUS UNDEFINED_VK 370 | #endif 371 | #ifndef VK_OEM_PERIOD 372 | # define VK_OEM_PERIOD UNDEFINED_VK 373 | #endif 374 | #ifndef VK_OEM_2 375 | # define VK_OEM_2 UNDEFINED_VK 376 | #endif 377 | #ifndef VK_OEM_3 378 | # define VK_OEM_3 UNDEFINED_VK 379 | #endif 380 | #ifndef VK_OEM_4 381 | # define VK_OEM_4 UNDEFINED_VK 382 | #endif 383 | #ifndef VK_OEM_5 384 | # define VK_OEM_5 UNDEFINED_VK 385 | #endif 386 | #ifndef VK_OEM_6 387 | # define VK_OEM_6 UNDEFINED_VK 388 | #endif 389 | #ifndef VK_OEM_7 390 | # define VK_OEM_7 UNDEFINED_VK 391 | #endif 392 | #ifndef VK_OEM_8 393 | # define VK_OEM_8 UNDEFINED_VK 394 | #endif 395 | #ifndef VK_OEM_AX 396 | # define VK_OEM_AX UNDEFINED_VK 397 | #endif 398 | #ifndef VK_OEM_102 399 | # define VK_OEM_102 UNDEFINED_VK 400 | #endif 401 | #ifndef VK_ICO_HELP 402 | # define VK_ICO_HELP UNDEFINED_VK 403 | #endif 404 | #ifndef VK_ICO_00 405 | # define VK_ICO_00 UNDEFINED_VK 406 | #endif 407 | #ifndef VK_PROCESSKEY 408 | # define VK_PROCESSKEY UNDEFINED_VK 409 | #endif 410 | #ifndef VK_ICO_CLEAR 411 | # define VK_ICO_CLEAR UNDEFINED_VK 412 | #endif 413 | #ifndef VK_PACKET 414 | # define VK_PACKET UNDEFINED_VK 415 | #endif 416 | #ifndef VK_OEM_RESET 417 | # define VK_OEM_RESET UNDEFINED_VK 418 | #endif 419 | #ifndef VK_OEM_JUMP 420 | # define VK_OEM_JUMP UNDEFINED_VK 421 | #endif 422 | #ifndef VK_OEM_PA1 423 | # define VK_OEM_PA1 UNDEFINED_VK 424 | #endif 425 | #ifndef VK_OEM_PA2 426 | # define VK_OEM_PA2 UNDEFINED_VK 427 | #endif 428 | #ifndef VK_OEM_PA3 429 | # define VK_OEM_PA3 UNDEFINED_VK 430 | #endif 431 | #ifndef VK_OEM_WSCTRL 432 | # define VK_OEM_WSCTRL UNDEFINED_VK 433 | #endif 434 | #ifndef VK_OEM_CUSEL 435 | # define VK_OEM_CUSEL UNDEFINED_VK 436 | #endif 437 | #ifndef VK_OEM_ATTN 438 | # define VK_OEM_ATTN UNDEFINED_VK 439 | #endif 440 | #ifndef VK_OEM_FINISH 441 | # define VK_OEM_FINISH UNDEFINED_VK 442 | #endif 443 | #ifndef VK_OEM_COPY 444 | # define VK_OEM_COPY UNDEFINED_VK 445 | #endif 446 | #ifndef VK_OEM_AUTO 447 | # define VK_OEM_AUTO UNDEFINED_VK 448 | #endif 449 | #ifndef VK_OEM_ENLW 450 | # define VK_OEM_ENLW UNDEFINED_VK 451 | #endif 452 | #ifndef VK_OEM_BACKTAB 453 | # define VK_OEM_BACKTAB UNDEFINED_VK 454 | #endif 455 | #ifndef VK_ATTN 456 | # define VK_ATTN UNDEFINED_VK 457 | #endif 458 | #ifndef VK_CRSEL 459 | # define VK_CRSEL UNDEFINED_VK 460 | #endif 461 | #ifndef VK_EXSEL 462 | # define VK_EXSEL UNDEFINED_VK 463 | #endif 464 | #ifndef VK_EREOF 465 | # define VK_EREOF UNDEFINED_VK 466 | #endif 467 | #ifndef VK_PLAY 468 | # define VK_PLAY UNDEFINED_VK 469 | #endif 470 | #ifndef VK_ZOOM 471 | # define VK_ZOOM UNDEFINED_VK 472 | #endif 473 | #ifndef VK_NONAME 474 | # define VK_NONAME UNDEFINED_VK 475 | #endif 476 | #ifndef VK_PA1 477 | # define VK_PA1 UNDEFINED_VK 478 | #endif 479 | #ifndef VK_OEM_CLEAR 480 | # define VK_OEM_CLEAR UNDEFINED_VK 481 | #endif 482 | /* ANSI-C code produced by gperf version 3.1 */ 483 | /* Command-line: gperf --ignore-case -L ANSI-C -E -C -P -p -j1 -i 1 -g -o -t -K ofs -N console_win32_vk -k'*' win32_vk.list */ 484 | 485 | #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ 486 | && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ 487 | && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ 488 | && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ 489 | && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ 490 | && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ 491 | && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ 492 | && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ 493 | && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ 494 | && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ 495 | && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ 496 | && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ 497 | && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ 498 | && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ 499 | && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ 500 | && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ 501 | && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ 502 | && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ 503 | && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ 504 | && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ 505 | && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ 506 | && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ 507 | && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) 508 | /* The character set is not based on ISO-646. */ 509 | #error "gperf generated tables don't work with this execution character set. Please report a bug to ." 510 | #endif 511 | 512 | #line 1 "win32_vk.list" 513 | 514 | struct vktable {short ofs; unsigned short vk;}; 515 | static const struct vktable *console_win32_vk(const char *, size_t); 516 | #line 5 "win32_vk.list" 517 | struct vktable; 518 | /* maximum key range = 245, duplicates = 0 */ 519 | 520 | #ifndef GPERF_DOWNCASE 521 | #define GPERF_DOWNCASE 1 522 | static unsigned char gperf_downcase[256] = 523 | { 524 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 525 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 526 | 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 527 | 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 528 | 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 529 | 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 530 | 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 531 | 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 532 | 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 533 | 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 534 | 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 535 | 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 536 | 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 537 | 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 538 | 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 539 | 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 540 | 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 541 | 255 542 | }; 543 | #endif 544 | 545 | #ifndef GPERF_CASE_STRCMP 546 | #define GPERF_CASE_STRCMP 1 547 | static int 548 | gperf_case_strcmp (register const char *s1, register const char *s2) 549 | { 550 | for (;;) 551 | { 552 | unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; 553 | unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; 554 | if (c1 != 0 && c1 == c2) 555 | continue; 556 | return (int)c1 - (int)c2; 557 | } 558 | } 559 | #endif 560 | 561 | #ifdef __GNUC__ 562 | __inline 563 | #else 564 | #ifdef __cplusplus 565 | inline 566 | #endif 567 | #endif 568 | static unsigned int 569 | hash (register const char *str, register size_t len) 570 | { 571 | static const unsigned short asso_values[] = 572 | { 573 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 574 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 575 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 576 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 577 | 257, 257, 257, 257, 257, 257, 257, 257, 51, 74, 578 | 80, 116, 127, 124, 95, 140, 77, 53, 7, 3, 579 | 257, 257, 257, 257, 257, 1, 11, 1, 55, 1, 580 | 25, 84, 31, 33, 13, 16, 2, 28, 8, 1, 581 | 6, 10, 1, 1, 3, 4, 45, 18, 73, 79, 582 | 30, 257, 257, 257, 257, 5, 257, 1, 11, 1, 583 | 55, 1, 25, 84, 31, 33, 13, 16, 2, 28, 584 | 8, 1, 6, 10, 1, 1, 3, 4, 45, 18, 585 | 73, 79, 30, 257, 257, 257, 257, 257, 257, 257, 586 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 587 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 588 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 589 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 590 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 591 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 592 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 593 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 594 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 595 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 596 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 597 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 598 | 257, 257, 257, 257, 257, 257, 257, 257 599 | }; 600 | register unsigned int hval = (unsigned int)len; 601 | 602 | switch (hval) 603 | { 604 | default: 605 | hval += asso_values[(unsigned char)str[18]]; 606 | /*FALLTHROUGH*/ 607 | case 18: 608 | hval += asso_values[(unsigned char)str[17]]; 609 | /*FALLTHROUGH*/ 610 | case 17: 611 | hval += asso_values[(unsigned char)str[16]]; 612 | /*FALLTHROUGH*/ 613 | case 16: 614 | hval += asso_values[(unsigned char)str[15]]; 615 | /*FALLTHROUGH*/ 616 | case 15: 617 | hval += asso_values[(unsigned char)str[14]]; 618 | /*FALLTHROUGH*/ 619 | case 14: 620 | hval += asso_values[(unsigned char)str[13]]; 621 | /*FALLTHROUGH*/ 622 | case 13: 623 | hval += asso_values[(unsigned char)str[12]]; 624 | /*FALLTHROUGH*/ 625 | case 12: 626 | hval += asso_values[(unsigned char)str[11]]; 627 | /*FALLTHROUGH*/ 628 | case 11: 629 | hval += asso_values[(unsigned char)str[10]]; 630 | /*FALLTHROUGH*/ 631 | case 10: 632 | hval += asso_values[(unsigned char)str[9]]; 633 | /*FALLTHROUGH*/ 634 | case 9: 635 | hval += asso_values[(unsigned char)str[8]]; 636 | /*FALLTHROUGH*/ 637 | case 8: 638 | hval += asso_values[(unsigned char)str[7]]; 639 | /*FALLTHROUGH*/ 640 | case 7: 641 | hval += asso_values[(unsigned char)str[6]]; 642 | /*FALLTHROUGH*/ 643 | case 6: 644 | hval += asso_values[(unsigned char)str[5]]; 645 | /*FALLTHROUGH*/ 646 | case 5: 647 | hval += asso_values[(unsigned char)str[4]]; 648 | /*FALLTHROUGH*/ 649 | case 4: 650 | hval += asso_values[(unsigned char)str[3]]; 651 | /*FALLTHROUGH*/ 652 | case 3: 653 | hval += asso_values[(unsigned char)str[2]+2]; 654 | /*FALLTHROUGH*/ 655 | case 2: 656 | hval += asso_values[(unsigned char)str[1]]; 657 | /*FALLTHROUGH*/ 658 | case 1: 659 | hval += asso_values[(unsigned char)str[0]]; 660 | break; 661 | } 662 | return (unsigned int)hval; 663 | } 664 | 665 | struct stringpool_t 666 | { 667 | char stringpool_str12[sizeof("UP")]; 668 | char stringpool_str13[sizeof("APPS")]; 669 | char stringpool_str14[sizeof("CRSEL")]; 670 | char stringpool_str15[sizeof("SPACE")]; 671 | char stringpool_str16[sizeof("SCROLL")]; 672 | char stringpool_str17[sizeof("ESCAPE")]; 673 | char stringpool_str18[sizeof("CANCEL")]; 674 | char stringpool_str19[sizeof("ACCEPT")]; 675 | char stringpool_str20[sizeof("SEPARATOR")]; 676 | char stringpool_str21[sizeof("SELECT")]; 677 | char stringpool_str22[sizeof("CONTROL")]; 678 | char stringpool_str23[sizeof("OEM_CLEAR")]; 679 | char stringpool_str24[sizeof("OEM_RESET")]; 680 | char stringpool_str25[sizeof("OEM_AUTO")]; 681 | char stringpool_str26[sizeof("OEM_CUSEL")]; 682 | char stringpool_str28[sizeof("KANA")]; 683 | char stringpool_str29[sizeof("OEM_PLUS")]; 684 | char stringpool_str30[sizeof("PRIOR")]; 685 | char stringpool_str31[sizeof("OEM_ATTN")]; 686 | char stringpool_str32[sizeof("PAUSE")]; 687 | char stringpool_str33[sizeof("BACK")]; 688 | char stringpool_str34[sizeof("PACKET")]; 689 | char stringpool_str35[sizeof("RCONTROL")]; 690 | char stringpool_str36[sizeof("LCONTROL")]; 691 | char stringpool_str37[sizeof("END")]; 692 | char stringpool_str38[sizeof("HOME")]; 693 | char stringpool_str39[sizeof("PRINT")]; 694 | char stringpool_str40[sizeof("NUMLOCK")]; 695 | char stringpool_str41[sizeof("LEFT")]; 696 | char stringpool_str42[sizeof("JUNJA")]; 697 | char stringpool_str43[sizeof("MENU")]; 698 | char stringpool_str44[sizeof("OEM_WSCTRL")]; 699 | char stringpool_str45[sizeof("OEM_ENLW")]; 700 | char stringpool_str46[sizeof("NEXT")]; 701 | char stringpool_str47[sizeof("RWIN")]; 702 | char stringpool_str48[sizeof("LWIN")]; 703 | char stringpool_str49[sizeof("CAPITAL")]; 704 | char stringpool_str50[sizeof("HELP")]; 705 | char stringpool_str51[sizeof("NONAME")]; 706 | char stringpool_str52[sizeof("RBUTTON")]; 707 | char stringpool_str53[sizeof("LBUTTON")]; 708 | char stringpool_str54[sizeof("OEM_NEC_EQUAL")]; 709 | char stringpool_str56[sizeof("INSERT")]; 710 | char stringpool_str57[sizeof("HANJA")]; 711 | char stringpool_str60[sizeof("SNAPSHOT")]; 712 | char stringpool_str61[sizeof("ATTN")]; 713 | char stringpool_str62[sizeof("TAB")]; 714 | char stringpool_str63[sizeof("OEM_BACKTAB")]; 715 | char stringpool_str64[sizeof("ICO_CLEAR")]; 716 | char stringpool_str65[sizeof("CONVERT")]; 717 | char stringpool_str66[sizeof("RETURN")]; 718 | char stringpool_str67[sizeof("OEM_JUMP")]; 719 | char stringpool_str71[sizeof("BROWSER_STOP")]; 720 | char stringpool_str72[sizeof("FINAL")]; 721 | char stringpool_str73[sizeof("ZOOM")]; 722 | char stringpool_str74[sizeof("KANJI")]; 723 | char stringpool_str75[sizeof("DELETE")]; 724 | char stringpool_str76[sizeof("OEM_COMMA")]; 725 | char stringpool_str77[sizeof("SUBTRACT")]; 726 | char stringpool_str79[sizeof("MBUTTON")]; 727 | char stringpool_str80[sizeof("F9")]; 728 | char stringpool_str81[sizeof("SHIFT")]; 729 | char stringpool_str82[sizeof("RSHIFT")]; 730 | char stringpool_str83[sizeof("LSHIFT")]; 731 | char stringpool_str84[sizeof("ADD")]; 732 | char stringpool_str85[sizeof("NONCONVERT")]; 733 | char stringpool_str86[sizeof("EXSEL")]; 734 | char stringpool_str87[sizeof("OEM_1")]; 735 | char stringpool_str88[sizeof("OEM_AX")]; 736 | char stringpool_str89[sizeof("BROWSER_BACK")]; 737 | char stringpool_str90[sizeof("OEM_8")]; 738 | char stringpool_str91[sizeof("OEM_MINUS")]; 739 | char stringpool_str92[sizeof("PLAY")]; 740 | char stringpool_str93[sizeof("OEM_2")]; 741 | char stringpool_str94[sizeof("CLEAR")]; 742 | char stringpool_str95[sizeof("OEM_FJ_TOUROKU")]; 743 | char stringpool_str96[sizeof("OEM_PA1")]; 744 | char stringpool_str97[sizeof("ICO_HELP")]; 745 | char stringpool_str98[sizeof("BROWSER_SEARCH")]; 746 | char stringpool_str99[sizeof("SLEEP")]; 747 | char stringpool_str101[sizeof("F1")]; 748 | char stringpool_str102[sizeof("OEM_PA2")]; 749 | char stringpool_str103[sizeof("OEM_COPY")]; 750 | char stringpool_str104[sizeof("F8")]; 751 | char stringpool_str105[sizeof("F19")]; 752 | char stringpool_str106[sizeof("RIGHT")]; 753 | char stringpool_str107[sizeof("F2")]; 754 | char stringpool_str108[sizeof("OEM_6")]; 755 | char stringpool_str109[sizeof("F18")]; 756 | char stringpool_str111[sizeof("VOLUME_UP")]; 757 | char stringpool_str114[sizeof("MEDIA_STOP")]; 758 | char stringpool_str115[sizeof("OEM_PERIOD")]; 759 | char stringpool_str117[sizeof("EREOF")]; 760 | char stringpool_str121[sizeof("BROWSER_HOME")]; 761 | char stringpool_str122[sizeof("F6")]; 762 | char stringpool_str124[sizeof("BROWSER_REFRESH")]; 763 | char stringpool_str126[sizeof("PA1")]; 764 | char stringpool_str127[sizeof("PROCESSKEY")]; 765 | char stringpool_str128[sizeof("DECIMAL")]; 766 | char stringpool_str129[sizeof("OEM_3")]; 767 | char stringpool_str130[sizeof("RMENU")]; 768 | char stringpool_str131[sizeof("LMENU")]; 769 | char stringpool_str132[sizeof("OEM_FJ_MASSHOU")]; 770 | char stringpool_str133[sizeof("NUMPAD0")]; 771 | char stringpool_str134[sizeof("HANGUL")]; 772 | char stringpool_str135[sizeof("NUMPAD9")]; 773 | char stringpool_str136[sizeof("HANGEUL")]; 774 | char stringpool_str137[sizeof("OEM_5")]; 775 | char stringpool_str138[sizeof("OEM_PA3")]; 776 | char stringpool_str139[sizeof("VOLUME_MUTE")]; 777 | char stringpool_str140[sizeof("OEM_4")]; 778 | char stringpool_str141[sizeof("LAUNCH_MAIL")]; 779 | char stringpool_str142[sizeof("OEM_FJ_JISHO")]; 780 | char stringpool_str143[sizeof("F3")]; 781 | char stringpool_str144[sizeof("OEM_FJ_ROYA")]; 782 | char stringpool_str145[sizeof("OEM_FJ_LOYA")]; 783 | char stringpool_str147[sizeof("DOWN")]; 784 | char stringpool_str149[sizeof("OEM_FINISH")]; 785 | char stringpool_str151[sizeof("F5")]; 786 | char stringpool_str153[sizeof("OEM_7")]; 787 | char stringpool_str154[sizeof("F4")]; 788 | char stringpool_str155[sizeof("F17")]; 789 | char stringpool_str156[sizeof("NUMPAD1")]; 790 | char stringpool_str157[sizeof("ICO_00")]; 791 | char stringpool_str159[sizeof("NUMPAD8")]; 792 | char stringpool_str162[sizeof("NUMPAD2")]; 793 | char stringpool_str164[sizeof("LAUNCH_APP1")]; 794 | char stringpool_str165[sizeof("BROWSER_FORWARD")]; 795 | char stringpool_str167[sizeof("F7")]; 796 | char stringpool_str170[sizeof("LAUNCH_APP2")]; 797 | char stringpool_str171[sizeof("MULTIPLY")]; 798 | char stringpool_str174[sizeof("EXECUTE")]; 799 | char stringpool_str176[sizeof("BROWSER_FAVORITES")]; 800 | char stringpool_str177[sizeof("NUMPAD6")]; 801 | char stringpool_str179[sizeof("F16")]; 802 | char stringpool_str182[sizeof("F10")]; 803 | char stringpool_str185[sizeof("VOLUME_DOWN")]; 804 | char stringpool_str188[sizeof("F20")]; 805 | char stringpool_str189[sizeof("MEDIA_PREV_TRACK")]; 806 | char stringpool_str191[sizeof("MODECHANGE")]; 807 | char stringpool_str197[sizeof("F14")]; 808 | char stringpool_str198[sizeof("NUMPAD3")]; 809 | char stringpool_str199[sizeof("XBUTTON1")]; 810 | char stringpool_str203[sizeof("F24")]; 811 | char stringpool_str205[sizeof("XBUTTON2")]; 812 | char stringpool_str206[sizeof("NUMPAD5")]; 813 | char stringpool_str209[sizeof("NUMPAD4")]; 814 | char stringpool_str215[sizeof("MEDIA_PLAY_PAUSE")]; 815 | char stringpool_str217[sizeof("LAUNCH_MEDIA_SELECT")]; 816 | char stringpool_str218[sizeof("F11")]; 817 | char stringpool_str220[sizeof("OEM_102")]; 818 | char stringpool_str221[sizeof("MEDIA_NEXT_TRACK")]; 819 | char stringpool_str222[sizeof("NUMPAD7")]; 820 | char stringpool_str224[sizeof("F21")]; 821 | char stringpool_str226[sizeof("F13")]; 822 | char stringpool_str229[sizeof("F12")]; 823 | char stringpool_str232[sizeof("F23")]; 824 | char stringpool_str235[sizeof("F22")]; 825 | char stringpool_str242[sizeof("F15")]; 826 | char stringpool_str256[sizeof("DIVIDE")]; 827 | }; 828 | static const struct stringpool_t stringpool_contents = 829 | { 830 | "UP", 831 | "APPS", 832 | "CRSEL", 833 | "SPACE", 834 | "SCROLL", 835 | "ESCAPE", 836 | "CANCEL", 837 | "ACCEPT", 838 | "SEPARATOR", 839 | "SELECT", 840 | "CONTROL", 841 | "OEM_CLEAR", 842 | "OEM_RESET", 843 | "OEM_AUTO", 844 | "OEM_CUSEL", 845 | "KANA", 846 | "OEM_PLUS", 847 | "PRIOR", 848 | "OEM_ATTN", 849 | "PAUSE", 850 | "BACK", 851 | "PACKET", 852 | "RCONTROL", 853 | "LCONTROL", 854 | "END", 855 | "HOME", 856 | "PRINT", 857 | "NUMLOCK", 858 | "LEFT", 859 | "JUNJA", 860 | "MENU", 861 | "OEM_WSCTRL", 862 | "OEM_ENLW", 863 | "NEXT", 864 | "RWIN", 865 | "LWIN", 866 | "CAPITAL", 867 | "HELP", 868 | "NONAME", 869 | "RBUTTON", 870 | "LBUTTON", 871 | "OEM_NEC_EQUAL", 872 | "INSERT", 873 | "HANJA", 874 | "SNAPSHOT", 875 | "ATTN", 876 | "TAB", 877 | "OEM_BACKTAB", 878 | "ICO_CLEAR", 879 | "CONVERT", 880 | "RETURN", 881 | "OEM_JUMP", 882 | "BROWSER_STOP", 883 | "FINAL", 884 | "ZOOM", 885 | "KANJI", 886 | "DELETE", 887 | "OEM_COMMA", 888 | "SUBTRACT", 889 | "MBUTTON", 890 | "F9", 891 | "SHIFT", 892 | "RSHIFT", 893 | "LSHIFT", 894 | "ADD", 895 | "NONCONVERT", 896 | "EXSEL", 897 | "OEM_1", 898 | "OEM_AX", 899 | "BROWSER_BACK", 900 | "OEM_8", 901 | "OEM_MINUS", 902 | "PLAY", 903 | "OEM_2", 904 | "CLEAR", 905 | "OEM_FJ_TOUROKU", 906 | "OEM_PA1", 907 | "ICO_HELP", 908 | "BROWSER_SEARCH", 909 | "SLEEP", 910 | "F1", 911 | "OEM_PA2", 912 | "OEM_COPY", 913 | "F8", 914 | "F19", 915 | "RIGHT", 916 | "F2", 917 | "OEM_6", 918 | "F18", 919 | "VOLUME_UP", 920 | "MEDIA_STOP", 921 | "OEM_PERIOD", 922 | "EREOF", 923 | "BROWSER_HOME", 924 | "F6", 925 | "BROWSER_REFRESH", 926 | "PA1", 927 | "PROCESSKEY", 928 | "DECIMAL", 929 | "OEM_3", 930 | "RMENU", 931 | "LMENU", 932 | "OEM_FJ_MASSHOU", 933 | "NUMPAD0", 934 | "HANGUL", 935 | "NUMPAD9", 936 | "HANGEUL", 937 | "OEM_5", 938 | "OEM_PA3", 939 | "VOLUME_MUTE", 940 | "OEM_4", 941 | "LAUNCH_MAIL", 942 | "OEM_FJ_JISHO", 943 | "F3", 944 | "OEM_FJ_ROYA", 945 | "OEM_FJ_LOYA", 946 | "DOWN", 947 | "OEM_FINISH", 948 | "F5", 949 | "OEM_7", 950 | "F4", 951 | "F17", 952 | "NUMPAD1", 953 | "ICO_00", 954 | "NUMPAD8", 955 | "NUMPAD2", 956 | "LAUNCH_APP1", 957 | "BROWSER_FORWARD", 958 | "F7", 959 | "LAUNCH_APP2", 960 | "MULTIPLY", 961 | "EXECUTE", 962 | "BROWSER_FAVORITES", 963 | "NUMPAD6", 964 | "F16", 965 | "F10", 966 | "VOLUME_DOWN", 967 | "F20", 968 | "MEDIA_PREV_TRACK", 969 | "MODECHANGE", 970 | "F14", 971 | "NUMPAD3", 972 | "XBUTTON1", 973 | "F24", 974 | "XBUTTON2", 975 | "NUMPAD5", 976 | "NUMPAD4", 977 | "MEDIA_PLAY_PAUSE", 978 | "LAUNCH_MEDIA_SELECT", 979 | "F11", 980 | "OEM_102", 981 | "MEDIA_NEXT_TRACK", 982 | "NUMPAD7", 983 | "F21", 984 | "F13", 985 | "F12", 986 | "F23", 987 | "F22", 988 | "F15", 989 | "DIVIDE" 990 | }; 991 | #define stringpool ((const char *) &stringpool_contents) 992 | const struct vktable * 993 | console_win32_vk (register const char *str, register size_t len) 994 | { 995 | enum 996 | { 997 | TOTAL_KEYWORDS = 160, 998 | MIN_WORD_LENGTH = 2, 999 | MAX_WORD_LENGTH = 19, 1000 | MIN_HASH_VALUE = 12, 1001 | MAX_HASH_VALUE = 256 1002 | }; 1003 | 1004 | static const struct vktable wordlist[] = 1005 | { 1006 | {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, 1007 | {-1}, {-1}, {-1}, 1008 | #line 40 "win32_vk.list" 1009 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str12, VK_UP}, 1010 | #line 52 "win32_vk.list" 1011 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str13, VK_APPS}, 1012 | #line 159 "win32_vk.list" 1013 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str14, VK_CRSEL}, 1014 | #line 34 "win32_vk.list" 1015 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str15, VK_SPACE}, 1016 | #line 95 "win32_vk.list" 1017 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str16, VK_SCROLL}, 1018 | #line 29 "win32_vk.list" 1019 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str17, VK_ESCAPE}, 1020 | #line 9 "win32_vk.list" 1021 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str18, VK_CANCEL}, 1022 | #line 32 "win32_vk.list" 1023 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str19, VK_ACCEPT}, 1024 | #line 66 "win32_vk.list" 1025 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str20, VK_SEPARATOR}, 1026 | #line 43 "win32_vk.list" 1027 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str21, VK_SELECT}, 1028 | #line 18 "win32_vk.list" 1029 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str22, VK_CONTROL}, 1030 | #line 166 "win32_vk.list" 1031 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str23, VK_OEM_CLEAR}, 1032 | #line 145 "win32_vk.list" 1033 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str24, VK_OEM_RESET}, 1034 | #line 155 "win32_vk.list" 1035 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str25, VK_OEM_AUTO}, 1036 | #line 151 "win32_vk.list" 1037 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str26, VK_OEM_CUSEL}, 1038 | {-1}, 1039 | #line 22 "win32_vk.list" 1040 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str28, VK_KANA}, 1041 | #line 127 "win32_vk.list" 1042 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str29, VK_OEM_PLUS}, 1043 | #line 35 "win32_vk.list" 1044 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str30, VK_PRIOR}, 1045 | #line 152 "win32_vk.list" 1046 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str31, VK_OEM_ATTN}, 1047 | #line 20 "win32_vk.list" 1048 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str32, VK_PAUSE}, 1049 | #line 13 "win32_vk.list" 1050 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str33, VK_BACK}, 1051 | #line 144 "win32_vk.list" 1052 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str34, VK_PACKET}, 1053 | #line 105 "win32_vk.list" 1054 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str35, VK_RCONTROL}, 1055 | #line 104 "win32_vk.list" 1056 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str36, VK_LCONTROL}, 1057 | #line 37 "win32_vk.list" 1058 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str37, VK_END}, 1059 | #line 38 "win32_vk.list" 1060 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str38, VK_HOME}, 1061 | #line 44 "win32_vk.list" 1062 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str39, VK_PRINT}, 1063 | #line 94 "win32_vk.list" 1064 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str40, VK_NUMLOCK}, 1065 | #line 39 "win32_vk.list" 1066 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str41, VK_LEFT}, 1067 | #line 25 "win32_vk.list" 1068 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str42, VK_JUNJA}, 1069 | #line 19 "win32_vk.list" 1070 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str43, VK_MENU}, 1071 | #line 150 "win32_vk.list" 1072 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str44, VK_OEM_WSCTRL}, 1073 | #line 156 "win32_vk.list" 1074 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str45, VK_OEM_ENLW}, 1075 | #line 36 "win32_vk.list" 1076 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str46, VK_NEXT}, 1077 | #line 51 "win32_vk.list" 1078 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str47, VK_RWIN}, 1079 | #line 50 "win32_vk.list" 1080 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str48, VK_LWIN}, 1081 | #line 21 "win32_vk.list" 1082 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str49, VK_CAPITAL}, 1083 | #line 49 "win32_vk.list" 1084 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str50, VK_HELP}, 1085 | #line 164 "win32_vk.list" 1086 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str51, VK_NONAME}, 1087 | #line 8 "win32_vk.list" 1088 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str52, VK_RBUTTON}, 1089 | #line 7 "win32_vk.list" 1090 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str53, VK_LBUTTON}, 1091 | #line 96 "win32_vk.list" 1092 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str54, VK_OEM_NEC_EQUAL}, 1093 | {-1}, 1094 | #line 47 "win32_vk.list" 1095 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str56, VK_INSERT}, 1096 | #line 27 "win32_vk.list" 1097 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str57, VK_HANJA}, 1098 | {-1}, {-1}, 1099 | #line 46 "win32_vk.list" 1100 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str60, VK_SNAPSHOT}, 1101 | #line 158 "win32_vk.list" 1102 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str61, VK_ATTN}, 1103 | #line 14 "win32_vk.list" 1104 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str62, VK_TAB}, 1105 | #line 157 "win32_vk.list" 1106 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str63, VK_OEM_BACKTAB}, 1107 | #line 143 "win32_vk.list" 1108 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str64, VK_ICO_CLEAR}, 1109 | #line 30 "win32_vk.list" 1110 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str65, VK_CONVERT}, 1111 | #line 16 "win32_vk.list" 1112 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str66, VK_RETURN}, 1113 | #line 146 "win32_vk.list" 1114 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str67, VK_OEM_JUMP}, 1115 | {-1}, {-1}, {-1}, 1116 | #line 111 "win32_vk.list" 1117 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str71, VK_BROWSER_STOP}, 1118 | #line 26 "win32_vk.list" 1119 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str72, VK_FINAL}, 1120 | #line 163 "win32_vk.list" 1121 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str73, VK_ZOOM}, 1122 | #line 28 "win32_vk.list" 1123 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str74, VK_KANJI}, 1124 | #line 48 "win32_vk.list" 1125 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str75, VK_DELETE}, 1126 | #line 128 "win32_vk.list" 1127 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str76, VK_OEM_COMMA}, 1128 | #line 67 "win32_vk.list" 1129 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str77, VK_SUBTRACT}, 1130 | {-1}, 1131 | #line 10 "win32_vk.list" 1132 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str79, VK_MBUTTON}, 1133 | #line 78 "win32_vk.list" 1134 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str80, VK_F9}, 1135 | #line 17 "win32_vk.list" 1136 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str81, VK_SHIFT}, 1137 | #line 103 "win32_vk.list" 1138 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str82, VK_RSHIFT}, 1139 | #line 102 "win32_vk.list" 1140 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str83, VK_LSHIFT}, 1141 | #line 65 "win32_vk.list" 1142 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str84, VK_ADD}, 1143 | #line 31 "win32_vk.list" 1144 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str85, VK_NONCONVERT}, 1145 | #line 160 "win32_vk.list" 1146 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str86, VK_EXSEL}, 1147 | #line 126 "win32_vk.list" 1148 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str87, VK_OEM_1}, 1149 | #line 138 "win32_vk.list" 1150 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str88, VK_OEM_AX}, 1151 | #line 108 "win32_vk.list" 1152 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str89, VK_BROWSER_BACK}, 1153 | #line 137 "win32_vk.list" 1154 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str90, VK_OEM_8}, 1155 | #line 129 "win32_vk.list" 1156 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str91, VK_OEM_MINUS}, 1157 | #line 162 "win32_vk.list" 1158 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str92, VK_PLAY}, 1159 | #line 131 "win32_vk.list" 1160 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str93, VK_OEM_2}, 1161 | #line 15 "win32_vk.list" 1162 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str94, VK_CLEAR}, 1163 | #line 99 "win32_vk.list" 1164 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str95, VK_OEM_FJ_TOUROKU}, 1165 | #line 147 "win32_vk.list" 1166 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str96, VK_OEM_PA1}, 1167 | #line 140 "win32_vk.list" 1168 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str97, VK_ICO_HELP}, 1169 | #line 112 "win32_vk.list" 1170 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str98, VK_BROWSER_SEARCH}, 1171 | #line 53 "win32_vk.list" 1172 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str99, VK_SLEEP}, 1173 | {-1}, 1174 | #line 70 "win32_vk.list" 1175 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str101, VK_F1}, 1176 | #line 148 "win32_vk.list" 1177 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str102, VK_OEM_PA2}, 1178 | #line 154 "win32_vk.list" 1179 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str103, VK_OEM_COPY}, 1180 | #line 77 "win32_vk.list" 1181 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str104, VK_F8}, 1182 | #line 88 "win32_vk.list" 1183 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str105, VK_F19}, 1184 | #line 41 "win32_vk.list" 1185 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str106, VK_RIGHT}, 1186 | #line 71 "win32_vk.list" 1187 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str107, VK_F2}, 1188 | #line 135 "win32_vk.list" 1189 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str108, VK_OEM_6}, 1190 | #line 87 "win32_vk.list" 1191 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str109, VK_F18}, 1192 | {-1}, 1193 | #line 117 "win32_vk.list" 1194 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str111, VK_VOLUME_UP}, 1195 | {-1}, {-1}, 1196 | #line 120 "win32_vk.list" 1197 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str114, VK_MEDIA_STOP}, 1198 | #line 130 "win32_vk.list" 1199 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str115, VK_OEM_PERIOD}, 1200 | {-1}, 1201 | #line 161 "win32_vk.list" 1202 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str117, VK_EREOF}, 1203 | {-1}, {-1}, {-1}, 1204 | #line 114 "win32_vk.list" 1205 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str121, VK_BROWSER_HOME}, 1206 | #line 75 "win32_vk.list" 1207 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str122, VK_F6}, 1208 | {-1}, 1209 | #line 110 "win32_vk.list" 1210 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str124, VK_BROWSER_REFRESH}, 1211 | {-1}, 1212 | #line 165 "win32_vk.list" 1213 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str126, VK_PA1}, 1214 | #line 142 "win32_vk.list" 1215 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str127, VK_PROCESSKEY}, 1216 | #line 68 "win32_vk.list" 1217 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str128, VK_DECIMAL}, 1218 | #line 132 "win32_vk.list" 1219 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str129, VK_OEM_3}, 1220 | #line 107 "win32_vk.list" 1221 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str130, VK_RMENU}, 1222 | #line 106 "win32_vk.list" 1223 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str131, VK_LMENU}, 1224 | #line 98 "win32_vk.list" 1225 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str132, VK_OEM_FJ_MASSHOU}, 1226 | #line 54 "win32_vk.list" 1227 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str133, VK_NUMPAD0}, 1228 | #line 24 "win32_vk.list" 1229 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str134, VK_HANGUL}, 1230 | #line 63 "win32_vk.list" 1231 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str135, VK_NUMPAD9}, 1232 | #line 23 "win32_vk.list" 1233 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str136, VK_HANGEUL}, 1234 | #line 134 "win32_vk.list" 1235 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str137, VK_OEM_5}, 1236 | #line 149 "win32_vk.list" 1237 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str138, VK_OEM_PA3}, 1238 | #line 115 "win32_vk.list" 1239 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str139, VK_VOLUME_MUTE}, 1240 | #line 133 "win32_vk.list" 1241 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str140, VK_OEM_4}, 1242 | #line 122 "win32_vk.list" 1243 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str141, VK_LAUNCH_MAIL}, 1244 | #line 97 "win32_vk.list" 1245 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str142, VK_OEM_FJ_JISHO}, 1246 | #line 72 "win32_vk.list" 1247 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str143, VK_F3}, 1248 | #line 101 "win32_vk.list" 1249 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str144, VK_OEM_FJ_ROYA}, 1250 | #line 100 "win32_vk.list" 1251 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str145, VK_OEM_FJ_LOYA}, 1252 | {-1}, 1253 | #line 42 "win32_vk.list" 1254 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str147, VK_DOWN}, 1255 | {-1}, 1256 | #line 153 "win32_vk.list" 1257 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str149, VK_OEM_FINISH}, 1258 | {-1}, 1259 | #line 74 "win32_vk.list" 1260 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str151, VK_F5}, 1261 | {-1}, 1262 | #line 136 "win32_vk.list" 1263 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str153, VK_OEM_7}, 1264 | #line 73 "win32_vk.list" 1265 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str154, VK_F4}, 1266 | #line 86 "win32_vk.list" 1267 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str155, VK_F17}, 1268 | #line 55 "win32_vk.list" 1269 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str156, VK_NUMPAD1}, 1270 | #line 141 "win32_vk.list" 1271 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str157, VK_ICO_00}, 1272 | {-1}, 1273 | #line 62 "win32_vk.list" 1274 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str159, VK_NUMPAD8}, 1275 | {-1}, {-1}, 1276 | #line 56 "win32_vk.list" 1277 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str162, VK_NUMPAD2}, 1278 | {-1}, 1279 | #line 124 "win32_vk.list" 1280 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str164, VK_LAUNCH_APP1}, 1281 | #line 109 "win32_vk.list" 1282 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str165, VK_BROWSER_FORWARD}, 1283 | {-1}, 1284 | #line 76 "win32_vk.list" 1285 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str167, VK_F7}, 1286 | {-1}, {-1}, 1287 | #line 125 "win32_vk.list" 1288 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str170, VK_LAUNCH_APP2}, 1289 | #line 64 "win32_vk.list" 1290 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str171, VK_MULTIPLY}, 1291 | {-1}, {-1}, 1292 | #line 45 "win32_vk.list" 1293 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str174, VK_EXECUTE}, 1294 | {-1}, 1295 | #line 113 "win32_vk.list" 1296 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str176, VK_BROWSER_FAVORITES}, 1297 | #line 60 "win32_vk.list" 1298 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str177, VK_NUMPAD6}, 1299 | {-1}, 1300 | #line 85 "win32_vk.list" 1301 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str179, VK_F16}, 1302 | {-1}, {-1}, 1303 | #line 79 "win32_vk.list" 1304 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str182, VK_F10}, 1305 | {-1}, {-1}, 1306 | #line 116 "win32_vk.list" 1307 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str185, VK_VOLUME_DOWN}, 1308 | {-1}, {-1}, 1309 | #line 89 "win32_vk.list" 1310 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str188, VK_F20}, 1311 | #line 119 "win32_vk.list" 1312 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str189, VK_MEDIA_PREV_TRACK}, 1313 | {-1}, 1314 | #line 33 "win32_vk.list" 1315 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str191, VK_MODECHANGE}, 1316 | {-1}, {-1}, {-1}, {-1}, {-1}, 1317 | #line 83 "win32_vk.list" 1318 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str197, VK_F14}, 1319 | #line 57 "win32_vk.list" 1320 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str198, VK_NUMPAD3}, 1321 | #line 11 "win32_vk.list" 1322 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str199, VK_XBUTTON1}, 1323 | {-1}, {-1}, {-1}, 1324 | #line 93 "win32_vk.list" 1325 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str203, VK_F24}, 1326 | {-1}, 1327 | #line 12 "win32_vk.list" 1328 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str205, VK_XBUTTON2}, 1329 | #line 59 "win32_vk.list" 1330 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str206, VK_NUMPAD5}, 1331 | {-1}, {-1}, 1332 | #line 58 "win32_vk.list" 1333 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str209, VK_NUMPAD4}, 1334 | {-1}, {-1}, {-1}, {-1}, {-1}, 1335 | #line 121 "win32_vk.list" 1336 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str215, VK_MEDIA_PLAY_PAUSE}, 1337 | {-1}, 1338 | #line 123 "win32_vk.list" 1339 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str217, VK_LAUNCH_MEDIA_SELECT}, 1340 | #line 80 "win32_vk.list" 1341 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str218, VK_F11}, 1342 | {-1}, 1343 | #line 139 "win32_vk.list" 1344 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str220, VK_OEM_102}, 1345 | #line 118 "win32_vk.list" 1346 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str221, VK_MEDIA_NEXT_TRACK}, 1347 | #line 61 "win32_vk.list" 1348 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str222, VK_NUMPAD7}, 1349 | {-1}, 1350 | #line 90 "win32_vk.list" 1351 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str224, VK_F21}, 1352 | {-1}, 1353 | #line 82 "win32_vk.list" 1354 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str226, VK_F13}, 1355 | {-1}, {-1}, 1356 | #line 81 "win32_vk.list" 1357 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str229, VK_F12}, 1358 | {-1}, {-1}, 1359 | #line 92 "win32_vk.list" 1360 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str232, VK_F23}, 1361 | {-1}, {-1}, 1362 | #line 91 "win32_vk.list" 1363 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str235, VK_F22}, 1364 | {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, 1365 | #line 84 "win32_vk.list" 1366 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str242, VK_F15}, 1367 | {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, 1368 | {-1}, {-1}, {-1}, {-1}, 1369 | #line 69 "win32_vk.list" 1370 | {(int)(size_t)&((struct stringpool_t *)0)->stringpool_str256, VK_DIVIDE} 1371 | }; 1372 | 1373 | if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) 1374 | { 1375 | register unsigned int key = hash (str, len); 1376 | 1377 | if (key <= MAX_HASH_VALUE) 1378 | { 1379 | register int o = wordlist[key].ofs; 1380 | if (o >= 0) 1381 | { 1382 | register const char *s = o + stringpool; 1383 | 1384 | if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strcmp (str, s)) 1385 | return &wordlist[key]; 1386 | } 1387 | } 1388 | } 1389 | return 0; 1390 | } 1391 | -------------------------------------------------------------------------------- /ext/io/console/console.c: -------------------------------------------------------------------------------- 1 | /* -*- c-file-style: "ruby"; indent-tabs-mode: t -*- */ 2 | /* 3 | * console IO module 4 | */ 5 | 6 | static const char *const 7 | IO_CONSOLE_VERSION = "0.8.2"; 8 | 9 | #include "ruby.h" 10 | #include "ruby/io.h" 11 | #include "ruby/thread.h" 12 | 13 | #ifdef HAVE_UNISTD_H 14 | #include 15 | #endif 16 | #ifdef HAVE_FCNTL_H 17 | #include 18 | #endif 19 | #ifdef HAVE_SYS_IOCTL_H 20 | #include 21 | #endif 22 | 23 | #if defined HAVE_TERMIOS_H 24 | # include 25 | typedef struct termios conmode; 26 | 27 | static int 28 | setattr(int fd, conmode *t) 29 | { 30 | while (tcsetattr(fd, TCSANOW, t)) { 31 | if (errno != EINTR) return 0; 32 | } 33 | return 1; 34 | } 35 | # define getattr(fd, t) (tcgetattr(fd, t) == 0) 36 | #elif defined HAVE_TERMIO_H 37 | # include 38 | typedef struct termio conmode; 39 | # define setattr(fd, t) (ioctl(fd, TCSETAF, t) == 0) 40 | # define getattr(fd, t) (ioctl(fd, TCGETA, t) == 0) 41 | #elif defined HAVE_SGTTY_H 42 | # include 43 | typedef struct sgttyb conmode; 44 | # ifdef HAVE_STTY 45 | # define setattr(fd, t) (stty(fd, t) == 0) 46 | # else 47 | # define setattr(fd, t) (ioctl((fd), TIOCSETP, (t)) == 0) 48 | # endif 49 | # ifdef HAVE_GTTY 50 | # define getattr(fd, t) (gtty(fd, t) == 0) 51 | # else 52 | # define getattr(fd, t) (ioctl((fd), TIOCGETP, (t)) == 0) 53 | # endif 54 | #elif defined _WIN32 55 | #include 56 | #include 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 | 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)) 90 | #endif 91 | 92 | #if defined HAVE_RUBY_FIBER_SCHEDULER_H 93 | # include "ruby/fiber/scheduler.h" 94 | #elif defined HAVE_RB_SCHEDULER_TIMEOUT 95 | extern VALUE rb_scheduler_timeout(struct timeval *timeout); 96 | # define rb_fiber_scheduler_make_timeout rb_scheduler_timeout 97 | #endif 98 | 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) 141 | 142 | #ifndef HAVE_RB_F_SEND 143 | #ifndef RB_PASS_CALLED_KEYWORDS 144 | # define rb_funcallv_kw(recv, mid, arg, argv, kw_splat) rb_funcallv(recv, mid, arg, argv) 145 | #endif 146 | 147 | static ID id___send__; 148 | 149 | static VALUE 150 | rb_f_send(int argc, VALUE *argv, VALUE recv) 151 | { 152 | VALUE sym = argv[0]; 153 | ID vid = rb_check_id(&sym); 154 | if (vid) { 155 | --argc; 156 | ++argv; 157 | } 158 | else { 159 | vid = id___send__; 160 | } 161 | return rb_funcallv_kw(recv, vid, argc, argv, RB_PASS_CALLED_KEYWORDS); 162 | } 163 | #endif 164 | 165 | enum rawmode_opt_ids { 166 | kwd_min, 167 | kwd_time, 168 | kwd_intr, 169 | rawmode_opt_id_count 170 | }; 171 | static ID rawmode_opt_ids[rawmode_opt_id_count]; 172 | 173 | typedef struct { 174 | int vmin; 175 | int vtime; 176 | int intr; 177 | } rawmode_arg_t; 178 | 179 | #ifndef UNDEF_P 180 | # define UNDEF_P(obj) ((obj) == Qundef) 181 | #endif 182 | #ifndef NIL_OR_UNDEF_P 183 | # define NIL_OR_UNDEF_P(obj) (NIL_P(obj) || UNDEF_P(obj)) 184 | #endif 185 | 186 | static rawmode_arg_t * 187 | rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts) 188 | { 189 | int argc = *argcp; 190 | rawmode_arg_t *optp = NULL; 191 | VALUE vopts = Qnil; 192 | VALUE optvals[rawmode_opt_id_count]; 193 | #ifdef RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 194 | argc = rb_scan_args(argc, argv, "*:", NULL, &vopts); 195 | #else 196 | if (argc > min_argc) { 197 | vopts = rb_check_hash_type(argv[argc-1]); 198 | if (!NIL_P(vopts)) { 199 | argv[argc-1] = vopts; 200 | vopts = rb_extract_keywords(&argv[argc-1]); 201 | if (!argv[argc-1]) *argcp = --argc; 202 | if (!vopts) vopts = Qnil; 203 | } 204 | } 205 | #endif 206 | rb_check_arity(argc, min_argc, max_argc); 207 | if (rb_get_kwargs(vopts, rawmode_opt_ids, 208 | 0, rawmode_opt_id_count, optvals)) { 209 | VALUE vmin = optvals[kwd_min]; 210 | VALUE vtime = optvals[kwd_time]; 211 | VALUE intr = optvals[kwd_intr]; 212 | /* default values by `stty raw` */ 213 | opts->vmin = 1; 214 | opts->vtime = 0; 215 | opts->intr = 0; 216 | if (!NIL_OR_UNDEF_P(vmin)) { 217 | opts->vmin = NUM2INT(vmin); 218 | optp = opts; 219 | } 220 | if (!NIL_OR_UNDEF_P(vtime)) { 221 | VALUE v10 = INT2FIX(10); 222 | vtime = rb_funcall3(vtime, '*', 1, &v10); 223 | opts->vtime = NUM2INT(vtime); 224 | optp = opts; 225 | } 226 | switch (intr) { 227 | case Qtrue: 228 | opts->intr = 1; 229 | optp = opts; 230 | break; 231 | case Qfalse: 232 | opts->intr = 0; 233 | optp = opts; 234 | break; 235 | case Qundef: 236 | case Qnil: 237 | break; 238 | default: 239 | rb_raise(rb_eArgError, "true or false expected as intr: %"PRIsVALUE, 240 | intr); 241 | } 242 | } 243 | return optp; 244 | } 245 | 246 | static void 247 | set_rawmode(conmode *t, void *arg) 248 | { 249 | #ifdef HAVE_CFMAKERAW 250 | cfmakeraw(t); 251 | t->c_lflag &= ~(ECHOE|ECHOK); 252 | #elif defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 253 | t->c_iflag &= ~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL); 254 | t->c_oflag &= ~OPOST; 255 | t->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN|XCASE); 256 | t->c_cflag &= ~(CSIZE|PARENB); 257 | t->c_cflag |= CS8; 258 | t->c_cc[VMIN] = 1; 259 | t->c_cc[VTIME] = 0; 260 | #elif defined HAVE_SGTTY_H 261 | t->sg_flags &= ~ECHO; 262 | t->sg_flags |= RAW; 263 | #elif defined _WIN32 264 | *t = 0; 265 | #endif 266 | if (arg) { 267 | const rawmode_arg_t *r = arg; 268 | #ifdef VMIN 269 | if (r->vmin >= 0) t->c_cc[VMIN] = r->vmin; 270 | #endif 271 | #ifdef VTIME 272 | if (r->vtime >= 0) t->c_cc[VTIME] = r->vtime; 273 | #endif 274 | #ifdef ISIG 275 | if (r->intr) { 276 | t->c_iflag |= BRKINT; 277 | t->c_lflag |= ISIG; 278 | t->c_oflag |= OPOST; 279 | } 280 | #endif 281 | (void)r; 282 | } 283 | } 284 | 285 | static void 286 | set_cookedmode(conmode *t, void *arg) 287 | { 288 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 289 | t->c_iflag |= (BRKINT|ISTRIP|ICRNL|IXON); 290 | t->c_oflag |= OPOST; 291 | t->c_lflag |= (ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|IEXTEN); 292 | #elif defined HAVE_SGTTY_H 293 | t->sg_flags |= ECHO; 294 | t->sg_flags &= ~RAW; 295 | #elif defined _WIN32 296 | *t |= ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT; 297 | #endif 298 | } 299 | 300 | static void 301 | set_noecho(conmode *t, void *arg) 302 | { 303 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 304 | t->c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); 305 | #elif defined HAVE_SGTTY_H 306 | t->sg_flags &= ~ECHO; 307 | #elif defined _WIN32 308 | *t &= ~ENABLE_ECHO_INPUT; 309 | #endif 310 | } 311 | 312 | static void 313 | set_echo(conmode *t, void *arg) 314 | { 315 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 316 | t->c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL); 317 | #elif defined HAVE_SGTTY_H 318 | t->sg_flags |= ECHO; 319 | #elif defined _WIN32 320 | *t |= ENABLE_ECHO_INPUT; 321 | #endif 322 | } 323 | 324 | static int 325 | echo_p(conmode *t) 326 | { 327 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 328 | return (t->c_lflag & (ECHO | ECHONL)) != 0; 329 | #elif defined HAVE_SGTTY_H 330 | return (t->sg_flags & ECHO) != 0; 331 | #elif defined _WIN32 332 | return (*t & ENABLE_ECHO_INPUT) != 0; 333 | #endif 334 | } 335 | 336 | static int 337 | set_ttymode(int fd, conmode *t, void (*setter)(conmode *, void *), void *arg) 338 | { 339 | conmode r; 340 | if (!getattr(fd, t)) return 0; 341 | r = *t; 342 | setter(&r, arg); 343 | return setattr(fd, &r); 344 | } 345 | 346 | #define GetReadFD(io) rb_io_descriptor(io) 347 | #define GetWriteFD(io) rb_io_descriptor(rb_io_get_write_io(io)) 348 | 349 | #define FD_PER_IO 2 350 | 351 | static VALUE 352 | ttymode(VALUE io, VALUE (*func)(VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg) 353 | { 354 | int status = -1; 355 | int error = 0; 356 | int fd[FD_PER_IO]; 357 | conmode t[FD_PER_IO]; 358 | VALUE result = Qnil; 359 | 360 | fd[0] = GetReadFD(io); 361 | if (fd[0] != -1) { 362 | if (set_ttymode(fd[0], t+0, setter, arg)) { 363 | status = 0; 364 | } 365 | else { 366 | error = errno; 367 | fd[0] = -1; 368 | } 369 | } 370 | fd[1] = GetWriteFD(io); 371 | if (fd[1] != -1 && fd[1] != fd[0]) { 372 | if (set_ttymode(fd[1], t+1, setter, arg)) { 373 | status = 0; 374 | } 375 | else { 376 | error = errno; 377 | fd[1] = -1; 378 | } 379 | } 380 | if (status == 0) { 381 | result = rb_protect(func, farg, &status); 382 | } 383 | if (fd[0] != -1 && fd[0] == GetReadFD(io)) { 384 | if (!setattr(fd[0], t+0)) { 385 | error = errno; 386 | status = -1; 387 | } 388 | } 389 | if (fd[1] != -1 && fd[1] != fd[0] && fd[1] == GetWriteFD(io)) { 390 | if (!setattr(fd[1], t+1)) { 391 | error = errno; 392 | status = -1; 393 | } 394 | } 395 | if (status) { 396 | if (status == -1) { 397 | rb_syserr_fail(error, 0); 398 | } 399 | rb_jump_tag(status); 400 | } 401 | return result; 402 | } 403 | 404 | #if !defined _WIN32 405 | struct ttymode_callback_args { 406 | VALUE (*func)(VALUE, VALUE); 407 | VALUE io; 408 | VALUE farg; 409 | }; 410 | 411 | static VALUE 412 | ttymode_callback(VALUE args) 413 | { 414 | struct ttymode_callback_args *argp = (struct ttymode_callback_args *)args; 415 | return argp->func(argp->io, argp->farg); 416 | } 417 | 418 | static VALUE 419 | ttymode_with_io(VALUE io, VALUE (*func)(VALUE, VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg) 420 | { 421 | struct ttymode_callback_args cargs; 422 | cargs.func = func; 423 | cargs.io = io; 424 | cargs.farg = farg; 425 | return ttymode(io, ttymode_callback, (VALUE)&cargs, setter, arg); 426 | } 427 | #endif 428 | 429 | /* 430 | * call-seq: 431 | * io.raw(min: nil, time: nil, intr: nil) {|io| } 432 | * 433 | * Yields +self+ within raw mode, and returns the result of the block. 434 | * 435 | * STDIN.raw(&:gets) 436 | * 437 | * will read and return a line without echo back and line editing. 438 | * 439 | * The parameter +min+ specifies the minimum number of bytes that 440 | * should be received when a read operation is performed. (default: 1) 441 | * 442 | * The parameter +time+ specifies the timeout in _seconds_ with a 443 | * precision of 1/10 of a second. (default: 0) 444 | * 445 | * If the parameter +intr+ is +true+, enables break, interrupt, quit, 446 | * and suspend special characters. 447 | * 448 | * Refer to the manual page of termios for further details. 449 | * 450 | * You must require 'io/console' to use this method. 451 | */ 452 | static VALUE 453 | console_raw(int argc, VALUE *argv, VALUE io) 454 | { 455 | rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); 456 | return ttymode(io, rb_yield, io, set_rawmode, optp); 457 | } 458 | 459 | /* 460 | * call-seq: 461 | * io.raw!(min: nil, time: nil, intr: nil) -> io 462 | * 463 | * Enables raw mode, and returns +io+. 464 | * 465 | * If the terminal mode needs to be back, use io.raw { ... }. 466 | * 467 | * See IO#raw for details on the parameters. 468 | * 469 | * You must require 'io/console' to use this method. 470 | */ 471 | static VALUE 472 | console_set_raw(int argc, VALUE *argv, VALUE io) 473 | { 474 | conmode t; 475 | rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); 476 | int fd = GetReadFD(io); 477 | if (!getattr(fd, &t)) sys_fail(io); 478 | set_rawmode(&t, optp); 479 | if (!setattr(fd, &t)) sys_fail(io); 480 | return io; 481 | } 482 | 483 | /* 484 | * call-seq: 485 | * io.cooked {|io| } 486 | * 487 | * Yields +self+ within cooked mode. 488 | * 489 | * STDIN.cooked(&:gets) 490 | * 491 | * will read and return a line with echo back and line editing. 492 | * 493 | * You must require 'io/console' to use this method. 494 | */ 495 | static VALUE 496 | console_cooked(VALUE io) 497 | { 498 | return ttymode(io, rb_yield, io, set_cookedmode, NULL); 499 | } 500 | 501 | /* 502 | * call-seq: 503 | * io.cooked! 504 | * 505 | * Enables cooked mode. 506 | * 507 | * If the terminal mode needs to be back, use io.cooked { ... }. 508 | * 509 | * You must require 'io/console' to use this method. 510 | */ 511 | static VALUE 512 | console_set_cooked(VALUE io) 513 | { 514 | conmode t; 515 | int fd = GetReadFD(io); 516 | if (!getattr(fd, &t)) sys_fail(io); 517 | set_cookedmode(&t, NULL); 518 | if (!setattr(fd, &t)) sys_fail(io); 519 | return io; 520 | } 521 | 522 | #ifndef _WIN32 523 | static VALUE 524 | getc_call(VALUE io) 525 | { 526 | return rb_funcallv(io, id_getc, 0, 0); 527 | } 528 | #else 529 | static void * 530 | nogvl_getch(void *p) 531 | { 532 | int len = 0; 533 | wint_t *buf = p, c = _getwch(); 534 | 535 | switch (c) { 536 | case WEOF: 537 | break; 538 | case 0x00: 539 | case 0xe0: 540 | buf[len++] = c; 541 | c = _getwch(); 542 | /* fall through */ 543 | default: 544 | buf[len++] = c; 545 | break; 546 | } 547 | return (void *)(VALUE)len; 548 | } 549 | #endif 550 | 551 | /* 552 | * call-seq: 553 | * io.getch(min: nil, time: nil, intr: nil) -> char 554 | * 555 | * Reads and returns a character in raw mode. 556 | * 557 | * See IO#raw for details on the parameters. 558 | * 559 | * You must require 'io/console' to use this method. 560 | */ 561 | static VALUE 562 | console_getch(int argc, VALUE *argv, VALUE io) 563 | { 564 | rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); 565 | #ifndef _WIN32 566 | return ttymode(io, getc_call, io, set_rawmode, optp); 567 | #else 568 | rb_io_t *fptr; 569 | VALUE str; 570 | wint_t c; 571 | int len; 572 | char buf[8]; 573 | wint_t wbuf[2]; 574 | # ifndef HAVE_RB_IO_WAIT 575 | struct timeval *to = NULL, tv; 576 | # else 577 | VALUE timeout = Qnil; 578 | # endif 579 | 580 | GetOpenFile(io, fptr); 581 | if (optp) { 582 | if (optp->vtime) { 583 | # ifndef HAVE_RB_IO_WAIT 584 | to = &tv; 585 | # else 586 | struct timeval tv; 587 | # endif 588 | tv.tv_sec = optp->vtime / 10; 589 | tv.tv_usec = (optp->vtime % 10) * 100000; 590 | # ifdef HAVE_RB_IO_WAIT 591 | timeout = rb_fiber_scheduler_make_timeout(&tv); 592 | # endif 593 | } 594 | switch (optp->vmin) { 595 | case 1: /* default */ 596 | break; 597 | case 0: /* return nil when timed out */ 598 | if (optp->vtime) break; 599 | /* fallthru */ 600 | default: 601 | rb_warning("min option larger than 1 ignored"); 602 | } 603 | if (optp->intr) { 604 | # ifndef HAVE_RB_IO_WAIT 605 | int w = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, to); 606 | if (w < 0) rb_eof_error(); 607 | if (!(w & RB_WAITFD_IN)) return Qnil; 608 | # else 609 | VALUE result = rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), timeout); 610 | if (!RTEST(result)) return Qnil; 611 | # endif 612 | } 613 | else if (optp->vtime) { 614 | rb_warning("Non-zero vtime option ignored if intr flag is unset"); 615 | } 616 | } 617 | len = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getch, wbuf, RUBY_UBF_IO, 0); 618 | switch (len) { 619 | case 0: 620 | return Qnil; 621 | case 2: 622 | buf[0] = (char)wbuf[0]; 623 | c = wbuf[1]; 624 | len = 1; 625 | do { 626 | buf[len++] = (unsigned char)c; 627 | } while ((c >>= CHAR_BIT) && len < (int)sizeof(buf)); 628 | return rb_str_new(buf, len); 629 | default: 630 | c = wbuf[0]; 631 | len = rb_uv_to_utf8(buf, c); 632 | str = rb_utf8_str_new(buf, len); 633 | return rb_str_conv_enc(str, NULL, rb_default_external_encoding()); 634 | } 635 | #endif 636 | } 637 | 638 | /* 639 | * call-seq: 640 | * io.noecho {|io| } 641 | * 642 | * Yields +self+ with disabling echo back. 643 | * 644 | * STDIN.noecho(&:gets) 645 | * 646 | * will read and return a line without echo back. 647 | * 648 | * You must require 'io/console' to use this method. 649 | */ 650 | static VALUE 651 | console_noecho(VALUE io) 652 | { 653 | return ttymode(io, rb_yield, io, set_noecho, NULL); 654 | } 655 | 656 | /* 657 | * call-seq: 658 | * io.echo = flag 659 | * 660 | * Enables/disables echo back. 661 | * On some platforms, all combinations of this flags and raw/cooked 662 | * mode may not be valid. 663 | * 664 | * You must require 'io/console' to use this method. 665 | */ 666 | static VALUE 667 | console_set_echo(VALUE io, VALUE f) 668 | { 669 | conmode t; 670 | int fd = GetReadFD(io); 671 | 672 | if (!getattr(fd, &t)) sys_fail(io); 673 | 674 | if (RTEST(f)) 675 | set_echo(&t, NULL); 676 | else 677 | set_noecho(&t, NULL); 678 | 679 | if (!setattr(fd, &t)) sys_fail(io); 680 | 681 | return io; 682 | } 683 | 684 | /* 685 | * call-seq: 686 | * io.echo? -> true or false 687 | * 688 | * Returns +true+ if echo back is enabled. 689 | * 690 | * You must require 'io/console' to use this method. 691 | */ 692 | static VALUE 693 | console_echo_p(VALUE io) 694 | { 695 | conmode t; 696 | int fd = GetReadFD(io); 697 | 698 | if (!getattr(fd, &t)) sys_fail(io); 699 | return echo_p(&t) ? Qtrue : Qfalse; 700 | } 701 | 702 | static const rb_data_type_t conmode_type = { 703 | "console-mode", 704 | {0, RUBY_TYPED_DEFAULT_FREE,}, 705 | 0, 0, 706 | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, 707 | }; 708 | static VALUE cConmode; 709 | 710 | static VALUE 711 | conmode_alloc(VALUE klass) 712 | { 713 | return rb_data_typed_object_zalloc(klass, sizeof(conmode), &conmode_type); 714 | } 715 | 716 | static VALUE 717 | conmode_new(VALUE klass, const conmode *t) 718 | { 719 | VALUE obj = conmode_alloc(klass); 720 | *(conmode *)DATA_PTR(obj) = *t; 721 | return obj; 722 | } 723 | 724 | static VALUE 725 | conmode_init_copy(VALUE obj, VALUE obj2) 726 | { 727 | conmode *t = rb_check_typeddata(obj, &conmode_type); 728 | conmode *t2 = rb_check_typeddata(obj2, &conmode_type); 729 | *t = *t2; 730 | return obj; 731 | } 732 | 733 | static VALUE 734 | conmode_set_echo(VALUE obj, VALUE f) 735 | { 736 | conmode *t = rb_check_typeddata(obj, &conmode_type); 737 | if (RTEST(f)) 738 | set_echo(t, NULL); 739 | else 740 | set_noecho(t, NULL); 741 | return obj; 742 | } 743 | 744 | static VALUE 745 | conmode_set_raw(int argc, VALUE *argv, VALUE obj) 746 | { 747 | conmode *t = rb_check_typeddata(obj, &conmode_type); 748 | rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); 749 | 750 | set_rawmode(t, optp); 751 | return obj; 752 | } 753 | 754 | static VALUE 755 | conmode_raw_new(int argc, VALUE *argv, VALUE obj) 756 | { 757 | conmode *r = rb_check_typeddata(obj, &conmode_type); 758 | conmode t = *r; 759 | rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts); 760 | 761 | set_rawmode(&t, optp); 762 | return conmode_new(rb_obj_class(obj), &t); 763 | } 764 | 765 | /* 766 | * call-seq: 767 | * io.console_mode -> mode 768 | * 769 | * Returns a data represents the current console mode. 770 | * 771 | * You must require 'io/console' to use this method. 772 | */ 773 | static VALUE 774 | console_conmode_get(VALUE io) 775 | { 776 | conmode t; 777 | int fd = GetReadFD(io); 778 | 779 | if (!getattr(fd, &t)) sys_fail(io); 780 | 781 | return conmode_new(cConmode, &t); 782 | } 783 | 784 | /* 785 | * call-seq: 786 | * io.console_mode = mode 787 | * 788 | * Sets the console mode to +mode+. 789 | * 790 | * You must require 'io/console' to use this method. 791 | */ 792 | static VALUE 793 | console_conmode_set(VALUE io, VALUE mode) 794 | { 795 | conmode *t, r; 796 | int fd = GetReadFD(io); 797 | 798 | TypedData_Get_Struct(mode, conmode, &conmode_type, t); 799 | r = *t; 800 | 801 | if (!setattr(fd, &r)) sys_fail(io); 802 | 803 | return mode; 804 | } 805 | 806 | #if defined TIOCGWINSZ 807 | typedef struct winsize rb_console_size_t; 808 | #define getwinsize(fd, buf) (ioctl((fd), TIOCGWINSZ, (buf)) == 0) 809 | #define setwinsize(fd, buf) (ioctl((fd), TIOCSWINSZ, (buf)) == 0) 810 | #define winsize_row(buf) (buf)->ws_row 811 | #define winsize_col(buf) (buf)->ws_col 812 | #elif defined _WIN32 813 | typedef CONSOLE_SCREEN_BUFFER_INFO rb_console_size_t; 814 | #define getwinsize(fd, buf) ( \ 815 | GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), (buf)) || \ 816 | SET_LAST_ERROR) 817 | #define winsize_row(buf) ((buf)->srWindow.Bottom - (buf)->srWindow.Top + 1) 818 | #define winsize_col(buf) (buf)->dwSize.X 819 | #endif 820 | 821 | #if defined TIOCGWINSZ || defined _WIN32 822 | #define USE_CONSOLE_GETSIZE 1 823 | #endif 824 | 825 | #ifdef USE_CONSOLE_GETSIZE 826 | /* 827 | * call-seq: 828 | * io.winsize -> [rows, columns] 829 | * 830 | * Returns console size. 831 | * 832 | * You must require 'io/console' to use this method. 833 | */ 834 | static VALUE 835 | console_winsize(VALUE io) 836 | { 837 | rb_console_size_t ws; 838 | int fd = GetWriteFD(io); 839 | if (!getwinsize(fd, &ws)) sys_fail(io); 840 | return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws))); 841 | } 842 | 843 | static VALUE console_scroll(VALUE io, int line); 844 | static VALUE console_goto(VALUE io, VALUE y, VALUE x); 845 | 846 | /* 847 | * call-seq: 848 | * io.winsize = [rows, columns] 849 | * 850 | * Tries to set console size. The effect depends on the platform and 851 | * the running environment. 852 | * 853 | * You must require 'io/console' to use this method. 854 | */ 855 | static VALUE 856 | console_set_winsize(VALUE io, VALUE size) 857 | { 858 | rb_console_size_t ws; 859 | #if defined _WIN32 860 | HANDLE wh; 861 | int newrow, newcol; 862 | COORD oldsize; 863 | SMALL_RECT oldwindow; 864 | BOOL ret; 865 | #endif 866 | VALUE row, col, xpixel, ypixel; 867 | const VALUE *sz; 868 | long sizelen; 869 | int fd; 870 | 871 | size = rb_Array(size); 872 | if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) { 873 | rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen); 874 | } 875 | sz = RARRAY_CONST_PTR(size); 876 | row = sz[0], col = sz[1], xpixel = ypixel = Qnil; 877 | if (sizelen == 4) xpixel = sz[2], ypixel = sz[3]; 878 | fd = GetWriteFD(io); 879 | #if defined TIOCSWINSZ 880 | ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0; 881 | #define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m) 882 | SET(row); 883 | SET(col); 884 | SET(xpixel); 885 | SET(ypixel); 886 | #undef SET 887 | if (!setwinsize(fd, &ws)) sys_fail(io); 888 | #elif defined _WIN32 889 | wh = (HANDLE)rb_w32_get_osfhandle(fd); 890 | #define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m) 891 | SET(row); 892 | SET(col); 893 | #undef SET 894 | if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel); 895 | if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel); 896 | if (!GetConsoleScreenBufferInfo(wh, &ws)) { 897 | rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo"); 898 | } 899 | oldsize = ws.dwSize; 900 | oldwindow = ws.srWindow; 901 | if (ws.srWindow.Right + 1 < newcol) { 902 | ws.dwSize.X = newcol; 903 | } 904 | if (ws.dwSize.Y < newrow) { 905 | ws.dwSize.Y = newrow; 906 | } 907 | /* expand screen buffer first if needed */ 908 | if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) { 909 | rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); 910 | } 911 | /* refresh ws for new dwMaximumWindowSize */ 912 | if (!GetConsoleScreenBufferInfo(wh, &ws)) { 913 | rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo"); 914 | } 915 | /* check new size before modifying buffer content */ 916 | if (newrow <= 0 || newcol <= 0 || 917 | newrow > ws.dwMaximumWindowSize.Y || 918 | newcol > ws.dwMaximumWindowSize.X) { 919 | SetConsoleScreenBufferSize(wh, oldsize); 920 | /* remove scrollbar if possible */ 921 | SetConsoleWindowInfo(wh, TRUE, &oldwindow); 922 | rb_raise(rb_eArgError, "out of range winsize: [%d, %d]", newrow, newcol); 923 | } 924 | /* shrink screen buffer width */ 925 | ws.dwSize.X = newcol; 926 | /* shrink screen buffer height if window height were the same */ 927 | if (oldsize.Y == ws.srWindow.Bottom - ws.srWindow.Top + 1) { 928 | ws.dwSize.Y = newrow; 929 | } 930 | ws.srWindow.Left = 0; 931 | ws.srWindow.Right = newcol - 1; 932 | ws.srWindow.Bottom = ws.srWindow.Top + newrow -1; 933 | if (ws.dwCursorPosition.Y > ws.srWindow.Bottom) { 934 | console_scroll(io, ws.dwCursorPosition.Y - ws.srWindow.Bottom); 935 | ws.dwCursorPosition.Y = ws.srWindow.Bottom; 936 | console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X)); 937 | } 938 | if (ws.srWindow.Bottom > ws.dwSize.Y - 1) { 939 | console_scroll(io, ws.srWindow.Bottom - (ws.dwSize.Y - 1)); 940 | ws.dwCursorPosition.Y -= ws.srWindow.Bottom - (ws.dwSize.Y - 1); 941 | console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X)); 942 | ws.srWindow.Bottom = ws.dwSize.Y - 1; 943 | } 944 | ws.srWindow.Top = ws.srWindow.Bottom - (newrow - 1); 945 | /* perform changes to winsize */ 946 | if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) { 947 | int last_error = LAST_ERROR; 948 | SetConsoleScreenBufferSize(wh, oldsize); 949 | rb_syserr_fail(last_error, "SetConsoleWindowInfo"); 950 | } 951 | /* perform screen buffer shrinking if necessary */ 952 | if ((ws.dwSize.Y < oldsize.Y || ws.dwSize.X < oldsize.X) && 953 | !SetConsoleScreenBufferSize(wh, ws.dwSize)) { 954 | rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo"); 955 | } 956 | /* remove scrollbar if possible */ 957 | if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) { 958 | rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo"); 959 | } 960 | #endif 961 | return io; 962 | } 963 | #endif 964 | 965 | #ifdef _WIN32 966 | /* 967 | * call-seq: 968 | * io.check_winsize_changed { ... } -> io 969 | * 970 | * Yields while console input events are queued. 971 | * 972 | * This method is Windows only. 973 | * 974 | * You must require 'io/console' to use this method. 975 | */ 976 | static VALUE 977 | console_check_winsize_changed(VALUE io) 978 | { 979 | HANDLE h; 980 | DWORD num; 981 | 982 | h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io)); 983 | while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) { 984 | INPUT_RECORD rec; 985 | if (ReadConsoleInput(h, &rec, 1, &num)) { 986 | if (rec.EventType == WINDOW_BUFFER_SIZE_EVENT) { 987 | rb_yield(Qnil); 988 | } 989 | } 990 | } 991 | return io; 992 | } 993 | #else 994 | #define console_check_winsize_changed rb_f_notimplement 995 | #endif 996 | 997 | /* 998 | * call-seq: 999 | * io.iflush 1000 | * 1001 | * Flushes input buffer in kernel. 1002 | * 1003 | * You must require 'io/console' to use this method. 1004 | */ 1005 | static VALUE 1006 | console_iflush(VALUE io) 1007 | { 1008 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 1009 | int fd = GetReadFD(io); 1010 | if (tcflush(fd, TCIFLUSH)) sys_fail(io); 1011 | #endif 1012 | 1013 | return io; 1014 | } 1015 | 1016 | /* 1017 | * call-seq: 1018 | * io.oflush 1019 | * 1020 | * Flushes output buffer in kernel. 1021 | * 1022 | * You must require 'io/console' to use this method. 1023 | */ 1024 | static VALUE 1025 | console_oflush(VALUE io) 1026 | { 1027 | int fd = GetWriteFD(io); 1028 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 1029 | if (tcflush(fd, TCOFLUSH)) sys_fail(io); 1030 | #endif 1031 | (void)fd; 1032 | return io; 1033 | } 1034 | 1035 | /* 1036 | * call-seq: 1037 | * io.ioflush 1038 | * 1039 | * Flushes input and output buffers in kernel. 1040 | * 1041 | * You must require 'io/console' to use this method. 1042 | */ 1043 | static VALUE 1044 | console_ioflush(VALUE io) 1045 | { 1046 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H 1047 | int fd1 = GetReadFD(io); 1048 | int fd2 = GetWriteFD(io); 1049 | 1050 | if (fd2 != -1 && fd1 != fd2) { 1051 | if (tcflush(fd1, TCIFLUSH)) sys_fail(io); 1052 | if (tcflush(fd2, TCOFLUSH)) sys_fail(io); 1053 | } 1054 | else { 1055 | if (tcflush(fd1, TCIOFLUSH)) sys_fail(io); 1056 | } 1057 | #endif 1058 | 1059 | return io; 1060 | } 1061 | 1062 | /* 1063 | * call-seq: 1064 | * io.beep 1065 | * 1066 | * Beeps on the output console. 1067 | * 1068 | * You must require 'io/console' to use this method. 1069 | */ 1070 | static VALUE 1071 | console_beep(VALUE io) 1072 | { 1073 | #ifdef _WIN32 1074 | MessageBeep(0); 1075 | #else 1076 | int fd = GetWriteFD(io); 1077 | if (write(fd, "\a", 1) < 0) sys_fail(io); 1078 | #endif 1079 | return io; 1080 | } 1081 | 1082 | static int 1083 | mode_in_range(VALUE val, int high, const char *modename) 1084 | { 1085 | int mode; 1086 | if (NIL_P(val)) return 0; 1087 | if (!RB_INTEGER_TYPE_P(val)) { 1088 | wrong_value: 1089 | rb_raise(rb_eArgError, "wrong %s mode: %"PRIsVALUE, modename, val); 1090 | } 1091 | if ((mode = NUM2INT(val)) < 0 || mode > high) { 1092 | goto wrong_value; 1093 | } 1094 | return mode; 1095 | } 1096 | 1097 | #if defined _WIN32 1098 | static void 1099 | constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos) 1100 | { 1101 | DWORD written; 1102 | 1103 | FillConsoleOutputAttribute(handle, attr, len, pos, &written); 1104 | FillConsoleOutputCharacterW(handle, L' ', len, pos, &written); 1105 | } 1106 | 1107 | static VALUE 1108 | console_scroll(VALUE io, int line) 1109 | { 1110 | HANDLE h; 1111 | rb_console_size_t ws; 1112 | 1113 | h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); 1114 | if (!GetConsoleScreenBufferInfo(h, &ws)) { 1115 | rb_syserr_fail(LAST_ERROR, 0); 1116 | } 1117 | if (line) { 1118 | SMALL_RECT scroll; 1119 | COORD destination; 1120 | CHAR_INFO fill; 1121 | scroll.Left = 0; 1122 | scroll.Top = line > 0 ? line : 0; 1123 | scroll.Right = winsize_col(&ws) - 1; 1124 | scroll.Bottom = winsize_row(&ws) - 1 + (line < 0 ? line : 0); 1125 | destination.X = 0; 1126 | destination.Y = line < 0 ? -line : 0; 1127 | fill.Char.UnicodeChar = L' '; 1128 | fill.Attributes = ws.wAttributes; 1129 | 1130 | ScrollConsoleScreenBuffer(h, &scroll, NULL, destination, &fill); 1131 | } 1132 | return io; 1133 | } 1134 | 1135 | #define GPERF_DOWNCASE 1 1136 | #define GPERF_CASE_STRCMP 1 1137 | #define gperf_case_strcmp STRCASECMP 1138 | #include "win32_vk.inc" 1139 | 1140 | /* 1141 | * call-seq: 1142 | * io.pressed?(key) -> bool 1143 | * 1144 | * Returns +true+ if +key+ is pressed. +key+ may be a virtual key 1145 | * code or its name (String or Symbol) with out "VK_" prefix. 1146 | * 1147 | * This method is Windows only. 1148 | * 1149 | * You must require 'io/console' to use this method. 1150 | */ 1151 | static VALUE 1152 | console_key_pressed_p(VALUE io, VALUE k) 1153 | { 1154 | int vk = -1; 1155 | 1156 | if (FIXNUM_P(k)) { 1157 | vk = NUM2UINT(k); 1158 | } 1159 | else { 1160 | const struct vktable *t; 1161 | const char *kn; 1162 | if (SYMBOL_P(k)) { 1163 | k = rb_sym2str(k); 1164 | kn = RSTRING_PTR(k); 1165 | } 1166 | else { 1167 | kn = StringValuePtr(k); 1168 | } 1169 | t = console_win32_vk(kn, RSTRING_LEN(k)); 1170 | if (!t || (vk = (short)t->vk) == -1) { 1171 | rb_raise(rb_eArgError, "unknown virtual key code: % "PRIsVALUE, k); 1172 | } 1173 | } 1174 | return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse; 1175 | } 1176 | #else 1177 | struct query_args { 1178 | char qstr[6]; 1179 | unsigned char opt; 1180 | }; 1181 | 1182 | static int 1183 | direct_query(VALUE io, const struct query_args *query) 1184 | { 1185 | if (RB_TYPE_P(io, T_FILE)) { 1186 | VALUE wio = rb_io_get_write_io(io); 1187 | VALUE s = rb_str_new_cstr(query->qstr); 1188 | rb_io_write(wio, s); 1189 | rb_io_flush(wio); 1190 | return 1; 1191 | } 1192 | return 0; 1193 | } 1194 | 1195 | static VALUE 1196 | read_vt_response(VALUE io, VALUE query) 1197 | { 1198 | struct query_args *qargs = (struct query_args *)query; 1199 | VALUE result, b; 1200 | int opt = 0; 1201 | int num = 0; 1202 | if (qargs) { 1203 | opt = qargs->opt; 1204 | if (!direct_query(io, qargs)) return Qnil; 1205 | } 1206 | if (rb_io_getbyte(io) != INT2FIX(0x1b)) return Qnil; 1207 | if (rb_io_getbyte(io) != INT2FIX('[')) return Qnil; 1208 | result = rb_ary_new(); 1209 | while (!NIL_P(b = rb_io_getbyte(io))) { 1210 | int c = NUM2UINT(b); 1211 | if (c == ';') { 1212 | rb_ary_push(result, INT2NUM(num)); 1213 | num = 0; 1214 | } 1215 | else if (ISDIGIT(c)) { 1216 | num = num * 10 + c - '0'; 1217 | } 1218 | else if (opt && c == opt) { 1219 | opt = 0; 1220 | } 1221 | else { 1222 | char last = (char)c; 1223 | rb_ary_push(result, INT2NUM(num)); 1224 | b = rb_str_new(&last, 1); 1225 | break; 1226 | } 1227 | } 1228 | return rb_ary_push(result, b); 1229 | } 1230 | 1231 | static VALUE 1232 | console_vt_response(int argc, VALUE *argv, VALUE io, const struct query_args *qargs) 1233 | { 1234 | rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 1, &opts); 1235 | VALUE query = (VALUE)qargs; 1236 | VALUE ret = ttymode_with_io(io, read_vt_response, query, set_rawmode, optp); 1237 | return ret; 1238 | } 1239 | 1240 | static VALUE 1241 | console_scroll(VALUE io, int line) 1242 | { 1243 | if (line) { 1244 | VALUE s = rb_sprintf(CSI "%d%c", line < 0 ? -line : line, 1245 | line < 0 ? 'T' : 'S'); 1246 | rb_io_write(io, s); 1247 | } 1248 | return io; 1249 | } 1250 | 1251 | # define console_key_pressed_p rb_f_notimplement 1252 | #endif 1253 | 1254 | /* 1255 | * call-seq: 1256 | * io.cursor -> [row, column] 1257 | * 1258 | * Returns the current cursor position as a two-element array of integers (row, column) 1259 | * 1260 | * io.cursor # => [3, 5] 1261 | * 1262 | * You must require 'io/console' to use this method. 1263 | */ 1264 | static VALUE 1265 | console_cursor_pos(VALUE io) 1266 | { 1267 | #ifdef _WIN32 1268 | rb_console_size_t ws; 1269 | int fd = GetWriteFD(io); 1270 | if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) { 1271 | rb_syserr_fail(LAST_ERROR, 0); 1272 | } 1273 | return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y - ws.srWindow.Top), UINT2NUM(ws.dwCursorPosition.X)); 1274 | #else 1275 | static const struct query_args query = {"\033[6n", 0}; 1276 | VALUE resp = console_vt_response(0, 0, io, &query); 1277 | VALUE row, column, term; 1278 | unsigned int r, c; 1279 | if (!RB_TYPE_P(resp, T_ARRAY) || RARRAY_LEN(resp) != 3) return Qnil; 1280 | term = RARRAY_AREF(resp, 2); 1281 | if (!RB_TYPE_P(term, T_STRING) || RSTRING_LEN(term) != 1) return Qnil; 1282 | if (RSTRING_PTR(term)[0] != 'R') return Qnil; 1283 | row = RARRAY_AREF(resp, 0); 1284 | column = RARRAY_AREF(resp, 1); 1285 | rb_ary_resize(resp, 2); 1286 | r = NUM2UINT(row) - 1; 1287 | c = NUM2UINT(column) - 1; 1288 | RARRAY_ASET(resp, 0, INT2NUM(r)); 1289 | RARRAY_ASET(resp, 1, INT2NUM(c)); 1290 | return resp; 1291 | #endif 1292 | } 1293 | 1294 | /* 1295 | * call-seq: 1296 | * io.goto(line, column) -> io 1297 | * 1298 | * Set the cursor position at +line+ and +column+. 1299 | * 1300 | * You must require 'io/console' to use this method. 1301 | */ 1302 | static VALUE 1303 | console_goto(VALUE io, VALUE y, VALUE x) 1304 | { 1305 | #ifdef _WIN32 1306 | HANDLE h; 1307 | rb_console_size_t ws; 1308 | COORD *pos = &ws.dwCursorPosition; 1309 | 1310 | h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); 1311 | if (!GetConsoleScreenBufferInfo(h, &ws)) { 1312 | rb_syserr_fail(LAST_ERROR, 0); 1313 | } 1314 | pos->X = NUM2UINT(x); 1315 | pos->Y = ws.srWindow.Top + NUM2UINT(y); 1316 | if (!SetConsoleCursorPosition(h, *pos)) { 1317 | rb_syserr_fail(LAST_ERROR, 0); 1318 | } 1319 | #else 1320 | rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1)); 1321 | #endif 1322 | return io; 1323 | } 1324 | 1325 | static VALUE 1326 | console_move(VALUE io, int y, int x) 1327 | { 1328 | #ifdef _WIN32 1329 | HANDLE h; 1330 | rb_console_size_t ws; 1331 | COORD *pos = &ws.dwCursorPosition; 1332 | 1333 | h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); 1334 | if (!GetConsoleScreenBufferInfo(h, &ws)) { 1335 | rb_syserr_fail(LAST_ERROR, 0); 1336 | } 1337 | pos->X += x; 1338 | pos->Y += y; 1339 | if (!SetConsoleCursorPosition(h, *pos)) { 1340 | rb_syserr_fail(LAST_ERROR, 0); 1341 | } 1342 | #else 1343 | if (x || y) { 1344 | VALUE s = rb_str_new_cstr(""); 1345 | if (y) rb_str_catf(s, CSI "%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B'); 1346 | if (x) rb_str_catf(s, CSI "%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C'); 1347 | rb_io_write(io, s); 1348 | rb_io_flush(io); 1349 | } 1350 | #endif 1351 | return io; 1352 | } 1353 | 1354 | /* 1355 | * call-seq: 1356 | * io.goto_column(column) -> io 1357 | * 1358 | * Set the cursor position at +column+ in the same line of the current 1359 | * position. 1360 | * 1361 | * You must require 'io/console' to use this method. 1362 | */ 1363 | static VALUE 1364 | console_goto_column(VALUE io, VALUE val) 1365 | { 1366 | #ifdef _WIN32 1367 | HANDLE h; 1368 | rb_console_size_t ws; 1369 | COORD *pos = &ws.dwCursorPosition; 1370 | 1371 | h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); 1372 | if (!GetConsoleScreenBufferInfo(h, &ws)) { 1373 | rb_syserr_fail(LAST_ERROR, 0); 1374 | } 1375 | pos->X = NUM2INT(val); 1376 | if (!SetConsoleCursorPosition(h, *pos)) { 1377 | rb_syserr_fail(LAST_ERROR, 0); 1378 | } 1379 | #else 1380 | rb_io_write(io, rb_sprintf(CSI "%dG", NUM2UINT(val)+1)); 1381 | #endif 1382 | return io; 1383 | } 1384 | 1385 | /* 1386 | * call-seq: 1387 | * io.erase_line(mode) -> io 1388 | * 1389 | * Erases the line at the cursor corresponding to +mode+. 1390 | * +mode+ may be either: 1391 | * 0: after cursor 1392 | * 1: before and cursor 1393 | * 2: entire line 1394 | * 1395 | * You must require 'io/console' to use this method. 1396 | */ 1397 | static VALUE 1398 | console_erase_line(VALUE io, VALUE val) 1399 | { 1400 | int mode = mode_in_range(val, 2, "line erase"); 1401 | #ifdef _WIN32 1402 | HANDLE h; 1403 | rb_console_size_t ws; 1404 | COORD *pos = &ws.dwCursorPosition; 1405 | DWORD w; 1406 | 1407 | h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); 1408 | if (!GetConsoleScreenBufferInfo(h, &ws)) { 1409 | rb_syserr_fail(LAST_ERROR, 0); 1410 | } 1411 | w = winsize_col(&ws); 1412 | switch (mode) { 1413 | case 0: /* after cursor */ 1414 | w -= pos->X; 1415 | break; 1416 | case 1: /* before *and* cursor */ 1417 | w = pos->X + 1; 1418 | pos->X = 0; 1419 | break; 1420 | case 2: /* entire line */ 1421 | pos->X = 0; 1422 | break; 1423 | } 1424 | constat_clear(h, ws.wAttributes, w, *pos); 1425 | return io; 1426 | #else 1427 | rb_io_write(io, rb_sprintf(CSI "%dK", mode)); 1428 | #endif 1429 | return io; 1430 | } 1431 | 1432 | /* 1433 | * call-seq: 1434 | * io.erase_screen(mode) -> io 1435 | * 1436 | * Erases the screen at the cursor corresponding to +mode+. 1437 | * +mode+ may be either: 1438 | * 0: after cursor 1439 | * 1: before and cursor 1440 | * 2: entire screen 1441 | * 1442 | * You must require 'io/console' to use this method. 1443 | */ 1444 | static VALUE 1445 | console_erase_screen(VALUE io, VALUE val) 1446 | { 1447 | int mode = mode_in_range(val, 3, "screen erase"); 1448 | #ifdef _WIN32 1449 | HANDLE h; 1450 | rb_console_size_t ws; 1451 | COORD *pos = &ws.dwCursorPosition; 1452 | DWORD w; 1453 | 1454 | h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io)); 1455 | if (!GetConsoleScreenBufferInfo(h, &ws)) { 1456 | rb_syserr_fail(LAST_ERROR, 0); 1457 | } 1458 | w = winsize_col(&ws); 1459 | switch (mode) { 1460 | case 0: /* erase after cursor */ 1461 | w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X); 1462 | break; 1463 | case 1: /* erase before *and* cursor */ 1464 | w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1); 1465 | pos->X = 0; 1466 | pos->Y = ws.srWindow.Top; 1467 | break; 1468 | case 2: /* erase entire screen */ 1469 | w = (w * winsize_row(&ws)); 1470 | pos->X = 0; 1471 | pos->Y = ws.srWindow.Top; 1472 | break; 1473 | case 3: /* erase entire screen */ 1474 | w = (w * ws.dwSize.Y); 1475 | pos->X = 0; 1476 | pos->Y = 0; 1477 | break; 1478 | } 1479 | constat_clear(h, ws.wAttributes, w, *pos); 1480 | #else 1481 | rb_io_write(io, rb_sprintf(CSI "%dJ", mode)); 1482 | #endif 1483 | return io; 1484 | } 1485 | 1486 | /* 1487 | * call-seq: 1488 | * io.cursor = [line, column] -> io 1489 | * 1490 | * Same as io.goto(line, column) 1491 | * 1492 | * See IO#goto. 1493 | * 1494 | * You must require 'io/console' to use this method. 1495 | */ 1496 | static VALUE 1497 | console_cursor_set(VALUE io, VALUE cpos) 1498 | { 1499 | cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary"); 1500 | if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate"); 1501 | return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1)); 1502 | } 1503 | 1504 | /* 1505 | * call-seq: 1506 | * io.cursor_up(n) -> io 1507 | * 1508 | * Moves the cursor up +n+ lines. 1509 | * 1510 | * You must require 'io/console' to use this method. 1511 | */ 1512 | static VALUE 1513 | console_cursor_up(VALUE io, VALUE val) 1514 | { 1515 | return console_move(io, -NUM2INT(val), 0); 1516 | } 1517 | 1518 | /* 1519 | * call-seq: 1520 | * io.cursor_down(n) -> io 1521 | * 1522 | * Moves the cursor down +n+ lines. 1523 | * 1524 | * You must require 'io/console' to use this method. 1525 | */ 1526 | static VALUE 1527 | console_cursor_down(VALUE io, VALUE val) 1528 | { 1529 | return console_move(io, +NUM2INT(val), 0); 1530 | } 1531 | 1532 | /* 1533 | * call-seq: 1534 | * io.cursor_left(n) -> io 1535 | * 1536 | * Moves the cursor left +n+ columns. 1537 | * 1538 | * You must require 'io/console' to use this method. 1539 | */ 1540 | static VALUE 1541 | console_cursor_left(VALUE io, VALUE val) 1542 | { 1543 | return console_move(io, 0, -NUM2INT(val)); 1544 | } 1545 | 1546 | /* 1547 | * call-seq: 1548 | * io.cursor_right(n) -> io 1549 | * 1550 | * Moves the cursor right +n+ columns. 1551 | * 1552 | * You must require 'io/console' to use this method. 1553 | */ 1554 | static VALUE 1555 | console_cursor_right(VALUE io, VALUE val) 1556 | { 1557 | return console_move(io, 0, +NUM2INT(val)); 1558 | } 1559 | 1560 | /* 1561 | * call-seq: 1562 | * io.scroll_forward(n) -> io 1563 | * 1564 | * Scrolls the entire scrolls forward +n+ lines. 1565 | * 1566 | * You must require 'io/console' to use this method. 1567 | */ 1568 | static VALUE 1569 | console_scroll_forward(VALUE io, VALUE val) 1570 | { 1571 | return console_scroll(io, +NUM2INT(val)); 1572 | } 1573 | 1574 | /* 1575 | * call-seq: 1576 | * io.scroll_backward(n) -> io 1577 | * 1578 | * Scrolls the entire scrolls backward +n+ lines. 1579 | * 1580 | * You must require 'io/console' to use this method. 1581 | */ 1582 | static VALUE 1583 | console_scroll_backward(VALUE io, VALUE val) 1584 | { 1585 | return console_scroll(io, -NUM2INT(val)); 1586 | } 1587 | 1588 | /* 1589 | * call-seq: 1590 | * io.clear_screen -> io 1591 | * 1592 | * Clears the entire screen and moves the cursor top-left corner. 1593 | * 1594 | * You must require 'io/console' to use this method. 1595 | */ 1596 | static VALUE 1597 | console_clear_screen(VALUE io) 1598 | { 1599 | console_erase_screen(io, INT2FIX(2)); 1600 | console_goto(io, INT2FIX(0), INT2FIX(0)); 1601 | return io; 1602 | } 1603 | 1604 | #ifndef HAVE_RB_IO_OPEN_DESCRIPTOR 1605 | static VALUE 1606 | io_open_descriptor_fallback(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, void *encoding) 1607 | { 1608 | VALUE arguments[2] = { 1609 | (rb_update_max_fd(descriptor), INT2NUM(descriptor)), 1610 | INT2FIX(mode), 1611 | }; 1612 | 1613 | VALUE self = rb_class_new_instance(2, arguments, klass); 1614 | 1615 | rb_io_t *fptr; 1616 | GetOpenFile(self, fptr); 1617 | fptr->pathv = path; 1618 | fptr->mode |= mode; 1619 | 1620 | return self; 1621 | } 1622 | #define rb_io_open_descriptor io_open_descriptor_fallback 1623 | #endif 1624 | 1625 | #ifndef HAVE_RB_IO_CLOSED_P 1626 | static VALUE 1627 | rb_io_closed_p(VALUE io) 1628 | { 1629 | rb_io_t *fptr = RFILE(io)->fptr; 1630 | return fptr->fd == -1 ? Qtrue : Qfalse; 1631 | } 1632 | #endif 1633 | 1634 | #if defined(RB_EXT_RACTOR_SAFE) && defined(HAVE_RB_RACTOR_LOCAL_STORAGE_VALUE_NEWKEY) 1635 | # define USE_RACTOR_STORAGE 1 1636 | #else 1637 | # define USE_RACTOR_STORAGE 0 1638 | #endif 1639 | 1640 | #if USE_RACTOR_STORAGE 1641 | #include "ruby/ractor.h" 1642 | static rb_ractor_local_key_t key_console_dev; 1643 | 1644 | static bool 1645 | console_dev_get(VALUE klass, VALUE *dev) 1646 | { 1647 | return rb_ractor_local_storage_value_lookup(key_console_dev, dev); 1648 | } 1649 | 1650 | static void 1651 | console_dev_set(VALUE klass, VALUE value) 1652 | { 1653 | rb_ractor_local_storage_value_set(key_console_dev, value); 1654 | } 1655 | 1656 | static void 1657 | console_dev_remove(VALUE klass) 1658 | { 1659 | console_dev_set(klass, Qnil); 1660 | } 1661 | 1662 | #else 1663 | 1664 | static ID id_console; 1665 | 1666 | static int 1667 | console_dev_get(VALUE klass, VALUE *dev) 1668 | { 1669 | if (rb_const_defined(klass, id_console)) { 1670 | *dev = rb_const_get(klass, id_console); 1671 | return 1; 1672 | } 1673 | return 0; 1674 | } 1675 | 1676 | static void 1677 | console_dev_set(VALUE klass, VALUE value) 1678 | { 1679 | rb_const_set(klass, id_console, value); 1680 | } 1681 | 1682 | static void 1683 | console_dev_remove(VALUE klass) 1684 | { 1685 | rb_const_remove(klass, id_console); 1686 | } 1687 | 1688 | #endif 1689 | 1690 | /* 1691 | * call-seq: 1692 | * IO.console -> # 1693 | * IO.console(sym, *args) 1694 | * 1695 | * Returns an File instance opened console. 1696 | * 1697 | * If +sym+ is given, it will be sent to the opened console with 1698 | * +args+ and the result will be returned instead of the console IO 1699 | * itself. 1700 | * 1701 | * You must require 'io/console' to use this method. 1702 | */ 1703 | static VALUE 1704 | console_dev(int argc, VALUE *argv, VALUE klass) 1705 | { 1706 | VALUE con = 0; 1707 | VALUE sym = 0; 1708 | 1709 | if (argc) { 1710 | Check_Type(sym = argv[0], T_SYMBOL); 1711 | } 1712 | 1713 | // Force the class to be File. 1714 | if (klass == rb_cIO) klass = rb_cFile; 1715 | 1716 | if (console_dev_get(klass, &con)) { 1717 | if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) { 1718 | console_dev_remove(klass); 1719 | con = 0; 1720 | } 1721 | } 1722 | 1723 | if (sym) { 1724 | if (sym == ID2SYM(id_close) && argc == 1) { 1725 | if (con) { 1726 | rb_io_close(con); 1727 | console_dev_remove(klass); 1728 | con = 0; 1729 | } 1730 | return Qnil; 1731 | } 1732 | } 1733 | 1734 | if (!con) { 1735 | #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H 1736 | # define CONSOLE_DEVICE "/dev/tty" 1737 | #elif defined _WIN32 1738 | # define CONSOLE_DEVICE "con$" 1739 | # define CONSOLE_DEVICE_FOR_READING "conin$" 1740 | # define CONSOLE_DEVICE_FOR_WRITING "conout$" 1741 | #endif 1742 | #ifndef CONSOLE_DEVICE_FOR_READING 1743 | # define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE 1744 | #endif 1745 | #ifdef CONSOLE_DEVICE_FOR_WRITING 1746 | VALUE out; 1747 | #endif 1748 | int fd; 1749 | VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE)); 1750 | 1751 | #ifdef CONSOLE_DEVICE_FOR_WRITING 1752 | fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0); 1753 | if (fd < 0) return Qnil; 1754 | out = rb_io_open_descriptor(klass, fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL); 1755 | #endif 1756 | fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0); 1757 | if (fd < 0) { 1758 | #ifdef CONSOLE_DEVICE_FOR_WRITING 1759 | rb_io_close(out); 1760 | #endif 1761 | return Qnil; 1762 | } 1763 | 1764 | con = rb_io_open_descriptor(klass, fd, FMODE_READWRITE | FMODE_SYNC, path, Qnil, NULL); 1765 | #ifdef CONSOLE_DEVICE_FOR_WRITING 1766 | rb_io_set_write_io(con, out); 1767 | #endif 1768 | console_dev_set(klass, con); 1769 | } 1770 | 1771 | if (sym) { 1772 | return rb_f_send(argc, argv, con); 1773 | } 1774 | 1775 | return con; 1776 | } 1777 | 1778 | /* 1779 | * call-seq: 1780 | * io.getch(min: nil, time: nil, intr: nil) -> char 1781 | * 1782 | * See IO#getch. 1783 | */ 1784 | static VALUE 1785 | io_getch(int argc, VALUE *argv, VALUE io) 1786 | { 1787 | return rb_funcallv(io, id_getc, argc, argv); 1788 | } 1789 | 1790 | static VALUE 1791 | puts_call(VALUE io) 1792 | { 1793 | return rb_io_write(io, rb_default_rs); 1794 | } 1795 | 1796 | static VALUE 1797 | gets_call(VALUE io) 1798 | { 1799 | return rb_funcallv(io, id_gets, 0, 0); 1800 | } 1801 | 1802 | static VALUE 1803 | getpass_call(VALUE io) 1804 | { 1805 | return ttymode(io, rb_io_gets, io, set_noecho, NULL); 1806 | } 1807 | 1808 | static void 1809 | prompt(int argc, VALUE *argv, VALUE io) 1810 | { 1811 | if (argc > 0 && !NIL_P(argv[0])) { 1812 | VALUE str = argv[0]; 1813 | StringValueCStr(str); 1814 | rb_io_write(io, str); 1815 | } 1816 | } 1817 | 1818 | static VALUE 1819 | str_chomp(VALUE str) 1820 | { 1821 | if (!NIL_P(str)) { 1822 | const VALUE rs = rb_default_rs; /* rvalue in TruffleRuby */ 1823 | rb_funcallv(str, id_chomp_bang, 1, &rs); 1824 | } 1825 | return str; 1826 | } 1827 | 1828 | /* 1829 | * call-seq: 1830 | * io.getpass(prompt=nil) -> string 1831 | * 1832 | * Reads and returns a line without echo back. 1833 | * Prints +prompt+ unless it is +nil+. 1834 | * 1835 | * The newline character that terminates the 1836 | * read line is removed from the returned string, 1837 | * see String#chomp!. 1838 | * 1839 | * You must require 'io/console' to use this method. 1840 | * 1841 | * require 'io/console' 1842 | * IO::console.getpass("Enter password:") 1843 | * Enter password: 1844 | * # => "mypassword" 1845 | * 1846 | */ 1847 | static VALUE 1848 | console_getpass(int argc, VALUE *argv, VALUE io) 1849 | { 1850 | VALUE str, wio; 1851 | 1852 | rb_check_arity(argc, 0, 1); 1853 | wio = rb_io_get_write_io(io); 1854 | if (wio == io && io == rb_stdin) wio = rb_stderr; 1855 | prompt(argc, argv, wio); 1856 | rb_io_flush(wio); 1857 | str = rb_ensure(getpass_call, io, puts_call, wio); 1858 | return str_chomp(str); 1859 | } 1860 | 1861 | /* 1862 | * call-seq: 1863 | * io.getpass(prompt=nil) -> string 1864 | * 1865 | * See IO#getpass. 1866 | */ 1867 | static VALUE 1868 | io_getpass(int argc, VALUE *argv, VALUE io) 1869 | { 1870 | VALUE str; 1871 | 1872 | rb_check_arity(argc, 0, 1); 1873 | prompt(argc, argv, io); 1874 | rb_check_funcall(io, id_flush, 0, 0); 1875 | str = rb_ensure(gets_call, io, puts_call, io); 1876 | return str_chomp(str); 1877 | } 1878 | 1879 | #if defined(_WIN32) || defined(HAVE_TTYNAME_R) || defined(HAVE_TTYNAME) 1880 | /* 1881 | * call-seq: 1882 | * io.ttyname -> string or nil 1883 | * 1884 | * Returns name of associated terminal (tty) if +io+ is not a tty. 1885 | * Returns +nil+ otherwise. 1886 | */ 1887 | static VALUE 1888 | console_ttyname(VALUE io) 1889 | { 1890 | int fd = rb_io_descriptor(io); 1891 | if (!isatty(fd)) return Qnil; 1892 | # if defined _WIN32 1893 | return rb_usascii_str_new_lit("con"); 1894 | # elif defined HAVE_TTYNAME_R 1895 | { 1896 | char termname[1024], *tn = termname; 1897 | size_t size = sizeof(termname); 1898 | int e; 1899 | if (ttyname_r(fd, tn, size) == 0) 1900 | return rb_interned_str_cstr(tn); 1901 | if ((e = errno) == ERANGE) { 1902 | VALUE s = rb_str_new(0, size); 1903 | while (1) { 1904 | tn = RSTRING_PTR(s); 1905 | size = rb_str_capacity(s); 1906 | if (ttyname_r(fd, tn, size) == 0) { 1907 | return rb_str_to_interned_str(rb_str_resize(s, strlen(tn))); 1908 | } 1909 | if ((e = errno) != ERANGE) break; 1910 | if ((size *= 2) >= INT_MAX/2) break; 1911 | rb_str_resize(s, size); 1912 | } 1913 | } 1914 | rb_syserr_fail_str(e, rb_sprintf("ttyname_r(%d)", fd)); 1915 | UNREACHABLE_RETURN(Qnil); 1916 | } 1917 | # elif defined HAVE_TTYNAME 1918 | { 1919 | const char *tn = ttyname(fd); 1920 | if (!tn) { 1921 | int e = errno; 1922 | rb_syserr_fail_str(e, rb_sprintf("ttyname(%d)", fd)); 1923 | } 1924 | return rb_interned_str_cstr(tn); 1925 | } 1926 | # else 1927 | # error No ttyname function 1928 | # endif 1929 | } 1930 | #else 1931 | # define console_ttyname rb_f_notimplement 1932 | #endif 1933 | 1934 | /* 1935 | * IO console methods 1936 | */ 1937 | void 1938 | Init_console(void) 1939 | { 1940 | #if USE_RACTOR_STORAGE 1941 | RB_EXT_RACTOR_SAFE(true); 1942 | #endif 1943 | 1944 | #undef rb_intern 1945 | #if USE_RACTOR_STORAGE 1946 | key_console_dev = rb_ractor_local_storage_value_newkey(); 1947 | #else 1948 | id_console = rb_intern("console"); 1949 | #endif 1950 | id_getc = rb_intern("getc"); 1951 | id_gets = rb_intern("gets"); 1952 | id_flush = rb_intern("flush"); 1953 | id_chomp_bang = rb_intern("chomp!"); 1954 | id_close = rb_intern("close"); 1955 | #define init_rawmode_opt_id(name) \ 1956 | rawmode_opt_ids[kwd_##name] = rb_intern(#name) 1957 | init_rawmode_opt_id(min); 1958 | init_rawmode_opt_id(time); 1959 | init_rawmode_opt_id(intr); 1960 | #ifndef HAVE_RB_F_SEND 1961 | id___send__ = rb_intern("__send__"); 1962 | #endif 1963 | InitVM(console); 1964 | } 1965 | 1966 | void 1967 | InitVM_console(void) 1968 | { 1969 | rb_define_method(rb_cIO, "raw", console_raw, -1); 1970 | rb_define_method(rb_cIO, "raw!", console_set_raw, -1); 1971 | rb_define_method(rb_cIO, "cooked", console_cooked, 0); 1972 | rb_define_method(rb_cIO, "cooked!", console_set_cooked, 0); 1973 | rb_define_method(rb_cIO, "getch", console_getch, -1); 1974 | rb_define_method(rb_cIO, "echo=", console_set_echo, 1); 1975 | rb_define_method(rb_cIO, "echo?", console_echo_p, 0); 1976 | rb_define_method(rb_cIO, "console_mode", console_conmode_get, 0); 1977 | rb_define_method(rb_cIO, "console_mode=", console_conmode_set, 1); 1978 | rb_define_method(rb_cIO, "noecho", console_noecho, 0); 1979 | rb_define_method(rb_cIO, "winsize", console_winsize, 0); 1980 | rb_define_method(rb_cIO, "winsize=", console_set_winsize, 1); 1981 | rb_define_method(rb_cIO, "iflush", console_iflush, 0); 1982 | rb_define_method(rb_cIO, "oflush", console_oflush, 0); 1983 | rb_define_method(rb_cIO, "ioflush", console_ioflush, 0); 1984 | rb_define_method(rb_cIO, "beep", console_beep, 0); 1985 | rb_define_method(rb_cIO, "goto", console_goto, 2); 1986 | rb_define_method(rb_cIO, "cursor", console_cursor_pos, 0); 1987 | rb_define_method(rb_cIO, "cursor=", console_cursor_set, 1); 1988 | rb_define_method(rb_cIO, "cursor_up", console_cursor_up, 1); 1989 | rb_define_method(rb_cIO, "cursor_down", console_cursor_down, 1); 1990 | rb_define_method(rb_cIO, "cursor_left", console_cursor_left, 1); 1991 | rb_define_method(rb_cIO, "cursor_right", console_cursor_right, 1); 1992 | rb_define_method(rb_cIO, "goto_column", console_goto_column, 1); 1993 | rb_define_method(rb_cIO, "erase_line", console_erase_line, 1); 1994 | rb_define_method(rb_cIO, "erase_screen", console_erase_screen, 1); 1995 | rb_define_method(rb_cIO, "scroll_forward", console_scroll_forward, 1); 1996 | rb_define_method(rb_cIO, "scroll_backward", console_scroll_backward, 1); 1997 | rb_define_method(rb_cIO, "clear_screen", console_clear_screen, 0); 1998 | rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1); 1999 | rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0); 2000 | rb_define_method(rb_cIO, "getpass", console_getpass, -1); 2001 | rb_define_method(rb_cIO, "ttyname", console_ttyname, 0); 2002 | rb_define_singleton_method(rb_cIO, "console", console_dev, -1); 2003 | { 2004 | /* :stopdoc: */ 2005 | VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable"); 2006 | /* :startdoc: */ 2007 | rb_define_method(mReadable, "getch", io_getch, -1); 2008 | rb_define_method(mReadable, "getpass", io_getpass, -1); 2009 | } 2010 | { 2011 | /* :stopdoc: */ 2012 | cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject); 2013 | rb_define_const(cConmode, "VERSION", rb_obj_freeze(rb_str_new_cstr(IO_CONSOLE_VERSION))); 2014 | rb_define_alloc_func(cConmode, conmode_alloc); 2015 | rb_undef_method(cConmode, "initialize"); 2016 | rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1); 2017 | rb_define_method(cConmode, "echo=", conmode_set_echo, 1); 2018 | rb_define_method(cConmode, "raw!", conmode_set_raw, -1); 2019 | rb_define_method(cConmode, "raw", conmode_raw_new, -1); 2020 | /* :startdoc: */ 2021 | } 2022 | } 2023 | --------------------------------------------------------------------------------