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.
Files changed (69) hide show
  1. data/.gitattributes +1 -0
  2. data/.gitignore +3 -0
  3. data/COPYING +674 -0
  4. data/COPYING.LESSER +165 -0
  5. data/README.rdoc +31 -0
  6. data/Rakefile +49 -0
  7. data/VERSION +1 -0
  8. data/bin/scriptty-capture +5 -0
  9. data/bin/scriptty-dump-screens +4 -0
  10. data/bin/scriptty-replay +5 -0
  11. data/bin/scriptty-term-test +4 -0
  12. data/bin/scriptty-transcript-parse +4 -0
  13. data/examples/captures/xterm-overlong-line-prompt.bin +9 -0
  14. data/examples/captures/xterm-vim-session.bin +262 -0
  15. data/examples/demo-capture.rb +19 -0
  16. data/examples/telnet-nego.rb +55 -0
  17. data/lib/scriptty/apps/capture_app/console.rb +104 -0
  18. data/lib/scriptty/apps/capture_app/password_prompt.rb +65 -0
  19. data/lib/scriptty/apps/capture_app.rb +213 -0
  20. data/lib/scriptty/apps/dump_screens_app.rb +166 -0
  21. data/lib/scriptty/apps/replay_app.rb +229 -0
  22. data/lib/scriptty/apps/term_test_app.rb +124 -0
  23. data/lib/scriptty/apps/transcript_parse_app.rb +143 -0
  24. data/lib/scriptty/cursor.rb +39 -0
  25. data/lib/scriptty/exception.rb +38 -0
  26. data/lib/scriptty/expect.rb +392 -0
  27. data/lib/scriptty/multiline_buffer.rb +192 -0
  28. data/lib/scriptty/net/event_loop.rb +610 -0
  29. data/lib/scriptty/screen_pattern/generator.rb +398 -0
  30. data/lib/scriptty/screen_pattern/parser.rb +558 -0
  31. data/lib/scriptty/screen_pattern.rb +104 -0
  32. data/lib/scriptty/term/dg410/dg410-client-escapes.txt +37 -0
  33. data/lib/scriptty/term/dg410/dg410-escapes.txt +82 -0
  34. data/lib/scriptty/term/dg410/parser.rb +162 -0
  35. data/lib/scriptty/term/dg410.rb +489 -0
  36. data/lib/scriptty/term/xterm/xterm-escapes.txt +73 -0
  37. data/lib/scriptty/term/xterm.rb +661 -0
  38. data/lib/scriptty/term.rb +40 -0
  39. data/lib/scriptty/util/fsm/definition_parser.rb +111 -0
  40. data/lib/scriptty/util/fsm/scriptty_fsm_definition.treetop +189 -0
  41. data/lib/scriptty/util/fsm.rb +177 -0
  42. data/lib/scriptty/util/transcript/reader.rb +96 -0
  43. data/lib/scriptty/util/transcript/writer.rb +111 -0
  44. data/test/apps/capture_app_test.rb +123 -0
  45. data/test/apps/transcript_parse_app_test.rb +118 -0
  46. data/test/cursor_test.rb +51 -0
  47. data/test/fsm_definition_parser_test.rb +220 -0
  48. data/test/fsm_test.rb +322 -0
  49. data/test/multiline_buffer_test.rb +275 -0
  50. data/test/net/event_loop_test.rb +402 -0
  51. data/test/screen_pattern/generator_test.rb +408 -0
  52. data/test/screen_pattern/parser_test/explicit_cursor_pattern.txt +14 -0
  53. data/test/screen_pattern/parser_test/explicit_fields.txt +22 -0
  54. data/test/screen_pattern/parser_test/multiple_patterns.txt +42 -0
  55. data/test/screen_pattern/parser_test/simple_pattern.txt +14 -0
  56. data/test/screen_pattern/parser_test/truncated_heredoc.txt +12 -0
  57. data/test/screen_pattern/parser_test/utf16bebom_pattern.bin +0 -0
  58. data/test/screen_pattern/parser_test/utf16lebom_pattern.bin +0 -0
  59. data/test/screen_pattern/parser_test/utf8_pattern.bin +14 -0
  60. data/test/screen_pattern/parser_test/utf8_unix_pattern.bin +14 -0
  61. data/test/screen_pattern/parser_test/utf8bom_pattern.bin +14 -0
  62. data/test/screen_pattern/parser_test.rb +266 -0
  63. data/test/term/dg410/parser_test.rb +139 -0
  64. data/test/term/xterm_test.rb +327 -0
  65. data/test/test_helper.rb +3 -0
  66. data/test/util/transcript/reader_test.rb +131 -0
  67. data/test/util/transcript/writer_test.rb +126 -0
  68. data/test.watchr +29 -0
  69. metadata +175 -0
