scriptty 0.5.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|