kaitai-struct-visualizer 0.5 → 0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '0294e63ea9c355016ef56c48aed683eec1204ee4'
4
- data.tar.gz: 49acc6230547d1c10c1978d626cb498a29d2cd0f
2
+ SHA256:
3
+ metadata.gz: a4e2b67e9f53f3b692de0e98c7c46c9106c2c88e001ef228e0bd3351f83037d2
4
+ data.tar.gz: 623ee7e33ececcc081b5309746dd8ac9e557e5bc57ae52e3229e71e8ae5a94cf
5
5
  SHA512:
6
- metadata.gz: 7962d68bdafde103d6cf56408436ec79299e3fc25ed7b76dee888bde59d06e65bc71231732d4c9fe6ba5dfd3ebb5c727c2f394aa9c081af1123c69f39fb20380
7
- data.tar.gz: 07ac40819e35d59ae48e3fcdba3f965df4288ec0c6fe45867f236ec3859f0f06ed62b5e1ea5e6e59fdae80c4a1a7d688fd2ff7658da81767e61fb1b389e41302
6
+ metadata.gz: b7ce146db58aab7d2a3c356263e4fde24a3f52517c729329beca1e6e1a26ae7d9b7ad4de84d0320d8b06f9c81054035a3d4a07dee5b61f6834f7f0adfc39c61a
7
+ data.tar.gz: '0867f535300fe5ae4d54bbd11562915249b35d51ff6cb57adae13a343c985c34d40cb916be25fe2baf3f111c9ca055bb2c9d5190ccd44bf640aa3b8df4e314fe'
data/LICENSE CHANGED
@@ -1,7 +1,7 @@
1
1
  GNU GENERAL PUBLIC LICENSE
2
2
  Version 3, 29 June 2007
3
3
 
4
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
5
  Everyone is permitted to copy and distribute verbatim copies
6
6
  of this license document, but changing it is not allowed.
7
7
 
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
645
645
  GNU General Public License for more details.
646
646
 
647
647
  You should have received a copy of the GNU General Public License
648
- along with this program. If not, see <http://www.gnu.org/licenses/>.
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
649
 
650
650
  Also add information on how to contact you by electronic and paper mail.
651
651
 
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
664
664
  You should also get your employer (if you work as a programmer) or school,
665
665
  if any, to sign a "copyright disclaimer" for the program, if necessary.
666
666
  For more information on this, and how to apply and follow the GNU GPL, see
667
- <http://www.gnu.org/licenses/>.
667
+ <https://www.gnu.org/licenses/>.
668
668
 
669
669
  The GNU General Public License does not permit incorporating your program
670
670
  into proprietary programs. If your program is a subroutine library, you
671
671
  may consider it more useful to permit linking proprietary applications with
672
672
  the library. If this is what you want to do, use the GNU Lesser General
673
673
  Public License instead of this License. But first, please read
674
- <http://www.gnu.org/philosophy/why-not-lgpl.html>.
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
data/README.md CHANGED
@@ -1,65 +1,125 @@
1
1
  # Kaitai Struct: visualizer
2
2
 