@@ -0,0 +1,489 @@
1
+ # = DG410 terminal emulation
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
+ # XXX TODO - deduplicate between this and xterm.rb
20
+
21
+ require 'scriptty/multiline_buffer'
22
+ require 'scriptty/cursor'
23
+ require 'scriptty/util/fsm'
24
+ require 'set'
25
+
26
+ module ScripTTY # :nodoc:
27
+ module Term
28
+ class DG410
29
+ require 'scriptty/term/dg410/parser' # we want to create the DG410 class here before parser.rb reopens it
30
+
31
+ DEFAULT_FLAGS = {
32
+ :insert_mode => false,
33
+ :wraparound_mode => false,
34
+ :roll_mode => true, # scroll up when the cursor moves beyond the bottom
35
+ }.freeze
36
+
37
+ # width and height of the display buffer
38
+ attr_reader :width, :height
39
+
40
+ # Return ScripTTY::Term::DG410::Parser
41
+ def self.parser_class
42
+ Parser
43
+ end
44
+
45
+ def initialize(height=24, width=80)
46
+ @parser = self.class.parser_class.new(:callback => self, :callback_method => :send)
47
+
48
+ @height = height
49
+ @width = width
50
+
51
+ @proprietary_escape_callback = nil
52
+
53
+ reset_to_initial_state!
54
+ end
55
+
56
+ def on_unknown_sequence(mode=nil, &block)
57
+ @parser.on_unknown_sequence(mode, &block)
58
+ end
59
+
60
+ def on_proprietary_escape(mode=nil, &block)
61
+ if mode == :ignore and !block
62
+ @proprietary_escape_callback = nil
63
+ elsif !mode and block
64
+ @proprietary_escape_callback = block
65
+ else
66
+ raise ArgumentError.new("mode and block are mutually exclusive")
67
+ end
68
+ nil
69
+ end
70
+
71
+ def feed_bytes(bytes)
72
+ @parser.feed_bytes(bytes)
73
+ end
74
+
75
+ def feed_byte(byte)
76
+ @parser.feed_byte(byte)
77
+ end
78
+
79
+ # Return an array of lines of text representing the state of the terminal.
80
+ # Used for debugging.
81
+ def debug_info
82
+ output = []
83
+ output << "state:#{@parser.fsm.state.inspect} seq:#{@parser.fsm.input_sequence && @parser.fsm.input_sequence.join.inspect}"
84
+ output << "scrolling_region: #{@scrolling_region.inspect}"
85
+ output << "flags: roll:#{@flags[:roll_mode]} wraparound:#{@flags[:wraparound_mode]} insert:#{@flags[:insert_mode]}"
86
+ output
87
+ end
88
+
89
+ def inspect # :nodoc:
90
+ # The default inspect method shows way too much information. Simplify it.
91
+ "#<#{self.class.name}:#{sprintf('0x%0x', object_id)} h=#{@height.inspect} w=#{@width.inspect} cursor=#{cursor_pos.inspect}>"
92
+ end
93
+
94
+ # Return an array of strings representing the lines of text on the screen
95
+ #
96
+ # NOTE: If passing copy=false, do not modify the return value or the strings inside it.
97
+ def text(copy=true)
98
+ if copy
99
+ @glyphs.content.map{|line| line.dup}
100
+ else
101
+ @glyphs.content
102
+ end
103
+ end
104
+
105
+ # Return the cursor position, as an array of [row, column].
106
+ #
107
+ # [0,0] represents the topmost, leftmost position.
108
+ def cursor_pos
109
+ [@cursor.row, @cursor.column]
110
+ end
111
+
112
+ # Set the cursor position to [row, column].
113
+ #
114
+ # [0,0] represents the topmost, leftmost position.
115
+ def cursor_pos=(v)
116
+ @cursor.pos = v
117
+ end
118
+
119
+ # Replace the text on the screen with the specified text.
120
+ #
121
+ # NOTE: This is API is very likely to change in the future.
122
+ def text=(a)
123
+ @glyphs.clear!
124
+ @glyphs.replace_at(0, 0, a)
125
+ a
126
+ end
127
+
128
+ protected
129
+
130
+ # Reset to the initial state. Return true.
131
+ def reset_to_initial_state!
132
+ @flags = DEFAULT_FLAGS.dup
133
+
134
+ # current cursor position
135
+ @cursor = Cursor.new
136
+ @cursor.row = @cursor.column = 0
137
+ @saved_cursor_position = [0,0]
138
+
139
+ # Screen buffer
140
+ @glyphs = MultilineBuffer.new(@height, @width) # the displayable characters (as bytes)
141
+ @attrs = MultilineBuffer.new(@height, @width) # character attributes (as bytes)
142
+
143
+ # Vertical scrolling region. An array of [start_row, end_row]. Defaults to [0, height-1].
144
+ @scrolling_region = [0, @height-1]
145
+ true
146
+ end
147
+
148
+ # Replace the character under the cursor with the specified character.
149
+ #
150
+ # If curfwd is true, the cursor is also moved forward.
151
+ #
152
+ # Returns true.
153
+ def put_char!(input, curfwd=false)
154
+ raise TypeError.new("input must be single-character string") unless input.is_a?(String) and input.length == 1
155
+ @glyphs.replace_at(@cursor.row, @cursor.column, input)
156
+ @attrs.replace_at(@cursor.row, @cursor.column, " ")
157
+ cursor_forward! if curfwd
158
+ true
159
+ end
160
+
161
+ # Move the cursor to the leftmost column in the current row, then return true.
162
+ def carriage_return!
163
+ @cursor.column = 0
164
+ true
165
+ end
166
+
167
+ # Move the cursor down one row and return true.
168
+ #
169
+ # If the cursor is on the bottom row of the vertical scrolling region,
170
+ # the region is scrolled. If bot, but the cursor is on the bottom of
171
+ # the screen, this command has no effect.
172
+ def line_feed!
173
+ if @cursor.row == @scrolling_region[1] # cursor is on the bottom row of the scrolling region
174
+ if @flags[:roll_enable]
175
+ scroll_up!
176
+ else
177
+ @cursor.row = @scrolling_region[0]
178
+ end
179
+ elsif @cursor.row >= @height-1
180
+ # do nothing
181
+ else
182
+ cursor_down!
183
+ end
184
+ true
185
+ end
186
+
187
+ # Save the cursor position. Return true.
188
+ def save_cursor!
189
+ @saved_cursor_position = [@cursor.row, @cursor.column]
190
+ true
191
+ end
192
+
193
+ # Restore the saved cursor position. If nothing has been saved, then go to the home position. Return true.
194
+ def restore_cursor!
195
+ @cursor.row, @cursor.column = @saved_cursor_position
196
+ true
197
+ end
198
+
199
+ # Move the cursor down one row and return true.
200
+ # If the cursor is on the bottom row, return false without moving the cursor.
201
+ def cursor_down!
202
+ if @cursor.row >= @height-1
203
+ false
204
+ else
205
+ @cursor.row += 1
206
+ true
207
+ end
208
+ end
209
+
210
+ # Move the cursor up one row and return true.
211
+ # If the cursor is on the top row, return false without moving the cursor.
212
+ def cursor_up!
213
+ if @cursor.row <= 0
214
+ false
215
+ else
216
+ @cursor.row -= 1
217
+ true
218
+ end
219
+ end
220
+
221
+ # Move the cursor right one column and return true.
222
+ # If the cursor is on the right-most column, return false without moving the cursor.
223
+ def cursor_right!
224
+ if @cursor.column >= @width-1
225
+ false
226
+ else
227
+ @cursor.column += 1
228
+ true
229
+ end
230
+ end
231
+
232
+ # Move the cursor to the right. Wrap around if we reach the end of the screen.
233
+ #
234
+ # Return true.
235
+ def cursor_forward!(options={})
236
+ if @cursor.column >= @width-1
237
+ line_feed!
238
+ carriage_return!
239
+ else
240
+ cursor_right!
241
+ end
242
+ end
243
+
244
+ # Move the cursor left one column and return true.
245
+ # If the cursor is on the left-most column, return false without moving the cursor.
246
+ def cursor_left!
247
+ if @cursor.column <= 0
248
+ false
249
+ else
250
+ @cursor.column -= 1
251
+ true
252
+ end
253
+ end
254
+
255
+ alias cursor_back! cursor_left! # In the future, this might not be an alias
256
+
257
+ # Scroll the contents of the screen up by one row and return true.
258
+ # The position of the cursor does not change.
259
+ def scroll_up!
260
+ @glyphs.scroll_up_region(@scrolling_region[0], 0, @scrolling_region[1], @width-1, 1)
261
+ @attrs.scroll_up_region(@scrolling_region[0], 0, @scrolling_region[1], @width-1, 1)
262
+ true
263
+ end
264
+
265
+ # Scroll the contents of the screen down by one row and return true.
266
+ # The position of the cursor does not change.
267
+ def scroll_down!
268
+ @glyphs.scroll_down_region(@scrolling_region[0], 0, @scrolling_region[1], @width-1, 1)
269
+ @attrs.scroll_down_region(@scrolling_region[0], 0, @scrolling_region[1], @width-1, 1)
270
+ true
271
+ end
272
+
273
+ # Erase, starting with the character under the cursor and extending to the end of the line.
274
+ # Return true.
275
+ def erase_to_end_of_line!
276
+ @glyphs.replace_at(@cursor.row, @cursor.column, " "*(@width-@cursor.column))
277
+ @attrs.replace_at(@cursor.row, @cursor.column, " "*(@width-@cursor.column))
278
+ true
279
+ end
280
+
281
+ # Erase, starting with the beginning of the line and extending to the character under the cursor.
282
+ # Return true.
283
+ def erase_to_start_of_line!
284
+ @glyphs.replace_at(@cursor.row, 0, " "*(@cursor.column+1))
285
+ @attrs.replace_at(@cursor.row, 0, " "*(@cursor.column+1))
286
+ true
287
+ end
288
+
289
+ # Erase the current (or specified) line. The cursor position is unchanged.
290
+ # Return true.
291
+ def erase_line!(row=nil)
292
+ row = @cursor.row unless row
293
+ @glyphs.replace_at(row, 0, " "*@width)
294
+ @attrs.replace_at(row, 0, " "*@width)
295
+ true
296
+ end
297
+
298
+ # Erase the window. Return true.
299
+ def erase_window!
300
+ empty_line = " "*@width
301
+ @height.times do |row|
302
+ @glyphs.replace_at(row, 0, empty_line)
303
+ @attrs.replace_at(row, 0, empty_line)
304
+ end
305
+ true
306
+ end
307
+
308
+ # Delete the specified number of lines, starting at the cursor position
309
+ # extending downwards. The lines below the deleted lines are scrolled up,
310
+ # and blank lines are inserted below them.
311
+ # Return true.
312
+ def delete_lines!(count=1)
313
+ @glyphs.scroll_up_region(@cursor.row, 0, @height-1, @width-1, count)
314
+ @attrs.scroll_up_region(@cursor.row, 0, @height-1, @width-1, count)
315
+ true
316
+ end
317
+
318
+ # Delete the specified number of characters, starting at the cursor position
319
+ # extending to the end of the line. The characters to the right of the
320
+ # cursor are scrolled left, and blanks are inserted after them.
321
+ # Return true.
322
+ def delete_characters!(count=1)
323
+ @glyphs.scroll_left_region(@cursor.row, @cursor.column, @cursor.row, @width-1, count)
324
+ @attrs.scroll_left_region(@cursor.row, @cursor.column, @cursor.row, @width-1, count)
325
+ true
326
+ end
327
+
328
+ # Insert the specified number of blank characters at the cursor position.
329
+ # The characters to the right of the cursor are scrolled right, and blanks
330
+ # are inserted in their place.
331
+ # Return true.
332
+ def insert_blank_characters!(count=1)
333
+ @glyphs.scroll_right_region(@cursor.row, @cursor.column, @cursor.row, @width-1, count)
334
+ @attrs.scroll_right_region(@cursor.row, @cursor.column, @cursor.row, @width-1, count)
335
+ true
336
+ end
337
+
338
+ # Insert the specified number of lines characters at the cursor position.
339
+ # The characters to the below the cursor are scrolled down, and blank
340
+ # lines are inserted in their place.
341
+ # Return true.
342
+ def insert_blank_lines!(count=1)
343
+ @glyphs.scroll_down_region(@cursor.row, 0, @height-1, @width-1, count)
344
+ @attrs.scroll_down_region(@cursor.row, 0, @height-1, @width-1, count)
345
+ true
346
+ end
347
+
348
+ private
349
+
350
+ # Set the vertical scrolling region.
351
+ #
352
+ # Values will be clipped.
353
+ def set_scrolling_region!(top, bottom)
354
+ @scrolling_region[0] = [0, [@height-1, top].min].max
355
+ @scrolling_region[1] = [0, [@width-1, bottom].min].max
356
+ nil
357
+ end
358
+
359
+ def error(message) # XXX - This sucks
360
+ raise ArgumentError.new(message)
361
+ #puts message # DEBUG FIXME
362
+ end
363
+
364
+ def t_reset(fsm)
365
+ reset_to_initial_state!
366
+ end
367
+
368
+ # Printable character
369
+ def t_printable(fsm) # :nodoc:
370
+ insert_blank_characters! if @flags[:insert_mode] # TODO
371
+ put_char!(fsm.input)
372
+ cursor_forward!
373
+ end
374
+
375
+ # Move the cursor to the left margin on the current row.
376
+ def t_carriage_return(fsm)
377
+ carriage_return!
378
+ end
379
+
380
+ # Move the cursor to the left margin on the next row.
381
+ def t_new_line(fsm)
382
+ carriage_return!
383
+ line_feed!
384
+ end
385
+
386
+ # Move cursor to the top-left cell of the window.
387
+ def t_window_home(fsm)
388
+ @cursor.pos = [0,0]
389
+ end
390
+
391
+ # Erase all characters starting at the cursor and extending to the end of the window.
392
+ def t_erase_unprotected(fsm)
393
+ # Erase to the end of the current line
394
+ erase_to_end_of_line!
395
+ # Erase subsequent lines to the end of the window.
396
+ (@cursor.row+1..@height-1).each do |row|
397
+ erase_line!(row)
398
+ end
399
+ end
400
+
401
+ # Erase all characters starting at the cursor and extending to the end of the line.
402
+ def t_erase_end_of_line(fsm)
403
+ erase_to_end_of_line!
404
+ end
405
+
406
+ # Move the cursor to the specified position within the window.
407
+ # <020> <column> <row>
408
+ def t_write_window_address(fsm)
409
+ column, row = fsm.input_sequence[1,2].join.unpack("C*")
410
+ row = @cursor.row if row == 0177 # 0177 == special case: do not change the row
411
+ column = @cursor.column if column == 0177 # 0177 == special case: do not change the column
412
+ column = @width-1 if column > @width-1
413
+ row = @height-1 if row > @height-1
414
+ @cursor.pos = [row, column]
415
+ end
416
+
417
+ # Erase all characters in the current window, and move the cursor to
418
+ # the top-left cell of the window.
419
+ def t_erase_window(fsm)
420
+ erase_window!
421
+ @cursor.pos = [0,0]
422
+ end
423
+
424
+ def t_cursor_up(fsm)
425
+ cursor_up!
426
+ end
427
+
428
+ def t_cursor_down(fsm)
429
+ cursor_down!
430
+ end
431
+
432
+ def t_cursor_right(fsm)
433
+ cursor_right!
434
+ end
435
+
436
+ def t_cursor_left(fsm)
437
+ cursor_left!
438
+ end
439
+
440
+ # ESC ] ... M
441
+ # XXX - What is this?
442
+ def t_osc_m(fsm) end # TODO
443
+ def t_osc_k(fsm) end # TODO
444
+
445
+ def t_roll_enable(fsm)
446
+ @flags[:roll_mode] = true
447
+ end
448
+
449
+ def t_roll_disable(fsm)
450
+ @flags[:roll_mode] = false
451
+ end
452
+
453
+ def t_print_window(fsm) end # TODO
454
+
455
+ def t_bell(fsm) end # TODO
456
+
457
+ def t_blink_enable(fsm) end # TODO
458
+ def t_blink_disable(fsm) end # TODO
459
+ def t_underscore_on(fsm) end # TODO
460
+ def t_underscore_off(fsm) end # TODO
461
+ def t_blink_on(fsm) end # TODO
462
+ def t_blink_off(fsm) end # TODO
463
+ def t_reverse_video_on(fsm) end # TODO
464
+ def t_reverse_video_off(fsm) end # TODO
465
+ def t_dim_on(fsm) end # TODO
466
+ def t_dim_off(fsm) end # TODO
467
+ def t_set_cursor_type(fsm) end # TODO
468
+
469
+ def t_telnet_will(fsm) end # TODO
470
+ def t_telnet_wont(fsm) end # TODO
471
+ def t_telnet_do(fsm) end # TODO
472
+ def t_telnet_dont(fsm) end # TODO
473
+ def t_telnet_subnegotiation(fsm) end # TODO
474
+
475
+ # Proprietary escape code
476
+ # <036> ~ <2-byte-command> <n> <n*bytes>
477
+ def t_proprietary_escape(fsm)
478
+ command = fsm.input_sequence[2,2].join
479
+ payload = fsm.input_sequence[5..-1].join
480
+ @proprietary_escape_callback.call(command, payload) if @proprietary_escape_callback
481
+ end
482
+
483
+ # Acknowledgement of proprietary escape sequence.
484
+ # From the server to the client, this is just a single-character ACK
485
+ def t_proprietary_ack(fsm) end # TODO
486
+
487
+ end
488
+ end
489
+ end
@@ -0,0 +1,73 @@
1
+ # Terminal escapes for xterm
2
+ # See:
3
+ # - ECMA-48 / ISO/IEC 6429 (formerly ANSI X3.64)
4
+ # http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
5
+ # - ECMA-6 7-bit Coded Character Set
6
+ # http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-006.pdf
7
+ # - "Xterm Control Sequences"
8
+ # http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
9
+ # - Wikipedia "ANSI Escape Code"
10
+ # http://en.wikipedia.org/wiki/ANSI_escape_code
11
+ '\x00' => t_nul
12
+ '\x07' => t_bell
13
+ '\x08' => t_bs
14
+ '\x0a' => t_new_line
15
+ '\x0d' => t_carriage_return
16
+ '\e' => {
17
+ '7' => t_save_cursor # cross reference: ESC [ s
18
+ '8' => t_restore_cursor # cross reference: ESC [ u
19
+ 'c' => t_reset
20
+ 'H' => t_tab_set
21
+ '=' => t_application_keypad
22
+ '>' => t_normal_keypad
23
+ '[' => t_parse_csi => { # ANSI CSI escape sequence - ESC [ ...
24
+ '?' => t_parse_csi => { # DEC escape sequences - ESC [ ? ...
25
+ 'h' => t_dec_private_mode_set
26
+ 'l' => t_dec_private_mode_reset
27
+ }
28
+ '>' => t_parse_csi => { # ESC [ > ...
29
+ 'c' => t_send_device_attributes_secondary # Secondary DA
30
+ }
31
+ 'A' => t_cursor_up
32
+ 'B' => t_cursor_down
33
+ 'C' => t_cursor_right
34
+ 'D' => t_cursor_left
35
+ 'H' => t_cursor_position
36
+ 'J' => t_erase_in_display
37
+ 'K' => t_erase_in_line
38
+ 'L' => t_insert_lines
39
+ 'M' => t_delete_lines
40
+ 'P' => t_delete_characters
41
+ 'S' => t_scroll_up
42
+ 'T' => t_scroll_down
43
+ 'c' => t_send_device_attributes_primary # Primary DA
44
+ 'g' => t_tab_clear
45
+ 'h' => t_set_mode
46
+ 'l' => t_reset_mode
47
+ 'm' => t_sgr # "select graphics rendition" - colour, bold, underline, etc.
48
+ 'r' => t_set_scrolling_region
49
+ 's' => t_save_cursor # cross reference: ESC 7
50
+ 'u' => t_restore_cursor # cross reference: ESC 8
51
+ }
52
+ ']' => t_parse_osc => { # OSC - "Operating system command"
53
+ '\007' => t_osc_set_text_params
54
+ }
55
+ }
56
+ '\377' => {
57
+ '\372' => t_parse_telnet_sb => {
58
+ * => t_telnet_subnegotiation
59
+ }
60
+ '\373' => {
61
+ * => t_telnet_will
62
+ }
63
+ '\374' => {
64
+ * => t_telnet_wont
65
+ }
66
+ '\375' => {
67
+ * => t_telnet_do
68
+ }
69
+ '\376' => {
70
+ * => t_telnet_dont
71
+ }
72
+ }
73
+ [\x20-\x7e] => t_printable