scriptty 0.5.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitattributes +1 -0
- data/.gitignore +3 -0
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/README.rdoc +31 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/scriptty-capture +5 -0
- data/bin/scriptty-dump-screens +4 -0
- data/bin/scriptty-replay +5 -0
- data/bin/scriptty-term-test +4 -0
- data/bin/scriptty-transcript-parse +4 -0
- data/examples/captures/xterm-overlong-line-prompt.bin +9 -0
- data/examples/captures/xterm-vim-session.bin +262 -0
- data/examples/demo-capture.rb +19 -0
- data/examples/telnet-nego.rb +55 -0
- data/lib/scriptty/apps/capture_app/console.rb +104 -0
- data/lib/scriptty/apps/capture_app/password_prompt.rb +65 -0
- data/lib/scriptty/apps/capture_app.rb +213 -0
- data/lib/scriptty/apps/dump_screens_app.rb +166 -0
- data/lib/scriptty/apps/replay_app.rb +229 -0
- data/lib/scriptty/apps/term_test_app.rb +124 -0
- data/lib/scriptty/apps/transcript_parse_app.rb +143 -0
- data/lib/scriptty/cursor.rb +39 -0
- data/lib/scriptty/exception.rb +38 -0
- data/lib/scriptty/expect.rb +392 -0
- data/lib/scriptty/multiline_buffer.rb +192 -0
- data/lib/scriptty/net/event_loop.rb +610 -0
- data/lib/scriptty/screen_pattern/generator.rb +398 -0
- data/lib/scriptty/screen_pattern/parser.rb +558 -0
- data/lib/scriptty/screen_pattern.rb +104 -0
- data/lib/scriptty/term/dg410/dg410-client-escapes.txt +37 -0
- data/lib/scriptty/term/dg410/dg410-escapes.txt +82 -0
- data/lib/scriptty/term/dg410/parser.rb +162 -0
- data/lib/scriptty/term/dg410.rb +489 -0
- data/lib/scriptty/term/xterm/xterm-escapes.txt +73 -0
- data/lib/scriptty/term/xterm.rb +661 -0
- data/lib/scriptty/term.rb +40 -0
- data/lib/scriptty/util/fsm/definition_parser.rb +111 -0
- data/lib/scriptty/util/fsm/scriptty_fsm_definition.treetop +189 -0
- data/lib/scriptty/util/fsm.rb +177 -0
- data/lib/scriptty/util/transcript/reader.rb +96 -0
- data/lib/scriptty/util/transcript/writer.rb +111 -0
- data/test/apps/capture_app_test.rb +123 -0
- data/test/apps/transcript_parse_app_test.rb +118 -0
- data/test/cursor_test.rb +51 -0
- data/test/fsm_definition_parser_test.rb +220 -0
- data/test/fsm_test.rb +322 -0
- data/test/multiline_buffer_test.rb +275 -0
- data/test/net/event_loop_test.rb +402 -0
- data/test/screen_pattern/generator_test.rb +408 -0
- data/test/screen_pattern/parser_test/explicit_cursor_pattern.txt +14 -0
- data/test/screen_pattern/parser_test/explicit_fields.txt +22 -0
- data/test/screen_pattern/parser_test/multiple_patterns.txt +42 -0
- data/test/screen_pattern/parser_test/simple_pattern.txt +14 -0
- data/test/screen_pattern/parser_test/truncated_heredoc.txt +12 -0
- data/test/screen_pattern/parser_test/utf16bebom_pattern.bin +0 -0
- data/test/screen_pattern/parser_test/utf16lebom_pattern.bin +0 -0
- data/test/screen_pattern/parser_test/utf8_pattern.bin +14 -0
- data/test/screen_pattern/parser_test/utf8_unix_pattern.bin +14 -0
- data/test/screen_pattern/parser_test/utf8bom_pattern.bin +14 -0
- data/test/screen_pattern/parser_test.rb +266 -0
- data/test/term/dg410/parser_test.rb +139 -0
- data/test/term/xterm_test.rb +327 -0
- data/test/test_helper.rb +3 -0
- data/test/util/transcript/reader_test.rb +131 -0
- data/test/util/transcript/writer_test.rb +126 -0
- data/test.watchr +29 -0
- metadata +175 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
# = Fake server that replays transcripts to a client
|
2
|
+
# Copyright (C) 2010 Infonium Inc.
|
3
|
+
#
|
4
|
+
# This file is part of ScripTTY.
|
5
|
+
#
|
6
|
+
# ScripTTY is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ScripTTY is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'optparse'
|
20
|
+
require 'scriptty/net/event_loop'
|
21
|
+
require 'scriptty/util/transcript/reader'
|
22
|
+
require 'scriptty/term'
|
23
|
+
require 'logger'
|
24
|
+
require 'stringio'
|
25
|
+
|
26
|
+
module ScripTTY
|
27
|
+
module Apps
|
28
|
+
class ReplayApp
|
29
|
+
attr_reader :term
|
30
|
+
|
31
|
+
def initialize(argv)
|
32
|
+
@client_connection = nil
|
33
|
+
@server_connection = nil
|
34
|
+
@output_file = nil
|
35
|
+
@options = parse_options(argv)
|
36
|
+
@console_password = "" # TODO SECURITY FIXME
|
37
|
+
@attached_consoles = []
|
38
|
+
@log_stringio = StringIO.new
|
39
|
+
@log = Logger.new(@log_stringio)
|
40
|
+
@net = ScripTTY::Net::EventLoop.new
|
41
|
+
@input_transcript = []
|
42
|
+
@last_command = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def detach_console(console)
|
46
|
+
@attached_consoles.delete(console)
|
47
|
+
end
|
48
|
+
|
49
|
+
def log_messages
|
50
|
+
([""]*10 + @log_stringio.string.split("\n"))[-10..-1].map{|line| line.sub(/^.*?\]/, '')}
|
51
|
+
end
|
52
|
+
|
53
|
+
def main
|
54
|
+
@output_file = Util::Transcript::Writer.new(File.open(@options[:output], "w")) if @options[:output]
|
55
|
+
@net.on_accept(@options[:console_addrs] || [], :multiple => true) do |conn|
|
56
|
+
p = CaptureApp::PasswordPrompt.new(conn, "Console password: ")
|
57
|
+
p.authenticate { |password| password == @console_password }
|
58
|
+
p.on_fail { conn.write("Authentiation failed.\r\n") { conn.close } }
|
59
|
+
p.on_success {
|
60
|
+
@attached_consoles << CaptureApp::Console.new(conn, self)
|
61
|
+
@attached_consoles.each { |c| c.refresh! }
|
62
|
+
}
|
63
|
+
end
|
64
|
+
@net.on_accept(@options[:listen_addrs], :multiple => true) do |conn|
|
65
|
+
@output_file.client_open(*conn.remote_address) if @output_file
|
66
|
+
@client_connection = conn
|
67
|
+
@client_connection.on_receive_bytes { |bytes| handle_client_receive_bytes(bytes) }
|
68
|
+
@client_connection.on_close { handle_client_close ; @client_connection = nil }
|
69
|
+
handle_client_connected
|
70
|
+
end
|
71
|
+
@net.main
|
72
|
+
ensure
|
73
|
+
if @output_file
|
74
|
+
@output_file.close
|
75
|
+
@output_file = nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_console_command_entered(cmd)
|
80
|
+
case cmd
|
81
|
+
when /^$/ # repeat last command
|
82
|
+
if @last_command
|
83
|
+
handle_console_command_entered(@last_command)
|
84
|
+
else
|
85
|
+
log.warn("No previous command entered")
|
86
|
+
end
|
87
|
+
return nil
|
88
|
+
when /^(\d*)(n|next)$/i # replay next step
|
89
|
+
count = $1 ? $1.to_i : 1
|
90
|
+
count.times { replay_next }
|
91
|
+
else
|
92
|
+
log.warn("Unknown console command: #{cmd}")
|
93
|
+
end
|
94
|
+
@last_command = cmd
|
95
|
+
end
|
96
|
+
|
97
|
+
# Instruct the event loop to exit.
|
98
|
+
#
|
99
|
+
# Can be invoked by another thread.
|
100
|
+
def exit
|
101
|
+
@net.exit
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
attr_reader :log
|
107
|
+
|
108
|
+
def handle_client_connected
|
109
|
+
@term = ScripTTY::Term.new(@options[:term])
|
110
|
+
@term.on_unknown_sequence do |sequence|
|
111
|
+
log.debug("Unknown escape sequence: #{sequence.inspect}")
|
112
|
+
end
|
113
|
+
replay_start
|
114
|
+
replay_next
|
115
|
+
refresh_consoles
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_client_receive_bytes(bytes)
|
119
|
+
log.info("Received from client: #{bytes.inspect}")
|
120
|
+
refresh_consoles
|
121
|
+
end
|
122
|
+
|
123
|
+
def handle_client_close
|
124
|
+
log.info("Client connection closed")
|
125
|
+
refresh_consoles
|
126
|
+
end
|
127
|
+
|
128
|
+
def replay_start
|
129
|
+
@next_bytes_to_send = nil
|
130
|
+
@transcript = []
|
131
|
+
@options[:input_files].each do |filename|
|
132
|
+
@transcript << ["--- File: #{filename} ---", nil]
|
133
|
+
reader = Util::Transcript::Reader.new
|
134
|
+
File.open(filename, "r") do |infile|
|
135
|
+
line_num = 0
|
136
|
+
until infile.eof?
|
137
|
+
line_num += 1
|
138
|
+
line = infile.readline
|
139
|
+
timestamp, type, args = reader.parse_line(line)
|
140
|
+
if type == :from_server
|
141
|
+
@transcript << ["(#{line_num}) #{line.strip}", args[0]]
|
142
|
+
elsif type == :server_parsed
|
143
|
+
@transcript << ["(#{line_num}) #{line.strip}", args[1]]
|
144
|
+
else
|
145
|
+
@transcript << ["(#{line_num}) #{line.strip}", nil]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def replay_next
|
153
|
+
if @next_bytes_to_send
|
154
|
+
@client_connection.write(@next_bytes_to_send) { refresh_consoles }
|
155
|
+
@term.feed_bytes(@next_bytes_to_send)
|
156
|
+
@next_bytes_to_send = nil
|
157
|
+
end
|
158
|
+
until @transcript.empty?
|
159
|
+
display, bytes = @transcript.shift
|
160
|
+
if bytes
|
161
|
+
log.debug("NXTD: #{display}") # next data
|
162
|
+
@next_bytes_to_send = bytes
|
163
|
+
break
|
164
|
+
else
|
165
|
+
log.debug("INFO: #{display}")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
if @transcript.empty?
|
169
|
+
log.debug("DONE")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def refresh_consoles
|
174
|
+
@attached_consoles.each { |c| c.refresh! }
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_options(argv)
|
178
|
+
args = argv.dup
|
179
|
+
options = {:listen_addrs => [], :console_addrs => []}
|
180
|
+
opts = OptionParser.new do |opts|
|
181
|
+
opts.banner = "Usage: #{opts.program_name} [options] FILE"
|
182
|
+
opts.separator "Stream capture application"
|
183
|
+
opts.on("-l", "--listen [HOST]:PORT", "Listen on the specified HOST:PORT") do |optarg|
|
184
|
+
addr = parse_hostport(optarg, :allow_empty_host => true, :allow_zero_port => true)
|
185
|
+
options[:listen_addrs] << addr
|
186
|
+
end
|
187
|
+
opts.on("-C", "--console [HOST]:PORT", "Debug console on the specified HOST:PORT") do |optarg|
|
188
|
+
addr = parse_hostport(optarg, :allow_empty_host => true, :allow_zero_port => true)
|
189
|
+
options[:console_addrs] << addr
|
190
|
+
end
|
191
|
+
opts.on("-t", "--term NAME", "Terminal to emulate") do |optarg|
|
192
|
+
raise ArgumentError.new("Unsupported terminal #{optarg.inspect}") unless ScripTTY::Term::TERMINAL_TYPES.include?(optarg)
|
193
|
+
options[:term] = optarg
|
194
|
+
end
|
195
|
+
end
|
196
|
+
opts.parse!(args)
|
197
|
+
if args.length < 1
|
198
|
+
$stderr.puts "error: No input file(s) specified."
|
199
|
+
exit 1
|
200
|
+
end
|
201
|
+
unless !options[:listen_addrs].empty? and !options[:console_addrs].empty? and options[:term]
|
202
|
+
$stderr.puts "error: --listen, --console, and --term are mandatory"
|
203
|
+
exit 1
|
204
|
+
end
|
205
|
+
options[:input_files] = args
|
206
|
+
options
|
207
|
+
end
|
208
|
+
|
209
|
+
# Parse [HOST:]PORT into separate host and port. Host is optional, and
|
210
|
+
# might be surrounded by square brackets.
|
211
|
+
def parse_hostport(s, opts={})
|
212
|
+
unless s =~ /\A(\[[^\[\]]*\]|[^\[\]]*):(\d+)\Z/
|
213
|
+
raise ArgumentError.new("Unable to parse host:port")
|
214
|
+
end
|
215
|
+
host, port = [$1, $2]
|
216
|
+
host.gsub!(/\A\[(.*?)\]\Z/, '\1')
|
217
|
+
port = port.to_i
|
218
|
+
raise ArgumentError.new("Invalid port") if port < 0 or port > 0xffff or (port == 0 and !opts[:allow_zero_port])
|
219
|
+
unless opts[:allow_empty_host]
|
220
|
+
raise ArgumentError.new("Host cannot be empty") if host.empty?
|
221
|
+
end
|
222
|
+
[host, port]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
require 'scriptty/apps/capture_app/password_prompt'
|
229
|
+
require 'scriptty/apps/capture_app/console'
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# = Terminal emulation testing app
|
2
|
+
# Copyright (C) 2010 Infonium Inc.
|
3
|
+
#
|
4
|
+
# This file is part of ScripTTY.
|
5
|
+
#
|
6
|
+
# ScripTTY is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ScripTTY is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'optparse'
|
20
|
+
require 'scriptty/term'
|
21
|
+
require 'scriptty/util/transcript/reader'
|
22
|
+
|
23
|
+
module ScripTTY
|
24
|
+
module Apps
|
25
|
+
class TermTestApp
|
26
|
+
def initialize(argv)
|
27
|
+
@options = parse_options(argv)
|
28
|
+
end
|
29
|
+
|
30
|
+
def log(message)
|
31
|
+
@log_buffer << message
|
32
|
+
@log_file.puts(message) if @log_file
|
33
|
+
end
|
34
|
+
|
35
|
+
def main
|
36
|
+
@log_buffer = []
|
37
|
+
@log_file = @options[:log] && File.open(@options[:log], "w")
|
38
|
+
@term = ScripTTY::Term.new(@options[:term])
|
39
|
+
@term.on_unknown_sequence do |seq|
|
40
|
+
log "Unknown escape sequence: #{seq.inspect}"
|
41
|
+
end
|
42
|
+
$stdout.print "\ec" # Reset terminal; clear the screen
|
43
|
+
$stdout.flush
|
44
|
+
time_multiplier = @options[:rate] > 0 ? 1.0/@options[:rate] : 0
|
45
|
+
@options[:input_files].each do |inp|
|
46
|
+
File.open(inp[:path], "rb") do |input_file|
|
47
|
+
time0 = Time.now
|
48
|
+
timestamp0 = timestamp = nil
|
49
|
+
reader = Util::Transcript::Reader.new
|
50
|
+
until input_file.eof?
|
51
|
+
# Read a chunk of the input file
|
52
|
+
case inp[:format]
|
53
|
+
when :binary
|
54
|
+
c = input_file.read(1)
|
55
|
+
@term.feed_byte(c)
|
56
|
+
when :capture
|
57
|
+
timestamp, type, args = reader.parse_line(input_file.readline)
|
58
|
+
next unless type == :from_server
|
59
|
+
bytes = args[0]
|
60
|
+
|
61
|
+
# Wait until the time specified in the timestamp passes
|
62
|
+
timestamp0 = timestamp unless timestamp0 # treat the first timestamp as time 0
|
63
|
+
sleep_time = (timestamp-timestamp0)*time_multiplier - (Time.now - time0)
|
64
|
+
sleep(sleep_time) if sleep_time > 0
|
65
|
+
|
66
|
+
@term.feed_bytes(bytes)
|
67
|
+
else
|
68
|
+
raise "BUG: Invalid format: #{inp[:format]}"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Output the screen contents
|
72
|
+
screen_lines = []
|
73
|
+
screen_lines << "Timestamp: #{timestamp}" if timestamp
|
74
|
+
screen_lines << "Cursor position: #{@term.cursor_pos.inspect}"
|
75
|
+
screen_lines += @term.debug_info if @term.respond_to?(:debug_info)
|
76
|
+
screen_lines << "+" + "-"*@term.width + "+"
|
77
|
+
@term.text.each do |line|
|
78
|
+
screen_lines << "|#{line}|"
|
79
|
+
end
|
80
|
+
screen_lines << "+" + "-"*@term.width + "+"
|
81
|
+
screen_lines << "--- Log ---"
|
82
|
+
([0,@log_buffer.length-10].max..@log_buffer.length-1).each do |i|
|
83
|
+
screen_lines << sprintf("%3d: %s", i+1, @log_buffer[i])
|
84
|
+
end
|
85
|
+
screen_lines << "--- End of log ---"
|
86
|
+
screen_lines << ""
|
87
|
+
$stdout.puts "\e[H" + screen_lines.map{|line| "\e[2K" + line}.join("\n")
|
88
|
+
$stdout.flush
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
@log_file.close if @log_file
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def parse_options(argv)
|
97
|
+
args = argv.dup
|
98
|
+
options = {:term => 'xterm', :input_files => [], :rate => 2}
|
99
|
+
opts = OptionParser.new do |opts|
|
100
|
+
opts.banner = "Usage: #{opts.program_name} [options]"
|
101
|
+
opts.separator "Debug a specified terminal emulator"
|
102
|
+
opts.on("-t", "--term NAME", "Terminal to emulate") do |optarg|
|
103
|
+
raise ArgumentError.new("Unsupported terminal #{optarg.inspect}") unless ScripTTY::Term::TERMINAL_TYPES.include?(optarg)
|
104
|
+
options[:term] = optarg
|
105
|
+
end
|
106
|
+
opts.on("-b", "--binary-input FILE", "Binary file to read") do |optarg|
|
107
|
+
options[:input_files] << {:path => optarg, :format => :binary}
|
108
|
+
end
|
109
|
+
opts.on("-c", "--capture-input FILE", "Capture-format file to read") do |optarg|
|
110
|
+
options[:input_files] << {:path => optarg, :format => :capture}
|
111
|
+
end
|
112
|
+
opts.on("-r", "--rate RATE", "Playback at the specified rate (0 for infinite; default: #{options[:rate]})") do |optarg|
|
113
|
+
options[:rate] = optarg.to_f
|
114
|
+
end
|
115
|
+
opts.on("-L", "--log FILE", "Write log to FILE") do |optarg|
|
116
|
+
options[:log] = optarg
|
117
|
+
end
|
118
|
+
end
|
119
|
+
opts.parse!(args)
|
120
|
+
options
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# = Transcript parsing app
|
2
|
+
# Copyright (C) 2010 Infonium Inc.
|
3
|
+
#
|
4
|
+
# This file is part of ScripTTY.
|
5
|
+
#
|
6
|
+
# ScripTTY is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ScripTTY is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'optparse'
|
20
|
+
require 'scriptty/term'
|
21
|
+
require 'scriptty/util/transcript/reader'
|
22
|
+
require 'scriptty/util/transcript/writer'
|
23
|
+
|
24
|
+
module ScripTTY
|
25
|
+
module Apps
|
26
|
+
class TranscriptParseApp
|
27
|
+
def initialize(argv)
|
28
|
+
@options = parse_options(argv)
|
29
|
+
end
|
30
|
+
|
31
|
+
def main
|
32
|
+
File.open(@options[:output], "w") do |output_file|
|
33
|
+
@writer = ScripTTY::Util::Transcript::Writer.new(output_file)
|
34
|
+
@options[:input_files].each do |input_filename|
|
35
|
+
File.open(input_filename, "rb") do |input_file|
|
36
|
+
reader = ScripTTY::Util::Transcript::Reader.new
|
37
|
+
@last_printable = nil
|
38
|
+
parser_class = ScripTTY::Term.class_by_name(@options[:term]).parser_class
|
39
|
+
|
40
|
+
# Set up server-side FSM
|
41
|
+
@server_parser = parser_class.new :callback => Proc.new { |event_name, fsm|
|
42
|
+
if event_name == :t_printable
|
43
|
+
# Merge printable character sequences together, rather than writing individual "t_printable" events.
|
44
|
+
@last_server_printable ||= ""
|
45
|
+
@last_server_printable += fsm.input_sequence.join
|
46
|
+
else
|
47
|
+
flush_server_printable
|
48
|
+
@writer.server_parsed(event_name, fsm.input_sequence.join)
|
49
|
+
end
|
50
|
+
}
|
51
|
+
@server_parser.on_unknown_sequence { |seq| @writer.server_parsed("?", seq) }
|
52
|
+
|
53
|
+
# Set up client-side FSM
|
54
|
+
if @options[:client]
|
55
|
+
@client_parser = parser_class.new :client => true, :callback => Proc.new { |event_name, fsm|
|
56
|
+
if event_name == :t_printable
|
57
|
+
# Merge printable character sequences together, rather than writing individual "t_printable" events.
|
58
|
+
@last_client_printable ||= ""
|
59
|
+
@last_client_printable += fsm.input_sequence.join
|
60
|
+
else
|
61
|
+
flush_client_printable
|
62
|
+
@writer.client_parsed(event_name, fsm.input_sequence.join)
|
63
|
+
end
|
64
|
+
}
|
65
|
+
@client_parser.on_unknown_sequence { |seq| @writer.client_parsed("?", seq) }
|
66
|
+
end
|
67
|
+
|
68
|
+
until input_file.eof?
|
69
|
+
timestamp, type, args = reader.parse_line(input_file.readline)
|
70
|
+
@writer.override_timestamp = timestamp
|
71
|
+
if type == :from_server
|
72
|
+
@writer.send(type, *args) if @options[:keep]
|
73
|
+
bytes = args[0]
|
74
|
+
@server_parser.feed_bytes(bytes)
|
75
|
+
elsif type == :from_client and @client_parser
|
76
|
+
@writer.send(type, *args) if @options[:keep]
|
77
|
+
bytes = args[0]
|
78
|
+
@client_parser.feed_bytes(bytes)
|
79
|
+
else
|
80
|
+
@writer.send(type, *args)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
flush_server_printable
|
84
|
+
flush_client_printable if @client_parser
|
85
|
+
end
|
86
|
+
end
|
87
|
+
@writer.close
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def flush_server_printable
|
94
|
+
if @last_server_printable
|
95
|
+
@writer.server_parsed(".", @last_server_printable)
|
96
|
+
@last_server_printable = nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def flush_client_printable
|
101
|
+
if @last_client_printable
|
102
|
+
@writer.client_parsed(".", @last_client_printable)
|
103
|
+
@last_client_printable = nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse_options(argv)
|
108
|
+
args = argv.dup
|
109
|
+
options = {:input_files => []}
|
110
|
+
opts = OptionParser.new do |opts|
|
111
|
+
opts.banner = "Usage: #{opts.program_name} [options] FILE..."
|
112
|
+
opts.separator "Parse transcript escape sequences according to a specified terminal emulation,"
|
113
|
+
opts.separator 'converting raw "S" transcript entries into parsed "Sp" entries.'
|
114
|
+
|
115
|
+
opts.on("-t", "--term NAME", "Terminal to emulate") do |optarg|
|
116
|
+
raise ArgumentError.new("Unsupported terminal #{optarg.inspect}") unless ScripTTY::Term::TERMINAL_TYPES.include?(optarg)
|
117
|
+
options[:term] = optarg
|
118
|
+
end
|
119
|
+
opts.on("-k", "--keep", 'Keep original "S" lines in the transcript') do |optarg|
|
120
|
+
options[:keep] = optarg
|
121
|
+
end
|
122
|
+
opts.on("-c", "--[no-]client", "Also parse client (\"C\") entries") do |optarg|
|
123
|
+
options[:client] = optarg
|
124
|
+
end
|
125
|
+
opts.on("-o", "--output FILE", "Write output to FILE") do |optarg|
|
126
|
+
options[:output] = optarg
|
127
|
+
end
|
128
|
+
end
|
129
|
+
opts.parse!(args)
|
130
|
+
if args.length < 1
|
131
|
+
$stderr.puts "error: No input file(s) specified."
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
unless options[:output] and options[:term]
|
135
|
+
$stderr.puts "error: --output and --term are mandatory"
|
136
|
+
exit 1
|
137
|
+
end
|
138
|
+
options[:input_files] = args
|
139
|
+
options
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# = Cursor object
|
2
|
+
# Copyright (C) 2010 Infonium Inc.
|
3
|
+
#
|
4
|
+
# This file is part of ScripTTY.
|
5
|
+
#
|
6
|
+
# ScripTTY is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ScripTTY is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module ScripTTY
|
20
|
+
class Cursor
|
21
|
+
attr_accessor :row, :column
|
22
|
+
def initialize
|
23
|
+
@row = 0
|
24
|
+
@column = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def pos
|
28
|
+
[@row, @column]
|
29
|
+
end
|
30
|
+
|
31
|
+
def pos=(value)
|
32
|
+
raise TypeError.new("must be 2-element array") unless value.is_a?(Array) and value.length == 2
|
33
|
+
@row, @column = value
|
34
|
+
@row ||= 0
|
35
|
+
@column ||= 0
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# = ScripTTY exceptions
|
2
|
+
# Copyright (C) 2010 Infonium Inc.
|
3
|
+
#
|
4
|
+
# This file is part of ScripTTY.
|
5
|
+
#
|
6
|
+
# ScripTTY is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published
|
8
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ScripTTY is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with ScripTTY. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module ScripTTY
|
20
|
+
module Exception
|
21
|
+
# Exception base class
|
22
|
+
class Base < StandardError
|
23
|
+
end
|
24
|
+
|
25
|
+
# Raised when a script times out.
|
26
|
+
class Timeout < Base
|
27
|
+
end
|
28
|
+
|
29
|
+
# Raised when a connection error occurs
|
30
|
+
class ConnectError < Base
|
31
|
+
attr_accessor :orig_exception
|
32
|
+
def initialize(exception)
|
33
|
+
@orig_exception = exception
|
34
|
+
super("Connect error (#{exception.class.name}): #{exception}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|