3
- This is a simple visualizer for [Kaitai Struct](https://github.com/kaitai-io/kaitai_struct) project.
3
+ This is a console visualizer for the [Kaitai Struct](https://kaitai.io/) project.
4
+
5
+ ![screenshot](screenshot.png)
4
6
 
5
7
  Kaitai Struct is a declarative language used for describe various
6
8
  binary data structures, laid out in files or in memory: i.e. binary
7
9
  file formats, network stream packet formats, etc.
8
10
 
9
11
  The main idea is that a particular format is described in Kaitai
10
- Struct language (`.ksy` files) only once and then can be compiled with
11
- this compiler into source files in one of the supported programming
12
- languages. These modules will include a generated code for a parser
13
- that can read described data structure from a file / stream and give
14
- access to it in a nice, easy-to-comprehend API.
12
+ Struct language (`.ksy` file) and then can be compiled with
13
+ `ksc` into source files in one of the supported programming
14
+ languages. These modules will contain generated code for a parser
15
+ that can read the described data structure from a file / stream and provide
16
+ access to it using a nice, easy-to-understand API.
15
17
 
16
- Please refer to [documentation in Kaitai Struct project](https://github.com/kaitai-io/kaitai_struct)
17
- for details on `.ksy` files and general usage patterns.
18
+ See the [Kaitai Struct homepage](https://kaitai.io/) for details on `.ksy` files and general usage patterns.
18
19
 
19
20
  ## Downloading and installing
20
21
 
21
- ### From Ruby Gems repository
22
+ ### Requirements
23
+
24
+ - [ksc](https://kaitai.io/#download) — `kaitai-struct-compiler`
25
+ - [Java](https://whichjdk.com/) (the latest LTS version 21 recommended, at least Java 8 required),
26
+ [JDK or JRE](https://whichjdk.com/#what-is-the-difference-between-jdk-and-jre) at your option
27
+ - [Ruby](https://www.ruby-lang.org/) (the latest Ruby 3.x recommended, at least Ruby 2.4 required)
28
+
29
+ ### From the RubyGems repository
22
30
 
23
- KS visualizer is written in [Ruby](https://www.ruby-lang.org/) and is
24
- available as
25
- [.gem package](https://rubygems.org/gems/kaitai-struct-visualizer). Thus,
26
- you'll need Ruby (RubyGems package manager comes bundled with Ruby
27
- since v1.9) installed on your box, and then you can just run:
31
+ Kaitai Struct visualizer is written in [Ruby](https://www.ruby-lang.org/) and is
32
+ available [on RubyGems](https://rubygems.org/gems/kaitai-struct-visualizer). Thus,
33
+ you'll need Ruby installed on your box and then you can just run:
28
34
 
29
35
  ```shell
30
36
  gem install kaitai-struct-visualizer
31
37
  ```
32
38
 
39
+ ---
40
+
41
+ You can use `ksv --version` to check what versions of `ksv` and its dependencies are installed, for example:
42
+
43
+ ```console
44
+ $ ksv --version
45
+ kaitai-struct-visualizer 0.11
46
+ kaitai-struct-compiler 0.11
47
+ kaitai-struct 0.11 (Kaitai Struct runtime library for Ruby)
48
+ ```
49
+
50
+ The versions of `kaitai-struct-compiler` and `kaitai-struct` should match. If not, see https://kaitai.io/#download for instructions how to install the latest version of `kaitai-struct-compiler` and/or use `gem update kaitai-struct` to update the [kaitai-struct](https://rubygems.org/gems/kaitai-struct) gem if needed.
51
+
33
52
  ### Source code
34
53
 
35
54
  If you're interested in developing the visualizer itself, you can check
36
- out source code in repository:
55
+ out the source code from the [kaitai_struct_visualizer](https://github.com/kaitai-io/kaitai_struct_visualizer) GitHub repository:
37
56
 
38
- git clone https://github.com/kaitai-io/kaitai_struct_visualizer
57
+ ```shell
58
+ git clone https://github.com/kaitai-io/kaitai_struct_visualizer.git
59
+ ```
60
+
61
+ Then run `bundle install` to install dependencies. After that, you can run [`bin/ksv`](bin/ksv) or [`bin/ksdump`](bin/ksdump) right away (without having to install the `kaitai-struct-visualizer` gem first), which makes development easier.
39
62
 
40
63
  ## Usage
41
64
 
42
- `ksv <binary-file> <ksy-file>...`
65
+ There are two executables provided by this package:
66
+
67
+ * `ksv` — interactive console visualizer with GUI
68
+ * `ksdump` — command-line tool for dumping parsed data in JSON, XML or YAML format to standard output (_stdout_)
69
+
70
+ The basic usage is similar for both programs:
71
+
72
+ ```shell
73
+ ksv <file_to_parse.bin> <format.ksy>|<format.rb>
74
+ ```
75
+
76
+ For `ksdump`, it may be useful to change the output format with the `-f` option (the default is `yaml`) and redirect the output to a file so that your terminal is not flooded with thousands of lines (for larger input files):
77
+
78
+ ```shell
79
+ ksdump -f json <file_to_parse.bin> <format.ksy> > output.json
80
+ ```
81
+
82
+ ### Running with Docker
83
+
84
+ This project is also available via the [kaitai/ksv](https://hub.docker.com/r/kaitai/ksv) image on Docker Hub. The default entrypoint is `ksv` (the interactive visualizer):
85
+
86
+ ```shell
87
+ docker run --rm -it -v "$(pwd):/share" kaitai/ksv <file_to_parse.bin> <format.ksy>
88
+ ```
89
+
90
+ You can specify `ksdump` as the entrypoint like this (and note that we don't need the `-it` flags anymore because `ksdump` is not interactive — omitting them in fact allows you to distinguish the `ksdump`'s output to _stdout_ and _stderr_, see [this comment](https://github.com/kaitai-io/kaitai_struct_visualizer/issues/56#issuecomment-1666629764)):
91
+
92
+ ```shell
93
+ docker run --rm -v "$(pwd):/share" --entrypoint ksdump kaitai/ksv -f json <file_to_parse.bin> <format.ksy> > output.json
94
+ ```
95
+
96
+ ---
97
+
98
+ Building the Docker image locally:
99
+ ```shell
100
+ docker build . --tag docker.io/kaitai/ksv
101
+ ```
43
102
 
44
103
  ## Licensing
45
104
 
46
- Kaitai Struct visualizer itself is copyright (C) 2015-2016 Kaitai
47
- Project.
105
+ Kaitai Struct visualizer is copyright (C) 2015-2025 Kaitai Project.
48
106
 
49
107
  This program is free software: you can redistribute it and/or modify
50
108
  it under the terms of the GNU General Public License as published by
51
- the Free Software Foundation, either version 3 of the License, or (at
52
- your option) any later version.
109
+ the Free Software Foundation, either version 3 of the License, or
110
+ (at your option) any later version.
53
111
 
54
- This program is distributed in the hope that it will be useful, but
55
- WITHOUT ANY WARRANTY; without even the implied warranty of
56
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57
- General Public License for more details.
112
+ This program is distributed in the hope that it will be useful,
113
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
114
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
115
+ GNU General Public License for more details.
58
116
 
59
117
  You should have received a copy of the GNU General Public License
60
- along with this program. If not, see <http://www.gnu.org/licenses/>.
118
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
119
+
120
+ ---
61
121
 
62
- Note that it applies only to compiler itself, not `.ksy` input files
122
+ Note that it applies only to visualizer itself, not `.ksy` input files
63
123
  that one supplies in normal process of compilation, nor to compiler's
64
- output files — that consitutes normal usage process and you obviously
124
+ output files — that constitutes normal usage process and you obviously
65
125
  keep copyright to both.
data/bin/ksdump ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+
6
+ # Some additional magic to make it work right after repo checkout,
7
+ # without installation to proper Ruby library dirs
8
+
9
+ # The `__dir__` method is supposed to provide the real absolute path of the
10
+ # `bin/` directory (which this script is in), but it can technically return
11
+ # `nil` or a dot `"."` (in an "irb" session) instead. Normally, it shouldn't
12
+ # happen unless some very unusual startup method is used, but if it happens,
13
+ # we'll skip adding the dirs to $LOAD_PATH and wait for a miracle (which might
14
+ # be that the dependencies we need are already available via $LOAD_PATH because
15
+ # they are installed as gems, for example).
16
+ #
17
+ # See also
18
+ # * https://stackoverflow.com/a/2206731
19
+ # * https://makandracards.com/makandra/42122-ruby-__file__-__dir__-and-symlinks
20
+ script_dir = __dir__
21
+ unless script_dir.nil? || script_dir == '.'
22
+ $LOAD_PATH << File.expand_path('../lib', script_dir)
23
+ $LOAD_PATH << File.expand_path('../../runtime/ruby/lib', script_dir)
24
+ end
25
+
26
+ require 'kaitai/struct/visualizer'
27
+ require 'kaitai/struct/visualizer/obj_to_h'
28
+
29
+ # ======================================================================
30
+
31
+ FORMATS = %w[json xml yaml].freeze
32
+
33
+ prog_name = File.basename($PROGRAM_NAME)
34
+ options = { format: 'yaml' }
35
+ parser = OptionParser.new do |opts|
36
+ opts.banner = "Usage: #{prog_name} [options] <file_to_parse.bin> <format.ksy>..."
37
+ opts.separator ''
38
+
39
+ opts.on('-I', '--import-path [DIRECTORIES]', '.ksy library search path(s) for imports (see also KSPATH env variable)') do |v|
40
+ options[:import_path] = v
41
+ end
42
+
43
+ opts.on('-f', '--format FORMAT', FORMATS, "choose dump format - #{FORMATS.join(', ')} (default: #{options[:format]})") do |v|
44
+ options[:format] = v
45
+ end
46
+
47
+ opts.on('--version', 'show versions of kaitai-struct-visualizer, kaitai-struct-compiler and kaitai-struct (Kaitai Struct runtime library for Ruby)') do |_v|
48
+ puts "kaitai-struct-visualizer #{Kaitai::Struct::Visualizer::VERSION}"
49
+ if system('kaitai-struct-compiler', '--version').nil?
50
+ $stderr.puts "#{prog_name}: unable to find and execute kaitai-struct-compiler in your PATH"
51
+ exit 1
52
+ end
53
+ require 'kaitai/struct/struct'
54
+ puts "kaitai-struct #{Kaitai::Struct::VERSION} (Kaitai Struct runtime library for Ruby)"
55
+ exit 0
56
+ end
57
+ end
58
+
59
+ begin
60
+ parser.parse!
61
+ rescue OptionParser::InvalidOption => e
62
+ puts e
63
+ puts parser
64
+ exit 1
65
+ end
66
+
67
+ if ARGV.size < 2
68
+ puts parser
69
+ exit 1
70
+ end
71
+
72
+ c = Kaitai::Struct::Visualizer::KSYCompiler.new(options, prog_name)
73
+ app = Kaitai::Struct::Visualizer::Parser.new(c, ARGV[0], ARGV[1..-1], options)
74
+ exc = app.load
75
+ raise exc if exc
76
+
77
+ # ======================================================================
78
+
79
+ tree = Kaitai::Struct::Visualizer.obj_to_h(app.data)
80
+ r = nil
81
+
82
+ case options[:format]
83
+ when 'json'
84
+ require 'json'
85
+ r = JSON.pretty_generate(tree)
86
+ when 'xml'
87
+ require 'active_support'
88
+ require 'active_support/core_ext'
89
+ r = tree.to_xml
90
+ when 'yaml'
91
+ require 'yaml'
92
+ r = tree.to_yaml
93
+ r = r[4..-1] if r[0..3] == "---\n"
94
+ end
95
+
96
+ puts r
data/bin/ksv CHANGED
@@ -1,20 +1,66 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
2
5
 
3
6
  # Some additional magic to make it work right after repo checkout,
4
7
  # without installation to proper Ruby library dirs
5
8
 
6
- full_bin_path = File.realpath($PROGRAM_NAME)
7
- dist_path = File.expand_path(File.dirname(full_bin_path) + '/..')
8
-
9
- $LOAD_PATH << "#{dist_path}/lib"
10
- $LOAD_PATH << "#{dist_path}/../runtime/ruby/lib"
9
+ # See `bin/ksdump` for more comments
10
+ script_dir = __dir__
11
+ unless script_dir.nil? || script_dir == '.'
12
+ $LOAD_PATH << File.expand_path('../lib', script_dir)
13
+ $LOAD_PATH << File.expand_path('../../runtime/ruby/lib', script_dir)
14
+ end
11
15
 
12
16
  require 'kaitai/struct/visualizer'
13
17
 
18
+ # ======================================================================
19
+
20
+ prog_name = File.basename($PROGRAM_NAME)
21
+ options = {}
22
+ parser = OptionParser.new do |opts|
23
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] <file_to_parse.bin> <format.ksy>...|<format.rb>"
24
+ opts.separator ''
25
+
26
+ opts.on('-I', '--import-path [DIRECTORIES]', '.ksy library search path(s) for imports (see also KSPATH env variable)') do |v|
27
+ options[:import_path] = v
28
+ end
29
+
30
+ opts.on('--opaque-types [BOOLEAN]', 'opaque types allowed, default: false') do |v|
31
+ options[:opaque_types] = v
32
+ end
33
+
34
+ opts.on('-r', '--require [PATH]', 'load ("require") .rb file into Ruby process') do |v|
35
+ $LOAD_PATH << '.' unless $LOAD_PATH.include?('.')
36
+ require v
37
+ end
38
+
39
+ opts.on('--version', 'show versions of kaitai-struct-visualizer, kaitai-struct-compiler and kaitai-struct (Kaitai Struct runtime library for Ruby)') do |_v|
40
+ puts "kaitai-struct-visualizer #{Kaitai::Struct::Visualizer::VERSION}"
41
+ if system('kaitai-struct-compiler', '--version').nil?
42
+ $stderr.puts "#{prog_name}: unable to find and execute kaitai-struct-compiler in your PATH"
43
+ exit 1
44
+ end
45
+ require 'kaitai/struct/struct'
46
+ puts "kaitai-struct #{Kaitai::Struct::VERSION} (Kaitai Struct runtime library for Ruby)"
47
+ exit 0
48
+ end
49
+ end
50
+
51
+ begin
52
+ parser.parse!
53
+ rescue OptionParser::InvalidOption => e
54
+ puts e
55
+ puts parser
56
+ exit 1
57
+ end
58
+
14
59
  if ARGV.size < 2
15
- puts "Usage: #{File.basename($PROGRAM_NAME)} <file_to_parse.bin> <format.yaml>..."
60
+ puts parser
16
61
  exit 1
17
62
  end
18
63
 
19
- v = Kaitai::Struct::Visualizer::ExternalCompilerVisualizer.new(ARGV[0], ARGV[1..-1])
64
+ c = Kaitai::Struct::Visualizer::KSYCompiler.new(options, prog_name)
65
+ v = Kaitai::Struct::Visualizer::Visualizer.new(c, ARGV[0], ARGV[1..-1], options)
20
66
  v.run
@@ -1,119 +1,124 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  require 'io/console'
3
4
  require 'readline'
4
5
 
5
6
  module Kaitai
7
+ class ConsoleANSI
8
+ attr_reader :cols, :rows
6
9
 
7
- class ConsoleANSI
8
- attr_reader :cols
9
- attr_reader :rows
10
+ def initialize
11
+ load_term_size
10
12
 
11
- def initialize
12
- # Normal POSIX way to determine console parameters
13
- @cols = `tput cols`.to_i
14
- @rows = `tput lines`.to_i
13
+ @seq_clear = `tput clear`
14
+ @seq_sgr0 = `tput sgr0`
15
15
 
16
- @seq_clear = `tput clear`
17
- @seq_sgr0 = `tput sgr0`
16
+ @seq_fgcolor = []
17
+ @seq_bgcolor = []
18
18
 
19
- @seq_fgcolor = []
20
- @seq_bgcolor = []
21
- end
19
+ @on_resize = nil
22
20
 
23
- def clear
24
- print @seq_clear
25
- end
21
+ Signal.trap('SIGWINCH', proc {
22
+ load_term_size
23
+ @on_resize&.call(true)
24
+ })
25
+ end
26
26
 
27
- ##
28
- # Put the cursor up to screen position (x, y). First line is 0,
29
- # first column is 0.
30
- def goto(x, y)
31
- #print `tput cup #{y} #{x}`
32
- printf "\e[%d;%dH", y + 1, x + 1
33
- end
27
+ attr_writer :on_resize
34
28
 
35
- COLORS = {
36
- :black => 0,
37
- :gray => 7,
38
- :gray0 => 232,
39
- :gray1 => 233,
40
- :gray2 => 234,
41
- :gray3 => 235,
42
- :gray4 => 236,
43
- :gray5 => 237,
44
- :gray6 => 238,
45
- :gray7 => 239,
46
- :gray8 => 240,
47
- :gray9 => 241,
48
- :gray10 => 242,
49
- :gray11 => 243,
50
- :gray12 => 244,
51
- :gray13 => 245,
52
- :gray14 => 246,
53
- :gray15 => 247,
54
- :gray16 => 248,
55
- :gray17 => 249,
56
- :gray18 => 250,
57
- :gray19 => 251,
58
- :gray20 => 252,
59
- :gray21 => 253,
60
- :gray22 => 254,
61
- :gray23 => 255,
62
- }
63
-
64
- def fg_color=(col)
65
- #print @seq_fgcolor[col] ||= `tput setaf #{col}`
66
- code = COLORS[col]
67
- raise "Invalid color: #{col}" unless code
68
- print "\e[38;5;#{code}m"
69
- end
29
+ def load_term_size
30
+ @rows, @cols = IO.console.winsize
31
+ end
70
32
 
71
- def bg_color=(col)
72
- #print @seq_bgcolor[col] ||= `tput setab #{col}`
73
- code = COLORS[col]
74
- raise "Invalid color: #{col}" unless code
75
- print "\e[48;5;#{code}m"
76
- end
33
+ def clear
34
+ print @seq_clear
35
+ end
77
36
 
78
- def reset_colors
79
- print @seq_sgr0
80
- end
37
+ # Put the cursor up to screen position (x, y). First line is 0, first column is 0.
38
+ def goto(x, y)
39
+ # print `tput cup #{y} #{x}`
40
+ printf "\e[%d;%dH", y + 1, x + 1
41
+ end
42
+
43
+ COLORS = {
44
+ black: 0,
45
+ red: 1,
46
+ green: 2,
47
+ yellow: 3,
48
+ blue: 4,
49
+ magenta: 5,
50
+ cyan: 6,
51
+ white: 7,
52
+ gray: 8,
53
+ bright_red: 9,
54
+ bright_green: 10,
55
+ bright_yellow: 11,
56
+ bright_blue: 12,
57
+ bright_magenta: 13,
58
+ bright_cyan: 14,
59
+ bright_white: 15
60
+ }.freeze
61
+
62
+ def fg_color=(col)
63
+ # print @seq_fgcolor[col] ||= `tput setaf #{col}`
64
+ code = COLORS[col]
65
+ raise "Invalid color: #{col}" unless code
66
+
67
+ print "\e[38;5;#{code}m"
68
+ end
81
69
 
82
- # Reads keypresses from the user including 2 and 3 escape character sequences.
83
- def read_char
84
- $stdin.echo = false
85
- $stdin.raw!
70
+ def bg_color=(col)
71
+ # print @seq_bgcolor[col] ||= `tput setab #{col}`
72
+ code = COLORS[col]
73
+ raise "Invalid color: #{col}" unless code
86
74
 
87
- input = $stdin.getc.chr
88
- if input == "\e" then
89
- input << $stdin.read_nonblock(3) rescue nil
90
- input << $stdin.read_nonblock(2) rescue nil
75
+ print "\e[48;5;#{code}m"
91
76
  end
92
- ensure
93
- $stdin.echo = true
94
- $stdin.cooked!
95
77
 
96
- return input
97
- end
78
+ def reset_colors
79
+ print @seq_sgr0
80
+ end
98
81
 
99
- def read_char_mapped
100
- c = read_char
101
- c2 = KEY_MAP[c]
102
- c2 ? c2 : c
103
- end
82
+ # Reads keypresses from the user including 2 and 3 escape character sequences.
83
+ def read_char
84
+ $stdin.echo = false
85
+ $stdin.raw!
86
+
87
+ input = $stdin.getc.chr
88
+ if input == "\e"
89
+ begin
90
+ # may return less than 3 bytes because it can only read as many bytes as are
91
+ # currently available
92
+ up_to_3bytes = $stdin.read_nonblock(3)
93
+ input << up_to_3bytes
94
+ rescue IO::WaitReadable
95
+ # not an ANSI sequence - the user probably just pressed the Esc key
96
+ end
97
+ end
98
+
99
+ $stdin.echo = true
100
+ $stdin.cooked!
101
+
102
+ input
103
+ end
104
104
 
105
- KEY_MAP = {
106
- "\t" => :tab,
107
- "\r" => :enter,
108
- "\e[A" => :up_arrow,
109
- "\e[B" => :down_arrow,
110
- "\e[C" => :right_arrow,
111
- "\e[D" => :left_arrow,
112
- "\e[5~" => :pg_up,
113
- "\e[6~" => :pg_dn,
114
- "\e[H" => :home,
115
- "\e[F" => :end,
116
- }
117
- end
105
+ def read_char_mapped
106
+ c = read_char
107
+ c2 = KEY_MAP[c]
108
+ c2 || c
109
+ end
118
110
 
111
+ KEY_MAP = {
112
+ "\t" => :tab,
113
+ "\r" => :enter,
114
+ "\e[A" => :up_arrow,
115
+ "\e[B" => :down_arrow,
116
+ "\e[C" => :right_arrow,
117
+ "\e[D" => :left_arrow,
118
+ "\e[5~" => :pg_up,
119
+ "\e[6~" => :pg_dn,
120
+ "\e[H" => :home,
121
+ "\e[F" => :end
122
+ }.freeze
123
+ end
119
124
  end