scriptty 0.5.0-java

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